diff options
Diffstat (limited to 'tools/testing/selftests/bpf')
27 files changed, 6230 insertions, 119 deletions
| diff --git a/tools/testing/selftests/bpf/.gitignore b/tools/testing/selftests/bpf/.gitignore index 541d9d7fad5a..9cf83f895d98 100644 --- a/tools/testing/selftests/bpf/.gitignore +++ b/tools/testing/selftests/bpf/.gitignore @@ -3,3 +3,12 @@ test_maps  test_lru_map  test_lpm_map  test_tag +FEATURE-DUMP.libbpf +fixdep +test_align +test_dev_cgroup +test_progs +test_tcpbpf_user +test_verifier_log +feature +test_libbpf_open diff --git a/tools/testing/selftests/bpf/Makefile b/tools/testing/selftests/bpf/Makefile index 9316e648a880..5c43c187f27c 100644 --- a/tools/testing/selftests/bpf/Makefile +++ b/tools/testing/selftests/bpf/Makefile @@ -11,23 +11,36 @@ ifneq ($(wildcard $(GENHDR)),)  endif  CFLAGS += -Wall -O2 -I$(APIDIR) -I$(LIBDIR) -I$(GENDIR) $(GENFLAGS) -I../../../include -LDLIBS += -lcap -lelf -lrt +LDLIBS += -lcap -lelf -lrt -lpthread +# Order correspond to 'make run_tests' order  TEST_GEN_PROGS = test_verifier test_tag test_maps test_lru_map test_lpm_map test_progs \ -	test_align test_verifier_log test_dev_cgroup +	test_align test_verifier_log test_dev_cgroup test_tcpbpf_user  TEST_GEN_FILES = test_pkt_access.o test_xdp.o test_l4lb.o test_tcp_estats.o test_obj_id.o \  	test_pkt_md_access.o test_xdp_redirect.o test_xdp_meta.o sockmap_parse_prog.o     \ -	sockmap_verdict_prog.o dev_cgroup.o +	sockmap_verdict_prog.o dev_cgroup.o sample_ret0.o test_tracepoint.o \ +	test_l4lb_noinline.o test_xdp_noinline.o test_stacktrace_map.o \ +	sample_map_ret0.o test_tcpbpf_kern.o -TEST_PROGS := test_kmod.sh test_xdp_redirect.sh test_xdp_meta.sh +# Order correspond to 'make run_tests' order +TEST_PROGS := test_kmod.sh \ +	test_libbpf.sh \ +	test_xdp_redirect.sh \ +	test_xdp_meta.sh \ +	test_offload.py + +# Compile but not part of 'make run_tests' +TEST_GEN_PROGS_EXTENDED = test_libbpf_open  include ../lib.mk -BPFOBJ := $(OUTPUT)/libbpf.a $(OUTPUT)/cgroup_helpers.c +BPFOBJ := $(OUTPUT)/libbpf.a cgroup_helpers.c  $(TEST_GEN_PROGS): $(BPFOBJ) +$(TEST_GEN_PROGS_EXTENDED): $(OUTPUT)/libbpf.a +  .PHONY: force  # force a rebuild of BPFOBJ when its dependencies are updated @@ -49,8 +62,13 @@ else    CPU ?= generic  endif -%.o: %.c -	$(CLANG) -I. -I./include/uapi -I../../../include/uapi \ -		 -Wno-compare-distinct-pointer-types          \ +CLANG_FLAGS = -I. -I./include/uapi -I../../../include/uapi \ +	      -Wno-compare-distinct-pointer-types + +$(OUTPUT)/test_l4lb_noinline.o: CLANG_FLAGS += -fno-inline +$(OUTPUT)/test_xdp_noinline.o: CLANG_FLAGS += -fno-inline + +$(OUTPUT)/%.o: %.c +	$(CLANG) $(CLANG_FLAGS) \  		 -O2 -target bpf -emit-llvm -c $< -o - |      \  	$(LLC) -march=bpf -mcpu=$(CPU) -filetype=obj -o $@ diff --git a/tools/testing/selftests/bpf/bpf_helpers.h b/tools/testing/selftests/bpf/bpf_helpers.h index fd9a17fa8a8b..dde2c11d7771 100644 --- a/tools/testing/selftests/bpf/bpf_helpers.h +++ b/tools/testing/selftests/bpf/bpf_helpers.h @@ -71,6 +71,8 @@ static int (*bpf_setsockopt)(void *ctx, int level, int optname, void *optval,  static int (*bpf_getsockopt)(void *ctx, int level, int optname, void *optval,  			     int optlen) =  	(void *) BPF_FUNC_getsockopt; +static int (*bpf_sock_ops_cb_flags_set)(void *ctx, int flags) = +	(void *) BPF_FUNC_sock_ops_cb_flags_set;  static int (*bpf_sk_redirect_map)(void *ctx, void *map, int key, int flags) =  	(void *) BPF_FUNC_sk_redirect_map;  static int (*bpf_sock_map_update)(void *map, void *key, void *value, @@ -82,7 +84,8 @@ static int (*bpf_perf_event_read_value)(void *map, unsigned long long flags,  static int (*bpf_perf_prog_read_value)(void *ctx, void *buf,  				       unsigned int buf_size) =  	(void *) BPF_FUNC_perf_prog_read_value; - +static int (*bpf_override_return)(void *ctx, unsigned long rc) = +	(void *) BPF_FUNC_override_return;  /* llvm builtin functions that eBPF C program may use to   * emit BPF_LD_ABS and BPF_LD_IND instructions diff --git a/tools/testing/selftests/bpf/config b/tools/testing/selftests/bpf/config index 52d53ed08769..983dd25d49f4 100644 --- a/tools/testing/selftests/bpf/config +++ b/tools/testing/selftests/bpf/config @@ -3,3 +3,5 @@ CONFIG_BPF_SYSCALL=y  CONFIG_NET_CLS_BPF=m  CONFIG_BPF_EVENTS=y  CONFIG_TEST_BPF=m +CONFIG_CGROUP_BPF=y +CONFIG_NETDEVSIM=m diff --git a/tools/testing/selftests/bpf/sample_map_ret0.c b/tools/testing/selftests/bpf/sample_map_ret0.c new file mode 100644 index 000000000000..0756303676ac --- /dev/null +++ b/tools/testing/selftests/bpf/sample_map_ret0.c @@ -0,0 +1,34 @@ +/* SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause) */ +#include <linux/bpf.h> +#include "bpf_helpers.h" + +struct bpf_map_def SEC("maps") htab = { +	.type = BPF_MAP_TYPE_HASH, +	.key_size = sizeof(__u32), +	.value_size = sizeof(long), +	.max_entries = 2, +}; + +struct bpf_map_def SEC("maps") array = { +	.type = BPF_MAP_TYPE_ARRAY, +	.key_size = sizeof(__u32), +	.value_size = sizeof(long), +	.max_entries = 2, +}; + +/* Sample program which should always load for testing control paths. */ +SEC(".text") int func() +{ +	__u64 key64 = 0; +	__u32 key = 0; +	long *value; + +	value = bpf_map_lookup_elem(&htab, &key); +	if (!value) +		return 1; +	value = bpf_map_lookup_elem(&array, &key64); +	if (!value) +		return 1; + +	return 0; +} diff --git a/tools/testing/selftests/bpf/sample_ret0.c b/tools/testing/selftests/bpf/sample_ret0.c new file mode 100644 index 000000000000..fec99750d6ea --- /dev/null +++ b/tools/testing/selftests/bpf/sample_ret0.c @@ -0,0 +1,7 @@ +/* SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause) */ + +/* Sample program which should always load for testing control paths. */ +int func() +{ +	return 0; +} diff --git a/tools/testing/selftests/bpf/tcp_client.py b/tools/testing/selftests/bpf/tcp_client.py new file mode 100755 index 000000000000..481dccdf140c --- /dev/null +++ b/tools/testing/selftests/bpf/tcp_client.py @@ -0,0 +1,51 @@ +#!/usr/bin/env python2 +# +# SPDX-License-Identifier: GPL-2.0 +# + +import sys, os, os.path, getopt +import socket, time +import subprocess +import select + +def read(sock, n): +    buf = '' +    while len(buf) < n: +        rem = n - len(buf) +        try: s = sock.recv(rem) +        except (socket.error), e: return '' +        buf += s +    return buf + +def send(sock, s): +    total = len(s) +    count = 0 +    while count < total: +        try: n = sock.send(s) +        except (socket.error), e: n = 0 +        if n == 0: +            return count; +        count += n +    return count + + +serverPort = int(sys.argv[1]) +HostName = socket.gethostname() + +# create active socket +sock = socket.socket(socket.AF_INET6, socket.SOCK_STREAM) +try: +    sock.connect((HostName, serverPort)) +except socket.error as e: +    sys.exit(1) + +buf = '' +n = 0 +while n < 1000: +    buf += '+' +    n += 1 + +sock.settimeout(1); +n = send(sock, buf) +n = read(sock, 500) +sys.exit(0) diff --git a/tools/testing/selftests/bpf/tcp_server.py b/tools/testing/selftests/bpf/tcp_server.py new file mode 100755 index 000000000000..bc454d7d0be2 --- /dev/null +++ b/tools/testing/selftests/bpf/tcp_server.py @@ -0,0 +1,83 @@ +#!/usr/bin/env python2 +# +# SPDX-License-Identifier: GPL-2.0 +# + +import sys, os, os.path, getopt +import socket, time +import subprocess +import select + +def read(sock, n): +    buf = '' +    while len(buf) < n: +        rem = n - len(buf) +        try: s = sock.recv(rem) +        except (socket.error), e: return '' +        buf += s +    return buf + +def send(sock, s): +    total = len(s) +    count = 0 +    while count < total: +        try: n = sock.send(s) +        except (socket.error), e: n = 0 +        if n == 0: +            return count; +        count += n +    return count + + +SERVER_PORT = 12877 +MAX_PORTS = 2 + +serverPort = SERVER_PORT +serverSocket = None + +HostName = socket.gethostname() + +# create passive socket +serverSocket = socket.socket(socket.AF_INET6, socket.SOCK_STREAM) +host = socket.gethostname() + +try: serverSocket.bind((host, 0)) +except socket.error as msg: +    print 'bind fails: ', msg + +sn = serverSocket.getsockname() +serverPort = sn[1] + +cmdStr = ("./tcp_client.py %d &") % (serverPort) +os.system(cmdStr) + +buf = '' +n = 0 +while n < 500: +    buf += '.' +    n += 1 + +serverSocket.listen(MAX_PORTS) +readList = [serverSocket] + +while True: +    readyRead, readyWrite, inError = \ +        select.select(readList, [], [], 2) + +    if len(readyRead) > 0: +        waitCount = 0 +        for sock in readyRead: +            if sock == serverSocket: +                (clientSocket, address) = serverSocket.accept() +                address = str(address[0]) +                readList.append(clientSocket) +            else: +                sock.settimeout(1); +                s = read(sock, 1000) +                n = send(sock, buf) +                sock.close() +                serverSocket.close() +                sys.exit(0) +    else: +        print 'Select timeout!' +        sys.exit(1) diff --git a/tools/testing/selftests/bpf/test_align.c b/tools/testing/selftests/bpf/test_align.c index 471bbbdb94db..ff8bd7e3e50c 100644 --- a/tools/testing/selftests/bpf/test_align.c +++ b/tools/testing/selftests/bpf/test_align.c @@ -64,11 +64,11 @@ static struct bpf_align_test tests[] = {  		.matches = {  			{1, "R1=ctx(id=0,off=0,imm=0)"},  			{1, "R10=fp0"}, -			{1, "R3=inv2"}, -			{2, "R3=inv4"}, -			{3, "R3=inv8"}, -			{4, "R3=inv16"}, -			{5, "R3=inv32"}, +			{1, "R3_w=inv2"}, +			{2, "R3_w=inv4"}, +			{3, "R3_w=inv8"}, +			{4, "R3_w=inv16"}, +			{5, "R3_w=inv32"},  		},  	},  	{ @@ -92,17 +92,17 @@ static struct bpf_align_test tests[] = {  		.matches = {  			{1, "R1=ctx(id=0,off=0,imm=0)"},  			{1, "R10=fp0"}, -			{1, "R3=inv1"}, -			{2, "R3=inv2"}, -			{3, "R3=inv4"}, -			{4, "R3=inv8"}, -			{5, "R3=inv16"}, -			{6, "R3=inv1"}, -			{7, "R4=inv32"}, -			{8, "R4=inv16"}, -			{9, "R4=inv8"}, -			{10, "R4=inv4"}, -			{11, "R4=inv2"}, +			{1, "R3_w=inv1"}, +			{2, "R3_w=inv2"}, +			{3, "R3_w=inv4"}, +			{4, "R3_w=inv8"}, +			{5, "R3_w=inv16"}, +			{6, "R3_w=inv1"}, +			{7, "R4_w=inv32"}, +			{8, "R4_w=inv16"}, +			{9, "R4_w=inv8"}, +			{10, "R4_w=inv4"}, +			{11, "R4_w=inv2"},  		},  	},  	{ @@ -121,12 +121,12 @@ static struct bpf_align_test tests[] = {  		.matches = {  			{1, "R1=ctx(id=0,off=0,imm=0)"},  			{1, "R10=fp0"}, -			{1, "R3=inv4"}, -			{2, "R3=inv8"}, -			{3, "R3=inv10"}, -			{4, "R4=inv8"}, -			{5, "R4=inv12"}, -			{6, "R4=inv14"}, +			{1, "R3_w=inv4"}, +			{2, "R3_w=inv8"}, +			{3, "R3_w=inv10"}, +			{4, "R4_w=inv8"}, +			{5, "R4_w=inv12"}, +			{6, "R4_w=inv14"},  		},  	},  	{ @@ -143,10 +143,10 @@ static struct bpf_align_test tests[] = {  		.matches = {  			{1, "R1=ctx(id=0,off=0,imm=0)"},  			{1, "R10=fp0"}, -			{1, "R3=inv7"}, -			{2, "R3=inv7"}, -			{3, "R3=inv14"}, -			{4, "R3=inv56"}, +			{1, "R3_w=inv7"}, +			{2, "R3_w=inv7"}, +			{3, "R3_w=inv14"}, +			{4, "R3_w=inv56"},  		},  	}, @@ -185,18 +185,18 @@ static struct bpf_align_test tests[] = {  		.prog_type = BPF_PROG_TYPE_SCHED_CLS,  		.matches = {  			{7, "R0=pkt(id=0,off=8,r=8,imm=0)"}, -			{7, "R3=inv(id=0,umax_value=255,var_off=(0x0; 0xff))"}, -			{8, "R3=inv(id=0,umax_value=510,var_off=(0x0; 0x1fe))"}, -			{9, "R3=inv(id=0,umax_value=1020,var_off=(0x0; 0x3fc))"}, -			{10, "R3=inv(id=0,umax_value=2040,var_off=(0x0; 0x7f8))"}, -			{11, "R3=inv(id=0,umax_value=4080,var_off=(0x0; 0xff0))"}, +			{7, "R3_w=inv(id=0,umax_value=255,var_off=(0x0; 0xff))"}, +			{8, "R3_w=inv(id=0,umax_value=510,var_off=(0x0; 0x1fe))"}, +			{9, "R3_w=inv(id=0,umax_value=1020,var_off=(0x0; 0x3fc))"}, +			{10, "R3_w=inv(id=0,umax_value=2040,var_off=(0x0; 0x7f8))"}, +			{11, "R3_w=inv(id=0,umax_value=4080,var_off=(0x0; 0xff0))"},  			{18, "R3=pkt_end(id=0,off=0,imm=0)"}, -			{18, "R4=inv(id=0,umax_value=255,var_off=(0x0; 0xff))"}, -			{19, "R4=inv(id=0,umax_value=8160,var_off=(0x0; 0x1fe0))"}, -			{20, "R4=inv(id=0,umax_value=4080,var_off=(0x0; 0xff0))"}, -			{21, "R4=inv(id=0,umax_value=2040,var_off=(0x0; 0x7f8))"}, -			{22, "R4=inv(id=0,umax_value=1020,var_off=(0x0; 0x3fc))"}, -			{23, "R4=inv(id=0,umax_value=510,var_off=(0x0; 0x1fe))"}, +			{18, "R4_w=inv(id=0,umax_value=255,var_off=(0x0; 0xff))"}, +			{19, "R4_w=inv(id=0,umax_value=8160,var_off=(0x0; 0x1fe0))"}, +			{20, "R4_w=inv(id=0,umax_value=4080,var_off=(0x0; 0xff0))"}, +			{21, "R4_w=inv(id=0,umax_value=2040,var_off=(0x0; 0x7f8))"}, +			{22, "R4_w=inv(id=0,umax_value=1020,var_off=(0x0; 0x3fc))"}, +			{23, "R4_w=inv(id=0,umax_value=510,var_off=(0x0; 0x1fe))"},  		},  	},  	{ @@ -217,16 +217,16 @@ static struct bpf_align_test tests[] = {  		},  		.prog_type = BPF_PROG_TYPE_SCHED_CLS,  		.matches = { -			{7, "R3=inv(id=0,umax_value=255,var_off=(0x0; 0xff))"}, -			{8, "R4=inv(id=0,umax_value=255,var_off=(0x0; 0xff))"}, -			{9, "R4=inv(id=0,umax_value=255,var_off=(0x0; 0xff))"}, -			{10, "R4=inv(id=0,umax_value=255,var_off=(0x0; 0xff))"}, -			{11, "R4=inv(id=0,umax_value=510,var_off=(0x0; 0x1fe))"}, -			{12, "R4=inv(id=0,umax_value=255,var_off=(0x0; 0xff))"}, -			{13, "R4=inv(id=0,umax_value=1020,var_off=(0x0; 0x3fc))"}, -			{14, "R4=inv(id=0,umax_value=255,var_off=(0x0; 0xff))"}, -			{15, "R4=inv(id=0,umax_value=2040,var_off=(0x0; 0x7f8))"}, -			{16, "R4=inv(id=0,umax_value=4080,var_off=(0x0; 0xff0))"}, +			{7, "R3_w=inv(id=0,umax_value=255,var_off=(0x0; 0xff))"}, +			{8, "R4_w=inv(id=0,umax_value=255,var_off=(0x0; 0xff))"}, +			{9, "R4_w=inv(id=0,umax_value=255,var_off=(0x0; 0xff))"}, +			{10, "R4_w=inv(id=0,umax_value=255,var_off=(0x0; 0xff))"}, +			{11, "R4_w=inv(id=0,umax_value=510,var_off=(0x0; 0x1fe))"}, +			{12, "R4_w=inv(id=0,umax_value=255,var_off=(0x0; 0xff))"}, +			{13, "R4_w=inv(id=0,umax_value=1020,var_off=(0x0; 0x3fc))"}, +			{14, "R4_w=inv(id=0,umax_value=255,var_off=(0x0; 0xff))"}, +			{15, "R4_w=inv(id=0,umax_value=2040,var_off=(0x0; 0x7f8))"}, +			{16, "R4_w=inv(id=0,umax_value=4080,var_off=(0x0; 0xff0))"},  		},  	},  	{ @@ -257,14 +257,14 @@ static struct bpf_align_test tests[] = {  		},  		.prog_type = BPF_PROG_TYPE_SCHED_CLS,  		.matches = { -			{4, "R5=pkt(id=0,off=0,r=0,imm=0)"}, -			{5, "R5=pkt(id=0,off=14,r=0,imm=0)"}, -			{6, "R4=pkt(id=0,off=14,r=0,imm=0)"}, +			{4, "R5_w=pkt(id=0,off=0,r=0,imm=0)"}, +			{5, "R5_w=pkt(id=0,off=14,r=0,imm=0)"}, +			{6, "R4_w=pkt(id=0,off=14,r=0,imm=0)"},  			{10, "R2=pkt(id=0,off=0,r=18,imm=0)"},  			{10, "R5=pkt(id=0,off=14,r=18,imm=0)"}, -			{10, "R4=inv(id=0,umax_value=255,var_off=(0x0; 0xff))"}, -			{14, "R4=inv(id=0,umax_value=65535,var_off=(0x0; 0xffff))"}, -			{15, "R4=inv(id=0,umax_value=65535,var_off=(0x0; 0xffff))"}, +			{10, "R4_w=inv(id=0,umax_value=255,var_off=(0x0; 0xff))"}, +			{14, "R4_w=inv(id=0,umax_value=65535,var_off=(0x0; 0xffff))"}, +			{15, "R4_w=inv(id=0,umax_value=65535,var_off=(0x0; 0xffff))"},  		},  	},  	{ @@ -320,11 +320,11 @@ static struct bpf_align_test tests[] = {  			 * alignment of 4.  			 */  			{8, "R2=pkt(id=0,off=0,r=8,imm=0)"}, -			{8, "R6=inv(id=0,umax_value=1020,var_off=(0x0; 0x3fc))"}, +			{8, "R6_w=inv(id=0,umax_value=1020,var_off=(0x0; 0x3fc))"},  			/* Offset is added to packet pointer R5, resulting in  			 * known fixed offset, and variable offset from R6.  			 */ -			{11, "R5=pkt(id=1,off=14,r=0,umax_value=1020,var_off=(0x0; 0x3fc))"}, +			{11, "R5_w=pkt(id=1,off=14,r=0,umax_value=1020,var_off=(0x0; 0x3fc))"},  			/* At the time the word size load is performed from R5,  			 * it's total offset is NET_IP_ALIGN + reg->off (0) +  			 * reg->aux_off (14) which is 16.  Then the variable @@ -336,11 +336,11 @@ static struct bpf_align_test tests[] = {  			/* Variable offset is added to R5 packet pointer,  			 * resulting in auxiliary alignment of 4.  			 */ -			{18, "R5=pkt(id=2,off=0,r=0,umax_value=1020,var_off=(0x0; 0x3fc))"}, +			{18, "R5_w=pkt(id=2,off=0,r=0,umax_value=1020,var_off=(0x0; 0x3fc))"},  			/* Constant offset is added to R5, resulting in  			 * reg->off of 14.  			 */ -			{19, "R5=pkt(id=2,off=14,r=0,umax_value=1020,var_off=(0x0; 0x3fc))"}, +			{19, "R5_w=pkt(id=2,off=14,r=0,umax_value=1020,var_off=(0x0; 0x3fc))"},  			/* At the time the word size load is performed from R5,  			 * its total fixed offset is NET_IP_ALIGN + reg->off  			 * (14) which is 16.  Then the variable offset is 4-byte @@ -352,18 +352,18 @@ static struct bpf_align_test tests[] = {  			/* Constant offset is added to R5 packet pointer,  			 * resulting in reg->off value of 14.  			 */ -			{26, "R5=pkt(id=0,off=14,r=8"}, +			{26, "R5_w=pkt(id=0,off=14,r=8"},  			/* Variable offset is added to R5, resulting in a  			 * variable offset of (4n).  			 */ -			{27, "R5=pkt(id=3,off=14,r=0,umax_value=1020,var_off=(0x0; 0x3fc))"}, +			{27, "R5_w=pkt(id=3,off=14,r=0,umax_value=1020,var_off=(0x0; 0x3fc))"},  			/* Constant is added to R5 again, setting reg->off to 18. */ -			{28, "R5=pkt(id=3,off=18,r=0,umax_value=1020,var_off=(0x0; 0x3fc))"}, +			{28, "R5_w=pkt(id=3,off=18,r=0,umax_value=1020,var_off=(0x0; 0x3fc))"},  			/* And once more we add a variable; resulting var_off  			 * is still (4n), fixed offset is not changed.  			 * Also, we create a new reg->id.  			 */ -			{29, "R5=pkt(id=4,off=18,r=0,umax_value=2040,var_off=(0x0; 0x7fc))"}, +			{29, "R5_w=pkt(id=4,off=18,r=0,umax_value=2040,var_off=(0x0; 0x7fc))"},  			/* At the time the word size load is performed from R5,  			 * its total fixed offset is NET_IP_ALIGN + reg->off (18)  			 * which is 20.  Then the variable offset is (4n), so @@ -410,11 +410,11 @@ static struct bpf_align_test tests[] = {  			 * alignment of 4.  			 */  			{8, "R2=pkt(id=0,off=0,r=8,imm=0)"}, -			{8, "R6=inv(id=0,umax_value=1020,var_off=(0x0; 0x3fc))"}, +			{8, "R6_w=inv(id=0,umax_value=1020,var_off=(0x0; 0x3fc))"},  			/* Adding 14 makes R6 be (4n+2) */ -			{9, "R6=inv(id=0,umin_value=14,umax_value=1034,var_off=(0x2; 0x7fc))"}, +			{9, "R6_w=inv(id=0,umin_value=14,umax_value=1034,var_off=(0x2; 0x7fc))"},  			/* Packet pointer has (4n+2) offset */ -			{11, "R5=pkt(id=1,off=0,r=0,umin_value=14,umax_value=1034,var_off=(0x2; 0x7fc))"}, +			{11, "R5_w=pkt(id=1,off=0,r=0,umin_value=14,umax_value=1034,var_off=(0x2; 0x7fc))"},  			{13, "R4=pkt(id=1,off=4,r=0,umin_value=14,umax_value=1034,var_off=(0x2; 0x7fc))"},  			/* At the time the word size load is performed from R5,  			 * its total fixed offset is NET_IP_ALIGN + reg->off (0) @@ -426,11 +426,11 @@ static struct bpf_align_test tests[] = {  			/* Newly read value in R6 was shifted left by 2, so has  			 * known alignment of 4.  			 */ -			{18, "R6=inv(id=0,umax_value=1020,var_off=(0x0; 0x3fc))"}, +			{18, "R6_w=inv(id=0,umax_value=1020,var_off=(0x0; 0x3fc))"},  			/* Added (4n) to packet pointer's (4n+2) var_off, giving  			 * another (4n+2).  			 */ -			{19, "R5=pkt(id=2,off=0,r=0,umin_value=14,umax_value=2054,var_off=(0x2; 0xffc))"}, +			{19, "R5_w=pkt(id=2,off=0,r=0,umin_value=14,umax_value=2054,var_off=(0x2; 0xffc))"},  			{21, "R4=pkt(id=2,off=4,r=0,umin_value=14,umax_value=2054,var_off=(0x2; 0xffc))"},  			/* At the time the word size load is performed from R5,  			 * its total fixed offset is NET_IP_ALIGN + reg->off (0) @@ -446,11 +446,9 @@ static struct bpf_align_test tests[] = {  		.insns = {  			PREP_PKT_POINTERS,  			BPF_MOV64_IMM(BPF_REG_0, 0), -			/* ptr & const => unknown & const */ -			BPF_MOV64_REG(BPF_REG_5, BPF_REG_2), -			BPF_ALU64_IMM(BPF_AND, BPF_REG_5, 0x40), -			/* ptr << const => unknown << const */ -			BPF_MOV64_REG(BPF_REG_5, BPF_REG_2), +			/* (ptr - ptr) << 2 */ +			BPF_MOV64_REG(BPF_REG_5, BPF_REG_3), +			BPF_ALU64_REG(BPF_SUB, BPF_REG_5, BPF_REG_2),  			BPF_ALU64_IMM(BPF_LSH, BPF_REG_5, 2),  			/* We have a (4n) value.  Let's make a packet offset  			 * out of it.  First add 14, to make it a (4n+2) @@ -473,8 +471,26 @@ static struct bpf_align_test tests[] = {  		.prog_type = BPF_PROG_TYPE_SCHED_CLS,  		.result = REJECT,  		.matches = { -			{4, "R5=pkt(id=0,off=0,r=0,imm=0)"}, -			/* R5 bitwise operator &= on pointer prohibited */ +			{4, "R5_w=pkt_end(id=0,off=0,imm=0)"}, +			/* (ptr - ptr) << 2 == unknown, (4n) */ +			{6, "R5_w=inv(id=0,smax_value=9223372036854775804,umax_value=18446744073709551612,var_off=(0x0; 0xfffffffffffffffc))"}, +			/* (4n) + 14 == (4n+2).  We blow our bounds, because +			 * the add could overflow. +			 */ +			{7, "R5=inv(id=0,var_off=(0x2; 0xfffffffffffffffc))"}, +			/* Checked s>=0 */ +			{9, "R5=inv(id=0,umin_value=2,umax_value=9223372036854775806,var_off=(0x2; 0x7ffffffffffffffc))"}, +			/* packet pointer + nonnegative (4n+2) */ +			{11, "R6_w=pkt(id=1,off=0,r=0,umin_value=2,umax_value=9223372036854775806,var_off=(0x2; 0x7ffffffffffffffc))"}, +			{13, "R4=pkt(id=1,off=4,r=0,umin_value=2,umax_value=9223372036854775806,var_off=(0x2; 0x7ffffffffffffffc))"}, +			/* NET_IP_ALIGN + (4n+2) == (4n), alignment is fine. +			 * We checked the bounds, but it might have been able +			 * to overflow if the packet pointer started in the +			 * upper half of the address space. +			 * So we did not get a 'range' on R6, and the access +			 * attempt will fail. +			 */ +			{15, "R6=pkt(id=1,off=0,r=0,umin_value=2,umax_value=9223372036854775806,var_off=(0x2; 0x7ffffffffffffffc))"},  		}  	},  	{ @@ -510,11 +526,11 @@ static struct bpf_align_test tests[] = {  			 * alignment of 4.  			 */  			{7, "R2=pkt(id=0,off=0,r=8,imm=0)"}, -			{9, "R6=inv(id=0,umax_value=1020,var_off=(0x0; 0x3fc))"}, +			{9, "R6_w=inv(id=0,umax_value=1020,var_off=(0x0; 0x3fc))"},  			/* Adding 14 makes R6 be (4n+2) */ -			{10, "R6=inv(id=0,umin_value=14,umax_value=1034,var_off=(0x2; 0x7fc))"}, +			{10, "R6_w=inv(id=0,umin_value=14,umax_value=1034,var_off=(0x2; 0x7fc))"},  			/* New unknown value in R7 is (4n) */ -			{11, "R7=inv(id=0,umax_value=1020,var_off=(0x0; 0x3fc))"}, +			{11, "R7_w=inv(id=0,umax_value=1020,var_off=(0x0; 0x3fc))"},  			/* Subtracting it from R6 blows our unsigned bounds */  			{12, "R6=inv(id=0,smin_value=-1006,smax_value=1034,var_off=(0x2; 0xfffffffffffffffc))"},  			/* Checked s>= 0 */ @@ -563,15 +579,15 @@ static struct bpf_align_test tests[] = {  			 * alignment of 4.  			 */  			{7, "R2=pkt(id=0,off=0,r=8,imm=0)"}, -			{10, "R6=inv(id=0,umax_value=60,var_off=(0x0; 0x3c))"}, +			{10, "R6_w=inv(id=0,umax_value=60,var_off=(0x0; 0x3c))"},  			/* Adding 14 makes R6 be (4n+2) */ -			{11, "R6=inv(id=0,umin_value=14,umax_value=74,var_off=(0x2; 0x7c))"}, +			{11, "R6_w=inv(id=0,umin_value=14,umax_value=74,var_off=(0x2; 0x7c))"},  			/* Subtracting from packet pointer overflows ubounds */ -			{13, "R5=pkt(id=1,off=0,r=8,umin_value=18446744073709551542,umax_value=18446744073709551602,var_off=(0xffffffffffffff82; 0x7c))"}, +			{13, "R5_w=pkt(id=1,off=0,r=8,umin_value=18446744073709551542,umax_value=18446744073709551602,var_off=(0xffffffffffffff82; 0x7c))"},  			/* New unknown value in R7 is (4n), >= 76 */ -			{15, "R7=inv(id=0,umin_value=76,umax_value=1096,var_off=(0x0; 0x7fc))"}, +			{15, "R7_w=inv(id=0,umin_value=76,umax_value=1096,var_off=(0x0; 0x7fc))"},  			/* Adding it to packet pointer gives nice bounds again */ -			{16, "R5=pkt(id=2,off=0,r=0,umin_value=2,umax_value=1082,var_off=(0x2; 0x7fc))"}, +			{16, "R5_w=pkt(id=2,off=0,r=0,umin_value=2,umax_value=1082,var_off=(0x2; 0x7fc))"},  			/* At the time the word size load is performed from R5,  			 * its total fixed offset is NET_IP_ALIGN + reg->off (0)  			 * which is 2.  Then the variable offset is (4n+2), so diff --git a/tools/testing/selftests/bpf/test_dev_cgroup.c b/tools/testing/selftests/bpf/test_dev_cgroup.c index 02c85d6c89b0..3489cc283433 100644 --- a/tools/testing/selftests/bpf/test_dev_cgroup.c +++ b/tools/testing/selftests/bpf/test_dev_cgroup.c @@ -10,6 +10,8 @@  #include <string.h>  #include <errno.h>  #include <assert.h> +#include <sys/time.h> +#include <sys/resource.h>  #include <linux/bpf.h>  #include <bpf/bpf.h> @@ -19,19 +21,23 @@  #define DEV_CGROUP_PROG "./dev_cgroup.o" -#define TEST_CGROUP "test-bpf-based-device-cgroup/" +#define TEST_CGROUP "/test-bpf-based-device-cgroup/"  int main(int argc, char **argv)  { +	struct rlimit limit  = { RLIM_INFINITY, RLIM_INFINITY };  	struct bpf_object *obj;  	int error = EXIT_FAILURE;  	int prog_fd, cgroup_fd;  	__u32 prog_cnt; +	if (setrlimit(RLIMIT_MEMLOCK, &limit) < 0) +		perror("Unable to lift memlock rlimit"); +  	if (bpf_prog_load(DEV_CGROUP_PROG, BPF_PROG_TYPE_CGROUP_DEVICE,  			  &obj, &prog_fd)) {  		printf("Failed to load DEV_CGROUP program\n"); -		goto err; +		goto out;  	}  	if (setup_cgroup_environment()) { @@ -89,5 +95,6 @@ int main(int argc, char **argv)  err:  	cleanup_cgroup_environment(); +out:  	return error;  } diff --git a/tools/testing/selftests/bpf/test_kmod.sh b/tools/testing/selftests/bpf/test_kmod.sh index ed4774d8d6ed..35669ccd4d23 100755 --- a/tools/testing/selftests/bpf/test_kmod.sh +++ b/tools/testing/selftests/bpf/test_kmod.sh @@ -10,9 +10,21 @@ test_run()  	echo "[ JIT enabled:$1 hardened:$2 ]"  	dmesg -C -	insmod $SRC_TREE/lib/test_bpf.ko 2> /dev/null -	if [ $? -ne 0 ]; then -		rc=1 +	if [ -f ${SRC_TREE}/lib/test_bpf.ko ]; then +		insmod ${SRC_TREE}/lib/test_bpf.ko 2> /dev/null +		if [ $? -ne 0 ]; then +			rc=1 +		fi +	else +		# Use modprobe dry run to check for missing test_bpf module +		if ! /sbin/modprobe -q -n test_bpf; then +			echo "test_bpf: [SKIP]" +		elif /sbin/modprobe -q test_bpf; then +			echo "test_bpf: ok" +		else +			echo "test_bpf: [FAIL]" +			rc=1 +		fi  	fi  	rmmod  test_bpf 2> /dev/null  	dmesg | grep FAIL diff --git a/tools/testing/selftests/bpf/test_l4lb_noinline.c b/tools/testing/selftests/bpf/test_l4lb_noinline.c new file mode 100644 index 000000000000..ba44a14e6dc4 --- /dev/null +++ b/tools/testing/selftests/bpf/test_l4lb_noinline.c @@ -0,0 +1,473 @@ +// SPDX-License-Identifier: GPL-2.0 +// Copyright (c) 2017 Facebook +#include <stddef.h> +#include <stdbool.h> +#include <string.h> +#include <linux/pkt_cls.h> +#include <linux/bpf.h> +#include <linux/in.h> +#include <linux/if_ether.h> +#include <linux/ip.h> +#include <linux/ipv6.h> +#include <linux/icmp.h> +#include <linux/icmpv6.h> +#include <linux/tcp.h> +#include <linux/udp.h> +#include "bpf_helpers.h" +#include "test_iptunnel_common.h" +#include "bpf_endian.h" + +int _version SEC("version") = 1; + +static __u32 rol32(__u32 word, unsigned int shift) +{ +	return (word << shift) | (word >> ((-shift) & 31)); +} + +/* copy paste of jhash from kernel sources to make sure llvm + * can compile it into valid sequence of bpf instructions + */ +#define __jhash_mix(a, b, c)			\ +{						\ +	a -= c;  a ^= rol32(c, 4);  c += b;	\ +	b -= a;  b ^= rol32(a, 6);  a += c;	\ +	c -= b;  c ^= rol32(b, 8);  b += a;	\ +	a -= c;  a ^= rol32(c, 16); c += b;	\ +	b -= a;  b ^= rol32(a, 19); a += c;	\ +	c -= b;  c ^= rol32(b, 4);  b += a;	\ +} + +#define __jhash_final(a, b, c)			\ +{						\ +	c ^= b; c -= rol32(b, 14);		\ +	a ^= c; a -= rol32(c, 11);		\ +	b ^= a; b -= rol32(a, 25);		\ +	c ^= b; c -= rol32(b, 16);		\ +	a ^= c; a -= rol32(c, 4);		\ +	b ^= a; b -= rol32(a, 14);		\ +	c ^= b; c -= rol32(b, 24);		\ +} + +#define JHASH_INITVAL		0xdeadbeef + +typedef unsigned int u32; + +static u32 jhash(const void *key, u32 length, u32 initval) +{ +	u32 a, b, c; +	const unsigned char *k = key; + +	a = b = c = JHASH_INITVAL + length + initval; + +	while (length > 12) { +		a += *(u32 *)(k); +		b += *(u32 *)(k + 4); +		c += *(u32 *)(k + 8); +		__jhash_mix(a, b, c); +		length -= 12; +		k += 12; +	} +	switch (length) { +	case 12: c += (u32)k[11]<<24; +	case 11: c += (u32)k[10]<<16; +	case 10: c += (u32)k[9]<<8; +	case 9:  c += k[8]; +	case 8:  b += (u32)k[7]<<24; +	case 7:  b += (u32)k[6]<<16; +	case 6:  b += (u32)k[5]<<8; +	case 5:  b += k[4]; +	case 4:  a += (u32)k[3]<<24; +	case 3:  a += (u32)k[2]<<16; +	case 2:  a += (u32)k[1]<<8; +	case 1:  a += k[0]; +		 __jhash_final(a, b, c); +	case 0: /* Nothing left to add */ +		break; +	} + +	return c; +} + +static u32 __jhash_nwords(u32 a, u32 b, u32 c, u32 initval) +{ +	a += initval; +	b += initval; +	c += initval; +	__jhash_final(a, b, c); +	return c; +} + +static u32 jhash_2words(u32 a, u32 b, u32 initval) +{ +	return __jhash_nwords(a, b, 0, initval + JHASH_INITVAL + (2 << 2)); +} + +#define PCKT_FRAGMENTED 65343 +#define IPV4_HDR_LEN_NO_OPT 20 +#define IPV4_PLUS_ICMP_HDR 28 +#define IPV6_PLUS_ICMP_HDR 48 +#define RING_SIZE 2 +#define MAX_VIPS 12 +#define MAX_REALS 5 +#define CTL_MAP_SIZE 16 +#define CH_RINGS_SIZE (MAX_VIPS * RING_SIZE) +#define F_IPV6 (1 << 0) +#define F_HASH_NO_SRC_PORT (1 << 0) +#define F_ICMP (1 << 0) +#define F_SYN_SET (1 << 1) + +struct packet_description { +	union { +		__be32 src; +		__be32 srcv6[4]; +	}; +	union { +		__be32 dst; +		__be32 dstv6[4]; +	}; +	union { +		__u32 ports; +		__u16 port16[2]; +	}; +	__u8 proto; +	__u8 flags; +}; + +struct ctl_value { +	union { +		__u64 value; +		__u32 ifindex; +		__u8 mac[6]; +	}; +}; + +struct vip_meta { +	__u32 flags; +	__u32 vip_num; +}; + +struct real_definition { +	union { +		__be32 dst; +		__be32 dstv6[4]; +	}; +	__u8 flags; +}; + +struct vip_stats { +	__u64 bytes; +	__u64 pkts; +}; + +struct eth_hdr { +	unsigned char eth_dest[ETH_ALEN]; +	unsigned char eth_source[ETH_ALEN]; +	unsigned short eth_proto; +}; + +struct bpf_map_def SEC("maps") vip_map = { +	.type = BPF_MAP_TYPE_HASH, +	.key_size = sizeof(struct vip), +	.value_size = sizeof(struct vip_meta), +	.max_entries = MAX_VIPS, +}; + +struct bpf_map_def SEC("maps") ch_rings = { +	.type = BPF_MAP_TYPE_ARRAY, +	.key_size = sizeof(__u32), +	.value_size = sizeof(__u32), +	.max_entries = CH_RINGS_SIZE, +}; + +struct bpf_map_def SEC("maps") reals = { +	.type = BPF_MAP_TYPE_ARRAY, +	.key_size = sizeof(__u32), +	.value_size = sizeof(struct real_definition), +	.max_entries = MAX_REALS, +}; + +struct bpf_map_def SEC("maps") stats = { +	.type = BPF_MAP_TYPE_PERCPU_ARRAY, +	.key_size = sizeof(__u32), +	.value_size = sizeof(struct vip_stats), +	.max_entries = MAX_VIPS, +}; + +struct bpf_map_def SEC("maps") ctl_array = { +	.type = BPF_MAP_TYPE_ARRAY, +	.key_size = sizeof(__u32), +	.value_size = sizeof(struct ctl_value), +	.max_entries = CTL_MAP_SIZE, +}; + +static __u32 get_packet_hash(struct packet_description *pckt, +			     bool ipv6) +{ +	if (ipv6) +		return jhash_2words(jhash(pckt->srcv6, 16, MAX_VIPS), +				    pckt->ports, CH_RINGS_SIZE); +	else +		return jhash_2words(pckt->src, pckt->ports, CH_RINGS_SIZE); +} + +static bool get_packet_dst(struct real_definition **real, +			   struct packet_description *pckt, +			   struct vip_meta *vip_info, +			   bool is_ipv6) +{ +	__u32 hash = get_packet_hash(pckt, is_ipv6); +	__u32 key = RING_SIZE * vip_info->vip_num + hash % RING_SIZE; +	__u32 *real_pos; + +	if (hash != 0x358459b7 /* jhash of ipv4 packet */  && +	    hash != 0x2f4bc6bb /* jhash of ipv6 packet */) +		return 0; + +	real_pos = bpf_map_lookup_elem(&ch_rings, &key); +	if (!real_pos) +		return false; +	key = *real_pos; +	*real = bpf_map_lookup_elem(&reals, &key); +	if (!(*real)) +		return false; +	return true; +} + +static int parse_icmpv6(void *data, void *data_end, __u64 off, +			struct packet_description *pckt) +{ +	struct icmp6hdr *icmp_hdr; +	struct ipv6hdr *ip6h; + +	icmp_hdr = data + off; +	if (icmp_hdr + 1 > data_end) +		return TC_ACT_SHOT; +	if (icmp_hdr->icmp6_type != ICMPV6_PKT_TOOBIG) +		return TC_ACT_OK; +	off += sizeof(struct icmp6hdr); +	ip6h = data + off; +	if (ip6h + 1 > data_end) +		return TC_ACT_SHOT; +	pckt->proto = ip6h->nexthdr; +	pckt->flags |= F_ICMP; +	memcpy(pckt->srcv6, ip6h->daddr.s6_addr32, 16); +	memcpy(pckt->dstv6, ip6h->saddr.s6_addr32, 16); +	return TC_ACT_UNSPEC; +} + +static int parse_icmp(void *data, void *data_end, __u64 off, +		      struct packet_description *pckt) +{ +	struct icmphdr *icmp_hdr; +	struct iphdr *iph; + +	icmp_hdr = data + off; +	if (icmp_hdr + 1 > data_end) +		return TC_ACT_SHOT; +	if (icmp_hdr->type != ICMP_DEST_UNREACH || +	    icmp_hdr->code != ICMP_FRAG_NEEDED) +		return TC_ACT_OK; +	off += sizeof(struct icmphdr); +	iph = data + off; +	if (iph + 1 > data_end) +		return TC_ACT_SHOT; +	if (iph->ihl != 5) +		return TC_ACT_SHOT; +	pckt->proto = iph->protocol; +	pckt->flags |= F_ICMP; +	pckt->src = iph->daddr; +	pckt->dst = iph->saddr; +	return TC_ACT_UNSPEC; +} + +static bool parse_udp(void *data, __u64 off, void *data_end, +		      struct packet_description *pckt) +{ +	struct udphdr *udp; +	udp = data + off; + +	if (udp + 1 > data_end) +		return false; + +	if (!(pckt->flags & F_ICMP)) { +		pckt->port16[0] = udp->source; +		pckt->port16[1] = udp->dest; +	} else { +		pckt->port16[0] = udp->dest; +		pckt->port16[1] = udp->source; +	} +	return true; +} + +static bool parse_tcp(void *data, __u64 off, void *data_end, +		      struct packet_description *pckt) +{ +	struct tcphdr *tcp; + +	tcp = data + off; +	if (tcp + 1 > data_end) +		return false; + +	if (tcp->syn) +		pckt->flags |= F_SYN_SET; + +	if (!(pckt->flags & F_ICMP)) { +		pckt->port16[0] = tcp->source; +		pckt->port16[1] = tcp->dest; +	} else { +		pckt->port16[0] = tcp->dest; +		pckt->port16[1] = tcp->source; +	} +	return true; +} + +static int process_packet(void *data, __u64 off, void *data_end, +			  bool is_ipv6, struct __sk_buff *skb) +{ +	void *pkt_start = (void *)(long)skb->data; +	struct packet_description pckt = {}; +	struct eth_hdr *eth = pkt_start; +	struct bpf_tunnel_key tkey = {}; +	struct vip_stats *data_stats; +	struct real_definition *dst; +	struct vip_meta *vip_info; +	struct ctl_value *cval; +	__u32 v4_intf_pos = 1; +	__u32 v6_intf_pos = 2; +	struct ipv6hdr *ip6h; +	struct vip vip = {}; +	struct iphdr *iph; +	int tun_flag = 0; +	__u16 pkt_bytes; +	__u64 iph_len; +	__u32 ifindex; +	__u8 protocol; +	__u32 vip_num; +	int action; + +	tkey.tunnel_ttl = 64; +	if (is_ipv6) { +		ip6h = data + off; +		if (ip6h + 1 > data_end) +			return TC_ACT_SHOT; + +		iph_len = sizeof(struct ipv6hdr); +		protocol = ip6h->nexthdr; +		pckt.proto = protocol; +		pkt_bytes = bpf_ntohs(ip6h->payload_len); +		off += iph_len; +		if (protocol == IPPROTO_FRAGMENT) { +			return TC_ACT_SHOT; +		} else if (protocol == IPPROTO_ICMPV6) { +			action = parse_icmpv6(data, data_end, off, &pckt); +			if (action >= 0) +				return action; +			off += IPV6_PLUS_ICMP_HDR; +		} else { +			memcpy(pckt.srcv6, ip6h->saddr.s6_addr32, 16); +			memcpy(pckt.dstv6, ip6h->daddr.s6_addr32, 16); +		} +	} else { +		iph = data + off; +		if (iph + 1 > data_end) +			return TC_ACT_SHOT; +		if (iph->ihl != 5) +			return TC_ACT_SHOT; + +		protocol = iph->protocol; +		pckt.proto = protocol; +		pkt_bytes = bpf_ntohs(iph->tot_len); +		off += IPV4_HDR_LEN_NO_OPT; + +		if (iph->frag_off & PCKT_FRAGMENTED) +			return TC_ACT_SHOT; +		if (protocol == IPPROTO_ICMP) { +			action = parse_icmp(data, data_end, off, &pckt); +			if (action >= 0) +				return action; +			off += IPV4_PLUS_ICMP_HDR; +		} else { +			pckt.src = iph->saddr; +			pckt.dst = iph->daddr; +		} +	} +	protocol = pckt.proto; + +	if (protocol == IPPROTO_TCP) { +		if (!parse_tcp(data, off, data_end, &pckt)) +			return TC_ACT_SHOT; +	} else if (protocol == IPPROTO_UDP) { +		if (!parse_udp(data, off, data_end, &pckt)) +			return TC_ACT_SHOT; +	} else { +		return TC_ACT_SHOT; +	} + +	if (is_ipv6) +		memcpy(vip.daddr.v6, pckt.dstv6, 16); +	else +		vip.daddr.v4 = pckt.dst; + +	vip.dport = pckt.port16[1]; +	vip.protocol = pckt.proto; +	vip_info = bpf_map_lookup_elem(&vip_map, &vip); +	if (!vip_info) { +		vip.dport = 0; +		vip_info = bpf_map_lookup_elem(&vip_map, &vip); +		if (!vip_info) +			return TC_ACT_SHOT; +		pckt.port16[1] = 0; +	} + +	if (vip_info->flags & F_HASH_NO_SRC_PORT) +		pckt.port16[0] = 0; + +	if (!get_packet_dst(&dst, &pckt, vip_info, is_ipv6)) +		return TC_ACT_SHOT; + +	if (dst->flags & F_IPV6) { +		cval = bpf_map_lookup_elem(&ctl_array, &v6_intf_pos); +		if (!cval) +			return TC_ACT_SHOT; +		ifindex = cval->ifindex; +		memcpy(tkey.remote_ipv6, dst->dstv6, 16); +		tun_flag = BPF_F_TUNINFO_IPV6; +	} else { +		cval = bpf_map_lookup_elem(&ctl_array, &v4_intf_pos); +		if (!cval) +			return TC_ACT_SHOT; +		ifindex = cval->ifindex; +		tkey.remote_ipv4 = dst->dst; +	} +	vip_num = vip_info->vip_num; +	data_stats = bpf_map_lookup_elem(&stats, &vip_num); +	if (!data_stats) +		return TC_ACT_SHOT; +	data_stats->pkts++; +	data_stats->bytes += pkt_bytes; +	bpf_skb_set_tunnel_key(skb, &tkey, sizeof(tkey), tun_flag); +	*(u32 *)eth->eth_dest = tkey.remote_ipv4; +	return bpf_redirect(ifindex, 0); +} + +SEC("l4lb-demo") +int balancer_ingress(struct __sk_buff *ctx) +{ +	void *data_end = (void *)(long)ctx->data_end; +	void *data = (void *)(long)ctx->data; +	struct eth_hdr *eth = data; +	__u32 eth_proto; +	__u32 nh_off; + +	nh_off = sizeof(struct eth_hdr); +	if (data + nh_off > data_end) +		return TC_ACT_SHOT; +	eth_proto = eth->eth_proto; +	if (eth_proto == bpf_htons(ETH_P_IP)) +		return process_packet(data, nh_off, data_end, false, ctx); +	else if (eth_proto == bpf_htons(ETH_P_IPV6)) +		return process_packet(data, nh_off, data_end, true, ctx); +	else +		return TC_ACT_SHOT; +} +char _license[] SEC("license") = "GPL"; diff --git a/tools/testing/selftests/bpf/test_libbpf.sh b/tools/testing/selftests/bpf/test_libbpf.sh new file mode 100755 index 000000000000..d97dc914cd49 --- /dev/null +++ b/tools/testing/selftests/bpf/test_libbpf.sh @@ -0,0 +1,49 @@ +#!/bin/sh +# SPDX-License-Identifier: GPL-2.0 + +export TESTNAME=test_libbpf + +# Determine selftest success via shell exit code +exit_handler() +{ +	if (( $? == 0 )); then +		echo "selftests: $TESTNAME [PASS]"; +	else +		echo "$TESTNAME: failed at file $LAST_LOADED" 1>&2 +		echo "selftests: $TESTNAME [FAILED]"; +	fi +} + +libbpf_open_file() +{ +	LAST_LOADED=$1 +	if [ -n "$VERBOSE" ]; then +	    ./test_libbpf_open $1 +	else +	    ./test_libbpf_open --quiet $1 +	fi +} + +# Exit script immediately (well catched by trap handler) if any +# program/thing exits with a non-zero status. +set -e + +# (Use 'trap -l' to list meaning of numbers) +trap exit_handler 0 2 3 6 9 + +libbpf_open_file test_l4lb.o + +# TODO: fix libbpf to load noinline functions +# [warning] libbpf: incorrect bpf_call opcode +#libbpf_open_file test_l4lb_noinline.o + +# TODO: fix test_xdp_meta.c to load with libbpf +# [warning] libbpf: test_xdp_meta.o doesn't provide kernel version +#libbpf_open_file test_xdp_meta.o + +# TODO: fix libbpf to handle .eh_frame +# [warning] libbpf: relocation failed: no section(10) +#libbpf_open_file ../../../../samples/bpf/tracex3_kern.o + +# Success +exit 0 diff --git a/tools/testing/selftests/bpf/test_libbpf_open.c b/tools/testing/selftests/bpf/test_libbpf_open.c new file mode 100644 index 000000000000..8fcd1c076add --- /dev/null +++ b/tools/testing/selftests/bpf/test_libbpf_open.c @@ -0,0 +1,150 @@ +/* SPDX-License-Identifier: GPL-2.0 + * Copyright (c) 2018 Jesper Dangaard Brouer, Red Hat Inc. + */ +static const char *__doc__ = +	"Libbpf test program for loading BPF ELF object files"; + +#include <stdlib.h> +#include <stdio.h> +#include <string.h> +#include <stdarg.h> +#include <bpf/libbpf.h> +#include <getopt.h> + +static const struct option long_options[] = { +	{"help",	no_argument,		NULL, 'h' }, +	{"debug",	no_argument,		NULL, 'D' }, +	{"quiet",	no_argument,		NULL, 'q' }, +	{0, 0, NULL,  0 } +}; + +static void usage(char *argv[]) +{ +	int i; + +	printf("\nDOCUMENTATION:\n%s\n\n", __doc__); +	printf(" Usage: %s (options-see-below) BPF_FILE\n", argv[0]); +	printf(" Listing options:\n"); +	for (i = 0; long_options[i].name != 0; i++) { +		printf(" --%-12s", long_options[i].name); +		printf(" short-option: -%c", +		       long_options[i].val); +		printf("\n"); +	} +	printf("\n"); +} + +#define DEFINE_PRINT_FN(name, enabled) \ +static int libbpf_##name(const char *fmt, ...)  	\ +{							\ +        va_list args;					\ +        int ret;					\ +							\ +        va_start(args, fmt);				\ +	if (enabled) {					\ +		fprintf(stderr, "[" #name "] ");	\ +		ret = vfprintf(stderr, fmt, args);	\ +	}						\ +        va_end(args);					\ +        return ret;					\ +} +DEFINE_PRINT_FN(warning, 1) +DEFINE_PRINT_FN(info, 1) +DEFINE_PRINT_FN(debug, 1) + +#define EXIT_FAIL_LIBBPF EXIT_FAILURE +#define EXIT_FAIL_OPTION 2 + +int test_walk_progs(struct bpf_object *obj, bool verbose) +{ +	struct bpf_program *prog; +	int cnt = 0; + +	bpf_object__for_each_program(prog, obj) { +		cnt++; +		if (verbose) +			printf("Prog (count:%d) section_name: %s\n", cnt, +			       bpf_program__title(prog, false)); +	} +	return 0; +} + +int test_walk_maps(struct bpf_object *obj, bool verbose) +{ +	struct bpf_map *map; +	int cnt = 0; + +	bpf_map__for_each(map, obj) { +		cnt++; +		if (verbose) +			printf("Map (count:%d) name: %s\n", cnt, +			       bpf_map__name(map)); +	} +	return 0; +} + +int test_open_file(char *filename, bool verbose) +{ +	struct bpf_object *bpfobj = NULL; +	long err; + +	if (verbose) +		printf("Open BPF ELF-file with libbpf: %s\n", filename); + +	/* Load BPF ELF object file and check for errors */ +	bpfobj = bpf_object__open(filename); +	err = libbpf_get_error(bpfobj); +	if (err) { +		char err_buf[128]; +		libbpf_strerror(err, err_buf, sizeof(err_buf)); +		if (verbose) +			printf("Unable to load eBPF objects in file '%s': %s\n", +			       filename, err_buf); +		return EXIT_FAIL_LIBBPF; +	} +	test_walk_progs(bpfobj, verbose); +	test_walk_maps(bpfobj, verbose); + +	if (verbose) +		printf("Close BPF ELF-file with libbpf: %s\n", +		       bpf_object__name(bpfobj)); +	bpf_object__close(bpfobj); + +	return 0; +} + +int main(int argc, char **argv) +{ +	char filename[1024] = { 0 }; +	bool verbose = 1; +	int longindex = 0; +	int opt; + +	libbpf_set_print(libbpf_warning, libbpf_info, NULL); + +	/* Parse commands line args */ +	while ((opt = getopt_long(argc, argv, "hDq", +				  long_options, &longindex)) != -1) { +		switch (opt) { +		case 'D': +			libbpf_set_print(libbpf_warning, libbpf_info, +					 libbpf_debug); +			break; +		case 'q': /* Use in scripting mode */ +			verbose = 0; +			break; +		case 'h': +		default: +			usage(argv); +			return EXIT_FAIL_OPTION; +		} +	} +	if (optind >= argc) { +		usage(argv); +		printf("ERROR: Expected BPF_FILE argument after options\n"); +		return EXIT_FAIL_OPTION; +	} +	snprintf(filename, sizeof(filename), "%s", argv[optind]); + +	return test_open_file(filename, verbose); +} diff --git a/tools/testing/selftests/bpf/test_lpm_map.c b/tools/testing/selftests/bpf/test_lpm_map.c index f61480641b6e..2be87e9ee28d 100644 --- a/tools/testing/selftests/bpf/test_lpm_map.c +++ b/tools/testing/selftests/bpf/test_lpm_map.c @@ -14,6 +14,7 @@  #include <errno.h>  #include <inttypes.h>  #include <linux/bpf.h> +#include <pthread.h>  #include <stdio.h>  #include <stdlib.h>  #include <string.h> @@ -521,6 +522,218 @@ static void test_lpm_delete(void)  	close(map_fd);  } +static void test_lpm_get_next_key(void) +{ +	struct bpf_lpm_trie_key *key_p, *next_key_p; +	size_t key_size; +	__u32 value = 0; +	int map_fd; + +	key_size = sizeof(*key_p) + sizeof(__u32); +	key_p = alloca(key_size); +	next_key_p = alloca(key_size); + +	map_fd = bpf_create_map(BPF_MAP_TYPE_LPM_TRIE, key_size, sizeof(value), +				100, BPF_F_NO_PREALLOC); +	assert(map_fd >= 0); + +	/* empty tree. get_next_key should return ENOENT */ +	assert(bpf_map_get_next_key(map_fd, NULL, key_p) == -1 && +	       errno == ENOENT); + +	/* get and verify the first key, get the second one should fail. */ +	key_p->prefixlen = 16; +	inet_pton(AF_INET, "192.168.0.0", key_p->data); +	assert(bpf_map_update_elem(map_fd, key_p, &value, 0) == 0); + +	memset(key_p, 0, key_size); +	assert(bpf_map_get_next_key(map_fd, NULL, key_p) == 0); +	assert(key_p->prefixlen == 16 && key_p->data[0] == 192 && +	       key_p->data[1] == 168); + +	assert(bpf_map_get_next_key(map_fd, key_p, next_key_p) == -1 && +	       errno == ENOENT); + +	/* no exact matching key should get the first one in post order. */ +	key_p->prefixlen = 8; +	assert(bpf_map_get_next_key(map_fd, NULL, key_p) == 0); +	assert(key_p->prefixlen == 16 && key_p->data[0] == 192 && +	       key_p->data[1] == 168); + +	/* add one more element (total two) */ +	key_p->prefixlen = 24; +	inet_pton(AF_INET, "192.168.0.0", key_p->data); +	assert(bpf_map_update_elem(map_fd, key_p, &value, 0) == 0); + +	memset(key_p, 0, key_size); +	assert(bpf_map_get_next_key(map_fd, NULL, key_p) == 0); +	assert(key_p->prefixlen == 24 && key_p->data[0] == 192 && +	       key_p->data[1] == 168 && key_p->data[2] == 0); + +	memset(next_key_p, 0, key_size); +	assert(bpf_map_get_next_key(map_fd, key_p, next_key_p) == 0); +	assert(next_key_p->prefixlen == 16 && next_key_p->data[0] == 192 && +	       next_key_p->data[1] == 168); + +	memcpy(key_p, next_key_p, key_size); +	assert(bpf_map_get_next_key(map_fd, key_p, next_key_p) == -1 && +	       errno == ENOENT); + +	/* Add one more element (total three) */ +	key_p->prefixlen = 24; +	inet_pton(AF_INET, "192.168.128.0", key_p->data); +	assert(bpf_map_update_elem(map_fd, key_p, &value, 0) == 0); + +	memset(key_p, 0, key_size); +	assert(bpf_map_get_next_key(map_fd, NULL, key_p) == 0); +	assert(key_p->prefixlen == 24 && key_p->data[0] == 192 && +	       key_p->data[1] == 168 && key_p->data[2] == 0); + +	memset(next_key_p, 0, key_size); +	assert(bpf_map_get_next_key(map_fd, key_p, next_key_p) == 0); +	assert(next_key_p->prefixlen == 24 && next_key_p->data[0] == 192 && +	       next_key_p->data[1] == 168 && next_key_p->data[2] == 128); + +	memcpy(key_p, next_key_p, key_size); +	assert(bpf_map_get_next_key(map_fd, key_p, next_key_p) == 0); +	assert(next_key_p->prefixlen == 16 && next_key_p->data[0] == 192 && +	       next_key_p->data[1] == 168); + +	memcpy(key_p, next_key_p, key_size); +	assert(bpf_map_get_next_key(map_fd, key_p, next_key_p) == -1 && +	       errno == ENOENT); + +	/* Add one more element (total four) */ +	key_p->prefixlen = 24; +	inet_pton(AF_INET, "192.168.1.0", key_p->data); +	assert(bpf_map_update_elem(map_fd, key_p, &value, 0) == 0); + +	memset(key_p, 0, key_size); +	assert(bpf_map_get_next_key(map_fd, NULL, key_p) == 0); +	assert(key_p->prefixlen == 24 && key_p->data[0] == 192 && +	       key_p->data[1] == 168 && key_p->data[2] == 0); + +	memset(next_key_p, 0, key_size); +	assert(bpf_map_get_next_key(map_fd, key_p, next_key_p) == 0); +	assert(next_key_p->prefixlen == 24 && next_key_p->data[0] == 192 && +	       next_key_p->data[1] == 168 && next_key_p->data[2] == 1); + +	memcpy(key_p, next_key_p, key_size); +	assert(bpf_map_get_next_key(map_fd, key_p, next_key_p) == 0); +	assert(next_key_p->prefixlen == 24 && next_key_p->data[0] == 192 && +	       next_key_p->data[1] == 168 && next_key_p->data[2] == 128); + +	memcpy(key_p, next_key_p, key_size); +	assert(bpf_map_get_next_key(map_fd, key_p, next_key_p) == 0); +	assert(next_key_p->prefixlen == 16 && next_key_p->data[0] == 192 && +	       next_key_p->data[1] == 168); + +	memcpy(key_p, next_key_p, key_size); +	assert(bpf_map_get_next_key(map_fd, key_p, next_key_p) == -1 && +	       errno == ENOENT); + +	/* no exact matching key should return the first one in post order */ +	key_p->prefixlen = 22; +	inet_pton(AF_INET, "192.168.1.0", key_p->data); +	assert(bpf_map_get_next_key(map_fd, key_p, next_key_p) == 0); +	assert(next_key_p->prefixlen == 24 && next_key_p->data[0] == 192 && +	       next_key_p->data[1] == 168 && next_key_p->data[2] == 0); + +	close(map_fd); +} + +#define MAX_TEST_KEYS	4 +struct lpm_mt_test_info { +	int cmd; /* 0: update, 1: delete, 2: lookup, 3: get_next_key */ +	int iter; +	int map_fd; +	struct { +		__u32 prefixlen; +		__u32 data; +	} key[MAX_TEST_KEYS]; +}; + +static void *lpm_test_command(void *arg) +{ +	int i, j, ret, iter, key_size; +	struct lpm_mt_test_info *info = arg; +	struct bpf_lpm_trie_key *key_p; + +	key_size = sizeof(struct bpf_lpm_trie_key) + sizeof(__u32); +	key_p = alloca(key_size); +	for (iter = 0; iter < info->iter; iter++) +		for (i = 0; i < MAX_TEST_KEYS; i++) { +			/* first half of iterations in forward order, +			 * and second half in backward order. +			 */ +			j = (iter < (info->iter / 2)) ? i : MAX_TEST_KEYS - i - 1; +			key_p->prefixlen = info->key[j].prefixlen; +			memcpy(key_p->data, &info->key[j].data, sizeof(__u32)); +			if (info->cmd == 0) { +				__u32 value = j; +				/* update must succeed */ +				assert(bpf_map_update_elem(info->map_fd, key_p, &value, 0) == 0); +			} else if (info->cmd == 1) { +				ret = bpf_map_delete_elem(info->map_fd, key_p); +				assert(ret == 0 || errno == ENOENT); +			} else if (info->cmd == 2) { +				__u32 value; +				ret = bpf_map_lookup_elem(info->map_fd, key_p, &value); +				assert(ret == 0 || errno == ENOENT); +			} else { +				struct bpf_lpm_trie_key *next_key_p = alloca(key_size); +				ret = bpf_map_get_next_key(info->map_fd, key_p, next_key_p); +				assert(ret == 0 || errno == ENOENT || errno == ENOMEM); +			} +		} + +	// Pass successful exit info back to the main thread +	pthread_exit((void *)info); +} + +static void setup_lpm_mt_test_info(struct lpm_mt_test_info *info, int map_fd) +{ +	info->iter = 2000; +	info->map_fd = map_fd; +	info->key[0].prefixlen = 16; +	inet_pton(AF_INET, "192.168.0.0", &info->key[0].data); +	info->key[1].prefixlen = 24; +	inet_pton(AF_INET, "192.168.0.0", &info->key[1].data); +	info->key[2].prefixlen = 24; +	inet_pton(AF_INET, "192.168.128.0", &info->key[2].data); +	info->key[3].prefixlen = 24; +	inet_pton(AF_INET, "192.168.1.0", &info->key[3].data); +} + +static void test_lpm_multi_thread(void) +{ +	struct lpm_mt_test_info info[4]; +	size_t key_size, value_size; +	pthread_t thread_id[4]; +	int i, map_fd; +	void *ret; + +	/* create a trie */ +	value_size = sizeof(__u32); +	key_size = sizeof(struct bpf_lpm_trie_key) + value_size; +	map_fd = bpf_create_map(BPF_MAP_TYPE_LPM_TRIE, key_size, value_size, +				100, BPF_F_NO_PREALLOC); + +	/* create 4 threads to test update, delete, lookup and get_next_key */ +	setup_lpm_mt_test_info(&info[0], map_fd); +	for (i = 0; i < 4; i++) { +		if (i != 0) +			memcpy(&info[i], &info[0], sizeof(info[i])); +		info[i].cmd = i; +		assert(pthread_create(&thread_id[i], NULL, &lpm_test_command, &info[i]) == 0); +	} + +	for (i = 0; i < 4; i++) +		assert(pthread_join(thread_id[i], &ret) == 0 && ret == (void *)&info[i]); + +	close(map_fd); +} +  int main(void)  {  	struct rlimit limit  = { RLIM_INFINITY, RLIM_INFINITY }; @@ -545,6 +758,10 @@ int main(void)  	test_lpm_delete(); +	test_lpm_get_next_key(); + +	test_lpm_multi_thread(); +  	printf("test_lpm: OK\n");  	return 0;  } diff --git a/tools/testing/selftests/bpf/test_maps.c b/tools/testing/selftests/bpf/test_maps.c index 040356ecc862..9e03a4c356a4 100644 --- a/tools/testing/selftests/bpf/test_maps.c +++ b/tools/testing/selftests/bpf/test_maps.c @@ -126,6 +126,8 @@ static void test_hashmap_sizes(int task, void *data)  			fd = bpf_create_map(BPF_MAP_TYPE_HASH, i, j,  					    2, map_flags);  			if (fd < 0) { +				if (errno == ENOMEM) +					return;  				printf("Failed to create hashmap key=%d value=%d '%s'\n",  				       i, j, strerror(errno));  				exit(1); @@ -242,7 +244,7 @@ static void test_hashmap_percpu(int task, void *data)  static void test_hashmap_walk(int task, void *data)  { -	int fd, i, max_entries = 100000; +	int fd, i, max_entries = 1000;  	long long key, value, next_key;  	bool next_key_valid = true; @@ -463,7 +465,7 @@ static void test_devmap(int task, void *data)  #define SOCKMAP_VERDICT_PROG "./sockmap_verdict_prog.o"  static void test_sockmap(int tasks, void *data)  { -	int one = 1, map_fd_rx, map_fd_tx, map_fd_break, s, sc, rc; +	int one = 1, map_fd_rx = 0, map_fd_tx = 0, map_fd_break, s, sc, rc;  	struct bpf_map *bpf_map_rx, *bpf_map_tx, *bpf_map_break;  	int ports[] = {50200, 50201, 50202, 50204};  	int err, i, fd, udp, sfd[6] = {0xdeadbeef}; @@ -868,9 +870,12 @@ static void test_sockmap(int tasks, void *data)  		goto out_sockmap;  	} -	/* Test map close sockets */ -	for (i = 0; i < 6; i++) +	/* Test map close sockets and empty maps */ +	for (i = 0; i < 6; i++) { +		bpf_map_delete_elem(map_fd_tx, &i); +		bpf_map_delete_elem(map_fd_rx, &i);  		close(sfd[i]); +	}  	close(fd);  	close(map_fd_rx);  	bpf_object__close(obj); @@ -881,8 +886,13 @@ out:  	printf("Failed to create sockmap '%i:%s'!\n", i, strerror(errno));  	exit(1);  out_sockmap: -	for (i = 0; i < 6; i++) +	for (i = 0; i < 6; i++) { +		if (map_fd_tx) +			bpf_map_delete_elem(map_fd_tx, &i); +		if (map_fd_rx) +			bpf_map_delete_elem(map_fd_rx, &i);  		close(sfd[i]); +	}  	close(fd);  	exit(1);  } @@ -931,8 +941,12 @@ static void test_map_large(void)  	close(fd);  } -static void run_parallel(int tasks, void (*fn)(int task, void *data), -			 void *data) +#define run_parallel(N, FN, DATA) \ +	printf("Fork %d tasks to '" #FN "'\n", N); \ +	__run_parallel(N, FN, DATA) + +static void __run_parallel(int tasks, void (*fn)(int task, void *data), +			   void *data)  {  	pid_t pid[tasks];  	int i; @@ -972,7 +986,7 @@ static void test_map_stress(void)  #define DO_UPDATE 1  #define DO_DELETE 0 -static void do_work(int fn, void *data) +static void test_update_delete(int fn, void *data)  {  	int do_update = ((int *)data)[1];  	int fd = ((int *)data)[0]; @@ -1012,7 +1026,7 @@ static void test_map_parallel(void)  	 */  	data[0] = fd;  	data[1] = DO_UPDATE; -	run_parallel(TASKS, do_work, data); +	run_parallel(TASKS, test_update_delete, data);  	/* Check that key=0 is already there. */  	assert(bpf_map_update_elem(fd, &key, &value, BPF_NOEXIST) == -1 && @@ -1035,7 +1049,7 @@ static void test_map_parallel(void)  	/* Now let's delete all elemenets in parallel. */  	data[1] = DO_DELETE; -	run_parallel(TASKS, do_work, data); +	run_parallel(TASKS, test_update_delete, data);  	/* Nothing should be left. */  	key = -1; diff --git a/tools/testing/selftests/bpf/test_offload.py b/tools/testing/selftests/bpf/test_offload.py new file mode 100755 index 000000000000..e78aad0a68bb --- /dev/null +++ b/tools/testing/selftests/bpf/test_offload.py @@ -0,0 +1,1085 @@ +#!/usr/bin/python3 + +# Copyright (C) 2017 Netronome Systems, Inc. +# +# This software is licensed under the GNU General License Version 2, +# June 1991 as shown in the file COPYING in the top-level directory of this +# source tree. +# +# THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" +# WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, +# BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +# FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE +# OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME +# THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + +from datetime import datetime +import argparse +import json +import os +import pprint +import random +import string +import struct +import subprocess +import time + +logfile = None +log_level = 1 +skip_extack = False +bpf_test_dir = os.path.dirname(os.path.realpath(__file__)) +pp = pprint.PrettyPrinter() +devs = [] # devices we created for clean up +files = [] # files to be removed +netns = [] # net namespaces to be removed + +def log_get_sec(level=0): +    return "*" * (log_level + level) + +def log_level_inc(add=1): +    global log_level +    log_level += add + +def log_level_dec(sub=1): +    global log_level +    log_level -= sub + +def log_level_set(level): +    global log_level +    log_level = level + +def log(header, data, level=None): +    """ +    Output to an optional log. +    """ +    if logfile is None: +        return +    if level is not None: +        log_level_set(level) + +    if not isinstance(data, str): +        data = pp.pformat(data) + +    if len(header): +        logfile.write("\n" + log_get_sec() + " ") +        logfile.write(header) +    if len(header) and len(data.strip()): +        logfile.write("\n") +    logfile.write(data) + +def skip(cond, msg): +    if not cond: +        return +    print("SKIP: " + msg) +    log("SKIP: " + msg, "", level=1) +    os.sys.exit(0) + +def fail(cond, msg): +    if not cond: +        return +    print("FAIL: " + msg) +    log("FAIL: " + msg, "", level=1) +    os.sys.exit(1) + +def start_test(msg): +    log(msg, "", level=1) +    log_level_inc() +    print(msg) + +def cmd(cmd, shell=True, include_stderr=False, background=False, fail=True): +    """ +    Run a command in subprocess and return tuple of (retval, stdout); +    optionally return stderr as well as third value. +    """ +    proc = subprocess.Popen(cmd, shell=shell, stdout=subprocess.PIPE, +                            stderr=subprocess.PIPE) +    if background: +        msg = "%s START: %s" % (log_get_sec(1), +                                datetime.now().strftime("%H:%M:%S.%f")) +        log("BKG " + proc.args, msg) +        return proc + +    return cmd_result(proc, include_stderr=include_stderr, fail=fail) + +def cmd_result(proc, include_stderr=False, fail=False): +    stdout, stderr = proc.communicate() +    stdout = stdout.decode("utf-8") +    stderr = stderr.decode("utf-8") +    proc.stdout.close() +    proc.stderr.close() + +    stderr = "\n" + stderr +    if stderr[-1] == "\n": +        stderr = stderr[:-1] + +    sec = log_get_sec(1) +    log("CMD " + proc.args, +        "RETCODE: %d\n%s STDOUT:\n%s%s STDERR:%s\n%s END: %s" % +        (proc.returncode, sec, stdout, sec, stderr, +         sec, datetime.now().strftime("%H:%M:%S.%f"))) + +    if proc.returncode != 0 and fail: +        if len(stderr) > 0 and stderr[-1] == "\n": +            stderr = stderr[:-1] +        raise Exception("Command failed: %s\n%s" % (proc.args, stderr)) + +    if include_stderr: +        return proc.returncode, stdout, stderr +    else: +        return proc.returncode, stdout + +def rm(f): +    cmd("rm -f %s" % (f)) +    if f in files: +        files.remove(f) + +def tool(name, args, flags, JSON=True, ns="", fail=True, include_stderr=False): +    params = "" +    if JSON: +        params += "%s " % (flags["json"]) + +    if ns != "": +        ns = "ip netns exec %s " % (ns) + +    if include_stderr: +        ret, stdout, stderr = cmd(ns + name + " " + params + args, +                                  fail=fail, include_stderr=True) +    else: +        ret, stdout = cmd(ns + name + " " + params + args, +                          fail=fail, include_stderr=False) + +    if JSON and len(stdout.strip()) != 0: +        out = json.loads(stdout) +    else: +        out = stdout + +    if include_stderr: +        return ret, out, stderr +    else: +        return ret, out + +def bpftool(args, JSON=True, ns="", fail=True): +    return tool("bpftool", args, {"json":"-p"}, JSON=JSON, ns=ns, fail=fail) + +def bpftool_prog_list(expected=None, ns=""): +    _, progs = bpftool("prog show", JSON=True, ns=ns, fail=True) +    if expected is not None: +        if len(progs) != expected: +            fail(True, "%d BPF programs loaded, expected %d" % +                 (len(progs), expected)) +    return progs + +def bpftool_map_list(expected=None, ns=""): +    _, maps = bpftool("map show", JSON=True, ns=ns, fail=True) +    if expected is not None: +        if len(maps) != expected: +            fail(True, "%d BPF maps loaded, expected %d" % +                 (len(maps), expected)) +    return maps + +def bpftool_prog_list_wait(expected=0, n_retry=20): +    for i in range(n_retry): +        nprogs = len(bpftool_prog_list()) +        if nprogs == expected: +            return +        time.sleep(0.05) +    raise Exception("Time out waiting for program counts to stabilize want %d, have %d" % (expected, nprogs)) + +def bpftool_map_list_wait(expected=0, n_retry=20): +    for i in range(n_retry): +        nmaps = len(bpftool_map_list()) +        if nmaps == expected: +            return +        time.sleep(0.05) +    raise Exception("Time out waiting for map counts to stabilize want %d, have %d" % (expected, nmaps)) + +def ip(args, force=False, JSON=True, ns="", fail=True, include_stderr=False): +    if force: +        args = "-force " + args +    return tool("ip", args, {"json":"-j"}, JSON=JSON, ns=ns, +                fail=fail, include_stderr=include_stderr) + +def tc(args, JSON=True, ns="", fail=True, include_stderr=False): +    return tool("tc", args, {"json":"-p"}, JSON=JSON, ns=ns, +                fail=fail, include_stderr=include_stderr) + +def ethtool(dev, opt, args, fail=True): +    return cmd("ethtool %s %s %s" % (opt, dev["ifname"], args), fail=fail) + +def bpf_obj(name, sec=".text", path=bpf_test_dir,): +    return "obj %s sec %s" % (os.path.join(path, name), sec) + +def bpf_pinned(name): +    return "pinned %s" % (name) + +def bpf_bytecode(bytecode): +    return "bytecode \"%s\"" % (bytecode) + +def mknetns(n_retry=10): +    for i in range(n_retry): +        name = ''.join([random.choice(string.ascii_letters) for i in range(8)]) +        ret, _ = ip("netns add %s" % (name), fail=False) +        if ret == 0: +            netns.append(name) +            return name +    return None + +def int2str(fmt, val): +    ret = [] +    for b in struct.pack(fmt, val): +        ret.append(int(b)) +    return " ".join(map(lambda x: str(x), ret)) + +def str2int(strtab): +    inttab = [] +    for i in strtab: +        inttab.append(int(i, 16)) +    ba = bytearray(inttab) +    if len(strtab) == 4: +        fmt = "I" +    elif len(strtab) == 8: +        fmt = "Q" +    else: +        raise Exception("String array of len %d can't be unpacked to an int" % +                        (len(strtab))) +    return struct.unpack(fmt, ba)[0] + +class DebugfsDir: +    """ +    Class for accessing DebugFS directories as a dictionary. +    """ + +    def __init__(self, path): +        self.path = path +        self._dict = self._debugfs_dir_read(path) + +    def __len__(self): +        return len(self._dict.keys()) + +    def __getitem__(self, key): +        if type(key) is int: +            key = list(self._dict.keys())[key] +        return self._dict[key] + +    def __setitem__(self, key, value): +        log("DebugFS set %s = %s" % (key, value), "") +        log_level_inc() + +        cmd("echo '%s' > %s/%s" % (value, self.path, key)) +        log_level_dec() + +        _, out = cmd('cat %s/%s' % (self.path, key)) +        self._dict[key] = out.strip() + +    def _debugfs_dir_read(self, path): +        dfs = {} + +        log("DebugFS state for %s" % (path), "") +        log_level_inc(add=2) + +        _, out = cmd('ls ' + path) +        for f in out.split(): +            p = os.path.join(path, f) +            if os.path.isfile(p): +                _, out = cmd('cat %s/%s' % (path, f)) +                dfs[f] = out.strip() +            elif os.path.isdir(p): +                dfs[f] = DebugfsDir(p) +            else: +                raise Exception("%s is neither file nor directory" % (p)) + +        log_level_dec() +        log("DebugFS state", dfs) +        log_level_dec() + +        return dfs + +class NetdevSim: +    """ +    Class for netdevsim netdevice and its attributes. +    """ + +    def __init__(self): +        self.dev = self._netdevsim_create() +        devs.append(self) + +        self.ns = "" + +        self.dfs_dir = '/sys/kernel/debug/netdevsim/%s' % (self.dev['ifname']) +        self.dfs_refresh() + +    def __getitem__(self, key): +        return self.dev[key] + +    def _netdevsim_create(self): +        _, old  = ip("link show") +        ip("link add sim%d type netdevsim") +        _, new  = ip("link show") + +        for dev in new: +            f = filter(lambda x: x["ifname"] == dev["ifname"], old) +            if len(list(f)) == 0: +                return dev + +        raise Exception("failed to create netdevsim device") + +    def remove(self): +        devs.remove(self) +        ip("link del dev %s" % (self.dev["ifname"]), ns=self.ns) + +    def dfs_refresh(self): +        self.dfs = DebugfsDir(self.dfs_dir) +        return self.dfs + +    def dfs_num_bound_progs(self): +        path = os.path.join(self.dfs_dir, "bpf_bound_progs") +        _, progs = cmd('ls %s' % (path)) +        return len(progs.split()) + +    def dfs_get_bound_progs(self, expected): +        progs = DebugfsDir(os.path.join(self.dfs_dir, "bpf_bound_progs")) +        if expected is not None: +            if len(progs) != expected: +                fail(True, "%d BPF programs bound, expected %d" % +                     (len(progs), expected)) +        return progs + +    def wait_for_flush(self, bound=0, total=0, n_retry=20): +        for i in range(n_retry): +            nbound = self.dfs_num_bound_progs() +            nprogs = len(bpftool_prog_list()) +            if nbound == bound and nprogs == total: +                return +            time.sleep(0.05) +        raise Exception("Time out waiting for program counts to stabilize want %d/%d, have %d bound, %d loaded" % (bound, total, nbound, nprogs)) + +    def set_ns(self, ns): +        name = "1" if ns == "" else ns +        ip("link set dev %s netns %s" % (self.dev["ifname"], name), ns=self.ns) +        self.ns = ns + +    def set_mtu(self, mtu, fail=True): +        return ip("link set dev %s mtu %d" % (self.dev["ifname"], mtu), +                  fail=fail) + +    def set_xdp(self, bpf, mode, force=False, JSON=True, verbose=False, +                fail=True, include_stderr=False): +        if verbose: +            bpf += " verbose" +        return ip("link set dev %s xdp%s %s" % (self.dev["ifname"], mode, bpf), +                  force=force, JSON=JSON, +                  fail=fail, include_stderr=include_stderr) + +    def unset_xdp(self, mode, force=False, JSON=True, +                  fail=True, include_stderr=False): +        return ip("link set dev %s xdp%s off" % (self.dev["ifname"], mode), +                  force=force, JSON=JSON, +                  fail=fail, include_stderr=include_stderr) + +    def ip_link_show(self, xdp): +        _, link = ip("link show dev %s" % (self['ifname'])) +        if len(link) > 1: +            raise Exception("Multiple objects on ip link show") +        if len(link) < 1: +            return {} +        fail(xdp != "xdp" in link, +             "XDP program not reporting in iplink (reported %s, expected %s)" % +             ("xdp" in link, xdp)) +        return link[0] + +    def tc_add_ingress(self): +        tc("qdisc add dev %s ingress" % (self['ifname'])) + +    def tc_del_ingress(self): +        tc("qdisc del dev %s ingress" % (self['ifname'])) + +    def tc_flush_filters(self, bound=0, total=0): +        self.tc_del_ingress() +        self.tc_add_ingress() +        self.wait_for_flush(bound=bound, total=total) + +    def tc_show_ingress(self, expected=None): +        # No JSON support, oh well... +        flags = ["skip_sw", "skip_hw", "in_hw"] +        named = ["protocol", "pref", "chain", "handle", "id", "tag"] + +        args = "-s filter show dev %s ingress" % (self['ifname']) +        _, out = tc(args, JSON=False) + +        filters = [] +        lines = out.split('\n') +        for line in lines: +            words = line.split() +            if "handle" not in words: +                continue +            fltr = {} +            for flag in flags: +                fltr[flag] = flag in words +            for name in named: +                try: +                    idx = words.index(name) +                    fltr[name] = words[idx + 1] +                except ValueError: +                    pass +            filters.append(fltr) + +        if expected is not None: +            fail(len(filters) != expected, +                 "%d ingress filters loaded, expected %d" % +                 (len(filters), expected)) +        return filters + +    def cls_filter_op(self, op, qdisc="ingress", prio=None, handle=None, +                      chain=None, cls="", params="", +                      fail=True, include_stderr=False): +        spec = "" +        if prio is not None: +            spec += " prio %d" % (prio) +        if handle: +            spec += " handle %s" % (handle) +        if chain is not None: +            spec += " chain %d" % (chain) + +        return tc("filter {op} dev {dev} {qdisc} {spec} {cls} {params}"\ +                  .format(op=op, dev=self['ifname'], qdisc=qdisc, spec=spec, +                          cls=cls, params=params), +                  fail=fail, include_stderr=include_stderr) + +    def cls_bpf_add_filter(self, bpf, op="add", prio=None, handle=None, +                           chain=None, da=False, verbose=False, +                           skip_sw=False, skip_hw=False, +                           fail=True, include_stderr=False): +        cls = "bpf " + bpf + +        params = "" +        if da: +            params += " da" +        if verbose: +            params += " verbose" +        if skip_sw: +            params += " skip_sw" +        if skip_hw: +            params += " skip_hw" + +        return self.cls_filter_op(op=op, prio=prio, handle=handle, cls=cls, +                                  chain=chain, params=params, +                                  fail=fail, include_stderr=include_stderr) + +    def set_ethtool_tc_offloads(self, enable, fail=True): +        args = "hw-tc-offload %s" % ("on" if enable else "off") +        return ethtool(self, "-K", args, fail=fail) + +################################################################################ +def clean_up(): +    global files, netns, devs + +    for dev in devs: +        dev.remove() +    for f in files: +        cmd("rm -f %s" % (f)) +    for ns in netns: +        cmd("ip netns delete %s" % (ns)) +    files = [] +    netns = [] + +def pin_prog(file_name, idx=0): +    progs = bpftool_prog_list(expected=(idx + 1)) +    prog = progs[idx] +    bpftool("prog pin id %d %s" % (prog["id"], file_name)) +    files.append(file_name) + +    return file_name, bpf_pinned(file_name) + +def pin_map(file_name, idx=0, expected=1): +    maps = bpftool_map_list(expected=expected) +    m = maps[idx] +    bpftool("map pin id %d %s" % (m["id"], file_name)) +    files.append(file_name) + +    return file_name, bpf_pinned(file_name) + +def check_dev_info_removed(prog_file=None, map_file=None): +    bpftool_prog_list(expected=0) +    ret, err = bpftool("prog show pin %s" % (prog_file), fail=False) +    fail(ret == 0, "Showing prog with removed device did not fail") +    fail(err["error"].find("No such device") == -1, +         "Showing prog with removed device expected ENODEV, error is %s" % +         (err["error"])) + +    bpftool_map_list(expected=0) +    ret, err = bpftool("map show pin %s" % (map_file), fail=False) +    fail(ret == 0, "Showing map with removed device did not fail") +    fail(err["error"].find("No such device") == -1, +         "Showing map with removed device expected ENODEV, error is %s" % +         (err["error"])) + +def check_dev_info(other_ns, ns, prog_file=None, map_file=None, removed=False): +    progs = bpftool_prog_list(expected=1, ns=ns) +    prog = progs[0] + +    fail("dev" not in prog.keys(), "Device parameters not reported") +    dev = prog["dev"] +    fail("ifindex" not in dev.keys(), "Device parameters not reported") +    fail("ns_dev" not in dev.keys(), "Device parameters not reported") +    fail("ns_inode" not in dev.keys(), "Device parameters not reported") + +    if not other_ns: +        fail("ifname" not in dev.keys(), "Ifname not reported") +        fail(dev["ifname"] != sim["ifname"], +             "Ifname incorrect %s vs %s" % (dev["ifname"], sim["ifname"])) +    else: +        fail("ifname" in dev.keys(), "Ifname is reported for other ns") + +    maps = bpftool_map_list(expected=2, ns=ns) +    for m in maps: +        fail("dev" not in m.keys(), "Device parameters not reported") +        fail(dev != m["dev"], "Map's device different than program's") + +def check_extack(output, reference, args): +    if skip_extack: +        return +    lines = output.split("\n") +    comp = len(lines) >= 2 and lines[1] == reference +    fail(not comp, "Missing or incorrect netlink extack message") + +def check_extack_nsim(output, reference, args): +    check_extack(output, "Error: netdevsim: " + reference, args) + +def check_no_extack(res, needle): +    fail((res[1] + res[2]).count(needle) or (res[1] + res[2]).count("Warning:"), +         "Found '%s' in command output, leaky extack?" % (needle)) + +def check_verifier_log(output, reference): +    lines = output.split("\n") +    for l in reversed(lines): +        if l == reference: +            return +    fail(True, "Missing or incorrect message from netdevsim in verifier log") + +def test_spurios_extack(sim, obj, skip_hw, needle): +    res = sim.cls_bpf_add_filter(obj, prio=1, handle=1, skip_hw=skip_hw, +                                 include_stderr=True) +    check_no_extack(res, needle) +    res = sim.cls_bpf_add_filter(obj, op="replace", prio=1, handle=1, +                                 skip_hw=skip_hw, include_stderr=True) +    check_no_extack(res, needle) +    res = sim.cls_filter_op(op="delete", prio=1, handle=1, cls="bpf", +                            include_stderr=True) +    check_no_extack(res, needle) + + +# Parse command line +parser = argparse.ArgumentParser() +parser.add_argument("--log", help="output verbose log to given file") +args = parser.parse_args() +if args.log: +    logfile = open(args.log, 'w+') +    logfile.write("# -*-Org-*-") + +log("Prepare...", "", level=1) +log_level_inc() + +# Check permissions +skip(os.getuid() != 0, "test must be run as root") + +# Check tools +ret, progs = bpftool("prog", fail=False) +skip(ret != 0, "bpftool not installed") +# Check no BPF programs are loaded +skip(len(progs) != 0, "BPF programs already loaded on the system") + +# Check netdevsim +ret, out = cmd("modprobe netdevsim", fail=False) +skip(ret != 0, "netdevsim module could not be loaded") + +# Check debugfs +_, out = cmd("mount") +if out.find("/sys/kernel/debug type debugfs") == -1: +    cmd("mount -t debugfs none /sys/kernel/debug") + +# Check samples are compiled +samples = ["sample_ret0.o", "sample_map_ret0.o"] +for s in samples: +    ret, out = cmd("ls %s/%s" % (bpf_test_dir, s), fail=False) +    skip(ret != 0, "sample %s/%s not found, please compile it" % +         (bpf_test_dir, s)) + +# Check if iproute2 is built with libmnl (needed by extack support) +_, _, err = cmd("tc qdisc delete dev lo handle 0", +                fail=False, include_stderr=True) +if err.find("Error: Failed to find qdisc with specified handle.") == -1: +    print("Warning: no extack message in iproute2 output, libmnl missing?") +    log("Warning: no extack message in iproute2 output, libmnl missing?", "") +    skip_extack = True + +# Check if net namespaces seem to work +ns = mknetns() +skip(ns is None, "Could not create a net namespace") +cmd("ip netns delete %s" % (ns)) +netns = [] + +try: +    obj = bpf_obj("sample_ret0.o") +    bytecode = bpf_bytecode("1,6 0 0 4294967295,") + +    start_test("Test destruction of generic XDP...") +    sim = NetdevSim() +    sim.set_xdp(obj, "generic") +    sim.remove() +    bpftool_prog_list_wait(expected=0) + +    sim = NetdevSim() +    sim.tc_add_ingress() + +    start_test("Test TC non-offloaded...") +    ret, _ = sim.cls_bpf_add_filter(obj, skip_hw=True, fail=False) +    fail(ret != 0, "Software TC filter did not load") + +    start_test("Test TC non-offloaded isn't getting bound...") +    ret, _ = sim.cls_bpf_add_filter(obj, fail=False) +    fail(ret != 0, "Software TC filter did not load") +    sim.dfs_get_bound_progs(expected=0) + +    sim.tc_flush_filters() + +    start_test("Test TC offloads are off by default...") +    ret, _, err = sim.cls_bpf_add_filter(obj, skip_sw=True, +                                         fail=False, include_stderr=True) +    fail(ret == 0, "TC filter loaded without enabling TC offloads") +    check_extack(err, "Error: TC offload is disabled on net device.", args) +    sim.wait_for_flush() + +    sim.set_ethtool_tc_offloads(True) +    sim.dfs["bpf_tc_non_bound_accept"] = "Y" + +    start_test("Test TC offload by default...") +    ret, _ = sim.cls_bpf_add_filter(obj, fail=False) +    fail(ret != 0, "Software TC filter did not load") +    sim.dfs_get_bound_progs(expected=0) +    ingress = sim.tc_show_ingress(expected=1) +    fltr = ingress[0] +    fail(not fltr["in_hw"], "Filter not offloaded by default") + +    sim.tc_flush_filters() + +    start_test("Test TC cBPF bytcode tries offload by default...") +    ret, _ = sim.cls_bpf_add_filter(bytecode, fail=False) +    fail(ret != 0, "Software TC filter did not load") +    sim.dfs_get_bound_progs(expected=0) +    ingress = sim.tc_show_ingress(expected=1) +    fltr = ingress[0] +    fail(not fltr["in_hw"], "Bytecode not offloaded by default") + +    sim.tc_flush_filters() +    sim.dfs["bpf_tc_non_bound_accept"] = "N" + +    start_test("Test TC cBPF unbound bytecode doesn't offload...") +    ret, _, err = sim.cls_bpf_add_filter(bytecode, skip_sw=True, +                                         fail=False, include_stderr=True) +    fail(ret == 0, "TC bytecode loaded for offload") +    check_extack_nsim(err, "netdevsim configured to reject unbound programs.", +                      args) +    sim.wait_for_flush() + +    start_test("Test non-0 chain offload...") +    ret, _, err = sim.cls_bpf_add_filter(obj, chain=1, prio=1, handle=1, +                                         skip_sw=True, +                                         fail=False, include_stderr=True) +    fail(ret == 0, "Offloaded a filter to chain other than 0") +    check_extack(err, "Error: Driver supports only offload of chain 0.", args) +    sim.tc_flush_filters() + +    start_test("Test TC replace...") +    sim.cls_bpf_add_filter(obj, prio=1, handle=1) +    sim.cls_bpf_add_filter(obj, op="replace", prio=1, handle=1) +    sim.cls_filter_op(op="delete", prio=1, handle=1, cls="bpf") + +    sim.cls_bpf_add_filter(obj, prio=1, handle=1, skip_sw=True) +    sim.cls_bpf_add_filter(obj, op="replace", prio=1, handle=1, skip_sw=True) +    sim.cls_filter_op(op="delete", prio=1, handle=1, cls="bpf") + +    sim.cls_bpf_add_filter(obj, prio=1, handle=1, skip_hw=True) +    sim.cls_bpf_add_filter(obj, op="replace", prio=1, handle=1, skip_hw=True) +    sim.cls_filter_op(op="delete", prio=1, handle=1, cls="bpf") + +    start_test("Test TC replace bad flags...") +    for i in range(3): +        for j in range(3): +            ret, _ = sim.cls_bpf_add_filter(obj, op="replace", prio=1, handle=1, +                                            skip_sw=(j == 1), skip_hw=(j == 2), +                                            fail=False) +            fail(bool(ret) != bool(j), +                 "Software TC incorrect load in replace test, iteration %d" % +                 (j)) +        sim.cls_filter_op(op="delete", prio=1, handle=1, cls="bpf") + +    start_test("Test spurious extack from the driver...") +    test_spurios_extack(sim, obj, False, "netdevsim") +    test_spurios_extack(sim, obj, True, "netdevsim") + +    sim.set_ethtool_tc_offloads(False) + +    test_spurios_extack(sim, obj, False, "TC offload is disabled") +    test_spurios_extack(sim, obj, True, "TC offload is disabled") + +    sim.set_ethtool_tc_offloads(True) + +    sim.tc_flush_filters() + +    start_test("Test TC offloads work...") +    ret, _, err = sim.cls_bpf_add_filter(obj, verbose=True, skip_sw=True, +                                         fail=False, include_stderr=True) +    fail(ret != 0, "TC filter did not load with TC offloads enabled") +    check_verifier_log(err, "[netdevsim] Hello from netdevsim!") + +    start_test("Test TC offload basics...") +    dfs = sim.dfs_get_bound_progs(expected=1) +    progs = bpftool_prog_list(expected=1) +    ingress = sim.tc_show_ingress(expected=1) + +    dprog = dfs[0] +    prog = progs[0] +    fltr = ingress[0] +    fail(fltr["skip_hw"], "TC does reports 'skip_hw' on offloaded filter") +    fail(not fltr["in_hw"], "TC does not report 'in_hw' for offloaded filter") +    fail(not fltr["skip_sw"], "TC does not report 'skip_sw' back") + +    start_test("Test TC offload is device-bound...") +    fail(str(prog["id"]) != fltr["id"], "Program IDs don't match") +    fail(prog["tag"] != fltr["tag"], "Program tags don't match") +    fail(fltr["id"] != dprog["id"], "Program IDs don't match") +    fail(dprog["state"] != "xlated", "Offloaded program state not translated") +    fail(dprog["loaded"] != "Y", "Offloaded program is not loaded") + +    start_test("Test disabling TC offloads is rejected while filters installed...") +    ret, _ = sim.set_ethtool_tc_offloads(False, fail=False) +    fail(ret == 0, "Driver should refuse to disable TC offloads with filters installed...") + +    start_test("Test qdisc removal frees things...") +    sim.tc_flush_filters() +    sim.tc_show_ingress(expected=0) + +    start_test("Test disabling TC offloads is OK without filters...") +    ret, _ = sim.set_ethtool_tc_offloads(False, fail=False) +    fail(ret != 0, +         "Driver refused to disable TC offloads without filters installed...") + +    sim.set_ethtool_tc_offloads(True) + +    start_test("Test destroying device gets rid of TC filters...") +    sim.cls_bpf_add_filter(obj, skip_sw=True) +    sim.remove() +    bpftool_prog_list_wait(expected=0) + +    sim = NetdevSim() +    sim.set_ethtool_tc_offloads(True) + +    start_test("Test destroying device gets rid of XDP...") +    sim.set_xdp(obj, "offload") +    sim.remove() +    bpftool_prog_list_wait(expected=0) + +    sim = NetdevSim() +    sim.set_ethtool_tc_offloads(True) + +    start_test("Test XDP prog reporting...") +    sim.set_xdp(obj, "drv") +    ipl = sim.ip_link_show(xdp=True) +    progs = bpftool_prog_list(expected=1) +    fail(ipl["xdp"]["prog"]["id"] != progs[0]["id"], +         "Loaded program has wrong ID") + +    start_test("Test XDP prog replace without force...") +    ret, _ = sim.set_xdp(obj, "drv", fail=False) +    fail(ret == 0, "Replaced XDP program without -force") +    sim.wait_for_flush(total=1) + +    start_test("Test XDP prog replace with force...") +    ret, _ = sim.set_xdp(obj, "drv", force=True, fail=False) +    fail(ret != 0, "Could not replace XDP program with -force") +    bpftool_prog_list_wait(expected=1) +    ipl = sim.ip_link_show(xdp=True) +    progs = bpftool_prog_list(expected=1) +    fail(ipl["xdp"]["prog"]["id"] != progs[0]["id"], +         "Loaded program has wrong ID") +    fail("dev" in progs[0].keys(), +         "Device parameters reported for non-offloaded program") + +    start_test("Test XDP prog replace with bad flags...") +    ret, _, err = sim.set_xdp(obj, "offload", force=True, +                              fail=False, include_stderr=True) +    fail(ret == 0, "Replaced XDP program with a program in different mode") +    check_extack_nsim(err, "program loaded with different flags.", args) +    ret, _, err = sim.set_xdp(obj, "", force=True, +                              fail=False, include_stderr=True) +    fail(ret == 0, "Replaced XDP program with a program in different mode") +    check_extack_nsim(err, "program loaded with different flags.", args) + +    start_test("Test XDP prog remove with bad flags...") +    ret, _, err = sim.unset_xdp("offload", force=True, +                                fail=False, include_stderr=True) +    fail(ret == 0, "Removed program with a bad mode mode") +    check_extack_nsim(err, "program loaded with different flags.", args) +    ret, _, err = sim.unset_xdp("", force=True, +                                fail=False, include_stderr=True) +    fail(ret == 0, "Removed program with a bad mode mode") +    check_extack_nsim(err, "program loaded with different flags.", args) + +    start_test("Test MTU restrictions...") +    ret, _ = sim.set_mtu(9000, fail=False) +    fail(ret == 0, +         "Driver should refuse to increase MTU to 9000 with XDP loaded...") +    sim.unset_xdp("drv") +    bpftool_prog_list_wait(expected=0) +    sim.set_mtu(9000) +    ret, _, err = sim.set_xdp(obj, "drv", fail=False, include_stderr=True) +    fail(ret == 0, "Driver should refuse to load program with MTU of 9000...") +    check_extack_nsim(err, "MTU too large w/ XDP enabled.", args) +    sim.set_mtu(1500) + +    sim.wait_for_flush() +    start_test("Test XDP offload...") +    _, _, err = sim.set_xdp(obj, "offload", verbose=True, include_stderr=True) +    ipl = sim.ip_link_show(xdp=True) +    link_xdp = ipl["xdp"]["prog"] +    progs = bpftool_prog_list(expected=1) +    prog = progs[0] +    fail(link_xdp["id"] != prog["id"], "Loaded program has wrong ID") +    check_verifier_log(err, "[netdevsim] Hello from netdevsim!") + +    start_test("Test XDP offload is device bound...") +    dfs = sim.dfs_get_bound_progs(expected=1) +    dprog = dfs[0] + +    fail(prog["id"] != link_xdp["id"], "Program IDs don't match") +    fail(prog["tag"] != link_xdp["tag"], "Program tags don't match") +    fail(str(link_xdp["id"]) != dprog["id"], "Program IDs don't match") +    fail(dprog["state"] != "xlated", "Offloaded program state not translated") +    fail(dprog["loaded"] != "Y", "Offloaded program is not loaded") + +    start_test("Test removing XDP program many times...") +    sim.unset_xdp("offload") +    sim.unset_xdp("offload") +    sim.unset_xdp("drv") +    sim.unset_xdp("drv") +    sim.unset_xdp("") +    sim.unset_xdp("") +    bpftool_prog_list_wait(expected=0) + +    start_test("Test attempt to use a program for a wrong device...") +    sim2 = NetdevSim() +    sim2.set_xdp(obj, "offload") +    pin_file, pinned = pin_prog("/sys/fs/bpf/tmp") + +    ret, _, err = sim.set_xdp(pinned, "offload", +                              fail=False, include_stderr=True) +    fail(ret == 0, "Pinned program loaded for a different device accepted") +    check_extack_nsim(err, "program bound to different dev.", args) +    sim2.remove() +    ret, _, err = sim.set_xdp(pinned, "offload", +                              fail=False, include_stderr=True) +    fail(ret == 0, "Pinned program loaded for a removed device accepted") +    check_extack_nsim(err, "xdpoffload of non-bound program.", args) +    rm(pin_file) +    bpftool_prog_list_wait(expected=0) + +    start_test("Test mixing of TC and XDP...") +    sim.tc_add_ingress() +    sim.set_xdp(obj, "offload") +    ret, _, err = sim.cls_bpf_add_filter(obj, skip_sw=True, +                                         fail=False, include_stderr=True) +    fail(ret == 0, "Loading TC when XDP active should fail") +    check_extack_nsim(err, "driver and netdev offload states mismatch.", args) +    sim.unset_xdp("offload") +    sim.wait_for_flush() + +    sim.cls_bpf_add_filter(obj, skip_sw=True) +    ret, _, err = sim.set_xdp(obj, "offload", fail=False, include_stderr=True) +    fail(ret == 0, "Loading XDP when TC active should fail") +    check_extack_nsim(err, "TC program is already loaded.", args) + +    start_test("Test binding TC from pinned...") +    pin_file, pinned = pin_prog("/sys/fs/bpf/tmp") +    sim.tc_flush_filters(bound=1, total=1) +    sim.cls_bpf_add_filter(pinned, da=True, skip_sw=True) +    sim.tc_flush_filters(bound=1, total=1) + +    start_test("Test binding XDP from pinned...") +    sim.set_xdp(obj, "offload") +    pin_file, pinned = pin_prog("/sys/fs/bpf/tmp2", idx=1) + +    sim.set_xdp(pinned, "offload", force=True) +    sim.unset_xdp("offload") +    sim.set_xdp(pinned, "offload", force=True) +    sim.unset_xdp("offload") + +    start_test("Test offload of wrong type fails...") +    ret, _ = sim.cls_bpf_add_filter(pinned, da=True, skip_sw=True, fail=False) +    fail(ret == 0, "Managed to attach XDP program to TC") + +    start_test("Test asking for TC offload of two filters...") +    sim.cls_bpf_add_filter(obj, da=True, skip_sw=True) +    ret, _, err = sim.cls_bpf_add_filter(obj, da=True, skip_sw=True, +                                         fail=False, include_stderr=True) +    fail(ret == 0, "Managed to offload two TC filters at the same time") +    check_extack_nsim(err, "driver and netdev offload states mismatch.", args) + +    sim.tc_flush_filters(bound=2, total=2) + +    start_test("Test if netdev removal waits for translation...") +    delay_msec = 500 +    sim.dfs["bpf_bind_verifier_delay"] = delay_msec +    start = time.time() +    cmd_line = "tc filter add dev %s ingress bpf %s da skip_sw" % \ +               (sim['ifname'], obj) +    tc_proc = cmd(cmd_line, background=True, fail=False) +    # Wait for the verifier to start +    while sim.dfs_num_bound_progs() <= 2: +        pass +    sim.remove() +    end = time.time() +    ret, _ = cmd_result(tc_proc, fail=False) +    time_diff = end - start +    log("Time", "start:\t%s\nend:\t%s\ndiff:\t%s" % (start, end, time_diff)) + +    fail(ret == 0, "Managed to load TC filter on a unregistering device") +    delay_sec = delay_msec * 0.001 +    fail(time_diff < delay_sec, "Removal process took %s, expected %s" % +         (time_diff, delay_sec)) + +    # Remove all pinned files and reinstantiate the netdev +    clean_up() +    bpftool_prog_list_wait(expected=0) + +    sim = NetdevSim() +    map_obj = bpf_obj("sample_map_ret0.o") +    start_test("Test loading program with maps...") +    sim.set_xdp(map_obj, "offload", JSON=False) # map fixup msg breaks JSON + +    start_test("Test bpftool bound info reporting (own ns)...") +    check_dev_info(False, "") + +    start_test("Test bpftool bound info reporting (other ns)...") +    ns = mknetns() +    sim.set_ns(ns) +    check_dev_info(True, "") + +    start_test("Test bpftool bound info reporting (remote ns)...") +    check_dev_info(False, ns) + +    start_test("Test bpftool bound info reporting (back to own ns)...") +    sim.set_ns("") +    check_dev_info(False, "") + +    prog_file, _ = pin_prog("/sys/fs/bpf/tmp_prog") +    map_file, _ = pin_map("/sys/fs/bpf/tmp_map", idx=1, expected=2) +    sim.remove() + +    start_test("Test bpftool bound info reporting (removed dev)...") +    check_dev_info_removed(prog_file=prog_file, map_file=map_file) + +    # Remove all pinned files and reinstantiate the netdev +    clean_up() +    bpftool_prog_list_wait(expected=0) + +    sim = NetdevSim() + +    start_test("Test map update (no flags)...") +    sim.set_xdp(map_obj, "offload", JSON=False) # map fixup msg breaks JSON +    maps = bpftool_map_list(expected=2) +    array = maps[0] if maps[0]["type"] == "array" else maps[1] +    htab = maps[0] if maps[0]["type"] == "hash" else maps[1] +    for m in maps: +        for i in range(2): +            bpftool("map update id %d key %s value %s" % +                    (m["id"], int2str("I", i), int2str("Q", i * 3))) + +    for m in maps: +        ret, _ = bpftool("map update id %d key %s value %s" % +                         (m["id"], int2str("I", 3), int2str("Q", 3 * 3)), +                         fail=False) +        fail(ret == 0, "added too many entries") + +    start_test("Test map update (exists)...") +    for m in maps: +        for i in range(2): +            bpftool("map update id %d key %s value %s exist" % +                    (m["id"], int2str("I", i), int2str("Q", i * 3))) + +    for m in maps: +        ret, err = bpftool("map update id %d key %s value %s exist" % +                           (m["id"], int2str("I", 3), int2str("Q", 3 * 3)), +                           fail=False) +        fail(ret == 0, "updated non-existing key") +        fail(err["error"].find("No such file or directory") == -1, +             "expected ENOENT, error is '%s'" % (err["error"])) + +    start_test("Test map update (noexist)...") +    for m in maps: +        for i in range(2): +            ret, err = bpftool("map update id %d key %s value %s noexist" % +                               (m["id"], int2str("I", i), int2str("Q", i * 3)), +                               fail=False) +        fail(ret == 0, "updated existing key") +        fail(err["error"].find("File exists") == -1, +             "expected EEXIST, error is '%s'" % (err["error"])) + +    start_test("Test map dump...") +    for m in maps: +        _, entries = bpftool("map dump id %d" % (m["id"])) +        for i in range(2): +            key = str2int(entries[i]["key"]) +            fail(key != i, "expected key %d, got %d" % (key, i)) +            val = str2int(entries[i]["value"]) +            fail(val != i * 3, "expected value %d, got %d" % (val, i * 3)) + +    start_test("Test map getnext...") +    for m in maps: +        _, entry = bpftool("map getnext id %d" % (m["id"])) +        key = str2int(entry["next_key"]) +        fail(key != 0, "next key %d, expected %d" % (key, 0)) +        _, entry = bpftool("map getnext id %d key %s" % +                           (m["id"], int2str("I", 0))) +        key = str2int(entry["next_key"]) +        fail(key != 1, "next key %d, expected %d" % (key, 1)) +        ret, err = bpftool("map getnext id %d key %s" % +                           (m["id"], int2str("I", 1)), fail=False) +        fail(ret == 0, "got next key past the end of map") +        fail(err["error"].find("No such file or directory") == -1, +             "expected ENOENT, error is '%s'" % (err["error"])) + +    start_test("Test map delete (htab)...") +    for i in range(2): +        bpftool("map delete id %d key %s" % (htab["id"], int2str("I", i))) + +    start_test("Test map delete (array)...") +    for i in range(2): +        ret, err = bpftool("map delete id %d key %s" % +                           (htab["id"], int2str("I", i)), fail=False) +        fail(ret == 0, "removed entry from an array") +        fail(err["error"].find("No such file or directory") == -1, +             "expected ENOENT, error is '%s'" % (err["error"])) + +    start_test("Test map remove...") +    sim.unset_xdp("offload") +    bpftool_map_list_wait(expected=0) +    sim.remove() + +    sim = NetdevSim() +    sim.set_xdp(map_obj, "offload", JSON=False) # map fixup msg breaks JSON +    sim.remove() +    bpftool_map_list_wait(expected=0) + +    start_test("Test map creation fail path...") +    sim = NetdevSim() +    sim.dfs["bpf_map_accept"] = "N" +    ret, _ = sim.set_xdp(map_obj, "offload", JSON=False, fail=False) +    fail(ret == 0, +         "netdevsim didn't refuse to create a map with offload disabled") + +    print("%s: OK" % (os.path.basename(__file__))) + +finally: +    log("Clean up...", "", level=1) +    log_level_inc() +    clean_up() diff --git a/tools/testing/selftests/bpf/test_progs.c b/tools/testing/selftests/bpf/test_progs.c index 6761be18a91f..b549308abd19 100644 --- a/tools/testing/selftests/bpf/test_progs.c +++ b/tools/testing/selftests/bpf/test_progs.c @@ -21,8 +21,10 @@ typedef __u16 __sum16;  #include <linux/ipv6.h>  #include <linux/tcp.h>  #include <linux/filter.h> +#include <linux/perf_event.h>  #include <linux/unistd.h> +#include <sys/ioctl.h>  #include <sys/wait.h>  #include <sys/resource.h>  #include <sys/types.h> @@ -167,10 +169,9 @@ out:  #define NUM_ITER 100000  #define VIP_NUM 5 -static void test_l4lb(void) +static void test_l4lb(const char *file)  {  	unsigned int nr_cpus = bpf_num_possible_cpus(); -	const char *file = "./test_l4lb.o";  	struct vip key = {.protocol = 6};  	struct vip_meta {  		__u32 flags; @@ -247,6 +248,95 @@ out:  	bpf_object__close(obj);  } +static void test_l4lb_all(void) +{ +	const char *file1 = "./test_l4lb.o"; +	const char *file2 = "./test_l4lb_noinline.o"; + +	test_l4lb(file1); +	test_l4lb(file2); +} + +static void test_xdp_noinline(void) +{ +	const char *file = "./test_xdp_noinline.o"; +	unsigned int nr_cpus = bpf_num_possible_cpus(); +	struct vip key = {.protocol = 6}; +	struct vip_meta { +		__u32 flags; +		__u32 vip_num; +	} value = {.vip_num = VIP_NUM}; +	__u32 stats_key = VIP_NUM; +	struct vip_stats { +		__u64 bytes; +		__u64 pkts; +	} stats[nr_cpus]; +	struct real_definition { +		union { +			__be32 dst; +			__be32 dstv6[4]; +		}; +		__u8 flags; +	} real_def = {.dst = MAGIC_VAL}; +	__u32 ch_key = 11, real_num = 3; +	__u32 duration, retval, size; +	int err, i, prog_fd, map_fd; +	__u64 bytes = 0, pkts = 0; +	struct bpf_object *obj; +	char buf[128]; +	u32 *magic = (u32 *)buf; + +	err = bpf_prog_load(file, BPF_PROG_TYPE_XDP, &obj, &prog_fd); +	if (err) { +		error_cnt++; +		return; +	} + +	map_fd = bpf_find_map(__func__, obj, "vip_map"); +	if (map_fd < 0) +		goto out; +	bpf_map_update_elem(map_fd, &key, &value, 0); + +	map_fd = bpf_find_map(__func__, obj, "ch_rings"); +	if (map_fd < 0) +		goto out; +	bpf_map_update_elem(map_fd, &ch_key, &real_num, 0); + +	map_fd = bpf_find_map(__func__, obj, "reals"); +	if (map_fd < 0) +		goto out; +	bpf_map_update_elem(map_fd, &real_num, &real_def, 0); + +	err = bpf_prog_test_run(prog_fd, NUM_ITER, &pkt_v4, sizeof(pkt_v4), +				buf, &size, &retval, &duration); +	CHECK(err || errno || retval != 1 || size != 54 || +	      *magic != MAGIC_VAL, "ipv4", +	      "err %d errno %d retval %d size %d magic %x\n", +	      err, errno, retval, size, *magic); + +	err = bpf_prog_test_run(prog_fd, NUM_ITER, &pkt_v6, sizeof(pkt_v6), +				buf, &size, &retval, &duration); +	CHECK(err || errno || retval != 1 || size != 74 || +	      *magic != MAGIC_VAL, "ipv6", +	      "err %d errno %d retval %d size %d magic %x\n", +	      err, errno, retval, size, *magic); + +	map_fd = bpf_find_map(__func__, obj, "stats"); +	if (map_fd < 0) +		goto out; +	bpf_map_lookup_elem(map_fd, &stats_key, stats); +	for (i = 0; i < nr_cpus; i++) { +		bytes += stats[i].bytes; +		pkts += stats[i].pkts; +	} +	if (bytes != MAGIC_BYTES * NUM_ITER * 2 || pkts != NUM_ITER * 2) { +		error_cnt++; +		printf("test_xdp_noinline:FAIL:stats %lld %lld\n", bytes, pkts); +	} +out: +	bpf_object__close(obj); +} +  static void test_tcp_estats(void)  {  	const char *file = "./test_tcp_estats.o"; @@ -617,6 +707,262 @@ static void test_obj_name(void)  	}  } +static void test_tp_attach_query(void) +{ +	const int num_progs = 3; +	int i, j, bytes, efd, err, prog_fd[num_progs], pmu_fd[num_progs]; +	__u32 duration = 0, info_len, saved_prog_ids[num_progs]; +	const char *file = "./test_tracepoint.o"; +	struct perf_event_query_bpf *query; +	struct perf_event_attr attr = {}; +	struct bpf_object *obj[num_progs]; +	struct bpf_prog_info prog_info; +	char buf[256]; + +	snprintf(buf, sizeof(buf), +		 "/sys/kernel/debug/tracing/events/sched/sched_switch/id"); +	efd = open(buf, O_RDONLY, 0); +	if (CHECK(efd < 0, "open", "err %d errno %d\n", efd, errno)) +		return; +	bytes = read(efd, buf, sizeof(buf)); +	close(efd); +	if (CHECK(bytes <= 0 || bytes >= sizeof(buf), +		  "read", "bytes %d errno %d\n", bytes, errno)) +		return; + +	attr.config = strtol(buf, NULL, 0); +	attr.type = PERF_TYPE_TRACEPOINT; +	attr.sample_type = PERF_SAMPLE_RAW | PERF_SAMPLE_CALLCHAIN; +	attr.sample_period = 1; +	attr.wakeup_events = 1; + +	query = malloc(sizeof(*query) + sizeof(__u32) * num_progs); +	for (i = 0; i < num_progs; i++) { +		err = bpf_prog_load(file, BPF_PROG_TYPE_TRACEPOINT, &obj[i], +				    &prog_fd[i]); +		if (CHECK(err, "prog_load", "err %d errno %d\n", err, errno)) +			goto cleanup1; + +		bzero(&prog_info, sizeof(prog_info)); +		prog_info.jited_prog_len = 0; +		prog_info.xlated_prog_len = 0; +		prog_info.nr_map_ids = 0; +		info_len = sizeof(prog_info); +		err = bpf_obj_get_info_by_fd(prog_fd[i], &prog_info, &info_len); +		if (CHECK(err, "bpf_obj_get_info_by_fd", "err %d errno %d\n", +			  err, errno)) +			goto cleanup1; +		saved_prog_ids[i] = prog_info.id; + +		pmu_fd[i] = syscall(__NR_perf_event_open, &attr, -1 /* pid */, +				    0 /* cpu 0 */, -1 /* group id */, +				    0 /* flags */); +		if (CHECK(pmu_fd[i] < 0, "perf_event_open", "err %d errno %d\n", +			  pmu_fd[i], errno)) +			goto cleanup2; +		err = ioctl(pmu_fd[i], PERF_EVENT_IOC_ENABLE, 0); +		if (CHECK(err, "perf_event_ioc_enable", "err %d errno %d\n", +			  err, errno)) +			goto cleanup3; + +		if (i == 0) { +			/* check NULL prog array query */ +			query->ids_len = num_progs; +			err = ioctl(pmu_fd[i], PERF_EVENT_IOC_QUERY_BPF, query); +			if (CHECK(err || query->prog_cnt != 0, +				  "perf_event_ioc_query_bpf", +				  "err %d errno %d query->prog_cnt %u\n", +				  err, errno, query->prog_cnt)) +				goto cleanup3; +		} + +		err = ioctl(pmu_fd[i], PERF_EVENT_IOC_SET_BPF, prog_fd[i]); +		if (CHECK(err, "perf_event_ioc_set_bpf", "err %d errno %d\n", +			  err, errno)) +			goto cleanup3; + +		if (i == 1) { +			/* try to get # of programs only */ +			query->ids_len = 0; +			err = ioctl(pmu_fd[i], PERF_EVENT_IOC_QUERY_BPF, query); +			if (CHECK(err || query->prog_cnt != 2, +				  "perf_event_ioc_query_bpf", +				  "err %d errno %d query->prog_cnt %u\n", +				  err, errno, query->prog_cnt)) +				goto cleanup3; + +			/* try a few negative tests */ +			/* invalid query pointer */ +			err = ioctl(pmu_fd[i], PERF_EVENT_IOC_QUERY_BPF, +				    (struct perf_event_query_bpf *)0x1); +			if (CHECK(!err || errno != EFAULT, +				  "perf_event_ioc_query_bpf", +				  "err %d errno %d\n", err, errno)) +				goto cleanup3; + +			/* no enough space */ +			query->ids_len = 1; +			err = ioctl(pmu_fd[i], PERF_EVENT_IOC_QUERY_BPF, query); +			if (CHECK(!err || errno != ENOSPC || query->prog_cnt != 2, +				  "perf_event_ioc_query_bpf", +				  "err %d errno %d query->prog_cnt %u\n", +				  err, errno, query->prog_cnt)) +				goto cleanup3; +		} + +		query->ids_len = num_progs; +		err = ioctl(pmu_fd[i], PERF_EVENT_IOC_QUERY_BPF, query); +		if (CHECK(err || query->prog_cnt != (i + 1), +			  "perf_event_ioc_query_bpf", +			  "err %d errno %d query->prog_cnt %u\n", +			  err, errno, query->prog_cnt)) +			goto cleanup3; +		for (j = 0; j < i + 1; j++) +			if (CHECK(saved_prog_ids[j] != query->ids[j], +				  "perf_event_ioc_query_bpf", +				  "#%d saved_prog_id %x query prog_id %x\n", +				  j, saved_prog_ids[j], query->ids[j])) +				goto cleanup3; +	} + +	i = num_progs - 1; +	for (; i >= 0; i--) { + cleanup3: +		ioctl(pmu_fd[i], PERF_EVENT_IOC_DISABLE); + cleanup2: +		close(pmu_fd[i]); + cleanup1: +		bpf_object__close(obj[i]); +	} +	free(query); +} + +static int compare_map_keys(int map1_fd, int map2_fd) +{ +	__u32 key, next_key; +	char val_buf[PERF_MAX_STACK_DEPTH * sizeof(__u64)]; +	int err; + +	err = bpf_map_get_next_key(map1_fd, NULL, &key); +	if (err) +		return err; +	err = bpf_map_lookup_elem(map2_fd, &key, val_buf); +	if (err) +		return err; + +	while (bpf_map_get_next_key(map1_fd, &key, &next_key) == 0) { +		err = bpf_map_lookup_elem(map2_fd, &next_key, val_buf); +		if (err) +			return err; + +		key = next_key; +	} +	if (errno != ENOENT) +		return -1; + +	return 0; +} + +static void test_stacktrace_map() +{ +	int control_map_fd, stackid_hmap_fd, stackmap_fd; +	const char *file = "./test_stacktrace_map.o"; +	int bytes, efd, err, pmu_fd, prog_fd; +	struct perf_event_attr attr = {}; +	__u32 key, val, duration = 0; +	struct bpf_object *obj; +	char buf[256]; + +	err = bpf_prog_load(file, BPF_PROG_TYPE_TRACEPOINT, &obj, &prog_fd); +	if (CHECK(err, "prog_load", "err %d errno %d\n", err, errno)) +		goto out; + +	/* Get the ID for the sched/sched_switch tracepoint */ +	snprintf(buf, sizeof(buf), +		 "/sys/kernel/debug/tracing/events/sched/sched_switch/id"); +	efd = open(buf, O_RDONLY, 0); +	if (CHECK(efd < 0, "open", "err %d errno %d\n", efd, errno)) +		goto close_prog; + +	bytes = read(efd, buf, sizeof(buf)); +	close(efd); +	if (CHECK(bytes <= 0 || bytes >= sizeof(buf), +		  "read", "bytes %d errno %d\n", bytes, errno)) +		goto close_prog; + +	/* Open the perf event and attach bpf progrram */ +	attr.config = strtol(buf, NULL, 0); +	attr.type = PERF_TYPE_TRACEPOINT; +	attr.sample_type = PERF_SAMPLE_RAW | PERF_SAMPLE_CALLCHAIN; +	attr.sample_period = 1; +	attr.wakeup_events = 1; +	pmu_fd = syscall(__NR_perf_event_open, &attr, -1 /* pid */, +			 0 /* cpu 0 */, -1 /* group id */, +			 0 /* flags */); +	if (CHECK(pmu_fd < 0, "perf_event_open", "err %d errno %d\n", +		  pmu_fd, errno)) +		goto close_prog; + +	err = ioctl(pmu_fd, PERF_EVENT_IOC_ENABLE, 0); +	if (CHECK(err, "perf_event_ioc_enable", "err %d errno %d\n", +		  err, errno)) +		goto close_pmu; + +	err = ioctl(pmu_fd, PERF_EVENT_IOC_SET_BPF, prog_fd); +	if (CHECK(err, "perf_event_ioc_set_bpf", "err %d errno %d\n", +		  err, errno)) +		goto disable_pmu; + +	/* find map fds */ +	control_map_fd = bpf_find_map(__func__, obj, "control_map"); +	if (CHECK(control_map_fd < 0, "bpf_find_map control_map", +		  "err %d errno %d\n", err, errno)) +		goto disable_pmu; + +	stackid_hmap_fd = bpf_find_map(__func__, obj, "stackid_hmap"); +	if (CHECK(stackid_hmap_fd < 0, "bpf_find_map stackid_hmap", +		  "err %d errno %d\n", err, errno)) +		goto disable_pmu; + +	stackmap_fd = bpf_find_map(__func__, obj, "stackmap"); +	if (CHECK(stackmap_fd < 0, "bpf_find_map stackmap", "err %d errno %d\n", +		  err, errno)) +		goto disable_pmu; + +	/* give some time for bpf program run */ +	sleep(1); + +	/* disable stack trace collection */ +	key = 0; +	val = 1; +	bpf_map_update_elem(control_map_fd, &key, &val, 0); + +	/* for every element in stackid_hmap, we can find a corresponding one +	 * in stackmap, and vise versa. +	 */ +	err = compare_map_keys(stackid_hmap_fd, stackmap_fd); +	if (CHECK(err, "compare_map_keys stackid_hmap vs. stackmap", +		  "err %d errno %d\n", err, errno)) +		goto disable_pmu; + +	err = compare_map_keys(stackmap_fd, stackid_hmap_fd); +	if (CHECK(err, "compare_map_keys stackmap vs. stackid_hmap", +		  "err %d errno %d\n", err, errno)) +		; /* fall through */ + +disable_pmu: +	ioctl(pmu_fd, PERF_EVENT_IOC_DISABLE); + +close_pmu: +	close(pmu_fd); + +close_prog: +	bpf_object__close(obj); + +out: +	return; +} +  int main(void)  {  	struct rlimit rinf = { RLIM_INFINITY, RLIM_INFINITY }; @@ -625,11 +971,14 @@ int main(void)  	test_pkt_access();  	test_xdp(); -	test_l4lb(); +	test_l4lb_all(); +	test_xdp_noinline();  	test_tcp_estats();  	test_bpf_obj_id();  	test_pkt_md_access();  	test_obj_name(); +	test_tp_attach_query(); +	test_stacktrace_map();  	printf("Summary: %d PASSED, %d FAILED\n", pass_cnt, error_cnt);  	return error_cnt ? EXIT_FAILURE : EXIT_SUCCESS; diff --git a/tools/testing/selftests/bpf/test_stacktrace_map.c b/tools/testing/selftests/bpf/test_stacktrace_map.c new file mode 100644 index 000000000000..76d85c5d08bd --- /dev/null +++ b/tools/testing/selftests/bpf/test_stacktrace_map.c @@ -0,0 +1,62 @@ +// SPDX-License-Identifier: GPL-2.0 +// Copyright (c) 2018 Facebook + +#include <linux/bpf.h> +#include "bpf_helpers.h" + +#ifndef PERF_MAX_STACK_DEPTH +#define PERF_MAX_STACK_DEPTH         127 +#endif + +struct bpf_map_def SEC("maps") control_map = { +	.type = BPF_MAP_TYPE_ARRAY, +	.key_size = sizeof(__u32), +	.value_size = sizeof(__u32), +	.max_entries = 1, +}; + +struct bpf_map_def SEC("maps") stackid_hmap = { +	.type = BPF_MAP_TYPE_HASH, +	.key_size = sizeof(__u32), +	.value_size = sizeof(__u32), +	.max_entries = 10000, +}; + +struct bpf_map_def SEC("maps") stackmap = { +	.type = BPF_MAP_TYPE_STACK_TRACE, +	.key_size = sizeof(__u32), +	.value_size = sizeof(__u64) * PERF_MAX_STACK_DEPTH, +	.max_entries = 10000, +}; + +/* taken from /sys/kernel/debug/tracing/events/sched/sched_switch/format */ +struct sched_switch_args { +	unsigned long long pad; +	char prev_comm[16]; +	int prev_pid; +	int prev_prio; +	long long prev_state; +	char next_comm[16]; +	int next_pid; +	int next_prio; +}; + +SEC("tracepoint/sched/sched_switch") +int oncpu(struct sched_switch_args *ctx) +{ +	__u32 key = 0, val = 0, *value_p; + +	value_p = bpf_map_lookup_elem(&control_map, &key); +	if (value_p && *value_p) +		return 0; /* skip if non-zero *value_p */ + +	/* The size of stackmap and stackid_hmap should be the same */ +	key = bpf_get_stackid(ctx, &stackmap, 0); +	if ((int)key >= 0) +		bpf_map_update_elem(&stackid_hmap, &key, &val, 0); + +	return 0; +} + +char _license[] SEC("license") = "GPL"; +__u32 _version SEC("version") = 1; /* ignored by tracepoints, required by libbpf.a */ diff --git a/tools/testing/selftests/bpf/test_tcpbpf.h b/tools/testing/selftests/bpf/test_tcpbpf.h new file mode 100644 index 000000000000..2fe43289943c --- /dev/null +++ b/tools/testing/selftests/bpf/test_tcpbpf.h @@ -0,0 +1,16 @@ +// SPDX-License-Identifier: GPL-2.0 + +#ifndef _TEST_TCPBPF_H +#define _TEST_TCPBPF_H + +struct tcpbpf_globals { +	__u32 event_map; +	__u32 total_retrans; +	__u32 data_segs_in; +	__u32 data_segs_out; +	__u32 bad_cb_test_rv; +	__u32 good_cb_test_rv; +	__u64 bytes_received; +	__u64 bytes_acked; +}; +#endif diff --git a/tools/testing/selftests/bpf/test_tcpbpf_kern.c b/tools/testing/selftests/bpf/test_tcpbpf_kern.c new file mode 100644 index 000000000000..3e645ee41ed5 --- /dev/null +++ b/tools/testing/selftests/bpf/test_tcpbpf_kern.c @@ -0,0 +1,114 @@ +// SPDX-License-Identifier: GPL-2.0 +#include <stddef.h> +#include <string.h> +#include <linux/bpf.h> +#include <linux/if_ether.h> +#include <linux/if_packet.h> +#include <linux/ip.h> +#include <linux/types.h> +#include <linux/socket.h> +#include <linux/tcp.h> +#include <netinet/in.h> +#include "bpf_helpers.h" +#include "bpf_endian.h" +#include "test_tcpbpf.h" + +struct bpf_map_def SEC("maps") global_map = { +	.type = BPF_MAP_TYPE_ARRAY, +	.key_size = sizeof(__u32), +	.value_size = sizeof(struct tcpbpf_globals), +	.max_entries = 2, +}; + +static inline void update_event_map(int event) +{ +	__u32 key = 0; +	struct tcpbpf_globals g, *gp; + +	gp = bpf_map_lookup_elem(&global_map, &key); +	if (gp == NULL) { +		struct tcpbpf_globals g = {0}; + +		g.event_map |= (1 << event); +		bpf_map_update_elem(&global_map, &key, &g, +			    BPF_ANY); +	} else { +		g = *gp; +		g.event_map |= (1 << event); +		bpf_map_update_elem(&global_map, &key, &g, +			    BPF_ANY); +	} +} + +int _version SEC("version") = 1; + +SEC("sockops") +int bpf_testcb(struct bpf_sock_ops *skops) +{ +	int rv = -1; +	int bad_call_rv = 0; +	int good_call_rv = 0; +	int op; +	int v = 0; + +	op = (int) skops->op; + +	update_event_map(op); + +	switch (op) { +	case BPF_SOCK_OPS_ACTIVE_ESTABLISHED_CB: +		/* Test failure to set largest cb flag (assumes not defined) */ +		bad_call_rv = bpf_sock_ops_cb_flags_set(skops, 0x80); +		/* Set callback */ +		good_call_rv = bpf_sock_ops_cb_flags_set(skops, +						 BPF_SOCK_OPS_STATE_CB_FLAG); +		/* Update results */ +		{ +			__u32 key = 0; +			struct tcpbpf_globals g, *gp; + +			gp = bpf_map_lookup_elem(&global_map, &key); +			if (!gp) +				break; +			g = *gp; +			g.bad_cb_test_rv = bad_call_rv; +			g.good_cb_test_rv = good_call_rv; +			bpf_map_update_elem(&global_map, &key, &g, +					    BPF_ANY); +		} +		break; +	case BPF_SOCK_OPS_PASSIVE_ESTABLISHED_CB: +		skops->sk_txhash = 0x12345f; +		v = 0xff; +		rv = bpf_setsockopt(skops, SOL_IPV6, IPV6_TCLASS, &v, +				    sizeof(v)); +		break; +	case BPF_SOCK_OPS_RTO_CB: +		break; +	case BPF_SOCK_OPS_RETRANS_CB: +		break; +	case BPF_SOCK_OPS_STATE_CB: +		if (skops->args[1] == BPF_TCP_CLOSE) { +			__u32 key = 0; +			struct tcpbpf_globals g, *gp; + +			gp = bpf_map_lookup_elem(&global_map, &key); +			if (!gp) +				break; +			g = *gp; +			g.total_retrans = skops->total_retrans; +			g.data_segs_in = skops->data_segs_in; +			g.data_segs_out = skops->data_segs_out; +			g.bytes_received = skops->bytes_received; +			g.bytes_acked = skops->bytes_acked; +			bpf_map_update_elem(&global_map, &key, &g, +					    BPF_ANY); +		} +		break; +	default: +		rv = -1; +	} +	skops->reply = rv; +	return 1; +} +char _license[] SEC("license") = "GPL"; diff --git a/tools/testing/selftests/bpf/test_tcpbpf_user.c b/tools/testing/selftests/bpf/test_tcpbpf_user.c new file mode 100644 index 000000000000..95a370f3d378 --- /dev/null +++ b/tools/testing/selftests/bpf/test_tcpbpf_user.c @@ -0,0 +1,126 @@ +// SPDX-License-Identifier: GPL-2.0 +#include <stdio.h> +#include <stdlib.h> +#include <stdio.h> +#include <unistd.h> +#include <errno.h> +#include <signal.h> +#include <string.h> +#include <assert.h> +#include <linux/perf_event.h> +#include <linux/ptrace.h> +#include <linux/bpf.h> +#include <sys/ioctl.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <fcntl.h> +#include <bpf/bpf.h> +#include <bpf/libbpf.h> +#include "bpf_util.h" +#include <linux/perf_event.h> +#include "test_tcpbpf.h" + +static int bpf_find_map(const char *test, struct bpf_object *obj, +			const char *name) +{ +	struct bpf_map *map; + +	map = bpf_object__find_map_by_name(obj, name); +	if (!map) { +		printf("%s:FAIL:map '%s' not found\n", test, name); +		return -1; +	} +	return bpf_map__fd(map); +} + +#define SYSTEM(CMD)						\ +	do {							\ +		if (system(CMD)) {				\ +			printf("system(%s) FAILS!\n", CMD);	\ +		}						\ +	} while (0) + +int main(int argc, char **argv) +{ +	const char *file = "test_tcpbpf_kern.o"; +	struct tcpbpf_globals g = {0}; +	int cg_fd, prog_fd, map_fd; +	bool debug_flag = false; +	int error = EXIT_FAILURE; +	struct bpf_object *obj; +	char cmd[100], *dir; +	struct stat buffer; +	__u32 key = 0; +	int pid; +	int rv; + +	if (argc > 1 && strcmp(argv[1], "-d") == 0) +		debug_flag = true; + +	dir = "/tmp/cgroupv2/foo"; + +	if (stat(dir, &buffer) != 0) { +		SYSTEM("mkdir -p /tmp/cgroupv2"); +		SYSTEM("mount -t cgroup2 none /tmp/cgroupv2"); +		SYSTEM("mkdir -p /tmp/cgroupv2/foo"); +	} +	pid = (int) getpid(); +	sprintf(cmd, "echo %d >> /tmp/cgroupv2/foo/cgroup.procs", pid); +	SYSTEM(cmd); + +	cg_fd = open(dir, O_DIRECTORY, O_RDONLY); +	if (bpf_prog_load(file, BPF_PROG_TYPE_SOCK_OPS, &obj, &prog_fd)) { +		printf("FAILED: load_bpf_file failed for: %s\n", file); +		goto err; +	} + +	rv = bpf_prog_attach(prog_fd, cg_fd, BPF_CGROUP_SOCK_OPS, 0); +	if (rv) { +		printf("FAILED: bpf_prog_attach: %d (%s)\n", +		       error, strerror(errno)); +		goto err; +	} + +	SYSTEM("./tcp_server.py"); + +	map_fd = bpf_find_map(__func__, obj, "global_map"); +	if (map_fd < 0) +		goto err; + +	rv = bpf_map_lookup_elem(map_fd, &key, &g); +	if (rv != 0) { +		printf("FAILED: bpf_map_lookup_elem returns %d\n", rv); +		goto err; +	} + +	if (g.bytes_received != 501 || g.bytes_acked != 1002 || +	    g.data_segs_in != 1 || g.data_segs_out != 1 || +	    (g.event_map ^ 0x47e) != 0 || g.bad_cb_test_rv != 0x80 || +		g.good_cb_test_rv != 0) { +		printf("FAILED: Wrong stats\n"); +		if (debug_flag) { +			printf("\n"); +			printf("bytes_received: %d (expecting 501)\n", +			       (int)g.bytes_received); +			printf("bytes_acked:    %d (expecting 1002)\n", +			       (int)g.bytes_acked); +			printf("data_segs_in:   %d (expecting 1)\n", +			       g.data_segs_in); +			printf("data_segs_out:  %d (expecting 1)\n", +			       g.data_segs_out); +			printf("event_map:      0x%x (at least 0x47e)\n", +			       g.event_map); +			printf("bad_cb_test_rv: 0x%x (expecting 0x80)\n", +			       g.bad_cb_test_rv); +			printf("good_cb_test_rv:0x%x (expecting 0)\n", +			       g.good_cb_test_rv); +		} +		goto err; +	} +	printf("PASSED!\n"); +	error = 0; +err: +	bpf_prog_detach(cg_fd, BPF_CGROUP_SOCK_OPS); +	return error; + +} diff --git a/tools/testing/selftests/bpf/test_tracepoint.c b/tools/testing/selftests/bpf/test_tracepoint.c new file mode 100644 index 000000000000..04bf084517e0 --- /dev/null +++ b/tools/testing/selftests/bpf/test_tracepoint.c @@ -0,0 +1,26 @@ +// SPDX-License-Identifier: GPL-2.0 +// Copyright (c) 2017 Facebook + +#include <linux/bpf.h> +#include "bpf_helpers.h" + +/* taken from /sys/kernel/debug/tracing/events/sched/sched_switch/format */ +struct sched_switch_args { +	unsigned long long pad; +	char prev_comm[16]; +	int prev_pid; +	int prev_prio; +	long long prev_state; +	char next_comm[16]; +	int next_pid; +	int next_prio; +}; + +SEC("tracepoint/sched/sched_switch") +int oncpu(struct sched_switch_args *ctx) +{ +	return 0; +} + +char _license[] SEC("license") = "GPL"; +__u32 _version SEC("version") = 1; /* ignored by tracepoints, required by libbpf.a */ diff --git a/tools/testing/selftests/bpf/test_verifier.c b/tools/testing/selftests/bpf/test_verifier.c index 5ed4175c4ff8..c73592fa3d41 100644 --- a/tools/testing/selftests/bpf/test_verifier.c +++ b/tools/testing/selftests/bpf/test_verifier.c @@ -2,6 +2,7 @@   * Testsuite for eBPF verifier   *   * Copyright (c) 2014 PLUMgrid, http://plumgrid.com + * Copyright (c) 2017 Facebook   *   * This program is free software; you can redistribute it and/or   * modify it under the terms of version 2 of the GNU General Public @@ -20,6 +21,7 @@  #include <stddef.h>  #include <stdbool.h>  #include <sched.h> +#include <limits.h>  #include <sys/capability.h>  #include <sys/resource.h> @@ -28,6 +30,7 @@  #include <linux/filter.h>  #include <linux/bpf_perf_event.h>  #include <linux/bpf.h> +#include <linux/if_ether.h>  #include <bpf/bpf.h> @@ -48,6 +51,8 @@  #define MAX_INSNS	512  #define MAX_FIXUPS	8  #define MAX_NR_MAPS	4 +#define POINTER_VALUE	0xcafe4all +#define TEST_DATA_LEN	64  #define F_NEEDS_EFFICIENT_UNALIGNED_ACCESS	(1 << 0)  #define F_LOAD_WITH_STRICT_ALIGNMENT		(1 << 1) @@ -61,6 +66,7 @@ struct bpf_test {  	int fixup_map_in_map[MAX_FIXUPS];  	const char *errstr;  	const char *errstr_unpriv; +	uint32_t retval;  	enum {  		UNDEF,  		ACCEPT, @@ -94,6 +100,326 @@ static struct bpf_test tests[] = {  			BPF_EXIT_INSN(),  		},  		.result = ACCEPT, +		.retval = -3, +	}, +	{ +		"DIV32 by 0, zero check 1", +		.insns = { +			BPF_MOV32_IMM(BPF_REG_0, 42), +			BPF_MOV32_IMM(BPF_REG_1, 0), +			BPF_MOV32_IMM(BPF_REG_2, 1), +			BPF_ALU32_REG(BPF_DIV, BPF_REG_2, BPF_REG_1), +			BPF_EXIT_INSN(), +		}, +		.result = ACCEPT, +		.retval = 42, +	}, +	{ +		"DIV32 by 0, zero check 2", +		.insns = { +			BPF_MOV32_IMM(BPF_REG_0, 42), +			BPF_LD_IMM64(BPF_REG_1, 0xffffffff00000000LL), +			BPF_MOV32_IMM(BPF_REG_2, 1), +			BPF_ALU32_REG(BPF_DIV, BPF_REG_2, BPF_REG_1), +			BPF_EXIT_INSN(), +		}, +		.result = ACCEPT, +		.retval = 42, +	}, +	{ +		"DIV64 by 0, zero check", +		.insns = { +			BPF_MOV32_IMM(BPF_REG_0, 42), +			BPF_MOV32_IMM(BPF_REG_1, 0), +			BPF_MOV32_IMM(BPF_REG_2, 1), +			BPF_ALU64_REG(BPF_DIV, BPF_REG_2, BPF_REG_1), +			BPF_EXIT_INSN(), +		}, +		.result = ACCEPT, +		.retval = 42, +	}, +	{ +		"MOD32 by 0, zero check 1", +		.insns = { +			BPF_MOV32_IMM(BPF_REG_0, 42), +			BPF_MOV32_IMM(BPF_REG_1, 0), +			BPF_MOV32_IMM(BPF_REG_2, 1), +			BPF_ALU32_REG(BPF_MOD, BPF_REG_2, BPF_REG_1), +			BPF_EXIT_INSN(), +		}, +		.result = ACCEPT, +		.retval = 42, +	}, +	{ +		"MOD32 by 0, zero check 2", +		.insns = { +			BPF_MOV32_IMM(BPF_REG_0, 42), +			BPF_LD_IMM64(BPF_REG_1, 0xffffffff00000000LL), +			BPF_MOV32_IMM(BPF_REG_2, 1), +			BPF_ALU32_REG(BPF_MOD, BPF_REG_2, BPF_REG_1), +			BPF_EXIT_INSN(), +		}, +		.result = ACCEPT, +		.retval = 42, +	}, +	{ +		"MOD64 by 0, zero check", +		.insns = { +			BPF_MOV32_IMM(BPF_REG_0, 42), +			BPF_MOV32_IMM(BPF_REG_1, 0), +			BPF_MOV32_IMM(BPF_REG_2, 1), +			BPF_ALU64_REG(BPF_MOD, BPF_REG_2, BPF_REG_1), +			BPF_EXIT_INSN(), +		}, +		.result = ACCEPT, +		.retval = 42, +	}, +	{ +		"DIV32 by 0, zero check ok, cls", +		.insns = { +			BPF_MOV32_IMM(BPF_REG_0, 42), +			BPF_MOV32_IMM(BPF_REG_1, 2), +			BPF_MOV32_IMM(BPF_REG_2, 16), +			BPF_ALU32_REG(BPF_DIV, BPF_REG_2, BPF_REG_1), +			BPF_MOV64_REG(BPF_REG_0, BPF_REG_2), +			BPF_EXIT_INSN(), +		}, +		.prog_type = BPF_PROG_TYPE_SCHED_CLS, +		.result = ACCEPT, +		.retval = 8, +	}, +	{ +		"DIV32 by 0, zero check 1, cls", +		.insns = { +			BPF_MOV32_IMM(BPF_REG_1, 0), +			BPF_MOV32_IMM(BPF_REG_0, 1), +			BPF_ALU32_REG(BPF_DIV, BPF_REG_0, BPF_REG_1), +			BPF_EXIT_INSN(), +		}, +		.prog_type = BPF_PROG_TYPE_SCHED_CLS, +		.result = ACCEPT, +		.retval = 0, +	}, +	{ +		"DIV32 by 0, zero check 2, cls", +		.insns = { +			BPF_LD_IMM64(BPF_REG_1, 0xffffffff00000000LL), +			BPF_MOV32_IMM(BPF_REG_0, 1), +			BPF_ALU32_REG(BPF_DIV, BPF_REG_0, BPF_REG_1), +			BPF_EXIT_INSN(), +		}, +		.prog_type = BPF_PROG_TYPE_SCHED_CLS, +		.result = ACCEPT, +		.retval = 0, +	}, +	{ +		"DIV64 by 0, zero check, cls", +		.insns = { +			BPF_MOV32_IMM(BPF_REG_1, 0), +			BPF_MOV32_IMM(BPF_REG_0, 1), +			BPF_ALU64_REG(BPF_DIV, BPF_REG_0, BPF_REG_1), +			BPF_EXIT_INSN(), +		}, +		.prog_type = BPF_PROG_TYPE_SCHED_CLS, +		.result = ACCEPT, +		.retval = 0, +	}, +	{ +		"MOD32 by 0, zero check ok, cls", +		.insns = { +			BPF_MOV32_IMM(BPF_REG_0, 42), +			BPF_MOV32_IMM(BPF_REG_1, 3), +			BPF_MOV32_IMM(BPF_REG_2, 5), +			BPF_ALU32_REG(BPF_MOD, BPF_REG_2, BPF_REG_1), +			BPF_MOV64_REG(BPF_REG_0, BPF_REG_2), +			BPF_EXIT_INSN(), +		}, +		.prog_type = BPF_PROG_TYPE_SCHED_CLS, +		.result = ACCEPT, +		.retval = 2, +	}, +	{ +		"MOD32 by 0, zero check 1, cls", +		.insns = { +			BPF_MOV32_IMM(BPF_REG_1, 0), +			BPF_MOV32_IMM(BPF_REG_0, 1), +			BPF_ALU32_REG(BPF_MOD, BPF_REG_0, BPF_REG_1), +			BPF_EXIT_INSN(), +		}, +		.prog_type = BPF_PROG_TYPE_SCHED_CLS, +		.result = ACCEPT, +		.retval = 1, +	}, +	{ +		"MOD32 by 0, zero check 2, cls", +		.insns = { +			BPF_LD_IMM64(BPF_REG_1, 0xffffffff00000000LL), +			BPF_MOV32_IMM(BPF_REG_0, 1), +			BPF_ALU32_REG(BPF_MOD, BPF_REG_0, BPF_REG_1), +			BPF_EXIT_INSN(), +		}, +		.prog_type = BPF_PROG_TYPE_SCHED_CLS, +		.result = ACCEPT, +		.retval = 1, +	}, +	{ +		"MOD64 by 0, zero check 1, cls", +		.insns = { +			BPF_MOV32_IMM(BPF_REG_1, 0), +			BPF_MOV32_IMM(BPF_REG_0, 2), +			BPF_ALU64_REG(BPF_MOD, BPF_REG_0, BPF_REG_1), +			BPF_EXIT_INSN(), +		}, +		.prog_type = BPF_PROG_TYPE_SCHED_CLS, +		.result = ACCEPT, +		.retval = 2, +	}, +	{ +		"MOD64 by 0, zero check 2, cls", +		.insns = { +			BPF_MOV32_IMM(BPF_REG_1, 0), +			BPF_MOV32_IMM(BPF_REG_0, -1), +			BPF_ALU64_REG(BPF_MOD, BPF_REG_0, BPF_REG_1), +			BPF_EXIT_INSN(), +		}, +		.prog_type = BPF_PROG_TYPE_SCHED_CLS, +		.result = ACCEPT, +		.retval = -1, +	}, +	/* Just make sure that JITs used udiv/umod as otherwise we get +	 * an exception from INT_MIN/-1 overflow similarly as with div +	 * by zero. +	 */ +	{ +		"DIV32 overflow, check 1", +		.insns = { +			BPF_MOV32_IMM(BPF_REG_1, -1), +			BPF_MOV32_IMM(BPF_REG_0, INT_MIN), +			BPF_ALU32_REG(BPF_DIV, BPF_REG_0, BPF_REG_1), +			BPF_EXIT_INSN(), +		}, +		.prog_type = BPF_PROG_TYPE_SCHED_CLS, +		.result = ACCEPT, +		.retval = 0, +	}, +	{ +		"DIV32 overflow, check 2", +		.insns = { +			BPF_MOV32_IMM(BPF_REG_0, INT_MIN), +			BPF_ALU32_IMM(BPF_DIV, BPF_REG_0, -1), +			BPF_EXIT_INSN(), +		}, +		.prog_type = BPF_PROG_TYPE_SCHED_CLS, +		.result = ACCEPT, +		.retval = 0, +	}, +	{ +		"DIV64 overflow, check 1", +		.insns = { +			BPF_MOV64_IMM(BPF_REG_1, -1), +			BPF_LD_IMM64(BPF_REG_0, LLONG_MIN), +			BPF_ALU64_REG(BPF_DIV, BPF_REG_0, BPF_REG_1), +			BPF_EXIT_INSN(), +		}, +		.prog_type = BPF_PROG_TYPE_SCHED_CLS, +		.result = ACCEPT, +		.retval = 0, +	}, +	{ +		"DIV64 overflow, check 2", +		.insns = { +			BPF_LD_IMM64(BPF_REG_0, LLONG_MIN), +			BPF_ALU64_IMM(BPF_DIV, BPF_REG_0, -1), +			BPF_EXIT_INSN(), +		}, +		.prog_type = BPF_PROG_TYPE_SCHED_CLS, +		.result = ACCEPT, +		.retval = 0, +	}, +	{ +		"MOD32 overflow, check 1", +		.insns = { +			BPF_MOV32_IMM(BPF_REG_1, -1), +			BPF_MOV32_IMM(BPF_REG_0, INT_MIN), +			BPF_ALU32_REG(BPF_MOD, BPF_REG_0, BPF_REG_1), +			BPF_EXIT_INSN(), +		}, +		.prog_type = BPF_PROG_TYPE_SCHED_CLS, +		.result = ACCEPT, +		.retval = INT_MIN, +	}, +	{ +		"MOD32 overflow, check 2", +		.insns = { +			BPF_MOV32_IMM(BPF_REG_0, INT_MIN), +			BPF_ALU32_IMM(BPF_MOD, BPF_REG_0, -1), +			BPF_EXIT_INSN(), +		}, +		.prog_type = BPF_PROG_TYPE_SCHED_CLS, +		.result = ACCEPT, +		.retval = INT_MIN, +	}, +	{ +		"MOD64 overflow, check 1", +		.insns = { +			BPF_MOV64_IMM(BPF_REG_1, -1), +			BPF_LD_IMM64(BPF_REG_2, LLONG_MIN), +			BPF_MOV64_REG(BPF_REG_3, BPF_REG_2), +			BPF_ALU64_REG(BPF_MOD, BPF_REG_2, BPF_REG_1), +			BPF_MOV32_IMM(BPF_REG_0, 0), +			BPF_JMP_REG(BPF_JNE, BPF_REG_3, BPF_REG_2, 1), +			BPF_MOV32_IMM(BPF_REG_0, 1), +			BPF_EXIT_INSN(), +		}, +		.prog_type = BPF_PROG_TYPE_SCHED_CLS, +		.result = ACCEPT, +		.retval = 1, +	}, +	{ +		"MOD64 overflow, check 2", +		.insns = { +			BPF_LD_IMM64(BPF_REG_2, LLONG_MIN), +			BPF_MOV64_REG(BPF_REG_3, BPF_REG_2), +			BPF_ALU64_IMM(BPF_MOD, BPF_REG_2, -1), +			BPF_MOV32_IMM(BPF_REG_0, 0), +			BPF_JMP_REG(BPF_JNE, BPF_REG_3, BPF_REG_2, 1), +			BPF_MOV32_IMM(BPF_REG_0, 1), +			BPF_EXIT_INSN(), +		}, +		.prog_type = BPF_PROG_TYPE_SCHED_CLS, +		.result = ACCEPT, +		.retval = 1, +	}, +	{ +		"xor32 zero extend check", +		.insns = { +			BPF_MOV32_IMM(BPF_REG_2, -1), +			BPF_ALU64_IMM(BPF_LSH, BPF_REG_2, 32), +			BPF_ALU64_IMM(BPF_OR, BPF_REG_2, 0xffff), +			BPF_ALU32_REG(BPF_XOR, BPF_REG_2, BPF_REG_2), +			BPF_MOV32_IMM(BPF_REG_0, 2), +			BPF_JMP_IMM(BPF_JNE, BPF_REG_2, 0, 1), +			BPF_MOV32_IMM(BPF_REG_0, 1), +			BPF_EXIT_INSN(), +		}, +		.prog_type = BPF_PROG_TYPE_SCHED_CLS, +		.result = ACCEPT, +		.retval = 1, +	}, +	{ +		"empty prog", +		.insns = { +		}, +		.errstr = "unknown opcode 00", +		.result = REJECT, +	}, +	{ +		"only exit insn", +		.insns = { +			BPF_EXIT_INSN(), +		}, +		.errstr = "R0 !read_ok", +		.result = REJECT,  	},  	{  		"unreachable", @@ -209,6 +535,7 @@ static struct bpf_test tests[] = {  			BPF_EXIT_INSN(),  		},  		.result = ACCEPT, +		.retval = 1,  	},  	{  		"test8 ld_imm64", @@ -280,7 +607,7 @@ static struct bpf_test tests[] = {  			BPF_EXIT_INSN(),  		},  		.result = REJECT, -		.errstr = "BPF_ARSH not supported for 32 bit ALU", +		.errstr = "unknown opcode c4",  	},  	{  		"arsh32 on reg", @@ -291,7 +618,7 @@ static struct bpf_test tests[] = {  			BPF_EXIT_INSN(),  		},  		.result = REJECT, -		.errstr = "BPF_ARSH not supported for 32 bit ALU", +		.errstr = "unknown opcode cc",  	},  	{  		"arsh64 on imm", @@ -317,7 +644,7 @@ static struct bpf_test tests[] = {  		.insns = {  			BPF_ALU64_REG(BPF_MOV, BPF_REG_0, BPF_REG_2),  		}, -		.errstr = "jump out of range", +		.errstr = "not an exit",  		.result = REJECT,  	},  	{ @@ -407,7 +734,7 @@ static struct bpf_test tests[] = {  			BPF_RAW_INSN(BPF_JMP | BPF_CALL | BPF_X, 0, 0, 0, 0),  			BPF_EXIT_INSN(),  		}, -		.errstr = "BPF_CALL uses reserved", +		.errstr = "unknown opcode 8d",  		.result = REJECT,  	},  	{ @@ -516,6 +843,7 @@ static struct bpf_test tests[] = {  		.errstr_unpriv = "R0 leaks addr",  		.result = ACCEPT,  		.result_unpriv = REJECT, +		.retval = POINTER_VALUE,  	},  	{  		"check valid spill/fill, skb mark", @@ -596,7 +924,7 @@ static struct bpf_test tests[] = {  			BPF_RAW_INSN(0, 0, 0, 0, 0),  			BPF_EXIT_INSN(),  		}, -		.errstr = "invalid BPF_LD_IMM", +		.errstr = "unknown opcode 00",  		.result = REJECT,  	},  	{ @@ -614,7 +942,7 @@ static struct bpf_test tests[] = {  			BPF_RAW_INSN(-1, 0, 0, 0, 0),  			BPF_EXIT_INSN(),  		}, -		.errstr = "invalid BPF_ALU opcode f0", +		.errstr = "unknown opcode ff",  		.result = REJECT,  	},  	{ @@ -623,7 +951,7 @@ static struct bpf_test tests[] = {  			BPF_RAW_INSN(-1, -1, -1, -1, -1),  			BPF_EXIT_INSN(),  		}, -		.errstr = "invalid BPF_ALU opcode f0", +		.errstr = "unknown opcode ff",  		.result = REJECT,  	},  	{ @@ -802,6 +1130,7 @@ static struct bpf_test tests[] = {  		.errstr_unpriv = "R1 pointer comparison",  		.result_unpriv = REJECT,  		.result = ACCEPT, +		.retval = -ENOENT,  	},  	{  		"jump test 4", @@ -1822,6 +2151,7 @@ static struct bpf_test tests[] = {  			BPF_EXIT_INSN(),  		},  		.result = ACCEPT, +		.retval = 0xfaceb00c,  	},  	{  		"PTR_TO_STACK store/load - bad alignment on off", @@ -1880,6 +2210,7 @@ static struct bpf_test tests[] = {  		.result = ACCEPT,  		.result_unpriv = REJECT,  		.errstr_unpriv = "R0 leaks addr", +		.retval = POINTER_VALUE,  	},  	{  		"unpriv: add const to pointer", @@ -2053,6 +2384,7 @@ static struct bpf_test tests[] = {  			BPF_LDX_MEM(BPF_DW, BPF_REG_1, BPF_REG_6, 0),  			BPF_RAW_INSN(BPF_JMP | BPF_CALL, 0, 0, 0,  				     BPF_FUNC_get_hash_recalc), +			BPF_MOV64_IMM(BPF_REG_0, 0),  			BPF_EXIT_INSN(),  		},  		.result = ACCEPT, @@ -2255,6 +2587,32 @@ static struct bpf_test tests[] = {  		.result = ACCEPT,  	},  	{ +		"runtime/jit: pass negative index to tail_call", +		.insns = { +			BPF_MOV64_IMM(BPF_REG_3, -1), +			BPF_LD_MAP_FD(BPF_REG_2, 0), +			BPF_RAW_INSN(BPF_JMP | BPF_CALL, 0, 0, 0, +				     BPF_FUNC_tail_call), +			BPF_MOV64_IMM(BPF_REG_0, 0), +			BPF_EXIT_INSN(), +		}, +		.fixup_prog = { 1 }, +		.result = ACCEPT, +	}, +	{ +		"runtime/jit: pass > 32bit index to tail_call", +		.insns = { +			BPF_LD_IMM64(BPF_REG_3, 0x100000000ULL), +			BPF_LD_MAP_FD(BPF_REG_2, 0), +			BPF_RAW_INSN(BPF_JMP | BPF_CALL, 0, 0, 0, +				     BPF_FUNC_tail_call), +			BPF_MOV64_IMM(BPF_REG_0, 0), +			BPF_EXIT_INSN(), +		}, +		.fixup_prog = { 2 }, +		.result = ACCEPT, +	}, +	{  		"stack pointer arithmetic",  		.insns = {  			BPF_MOV64_IMM(BPF_REG_1, 4), @@ -2840,6 +3198,7 @@ static struct bpf_test tests[] = {  		},  		.result = ACCEPT,  		.prog_type = BPF_PROG_TYPE_SCHED_CLS, +		.retval = 1,  	},  	{  		"direct packet access: test12 (and, good access)", @@ -2864,6 +3223,7 @@ static struct bpf_test tests[] = {  		},  		.result = ACCEPT,  		.prog_type = BPF_PROG_TYPE_SCHED_CLS, +		.retval = 1,  	},  	{  		"direct packet access: test13 (branches, good access)", @@ -2894,6 +3254,7 @@ static struct bpf_test tests[] = {  		},  		.result = ACCEPT,  		.prog_type = BPF_PROG_TYPE_SCHED_CLS, +		.retval = 1,  	},  	{  		"direct packet access: test14 (pkt_ptr += 0, CONST_IMM, good access)", @@ -2917,6 +3278,7 @@ static struct bpf_test tests[] = {  		},  		.result = ACCEPT,  		.prog_type = BPF_PROG_TYPE_SCHED_CLS, +		.retval = 1,  	},  	{  		"direct packet access: test15 (spill with xadd)", @@ -3203,6 +3565,7 @@ static struct bpf_test tests[] = {  		},  		.result = ACCEPT,  		.prog_type = BPF_PROG_TYPE_SCHED_CLS, +		.retval = 1,  	},  	{  		"direct packet access: test28 (marking on <=, bad access)", @@ -5700,7 +6063,7 @@ static struct bpf_test tests[] = {  		"helper access to variable memory: size > 0 not allowed on NULL (ARG_PTR_TO_MEM_OR_NULL)",  		.insns = {  			BPF_MOV64_IMM(BPF_REG_1, 0), -			BPF_MOV64_IMM(BPF_REG_2, 0), +			BPF_MOV64_IMM(BPF_REG_2, 1),  			BPF_STX_MEM(BPF_DW, BPF_REG_10, BPF_REG_2, -128),  			BPF_LDX_MEM(BPF_DW, BPF_REG_2, BPF_REG_10, -128),  			BPF_ALU64_IMM(BPF_AND, BPF_REG_2, 64), @@ -5822,6 +6185,7 @@ static struct bpf_test tests[] = {  		},  		.result = ACCEPT,  		.prog_type = BPF_PROG_TYPE_SCHED_CLS, +		.retval = 0 /* csum_diff of 64-byte packet */,  	},  	{  		"helper access to variable memory: size = 0 not allowed on NULL (!ARG_PTR_TO_MEM_OR_NULL)", @@ -5935,7 +6299,7 @@ static struct bpf_test tests[] = {  			BPF_STX_MEM(BPF_DW, BPF_REG_10, BPF_REG_0, -24),  			BPF_STX_MEM(BPF_DW, BPF_REG_10, BPF_REG_0, -16),  			BPF_STX_MEM(BPF_DW, BPF_REG_10, BPF_REG_0, -8), -			BPF_MOV64_IMM(BPF_REG_2, 0), +			BPF_MOV64_IMM(BPF_REG_2, 1),  			BPF_STX_MEM(BPF_DW, BPF_REG_10, BPF_REG_2, -128),  			BPF_LDX_MEM(BPF_DW, BPF_REG_2, BPF_REG_10, -128),  			BPF_ALU64_IMM(BPF_AND, BPF_REG_2, 63), @@ -6190,6 +6554,7 @@ static struct bpf_test tests[] = {  		},  		.prog_type = BPF_PROG_TYPE_SCHED_CLS,  		.result = ACCEPT, +		.retval = 42 /* ultimate return value */,  	},  	{  		"ld_ind: check calling conv, r1", @@ -6261,6 +6626,7 @@ static struct bpf_test tests[] = {  			BPF_EXIT_INSN(),  		},  		.result = ACCEPT, +		.retval = 1,  	},  	{  		"check bpf_perf_event_data->sample_period byte load permitted", @@ -7248,6 +7614,7 @@ static struct bpf_test tests[] = {  		},  		.fixup_map1 = { 3 },  		.result = ACCEPT, +		.retval = POINTER_VALUE,  		.result_unpriv = REJECT,  		.errstr_unpriv = "R0 leaks addr as return value"  	}, @@ -7268,6 +7635,7 @@ static struct bpf_test tests[] = {  		},  		.fixup_map1 = { 3 },  		.result = ACCEPT, +		.retval = POINTER_VALUE,  		.result_unpriv = REJECT,  		.errstr_unpriv = "R0 leaks addr as return value"  	}, @@ -7434,10 +7802,24 @@ static struct bpf_test tests[] = {  			},  			BPF_EXIT_INSN(),  		}, -		.errstr = "BPF_END uses reserved fields", +		.errstr = "unknown opcode d7",  		.result = REJECT,  	},  	{ +		"XDP, using ifindex from netdev", +		.insns = { +			BPF_MOV64_IMM(BPF_REG_0, 0), +			BPF_LDX_MEM(BPF_W, BPF_REG_2, BPF_REG_1, +				    offsetof(struct xdp_md, ingress_ifindex)), +			BPF_JMP_IMM(BPF_JLT, BPF_REG_2, 1, 1), +			BPF_MOV64_IMM(BPF_REG_0, 1), +			BPF_EXIT_INSN(), +		}, +		.result = ACCEPT, +		.prog_type = BPF_PROG_TYPE_XDP, +		.retval = 1, +	}, +	{  		"meta access, test1",  		.insns = {  			BPF_LDX_MEM(BPF_W, BPF_REG_2, BPF_REG_1, @@ -7709,6 +8091,7 @@ static struct bpf_test tests[] = {  			BPF_EXIT_INSN(),  		},  		.result = ACCEPT, +		.retval = TEST_DATA_LEN,  		.prog_type = BPF_PROG_TYPE_SCHED_CLS,  	},  	{ @@ -8656,6 +9039,7 @@ static struct bpf_test tests[] = {  			BPF_EXIT_INSN(),  		},  		.result = ACCEPT, +		.retval = 1,  	},  	{  		"check deducing bounds from const, 3", @@ -8826,6 +9210,1959 @@ static struct bpf_test tests[] = {  		.result = REJECT,  		.prog_type = BPF_PROG_TYPE_CGROUP_SOCK,  	}, +	{ +		"calls: basic sanity", +		.insns = { +			BPF_RAW_INSN(BPF_JMP | BPF_CALL, 0, 1, 0, 2), +			BPF_MOV64_IMM(BPF_REG_0, 1), +			BPF_EXIT_INSN(), +			BPF_MOV64_IMM(BPF_REG_0, 2), +			BPF_EXIT_INSN(), +		}, +		.prog_type = BPF_PROG_TYPE_TRACEPOINT, +		.result = ACCEPT, +	}, +	{ +		"calls: not on unpriviledged", +		.insns = { +			BPF_RAW_INSN(BPF_JMP | BPF_CALL, 0, 1, 0, 2), +			BPF_MOV64_IMM(BPF_REG_0, 1), +			BPF_EXIT_INSN(), +			BPF_MOV64_IMM(BPF_REG_0, 2), +			BPF_EXIT_INSN(), +		}, +		.errstr_unpriv = "function calls to other bpf functions are allowed for root only", +		.result_unpriv = REJECT, +		.result = ACCEPT, +		.retval = 1, +	}, +	{ +		"calls: div by 0 in subprog", +		.insns = { +			BPF_MOV64_REG(BPF_REG_6, BPF_REG_1), +			BPF_RAW_INSN(BPF_JMP | BPF_CALL, 0, 1, 0, 8), +			BPF_MOV64_REG(BPF_REG_1, BPF_REG_6), +			BPF_LDX_MEM(BPF_W, BPF_REG_1, BPF_REG_1, +				    offsetof(struct __sk_buff, data_end)), +			BPF_MOV64_REG(BPF_REG_2, BPF_REG_0), +			BPF_ALU64_IMM(BPF_ADD, BPF_REG_2, 8), +			BPF_JMP_REG(BPF_JGT, BPF_REG_2, BPF_REG_1, 1), +			BPF_LDX_MEM(BPF_B, BPF_REG_0, BPF_REG_0, 0), +			BPF_MOV64_IMM(BPF_REG_0, 1), +			BPF_EXIT_INSN(), +			BPF_MOV32_IMM(BPF_REG_2, 0), +			BPF_MOV32_IMM(BPF_REG_3, 1), +			BPF_ALU32_REG(BPF_DIV, BPF_REG_3, BPF_REG_2), +			BPF_LDX_MEM(BPF_W, BPF_REG_0, BPF_REG_1, +				    offsetof(struct __sk_buff, data)), +			BPF_EXIT_INSN(), +		}, +		.prog_type = BPF_PROG_TYPE_SCHED_CLS, +		.result = ACCEPT, +		.retval = 1, +	}, +	{ +		"calls: multiple ret types in subprog 1", +		.insns = { +			BPF_MOV64_REG(BPF_REG_6, BPF_REG_1), +			BPF_RAW_INSN(BPF_JMP | BPF_CALL, 0, 1, 0, 8), +			BPF_MOV64_REG(BPF_REG_1, BPF_REG_6), +			BPF_LDX_MEM(BPF_W, BPF_REG_1, BPF_REG_1, +				    offsetof(struct __sk_buff, data_end)), +			BPF_MOV64_REG(BPF_REG_2, BPF_REG_0), +			BPF_ALU64_IMM(BPF_ADD, BPF_REG_2, 8), +			BPF_JMP_REG(BPF_JGT, BPF_REG_2, BPF_REG_1, 1), +			BPF_LDX_MEM(BPF_B, BPF_REG_0, BPF_REG_0, 0), +			BPF_MOV64_IMM(BPF_REG_0, 1), +			BPF_EXIT_INSN(), +			BPF_LDX_MEM(BPF_W, BPF_REG_0, BPF_REG_1, +				    offsetof(struct __sk_buff, data)), +			BPF_JMP_IMM(BPF_JNE, BPF_REG_0, 0, 1), +			BPF_MOV32_IMM(BPF_REG_0, 42), +			BPF_EXIT_INSN(), +		}, +		.prog_type = BPF_PROG_TYPE_SCHED_CLS, +		.result = REJECT, +		.errstr = "R0 invalid mem access 'inv'", +	}, +	{ +		"calls: multiple ret types in subprog 2", +		.insns = { +			BPF_MOV64_REG(BPF_REG_6, BPF_REG_1), +			BPF_RAW_INSN(BPF_JMP | BPF_CALL, 0, 1, 0, 8), +			BPF_MOV64_REG(BPF_REG_1, BPF_REG_6), +			BPF_LDX_MEM(BPF_W, BPF_REG_1, BPF_REG_1, +				    offsetof(struct __sk_buff, data_end)), +			BPF_MOV64_REG(BPF_REG_2, BPF_REG_0), +			BPF_ALU64_IMM(BPF_ADD, BPF_REG_2, 8), +			BPF_JMP_REG(BPF_JGT, BPF_REG_2, BPF_REG_1, 1), +			BPF_LDX_MEM(BPF_B, BPF_REG_0, BPF_REG_0, 0), +			BPF_MOV64_IMM(BPF_REG_0, 1), +			BPF_EXIT_INSN(), +			BPF_LDX_MEM(BPF_W, BPF_REG_0, BPF_REG_1, +				    offsetof(struct __sk_buff, data)), +			BPF_MOV64_REG(BPF_REG_6, BPF_REG_1), +			BPF_JMP_IMM(BPF_JNE, BPF_REG_0, 0, 9), +			BPF_ST_MEM(BPF_DW, BPF_REG_10, -8, 0), +			BPF_MOV64_REG(BPF_REG_2, BPF_REG_10), +			BPF_ALU64_IMM(BPF_ADD, BPF_REG_2, -8), +			BPF_LD_MAP_FD(BPF_REG_1, 0), +			BPF_RAW_INSN(BPF_JMP | BPF_CALL, 0, 0, 0, +				     BPF_FUNC_map_lookup_elem), +			BPF_JMP_IMM(BPF_JNE, BPF_REG_0, 0, 1), +			BPF_LDX_MEM(BPF_W, BPF_REG_0, BPF_REG_6, +				    offsetof(struct __sk_buff, data)), +			BPF_ALU64_IMM(BPF_ADD, BPF_REG_0, 64), +			BPF_EXIT_INSN(), +		}, +		.prog_type = BPF_PROG_TYPE_SCHED_CLS, +		.fixup_map1 = { 16 }, +		.result = REJECT, +		.errstr = "R0 min value is outside of the array range", +	}, +	{ +		"calls: overlapping caller/callee", +		.insns = { +			BPF_RAW_INSN(BPF_JMP | BPF_CALL, 0, 1, 0, 0), +			BPF_MOV64_IMM(BPF_REG_0, 1), +			BPF_EXIT_INSN(), +		}, +		.prog_type = BPF_PROG_TYPE_TRACEPOINT, +		.errstr = "last insn is not an exit or jmp", +		.result = REJECT, +	}, +	{ +		"calls: wrong recursive calls", +		.insns = { +			BPF_JMP_IMM(BPF_JA, 0, 0, 4), +			BPF_JMP_IMM(BPF_JA, 0, 0, 4), +			BPF_RAW_INSN(BPF_JMP | BPF_CALL, 0, 1, 0, -2), +			BPF_RAW_INSN(BPF_JMP | BPF_CALL, 0, 1, 0, -2), +			BPF_RAW_INSN(BPF_JMP | BPF_CALL, 0, 1, 0, -2), +			BPF_MOV64_IMM(BPF_REG_0, 1), +			BPF_EXIT_INSN(), +		}, +		.prog_type = BPF_PROG_TYPE_TRACEPOINT, +		.errstr = "jump out of range", +		.result = REJECT, +	}, +	{ +		"calls: wrong src reg", +		.insns = { +			BPF_RAW_INSN(BPF_JMP | BPF_CALL, 0, 2, 0, 0), +			BPF_MOV64_IMM(BPF_REG_0, 1), +			BPF_EXIT_INSN(), +		}, +		.prog_type = BPF_PROG_TYPE_TRACEPOINT, +		.errstr = "BPF_CALL uses reserved fields", +		.result = REJECT, +	}, +	{ +		"calls: wrong off value", +		.insns = { +			BPF_RAW_INSN(BPF_JMP | BPF_CALL, 0, 1, -1, 2), +			BPF_MOV64_IMM(BPF_REG_0, 1), +			BPF_EXIT_INSN(), +			BPF_MOV64_IMM(BPF_REG_0, 2), +			BPF_EXIT_INSN(), +		}, +		.prog_type = BPF_PROG_TYPE_TRACEPOINT, +		.errstr = "BPF_CALL uses reserved fields", +		.result = REJECT, +	}, +	{ +		"calls: jump back loop", +		.insns = { +			BPF_RAW_INSN(BPF_JMP | BPF_CALL, 0, 1, 0, -1), +			BPF_MOV64_IMM(BPF_REG_0, 1), +			BPF_EXIT_INSN(), +		}, +		.prog_type = BPF_PROG_TYPE_TRACEPOINT, +		.errstr = "back-edge from insn 0 to 0", +		.result = REJECT, +	}, +	{ +		"calls: conditional call", +		.insns = { +			BPF_LDX_MEM(BPF_W, BPF_REG_0, BPF_REG_1, +				    offsetof(struct __sk_buff, mark)), +			BPF_JMP_IMM(BPF_JEQ, BPF_REG_0, 0, 3), +			BPF_RAW_INSN(BPF_JMP | BPF_CALL, 0, 1, 0, 2), +			BPF_MOV64_IMM(BPF_REG_0, 1), +			BPF_EXIT_INSN(), +			BPF_MOV64_IMM(BPF_REG_0, 2), +			BPF_EXIT_INSN(), +		}, +		.prog_type = BPF_PROG_TYPE_TRACEPOINT, +		.errstr = "jump out of range", +		.result = REJECT, +	}, +	{ +		"calls: conditional call 2", +		.insns = { +			BPF_LDX_MEM(BPF_W, BPF_REG_0, BPF_REG_1, +				    offsetof(struct __sk_buff, mark)), +			BPF_JMP_IMM(BPF_JEQ, BPF_REG_0, 0, 3), +			BPF_RAW_INSN(BPF_JMP | BPF_CALL, 0, 1, 0, 4), +			BPF_MOV64_IMM(BPF_REG_0, 1), +			BPF_EXIT_INSN(), +			BPF_MOV64_IMM(BPF_REG_0, 2), +			BPF_EXIT_INSN(), +			BPF_MOV64_IMM(BPF_REG_0, 3), +			BPF_EXIT_INSN(), +		}, +		.prog_type = BPF_PROG_TYPE_TRACEPOINT, +		.result = ACCEPT, +	}, +	{ +		"calls: conditional call 3", +		.insns = { +			BPF_LDX_MEM(BPF_W, BPF_REG_0, BPF_REG_1, +				    offsetof(struct __sk_buff, mark)), +			BPF_JMP_IMM(BPF_JEQ, BPF_REG_0, 0, 3), +			BPF_JMP_IMM(BPF_JA, 0, 0, 4), +			BPF_MOV64_IMM(BPF_REG_0, 1), +			BPF_EXIT_INSN(), +			BPF_MOV64_IMM(BPF_REG_0, 1), +			BPF_JMP_IMM(BPF_JA, 0, 0, -6), +			BPF_MOV64_IMM(BPF_REG_0, 3), +			BPF_JMP_IMM(BPF_JA, 0, 0, -6), +		}, +		.prog_type = BPF_PROG_TYPE_TRACEPOINT, +		.errstr = "back-edge from insn", +		.result = REJECT, +	}, +	{ +		"calls: conditional call 4", +		.insns = { +			BPF_LDX_MEM(BPF_W, BPF_REG_0, BPF_REG_1, +				    offsetof(struct __sk_buff, mark)), +			BPF_JMP_IMM(BPF_JEQ, BPF_REG_0, 0, 3), +			BPF_RAW_INSN(BPF_JMP | BPF_CALL, 0, 1, 0, 4), +			BPF_MOV64_IMM(BPF_REG_0, 1), +			BPF_EXIT_INSN(), +			BPF_MOV64_IMM(BPF_REG_0, 1), +			BPF_JMP_IMM(BPF_JA, 0, 0, -5), +			BPF_MOV64_IMM(BPF_REG_0, 3), +			BPF_EXIT_INSN(), +		}, +		.prog_type = BPF_PROG_TYPE_TRACEPOINT, +		.result = ACCEPT, +	}, +	{ +		"calls: conditional call 5", +		.insns = { +			BPF_LDX_MEM(BPF_W, BPF_REG_0, BPF_REG_1, +				    offsetof(struct __sk_buff, mark)), +			BPF_JMP_IMM(BPF_JEQ, BPF_REG_0, 0, 3), +			BPF_RAW_INSN(BPF_JMP | BPF_CALL, 0, 1, 0, 4), +			BPF_MOV64_IMM(BPF_REG_0, 1), +			BPF_EXIT_INSN(), +			BPF_MOV64_IMM(BPF_REG_0, 1), +			BPF_JMP_IMM(BPF_JA, 0, 0, -6), +			BPF_MOV64_IMM(BPF_REG_0, 3), +			BPF_EXIT_INSN(), +		}, +		.prog_type = BPF_PROG_TYPE_TRACEPOINT, +		.errstr = "back-edge from insn", +		.result = REJECT, +	}, +	{ +		"calls: conditional call 6", +		.insns = { +			BPF_RAW_INSN(BPF_JMP | BPF_CALL, 0, 1, 0, 2), +			BPF_JMP_IMM(BPF_JEQ, BPF_REG_0, 0, -2), +			BPF_EXIT_INSN(), +			BPF_LDX_MEM(BPF_W, BPF_REG_0, BPF_REG_1, +				    offsetof(struct __sk_buff, mark)), +			BPF_EXIT_INSN(), +		}, +		.prog_type = BPF_PROG_TYPE_TRACEPOINT, +		.errstr = "back-edge from insn", +		.result = REJECT, +	}, +	{ +		"calls: using r0 returned by callee", +		.insns = { +			BPF_RAW_INSN(BPF_JMP | BPF_CALL, 0, 1, 0, 1), +			BPF_EXIT_INSN(), +			BPF_MOV64_IMM(BPF_REG_0, 2), +			BPF_EXIT_INSN(), +		}, +		.prog_type = BPF_PROG_TYPE_TRACEPOINT, +		.result = ACCEPT, +	}, +	{ +		"calls: using uninit r0 from callee", +		.insns = { +			BPF_RAW_INSN(BPF_JMP | BPF_CALL, 0, 1, 0, 1), +			BPF_EXIT_INSN(), +			BPF_EXIT_INSN(), +		}, +		.prog_type = BPF_PROG_TYPE_TRACEPOINT, +		.errstr = "!read_ok", +		.result = REJECT, +	}, +	{ +		"calls: callee is using r1", +		.insns = { +			BPF_RAW_INSN(BPF_JMP | BPF_CALL, 0, 1, 0, 1), +			BPF_EXIT_INSN(), +			BPF_LDX_MEM(BPF_W, BPF_REG_0, BPF_REG_1, +				    offsetof(struct __sk_buff, len)), +			BPF_EXIT_INSN(), +		}, +		.prog_type = BPF_PROG_TYPE_SCHED_ACT, +		.result = ACCEPT, +		.retval = TEST_DATA_LEN, +	}, +	{ +		"calls: callee using args1", +		.insns = { +			BPF_RAW_INSN(BPF_JMP | BPF_CALL, 0, 1, 0, 1), +			BPF_EXIT_INSN(), +			BPF_MOV64_REG(BPF_REG_0, BPF_REG_1), +			BPF_EXIT_INSN(), +		}, +		.errstr_unpriv = "allowed for root only", +		.result_unpriv = REJECT, +		.result = ACCEPT, +		.retval = POINTER_VALUE, +	}, +	{ +		"calls: callee using wrong args2", +		.insns = { +			BPF_RAW_INSN(BPF_JMP | BPF_CALL, 0, 1, 0, 1), +			BPF_EXIT_INSN(), +			BPF_MOV64_REG(BPF_REG_0, BPF_REG_2), +			BPF_EXIT_INSN(), +		}, +		.prog_type = BPF_PROG_TYPE_TRACEPOINT, +		.errstr = "R2 !read_ok", +		.result = REJECT, +	}, +	{ +		"calls: callee using two args", +		.insns = { +			BPF_MOV64_REG(BPF_REG_6, BPF_REG_1), +			BPF_LDX_MEM(BPF_W, BPF_REG_1, BPF_REG_6, +				    offsetof(struct __sk_buff, len)), +			BPF_LDX_MEM(BPF_W, BPF_REG_2, BPF_REG_6, +				    offsetof(struct __sk_buff, len)), +			BPF_RAW_INSN(BPF_JMP | BPF_CALL, 0, 1, 0, 1), +			BPF_EXIT_INSN(), +			BPF_MOV64_REG(BPF_REG_0, BPF_REG_1), +			BPF_ALU64_REG(BPF_ADD, BPF_REG_0, BPF_REG_2), +			BPF_EXIT_INSN(), +		}, +		.errstr_unpriv = "allowed for root only", +		.result_unpriv = REJECT, +		.result = ACCEPT, +		.retval = TEST_DATA_LEN + TEST_DATA_LEN - ETH_HLEN - ETH_HLEN, +	}, +	{ +		"calls: callee changing pkt pointers", +		.insns = { +			BPF_LDX_MEM(BPF_W, BPF_REG_6, BPF_REG_1, +				    offsetof(struct xdp_md, data)), +			BPF_LDX_MEM(BPF_W, BPF_REG_7, BPF_REG_1, +				    offsetof(struct xdp_md, data_end)), +			BPF_MOV64_REG(BPF_REG_8, BPF_REG_6), +			BPF_ALU64_IMM(BPF_ADD, BPF_REG_8, 8), +			BPF_JMP_REG(BPF_JGT, BPF_REG_8, BPF_REG_7, 2), +			BPF_RAW_INSN(BPF_JMP | BPF_CALL, 0, 1, 0, 3), +			/* clear_all_pkt_pointers() has to walk all frames +			 * to make sure that pkt pointers in the caller +			 * are cleared when callee is calling a helper that +			 * adjusts packet size +			 */ +			BPF_LDX_MEM(BPF_DW, BPF_REG_0, BPF_REG_6, 0), +			BPF_MOV32_IMM(BPF_REG_0, 0), +			BPF_EXIT_INSN(), +			BPF_MOV64_IMM(BPF_REG_2, 0), +			BPF_RAW_INSN(BPF_JMP | BPF_CALL, 0, 0, 0, +				     BPF_FUNC_xdp_adjust_head), +			BPF_EXIT_INSN(), +		}, +		.result = REJECT, +		.errstr = "R6 invalid mem access 'inv'", +		.prog_type = BPF_PROG_TYPE_XDP, +	}, +	{ +		"calls: two calls with args", +		.insns = { +			BPF_RAW_INSN(BPF_JMP | BPF_CALL, 0, 1, 0, 1), +			BPF_EXIT_INSN(), +			BPF_MOV64_REG(BPF_REG_6, BPF_REG_1), +			BPF_RAW_INSN(BPF_JMP | BPF_CALL, 0, 1, 0, 6), +			BPF_MOV64_REG(BPF_REG_7, BPF_REG_0), +			BPF_MOV64_REG(BPF_REG_1, BPF_REG_6), +			BPF_RAW_INSN(BPF_JMP | BPF_CALL, 0, 1, 0, 3), +			BPF_ALU64_REG(BPF_ADD, BPF_REG_7, BPF_REG_0), +			BPF_MOV64_REG(BPF_REG_0, BPF_REG_7), +			BPF_EXIT_INSN(), +			BPF_LDX_MEM(BPF_W, BPF_REG_0, BPF_REG_1, +				    offsetof(struct __sk_buff, len)), +			BPF_EXIT_INSN(), +		}, +		.prog_type = BPF_PROG_TYPE_SCHED_CLS, +		.result = ACCEPT, +		.retval = TEST_DATA_LEN + TEST_DATA_LEN, +	}, +	{ +		"calls: calls with stack arith", +		.insns = { +			BPF_MOV64_REG(BPF_REG_2, BPF_REG_10), +			BPF_ALU64_IMM(BPF_ADD, BPF_REG_2, -64), +			BPF_RAW_INSN(BPF_JMP | BPF_CALL, 0, 1, 0, 1), +			BPF_EXIT_INSN(), +			BPF_ALU64_IMM(BPF_ADD, BPF_REG_2, -64), +			BPF_RAW_INSN(BPF_JMP | BPF_CALL, 0, 1, 0, 1), +			BPF_EXIT_INSN(), +			BPF_ALU64_IMM(BPF_ADD, BPF_REG_2, -64), +			BPF_MOV64_IMM(BPF_REG_0, 42), +			BPF_STX_MEM(BPF_DW, BPF_REG_2, BPF_REG_0, 0), +			BPF_EXIT_INSN(), +		}, +		.prog_type = BPF_PROG_TYPE_SCHED_CLS, +		.result = ACCEPT, +		.retval = 42, +	}, +	{ +		"calls: calls with misaligned stack access", +		.insns = { +			BPF_MOV64_REG(BPF_REG_2, BPF_REG_10), +			BPF_ALU64_IMM(BPF_ADD, BPF_REG_2, -63), +			BPF_RAW_INSN(BPF_JMP | BPF_CALL, 0, 1, 0, 1), +			BPF_EXIT_INSN(), +			BPF_ALU64_IMM(BPF_ADD, BPF_REG_2, -61), +			BPF_RAW_INSN(BPF_JMP | BPF_CALL, 0, 1, 0, 1), +			BPF_EXIT_INSN(), +			BPF_ALU64_IMM(BPF_ADD, BPF_REG_2, -63), +			BPF_MOV64_IMM(BPF_REG_0, 42), +			BPF_STX_MEM(BPF_DW, BPF_REG_2, BPF_REG_0, 0), +			BPF_EXIT_INSN(), +		}, +		.prog_type = BPF_PROG_TYPE_SCHED_CLS, +		.flags = F_LOAD_WITH_STRICT_ALIGNMENT, +		.errstr = "misaligned stack access", +		.result = REJECT, +	}, +	{ +		"calls: calls control flow, jump test", +		.insns = { +			BPF_MOV64_IMM(BPF_REG_0, 42), +			BPF_JMP_IMM(BPF_JA, 0, 0, 2), +			BPF_MOV64_IMM(BPF_REG_0, 43), +			BPF_JMP_IMM(BPF_JA, 0, 0, 1), +			BPF_JMP_IMM(BPF_JA, 0, 0, -3), +			BPF_EXIT_INSN(), +		}, +		.prog_type = BPF_PROG_TYPE_SCHED_CLS, +		.result = ACCEPT, +		.retval = 43, +	}, +	{ +		"calls: calls control flow, jump test 2", +		.insns = { +			BPF_MOV64_IMM(BPF_REG_0, 42), +			BPF_JMP_IMM(BPF_JA, 0, 0, 2), +			BPF_MOV64_IMM(BPF_REG_0, 43), +			BPF_JMP_IMM(BPF_JA, 0, 0, 1), +			BPF_RAW_INSN(BPF_JMP | BPF_CALL, 0, 1, 0, -3), +			BPF_EXIT_INSN(), +		}, +		.prog_type = BPF_PROG_TYPE_SCHED_CLS, +		.errstr = "jump out of range from insn 1 to 4", +		.result = REJECT, +	}, +	{ +		"calls: two calls with bad jump", +		.insns = { +			BPF_RAW_INSN(BPF_JMP | BPF_CALL, 0, 1, 0, 1), +			BPF_EXIT_INSN(), +			BPF_MOV64_REG(BPF_REG_6, BPF_REG_1), +			BPF_RAW_INSN(BPF_JMP | BPF_CALL, 0, 1, 0, 6), +			BPF_MOV64_REG(BPF_REG_7, BPF_REG_0), +			BPF_MOV64_REG(BPF_REG_1, BPF_REG_6), +			BPF_RAW_INSN(BPF_JMP | BPF_CALL, 0, 1, 0, 3), +			BPF_ALU64_REG(BPF_ADD, BPF_REG_7, BPF_REG_0), +			BPF_MOV64_REG(BPF_REG_0, BPF_REG_7), +			BPF_EXIT_INSN(), +			BPF_LDX_MEM(BPF_W, BPF_REG_0, BPF_REG_1, +				    offsetof(struct __sk_buff, len)), +			BPF_JMP_IMM(BPF_JEQ, BPF_REG_0, 0, -3), +			BPF_EXIT_INSN(), +		}, +		.prog_type = BPF_PROG_TYPE_TRACEPOINT, +		.errstr = "jump out of range from insn 11 to 9", +		.result = REJECT, +	}, +	{ +		"calls: recursive call. test1", +		.insns = { +			BPF_RAW_INSN(BPF_JMP | BPF_CALL, 0, 1, 0, 1), +			BPF_EXIT_INSN(), +			BPF_RAW_INSN(BPF_JMP | BPF_CALL, 0, 1, 0, -1), +			BPF_EXIT_INSN(), +		}, +		.prog_type = BPF_PROG_TYPE_TRACEPOINT, +		.errstr = "back-edge", +		.result = REJECT, +	}, +	{ +		"calls: recursive call. test2", +		.insns = { +			BPF_RAW_INSN(BPF_JMP | BPF_CALL, 0, 1, 0, 1), +			BPF_EXIT_INSN(), +			BPF_RAW_INSN(BPF_JMP | BPF_CALL, 0, 1, 0, -3), +			BPF_EXIT_INSN(), +		}, +		.prog_type = BPF_PROG_TYPE_TRACEPOINT, +		.errstr = "back-edge", +		.result = REJECT, +	}, +	{ +		"calls: unreachable code", +		.insns = { +			BPF_RAW_INSN(BPF_JMP | BPF_CALL, 0, 1, 0, 1), +			BPF_EXIT_INSN(), +			BPF_RAW_INSN(BPF_JMP | BPF_CALL, 0, 1, 0, 1), +			BPF_EXIT_INSN(), +			BPF_MOV64_IMM(BPF_REG_0, 0), +			BPF_EXIT_INSN(), +			BPF_MOV64_IMM(BPF_REG_0, 0), +			BPF_EXIT_INSN(), +		}, +		.prog_type = BPF_PROG_TYPE_TRACEPOINT, +		.errstr = "unreachable insn 6", +		.result = REJECT, +	}, +	{ +		"calls: invalid call", +		.insns = { +			BPF_RAW_INSN(BPF_JMP | BPF_CALL, 0, 1, 0, 1), +			BPF_EXIT_INSN(), +			BPF_RAW_INSN(BPF_JMP | BPF_CALL, 0, 1, 0, -4), +			BPF_EXIT_INSN(), +		}, +		.prog_type = BPF_PROG_TYPE_TRACEPOINT, +		.errstr = "invalid destination", +		.result = REJECT, +	}, +	{ +		"calls: invalid call 2", +		.insns = { +			BPF_RAW_INSN(BPF_JMP | BPF_CALL, 0, 1, 0, 1), +			BPF_EXIT_INSN(), +			BPF_RAW_INSN(BPF_JMP | BPF_CALL, 0, 1, 0, 0x7fffffff), +			BPF_EXIT_INSN(), +		}, +		.prog_type = BPF_PROG_TYPE_TRACEPOINT, +		.errstr = "invalid destination", +		.result = REJECT, +	}, +	{ +		"calls: jumping across function bodies. test1", +		.insns = { +			BPF_RAW_INSN(BPF_JMP | BPF_CALL, 0, 1, 0, 2), +			BPF_MOV64_IMM(BPF_REG_0, 0), +			BPF_EXIT_INSN(), +			BPF_JMP_IMM(BPF_JEQ, BPF_REG_1, 0, -3), +			BPF_EXIT_INSN(), +		}, +		.prog_type = BPF_PROG_TYPE_TRACEPOINT, +		.errstr = "jump out of range", +		.result = REJECT, +	}, +	{ +		"calls: jumping across function bodies. test2", +		.insns = { +			BPF_JMP_IMM(BPF_JEQ, BPF_REG_1, 0, 3), +			BPF_RAW_INSN(BPF_JMP | BPF_CALL, 0, 1, 0, 2), +			BPF_MOV64_IMM(BPF_REG_0, 0), +			BPF_EXIT_INSN(), +			BPF_EXIT_INSN(), +		}, +		.prog_type = BPF_PROG_TYPE_TRACEPOINT, +		.errstr = "jump out of range", +		.result = REJECT, +	}, +	{ +		"calls: call without exit", +		.insns = { +			BPF_RAW_INSN(BPF_JMP | BPF_CALL, 0, 1, 0, 1), +			BPF_EXIT_INSN(), +			BPF_RAW_INSN(BPF_JMP | BPF_CALL, 0, 1, 0, 1), +			BPF_EXIT_INSN(), +			BPF_MOV64_IMM(BPF_REG_0, 0), +			BPF_JMP_IMM(BPF_JEQ, BPF_REG_1, 0, -2), +		}, +		.prog_type = BPF_PROG_TYPE_TRACEPOINT, +		.errstr = "not an exit", +		.result = REJECT, +	}, +	{ +		"calls: call into middle of ld_imm64", +		.insns = { +			BPF_RAW_INSN(BPF_JMP | BPF_CALL, 0, 1, 0, 3), +			BPF_RAW_INSN(BPF_JMP | BPF_CALL, 0, 1, 0, 3), +			BPF_MOV64_IMM(BPF_REG_0, 0), +			BPF_EXIT_INSN(), +			BPF_LD_IMM64(BPF_REG_0, 0), +			BPF_EXIT_INSN(), +		}, +		.prog_type = BPF_PROG_TYPE_TRACEPOINT, +		.errstr = "last insn", +		.result = REJECT, +	}, +	{ +		"calls: call into middle of other call", +		.insns = { +			BPF_RAW_INSN(BPF_JMP | BPF_CALL, 0, 1, 0, 3), +			BPF_RAW_INSN(BPF_JMP | BPF_CALL, 0, 1, 0, 3), +			BPF_MOV64_IMM(BPF_REG_0, 0), +			BPF_EXIT_INSN(), +			BPF_MOV64_IMM(BPF_REG_0, 0), +			BPF_MOV64_IMM(BPF_REG_0, 0), +			BPF_EXIT_INSN(), +		}, +		.prog_type = BPF_PROG_TYPE_TRACEPOINT, +		.errstr = "last insn", +		.result = REJECT, +	}, +	{ +		"calls: ld_abs with changing ctx data in callee", +		.insns = { +			BPF_MOV64_REG(BPF_REG_6, BPF_REG_1), +			BPF_LD_ABS(BPF_B, 0), +			BPF_LD_ABS(BPF_H, 0), +			BPF_LD_ABS(BPF_W, 0), +			BPF_MOV64_REG(BPF_REG_7, BPF_REG_6), +			BPF_RAW_INSN(BPF_JMP | BPF_CALL, 0, 1, 0, 5), +			BPF_MOV64_REG(BPF_REG_6, BPF_REG_7), +			BPF_LD_ABS(BPF_B, 0), +			BPF_LD_ABS(BPF_H, 0), +			BPF_LD_ABS(BPF_W, 0), +			BPF_EXIT_INSN(), +			BPF_MOV64_IMM(BPF_REG_2, 1), +			BPF_MOV64_IMM(BPF_REG_3, 2), +			BPF_RAW_INSN(BPF_JMP | BPF_CALL, 0, 0, 0, +				     BPF_FUNC_skb_vlan_push), +			BPF_EXIT_INSN(), +		}, +		.prog_type = BPF_PROG_TYPE_SCHED_CLS, +		.errstr = "BPF_LD_[ABS|IND] instructions cannot be mixed", +		.result = REJECT, +	}, +	{ +		"calls: two calls with bad fallthrough", +		.insns = { +			BPF_RAW_INSN(BPF_JMP | BPF_CALL, 0, 1, 0, 1), +			BPF_EXIT_INSN(), +			BPF_MOV64_REG(BPF_REG_6, BPF_REG_1), +			BPF_RAW_INSN(BPF_JMP | BPF_CALL, 0, 1, 0, 6), +			BPF_MOV64_REG(BPF_REG_7, BPF_REG_0), +			BPF_MOV64_REG(BPF_REG_1, BPF_REG_6), +			BPF_RAW_INSN(BPF_JMP | BPF_CALL, 0, 1, 0, 3), +			BPF_ALU64_REG(BPF_ADD, BPF_REG_7, BPF_REG_0), +			BPF_MOV64_REG(BPF_REG_0, BPF_REG_7), +			BPF_MOV64_REG(BPF_REG_0, BPF_REG_0), +			BPF_LDX_MEM(BPF_W, BPF_REG_0, BPF_REG_1, +				    offsetof(struct __sk_buff, len)), +			BPF_EXIT_INSN(), +		}, +		.prog_type = BPF_PROG_TYPE_TRACEPOINT, +		.errstr = "not an exit", +		.result = REJECT, +	}, +	{ +		"calls: two calls with stack read", +		.insns = { +			BPF_ST_MEM(BPF_DW, BPF_REG_10, -8, 0), +			BPF_MOV64_REG(BPF_REG_1, BPF_REG_10), +			BPF_ALU64_IMM(BPF_ADD, BPF_REG_1, -8), +			BPF_RAW_INSN(BPF_JMP | BPF_CALL, 0, 1, 0, 1), +			BPF_EXIT_INSN(), +			BPF_MOV64_REG(BPF_REG_6, BPF_REG_1), +			BPF_RAW_INSN(BPF_JMP | BPF_CALL, 0, 1, 0, 6), +			BPF_MOV64_REG(BPF_REG_7, BPF_REG_0), +			BPF_MOV64_REG(BPF_REG_1, BPF_REG_6), +			BPF_RAW_INSN(BPF_JMP | BPF_CALL, 0, 1, 0, 3), +			BPF_ALU64_REG(BPF_ADD, BPF_REG_7, BPF_REG_0), +			BPF_MOV64_REG(BPF_REG_0, BPF_REG_7), +			BPF_EXIT_INSN(), +			BPF_LDX_MEM(BPF_W, BPF_REG_0, BPF_REG_1, 0), +			BPF_EXIT_INSN(), +		}, +		.prog_type = BPF_PROG_TYPE_XDP, +		.result = ACCEPT, +	}, +	{ +		"calls: two calls with stack write", +		.insns = { +			/* main prog */ +			BPF_ST_MEM(BPF_DW, BPF_REG_10, -8, 0), +			BPF_MOV64_REG(BPF_REG_1, BPF_REG_10), +			BPF_ALU64_IMM(BPF_ADD, BPF_REG_1, -8), +			BPF_MOV64_REG(BPF_REG_2, BPF_REG_10), +			BPF_ALU64_IMM(BPF_ADD, BPF_REG_2, -16), +			BPF_RAW_INSN(BPF_JMP | BPF_CALL, 0, 1, 0, 2), +			BPF_LDX_MEM(BPF_DW, BPF_REG_0, BPF_REG_10, -16), +			BPF_EXIT_INSN(), + +			/* subprog 1 */ +			BPF_MOV64_REG(BPF_REG_6, BPF_REG_1), +			BPF_MOV64_REG(BPF_REG_7, BPF_REG_2), +			BPF_RAW_INSN(BPF_JMP | BPF_CALL, 0, 1, 0, 7), +			BPF_MOV64_REG(BPF_REG_8, BPF_REG_0), +			BPF_MOV64_REG(BPF_REG_1, BPF_REG_6), +			BPF_RAW_INSN(BPF_JMP | BPF_CALL, 0, 1, 0, 4), +			BPF_ALU64_REG(BPF_ADD, BPF_REG_8, BPF_REG_0), +			BPF_MOV64_REG(BPF_REG_0, BPF_REG_8), +			/* write into stack frame of main prog */ +			BPF_STX_MEM(BPF_DW, BPF_REG_7, BPF_REG_0, 0), +			BPF_EXIT_INSN(), + +			/* subprog 2 */ +			/* read from stack frame of main prog */ +			BPF_LDX_MEM(BPF_W, BPF_REG_0, BPF_REG_1, 0), +			BPF_EXIT_INSN(), +		}, +		.prog_type = BPF_PROG_TYPE_XDP, +		.result = ACCEPT, +	}, +	{ +		"calls: stack overflow using two frames (pre-call access)", +		.insns = { +			/* prog 1 */ +			BPF_ST_MEM(BPF_B, BPF_REG_10, -300, 0), +			BPF_RAW_INSN(BPF_JMP|BPF_CALL, 0, 1, 0, 1), +			BPF_EXIT_INSN(), + +			/* prog 2 */ +			BPF_ST_MEM(BPF_B, BPF_REG_10, -300, 0), +			BPF_MOV64_IMM(BPF_REG_0, 0), +			BPF_EXIT_INSN(), +		}, +		.prog_type = BPF_PROG_TYPE_XDP, +		.errstr = "combined stack size", +		.result = REJECT, +	}, +	{ +		"calls: stack overflow using two frames (post-call access)", +		.insns = { +			/* prog 1 */ +			BPF_RAW_INSN(BPF_JMP|BPF_CALL, 0, 1, 0, 2), +			BPF_ST_MEM(BPF_B, BPF_REG_10, -300, 0), +			BPF_EXIT_INSN(), + +			/* prog 2 */ +			BPF_ST_MEM(BPF_B, BPF_REG_10, -300, 0), +			BPF_MOV64_IMM(BPF_REG_0, 0), +			BPF_EXIT_INSN(), +		}, +		.prog_type = BPF_PROG_TYPE_XDP, +		.errstr = "combined stack size", +		.result = REJECT, +	}, +	{ +		"calls: stack depth check using three frames. test1", +		.insns = { +			/* main */ +			BPF_RAW_INSN(BPF_JMP|BPF_CALL, 0, 1, 0, 4), /* call A */ +			BPF_RAW_INSN(BPF_JMP|BPF_CALL, 0, 1, 0, 5), /* call B */ +			BPF_ST_MEM(BPF_B, BPF_REG_10, -32, 0), +			BPF_MOV64_IMM(BPF_REG_0, 0), +			BPF_EXIT_INSN(), +			/* A */ +			BPF_ST_MEM(BPF_B, BPF_REG_10, -256, 0), +			BPF_EXIT_INSN(), +			/* B */ +			BPF_RAW_INSN(BPF_JMP|BPF_CALL, 0, 1, 0, -3), /* call A */ +			BPF_ST_MEM(BPF_B, BPF_REG_10, -64, 0), +			BPF_EXIT_INSN(), +		}, +		.prog_type = BPF_PROG_TYPE_XDP, +		/* stack_main=32, stack_A=256, stack_B=64 +		 * and max(main+A, main+A+B) < 512 +		 */ +		.result = ACCEPT, +	}, +	{ +		"calls: stack depth check using three frames. test2", +		.insns = { +			/* main */ +			BPF_RAW_INSN(BPF_JMP|BPF_CALL, 0, 1, 0, 4), /* call A */ +			BPF_RAW_INSN(BPF_JMP|BPF_CALL, 0, 1, 0, 5), /* call B */ +			BPF_ST_MEM(BPF_B, BPF_REG_10, -32, 0), +			BPF_MOV64_IMM(BPF_REG_0, 0), +			BPF_EXIT_INSN(), +			/* A */ +			BPF_ST_MEM(BPF_B, BPF_REG_10, -64, 0), +			BPF_EXIT_INSN(), +			/* B */ +			BPF_RAW_INSN(BPF_JMP|BPF_CALL, 0, 1, 0, -3), /* call A */ +			BPF_ST_MEM(BPF_B, BPF_REG_10, -256, 0), +			BPF_EXIT_INSN(), +		}, +		.prog_type = BPF_PROG_TYPE_XDP, +		/* stack_main=32, stack_A=64, stack_B=256 +		 * and max(main+A, main+A+B) < 512 +		 */ +		.result = ACCEPT, +	}, +	{ +		"calls: stack depth check using three frames. test3", +		.insns = { +			/* main */ +			BPF_MOV64_REG(BPF_REG_6, BPF_REG_1), +			BPF_RAW_INSN(BPF_JMP|BPF_CALL, 0, 1, 0, 6), /* call A */ +			BPF_MOV64_REG(BPF_REG_1, BPF_REG_6), +			BPF_RAW_INSN(BPF_JMP|BPF_CALL, 0, 1, 0, 8), /* call B */ +			BPF_JMP_IMM(BPF_JGE, BPF_REG_6, 0, 1), +			BPF_ST_MEM(BPF_B, BPF_REG_10, -64, 0), +			BPF_MOV64_IMM(BPF_REG_0, 0), +			BPF_EXIT_INSN(), +			/* A */ +			BPF_JMP_IMM(BPF_JLT, BPF_REG_1, 10, 1), +			BPF_EXIT_INSN(), +			BPF_ST_MEM(BPF_B, BPF_REG_10, -224, 0), +			BPF_JMP_IMM(BPF_JA, 0, 0, -3), +			/* B */ +			BPF_JMP_IMM(BPF_JGT, BPF_REG_1, 2, 1), +			BPF_RAW_INSN(BPF_JMP|BPF_CALL, 0, 1, 0, -6), /* call A */ +			BPF_ST_MEM(BPF_B, BPF_REG_10, -256, 0), +			BPF_EXIT_INSN(), +		}, +		.prog_type = BPF_PROG_TYPE_XDP, +		/* stack_main=64, stack_A=224, stack_B=256 +		 * and max(main+A, main+A+B) > 512 +		 */ +		.errstr = "combined stack", +		.result = REJECT, +	}, +	{ +		"calls: stack depth check using three frames. test4", +		/* void main(void) { +		 *   func1(0); +		 *   func1(1); +		 *   func2(1); +		 * } +		 * void func1(int alloc_or_recurse) { +		 *   if (alloc_or_recurse) { +		 *     frame_pointer[-300] = 1; +		 *   } else { +		 *     func2(alloc_or_recurse); +		 *   } +		 * } +		 * void func2(int alloc_or_recurse) { +		 *   if (alloc_or_recurse) { +		 *     frame_pointer[-300] = 1; +		 *   } +		 * } +		 */ +		.insns = { +			/* main */ +			BPF_MOV64_IMM(BPF_REG_1, 0), +			BPF_RAW_INSN(BPF_JMP|BPF_CALL, 0, 1, 0, 6), /* call A */ +			BPF_MOV64_IMM(BPF_REG_1, 1), +			BPF_RAW_INSN(BPF_JMP|BPF_CALL, 0, 1, 0, 4), /* call A */ +			BPF_MOV64_IMM(BPF_REG_1, 1), +			BPF_RAW_INSN(BPF_JMP|BPF_CALL, 0, 1, 0, 7), /* call B */ +			BPF_MOV64_IMM(BPF_REG_0, 0), +			BPF_EXIT_INSN(), +			/* A */ +			BPF_JMP_IMM(BPF_JEQ, BPF_REG_1, 0, 2), +			BPF_ST_MEM(BPF_B, BPF_REG_10, -300, 0), +			BPF_EXIT_INSN(), +			BPF_RAW_INSN(BPF_JMP|BPF_CALL, 0, 1, 0, 1), /* call B */ +			BPF_EXIT_INSN(), +			/* B */ +			BPF_JMP_IMM(BPF_JEQ, BPF_REG_1, 0, 1), +			BPF_ST_MEM(BPF_B, BPF_REG_10, -300, 0), +			BPF_EXIT_INSN(), +		}, +		.prog_type = BPF_PROG_TYPE_XDP, +		.result = REJECT, +		.errstr = "combined stack", +	}, +	{ +		"calls: stack depth check using three frames. test5", +		.insns = { +			/* main */ +			BPF_RAW_INSN(BPF_JMP|BPF_CALL, 0, 1, 0, 1), /* call A */ +			BPF_EXIT_INSN(), +			/* A */ +			BPF_RAW_INSN(BPF_JMP|BPF_CALL, 0, 1, 0, 1), /* call B */ +			BPF_EXIT_INSN(), +			/* B */ +			BPF_RAW_INSN(BPF_JMP|BPF_CALL, 0, 1, 0, 1), /* call C */ +			BPF_EXIT_INSN(), +			/* C */ +			BPF_RAW_INSN(BPF_JMP|BPF_CALL, 0, 1, 0, 1), /* call D */ +			BPF_EXIT_INSN(), +			/* D */ +			BPF_RAW_INSN(BPF_JMP|BPF_CALL, 0, 1, 0, 1), /* call E */ +			BPF_EXIT_INSN(), +			/* E */ +			BPF_RAW_INSN(BPF_JMP|BPF_CALL, 0, 1, 0, 1), /* call F */ +			BPF_EXIT_INSN(), +			/* F */ +			BPF_RAW_INSN(BPF_JMP|BPF_CALL, 0, 1, 0, 1), /* call G */ +			BPF_EXIT_INSN(), +			/* G */ +			BPF_RAW_INSN(BPF_JMP|BPF_CALL, 0, 1, 0, 1), /* call H */ +			BPF_EXIT_INSN(), +			/* H */ +			BPF_MOV64_IMM(BPF_REG_0, 0), +			BPF_EXIT_INSN(), +		}, +		.prog_type = BPF_PROG_TYPE_XDP, +		.errstr = "call stack", +		.result = REJECT, +	}, +	{ +		"calls: spill into caller stack frame", +		.insns = { +			BPF_ST_MEM(BPF_DW, BPF_REG_10, -8, 0), +			BPF_MOV64_REG(BPF_REG_1, BPF_REG_10), +			BPF_ALU64_IMM(BPF_ADD, BPF_REG_1, -8), +			BPF_RAW_INSN(BPF_JMP | BPF_CALL, 0, 1, 0, 1), +			BPF_EXIT_INSN(), +			BPF_STX_MEM(BPF_DW, BPF_REG_1, BPF_REG_1, 0), +			BPF_MOV64_IMM(BPF_REG_0, 0), +			BPF_EXIT_INSN(), +		}, +		.prog_type = BPF_PROG_TYPE_XDP, +		.errstr = "cannot spill", +		.result = REJECT, +	}, +	{ +		"calls: write into caller stack frame", +		.insns = { +			BPF_MOV64_REG(BPF_REG_1, BPF_REG_10), +			BPF_ALU64_IMM(BPF_ADD, BPF_REG_1, -8), +			BPF_MOV64_REG(BPF_REG_6, BPF_REG_1), +			BPF_RAW_INSN(BPF_JMP | BPF_CALL, 0, 1, 0, 2), +			BPF_LDX_MEM(BPF_DW, BPF_REG_0, BPF_REG_6, 0), +			BPF_EXIT_INSN(), +			BPF_ST_MEM(BPF_DW, BPF_REG_1, 0, 42), +			BPF_MOV64_IMM(BPF_REG_0, 0), +			BPF_EXIT_INSN(), +		}, +		.prog_type = BPF_PROG_TYPE_XDP, +		.result = ACCEPT, +		.retval = 42, +	}, +	{ +		"calls: write into callee stack frame", +		.insns = { +			BPF_RAW_INSN(BPF_JMP | BPF_CALL, 0, 1, 0, 2), +			BPF_ST_MEM(BPF_DW, BPF_REG_0, 0, 42), +			BPF_EXIT_INSN(), +			BPF_MOV64_REG(BPF_REG_0, BPF_REG_10), +			BPF_ALU64_IMM(BPF_ADD, BPF_REG_0, -8), +			BPF_EXIT_INSN(), +		}, +		.prog_type = BPF_PROG_TYPE_XDP, +		.errstr = "cannot return stack pointer", +		.result = REJECT, +	}, +	{ +		"calls: two calls with stack write and void return", +		.insns = { +			/* main prog */ +			BPF_ST_MEM(BPF_DW, BPF_REG_10, -8, 0), +			BPF_MOV64_REG(BPF_REG_1, BPF_REG_10), +			BPF_ALU64_IMM(BPF_ADD, BPF_REG_1, -8), +			BPF_MOV64_REG(BPF_REG_2, BPF_REG_10), +			BPF_ALU64_IMM(BPF_ADD, BPF_REG_2, -16), +			BPF_RAW_INSN(BPF_JMP | BPF_CALL, 0, 1, 0, 2), +			BPF_LDX_MEM(BPF_DW, BPF_REG_0, BPF_REG_10, -16), +			BPF_EXIT_INSN(), + +			/* subprog 1 */ +			BPF_MOV64_REG(BPF_REG_6, BPF_REG_1), +			BPF_MOV64_REG(BPF_REG_7, BPF_REG_2), +			BPF_RAW_INSN(BPF_JMP | BPF_CALL, 0, 1, 0, 3), +			BPF_MOV64_REG(BPF_REG_1, BPF_REG_7), +			BPF_RAW_INSN(BPF_JMP | BPF_CALL, 0, 1, 0, 1), +			BPF_EXIT_INSN(), + +			/* subprog 2 */ +			/* write into stack frame of main prog */ +			BPF_ST_MEM(BPF_DW, BPF_REG_1, 0, 0), +			BPF_EXIT_INSN(), /* void return */ +		}, +		.prog_type = BPF_PROG_TYPE_XDP, +		.result = ACCEPT, +	}, +	{ +		"calls: ambiguous return value", +		.insns = { +			BPF_MOV64_REG(BPF_REG_6, BPF_REG_1), +			BPF_RAW_INSN(BPF_JMP | BPF_CALL, 0, 1, 0, 5), +			BPF_MOV64_REG(BPF_REG_1, BPF_REG_0), +			BPF_MOV64_REG(BPF_REG_1, BPF_REG_6), +			BPF_RAW_INSN(BPF_JMP | BPF_CALL, 0, 1, 0, 2), +			BPF_MOV64_REG(BPF_REG_1, BPF_REG_0), +			BPF_EXIT_INSN(), +			BPF_JMP_IMM(BPF_JEQ, BPF_REG_1, 0, 1), +			BPF_MOV64_IMM(BPF_REG_0, 0), +			BPF_EXIT_INSN(), +		}, +		.errstr_unpriv = "allowed for root only", +		.result_unpriv = REJECT, +		.errstr = "R0 !read_ok", +		.result = REJECT, +	}, +	{ +		"calls: two calls that return map_value", +		.insns = { +			/* main prog */ +			/* pass fp-16, fp-8 into a function */ +			BPF_MOV64_REG(BPF_REG_1, BPF_REG_10), +			BPF_ALU64_IMM(BPF_ADD, BPF_REG_1, -8), +			BPF_MOV64_REG(BPF_REG_2, BPF_REG_10), +			BPF_ALU64_IMM(BPF_ADD, BPF_REG_2, -16), +			BPF_RAW_INSN(BPF_JMP | BPF_CALL, 0, 1, 0, 8), + +			/* fetch map_value_ptr from the stack of this function */ +			BPF_LDX_MEM(BPF_DW, BPF_REG_0, BPF_REG_10, -8), +			BPF_JMP_IMM(BPF_JEQ, BPF_REG_0, 0, 1), +			/* write into map value */ +			BPF_ST_MEM(BPF_DW, BPF_REG_0, 0, 0), +			/* fetch secound map_value_ptr from the stack */ +			BPF_LDX_MEM(BPF_DW, BPF_REG_0, BPF_REG_10, -16), +			BPF_JMP_IMM(BPF_JEQ, BPF_REG_0, 0, 1), +			/* write into map value */ +			BPF_ST_MEM(BPF_DW, BPF_REG_0, 0, 0), +			BPF_MOV64_IMM(BPF_REG_0, 0), +			BPF_EXIT_INSN(), + +			/* subprog 1 */ +			/* call 3rd function twice */ +			BPF_MOV64_REG(BPF_REG_6, BPF_REG_1), +			BPF_MOV64_REG(BPF_REG_7, BPF_REG_2), +			/* first time with fp-8 */ +			BPF_RAW_INSN(BPF_JMP | BPF_CALL, 0, 1, 0, 3), +			BPF_MOV64_REG(BPF_REG_1, BPF_REG_7), +			/* second time with fp-16 */ +			BPF_RAW_INSN(BPF_JMP | BPF_CALL, 0, 1, 0, 1), +			BPF_EXIT_INSN(), + +			/* subprog 2 */ +			BPF_MOV64_REG(BPF_REG_6, BPF_REG_1), +			/* lookup from map */ +			BPF_ST_MEM(BPF_DW, BPF_REG_10, -8, 0), +			BPF_MOV64_REG(BPF_REG_2, BPF_REG_10), +			BPF_ALU64_IMM(BPF_ADD, BPF_REG_2, -8), +			BPF_LD_MAP_FD(BPF_REG_1, 0), +			BPF_RAW_INSN(BPF_JMP | BPF_CALL, 0, 0, 0, +				     BPF_FUNC_map_lookup_elem), +			/* write map_value_ptr into stack frame of main prog */ +			BPF_STX_MEM(BPF_DW, BPF_REG_6, BPF_REG_0, 0), +			BPF_MOV64_IMM(BPF_REG_0, 0), +			BPF_EXIT_INSN(), /* return 0 */ +		}, +		.prog_type = BPF_PROG_TYPE_XDP, +		.fixup_map1 = { 23 }, +		.result = ACCEPT, +	}, +	{ +		"calls: two calls that return map_value with bool condition", +		.insns = { +			/* main prog */ +			/* pass fp-16, fp-8 into a function */ +			BPF_MOV64_REG(BPF_REG_1, BPF_REG_10), +			BPF_ALU64_IMM(BPF_ADD, BPF_REG_1, -8), +			BPF_MOV64_REG(BPF_REG_2, BPF_REG_10), +			BPF_ALU64_IMM(BPF_ADD, BPF_REG_2, -16), +			BPF_RAW_INSN(BPF_JMP | BPF_CALL, 0, 1, 0, 2), +			BPF_MOV64_IMM(BPF_REG_0, 0), +			BPF_EXIT_INSN(), + +			/* subprog 1 */ +			/* call 3rd function twice */ +			BPF_MOV64_REG(BPF_REG_6, BPF_REG_1), +			BPF_MOV64_REG(BPF_REG_7, BPF_REG_2), +			/* first time with fp-8 */ +			BPF_RAW_INSN(BPF_JMP | BPF_CALL, 0, 1, 0, 9), +			BPF_JMP_IMM(BPF_JNE, BPF_REG_0, 1, 2), +			/* fetch map_value_ptr from the stack of this function */ +			BPF_LDX_MEM(BPF_DW, BPF_REG_0, BPF_REG_6, 0), +			/* write into map value */ +			BPF_ST_MEM(BPF_DW, BPF_REG_0, 0, 0), +			BPF_MOV64_REG(BPF_REG_1, BPF_REG_7), +			/* second time with fp-16 */ +			BPF_RAW_INSN(BPF_JMP | BPF_CALL, 0, 1, 0, 4), +			BPF_JMP_IMM(BPF_JNE, BPF_REG_0, 1, 2), +			/* fetch secound map_value_ptr from the stack */ +			BPF_LDX_MEM(BPF_DW, BPF_REG_0, BPF_REG_7, 0), +			/* write into map value */ +			BPF_ST_MEM(BPF_DW, BPF_REG_0, 0, 0), +			BPF_EXIT_INSN(), + +			/* subprog 2 */ +			BPF_MOV64_REG(BPF_REG_6, BPF_REG_1), +			/* lookup from map */ +			BPF_ST_MEM(BPF_DW, BPF_REG_10, -8, 0), +			BPF_MOV64_REG(BPF_REG_2, BPF_REG_10), +			BPF_ALU64_IMM(BPF_ADD, BPF_REG_2, -8), +			BPF_LD_MAP_FD(BPF_REG_1, 0), +			BPF_RAW_INSN(BPF_JMP | BPF_CALL, 0, 0, 0, +				     BPF_FUNC_map_lookup_elem), +			BPF_JMP_IMM(BPF_JNE, BPF_REG_0, 0, 2), +			BPF_MOV64_IMM(BPF_REG_0, 0), +			BPF_EXIT_INSN(), /* return 0 */ +			/* write map_value_ptr into stack frame of main prog */ +			BPF_STX_MEM(BPF_DW, BPF_REG_6, BPF_REG_0, 0), +			BPF_MOV64_IMM(BPF_REG_0, 1), +			BPF_EXIT_INSN(), /* return 1 */ +		}, +		.prog_type = BPF_PROG_TYPE_XDP, +		.fixup_map1 = { 23 }, +		.result = ACCEPT, +	}, +	{ +		"calls: two calls that return map_value with incorrect bool check", +		.insns = { +			/* main prog */ +			/* pass fp-16, fp-8 into a function */ +			BPF_MOV64_REG(BPF_REG_1, BPF_REG_10), +			BPF_ALU64_IMM(BPF_ADD, BPF_REG_1, -8), +			BPF_MOV64_REG(BPF_REG_2, BPF_REG_10), +			BPF_ALU64_IMM(BPF_ADD, BPF_REG_2, -16), +			BPF_RAW_INSN(BPF_JMP | BPF_CALL, 0, 1, 0, 2), +			BPF_MOV64_IMM(BPF_REG_0, 0), +			BPF_EXIT_INSN(), + +			/* subprog 1 */ +			/* call 3rd function twice */ +			BPF_MOV64_REG(BPF_REG_6, BPF_REG_1), +			BPF_MOV64_REG(BPF_REG_7, BPF_REG_2), +			/* first time with fp-8 */ +			BPF_RAW_INSN(BPF_JMP | BPF_CALL, 0, 1, 0, 9), +			BPF_JMP_IMM(BPF_JNE, BPF_REG_0, 1, 2), +			/* fetch map_value_ptr from the stack of this function */ +			BPF_LDX_MEM(BPF_DW, BPF_REG_0, BPF_REG_6, 0), +			/* write into map value */ +			BPF_ST_MEM(BPF_DW, BPF_REG_0, 0, 0), +			BPF_MOV64_REG(BPF_REG_1, BPF_REG_7), +			/* second time with fp-16 */ +			BPF_RAW_INSN(BPF_JMP | BPF_CALL, 0, 1, 0, 4), +			BPF_JMP_IMM(BPF_JNE, BPF_REG_0, 0, 2), +			/* fetch secound map_value_ptr from the stack */ +			BPF_LDX_MEM(BPF_DW, BPF_REG_0, BPF_REG_7, 0), +			/* write into map value */ +			BPF_ST_MEM(BPF_DW, BPF_REG_0, 0, 0), +			BPF_EXIT_INSN(), + +			/* subprog 2 */ +			BPF_MOV64_REG(BPF_REG_6, BPF_REG_1), +			/* lookup from map */ +			BPF_ST_MEM(BPF_DW, BPF_REG_10, -8, 0), +			BPF_MOV64_REG(BPF_REG_2, BPF_REG_10), +			BPF_ALU64_IMM(BPF_ADD, BPF_REG_2, -8), +			BPF_LD_MAP_FD(BPF_REG_1, 0), +			BPF_RAW_INSN(BPF_JMP | BPF_CALL, 0, 0, 0, +				     BPF_FUNC_map_lookup_elem), +			BPF_JMP_IMM(BPF_JNE, BPF_REG_0, 0, 2), +			BPF_MOV64_IMM(BPF_REG_0, 0), +			BPF_EXIT_INSN(), /* return 0 */ +			/* write map_value_ptr into stack frame of main prog */ +			BPF_STX_MEM(BPF_DW, BPF_REG_6, BPF_REG_0, 0), +			BPF_MOV64_IMM(BPF_REG_0, 1), +			BPF_EXIT_INSN(), /* return 1 */ +		}, +		.prog_type = BPF_PROG_TYPE_XDP, +		.fixup_map1 = { 23 }, +		.result = REJECT, +		.errstr = "invalid read from stack off -16+0 size 8", +	}, +	{ +		"calls: two calls that receive map_value via arg=ptr_stack_of_caller. test1", +		.insns = { +			/* main prog */ +			/* pass fp-16, fp-8 into a function */ +			BPF_MOV64_REG(BPF_REG_1, BPF_REG_10), +			BPF_ALU64_IMM(BPF_ADD, BPF_REG_1, -8), +			BPF_MOV64_REG(BPF_REG_2, BPF_REG_10), +			BPF_ALU64_IMM(BPF_ADD, BPF_REG_2, -16), +			BPF_RAW_INSN(BPF_JMP | BPF_CALL, 0, 1, 0, 2), +			BPF_MOV64_IMM(BPF_REG_0, 0), +			BPF_EXIT_INSN(), + +			/* subprog 1 */ +			BPF_MOV64_REG(BPF_REG_6, BPF_REG_1), +			BPF_MOV64_REG(BPF_REG_7, BPF_REG_2), +			/* 1st lookup from map */ +			BPF_ST_MEM(BPF_DW, BPF_REG_10, -8, 0), +			BPF_MOV64_REG(BPF_REG_2, BPF_REG_10), +			BPF_ALU64_IMM(BPF_ADD, BPF_REG_2, -8), +			BPF_LD_MAP_FD(BPF_REG_1, 0), +			BPF_RAW_INSN(BPF_JMP | BPF_CALL, 0, 0, 0, +				     BPF_FUNC_map_lookup_elem), +			BPF_JMP_IMM(BPF_JNE, BPF_REG_0, 0, 2), +			BPF_MOV64_IMM(BPF_REG_8, 0), +			BPF_JMP_IMM(BPF_JA, 0, 0, 2), +			/* write map_value_ptr into stack frame of main prog at fp-8 */ +			BPF_STX_MEM(BPF_DW, BPF_REG_6, BPF_REG_0, 0), +			BPF_MOV64_IMM(BPF_REG_8, 1), + +			/* 2nd lookup from map */ +			BPF_MOV64_REG(BPF_REG_2, BPF_REG_10), /* 20 */ +			BPF_ALU64_IMM(BPF_ADD, BPF_REG_2, -8), +			BPF_LD_MAP_FD(BPF_REG_1, 0), +			BPF_RAW_INSN(BPF_JMP | BPF_CALL, 0, 0, 0, /* 24 */ +				     BPF_FUNC_map_lookup_elem), +			BPF_JMP_IMM(BPF_JNE, BPF_REG_0, 0, 2), +			BPF_MOV64_IMM(BPF_REG_9, 0), +			BPF_JMP_IMM(BPF_JA, 0, 0, 2), +			/* write map_value_ptr into stack frame of main prog at fp-16 */ +			BPF_STX_MEM(BPF_DW, BPF_REG_7, BPF_REG_0, 0), +			BPF_MOV64_IMM(BPF_REG_9, 1), + +			/* call 3rd func with fp-8, 0|1, fp-16, 0|1 */ +			BPF_MOV64_REG(BPF_REG_1, BPF_REG_6), /* 30 */ +			BPF_MOV64_REG(BPF_REG_2, BPF_REG_8), +			BPF_MOV64_REG(BPF_REG_3, BPF_REG_7), +			BPF_MOV64_REG(BPF_REG_4, BPF_REG_9), +			BPF_RAW_INSN(BPF_JMP | BPF_CALL, 0, 1, 0, 1),  /* 34 */ +			BPF_EXIT_INSN(), + +			/* subprog 2 */ +			/* if arg2 == 1 do *arg1 = 0 */ +			BPF_JMP_IMM(BPF_JNE, BPF_REG_2, 1, 2), +			/* fetch map_value_ptr from the stack of this function */ +			BPF_LDX_MEM(BPF_DW, BPF_REG_0, BPF_REG_1, 0), +			/* write into map value */ +			BPF_ST_MEM(BPF_DW, BPF_REG_0, 0, 0), + +			/* if arg4 == 1 do *arg3 = 0 */ +			BPF_JMP_IMM(BPF_JNE, BPF_REG_4, 1, 2), +			/* fetch map_value_ptr from the stack of this function */ +			BPF_LDX_MEM(BPF_DW, BPF_REG_0, BPF_REG_3, 0), +			/* write into map value */ +			BPF_ST_MEM(BPF_DW, BPF_REG_0, 2, 0), +			BPF_EXIT_INSN(), +		}, +		.prog_type = BPF_PROG_TYPE_SCHED_CLS, +		.fixup_map1 = { 12, 22 }, +		.result = REJECT, +		.errstr = "invalid access to map value, value_size=8 off=2 size=8", +	}, +	{ +		"calls: two calls that receive map_value via arg=ptr_stack_of_caller. test2", +		.insns = { +			/* main prog */ +			/* pass fp-16, fp-8 into a function */ +			BPF_MOV64_REG(BPF_REG_1, BPF_REG_10), +			BPF_ALU64_IMM(BPF_ADD, BPF_REG_1, -8), +			BPF_MOV64_REG(BPF_REG_2, BPF_REG_10), +			BPF_ALU64_IMM(BPF_ADD, BPF_REG_2, -16), +			BPF_RAW_INSN(BPF_JMP | BPF_CALL, 0, 1, 0, 2), +			BPF_MOV64_IMM(BPF_REG_0, 0), +			BPF_EXIT_INSN(), + +			/* subprog 1 */ +			BPF_MOV64_REG(BPF_REG_6, BPF_REG_1), +			BPF_MOV64_REG(BPF_REG_7, BPF_REG_2), +			/* 1st lookup from map */ +			BPF_ST_MEM(BPF_DW, BPF_REG_10, -8, 0), +			BPF_MOV64_REG(BPF_REG_2, BPF_REG_10), +			BPF_ALU64_IMM(BPF_ADD, BPF_REG_2, -8), +			BPF_LD_MAP_FD(BPF_REG_1, 0), +			BPF_RAW_INSN(BPF_JMP | BPF_CALL, 0, 0, 0, +				     BPF_FUNC_map_lookup_elem), +			BPF_JMP_IMM(BPF_JNE, BPF_REG_0, 0, 2), +			BPF_MOV64_IMM(BPF_REG_8, 0), +			BPF_JMP_IMM(BPF_JA, 0, 0, 2), +			/* write map_value_ptr into stack frame of main prog at fp-8 */ +			BPF_STX_MEM(BPF_DW, BPF_REG_6, BPF_REG_0, 0), +			BPF_MOV64_IMM(BPF_REG_8, 1), + +			/* 2nd lookup from map */ +			BPF_MOV64_REG(BPF_REG_2, BPF_REG_10), /* 20 */ +			BPF_ALU64_IMM(BPF_ADD, BPF_REG_2, -8), +			BPF_LD_MAP_FD(BPF_REG_1, 0), +			BPF_RAW_INSN(BPF_JMP | BPF_CALL, 0, 0, 0, /* 24 */ +				     BPF_FUNC_map_lookup_elem), +			BPF_JMP_IMM(BPF_JNE, BPF_REG_0, 0, 2), +			BPF_MOV64_IMM(BPF_REG_9, 0), +			BPF_JMP_IMM(BPF_JA, 0, 0, 2), +			/* write map_value_ptr into stack frame of main prog at fp-16 */ +			BPF_STX_MEM(BPF_DW, BPF_REG_7, BPF_REG_0, 0), +			BPF_MOV64_IMM(BPF_REG_9, 1), + +			/* call 3rd func with fp-8, 0|1, fp-16, 0|1 */ +			BPF_MOV64_REG(BPF_REG_1, BPF_REG_6), /* 30 */ +			BPF_MOV64_REG(BPF_REG_2, BPF_REG_8), +			BPF_MOV64_REG(BPF_REG_3, BPF_REG_7), +			BPF_MOV64_REG(BPF_REG_4, BPF_REG_9), +			BPF_RAW_INSN(BPF_JMP | BPF_CALL, 0, 1, 0, 1),  /* 34 */ +			BPF_EXIT_INSN(), + +			/* subprog 2 */ +			/* if arg2 == 1 do *arg1 = 0 */ +			BPF_JMP_IMM(BPF_JNE, BPF_REG_2, 1, 2), +			/* fetch map_value_ptr from the stack of this function */ +			BPF_LDX_MEM(BPF_DW, BPF_REG_0, BPF_REG_1, 0), +			/* write into map value */ +			BPF_ST_MEM(BPF_DW, BPF_REG_0, 0, 0), + +			/* if arg4 == 1 do *arg3 = 0 */ +			BPF_JMP_IMM(BPF_JNE, BPF_REG_4, 1, 2), +			/* fetch map_value_ptr from the stack of this function */ +			BPF_LDX_MEM(BPF_DW, BPF_REG_0, BPF_REG_3, 0), +			/* write into map value */ +			BPF_ST_MEM(BPF_DW, BPF_REG_0, 0, 0), +			BPF_EXIT_INSN(), +		}, +		.prog_type = BPF_PROG_TYPE_SCHED_CLS, +		.fixup_map1 = { 12, 22 }, +		.result = ACCEPT, +	}, +	{ +		"calls: two jumps that receive map_value via arg=ptr_stack_of_jumper. test3", +		.insns = { +			/* main prog */ +			/* pass fp-16, fp-8 into a function */ +			BPF_MOV64_REG(BPF_REG_1, BPF_REG_10), +			BPF_ALU64_IMM(BPF_ADD, BPF_REG_1, -8), +			BPF_MOV64_REG(BPF_REG_2, BPF_REG_10), +			BPF_ALU64_IMM(BPF_ADD, BPF_REG_2, -16), +			BPF_JMP_IMM(BPF_JNE, BPF_REG_1, 0, 2), +			BPF_MOV64_IMM(BPF_REG_0, 0), +			BPF_EXIT_INSN(), + +			/* subprog 1 */ +			BPF_MOV64_REG(BPF_REG_6, BPF_REG_1), +			BPF_MOV64_REG(BPF_REG_7, BPF_REG_2), +			/* 1st lookup from map */ +			BPF_ST_MEM(BPF_DW, BPF_REG_10, -24, 0), +			BPF_MOV64_REG(BPF_REG_2, BPF_REG_10), +			BPF_ALU64_IMM(BPF_ADD, BPF_REG_2, -24), +			BPF_LD_MAP_FD(BPF_REG_1, 0), +			BPF_RAW_INSN(BPF_JMP | BPF_CALL, 0, 0, 0, +				     BPF_FUNC_map_lookup_elem), +			BPF_JMP_IMM(BPF_JNE, BPF_REG_0, 0, 2), +			BPF_MOV64_IMM(BPF_REG_8, 0), +			BPF_JMP_IMM(BPF_JA, 0, 0, 2), +			/* write map_value_ptr into stack frame of main prog at fp-8 */ +			BPF_STX_MEM(BPF_DW, BPF_REG_6, BPF_REG_0, 0), +			BPF_MOV64_IMM(BPF_REG_8, 1), + +			/* 2nd lookup from map */ +			BPF_MOV64_REG(BPF_REG_2, BPF_REG_10), +			BPF_ALU64_IMM(BPF_ADD, BPF_REG_2, -24), +			BPF_LD_MAP_FD(BPF_REG_1, 0), +			BPF_RAW_INSN(BPF_JMP | BPF_CALL, 0, 0, 0, +				     BPF_FUNC_map_lookup_elem), +			BPF_JMP_IMM(BPF_JNE, BPF_REG_0, 0, 2), +			BPF_MOV64_IMM(BPF_REG_9, 0),  // 26 +			BPF_JMP_IMM(BPF_JA, 0, 0, 2), +			/* write map_value_ptr into stack frame of main prog at fp-16 */ +			BPF_STX_MEM(BPF_DW, BPF_REG_7, BPF_REG_0, 0), +			BPF_MOV64_IMM(BPF_REG_9, 1), + +			/* call 3rd func with fp-8, 0|1, fp-16, 0|1 */ +			BPF_MOV64_REG(BPF_REG_1, BPF_REG_6), // 30 +			BPF_MOV64_REG(BPF_REG_2, BPF_REG_8), +			BPF_MOV64_REG(BPF_REG_3, BPF_REG_7), +			BPF_MOV64_REG(BPF_REG_4, BPF_REG_9), +			BPF_JMP_IMM(BPF_JNE, BPF_REG_1, 0, 1), // 34 +			BPF_JMP_IMM(BPF_JA, 0, 0, -30), + +			/* subprog 2 */ +			/* if arg2 == 1 do *arg1 = 0 */ +			BPF_JMP_IMM(BPF_JNE, BPF_REG_2, 1, 2), +			/* fetch map_value_ptr from the stack of this function */ +			BPF_LDX_MEM(BPF_DW, BPF_REG_0, BPF_REG_1, 0), +			/* write into map value */ +			BPF_ST_MEM(BPF_DW, BPF_REG_0, 0, 0), + +			/* if arg4 == 1 do *arg3 = 0 */ +			BPF_JMP_IMM(BPF_JNE, BPF_REG_4, 1, 2), +			/* fetch map_value_ptr from the stack of this function */ +			BPF_LDX_MEM(BPF_DW, BPF_REG_0, BPF_REG_3, 0), +			/* write into map value */ +			BPF_ST_MEM(BPF_DW, BPF_REG_0, 2, 0), +			BPF_JMP_IMM(BPF_JA, 0, 0, -8), +		}, +		.prog_type = BPF_PROG_TYPE_SCHED_CLS, +		.fixup_map1 = { 12, 22 }, +		.result = REJECT, +		.errstr = "invalid access to map value, value_size=8 off=2 size=8", +	}, +	{ +		"calls: two calls that receive map_value_ptr_or_null via arg. test1", +		.insns = { +			/* main prog */ +			/* pass fp-16, fp-8 into a function */ +			BPF_MOV64_REG(BPF_REG_1, BPF_REG_10), +			BPF_ALU64_IMM(BPF_ADD, BPF_REG_1, -8), +			BPF_MOV64_REG(BPF_REG_2, BPF_REG_10), +			BPF_ALU64_IMM(BPF_ADD, BPF_REG_2, -16), +			BPF_RAW_INSN(BPF_JMP | BPF_CALL, 0, 1, 0, 2), +			BPF_MOV64_IMM(BPF_REG_0, 0), +			BPF_EXIT_INSN(), + +			/* subprog 1 */ +			BPF_MOV64_REG(BPF_REG_6, BPF_REG_1), +			BPF_MOV64_REG(BPF_REG_7, BPF_REG_2), +			/* 1st lookup from map */ +			BPF_ST_MEM(BPF_DW, BPF_REG_10, -8, 0), +			BPF_MOV64_REG(BPF_REG_2, BPF_REG_10), +			BPF_ALU64_IMM(BPF_ADD, BPF_REG_2, -8), +			BPF_LD_MAP_FD(BPF_REG_1, 0), +			BPF_RAW_INSN(BPF_JMP | BPF_CALL, 0, 0, 0, +				     BPF_FUNC_map_lookup_elem), +			/* write map_value_ptr_or_null into stack frame of main prog at fp-8 */ +			BPF_STX_MEM(BPF_DW, BPF_REG_6, BPF_REG_0, 0), +			BPF_JMP_IMM(BPF_JNE, BPF_REG_0, 0, 2), +			BPF_MOV64_IMM(BPF_REG_8, 0), +			BPF_JMP_IMM(BPF_JA, 0, 0, 1), +			BPF_MOV64_IMM(BPF_REG_8, 1), + +			/* 2nd lookup from map */ +			BPF_MOV64_REG(BPF_REG_2, BPF_REG_10), +			BPF_ALU64_IMM(BPF_ADD, BPF_REG_2, -8), +			BPF_LD_MAP_FD(BPF_REG_1, 0), +			BPF_RAW_INSN(BPF_JMP | BPF_CALL, 0, 0, 0, +				     BPF_FUNC_map_lookup_elem), +			/* write map_value_ptr_or_null into stack frame of main prog at fp-16 */ +			BPF_STX_MEM(BPF_DW, BPF_REG_7, BPF_REG_0, 0), +			BPF_JMP_IMM(BPF_JNE, BPF_REG_0, 0, 2), +			BPF_MOV64_IMM(BPF_REG_9, 0), +			BPF_JMP_IMM(BPF_JA, 0, 0, 1), +			BPF_MOV64_IMM(BPF_REG_9, 1), + +			/* call 3rd func with fp-8, 0|1, fp-16, 0|1 */ +			BPF_MOV64_REG(BPF_REG_1, BPF_REG_6), +			BPF_MOV64_REG(BPF_REG_2, BPF_REG_8), +			BPF_MOV64_REG(BPF_REG_3, BPF_REG_7), +			BPF_MOV64_REG(BPF_REG_4, BPF_REG_9), +			BPF_RAW_INSN(BPF_JMP | BPF_CALL, 0, 1, 0, 1), +			BPF_EXIT_INSN(), + +			/* subprog 2 */ +			/* if arg2 == 1 do *arg1 = 0 */ +			BPF_JMP_IMM(BPF_JNE, BPF_REG_2, 1, 2), +			/* fetch map_value_ptr from the stack of this function */ +			BPF_LDX_MEM(BPF_DW, BPF_REG_0, BPF_REG_1, 0), +			/* write into map value */ +			BPF_ST_MEM(BPF_DW, BPF_REG_0, 0, 0), + +			/* if arg4 == 1 do *arg3 = 0 */ +			BPF_JMP_IMM(BPF_JNE, BPF_REG_4, 1, 2), +			/* fetch map_value_ptr from the stack of this function */ +			BPF_LDX_MEM(BPF_DW, BPF_REG_0, BPF_REG_3, 0), +			/* write into map value */ +			BPF_ST_MEM(BPF_DW, BPF_REG_0, 0, 0), +			BPF_EXIT_INSN(), +		}, +		.prog_type = BPF_PROG_TYPE_SCHED_CLS, +		.fixup_map1 = { 12, 22 }, +		.result = ACCEPT, +	}, +	{ +		"calls: two calls that receive map_value_ptr_or_null via arg. test2", +		.insns = { +			/* main prog */ +			/* pass fp-16, fp-8 into a function */ +			BPF_MOV64_REG(BPF_REG_1, BPF_REG_10), +			BPF_ALU64_IMM(BPF_ADD, BPF_REG_1, -8), +			BPF_MOV64_REG(BPF_REG_2, BPF_REG_10), +			BPF_ALU64_IMM(BPF_ADD, BPF_REG_2, -16), +			BPF_RAW_INSN(BPF_JMP | BPF_CALL, 0, 1, 0, 2), +			BPF_MOV64_IMM(BPF_REG_0, 0), +			BPF_EXIT_INSN(), + +			/* subprog 1 */ +			BPF_MOV64_REG(BPF_REG_6, BPF_REG_1), +			BPF_MOV64_REG(BPF_REG_7, BPF_REG_2), +			/* 1st lookup from map */ +			BPF_ST_MEM(BPF_DW, BPF_REG_10, -8, 0), +			BPF_MOV64_REG(BPF_REG_2, BPF_REG_10), +			BPF_ALU64_IMM(BPF_ADD, BPF_REG_2, -8), +			BPF_LD_MAP_FD(BPF_REG_1, 0), +			BPF_RAW_INSN(BPF_JMP | BPF_CALL, 0, 0, 0, +				     BPF_FUNC_map_lookup_elem), +			/* write map_value_ptr_or_null into stack frame of main prog at fp-8 */ +			BPF_STX_MEM(BPF_DW, BPF_REG_6, BPF_REG_0, 0), +			BPF_JMP_IMM(BPF_JNE, BPF_REG_0, 0, 2), +			BPF_MOV64_IMM(BPF_REG_8, 0), +			BPF_JMP_IMM(BPF_JA, 0, 0, 1), +			BPF_MOV64_IMM(BPF_REG_8, 1), + +			/* 2nd lookup from map */ +			BPF_MOV64_REG(BPF_REG_2, BPF_REG_10), +			BPF_ALU64_IMM(BPF_ADD, BPF_REG_2, -8), +			BPF_LD_MAP_FD(BPF_REG_1, 0), +			BPF_RAW_INSN(BPF_JMP | BPF_CALL, 0, 0, 0, +				     BPF_FUNC_map_lookup_elem), +			/* write map_value_ptr_or_null into stack frame of main prog at fp-16 */ +			BPF_STX_MEM(BPF_DW, BPF_REG_7, BPF_REG_0, 0), +			BPF_JMP_IMM(BPF_JNE, BPF_REG_0, 0, 2), +			BPF_MOV64_IMM(BPF_REG_9, 0), +			BPF_JMP_IMM(BPF_JA, 0, 0, 1), +			BPF_MOV64_IMM(BPF_REG_9, 1), + +			/* call 3rd func with fp-8, 0|1, fp-16, 0|1 */ +			BPF_MOV64_REG(BPF_REG_1, BPF_REG_6), +			BPF_MOV64_REG(BPF_REG_2, BPF_REG_8), +			BPF_MOV64_REG(BPF_REG_3, BPF_REG_7), +			BPF_MOV64_REG(BPF_REG_4, BPF_REG_9), +			BPF_RAW_INSN(BPF_JMP | BPF_CALL, 0, 1, 0, 1), +			BPF_EXIT_INSN(), + +			/* subprog 2 */ +			/* if arg2 == 1 do *arg1 = 0 */ +			BPF_JMP_IMM(BPF_JNE, BPF_REG_2, 1, 2), +			/* fetch map_value_ptr from the stack of this function */ +			BPF_LDX_MEM(BPF_DW, BPF_REG_0, BPF_REG_1, 0), +			/* write into map value */ +			BPF_ST_MEM(BPF_DW, BPF_REG_0, 0, 0), + +			/* if arg4 == 0 do *arg3 = 0 */ +			BPF_JMP_IMM(BPF_JNE, BPF_REG_4, 0, 2), +			/* fetch map_value_ptr from the stack of this function */ +			BPF_LDX_MEM(BPF_DW, BPF_REG_0, BPF_REG_3, 0), +			/* write into map value */ +			BPF_ST_MEM(BPF_DW, BPF_REG_0, 0, 0), +			BPF_EXIT_INSN(), +		}, +		.prog_type = BPF_PROG_TYPE_SCHED_CLS, +		.fixup_map1 = { 12, 22 }, +		.result = REJECT, +		.errstr = "R0 invalid mem access 'inv'", +	}, +	{ +		"calls: pkt_ptr spill into caller stack", +		.insns = { +			BPF_MOV64_REG(BPF_REG_4, BPF_REG_10), +			BPF_ALU64_IMM(BPF_ADD, BPF_REG_4, -8), +			BPF_RAW_INSN(BPF_JMP | BPF_CALL, 0, 1, 0, 1), +			BPF_EXIT_INSN(), + +			/* subprog 1 */ +			BPF_LDX_MEM(BPF_W, BPF_REG_2, BPF_REG_1, +				    offsetof(struct __sk_buff, data)), +			BPF_LDX_MEM(BPF_W, BPF_REG_3, BPF_REG_1, +				    offsetof(struct __sk_buff, data_end)), +			BPF_MOV64_REG(BPF_REG_0, BPF_REG_2), +			BPF_ALU64_IMM(BPF_ADD, BPF_REG_0, 8), +			/* spill unchecked pkt_ptr into stack of caller */ +			BPF_STX_MEM(BPF_DW, BPF_REG_4, BPF_REG_2, 0), +			BPF_JMP_REG(BPF_JGT, BPF_REG_0, BPF_REG_3, 2), +			/* now the pkt range is verified, read pkt_ptr from stack */ +			BPF_LDX_MEM(BPF_DW, BPF_REG_2, BPF_REG_4, 0), +			/* write 4 bytes into packet */ +			BPF_ST_MEM(BPF_W, BPF_REG_2, 0, 0), +			BPF_EXIT_INSN(), +		}, +		.result = ACCEPT, +		.prog_type = BPF_PROG_TYPE_SCHED_CLS, +		.retval = POINTER_VALUE, +	}, +	{ +		"calls: pkt_ptr spill into caller stack 2", +		.insns = { +			BPF_MOV64_REG(BPF_REG_4, BPF_REG_10), +			BPF_ALU64_IMM(BPF_ADD, BPF_REG_4, -8), +			BPF_RAW_INSN(BPF_JMP | BPF_CALL, 0, 1, 0, 3), +			/* Marking is still kept, but not in all cases safe. */ +			BPF_LDX_MEM(BPF_DW, BPF_REG_4, BPF_REG_10, -8), +			BPF_ST_MEM(BPF_W, BPF_REG_4, 0, 0), +			BPF_EXIT_INSN(), + +			/* subprog 1 */ +			BPF_LDX_MEM(BPF_W, BPF_REG_2, BPF_REG_1, +				    offsetof(struct __sk_buff, data)), +			BPF_LDX_MEM(BPF_W, BPF_REG_3, BPF_REG_1, +				    offsetof(struct __sk_buff, data_end)), +			BPF_MOV64_REG(BPF_REG_0, BPF_REG_2), +			BPF_ALU64_IMM(BPF_ADD, BPF_REG_0, 8), +			/* spill unchecked pkt_ptr into stack of caller */ +			BPF_STX_MEM(BPF_DW, BPF_REG_4, BPF_REG_2, 0), +			BPF_JMP_REG(BPF_JGT, BPF_REG_0, BPF_REG_3, 2), +			/* now the pkt range is verified, read pkt_ptr from stack */ +			BPF_LDX_MEM(BPF_DW, BPF_REG_2, BPF_REG_4, 0), +			/* write 4 bytes into packet */ +			BPF_ST_MEM(BPF_W, BPF_REG_2, 0, 0), +			BPF_EXIT_INSN(), +		}, +		.prog_type = BPF_PROG_TYPE_SCHED_CLS, +		.errstr = "invalid access to packet", +		.result = REJECT, +	}, +	{ +		"calls: pkt_ptr spill into caller stack 3", +		.insns = { +			BPF_MOV64_REG(BPF_REG_4, BPF_REG_10), +			BPF_ALU64_IMM(BPF_ADD, BPF_REG_4, -8), +			BPF_RAW_INSN(BPF_JMP | BPF_CALL, 0, 1, 0, 4), +			BPF_JMP_IMM(BPF_JEQ, BPF_REG_0, 0, 2), +			/* Marking is still kept and safe here. */ +			BPF_LDX_MEM(BPF_DW, BPF_REG_4, BPF_REG_10, -8), +			BPF_ST_MEM(BPF_W, BPF_REG_4, 0, 0), +			BPF_EXIT_INSN(), + +			/* subprog 1 */ +			BPF_LDX_MEM(BPF_W, BPF_REG_2, BPF_REG_1, +				    offsetof(struct __sk_buff, data)), +			BPF_LDX_MEM(BPF_W, BPF_REG_3, BPF_REG_1, +				    offsetof(struct __sk_buff, data_end)), +			BPF_MOV64_REG(BPF_REG_0, BPF_REG_2), +			BPF_ALU64_IMM(BPF_ADD, BPF_REG_0, 8), +			/* spill unchecked pkt_ptr into stack of caller */ +			BPF_STX_MEM(BPF_DW, BPF_REG_4, BPF_REG_2, 0), +			BPF_MOV64_IMM(BPF_REG_5, 0), +			BPF_JMP_REG(BPF_JGT, BPF_REG_0, BPF_REG_3, 3), +			BPF_MOV64_IMM(BPF_REG_5, 1), +			/* now the pkt range is verified, read pkt_ptr from stack */ +			BPF_LDX_MEM(BPF_DW, BPF_REG_2, BPF_REG_4, 0), +			/* write 4 bytes into packet */ +			BPF_ST_MEM(BPF_W, BPF_REG_2, 0, 0), +			BPF_MOV64_REG(BPF_REG_0, BPF_REG_5), +			BPF_EXIT_INSN(), +		}, +		.prog_type = BPF_PROG_TYPE_SCHED_CLS, +		.result = ACCEPT, +		.retval = 1, +	}, +	{ +		"calls: pkt_ptr spill into caller stack 4", +		.insns = { +			BPF_MOV64_REG(BPF_REG_4, BPF_REG_10), +			BPF_ALU64_IMM(BPF_ADD, BPF_REG_4, -8), +			BPF_RAW_INSN(BPF_JMP | BPF_CALL, 0, 1, 0, 4), +			BPF_JMP_IMM(BPF_JEQ, BPF_REG_0, 0, 2), +			/* Check marking propagated. */ +			BPF_LDX_MEM(BPF_DW, BPF_REG_4, BPF_REG_10, -8), +			BPF_ST_MEM(BPF_W, BPF_REG_4, 0, 0), +			BPF_EXIT_INSN(), + +			/* subprog 1 */ +			BPF_LDX_MEM(BPF_W, BPF_REG_2, BPF_REG_1, +				    offsetof(struct __sk_buff, data)), +			BPF_LDX_MEM(BPF_W, BPF_REG_3, BPF_REG_1, +				    offsetof(struct __sk_buff, data_end)), +			BPF_MOV64_REG(BPF_REG_0, BPF_REG_2), +			BPF_ALU64_IMM(BPF_ADD, BPF_REG_0, 8), +			/* spill unchecked pkt_ptr into stack of caller */ +			BPF_STX_MEM(BPF_DW, BPF_REG_4, BPF_REG_2, 0), +			BPF_MOV64_IMM(BPF_REG_5, 0), +			BPF_JMP_REG(BPF_JGT, BPF_REG_0, BPF_REG_3, 2), +			BPF_MOV64_IMM(BPF_REG_5, 1), +			/* don't read back pkt_ptr from stack here */ +			/* write 4 bytes into packet */ +			BPF_ST_MEM(BPF_W, BPF_REG_2, 0, 0), +			BPF_MOV64_REG(BPF_REG_0, BPF_REG_5), +			BPF_EXIT_INSN(), +		}, +		.prog_type = BPF_PROG_TYPE_SCHED_CLS, +		.result = ACCEPT, +		.retval = 1, +	}, +	{ +		"calls: pkt_ptr spill into caller stack 5", +		.insns = { +			BPF_MOV64_REG(BPF_REG_4, BPF_REG_10), +			BPF_ALU64_IMM(BPF_ADD, BPF_REG_4, -8), +			BPF_STX_MEM(BPF_DW, BPF_REG_4, BPF_REG_1, 0), +			BPF_RAW_INSN(BPF_JMP | BPF_CALL, 0, 1, 0, 3), +			BPF_LDX_MEM(BPF_DW, BPF_REG_4, BPF_REG_10, -8), +			BPF_LDX_MEM(BPF_W, BPF_REG_0, BPF_REG_4, 0), +			BPF_EXIT_INSN(), + +			/* subprog 1 */ +			BPF_LDX_MEM(BPF_W, BPF_REG_2, BPF_REG_1, +				    offsetof(struct __sk_buff, data)), +			BPF_LDX_MEM(BPF_W, BPF_REG_3, BPF_REG_1, +				    offsetof(struct __sk_buff, data_end)), +			BPF_MOV64_REG(BPF_REG_0, BPF_REG_2), +			BPF_ALU64_IMM(BPF_ADD, BPF_REG_0, 8), +			BPF_MOV64_IMM(BPF_REG_5, 0), +			BPF_JMP_REG(BPF_JGT, BPF_REG_0, BPF_REG_3, 3), +			/* spill checked pkt_ptr into stack of caller */ +			BPF_STX_MEM(BPF_DW, BPF_REG_4, BPF_REG_2, 0), +			BPF_MOV64_IMM(BPF_REG_5, 1), +			/* don't read back pkt_ptr from stack here */ +			/* write 4 bytes into packet */ +			BPF_ST_MEM(BPF_W, BPF_REG_2, 0, 0), +			BPF_MOV64_REG(BPF_REG_0, BPF_REG_5), +			BPF_EXIT_INSN(), +		}, +		.prog_type = BPF_PROG_TYPE_SCHED_CLS, +		.errstr = "same insn cannot be used with different", +		.result = REJECT, +	}, +	{ +		"calls: pkt_ptr spill into caller stack 6", +		.insns = { +			BPF_LDX_MEM(BPF_W, BPF_REG_2, BPF_REG_1, +				    offsetof(struct __sk_buff, data_end)), +			BPF_MOV64_REG(BPF_REG_4, BPF_REG_10), +			BPF_ALU64_IMM(BPF_ADD, BPF_REG_4, -8), +			BPF_STX_MEM(BPF_DW, BPF_REG_4, BPF_REG_2, 0), +			BPF_RAW_INSN(BPF_JMP | BPF_CALL, 0, 1, 0, 3), +			BPF_LDX_MEM(BPF_DW, BPF_REG_4, BPF_REG_10, -8), +			BPF_LDX_MEM(BPF_W, BPF_REG_0, BPF_REG_4, 0), +			BPF_EXIT_INSN(), + +			/* subprog 1 */ +			BPF_LDX_MEM(BPF_W, BPF_REG_2, BPF_REG_1, +				    offsetof(struct __sk_buff, data)), +			BPF_LDX_MEM(BPF_W, BPF_REG_3, BPF_REG_1, +				    offsetof(struct __sk_buff, data_end)), +			BPF_MOV64_REG(BPF_REG_0, BPF_REG_2), +			BPF_ALU64_IMM(BPF_ADD, BPF_REG_0, 8), +			BPF_MOV64_IMM(BPF_REG_5, 0), +			BPF_JMP_REG(BPF_JGT, BPF_REG_0, BPF_REG_3, 3), +			/* spill checked pkt_ptr into stack of caller */ +			BPF_STX_MEM(BPF_DW, BPF_REG_4, BPF_REG_2, 0), +			BPF_MOV64_IMM(BPF_REG_5, 1), +			/* don't read back pkt_ptr from stack here */ +			/* write 4 bytes into packet */ +			BPF_ST_MEM(BPF_W, BPF_REG_2, 0, 0), +			BPF_MOV64_REG(BPF_REG_0, BPF_REG_5), +			BPF_EXIT_INSN(), +		}, +		.prog_type = BPF_PROG_TYPE_SCHED_CLS, +		.errstr = "R4 invalid mem access", +		.result = REJECT, +	}, +	{ +		"calls: pkt_ptr spill into caller stack 7", +		.insns = { +			BPF_MOV64_IMM(BPF_REG_2, 0), +			BPF_MOV64_REG(BPF_REG_4, BPF_REG_10), +			BPF_ALU64_IMM(BPF_ADD, BPF_REG_4, -8), +			BPF_STX_MEM(BPF_DW, BPF_REG_4, BPF_REG_2, 0), +			BPF_RAW_INSN(BPF_JMP | BPF_CALL, 0, 1, 0, 3), +			BPF_LDX_MEM(BPF_DW, BPF_REG_4, BPF_REG_10, -8), +			BPF_LDX_MEM(BPF_W, BPF_REG_0, BPF_REG_4, 0), +			BPF_EXIT_INSN(), + +			/* subprog 1 */ +			BPF_LDX_MEM(BPF_W, BPF_REG_2, BPF_REG_1, +				    offsetof(struct __sk_buff, data)), +			BPF_LDX_MEM(BPF_W, BPF_REG_3, BPF_REG_1, +				    offsetof(struct __sk_buff, data_end)), +			BPF_MOV64_REG(BPF_REG_0, BPF_REG_2), +			BPF_ALU64_IMM(BPF_ADD, BPF_REG_0, 8), +			BPF_MOV64_IMM(BPF_REG_5, 0), +			BPF_JMP_REG(BPF_JGT, BPF_REG_0, BPF_REG_3, 3), +			/* spill checked pkt_ptr into stack of caller */ +			BPF_STX_MEM(BPF_DW, BPF_REG_4, BPF_REG_2, 0), +			BPF_MOV64_IMM(BPF_REG_5, 1), +			/* don't read back pkt_ptr from stack here */ +			/* write 4 bytes into packet */ +			BPF_ST_MEM(BPF_W, BPF_REG_2, 0, 0), +			BPF_MOV64_REG(BPF_REG_0, BPF_REG_5), +			BPF_EXIT_INSN(), +		}, +		.prog_type = BPF_PROG_TYPE_SCHED_CLS, +		.errstr = "R4 invalid mem access", +		.result = REJECT, +	}, +	{ +		"calls: pkt_ptr spill into caller stack 8", +		.insns = { +			BPF_LDX_MEM(BPF_W, BPF_REG_2, BPF_REG_1, +				    offsetof(struct __sk_buff, data)), +			BPF_LDX_MEM(BPF_W, BPF_REG_3, BPF_REG_1, +				    offsetof(struct __sk_buff, data_end)), +			BPF_MOV64_REG(BPF_REG_0, BPF_REG_2), +			BPF_ALU64_IMM(BPF_ADD, BPF_REG_0, 8), +			BPF_JMP_REG(BPF_JLE, BPF_REG_0, BPF_REG_3, 1), +			BPF_EXIT_INSN(), +			BPF_MOV64_REG(BPF_REG_4, BPF_REG_10), +			BPF_ALU64_IMM(BPF_ADD, BPF_REG_4, -8), +			BPF_STX_MEM(BPF_DW, BPF_REG_4, BPF_REG_2, 0), +			BPF_RAW_INSN(BPF_JMP | BPF_CALL, 0, 1, 0, 3), +			BPF_LDX_MEM(BPF_DW, BPF_REG_4, BPF_REG_10, -8), +			BPF_LDX_MEM(BPF_W, BPF_REG_0, BPF_REG_4, 0), +			BPF_EXIT_INSN(), + +			/* subprog 1 */ +			BPF_LDX_MEM(BPF_W, BPF_REG_2, BPF_REG_1, +				    offsetof(struct __sk_buff, data)), +			BPF_LDX_MEM(BPF_W, BPF_REG_3, BPF_REG_1, +				    offsetof(struct __sk_buff, data_end)), +			BPF_MOV64_REG(BPF_REG_0, BPF_REG_2), +			BPF_ALU64_IMM(BPF_ADD, BPF_REG_0, 8), +			BPF_MOV64_IMM(BPF_REG_5, 0), +			BPF_JMP_REG(BPF_JGT, BPF_REG_0, BPF_REG_3, 3), +			/* spill checked pkt_ptr into stack of caller */ +			BPF_STX_MEM(BPF_DW, BPF_REG_4, BPF_REG_2, 0), +			BPF_MOV64_IMM(BPF_REG_5, 1), +			/* don't read back pkt_ptr from stack here */ +			/* write 4 bytes into packet */ +			BPF_ST_MEM(BPF_W, BPF_REG_2, 0, 0), +			BPF_MOV64_REG(BPF_REG_0, BPF_REG_5), +			BPF_EXIT_INSN(), +		}, +		.prog_type = BPF_PROG_TYPE_SCHED_CLS, +		.result = ACCEPT, +	}, +	{ +		"calls: pkt_ptr spill into caller stack 9", +		.insns = { +			BPF_LDX_MEM(BPF_W, BPF_REG_2, BPF_REG_1, +				    offsetof(struct __sk_buff, data)), +			BPF_LDX_MEM(BPF_W, BPF_REG_3, BPF_REG_1, +				    offsetof(struct __sk_buff, data_end)), +			BPF_MOV64_REG(BPF_REG_0, BPF_REG_2), +			BPF_ALU64_IMM(BPF_ADD, BPF_REG_0, 8), +			BPF_JMP_REG(BPF_JLE, BPF_REG_0, BPF_REG_3, 1), +			BPF_EXIT_INSN(), +			BPF_MOV64_REG(BPF_REG_4, BPF_REG_10), +			BPF_ALU64_IMM(BPF_ADD, BPF_REG_4, -8), +			BPF_STX_MEM(BPF_DW, BPF_REG_4, BPF_REG_2, 0), +			BPF_RAW_INSN(BPF_JMP | BPF_CALL, 0, 1, 0, 3), +			BPF_LDX_MEM(BPF_DW, BPF_REG_4, BPF_REG_10, -8), +			BPF_LDX_MEM(BPF_W, BPF_REG_0, BPF_REG_4, 0), +			BPF_EXIT_INSN(), + +			/* subprog 1 */ +			BPF_LDX_MEM(BPF_W, BPF_REG_2, BPF_REG_1, +				    offsetof(struct __sk_buff, data)), +			BPF_LDX_MEM(BPF_W, BPF_REG_3, BPF_REG_1, +				    offsetof(struct __sk_buff, data_end)), +			BPF_MOV64_REG(BPF_REG_0, BPF_REG_2), +			BPF_ALU64_IMM(BPF_ADD, BPF_REG_0, 8), +			BPF_MOV64_IMM(BPF_REG_5, 0), +			/* spill unchecked pkt_ptr into stack of caller */ +			BPF_STX_MEM(BPF_DW, BPF_REG_4, BPF_REG_2, 0), +			BPF_JMP_REG(BPF_JGT, BPF_REG_0, BPF_REG_3, 2), +			BPF_MOV64_IMM(BPF_REG_5, 1), +			/* don't read back pkt_ptr from stack here */ +			/* write 4 bytes into packet */ +			BPF_ST_MEM(BPF_W, BPF_REG_2, 0, 0), +			BPF_MOV64_REG(BPF_REG_0, BPF_REG_5), +			BPF_EXIT_INSN(), +		}, +		.prog_type = BPF_PROG_TYPE_SCHED_CLS, +		.errstr = "invalid access to packet", +		.result = REJECT, +	}, +	{ +		"calls: caller stack init to zero or map_value_or_null", +		.insns = { +			BPF_MOV64_IMM(BPF_REG_0, 0), +			BPF_STX_MEM(BPF_DW, BPF_REG_10, BPF_REG_0, -8), +			BPF_MOV64_REG(BPF_REG_2, BPF_REG_10), +			BPF_ALU64_IMM(BPF_ADD, BPF_REG_2, -8), +			BPF_RAW_INSN(BPF_JMP | BPF_CALL, 0, 1, 0, 4), +			/* fetch map_value_or_null or const_zero from stack */ +			BPF_LDX_MEM(BPF_DW, BPF_REG_0, BPF_REG_10, -8), +			BPF_JMP_IMM(BPF_JEQ, BPF_REG_0, 0, 1), +			/* store into map_value */ +			BPF_ST_MEM(BPF_W, BPF_REG_0, 0, 0), +			BPF_EXIT_INSN(), + +			/* subprog 1 */ +			/* if (ctx == 0) return; */ +			BPF_JMP_IMM(BPF_JEQ, BPF_REG_1, 0, 8), +			/* else bpf_map_lookup() and *(fp - 8) = r0 */ +			BPF_MOV64_REG(BPF_REG_6, BPF_REG_2), +			BPF_MOV64_REG(BPF_REG_2, BPF_REG_10), +			BPF_ALU64_IMM(BPF_ADD, BPF_REG_2, -8), +			BPF_LD_MAP_FD(BPF_REG_1, 0), +			BPF_ST_MEM(BPF_DW, BPF_REG_10, -8, 0), +			BPF_RAW_INSN(BPF_JMP | BPF_CALL, 0, 0, 0, +				     BPF_FUNC_map_lookup_elem), +			/* write map_value_ptr_or_null into stack frame of main prog at fp-8 */ +			BPF_STX_MEM(BPF_DW, BPF_REG_6, BPF_REG_0, 0), +			BPF_EXIT_INSN(), +		}, +		.fixup_map1 = { 13 }, +		.result = ACCEPT, +		.prog_type = BPF_PROG_TYPE_XDP, +	}, +	{ +		"calls: stack init to zero and pruning", +		.insns = { +			/* first make allocated_stack 16 byte */ +			BPF_ST_MEM(BPF_DW, BPF_REG_10, -16, 0), +			/* now fork the execution such that the false branch +			 * of JGT insn will be verified second and it skisp zero +			 * init of fp-8 stack slot. If stack liveness marking +			 * is missing live_read marks from call map_lookup +			 * processing then pruning will incorrectly assume +			 * that fp-8 stack slot was unused in the fall-through +			 * branch and will accept the program incorrectly +			 */ +			BPF_JMP_IMM(BPF_JGT, BPF_REG_1, 2, 2), +			BPF_ST_MEM(BPF_DW, BPF_REG_10, -8, 0), +			BPF_JMP_IMM(BPF_JA, 0, 0, 0), +			BPF_MOV64_REG(BPF_REG_2, BPF_REG_10), +			BPF_ALU64_IMM(BPF_ADD, BPF_REG_2, -8), +			BPF_LD_MAP_FD(BPF_REG_1, 0), +			BPF_RAW_INSN(BPF_JMP | BPF_CALL, 0, 0, 0, +				     BPF_FUNC_map_lookup_elem), +			BPF_EXIT_INSN(), +		}, +		.fixup_map2 = { 6 }, +		.errstr = "invalid indirect read from stack off -8+0 size 8", +		.result = REJECT, +		.prog_type = BPF_PROG_TYPE_XDP, +	}, +	{ +		"search pruning: all branches should be verified (nop operation)", +		.insns = { +			BPF_MOV64_REG(BPF_REG_2, BPF_REG_10), +			BPF_ALU64_IMM(BPF_ADD, BPF_REG_2, -8), +			BPF_ST_MEM(BPF_DW, BPF_REG_2, 0, 0), +			BPF_LD_MAP_FD(BPF_REG_1, 0), +			BPF_EMIT_CALL(BPF_FUNC_map_lookup_elem), +			BPF_JMP_IMM(BPF_JEQ, BPF_REG_0, 0, 11), +			BPF_LDX_MEM(BPF_DW, BPF_REG_3, BPF_REG_0, 0), +			BPF_JMP_IMM(BPF_JEQ, BPF_REG_3, 0xbeef, 2), +			BPF_MOV64_IMM(BPF_REG_4, 0), +			BPF_JMP_A(1), +			BPF_MOV64_IMM(BPF_REG_4, 1), +			BPF_STX_MEM(BPF_DW, BPF_REG_10, BPF_REG_4, -16), +			BPF_EMIT_CALL(BPF_FUNC_ktime_get_ns), +			BPF_LDX_MEM(BPF_DW, BPF_REG_5, BPF_REG_10, -16), +			BPF_JMP_IMM(BPF_JEQ, BPF_REG_5, 0, 2), +			BPF_MOV64_IMM(BPF_REG_6, 0), +			BPF_ST_MEM(BPF_DW, BPF_REG_6, 0, 0xdead), +			BPF_EXIT_INSN(), +		}, +		.fixup_map1 = { 3 }, +		.errstr = "R6 invalid mem access 'inv'", +		.result = REJECT, +		.prog_type = BPF_PROG_TYPE_TRACEPOINT, +	}, +	{ +		"search pruning: all branches should be verified (invalid stack access)", +		.insns = { +			BPF_MOV64_REG(BPF_REG_2, BPF_REG_10), +			BPF_ALU64_IMM(BPF_ADD, BPF_REG_2, -8), +			BPF_ST_MEM(BPF_DW, BPF_REG_2, 0, 0), +			BPF_LD_MAP_FD(BPF_REG_1, 0), +			BPF_EMIT_CALL(BPF_FUNC_map_lookup_elem), +			BPF_JMP_IMM(BPF_JEQ, BPF_REG_0, 0, 8), +			BPF_LDX_MEM(BPF_DW, BPF_REG_3, BPF_REG_0, 0), +			BPF_MOV64_IMM(BPF_REG_4, 0), +			BPF_JMP_IMM(BPF_JEQ, BPF_REG_3, 0xbeef, 2), +			BPF_STX_MEM(BPF_DW, BPF_REG_10, BPF_REG_4, -16), +			BPF_JMP_A(1), +			BPF_STX_MEM(BPF_DW, BPF_REG_10, BPF_REG_4, -24), +			BPF_EMIT_CALL(BPF_FUNC_ktime_get_ns), +			BPF_LDX_MEM(BPF_DW, BPF_REG_5, BPF_REG_10, -16), +			BPF_EXIT_INSN(), +		}, +		.fixup_map1 = { 3 }, +		.errstr = "invalid read from stack off -16+0 size 8", +		.result = REJECT, +		.prog_type = BPF_PROG_TYPE_TRACEPOINT, +	},  };  static int probe_filter_length(const struct bpf_insn *fp) @@ -8937,10 +11274,12 @@ static void do_test_single(struct bpf_test *test, bool unpriv,  	int fd_prog, expected_ret, reject_from_alignment;  	struct bpf_insn *prog = test->insns;  	int prog_len = probe_filter_length(prog); +	char data_in[TEST_DATA_LEN] = {};  	int prog_type = test->prog_type;  	int map_fds[MAX_NR_MAPS];  	const char *expected_err; -	int i; +	uint32_t retval; +	int i, err;  	for (i = 0; i < MAX_NR_MAPS; i++)  		map_fds[i] = -1; @@ -8983,6 +11322,19 @@ static void do_test_single(struct bpf_test *test, bool unpriv,  		}  	} +	if (fd_prog >= 0) { +		err = bpf_prog_test_run(fd_prog, 1, data_in, sizeof(data_in), +					NULL, NULL, &retval, NULL); +		if (err && errno != 524/*ENOTSUPP*/ && errno != EPERM) { +			printf("Unexpected bpf_prog_test_run error\n"); +			goto fail_log; +		} +		if (!err && retval != test->retval && +		    test->retval != POINTER_VALUE) { +			printf("FAIL retval %d != %d\n", retval, test->retval); +			goto fail_log; +		} +	}  	(*passes)++;  	printf("OK%s\n", reject_from_alignment ?  	       " (NOTE: reject due to unknown alignment)" : ""); diff --git a/tools/testing/selftests/bpf/test_xdp_meta.sh b/tools/testing/selftests/bpf/test_xdp_meta.sh index 307aa856cee3..637fcf4fe4e3 100755 --- a/tools/testing/selftests/bpf/test_xdp_meta.sh +++ b/tools/testing/selftests/bpf/test_xdp_meta.sh @@ -9,6 +9,7 @@ cleanup()  	fi  	set +e +	ip link del veth1 2> /dev/null  	ip netns del ns1 2> /dev/null  	ip netns del ns2 2> /dev/null  } diff --git a/tools/testing/selftests/bpf/test_xdp_noinline.c b/tools/testing/selftests/bpf/test_xdp_noinline.c new file mode 100644 index 000000000000..5e4aac74f9d0 --- /dev/null +++ b/tools/testing/selftests/bpf/test_xdp_noinline.c @@ -0,0 +1,833 @@ +// SPDX-License-Identifier: GPL-2.0 +// Copyright (c) 2017 Facebook +#include <stddef.h> +#include <stdbool.h> +#include <string.h> +#include <linux/pkt_cls.h> +#include <linux/bpf.h> +#include <linux/in.h> +#include <linux/if_ether.h> +#include <linux/ip.h> +#include <linux/ipv6.h> +#include <linux/icmp.h> +#include <linux/icmpv6.h> +#include <linux/tcp.h> +#include <linux/udp.h> +#include "bpf_helpers.h" + +#define bpf_printk(fmt, ...)				\ +({							\ +	char ____fmt[] = fmt;				\ +	bpf_trace_printk(____fmt, sizeof(____fmt),	\ +			##__VA_ARGS__);			\ +}) + +static __u32 rol32(__u32 word, unsigned int shift) +{ +	return (word << shift) | (word >> ((-shift) & 31)); +} + +/* copy paste of jhash from kernel sources to make sure llvm + * can compile it into valid sequence of bpf instructions + */ +#define __jhash_mix(a, b, c)			\ +{						\ +	a -= c;  a ^= rol32(c, 4);  c += b;	\ +	b -= a;  b ^= rol32(a, 6);  a += c;	\ +	c -= b;  c ^= rol32(b, 8);  b += a;	\ +	a -= c;  a ^= rol32(c, 16); c += b;	\ +	b -= a;  b ^= rol32(a, 19); a += c;	\ +	c -= b;  c ^= rol32(b, 4);  b += a;	\ +} + +#define __jhash_final(a, b, c)			\ +{						\ +	c ^= b; c -= rol32(b, 14);		\ +	a ^= c; a -= rol32(c, 11);		\ +	b ^= a; b -= rol32(a, 25);		\ +	c ^= b; c -= rol32(b, 16);		\ +	a ^= c; a -= rol32(c, 4);		\ +	b ^= a; b -= rol32(a, 14);		\ +	c ^= b; c -= rol32(b, 24);		\ +} + +#define JHASH_INITVAL		0xdeadbeef + +typedef unsigned int u32; + +static __attribute__ ((noinline)) +u32 jhash(const void *key, u32 length, u32 initval) +{ +	u32 a, b, c; +	const unsigned char *k = key; + +	a = b = c = JHASH_INITVAL + length + initval; + +	while (length > 12) { +		a += *(u32 *)(k); +		b += *(u32 *)(k + 4); +		c += *(u32 *)(k + 8); +		__jhash_mix(a, b, c); +		length -= 12; +		k += 12; +	} +	switch (length) { +	case 12: c += (u32)k[11]<<24; +	case 11: c += (u32)k[10]<<16; +	case 10: c += (u32)k[9]<<8; +	case 9:  c += k[8]; +	case 8:  b += (u32)k[7]<<24; +	case 7:  b += (u32)k[6]<<16; +	case 6:  b += (u32)k[5]<<8; +	case 5:  b += k[4]; +	case 4:  a += (u32)k[3]<<24; +	case 3:  a += (u32)k[2]<<16; +	case 2:  a += (u32)k[1]<<8; +	case 1:  a += k[0]; +		 __jhash_final(a, b, c); +	case 0: /* Nothing left to add */ +		break; +	} + +	return c; +} + +static __attribute__ ((noinline)) +u32 __jhash_nwords(u32 a, u32 b, u32 c, u32 initval) +{ +	a += initval; +	b += initval; +	c += initval; +	__jhash_final(a, b, c); +	return c; +} + +static __attribute__ ((noinline)) +u32 jhash_2words(u32 a, u32 b, u32 initval) +{ +	return __jhash_nwords(a, b, 0, initval + JHASH_INITVAL + (2 << 2)); +} + +struct flow_key { +	union { +		__be32 src; +		__be32 srcv6[4]; +	}; +	union { +		__be32 dst; +		__be32 dstv6[4]; +	}; +	union { +		__u32 ports; +		__u16 port16[2]; +	}; +	__u8 proto; +}; + +struct packet_description { +	struct flow_key flow; +	__u8 flags; +}; + +struct ctl_value { +	union { +		__u64 value; +		__u32 ifindex; +		__u8 mac[6]; +	}; +}; + +struct vip_definition { +	union { +		__be32 vip; +		__be32 vipv6[4]; +	}; +	__u16 port; +	__u16 family; +	__u8 proto; +}; + +struct vip_meta { +	__u32 flags; +	__u32 vip_num; +}; + +struct real_pos_lru { +	__u32 pos; +	__u64 atime; +}; + +struct real_definition { +	union { +		__be32 dst; +		__be32 dstv6[4]; +	}; +	__u8 flags; +}; + +struct lb_stats { +	__u64 v2; +	__u64 v1; +}; + +struct bpf_map_def __attribute__ ((section("maps"), used)) vip_map = { +	.type = BPF_MAP_TYPE_HASH, +	.key_size = sizeof(struct vip_definition), +	.value_size = sizeof(struct vip_meta), +	.max_entries = 512, +	.map_flags = 0, +}; + +struct bpf_map_def __attribute__ ((section("maps"), used)) lru_cache = { +	.type = BPF_MAP_TYPE_LRU_HASH, +	.key_size = sizeof(struct flow_key), +	.value_size = sizeof(struct real_pos_lru), +	.max_entries = 300, +	.map_flags = 1U << 1, +}; + +struct bpf_map_def __attribute__ ((section("maps"), used)) ch_rings = { +	.type = BPF_MAP_TYPE_ARRAY, +	.key_size = sizeof(__u32), +	.value_size = sizeof(__u32), +	.max_entries = 12 * 655, +	.map_flags = 0, +}; + +struct bpf_map_def __attribute__ ((section("maps"), used)) reals = { +	.type = BPF_MAP_TYPE_ARRAY, +	.key_size = sizeof(__u32), +	.value_size = sizeof(struct real_definition), +	.max_entries = 40, +	.map_flags = 0, +}; + +struct bpf_map_def __attribute__ ((section("maps"), used)) stats = { +	.type = BPF_MAP_TYPE_PERCPU_ARRAY, +	.key_size = sizeof(__u32), +	.value_size = sizeof(struct lb_stats), +	.max_entries = 515, +	.map_flags = 0, +}; + +struct bpf_map_def __attribute__ ((section("maps"), used)) ctl_array = { +	.type = BPF_MAP_TYPE_ARRAY, +	.key_size = sizeof(__u32), +	.value_size = sizeof(struct ctl_value), +	.max_entries = 16, +	.map_flags = 0, +}; + +struct eth_hdr { +	unsigned char eth_dest[6]; +	unsigned char eth_source[6]; +	unsigned short eth_proto; +}; + +static inline __u64 calc_offset(bool is_ipv6, bool is_icmp) +{ +	__u64 off = sizeof(struct eth_hdr); +	if (is_ipv6) { +		off += sizeof(struct ipv6hdr); +		if (is_icmp) +			off += sizeof(struct icmp6hdr) + sizeof(struct ipv6hdr); +	} else { +		off += sizeof(struct iphdr); +		if (is_icmp) +			off += sizeof(struct icmphdr) + sizeof(struct iphdr); +	} +	return off; +} + +static __attribute__ ((noinline)) +bool parse_udp(void *data, void *data_end, +	       bool is_ipv6, struct packet_description *pckt) +{ + +	bool is_icmp = !((pckt->flags & (1 << 0)) == 0); +	__u64 off = calc_offset(is_ipv6, is_icmp); +	struct udphdr *udp; +	udp = data + off; + +	if (udp + 1 > data_end) +		return 0; +	if (!is_icmp) { +		pckt->flow.port16[0] = udp->source; +		pckt->flow.port16[1] = udp->dest; +	} else { +		pckt->flow.port16[0] = udp->dest; +		pckt->flow.port16[1] = udp->source; +	} +	return 1; +} + +static __attribute__ ((noinline)) +bool parse_tcp(void *data, void *data_end, +	       bool is_ipv6, struct packet_description *pckt) +{ + +	bool is_icmp = !((pckt->flags & (1 << 0)) == 0); +	__u64 off = calc_offset(is_ipv6, is_icmp); +	struct tcphdr *tcp; + +	tcp = data + off; +	if (tcp + 1 > data_end) +		return 0; +	if (tcp->syn) +		pckt->flags |= (1 << 1); +	if (!is_icmp) { +		pckt->flow.port16[0] = tcp->source; +		pckt->flow.port16[1] = tcp->dest; +	} else { +		pckt->flow.port16[0] = tcp->dest; +		pckt->flow.port16[1] = tcp->source; +	} +	return 1; +} + +static __attribute__ ((noinline)) +bool encap_v6(struct xdp_md *xdp, struct ctl_value *cval, +	      struct packet_description *pckt, +	      struct real_definition *dst, __u32 pkt_bytes) +{ +	struct eth_hdr *new_eth; +	struct eth_hdr *old_eth; +	struct ipv6hdr *ip6h; +	__u32 ip_suffix; +	void *data_end; +	void *data; + +	if (bpf_xdp_adjust_head(xdp, 0 - (int)sizeof(struct ipv6hdr))) +		return 0; +	data = (void *)(long)xdp->data; +	data_end = (void *)(long)xdp->data_end; +	new_eth = data; +	ip6h = data + sizeof(struct eth_hdr); +	old_eth = data + sizeof(struct ipv6hdr); +	if (new_eth + 1 > data_end || +	    old_eth + 1 > data_end || ip6h + 1 > data_end) +		return 0; +	memcpy(new_eth->eth_dest, cval->mac, 6); +	memcpy(new_eth->eth_source, old_eth->eth_dest, 6); +	new_eth->eth_proto = 56710; +	ip6h->version = 6; +	ip6h->priority = 0; +	memset(ip6h->flow_lbl, 0, sizeof(ip6h->flow_lbl)); + +	ip6h->nexthdr = IPPROTO_IPV6; +	ip_suffix = pckt->flow.srcv6[3] ^ pckt->flow.port16[0]; +	ip6h->payload_len = +	    __builtin_bswap16(pkt_bytes + sizeof(struct ipv6hdr)); +	ip6h->hop_limit = 4; + +	ip6h->saddr.in6_u.u6_addr32[0] = 1; +	ip6h->saddr.in6_u.u6_addr32[1] = 2; +	ip6h->saddr.in6_u.u6_addr32[2] = 3; +	ip6h->saddr.in6_u.u6_addr32[3] = ip_suffix; +	memcpy(ip6h->daddr.in6_u.u6_addr32, dst->dstv6, 16); +	return 1; +} + +static __attribute__ ((noinline)) +bool encap_v4(struct xdp_md *xdp, struct ctl_value *cval, +	      struct packet_description *pckt, +	      struct real_definition *dst, __u32 pkt_bytes) +{ + +	__u32 ip_suffix = __builtin_bswap16(pckt->flow.port16[0]); +	struct eth_hdr *new_eth; +	struct eth_hdr *old_eth; +	__u16 *next_iph_u16; +	struct iphdr *iph; +	__u32 csum = 0; +	void *data_end; +	void *data; + +	ip_suffix <<= 15; +	ip_suffix ^= pckt->flow.src; +	if (bpf_xdp_adjust_head(xdp, 0 - (int)sizeof(struct iphdr))) +		return 0; +	data = (void *)(long)xdp->data; +	data_end = (void *)(long)xdp->data_end; +	new_eth = data; +	iph = data + sizeof(struct eth_hdr); +	old_eth = data + sizeof(struct iphdr); +	if (new_eth + 1 > data_end || +	    old_eth + 1 > data_end || iph + 1 > data_end) +		return 0; +	memcpy(new_eth->eth_dest, cval->mac, 6); +	memcpy(new_eth->eth_source, old_eth->eth_dest, 6); +	new_eth->eth_proto = 8; +	iph->version = 4; +	iph->ihl = 5; +	iph->frag_off = 0; +	iph->protocol = IPPROTO_IPIP; +	iph->check = 0; +	iph->tos = 1; +	iph->tot_len = __builtin_bswap16(pkt_bytes + sizeof(struct iphdr)); +	/* don't update iph->daddr, since it will overwrite old eth_proto +	 * and multiple iterations of bpf_prog_run() will fail +	 */ + +	iph->saddr = ((0xFFFF0000 & ip_suffix) | 4268) ^ dst->dst; +	iph->ttl = 4; + +	next_iph_u16 = (__u16 *) iph; +#pragma clang loop unroll(full) +	for (int i = 0; i < sizeof(struct iphdr) >> 1; i++) +		csum += *next_iph_u16++; +	iph->check = ~((csum & 0xffff) + (csum >> 16)); +	if (bpf_xdp_adjust_head(xdp, (int)sizeof(struct iphdr))) +		return 0; +	return 1; +} + +static __attribute__ ((noinline)) +bool decap_v6(struct xdp_md *xdp, void **data, void **data_end, bool inner_v4) +{ +	struct eth_hdr *new_eth; +	struct eth_hdr *old_eth; + +	old_eth = *data; +	new_eth = *data + sizeof(struct ipv6hdr); +	memcpy(new_eth->eth_source, old_eth->eth_source, 6); +	memcpy(new_eth->eth_dest, old_eth->eth_dest, 6); +	if (inner_v4) +		new_eth->eth_proto = 8; +	else +		new_eth->eth_proto = 56710; +	if (bpf_xdp_adjust_head(xdp, (int)sizeof(struct ipv6hdr))) +		return 0; +	*data = (void *)(long)xdp->data; +	*data_end = (void *)(long)xdp->data_end; +	return 1; +} + +static __attribute__ ((noinline)) +bool decap_v4(struct xdp_md *xdp, void **data, void **data_end) +{ +	struct eth_hdr *new_eth; +	struct eth_hdr *old_eth; + +	old_eth = *data; +	new_eth = *data + sizeof(struct iphdr); +	memcpy(new_eth->eth_source, old_eth->eth_source, 6); +	memcpy(new_eth->eth_dest, old_eth->eth_dest, 6); +	new_eth->eth_proto = 8; +	if (bpf_xdp_adjust_head(xdp, (int)sizeof(struct iphdr))) +		return 0; +	*data = (void *)(long)xdp->data; +	*data_end = (void *)(long)xdp->data_end; +	return 1; +} + +static __attribute__ ((noinline)) +int swap_mac_and_send(void *data, void *data_end) +{ +	unsigned char tmp_mac[6]; +	struct eth_hdr *eth; + +	eth = data; +	memcpy(tmp_mac, eth->eth_source, 6); +	memcpy(eth->eth_source, eth->eth_dest, 6); +	memcpy(eth->eth_dest, tmp_mac, 6); +	return XDP_TX; +} + +static __attribute__ ((noinline)) +int send_icmp_reply(void *data, void *data_end) +{ +	struct icmphdr *icmp_hdr; +	__u16 *next_iph_u16; +	__u32 tmp_addr = 0; +	struct iphdr *iph; +	__u32 csum1 = 0; +	__u32 csum = 0; +	__u64 off = 0; + +	if (data + sizeof(struct eth_hdr) +	     + sizeof(struct iphdr) + sizeof(struct icmphdr) > data_end) +		return XDP_DROP; +	off += sizeof(struct eth_hdr); +	iph = data + off; +	off += sizeof(struct iphdr); +	icmp_hdr = data + off; +	icmp_hdr->type = 0; +	icmp_hdr->checksum += 0x0007; +	iph->ttl = 4; +	tmp_addr = iph->daddr; +	iph->daddr = iph->saddr; +	iph->saddr = tmp_addr; +	iph->check = 0; +	next_iph_u16 = (__u16 *) iph; +#pragma clang loop unroll(full) +	for (int i = 0; i < sizeof(struct iphdr) >> 1; i++) +		csum += *next_iph_u16++; +	iph->check = ~((csum & 0xffff) + (csum >> 16)); +	return swap_mac_and_send(data, data_end); +} + +static __attribute__ ((noinline)) +int send_icmp6_reply(void *data, void *data_end) +{ +	struct icmp6hdr *icmp_hdr; +	struct ipv6hdr *ip6h; +	__be32 tmp_addr[4]; +	__u64 off = 0; + +	if (data + sizeof(struct eth_hdr) +	     + sizeof(struct ipv6hdr) + sizeof(struct icmp6hdr) > data_end) +		return XDP_DROP; +	off += sizeof(struct eth_hdr); +	ip6h = data + off; +	off += sizeof(struct ipv6hdr); +	icmp_hdr = data + off; +	icmp_hdr->icmp6_type = 129; +	icmp_hdr->icmp6_cksum -= 0x0001; +	ip6h->hop_limit = 4; +	memcpy(tmp_addr, ip6h->saddr.in6_u.u6_addr32, 16); +	memcpy(ip6h->saddr.in6_u.u6_addr32, ip6h->daddr.in6_u.u6_addr32, 16); +	memcpy(ip6h->daddr.in6_u.u6_addr32, tmp_addr, 16); +	return swap_mac_and_send(data, data_end); +} + +static __attribute__ ((noinline)) +int parse_icmpv6(void *data, void *data_end, __u64 off, +		 struct packet_description *pckt) +{ +	struct icmp6hdr *icmp_hdr; +	struct ipv6hdr *ip6h; + +	icmp_hdr = data + off; +	if (icmp_hdr + 1 > data_end) +		return XDP_DROP; +	if (icmp_hdr->icmp6_type == 128) +		return send_icmp6_reply(data, data_end); +	if (icmp_hdr->icmp6_type != 3) +		return XDP_PASS; +	off += sizeof(struct icmp6hdr); +	ip6h = data + off; +	if (ip6h + 1 > data_end) +		return XDP_DROP; +	pckt->flow.proto = ip6h->nexthdr; +	pckt->flags |= (1 << 0); +	memcpy(pckt->flow.srcv6, ip6h->daddr.in6_u.u6_addr32, 16); +	memcpy(pckt->flow.dstv6, ip6h->saddr.in6_u.u6_addr32, 16); +	return -1; +} + +static __attribute__ ((noinline)) +int parse_icmp(void *data, void *data_end, __u64 off, +	       struct packet_description *pckt) +{ +	struct icmphdr *icmp_hdr; +	struct iphdr *iph; + +	icmp_hdr = data + off; +	if (icmp_hdr + 1 > data_end) +		return XDP_DROP; +	if (icmp_hdr->type == 8) +		return send_icmp_reply(data, data_end); +	if ((icmp_hdr->type != 3) || (icmp_hdr->code != 4)) +		return XDP_PASS; +	off += sizeof(struct icmphdr); +	iph = data + off; +	if (iph + 1 > data_end) +		return XDP_DROP; +	if (iph->ihl != 5) +		return XDP_DROP; +	pckt->flow.proto = iph->protocol; +	pckt->flags |= (1 << 0); +	pckt->flow.src = iph->daddr; +	pckt->flow.dst = iph->saddr; +	return -1; +} + +static __attribute__ ((noinline)) +__u32 get_packet_hash(struct packet_description *pckt, +		      bool hash_16bytes) +{ +	if (hash_16bytes) +		return jhash_2words(jhash(pckt->flow.srcv6, 16, 12), +				    pckt->flow.ports, 24); +	else +		return jhash_2words(pckt->flow.src, pckt->flow.ports, +				    24); +} + +__attribute__ ((noinline)) +static bool get_packet_dst(struct real_definition **real, +			   struct packet_description *pckt, +			   struct vip_meta *vip_info, +			   bool is_ipv6, void *lru_map) +{ +	struct real_pos_lru new_dst_lru = { }; +	bool hash_16bytes = is_ipv6; +	__u32 *real_pos, hash, key; +	__u64 cur_time; + +	if (vip_info->flags & (1 << 2)) +		hash_16bytes = 1; +	if (vip_info->flags & (1 << 3)) { +		pckt->flow.port16[0] = pckt->flow.port16[1]; +		memset(pckt->flow.srcv6, 0, 16); +	} +	hash = get_packet_hash(pckt, hash_16bytes); +	if (hash != 0x358459b7 /* jhash of ipv4 packet */  && +	    hash != 0x2f4bc6bb /* jhash of ipv6 packet */) +		return 0; +	key = 2 * vip_info->vip_num + hash % 2; +	real_pos = bpf_map_lookup_elem(&ch_rings, &key); +	if (!real_pos) +		return 0; +	key = *real_pos; +	*real = bpf_map_lookup_elem(&reals, &key); +	if (!(*real)) +		return 0; +	if (!(vip_info->flags & (1 << 1))) { +		__u32 conn_rate_key = 512 + 2; +		struct lb_stats *conn_rate_stats = +		    bpf_map_lookup_elem(&stats, &conn_rate_key); + +		if (!conn_rate_stats) +			return 1; +		cur_time = bpf_ktime_get_ns(); +		if ((cur_time - conn_rate_stats->v2) >> 32 > 0xffFFFF) { +			conn_rate_stats->v1 = 1; +			conn_rate_stats->v2 = cur_time; +		} else { +			conn_rate_stats->v1 += 1; +			if (conn_rate_stats->v1 >= 1) +				return 1; +		} +		if (pckt->flow.proto == IPPROTO_UDP) +			new_dst_lru.atime = cur_time; +		new_dst_lru.pos = key; +		bpf_map_update_elem(lru_map, &pckt->flow, &new_dst_lru, 0); +	} +	return 1; +} + +__attribute__ ((noinline)) +static void connection_table_lookup(struct real_definition **real, +				    struct packet_description *pckt, +				    void *lru_map) +{ + +	struct real_pos_lru *dst_lru; +	__u64 cur_time; +	__u32 key; + +	dst_lru = bpf_map_lookup_elem(lru_map, &pckt->flow); +	if (!dst_lru) +		return; +	if (pckt->flow.proto == IPPROTO_UDP) { +		cur_time = bpf_ktime_get_ns(); +		if (cur_time - dst_lru->atime > 300000) +			return; +		dst_lru->atime = cur_time; +	} +	key = dst_lru->pos; +	*real = bpf_map_lookup_elem(&reals, &key); +} + +/* don't believe your eyes! + * below function has 6 arguments whereas bpf and llvm allow maximum of 5 + * but since it's _static_ llvm can optimize one argument away + */ +__attribute__ ((noinline)) +static int process_l3_headers_v6(struct packet_description *pckt, +				 __u8 *protocol, __u64 off, +				 __u16 *pkt_bytes, void *data, +				 void *data_end) +{ +	struct ipv6hdr *ip6h; +	__u64 iph_len; +	int action; + +	ip6h = data + off; +	if (ip6h + 1 > data_end) +		return XDP_DROP; +	iph_len = sizeof(struct ipv6hdr); +	*protocol = ip6h->nexthdr; +	pckt->flow.proto = *protocol; +	*pkt_bytes = __builtin_bswap16(ip6h->payload_len); +	off += iph_len; +	if (*protocol == 45) { +		return XDP_DROP; +	} else if (*protocol == 59) { +		action = parse_icmpv6(data, data_end, off, pckt); +		if (action >= 0) +			return action; +	} else { +		memcpy(pckt->flow.srcv6, ip6h->saddr.in6_u.u6_addr32, 16); +		memcpy(pckt->flow.dstv6, ip6h->daddr.in6_u.u6_addr32, 16); +	} +	return -1; +} + +__attribute__ ((noinline)) +static int process_l3_headers_v4(struct packet_description *pckt, +				 __u8 *protocol, __u64 off, +				 __u16 *pkt_bytes, void *data, +				 void *data_end) +{ +	struct iphdr *iph; +	__u64 iph_len; +	int action; + +	iph = data + off; +	if (iph + 1 > data_end) +		return XDP_DROP; +	if (iph->ihl != 5) +		return XDP_DROP; +	*protocol = iph->protocol; +	pckt->flow.proto = *protocol; +	*pkt_bytes = __builtin_bswap16(iph->tot_len); +	off += 20; +	if (iph->frag_off & 65343) +		return XDP_DROP; +	if (*protocol == IPPROTO_ICMP) { +		action = parse_icmp(data, data_end, off, pckt); +		if (action >= 0) +			return action; +	} else { +		pckt->flow.src = iph->saddr; +		pckt->flow.dst = iph->daddr; +	} +	return -1; +} + +__attribute__ ((noinline)) +static int process_packet(void *data, __u64 off, void *data_end, +			  bool is_ipv6, struct xdp_md *xdp) +{ + +	struct real_definition *dst = NULL; +	struct packet_description pckt = { }; +	struct vip_definition vip = { }; +	struct lb_stats *data_stats; +	struct eth_hdr *eth = data; +	void *lru_map = &lru_cache; +	struct vip_meta *vip_info; +	__u32 lru_stats_key = 513; +	__u32 mac_addr_pos = 0; +	__u32 stats_key = 512; +	struct ctl_value *cval; +	__u16 pkt_bytes; +	__u64 iph_len; +	__u8 protocol; +	__u32 vip_num; +	int action; + +	if (is_ipv6) +		action = process_l3_headers_v6(&pckt, &protocol, off, +					       &pkt_bytes, data, data_end); +	else +		action = process_l3_headers_v4(&pckt, &protocol, off, +					       &pkt_bytes, data, data_end); +	if (action >= 0) +		return action; +	protocol = pckt.flow.proto; +	if (protocol == IPPROTO_TCP) { +		if (!parse_tcp(data, data_end, is_ipv6, &pckt)) +			return XDP_DROP; +	} else if (protocol == IPPROTO_UDP) { +		if (!parse_udp(data, data_end, is_ipv6, &pckt)) +			return XDP_DROP; +	} else { +		return XDP_TX; +	} + +	if (is_ipv6) +		memcpy(vip.vipv6, pckt.flow.dstv6, 16); +	else +		vip.vip = pckt.flow.dst; +	vip.port = pckt.flow.port16[1]; +	vip.proto = pckt.flow.proto; +	vip_info = bpf_map_lookup_elem(&vip_map, &vip); +	if (!vip_info) { +		vip.port = 0; +		vip_info = bpf_map_lookup_elem(&vip_map, &vip); +		if (!vip_info) +			return XDP_PASS; +		if (!(vip_info->flags & (1 << 4))) +			pckt.flow.port16[1] = 0; +	} +	if (data_end - data > 1400) +		return XDP_DROP; +	data_stats = bpf_map_lookup_elem(&stats, &stats_key); +	if (!data_stats) +		return XDP_DROP; +	data_stats->v1 += 1; +	if (!dst) { +		if (vip_info->flags & (1 << 0)) +			pckt.flow.port16[0] = 0; +		if (!(pckt.flags & (1 << 1)) && !(vip_info->flags & (1 << 1))) +			connection_table_lookup(&dst, &pckt, lru_map); +		if (dst) +			goto out; +		if (pckt.flow.proto == IPPROTO_TCP) { +			struct lb_stats *lru_stats = +			    bpf_map_lookup_elem(&stats, &lru_stats_key); + +			if (!lru_stats) +				return XDP_DROP; +			if (pckt.flags & (1 << 1)) +				lru_stats->v1 += 1; +			else +				lru_stats->v2 += 1; +		} +		if (!get_packet_dst(&dst, &pckt, vip_info, is_ipv6, lru_map)) +			return XDP_DROP; +		data_stats->v2 += 1; +	} +out: +	cval = bpf_map_lookup_elem(&ctl_array, &mac_addr_pos); +	if (!cval) +		return XDP_DROP; +	if (dst->flags & (1 << 0)) { +		if (!encap_v6(xdp, cval, &pckt, dst, pkt_bytes)) +			return XDP_DROP; +	} else { +		if (!encap_v4(xdp, cval, &pckt, dst, pkt_bytes)) +			return XDP_DROP; +	} +	vip_num = vip_info->vip_num; +	data_stats = bpf_map_lookup_elem(&stats, &vip_num); +	if (!data_stats) +		return XDP_DROP; +	data_stats->v1 += 1; +	data_stats->v2 += pkt_bytes; + +	data = (void *)(long)xdp->data; +	data_end = (void *)(long)xdp->data_end; +	if (data + 4 > data_end) +		return XDP_DROP; +	*(u32 *)data = dst->dst; +	return XDP_DROP; +} + +__attribute__ ((section("xdp-test"), used)) +int balancer_ingress(struct xdp_md *ctx) +{ +	void *data = (void *)(long)ctx->data; +	void *data_end = (void *)(long)ctx->data_end; +	struct eth_hdr *eth = data; +	__u32 eth_proto; +	__u32 nh_off; + +	nh_off = sizeof(struct eth_hdr); +	if (data + nh_off > data_end) +		return XDP_DROP; +	eth_proto = eth->eth_proto; +	if (eth_proto == 8) +		return process_packet(data, nh_off, data_end, 0, ctx); +	else if (eth_proto == 56710) +		return process_packet(data, nh_off, data_end, 1, ctx); +	else +		return XDP_DROP; +} + +char _license[] __attribute__ ((section("license"), used)) = "GPL"; +int _version __attribute__ ((section("version"), used)) = 1; diff --git a/tools/testing/selftests/bpf/test_xdp_redirect.sh b/tools/testing/selftests/bpf/test_xdp_redirect.sh index 344a3656dea6..c4b17e08d431 100755 --- a/tools/testing/selftests/bpf/test_xdp_redirect.sh +++ b/tools/testing/selftests/bpf/test_xdp_redirect.sh @@ -19,6 +19,8 @@ cleanup()  	fi  	set +e +	ip link del veth1 2> /dev/null +	ip link del veth2 2> /dev/null  	ip netns del ns1 2> /dev/null  	ip netns del ns2 2> /dev/null  } |