diff options
Diffstat (limited to 'tools/testing/selftests/bpf/prog_tests')
33 files changed, 1976 insertions, 167 deletions
| diff --git a/tools/testing/selftests/bpf/prog_tests/arena_atomics.c b/tools/testing/selftests/bpf/prog_tests/arena_atomics.c index 0807a48a58ee..26e7c06c6cb4 100644 --- a/tools/testing/selftests/bpf/prog_tests/arena_atomics.c +++ b/tools/testing/selftests/bpf/prog_tests/arena_atomics.c @@ -146,6 +146,22 @@ static void test_xchg(struct arena_atomics *skel)  	ASSERT_EQ(skel->arena->xchg32_result, 1, "xchg32_result");  } +static void test_uaf(struct arena_atomics *skel) +{ +	LIBBPF_OPTS(bpf_test_run_opts, topts); +	int err, prog_fd; + +	/* No need to attach it, just run it directly */ +	prog_fd = bpf_program__fd(skel->progs.uaf); +	err = bpf_prog_test_run_opts(prog_fd, &topts); +	if (!ASSERT_OK(err, "test_run_opts err")) +		return; +	if (!ASSERT_OK(topts.retval, "test_run_opts retval")) +		return; + +	ASSERT_EQ(skel->arena->uaf_recovery_fails, 0, "uaf_recovery_fails"); +} +  void test_arena_atomics(void)  {  	struct arena_atomics *skel; @@ -180,6 +196,8 @@ void test_arena_atomics(void)  		test_cmpxchg(skel);  	if (test__start_subtest("xchg"))  		test_xchg(skel); +	if (test__start_subtest("uaf")) +		test_uaf(skel);  cleanup:  	arena_atomics__destroy(skel); diff --git a/tools/testing/selftests/bpf/prog_tests/bpf_cookie.c b/tools/testing/selftests/bpf/prog_tests/bpf_cookie.c index 4407ea428e77..070c52c312e5 100644 --- a/tools/testing/selftests/bpf/prog_tests/bpf_cookie.c +++ b/tools/testing/selftests/bpf/prog_tests/bpf_cookie.c @@ -451,7 +451,7 @@ static void pe_subtest(struct test_bpf_cookie *skel)  	attr.type = PERF_TYPE_SOFTWARE;  	attr.config = PERF_COUNT_SW_CPU_CLOCK;  	attr.freq = 1; -	attr.sample_freq = 1000; +	attr.sample_freq = 10000;  	pfd = syscall(__NR_perf_event_open, &attr, -1, 0, -1, PERF_FLAG_FD_CLOEXEC);  	if (!ASSERT_GE(pfd, 0, "perf_fd"))  		goto cleanup; diff --git a/tools/testing/selftests/bpf/prog_tests/bpf_nf.c b/tools/testing/selftests/bpf/prog_tests/bpf_nf.c index b30ff6b3b81a..a4a1f93878d4 100644 --- a/tools/testing/selftests/bpf/prog_tests/bpf_nf.c +++ b/tools/testing/selftests/bpf/prog_tests/bpf_nf.c @@ -104,6 +104,7 @@ static void test_bpf_nf_ct(int mode)  	ASSERT_EQ(skel->bss->test_einval_bpf_tuple, -EINVAL, "Test EINVAL for NULL bpf_tuple");  	ASSERT_EQ(skel->bss->test_einval_reserved, -EINVAL, "Test EINVAL for reserved not set to 0"); +	ASSERT_EQ(skel->bss->test_einval_reserved_new, -EINVAL, "Test EINVAL for reserved in new struct not set to 0");  	ASSERT_EQ(skel->bss->test_einval_netns_id, -EINVAL, "Test EINVAL for netns_id < -1");  	ASSERT_EQ(skel->bss->test_einval_len_opts, -EINVAL, "Test EINVAL for len__opts != NF_BPF_CT_OPTS_SZ");  	ASSERT_EQ(skel->bss->test_eproto_l4proto, -EPROTO, "Test EPROTO for l4proto != TCP or UDP"); @@ -122,6 +123,12 @@ static void test_bpf_nf_ct(int mode)  	ASSERT_EQ(skel->bss->test_exist_lookup_mark, 43, "Test existing connection lookup ctmark");  	ASSERT_EQ(skel->data->test_snat_addr, 0, "Test for source natting");  	ASSERT_EQ(skel->data->test_dnat_addr, 0, "Test for destination natting"); +	ASSERT_EQ(skel->data->test_ct_zone_id_alloc_entry, 0, "Test for alloc new entry in specified ct zone"); +	ASSERT_EQ(skel->data->test_ct_zone_id_insert_entry, 0, "Test for insert new entry in specified ct zone"); +	ASSERT_EQ(skel->data->test_ct_zone_id_succ_lookup, 0, "Test for successful lookup in specified ct_zone"); +	ASSERT_EQ(skel->bss->test_ct_zone_dir_enoent_lookup, -ENOENT, "Test ENOENT for lookup with wrong ct zone dir"); +	ASSERT_EQ(skel->bss->test_ct_zone_id_enoent_lookup, -ENOENT, "Test ENOENT for lookup in wrong ct zone"); +  end:  	if (client_fd != -1)  		close(client_fd); diff --git a/tools/testing/selftests/bpf/prog_tests/bpf_tcp_ca.c b/tools/testing/selftests/bpf/prog_tests/bpf_tcp_ca.c index 0aca02532794..63422f4f3896 100644 --- a/tools/testing/selftests/bpf/prog_tests/bpf_tcp_ca.c +++ b/tools/testing/selftests/bpf/prog_tests/bpf_tcp_ca.c @@ -23,6 +23,11 @@  static const unsigned int total_bytes = 10 * 1024 * 1024;  static int expected_stg = 0xeB9F; +struct cb_opts { +	const char *cc; +	int map_fd; +}; +  static int settcpca(int fd, const char *tcp_ca)  {  	int err; @@ -34,55 +39,66 @@ static int settcpca(int fd, const char *tcp_ca)  	return 0;  } -static void do_test(const char *tcp_ca, const struct bpf_map *sk_stg_map) +static bool start_test(char *addr_str, +		       const struct network_helper_opts *srv_opts, +		       const struct network_helper_opts *cli_opts, +		       int *srv_fd, int *cli_fd)  { -	int lfd = -1, fd = -1; -	int err; +	*srv_fd = start_server_str(AF_INET6, SOCK_STREAM, addr_str, 0, srv_opts); +	if (!ASSERT_NEQ(*srv_fd, -1, "start_server_str")) +		goto err; -	lfd = start_server(AF_INET6, SOCK_STREAM, NULL, 0, 0); -	if (!ASSERT_NEQ(lfd, -1, "socket")) -		return; - -	fd = socket(AF_INET6, SOCK_STREAM, 0); -	if (!ASSERT_NEQ(fd, -1, "socket")) { -		close(lfd); -		return; -	} +	/* connect to server */ +	*cli_fd = connect_to_fd_opts(*srv_fd, SOCK_STREAM, cli_opts); +	if (!ASSERT_NEQ(*cli_fd, -1, "connect_to_fd_opts")) +		goto err; -	if (settcpca(lfd, tcp_ca) || settcpca(fd, tcp_ca)) -		goto done; +	return true; -	if (sk_stg_map) { -		err = bpf_map_update_elem(bpf_map__fd(sk_stg_map), &fd, -					  &expected_stg, BPF_NOEXIST); -		if (!ASSERT_OK(err, "bpf_map_update_elem(sk_stg_map)")) -			goto done; +err: +	if (*srv_fd != -1) { +		close(*srv_fd); +		*srv_fd = -1;  	} +	if (*cli_fd != -1) { +		close(*cli_fd); +		*cli_fd = -1; +	} +	return false; +} -	/* connect to server */ -	err = connect_fd_to_fd(fd, lfd, 0); -	if (!ASSERT_NEQ(err, -1, "connect")) -		goto done; - -	if (sk_stg_map) { -		int tmp_stg; +static void do_test(const struct network_helper_opts *opts) +{ +	int lfd = -1, fd = -1; -		err = bpf_map_lookup_elem(bpf_map__fd(sk_stg_map), &fd, -					  &tmp_stg); -		if (!ASSERT_ERR(err, "bpf_map_lookup_elem(sk_stg_map)") || -				!ASSERT_EQ(errno, ENOENT, "bpf_map_lookup_elem(sk_stg_map)")) -			goto done; -	} +	if (!start_test(NULL, opts, opts, &lfd, &fd)) +		goto done;  	ASSERT_OK(send_recv_data(lfd, fd, total_bytes), "send_recv_data");  done: -	close(lfd); -	close(fd); +	if (lfd != -1) +		close(lfd); +	if (fd != -1) +		close(fd); +} + +static int cc_cb(int fd, void *opts) +{ +	struct cb_opts *cb_opts = (struct cb_opts *)opts; + +	return settcpca(fd, cb_opts->cc);  }  static void test_cubic(void)  { +	struct cb_opts cb_opts = { +		.cc = "bpf_cubic", +	}; +	struct network_helper_opts opts = { +		.post_socket_cb	= cc_cb, +		.cb_opts	= &cb_opts, +	};  	struct bpf_cubic *cubic_skel;  	struct bpf_link *link; @@ -96,7 +112,7 @@ static void test_cubic(void)  		return;  	} -	do_test("bpf_cubic", NULL); +	do_test(&opts);  	ASSERT_EQ(cubic_skel->bss->bpf_cubic_acked_called, 1, "pkts_acked called"); @@ -104,8 +120,37 @@ static void test_cubic(void)  	bpf_cubic__destroy(cubic_skel);  } +static int stg_post_socket_cb(int fd, void *opts) +{ +	struct cb_opts *cb_opts = (struct cb_opts *)opts; +	int err; + +	err = settcpca(fd, cb_opts->cc); +	if (err) +		return err; + +	err = bpf_map_update_elem(cb_opts->map_fd, &fd, +				  &expected_stg, BPF_NOEXIST); +	if (!ASSERT_OK(err, "bpf_map_update_elem(sk_stg_map)")) +		return err; + +	return 0; +} +  static void test_dctcp(void)  { +	struct cb_opts cb_opts = { +		.cc = "bpf_dctcp", +	}; +	struct network_helper_opts opts = { +		.post_socket_cb	= cc_cb, +		.cb_opts	= &cb_opts, +	}; +	struct network_helper_opts cli_opts = { +		.post_socket_cb	= stg_post_socket_cb, +		.cb_opts	= &cb_opts, +	}; +	int lfd = -1, fd = -1, tmp_stg, err;  	struct bpf_dctcp *dctcp_skel;  	struct bpf_link *link; @@ -119,11 +164,58 @@ static void test_dctcp(void)  		return;  	} -	do_test("bpf_dctcp", dctcp_skel->maps.sk_stg_map); +	cb_opts.map_fd = bpf_map__fd(dctcp_skel->maps.sk_stg_map); +	if (!start_test(NULL, &opts, &cli_opts, &lfd, &fd)) +		goto done; + +	err = bpf_map_lookup_elem(cb_opts.map_fd, &fd, &tmp_stg); +	if (!ASSERT_ERR(err, "bpf_map_lookup_elem(sk_stg_map)") || +			!ASSERT_EQ(errno, ENOENT, "bpf_map_lookup_elem(sk_stg_map)")) +		goto done; + +	ASSERT_OK(send_recv_data(lfd, fd, total_bytes), "send_recv_data");  	ASSERT_EQ(dctcp_skel->bss->stg_result, expected_stg, "stg_result"); +done:  	bpf_link__destroy(link);  	bpf_dctcp__destroy(dctcp_skel); +	if (lfd != -1) +		close(lfd); +	if (fd != -1) +		close(fd); +} + +static void test_dctcp_autoattach_map(void) +{ +	struct cb_opts cb_opts = { +		.cc = "bpf_dctcp", +	}; +	struct network_helper_opts opts = { +		.post_socket_cb	= cc_cb, +		.cb_opts	= &cb_opts, +	}; +	struct bpf_dctcp *dctcp_skel; +	struct bpf_link *link; + +	dctcp_skel = bpf_dctcp__open_and_load(); +	if (!ASSERT_OK_PTR(dctcp_skel, "bpf_dctcp__open_and_load")) +		return; + +	bpf_map__set_autoattach(dctcp_skel->maps.dctcp, true); +	bpf_map__set_autoattach(dctcp_skel->maps.dctcp_nouse, false); + +	if (!ASSERT_OK(bpf_dctcp__attach(dctcp_skel), "bpf_dctcp__attach")) +		goto destroy; + +	/* struct_ops is auto-attached  */ +	link = dctcp_skel->links.dctcp; +	if (!ASSERT_OK_PTR(link, "link")) +		goto destroy; + +	do_test(&opts); + +destroy: +	bpf_dctcp__destroy(dctcp_skel);  }  static char *err_str; @@ -171,11 +263,22 @@ static void test_invalid_license(void)  static void test_dctcp_fallback(void)  {  	int err, lfd = -1, cli_fd = -1, srv_fd = -1; -	struct network_helper_opts opts = { -		.cc = "cubic", -	};  	struct bpf_dctcp *dctcp_skel;  	struct bpf_link *link = NULL; +	struct cb_opts dctcp = { +		.cc = "bpf_dctcp", +	}; +	struct network_helper_opts srv_opts = { +		.post_socket_cb = cc_cb, +		.cb_opts = &dctcp, +	}; +	struct cb_opts cubic = { +		.cc = "cubic", +	}; +	struct network_helper_opts cli_opts = { +		.post_socket_cb = cc_cb, +		.cb_opts = &cubic, +	};  	char srv_cc[16];  	socklen_t cc_len = sizeof(srv_cc); @@ -190,13 +293,7 @@ static void test_dctcp_fallback(void)  	if (!ASSERT_OK_PTR(link, "dctcp link"))  		goto done; -	lfd = start_server(AF_INET6, SOCK_STREAM, "::1", 0, 0); -	if (!ASSERT_GE(lfd, 0, "lfd") || -	    !ASSERT_OK(settcpca(lfd, "bpf_dctcp"), "lfd=>bpf_dctcp")) -		goto done; - -	cli_fd = connect_to_fd_opts(lfd, &opts); -	if (!ASSERT_GE(cli_fd, 0, "cli_fd")) +	if (!start_test("::1", &srv_opts, &cli_opts, &lfd, &cli_fd))  		goto done;  	srv_fd = accept(lfd, NULL, 0); @@ -297,6 +394,13 @@ static void test_unsupp_cong_op(void)  static void test_update_ca(void)  { +	struct cb_opts cb_opts = { +		.cc = "tcp_ca_update", +	}; +	struct network_helper_opts opts = { +		.post_socket_cb	= cc_cb, +		.cb_opts	= &cb_opts, +	};  	struct tcp_ca_update *skel;  	struct bpf_link *link;  	int saved_ca1_cnt; @@ -307,25 +411,34 @@ static void test_update_ca(void)  		return;  	link = bpf_map__attach_struct_ops(skel->maps.ca_update_1); -	ASSERT_OK_PTR(link, "attach_struct_ops"); +	if (!ASSERT_OK_PTR(link, "attach_struct_ops")) +		goto out; -	do_test("tcp_ca_update", NULL); +	do_test(&opts);  	saved_ca1_cnt = skel->bss->ca1_cnt;  	ASSERT_GT(saved_ca1_cnt, 0, "ca1_ca1_cnt");  	err = bpf_link__update_map(link, skel->maps.ca_update_2);  	ASSERT_OK(err, "update_map"); -	do_test("tcp_ca_update", NULL); +	do_test(&opts);  	ASSERT_EQ(skel->bss->ca1_cnt, saved_ca1_cnt, "ca2_ca1_cnt");  	ASSERT_GT(skel->bss->ca2_cnt, 0, "ca2_ca2_cnt");  	bpf_link__destroy(link); +out:  	tcp_ca_update__destroy(skel);  }  static void test_update_wrong(void)  { +	struct cb_opts cb_opts = { +		.cc = "tcp_ca_update", +	}; +	struct network_helper_opts opts = { +		.post_socket_cb	= cc_cb, +		.cb_opts	= &cb_opts, +	};  	struct tcp_ca_update *skel;  	struct bpf_link *link;  	int saved_ca1_cnt; @@ -336,24 +449,33 @@ static void test_update_wrong(void)  		return;  	link = bpf_map__attach_struct_ops(skel->maps.ca_update_1); -	ASSERT_OK_PTR(link, "attach_struct_ops"); +	if (!ASSERT_OK_PTR(link, "attach_struct_ops")) +		goto out; -	do_test("tcp_ca_update", NULL); +	do_test(&opts);  	saved_ca1_cnt = skel->bss->ca1_cnt;  	ASSERT_GT(saved_ca1_cnt, 0, "ca1_ca1_cnt");  	err = bpf_link__update_map(link, skel->maps.ca_wrong);  	ASSERT_ERR(err, "update_map"); -	do_test("tcp_ca_update", NULL); +	do_test(&opts);  	ASSERT_GT(skel->bss->ca1_cnt, saved_ca1_cnt, "ca2_ca1_cnt");  	bpf_link__destroy(link); +out:  	tcp_ca_update__destroy(skel);  }  static void test_mixed_links(void)  { +	struct cb_opts cb_opts = { +		.cc = "tcp_ca_update", +	}; +	struct network_helper_opts opts = { +		.post_socket_cb	= cc_cb, +		.cb_opts	= &cb_opts, +	};  	struct tcp_ca_update *skel;  	struct bpf_link *link, *link_nl;  	int err; @@ -363,12 +485,13 @@ static void test_mixed_links(void)  		return;  	link_nl = bpf_map__attach_struct_ops(skel->maps.ca_no_link); -	ASSERT_OK_PTR(link_nl, "attach_struct_ops_nl"); +	if (!ASSERT_OK_PTR(link_nl, "attach_struct_ops_nl")) +		goto out;  	link = bpf_map__attach_struct_ops(skel->maps.ca_update_1);  	ASSERT_OK_PTR(link, "attach_struct_ops"); -	do_test("tcp_ca_update", NULL); +	do_test(&opts);  	ASSERT_GT(skel->bss->ca1_cnt, 0, "ca1_ca1_cnt");  	err = bpf_link__update_map(link, skel->maps.ca_no_link); @@ -376,6 +499,7 @@ static void test_mixed_links(void)  	bpf_link__destroy(link);  	bpf_link__destroy(link_nl); +out:  	tcp_ca_update__destroy(skel);  } @@ -418,7 +542,8 @@ static void test_link_replace(void)  	bpf_link__destroy(link);  	link = bpf_map__attach_struct_ops(skel->maps.ca_update_2); -	ASSERT_OK_PTR(link, "attach_struct_ops_2nd"); +	if (!ASSERT_OK_PTR(link, "attach_struct_ops_2nd")) +		goto out;  	/* BPF_F_REPLACE with a wrong old map Fd. It should fail!  	 * @@ -441,6 +566,7 @@ static void test_link_replace(void)  	bpf_link__destroy(link); +out:  	tcp_ca_update__destroy(skel);  } @@ -455,6 +581,13 @@ static void test_tcp_ca_kfunc(void)  static void test_cc_cubic(void)  { +	struct cb_opts cb_opts = { +		.cc = "bpf_cc_cubic", +	}; +	struct network_helper_opts opts = { +		.post_socket_cb	= cc_cb, +		.cb_opts	= &cb_opts, +	};  	struct bpf_cc_cubic *cc_cubic_skel;  	struct bpf_link *link; @@ -468,7 +601,7 @@ static void test_cc_cubic(void)  		return;  	} -	do_test("bpf_cc_cubic", NULL); +	do_test(&opts);  	bpf_link__destroy(link);  	bpf_cc_cubic__destroy(cc_cubic_skel); @@ -506,4 +639,6 @@ void test_bpf_tcp_ca(void)  		test_tcp_ca_kfunc();  	if (test__start_subtest("cc_cubic"))  		test_cc_cubic(); +	if (test__start_subtest("dctcp_autoattach_map")) +		test_dctcp_autoattach_map();  } diff --git a/tools/testing/selftests/bpf/prog_tests/bpf_verif_scale.c b/tools/testing/selftests/bpf/prog_tests/bpf_verif_scale.c index 4c6ada5b270b..73f669014b69 100644 --- a/tools/testing/selftests/bpf/prog_tests/bpf_verif_scale.c +++ b/tools/testing/selftests/bpf/prog_tests/bpf_verif_scale.c @@ -45,12 +45,6 @@ err_out:  	return err;  } -struct scale_test_def { -	const char *file; -	enum bpf_prog_type attach_type; -	bool fails; -}; -  static void scale_test(const char *file,  		       enum bpf_prog_type attach_type,  		       bool should_fail) diff --git a/tools/testing/selftests/bpf/prog_tests/btf_distill.c b/tools/testing/selftests/bpf/prog_tests/btf_distill.c new file mode 100644 index 000000000000..bfbe795823a2 --- /dev/null +++ b/tools/testing/selftests/bpf/prog_tests/btf_distill.c @@ -0,0 +1,552 @@ +// SPDX-License-Identifier: GPL-2.0 +/* Copyright (c) 2024, Oracle and/or its affiliates. */ + +#include <test_progs.h> +#include <bpf/btf.h> +#include "btf_helpers.h" + +/* Fabricate base, split BTF with references to base types needed; then create + * split BTF with distilled base BTF and ensure expectations are met: + *  - only referenced base types from split BTF are present + *  - struct/union/enum are represented as empty unless anonymous, when they + *    are represented in full in split BTF + */ +static void test_distilled_base(void) +{ +	struct btf *btf1 = NULL, *btf2 = NULL, *btf3 = NULL, *btf4 = NULL; + +	btf1 = btf__new_empty(); +	if (!ASSERT_OK_PTR(btf1, "empty_main_btf")) +		return; + +	btf__add_int(btf1, "int", 4, BTF_INT_SIGNED);	/* [1] int */ +	btf__add_ptr(btf1, 1);				/* [2] ptr to int */ +	btf__add_struct(btf1, "s1", 8);			/* [3] struct s1 { */ +	btf__add_field(btf1, "f1", 2, 0, 0);		/*      int *f1; */ +							/* } */ +	btf__add_struct(btf1, "", 12);			/* [4] struct { */ +	btf__add_field(btf1, "f1", 1, 0, 0);		/*	int f1; */ +	btf__add_field(btf1, "f2", 3, 32, 0);		/*	struct s1 f2; */ +							/* } */ +	btf__add_int(btf1, "unsigned int", 4, 0);	/* [5] unsigned int */ +	btf__add_union(btf1, "u1", 12);			/* [6] union u1 { */ +	btf__add_field(btf1, "f1", 1, 0, 0);		/*	int f1; */ +	btf__add_field(btf1, "f2", 2, 0, 0);		/*	int *f2; */ +							/* } */ +	btf__add_union(btf1, "", 4);			/* [7] union { */ +	btf__add_field(btf1, "f1", 1, 0, 0);		/*	int f1; */ +							/* } */ +	btf__add_enum(btf1, "e1", 4);			/* [8] enum e1 { */ +	btf__add_enum_value(btf1, "v1", 1);		/*	v1 = 1; */ +							/* } */ +	btf__add_enum(btf1, "", 4);			/* [9] enum { */ +	btf__add_enum_value(btf1, "av1", 2);		/*	av1 = 2; */ +							/* } */ +	btf__add_enum64(btf1, "e641", 8, true);		/* [10] enum64 { */ +	btf__add_enum64_value(btf1, "v1", 1024);	/*	v1 = 1024; */ +							/* } */ +	btf__add_enum64(btf1, "", 8, true);		/* [11] enum64 { */ +	btf__add_enum64_value(btf1, "v1", 1025);	/*	v1 = 1025; */ +							/* } */ +	btf__add_struct(btf1, "unneeded", 4);		/* [12] struct unneeded { */ +	btf__add_field(btf1, "f1", 1, 0, 0);		/*	int f1; */ +							/* } */ +	btf__add_struct(btf1, "embedded", 4);		/* [13] struct embedded { */ +	btf__add_field(btf1, "f1", 1, 0, 0);		/*	int f1; */ +							/* } */ +	btf__add_func_proto(btf1, 1);			/* [14] int (*)(int *p1); */ +	btf__add_func_param(btf1, "p1", 1); + +	btf__add_array(btf1, 1, 1, 3);			/* [15] int [3]; */ + +	btf__add_struct(btf1, "from_proto", 4);		/* [16] struct from_proto { */ +	btf__add_field(btf1, "f1", 1, 0, 0);		/*	int f1; */ +							/* } */ +	btf__add_union(btf1, "u1", 4);			/* [17] union u1 { */ +	btf__add_field(btf1, "f1", 1, 0, 0);		/*	 int f1; */ +							/* } */ +	VALIDATE_RAW_BTF( +		btf1, +		"[1] INT 'int' size=4 bits_offset=0 nr_bits=32 encoding=SIGNED", +		"[2] PTR '(anon)' type_id=1", +		"[3] STRUCT 's1' size=8 vlen=1\n" +		"\t'f1' type_id=2 bits_offset=0", +		"[4] STRUCT '(anon)' size=12 vlen=2\n" +		"\t'f1' type_id=1 bits_offset=0\n" +		"\t'f2' type_id=3 bits_offset=32", +		"[5] INT 'unsigned int' size=4 bits_offset=0 nr_bits=32 encoding=(none)", +		"[6] UNION 'u1' size=12 vlen=2\n" +		"\t'f1' type_id=1 bits_offset=0\n" +		"\t'f2' type_id=2 bits_offset=0", +		"[7] UNION '(anon)' size=4 vlen=1\n" +		"\t'f1' type_id=1 bits_offset=0", +		"[8] ENUM 'e1' encoding=UNSIGNED size=4 vlen=1\n" +		"\t'v1' val=1", +		"[9] ENUM '(anon)' encoding=UNSIGNED size=4 vlen=1\n" +		"\t'av1' val=2", +		"[10] ENUM64 'e641' encoding=SIGNED size=8 vlen=1\n" +		"\t'v1' val=1024", +		"[11] ENUM64 '(anon)' encoding=SIGNED size=8 vlen=1\n" +		"\t'v1' val=1025", +		"[12] STRUCT 'unneeded' size=4 vlen=1\n" +		"\t'f1' type_id=1 bits_offset=0", +		"[13] STRUCT 'embedded' size=4 vlen=1\n" +		"\t'f1' type_id=1 bits_offset=0", +		"[14] FUNC_PROTO '(anon)' ret_type_id=1 vlen=1\n" +		"\t'p1' type_id=1", +		"[15] ARRAY '(anon)' type_id=1 index_type_id=1 nr_elems=3", +		"[16] STRUCT 'from_proto' size=4 vlen=1\n" +		"\t'f1' type_id=1 bits_offset=0", +		"[17] UNION 'u1' size=4 vlen=1\n" +		"\t'f1' type_id=1 bits_offset=0"); + +	btf2 = btf__new_empty_split(btf1); +	if (!ASSERT_OK_PTR(btf2, "empty_split_btf")) +		goto cleanup; + +	btf__add_ptr(btf2, 3);				/* [18] ptr to struct s1 */ +	/* add ptr to struct anon */ +	btf__add_ptr(btf2, 4);				/* [19] ptr to struct (anon) */ +	btf__add_const(btf2, 6);			/* [20] const union u1 */ +	btf__add_restrict(btf2, 7);			/* [21] restrict union (anon) */ +	btf__add_volatile(btf2, 8);			/* [22] volatile enum e1 */ +	btf__add_typedef(btf2, "et", 9);		/* [23] typedef enum (anon) */ +	btf__add_const(btf2, 10);			/* [24] const enum64 e641 */ +	btf__add_ptr(btf2, 11);				/* [25] restrict enum64 (anon) */ +	btf__add_struct(btf2, "with_embedded", 4);	/* [26] struct with_embedded { */ +	btf__add_field(btf2, "f1", 13, 0, 0);		/*	struct embedded f1; */ +							/* } */ +	btf__add_func(btf2, "fn", BTF_FUNC_STATIC, 14);	/* [27] int fn(int p1); */ +	btf__add_typedef(btf2, "arraytype", 15);	/* [28] typedef int[3] foo; */ +	btf__add_func_proto(btf2, 1);			/* [29] int (*)(struct from proto p1); */ +	btf__add_func_param(btf2, "p1", 16); + +	VALIDATE_RAW_BTF( +		btf2, +		"[1] INT 'int' size=4 bits_offset=0 nr_bits=32 encoding=SIGNED", +		"[2] PTR '(anon)' type_id=1", +		"[3] STRUCT 's1' size=8 vlen=1\n" +		"\t'f1' type_id=2 bits_offset=0", +		"[4] STRUCT '(anon)' size=12 vlen=2\n" +		"\t'f1' type_id=1 bits_offset=0\n" +		"\t'f2' type_id=3 bits_offset=32", +		"[5] INT 'unsigned int' size=4 bits_offset=0 nr_bits=32 encoding=(none)", +		"[6] UNION 'u1' size=12 vlen=2\n" +		"\t'f1' type_id=1 bits_offset=0\n" +		"\t'f2' type_id=2 bits_offset=0", +		"[7] UNION '(anon)' size=4 vlen=1\n" +		"\t'f1' type_id=1 bits_offset=0", +		"[8] ENUM 'e1' encoding=UNSIGNED size=4 vlen=1\n" +		"\t'v1' val=1", +		"[9] ENUM '(anon)' encoding=UNSIGNED size=4 vlen=1\n" +		"\t'av1' val=2", +		"[10] ENUM64 'e641' encoding=SIGNED size=8 vlen=1\n" +		"\t'v1' val=1024", +		"[11] ENUM64 '(anon)' encoding=SIGNED size=8 vlen=1\n" +		"\t'v1' val=1025", +		"[12] STRUCT 'unneeded' size=4 vlen=1\n" +		"\t'f1' type_id=1 bits_offset=0", +		"[13] STRUCT 'embedded' size=4 vlen=1\n" +		"\t'f1' type_id=1 bits_offset=0", +		"[14] FUNC_PROTO '(anon)' ret_type_id=1 vlen=1\n" +		"\t'p1' type_id=1", +		"[15] ARRAY '(anon)' type_id=1 index_type_id=1 nr_elems=3", +		"[16] STRUCT 'from_proto' size=4 vlen=1\n" +		"\t'f1' type_id=1 bits_offset=0", +		"[17] UNION 'u1' size=4 vlen=1\n" +		"\t'f1' type_id=1 bits_offset=0", +		"[18] PTR '(anon)' type_id=3", +		"[19] PTR '(anon)' type_id=4", +		"[20] CONST '(anon)' type_id=6", +		"[21] RESTRICT '(anon)' type_id=7", +		"[22] VOLATILE '(anon)' type_id=8", +		"[23] TYPEDEF 'et' type_id=9", +		"[24] CONST '(anon)' type_id=10", +		"[25] PTR '(anon)' type_id=11", +		"[26] STRUCT 'with_embedded' size=4 vlen=1\n" +		"\t'f1' type_id=13 bits_offset=0", +		"[27] FUNC 'fn' type_id=14 linkage=static", +		"[28] TYPEDEF 'arraytype' type_id=15", +		"[29] FUNC_PROTO '(anon)' ret_type_id=1 vlen=1\n" +		"\t'p1' type_id=16"); + +	if (!ASSERT_EQ(0, btf__distill_base(btf2, &btf3, &btf4), +		       "distilled_base") || +	    !ASSERT_OK_PTR(btf3, "distilled_base") || +	    !ASSERT_OK_PTR(btf4, "distilled_split") || +	    !ASSERT_EQ(8, btf__type_cnt(btf3), "distilled_base_type_cnt")) +		goto cleanup; + +	VALIDATE_RAW_BTF( +		btf4, +		"[1] INT 'int' size=4 bits_offset=0 nr_bits=32 encoding=SIGNED", +		"[2] STRUCT 's1' size=8 vlen=0", +		"[3] UNION 'u1' size=12 vlen=0", +		"[4] ENUM 'e1' encoding=UNSIGNED size=4 vlen=0", +		"[5] ENUM 'e641' encoding=UNSIGNED size=8 vlen=0", +		"[6] STRUCT 'embedded' size=4 vlen=0", +		"[7] STRUCT 'from_proto' size=4 vlen=0", +		/* split BTF; these types should match split BTF above from 17-28, with +		 * updated type id references +		 */ +		"[8] PTR '(anon)' type_id=2", +		"[9] PTR '(anon)' type_id=20", +		"[10] CONST '(anon)' type_id=3", +		"[11] RESTRICT '(anon)' type_id=21", +		"[12] VOLATILE '(anon)' type_id=4", +		"[13] TYPEDEF 'et' type_id=22", +		"[14] CONST '(anon)' type_id=5", +		"[15] PTR '(anon)' type_id=23", +		"[16] STRUCT 'with_embedded' size=4 vlen=1\n" +		"\t'f1' type_id=6 bits_offset=0", +		"[17] FUNC 'fn' type_id=24 linkage=static", +		"[18] TYPEDEF 'arraytype' type_id=25", +		"[19] FUNC_PROTO '(anon)' ret_type_id=1 vlen=1\n" +		"\t'p1' type_id=7", +		/* split BTF types added from original base BTF below */ +		"[20] STRUCT '(anon)' size=12 vlen=2\n" +		"\t'f1' type_id=1 bits_offset=0\n" +		"\t'f2' type_id=2 bits_offset=32", +		"[21] UNION '(anon)' size=4 vlen=1\n" +		"\t'f1' type_id=1 bits_offset=0", +		"[22] ENUM '(anon)' encoding=UNSIGNED size=4 vlen=1\n" +		"\t'av1' val=2", +		"[23] ENUM64 '(anon)' encoding=SIGNED size=8 vlen=1\n" +		"\t'v1' val=1025", +		"[24] FUNC_PROTO '(anon)' ret_type_id=1 vlen=1\n" +		"\t'p1' type_id=1", +		"[25] ARRAY '(anon)' type_id=1 index_type_id=1 nr_elems=3"); + +	if (!ASSERT_EQ(btf__relocate(btf4, btf1), 0, "relocate_split")) +		goto cleanup; + +	VALIDATE_RAW_BTF( +		btf4, +		"[1] INT 'int' size=4 bits_offset=0 nr_bits=32 encoding=SIGNED", +		"[2] PTR '(anon)' type_id=1", +		"[3] STRUCT 's1' size=8 vlen=1\n" +		"\t'f1' type_id=2 bits_offset=0", +		"[4] STRUCT '(anon)' size=12 vlen=2\n" +		"\t'f1' type_id=1 bits_offset=0\n" +		"\t'f2' type_id=3 bits_offset=32", +		"[5] INT 'unsigned int' size=4 bits_offset=0 nr_bits=32 encoding=(none)", +		"[6] UNION 'u1' size=12 vlen=2\n" +		"\t'f1' type_id=1 bits_offset=0\n" +		"\t'f2' type_id=2 bits_offset=0", +		"[7] UNION '(anon)' size=4 vlen=1\n" +		"\t'f1' type_id=1 bits_offset=0", +		"[8] ENUM 'e1' encoding=UNSIGNED size=4 vlen=1\n" +		"\t'v1' val=1", +		"[9] ENUM '(anon)' encoding=UNSIGNED size=4 vlen=1\n" +		"\t'av1' val=2", +		"[10] ENUM64 'e641' encoding=SIGNED size=8 vlen=1\n" +		"\t'v1' val=1024", +		"[11] ENUM64 '(anon)' encoding=SIGNED size=8 vlen=1\n" +		"\t'v1' val=1025", +		"[12] STRUCT 'unneeded' size=4 vlen=1\n" +		"\t'f1' type_id=1 bits_offset=0", +		"[13] STRUCT 'embedded' size=4 vlen=1\n" +		"\t'f1' type_id=1 bits_offset=0", +		"[14] FUNC_PROTO '(anon)' ret_type_id=1 vlen=1\n" +		"\t'p1' type_id=1", +		"[15] ARRAY '(anon)' type_id=1 index_type_id=1 nr_elems=3", +		"[16] STRUCT 'from_proto' size=4 vlen=1\n" +		"\t'f1' type_id=1 bits_offset=0", +		"[17] UNION 'u1' size=4 vlen=1\n" +		"\t'f1' type_id=1 bits_offset=0", +		"[18] PTR '(anon)' type_id=3", +		"[19] PTR '(anon)' type_id=30", +		"[20] CONST '(anon)' type_id=6", +		"[21] RESTRICT '(anon)' type_id=31", +		"[22] VOLATILE '(anon)' type_id=8", +		"[23] TYPEDEF 'et' type_id=32", +		"[24] CONST '(anon)' type_id=10", +		"[25] PTR '(anon)' type_id=33", +		"[26] STRUCT 'with_embedded' size=4 vlen=1\n" +		"\t'f1' type_id=13 bits_offset=0", +		"[27] FUNC 'fn' type_id=34 linkage=static", +		"[28] TYPEDEF 'arraytype' type_id=35", +		"[29] FUNC_PROTO '(anon)' ret_type_id=1 vlen=1\n" +		"\t'p1' type_id=16", +		/* below here are (duplicate) anon base types added by distill +		 * process to split BTF. +		 */ +		"[30] STRUCT '(anon)' size=12 vlen=2\n" +		"\t'f1' type_id=1 bits_offset=0\n" +		"\t'f2' type_id=3 bits_offset=32", +		"[31] UNION '(anon)' size=4 vlen=1\n" +		"\t'f1' type_id=1 bits_offset=0", +		"[32] ENUM '(anon)' encoding=UNSIGNED size=4 vlen=1\n" +		"\t'av1' val=2", +		"[33] ENUM64 '(anon)' encoding=SIGNED size=8 vlen=1\n" +		"\t'v1' val=1025", +		"[34] FUNC_PROTO '(anon)' ret_type_id=1 vlen=1\n" +		"\t'p1' type_id=1", +		"[35] ARRAY '(anon)' type_id=1 index_type_id=1 nr_elems=3"); + +cleanup: +	btf__free(btf4); +	btf__free(btf3); +	btf__free(btf2); +	btf__free(btf1); +} + +/* ensure we can cope with multiple types with the same name in + * distilled base BTF.  In this case because sizes are different, + * we can still disambiguate them. + */ +static void test_distilled_base_multi(void) +{ +	struct btf *btf1 = NULL, *btf2 = NULL, *btf3 = NULL, *btf4 = NULL; + +	btf1 = btf__new_empty(); +	if (!ASSERT_OK_PTR(btf1, "empty_main_btf")) +		return; +	btf__add_int(btf1, "int", 4, BTF_INT_SIGNED);   /* [1] int */ +	btf__add_int(btf1, "int", 8, BTF_INT_SIGNED);	/* [2] int */ +	VALIDATE_RAW_BTF( +		btf1, +		"[1] INT 'int' size=4 bits_offset=0 nr_bits=32 encoding=SIGNED", +		"[2] INT 'int' size=8 bits_offset=0 nr_bits=64 encoding=SIGNED"); +	btf2 = btf__new_empty_split(btf1); +	if (!ASSERT_OK_PTR(btf2, "empty_split_btf")) +		goto cleanup; +	btf__add_ptr(btf2, 1); +	btf__add_const(btf2, 2); +	VALIDATE_RAW_BTF( +		btf2, +		"[1] INT 'int' size=4 bits_offset=0 nr_bits=32 encoding=SIGNED", +		"[2] INT 'int' size=8 bits_offset=0 nr_bits=64 encoding=SIGNED", +		"[3] PTR '(anon)' type_id=1", +		"[4] CONST '(anon)' type_id=2"); +	if (!ASSERT_EQ(0, btf__distill_base(btf2, &btf3, &btf4), +		       "distilled_base") || +	    !ASSERT_OK_PTR(btf3, "distilled_base") || +	    !ASSERT_OK_PTR(btf4, "distilled_split") || +	    !ASSERT_EQ(3, btf__type_cnt(btf3), "distilled_base_type_cnt")) +		goto cleanup; +	VALIDATE_RAW_BTF( +		btf3, +		"[1] INT 'int' size=4 bits_offset=0 nr_bits=32 encoding=SIGNED", +		"[2] INT 'int' size=8 bits_offset=0 nr_bits=64 encoding=SIGNED"); +	if (!ASSERT_EQ(btf__relocate(btf4, btf1), 0, "relocate_split")) +		goto cleanup; + +	VALIDATE_RAW_BTF( +		btf4, +		"[1] INT 'int' size=4 bits_offset=0 nr_bits=32 encoding=SIGNED", +		"[2] INT 'int' size=8 bits_offset=0 nr_bits=64 encoding=SIGNED", +		"[3] PTR '(anon)' type_id=1", +		"[4] CONST '(anon)' type_id=2"); + +cleanup: +	btf__free(btf4); +	btf__free(btf3); +	btf__free(btf2); +	btf__free(btf1); +} + +/* If a needed type is not present in the base BTF we wish to relocate + * with, btf__relocate() should error our. + */ +static void test_distilled_base_missing_err(void) +{ +	struct btf *btf1 = NULL, *btf2 = NULL, *btf3 = NULL, *btf4 = NULL, *btf5 = NULL; + +	btf1 = btf__new_empty(); +	if (!ASSERT_OK_PTR(btf1, "empty_main_btf")) +		return; +	btf__add_int(btf1, "int", 4, BTF_INT_SIGNED);   /* [1] int */ +	btf__add_int(btf1, "int", 8, BTF_INT_SIGNED);   /* [2] int */ +	VALIDATE_RAW_BTF( +		btf1, +		"[1] INT 'int' size=4 bits_offset=0 nr_bits=32 encoding=SIGNED", +		"[2] INT 'int' size=8 bits_offset=0 nr_bits=64 encoding=SIGNED"); +	btf2 = btf__new_empty_split(btf1); +	if (!ASSERT_OK_PTR(btf2, "empty_split_btf")) +		goto cleanup; +	btf__add_ptr(btf2, 1); +	btf__add_const(btf2, 2); +	VALIDATE_RAW_BTF( +		btf2, +		"[1] INT 'int' size=4 bits_offset=0 nr_bits=32 encoding=SIGNED", +		"[2] INT 'int' size=8 bits_offset=0 nr_bits=64 encoding=SIGNED", +		"[3] PTR '(anon)' type_id=1", +		"[4] CONST '(anon)' type_id=2"); +	if (!ASSERT_EQ(0, btf__distill_base(btf2, &btf3, &btf4), +		       "distilled_base") || +	    !ASSERT_OK_PTR(btf3, "distilled_base") || +	    !ASSERT_OK_PTR(btf4, "distilled_split") || +	    !ASSERT_EQ(3, btf__type_cnt(btf3), "distilled_base_type_cnt")) +		goto cleanup; +	VALIDATE_RAW_BTF( +		btf3, +		"[1] INT 'int' size=4 bits_offset=0 nr_bits=32 encoding=SIGNED", +		"[2] INT 'int' size=8 bits_offset=0 nr_bits=64 encoding=SIGNED"); +	btf5 = btf__new_empty(); +	if (!ASSERT_OK_PTR(btf5, "empty_reloc_btf")) +		return; +	btf__add_int(btf5, "int", 4, BTF_INT_SIGNED);   /* [1] int */ +	VALIDATE_RAW_BTF( +		btf5, +		"[1] INT 'int' size=4 bits_offset=0 nr_bits=32 encoding=SIGNED"); +	ASSERT_EQ(btf__relocate(btf4, btf5), -EINVAL, "relocate_split"); + +cleanup: +	btf__free(btf5); +	btf__free(btf4); +	btf__free(btf3); +	btf__free(btf2); +	btf__free(btf1); +} + +/* With 2 types of same size in distilled base BTF, relocation should + * fail as we have no means to choose between them. + */ +static void test_distilled_base_multi_err(void) +{ +	struct btf *btf1 = NULL, *btf2 = NULL, *btf3 = NULL, *btf4 = NULL; + +	btf1 = btf__new_empty(); +	if (!ASSERT_OK_PTR(btf1, "empty_main_btf")) +		return; +	btf__add_int(btf1, "int", 4, BTF_INT_SIGNED);   /* [1] int */ +	btf__add_int(btf1, "int", 4, BTF_INT_SIGNED);   /* [2] int */ +	VALIDATE_RAW_BTF( +		btf1, +		"[1] INT 'int' size=4 bits_offset=0 nr_bits=32 encoding=SIGNED", +		"[2] INT 'int' size=4 bits_offset=0 nr_bits=32 encoding=SIGNED"); +	btf2 = btf__new_empty_split(btf1); +	if (!ASSERT_OK_PTR(btf2, "empty_split_btf")) +		goto cleanup; +	btf__add_ptr(btf2, 1); +	btf__add_const(btf2, 2); +	VALIDATE_RAW_BTF( +		btf2, +		"[1] INT 'int' size=4 bits_offset=0 nr_bits=32 encoding=SIGNED", +		"[2] INT 'int' size=4 bits_offset=0 nr_bits=32 encoding=SIGNED", +		"[3] PTR '(anon)' type_id=1", +		"[4] CONST '(anon)' type_id=2"); +	if (!ASSERT_EQ(0, btf__distill_base(btf2, &btf3, &btf4), +		       "distilled_base") || +	    !ASSERT_OK_PTR(btf3, "distilled_base") || +	    !ASSERT_OK_PTR(btf4, "distilled_split") || +	    !ASSERT_EQ(3, btf__type_cnt(btf3), "distilled_base_type_cnt")) +		goto cleanup; +	VALIDATE_RAW_BTF( +		btf3, +		"[1] INT 'int' size=4 bits_offset=0 nr_bits=32 encoding=SIGNED", +		"[2] INT 'int' size=4 bits_offset=0 nr_bits=32 encoding=SIGNED"); +	ASSERT_EQ(btf__relocate(btf4, btf1), -EINVAL, "relocate_split"); +cleanup: +	btf__free(btf4); +	btf__free(btf3); +	btf__free(btf2); +	btf__free(btf1); +} + +/* With 2 types of same size in base BTF, relocation should + * fail as we have no means to choose between them. + */ +static void test_distilled_base_multi_err2(void) +{ +	struct btf *btf1 = NULL, *btf2 = NULL, *btf3 = NULL, *btf4 = NULL, *btf5 = NULL; + +	btf1 = btf__new_empty(); +	if (!ASSERT_OK_PTR(btf1, "empty_main_btf")) +		return; +	btf__add_int(btf1, "int", 4, BTF_INT_SIGNED);   /* [1] int */ +	VALIDATE_RAW_BTF( +		btf1, +		"[1] INT 'int' size=4 bits_offset=0 nr_bits=32 encoding=SIGNED"); +	btf2 = btf__new_empty_split(btf1); +	if (!ASSERT_OK_PTR(btf2, "empty_split_btf")) +		goto cleanup; +	btf__add_ptr(btf2, 1); +	VALIDATE_RAW_BTF( +		btf2, +		"[1] INT 'int' size=4 bits_offset=0 nr_bits=32 encoding=SIGNED", +		"[2] PTR '(anon)' type_id=1"); +	if (!ASSERT_EQ(0, btf__distill_base(btf2, &btf3, &btf4), +		       "distilled_base") || +	    !ASSERT_OK_PTR(btf3, "distilled_base") || +	    !ASSERT_OK_PTR(btf4, "distilled_split") || +	    !ASSERT_EQ(2, btf__type_cnt(btf3), "distilled_base_type_cnt")) +		goto cleanup; +	VALIDATE_RAW_BTF( +		btf3, +		"[1] INT 'int' size=4 bits_offset=0 nr_bits=32 encoding=SIGNED"); +	btf5 = btf__new_empty(); +	if (!ASSERT_OK_PTR(btf5, "empty_reloc_btf")) +		return; +	btf__add_int(btf5, "int", 4, BTF_INT_SIGNED);   /* [1] int */ +	btf__add_int(btf5, "int", 4, BTF_INT_SIGNED);   /* [2] int */ +	VALIDATE_RAW_BTF( +		btf5, +		"[1] INT 'int' size=4 bits_offset=0 nr_bits=32 encoding=SIGNED", +		"[2] INT 'int' size=4 bits_offset=0 nr_bits=32 encoding=SIGNED"); +	ASSERT_EQ(btf__relocate(btf4, btf5), -EINVAL, "relocate_split"); +cleanup: +	btf__free(btf5); +	btf__free(btf4); +	btf__free(btf3); +	btf__free(btf2); +	btf__free(btf1); +} + +/* create split reference BTF from vmlinux + split BTF with a few type references; + * ensure the resultant split reference BTF is as expected, containing only types + * needed to disambiguate references from split BTF. + */ +static void test_distilled_base_vmlinux(void) +{ +	struct btf *split_btf = NULL, *vmlinux_btf = btf__load_vmlinux_btf(); +	struct btf *split_dist = NULL, *base_dist = NULL; +	__s32 int_id, myint_id; + +	if (!ASSERT_OK_PTR(vmlinux_btf, "load_vmlinux")) +		return; +	int_id = btf__find_by_name_kind(vmlinux_btf, "int", BTF_KIND_INT); +	if (!ASSERT_GT(int_id, 0, "find_int")) +		goto cleanup; +	split_btf = btf__new_empty_split(vmlinux_btf); +	if (!ASSERT_OK_PTR(split_btf, "new_split")) +		goto cleanup; +	myint_id = btf__add_typedef(split_btf, "myint", int_id); +	btf__add_ptr(split_btf, myint_id); + +	if (!ASSERT_EQ(btf__distill_base(split_btf, &base_dist, &split_dist), 0, +		       "distill_vmlinux_base")) +		goto cleanup; + +	if (!ASSERT_OK_PTR(split_dist, "split_distilled") || +	    !ASSERT_OK_PTR(base_dist, "base_dist")) +		goto cleanup; +	VALIDATE_RAW_BTF( +		split_dist, +		"[1] INT 'int' size=4 bits_offset=0 nr_bits=32 encoding=SIGNED", +		"[2] TYPEDEF 'myint' type_id=1", +		"[3] PTR '(anon)' type_id=2"); + +cleanup: +	btf__free(split_dist); +	btf__free(base_dist); +	btf__free(split_btf); +	btf__free(vmlinux_btf); +} + +void test_btf_distill(void) +{ +	if (test__start_subtest("distilled_base")) +		test_distilled_base(); +	if (test__start_subtest("distilled_base_multi")) +		test_distilled_base_multi(); +	if (test__start_subtest("distilled_base_missing_err")) +		test_distilled_base_missing_err(); +	if (test__start_subtest("distilled_base_multi_err")) +		test_distilled_base_multi_err(); +	if (test__start_subtest("distilled_base_multi_err2")) +		test_distilled_base_multi_err2(); +	if (test__start_subtest("distilled_base_vmlinux")) +		test_distilled_base_vmlinux(); +} diff --git a/tools/testing/selftests/bpf/prog_tests/btf_field_iter.c b/tools/testing/selftests/bpf/prog_tests/btf_field_iter.c new file mode 100644 index 000000000000..32159d3eb281 --- /dev/null +++ b/tools/testing/selftests/bpf/prog_tests/btf_field_iter.c @@ -0,0 +1,161 @@ +// SPDX-License-Identifier: GPL-2.0 +/* Copyright (c) 2024, Oracle and/or its affiliates. */ + +#include <test_progs.h> +#include <bpf/btf.h> +#include "btf_helpers.h" +#include "bpf/libbpf_internal.h" + +struct field_data { +	__u32 ids[5]; +	const char *strs[5]; +} fields[] = { +	{ .ids = {},		.strs = {} }, +	{ .ids = {},		.strs = { "int" } }, +	{ .ids = {},		.strs = { "int64" } }, +	{ .ids = { 1 },		.strs = { "" } }, +	{ .ids = { 2, 1 },	.strs = { "" } }, +	{ .ids = { 3, 1 },	.strs = { "s1", "f1", "f2" } }, +	{ .ids = { 1, 5 },	.strs = { "u1", "f1", "f2" } }, +	{ .ids = {},		.strs = { "e1", "v1", "v2" } }, +	{ .ids = {},		.strs = { "fw1" } }, +	{ .ids = { 1 },		.strs = { "t" } }, +	{ .ids = { 2 },		.strs = { "" } }, +	{ .ids = { 1 },		.strs = { "" } }, +	{ .ids = { 3 },		.strs = { "" } }, +	{ .ids = { 1, 1, 3 },	.strs = { "", "p1", "p2" } }, +	{ .ids = { 13 },	.strs = { "func" } }, +	{ .ids = { 1 },		.strs = { "var1" } }, +	{ .ids = { 3 },		.strs = { "var2" } }, +	{ .ids = {},		.strs = { "float" } }, +	{ .ids = { 11 },	.strs = { "decltag" } }, +	{ .ids = { 6 },		.strs = { "typetag" } }, +	{ .ids = {},		.strs = { "e64", "eval1", "eval2", "eval3" } }, +	{ .ids = { 15, 16 },	.strs = { "datasec1" } } + +}; + +/* Fabricate BTF with various types and check BTF field iteration finds types, + * strings expected. + */ +void test_btf_field_iter(void) +{ +	struct btf *btf = NULL; +	int id; + +	btf = btf__new_empty(); +	if (!ASSERT_OK_PTR(btf, "empty_btf")) +		return; + +	btf__add_int(btf, "int", 4, BTF_INT_SIGNED);	/* [1] int */ +	btf__add_int(btf, "int64", 8, BTF_INT_SIGNED);	/* [2] int64 */ +	btf__add_ptr(btf, 1);				/* [3] int * */ +	btf__add_array(btf, 1, 2, 3);			/* [4] int64[3] */ +	btf__add_struct(btf, "s1", 12);			/* [5] struct s1 { */ +	btf__add_field(btf, "f1", 3, 0, 0);		/*      int *f1; */ +	btf__add_field(btf, "f2", 1, 0, 0);		/*	int f2; */ +							/* } */ +	btf__add_union(btf, "u1", 12);			/* [6] union u1 { */ +	btf__add_field(btf, "f1", 1, 0, 0);		/*	int f1; */ +	btf__add_field(btf, "f2", 5, 0, 0);		/*	struct s1 f2; */ +							/* } */ +	btf__add_enum(btf, "e1", 4);			/* [7] enum e1 { */ +	btf__add_enum_value(btf, "v1", 1);		/*	v1 = 1; */ +	btf__add_enum_value(btf, "v2", 2);		/*	v2 = 2; */ +							/* } */ + +	btf__add_fwd(btf, "fw1", BTF_FWD_STRUCT);	/* [8] struct fw1; */ +	btf__add_typedef(btf, "t", 1);			/* [9] typedef int t; */ +	btf__add_volatile(btf, 2);			/* [10] volatile int64; */ +	btf__add_const(btf, 1);				/* [11] const int; */ +	btf__add_restrict(btf, 3);			/* [12] restrict int *; */ +	btf__add_func_proto(btf, 1);			/* [13] int (*)(int p1, int *p2); */ +	btf__add_func_param(btf, "p1", 1); +	btf__add_func_param(btf, "p2", 3); + +	btf__add_func(btf, "func", BTF_FUNC_GLOBAL, 13);/* [14] int func(int p1, int *p2); */ +	btf__add_var(btf, "var1", BTF_VAR_STATIC, 1);	/* [15] static int var1; */ +	btf__add_var(btf, "var2", BTF_VAR_STATIC, 3);	/* [16] static int *var2; */ +	btf__add_float(btf, "float", 4);		/* [17] float; */ +	btf__add_decl_tag(btf, "decltag", 11, -1);	/* [18] decltag const int; */ +	btf__add_type_tag(btf, "typetag", 6);		/* [19] typetag union u1; */ +	btf__add_enum64(btf, "e64", 8, true);		/* [20] enum { */ +	btf__add_enum64_value(btf, "eval1", 1000);	/*	 eval1 = 1000, */ +	btf__add_enum64_value(btf, "eval2", 2000);	/*	 eval2 = 2000, */ +	btf__add_enum64_value(btf, "eval3", 3000);	/*	 eval3 = 3000 */ +							/* } */ +	btf__add_datasec(btf, "datasec1", 12);		/* [21] datasec datasec1 */ +	btf__add_datasec_var_info(btf, 15, 0, 4); +	btf__add_datasec_var_info(btf, 16, 4, 8); + +	VALIDATE_RAW_BTF( +		btf, +		"[1] INT 'int' size=4 bits_offset=0 nr_bits=32 encoding=SIGNED", +		"[2] INT 'int64' size=8 bits_offset=0 nr_bits=64 encoding=SIGNED", +		"[3] PTR '(anon)' type_id=1", +		"[4] ARRAY '(anon)' type_id=2 index_type_id=1 nr_elems=3", +		"[5] STRUCT 's1' size=12 vlen=2\n" +		"\t'f1' type_id=3 bits_offset=0\n" +		"\t'f2' type_id=1 bits_offset=0", +		"[6] UNION 'u1' size=12 vlen=2\n" +		"\t'f1' type_id=1 bits_offset=0\n" +		"\t'f2' type_id=5 bits_offset=0", +		"[7] ENUM 'e1' encoding=UNSIGNED size=4 vlen=2\n" +		"\t'v1' val=1\n" +		"\t'v2' val=2", +		"[8] FWD 'fw1' fwd_kind=struct", +		"[9] TYPEDEF 't' type_id=1", +		"[10] VOLATILE '(anon)' type_id=2", +		"[11] CONST '(anon)' type_id=1", +		"[12] RESTRICT '(anon)' type_id=3", +		"[13] FUNC_PROTO '(anon)' ret_type_id=1 vlen=2\n" +		"\t'p1' type_id=1\n" +		"\t'p2' type_id=3", +		"[14] FUNC 'func' type_id=13 linkage=global", +		"[15] VAR 'var1' type_id=1, linkage=static", +		"[16] VAR 'var2' type_id=3, linkage=static", +		"[17] FLOAT 'float' size=4", +		"[18] DECL_TAG 'decltag' type_id=11 component_idx=-1", +		"[19] TYPE_TAG 'typetag' type_id=6", +		"[20] ENUM64 'e64' encoding=SIGNED size=8 vlen=3\n" +		"\t'eval1' val=1000\n" +		"\t'eval2' val=2000\n" +		"\t'eval3' val=3000", +		"[21] DATASEC 'datasec1' size=12 vlen=2\n" +		"\ttype_id=15 offset=0 size=4\n" +		"\ttype_id=16 offset=4 size=8"); + +	for (id = 1; id < btf__type_cnt(btf); id++) { +		struct btf_type *t = btf_type_by_id(btf, id); +		struct btf_field_iter it_strs, it_ids; +		int str_idx = 0, id_idx = 0; +		__u32 *next_str, *next_id; + +		if (!ASSERT_OK_PTR(t, "btf_type_by_id")) +			break; +		if (!ASSERT_OK(btf_field_iter_init(&it_strs, t, BTF_FIELD_ITER_STRS), +			       "iter_init_strs")) +			break; +		if (!ASSERT_OK(btf_field_iter_init(&it_ids, t, BTF_FIELD_ITER_IDS), +			       "iter_init_ids")) +			break; +		while ((next_str = btf_field_iter_next(&it_strs))) { +			const char *str = btf__str_by_offset(btf, *next_str); + +			if (!ASSERT_OK(strcmp(fields[id].strs[str_idx], str), "field_str_match")) +				break; +			str_idx++; +		} +		/* ensure no more strings are expected */ +		ASSERT_EQ(fields[id].strs[str_idx], NULL, "field_str_cnt"); + +		while ((next_id = btf_field_iter_next(&it_ids))) { +			if (!ASSERT_EQ(*next_id, fields[id].ids[id_idx], "field_id_match")) +				break; +			id_idx++; +		} +		/* ensure no more ids are expected */ +		ASSERT_EQ(fields[id].ids[id_idx], 0, "field_id_cnt"); +	} +	btf__free(btf); +} diff --git a/tools/testing/selftests/bpf/prog_tests/cgroup_v1v2.c b/tools/testing/selftests/bpf/prog_tests/cgroup_v1v2.c index addf720428f7..9709c8db7275 100644 --- a/tools/testing/selftests/bpf/prog_tests/cgroup_v1v2.c +++ b/tools/testing/selftests/bpf/prog_tests/cgroup_v1v2.c @@ -32,7 +32,7 @@ static int run_test(int cgroup_fd, int server_fd, bool classid)  		goto out;  	} -	fd = connect_to_fd_opts(server_fd, &opts); +	fd = connect_to_fd_opts(server_fd, SOCK_STREAM, &opts);  	if (fd < 0)  		err = -1;  	else @@ -52,7 +52,7 @@ void test_cgroup_v1v2(void)  	server_fd = start_server(AF_INET, SOCK_STREAM, NULL, port, 0);  	if (!ASSERT_GE(server_fd, 0, "server_fd"))  		return; -	client_fd = connect_to_fd_opts(server_fd, &opts); +	client_fd = connect_to_fd_opts(server_fd, SOCK_STREAM, &opts);  	if (!ASSERT_GE(client_fd, 0, "client_fd")) {  		close(server_fd);  		return; diff --git a/tools/testing/selftests/bpf/prog_tests/cpumask.c b/tools/testing/selftests/bpf/prog_tests/cpumask.c index ecf89df78109..2570bd4b0cb2 100644 --- a/tools/testing/selftests/bpf/prog_tests/cpumask.c +++ b/tools/testing/selftests/bpf/prog_tests/cpumask.c @@ -18,6 +18,11 @@ static const char * const cpumask_success_testcases[] = {  	"test_insert_leave",  	"test_insert_remove_release",  	"test_global_mask_rcu", +	"test_global_mask_array_one_rcu", +	"test_global_mask_array_rcu", +	"test_global_mask_array_l2_rcu", +	"test_global_mask_nested_rcu", +	"test_global_mask_nested_deep_rcu",  	"test_cpumask_weight",  }; diff --git a/tools/testing/selftests/bpf/prog_tests/ctx_rewrite.c b/tools/testing/selftests/bpf/prog_tests/ctx_rewrite.c index 3b7c57fe55a5..08b6391f2f56 100644 --- a/tools/testing/selftests/bpf/prog_tests/ctx_rewrite.c +++ b/tools/testing/selftests/bpf/prog_tests/ctx_rewrite.c @@ -69,15 +69,17 @@ static struct test_case test_cases[] = {  	{  		N(SCHED_CLS, struct __sk_buff, tstamp),  		.read  = "r11 = *(u8 *)($ctx + sk_buff::__mono_tc_offset);" -			 "w11 &= 3;" -			 "if w11 != 0x3 goto pc+2;" +			 "if w11 & 0x4 goto pc+1;" +			 "goto pc+4;" +			 "if w11 & 0x3 goto pc+1;" +			 "goto pc+2;"  			 "$dst = 0;"  			 "goto pc+1;"  			 "$dst = *(u64 *)($ctx + sk_buff::tstamp);",  		.write = "r11 = *(u8 *)($ctx + sk_buff::__mono_tc_offset);" -			 "if w11 & 0x2 goto pc+1;" +			 "if w11 & 0x4 goto pc+1;"  			 "goto pc+2;" -			 "w11 &= -2;" +			 "w11 &= -4;"  			 "*(u8 *)($ctx + sk_buff::__mono_tc_offset) = r11;"  			 "*(u64 *)($ctx + sk_buff::tstamp) = $src;",  	}, diff --git a/tools/testing/selftests/bpf/prog_tests/fexit_sleep.c b/tools/testing/selftests/bpf/prog_tests/fexit_sleep.c index f949647dbbc2..552a0875ca6d 100644 --- a/tools/testing/selftests/bpf/prog_tests/fexit_sleep.c +++ b/tools/testing/selftests/bpf/prog_tests/fexit_sleep.c @@ -21,13 +21,13 @@ static int do_sleep(void *skel)  }  #define STACK_SIZE (1024 * 1024) -static char child_stack[STACK_SIZE];  void test_fexit_sleep(void)  {  	struct fexit_sleep_lskel *fexit_skel = NULL;  	int wstatus, duration = 0;  	pid_t cpid; +	char *child_stack = NULL;  	int err, fexit_cnt;  	fexit_skel = fexit_sleep_lskel__open_and_load(); @@ -38,6 +38,11 @@ void test_fexit_sleep(void)  	if (CHECK(err, "fexit_attach", "fexit attach failed: %d\n", err))  		goto cleanup; +	child_stack = mmap(NULL, STACK_SIZE, PROT_READ | PROT_WRITE, MAP_PRIVATE | +			   MAP_ANONYMOUS | MAP_STACK, -1, 0); +	if (!ASSERT_NEQ(child_stack, MAP_FAILED, "mmap")) +		goto cleanup; +  	cpid = clone(do_sleep, child_stack + STACK_SIZE, CLONE_FILES | SIGCHLD, fexit_skel);  	if (CHECK(cpid == -1, "clone", "%s\n", strerror(errno)))  		goto cleanup; @@ -78,5 +83,6 @@ void test_fexit_sleep(void)  		goto cleanup;  cleanup: +	munmap(child_stack, STACK_SIZE);  	fexit_sleep_lskel__destroy(fexit_skel);  } diff --git a/tools/testing/selftests/bpf/prog_tests/fexit_stress.c b/tools/testing/selftests/bpf/prog_tests/fexit_stress.c index 596536def43d..49b1ffc9af1f 100644 --- a/tools/testing/selftests/bpf/prog_tests/fexit_stress.c +++ b/tools/testing/selftests/bpf/prog_tests/fexit_stress.c @@ -50,9 +50,9 @@ void serial_test_fexit_stress(void)  out:  	for (i = 0; i < bpf_max_tramp_links; i++) { -		if (link_fd[i]) +		if (link_fd[i] > 0)  			close(link_fd[i]); -		if (fexit_fd[i]) +		if (fexit_fd[i] > 0)  			close(fexit_fd[i]);  	}  	free(fd); diff --git a/tools/testing/selftests/bpf/prog_tests/find_vma.c b/tools/testing/selftests/bpf/prog_tests/find_vma.c index 5165b38f0e59..f7619e0ade10 100644 --- a/tools/testing/selftests/bpf/prog_tests/find_vma.c +++ b/tools/testing/selftests/bpf/prog_tests/find_vma.c @@ -29,8 +29,8 @@ static int open_pe(void)  	/* create perf event */  	attr.size = sizeof(attr); -	attr.type = PERF_TYPE_HARDWARE; -	attr.config = PERF_COUNT_HW_CPU_CYCLES; +	attr.type = PERF_TYPE_SOFTWARE; +	attr.config = PERF_COUNT_SW_CPU_CLOCK;  	attr.freq = 1;  	attr.sample_freq = 1000;  	pfd = syscall(__NR_perf_event_open, &attr, 0, -1, -1, PERF_FLAG_FD_CLOEXEC); diff --git a/tools/testing/selftests/bpf/prog_tests/ip_check_defrag.c b/tools/testing/selftests/bpf/prog_tests/ip_check_defrag.c index 284764e7179f..4ddb8a5fece8 100644 --- a/tools/testing/selftests/bpf/prog_tests/ip_check_defrag.c +++ b/tools/testing/selftests/bpf/prog_tests/ip_check_defrag.c @@ -158,15 +158,13 @@ static int send_frags6(int client)  void test_bpf_ip_check_defrag_ok(bool ipv6)  { +	int family = ipv6 ? AF_INET6 : AF_INET;  	struct network_helper_opts rx_opts = {  		.timeout_ms = 1000, -		.noconnect = true,  	};  	struct network_helper_opts tx_ops = {  		.timeout_ms = 1000, -		.type = SOCK_RAW,  		.proto = IPPROTO_RAW, -		.noconnect = true,  	};  	struct sockaddr_storage caddr;  	struct ip_check_defrag *skel; @@ -192,7 +190,7 @@ void test_bpf_ip_check_defrag_ok(bool ipv6)  	nstoken = open_netns(NS1);  	if (!ASSERT_OK_PTR(nstoken, "setns ns1"))  		goto out; -	srv_fd = start_server(ipv6 ? AF_INET6 : AF_INET, SOCK_DGRAM, NULL, SERVER_PORT, 0); +	srv_fd = start_server(family, SOCK_DGRAM, NULL, SERVER_PORT, 0);  	close_netns(nstoken);  	if (!ASSERT_GE(srv_fd, 0, "start_server"))  		goto out; @@ -201,18 +199,18 @@ void test_bpf_ip_check_defrag_ok(bool ipv6)  	nstoken = open_netns(NS0);  	if (!ASSERT_OK_PTR(nstoken, "setns ns0"))  		goto out; -	client_tx_fd = connect_to_fd_opts(srv_fd, &tx_ops); +	client_tx_fd = client_socket(family, SOCK_RAW, &tx_ops);  	close_netns(nstoken); -	if (!ASSERT_GE(client_tx_fd, 0, "connect_to_fd_opts")) +	if (!ASSERT_GE(client_tx_fd, 0, "client_socket"))  		goto out;  	/* Open rx socket in ns0 */  	nstoken = open_netns(NS0);  	if (!ASSERT_OK_PTR(nstoken, "setns ns0"))  		goto out; -	client_rx_fd = connect_to_fd_opts(srv_fd, &rx_opts); +	client_rx_fd = client_socket(family, SOCK_DGRAM, &rx_opts);  	close_netns(nstoken); -	if (!ASSERT_GE(client_rx_fd, 0, "connect_to_fd_opts")) +	if (!ASSERT_GE(client_rx_fd, 0, "client_socket"))  		goto out;  	/* Bind rx socket to a premeditated port */ diff --git a/tools/testing/selftests/bpf/prog_tests/kfunc_call.c b/tools/testing/selftests/bpf/prog_tests/kfunc_call.c index 2eb71559713c..5b743212292f 100644 --- a/tools/testing/selftests/bpf/prog_tests/kfunc_call.c +++ b/tools/testing/selftests/bpf/prog_tests/kfunc_call.c @@ -78,6 +78,7 @@ static struct kfunc_test_params kfunc_tests[] = {  	SYSCALL_TEST(kfunc_syscall_test, 0),  	SYSCALL_NULL_CTX_TEST(kfunc_syscall_test_null, 0),  	TC_TEST(kfunc_call_test_static_unused_arg, 0), +	TC_TEST(kfunc_call_ctx, 0),  };  struct syscall_test_args { diff --git a/tools/testing/selftests/bpf/prog_tests/kfunc_param_nullable.c b/tools/testing/selftests/bpf/prog_tests/kfunc_param_nullable.c new file mode 100644 index 000000000000..c8f4dcaac7c7 --- /dev/null +++ b/tools/testing/selftests/bpf/prog_tests/kfunc_param_nullable.c @@ -0,0 +1,11 @@ +// SPDX-License-Identifier: GPL-2.0 + +/* Copyright (c) 2024 Meta Platforms, Inc */ + +#include <test_progs.h> +#include "test_kfunc_param_nullable.skel.h" + +void test_kfunc_param_nullable(void) +{ +	RUN_TESTS(test_kfunc_param_nullable); +} diff --git a/tools/testing/selftests/bpf/prog_tests/linked_list.c b/tools/testing/selftests/bpf/prog_tests/linked_list.c index 2fb89de63bd2..77d07e0a4a55 100644 --- a/tools/testing/selftests/bpf/prog_tests/linked_list.c +++ b/tools/testing/selftests/bpf/prog_tests/linked_list.c @@ -183,6 +183,18 @@ static void test_linked_list_success(int mode, bool leave_in_map)  	if (!leave_in_map)  		clear_fields(skel->maps.bss_A); +	ret = bpf_prog_test_run_opts(bpf_program__fd(skel->progs.global_list_push_pop_nested), &opts); +	ASSERT_OK(ret, "global_list_push_pop_nested"); +	ASSERT_OK(opts.retval, "global_list_push_pop_nested retval"); +	if (!leave_in_map) +		clear_fields(skel->maps.bss_A); + +	ret = bpf_prog_test_run_opts(bpf_program__fd(skel->progs.global_list_array_push_pop), &opts); +	ASSERT_OK(ret, "global_list_array_push_pop"); +	ASSERT_OK(opts.retval, "global_list_array_push_pop retval"); +	if (!leave_in_map) +		clear_fields(skel->maps.bss_A); +  	if (mode == PUSH_POP)  		goto end; diff --git a/tools/testing/selftests/bpf/prog_tests/mptcp.c b/tools/testing/selftests/bpf/prog_tests/mptcp.c index 274d2e033e39..d2ca32fa3b21 100644 --- a/tools/testing/selftests/bpf/prog_tests/mptcp.c +++ b/tools/testing/selftests/bpf/prog_tests/mptcp.c @@ -89,13 +89,8 @@ static int start_mptcp_server(int family, const char *addr_str, __u16 port,  		.timeout_ms	= timeout_ms,  		.proto		= IPPROTO_MPTCP,  	}; -	struct sockaddr_storage addr; -	socklen_t addrlen; -	if (make_sockaddr(family, addr_str, port, &addr, &addrlen)) -		return -1; - -	return start_server_addr(SOCK_STREAM, &addr, addrlen, &opts); +	return start_server_str(family, SOCK_STREAM, addr_str, port, &opts);  }  static int verify_tsk(int map_fd, int client_fd) diff --git a/tools/testing/selftests/bpf/prog_tests/rbtree.c b/tools/testing/selftests/bpf/prog_tests/rbtree.c index e9300c96607d..9818f06c97c5 100644 --- a/tools/testing/selftests/bpf/prog_tests/rbtree.c +++ b/tools/testing/selftests/bpf/prog_tests/rbtree.c @@ -31,6 +31,28 @@ static void test_rbtree_add_nodes(void)  	rbtree__destroy(skel);  } +static void test_rbtree_add_nodes_nested(void) +{ +	LIBBPF_OPTS(bpf_test_run_opts, opts, +		    .data_in = &pkt_v4, +		    .data_size_in = sizeof(pkt_v4), +		    .repeat = 1, +	); +	struct rbtree *skel; +	int ret; + +	skel = rbtree__open_and_load(); +	if (!ASSERT_OK_PTR(skel, "rbtree__open_and_load")) +		return; + +	ret = bpf_prog_test_run_opts(bpf_program__fd(skel->progs.rbtree_add_nodes_nested), &opts); +	ASSERT_OK(ret, "rbtree_add_nodes_nested run"); +	ASSERT_OK(opts.retval, "rbtree_add_nodes_nested retval"); +	ASSERT_EQ(skel->data->less_callback_ran, 1, "rbtree_add_nodes_nested less_callback_ran"); + +	rbtree__destroy(skel); +} +  static void test_rbtree_add_and_remove(void)  {  	LIBBPF_OPTS(bpf_test_run_opts, opts, @@ -53,6 +75,27 @@ static void test_rbtree_add_and_remove(void)  	rbtree__destroy(skel);  } +static void test_rbtree_add_and_remove_array(void) +{ +	LIBBPF_OPTS(bpf_test_run_opts, opts, +		    .data_in = &pkt_v4, +		    .data_size_in = sizeof(pkt_v4), +		    .repeat = 1, +	); +	struct rbtree *skel; +	int ret; + +	skel = rbtree__open_and_load(); +	if (!ASSERT_OK_PTR(skel, "rbtree__open_and_load")) +		return; + +	ret = bpf_prog_test_run_opts(bpf_program__fd(skel->progs.rbtree_add_and_remove_array), &opts); +	ASSERT_OK(ret, "rbtree_add_and_remove_array"); +	ASSERT_OK(opts.retval, "rbtree_add_and_remove_array retval"); + +	rbtree__destroy(skel); +} +  static void test_rbtree_first_and_remove(void)  {  	LIBBPF_OPTS(bpf_test_run_opts, opts, @@ -104,8 +147,12 @@ void test_rbtree_success(void)  {  	if (test__start_subtest("rbtree_add_nodes"))  		test_rbtree_add_nodes(); +	if (test__start_subtest("rbtree_add_nodes_nested")) +		test_rbtree_add_nodes_nested();  	if (test__start_subtest("rbtree_add_and_remove"))  		test_rbtree_add_and_remove(); +	if (test__start_subtest("rbtree_add_and_remove_array")) +		test_rbtree_add_and_remove_array();  	if (test__start_subtest("rbtree_first_and_remove"))  		test_rbtree_first_and_remove();  	if (test__start_subtest("rbtree_api_release_aliasing")) diff --git a/tools/testing/selftests/bpf/prog_tests/send_signal.c b/tools/testing/selftests/bpf/prog_tests/send_signal.c index 920aee41bd58..6cc69900b310 100644 --- a/tools/testing/selftests/bpf/prog_tests/send_signal.c +++ b/tools/testing/selftests/bpf/prog_tests/send_signal.c @@ -156,7 +156,8 @@ static void test_send_signal_tracepoint(bool signal_thread)  static void test_send_signal_perf(bool signal_thread)  {  	struct perf_event_attr attr = { -		.sample_period = 1, +		.freq = 1, +		.sample_freq = 1000,  		.type = PERF_TYPE_SOFTWARE,  		.config = PERF_COUNT_SW_CPU_CLOCK,  	}; diff --git a/tools/testing/selftests/bpf/prog_tests/sk_lookup.c b/tools/testing/selftests/bpf/prog_tests/sk_lookup.c index 597d0467a926..ae87c00867ba 100644 --- a/tools/testing/selftests/bpf/prog_tests/sk_lookup.c +++ b/tools/testing/selftests/bpf/prog_tests/sk_lookup.c @@ -77,6 +77,12 @@ struct test {  	bool reuseport_has_conns; /* Add a connected socket to reuseport group */  }; +struct cb_opts { +	int family; +	int sotype; +	bool reuseport; +}; +  static __u32 duration;		/* for CHECK macro */  static bool is_ipv6(const char *ip) @@ -142,19 +148,14 @@ static int make_socket(int sotype, const char *ip, int port,  	return fd;  } -static int make_server(int sotype, const char *ip, int port, -		       struct bpf_program *reuseport_prog) +static int setsockopts(int fd, void *opts)  { -	struct sockaddr_storage addr = {0}; +	struct cb_opts *co = (struct cb_opts *)opts;  	const int one = 1; -	int err, fd = -1; - -	fd = make_socket(sotype, ip, port, &addr); -	if (fd < 0) -		return -1; +	int err = 0;  	/* Enabled for UDPv6 sockets for IPv4-mapped IPv6 to work. */ -	if (sotype == SOCK_DGRAM) { +	if (co->sotype == SOCK_DGRAM) {  		err = setsockopt(fd, SOL_IP, IP_RECVORIGDSTADDR, &one,  				 sizeof(one));  		if (CHECK(err, "setsockopt(IP_RECVORIGDSTADDR)", "failed\n")) { @@ -163,7 +164,7 @@ static int make_server(int sotype, const char *ip, int port,  		}  	} -	if (sotype == SOCK_DGRAM && addr.ss_family == AF_INET6) { +	if (co->sotype == SOCK_DGRAM && co->family == AF_INET6) {  		err = setsockopt(fd, SOL_IPV6, IPV6_RECVORIGDSTADDR, &one,  				 sizeof(one));  		if (CHECK(err, "setsockopt(IPV6_RECVORIGDSTADDR)", "failed\n")) { @@ -172,7 +173,7 @@ static int make_server(int sotype, const char *ip, int port,  		}  	} -	if (sotype == SOCK_STREAM) { +	if (co->sotype == SOCK_STREAM) {  		err = setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &one,  				 sizeof(one));  		if (CHECK(err, "setsockopt(SO_REUSEADDR)", "failed\n")) { @@ -181,7 +182,7 @@ static int make_server(int sotype, const char *ip, int port,  		}  	} -	if (reuseport_prog) { +	if (co->reuseport) {  		err = setsockopt(fd, SOL_SOCKET, SO_REUSEPORT, &one,  				 sizeof(one));  		if (CHECK(err, "setsockopt(SO_REUSEPORT)", "failed\n")) { @@ -190,19 +191,28 @@ static int make_server(int sotype, const char *ip, int port,  		}  	} -	err = bind(fd, (void *)&addr, inetaddr_len(&addr)); -	if (CHECK(err, "bind", "failed\n")) { -		log_err("failed to bind listen socket"); -		goto fail; -	} +fail: +	return err; +} -	if (sotype == SOCK_STREAM) { -		err = listen(fd, SOMAXCONN); -		if (CHECK(err, "make_server", "listen")) { -			log_err("failed to listen on port %d", port); -			goto fail; -		} -	} +static int make_server(int sotype, const char *ip, int port, +		       struct bpf_program *reuseport_prog) +{ +	struct cb_opts cb_opts = { +		.family = is_ipv6(ip) ? AF_INET6 : AF_INET, +		.sotype = sotype, +		.reuseport = reuseport_prog, +	}; +	struct network_helper_opts opts = { +		.backlog	= SOMAXCONN, +		.post_socket_cb = setsockopts, +		.cb_opts	= &cb_opts, +	}; +	int err, fd; + +	fd = start_server_str(cb_opts.family, sotype, ip, port, &opts); +	if (!ASSERT_OK_FD(fd, "start_server_str")) +		return -1;  	/* Late attach reuseport prog so we can have one init path */  	if (reuseport_prog) { @@ -406,18 +416,12 @@ static int udp_recv_send(int server_fd)  	}  	/* Reply from original destination address. */ -	fd = socket(dst_addr->ss_family, SOCK_DGRAM, 0); -	if (CHECK(fd < 0, "socket", "failed\n")) { +	fd = start_server_addr(SOCK_DGRAM, dst_addr, sizeof(*dst_addr), NULL); +	if (!ASSERT_OK_FD(fd, "start_server_addr")) {  		log_err("failed to create tx socket");  		return -1;  	} -	ret = bind(fd, (struct sockaddr *)dst_addr, sizeof(*dst_addr)); -	if (CHECK(ret, "bind", "failed\n")) { -		log_err("failed to bind tx socket"); -		goto out; -	} -  	msg.msg_control = NULL;  	msg.msg_controllen = 0;  	n = sendmsg(fd, &msg, 0); @@ -629,9 +633,6 @@ static void run_lookup_prog(const struct test *t)  	 * BPF socket lookup.  	 */  	if (t->reuseport_has_conns) { -		struct sockaddr_storage addr = {}; -		socklen_t len = sizeof(addr); -  		/* Add an extra socket to reuseport group */  		reuse_conn_fd = make_server(t->sotype, t->listen_at.ip,  					    t->listen_at.port, @@ -639,12 +640,9 @@ static void run_lookup_prog(const struct test *t)  		if (reuse_conn_fd < 0)  			goto close; -		/* Connect the extra socket to itself */ -		err = getsockname(reuse_conn_fd, (void *)&addr, &len); -		if (CHECK(err, "getsockname", "errno %d\n", errno)) -			goto close; -		err = connect(reuse_conn_fd, (void *)&addr, len); -		if (CHECK(err, "connect", "errno %d\n", errno)) +		 /* Connect the extra socket to itself */ +		err = connect_fd_to_fd(reuse_conn_fd, reuse_conn_fd, 0); +		if (!ASSERT_OK(err, "connect_fd_to_fd"))  			goto close;  	} @@ -994,7 +992,7 @@ static void drop_on_reuseport(const struct test *t)  	err = update_lookup_map(t->sock_map, SERVER_A, server1);  	if (err) -		goto detach; +		goto close_srv1;  	/* second server on destination address we should never reach */  	server2 = make_server(t->sotype, t->connect_to.ip, t->connect_to.port, diff --git a/tools/testing/selftests/bpf/prog_tests/sockmap_listen.c b/tools/testing/selftests/bpf/prog_tests/sockmap_listen.c index e91b59366030..9ce0e0e0b7da 100644 --- a/tools/testing/selftests/bpf/prog_tests/sockmap_listen.c +++ b/tools/testing/selftests/bpf/prog_tests/sockmap_listen.c @@ -29,6 +29,8 @@  #include "sockmap_helpers.h" +#define NO_FLAGS 0 +  static void test_insert_invalid(struct test_sockmap_listen *skel __always_unused,  				int family, int sotype, int mapfd)  { @@ -1376,7 +1378,8 @@ static void test_redir(struct test_sockmap_listen *skel, struct bpf_map *map,  static void pairs_redir_to_connected(int cli0, int peer0, int cli1, int peer1,  				     int sock_mapfd, int nop_mapfd, -				     int verd_mapfd, enum redir_mode mode) +				     int verd_mapfd, enum redir_mode mode, +				     int send_flags)  {  	const char *log_prefix = redir_mode_str(mode);  	unsigned int pass; @@ -1396,12 +1399,11 @@ static void pairs_redir_to_connected(int cli0, int peer0, int cli1, int peer1,  			return;  	} -	n = write(cli1, "a", 1); -	if (n < 0) -		FAIL_ERRNO("%s: write", log_prefix); -	if (n == 0) -		FAIL("%s: incomplete write", log_prefix); -	if (n < 1) +	/* Last byte is OOB data when send_flags has MSG_OOB bit set */ +	n = xsend(cli1, "ab", 2, send_flags); +	if (n >= 0 && n < 2) +		FAIL("%s: incomplete send", log_prefix); +	if (n < 2)  		return;  	key = SK_PASS; @@ -1416,6 +1418,25 @@ static void pairs_redir_to_connected(int cli0, int peer0, int cli1, int peer1,  		FAIL_ERRNO("%s: recv_timeout", log_prefix);  	if (n == 0)  		FAIL("%s: incomplete recv", log_prefix); + +	if (send_flags & MSG_OOB) { +		/* Check that we can't read OOB while in sockmap */ +		errno = 0; +		n = recv(peer1, &b, 1, MSG_OOB | MSG_DONTWAIT); +		if (n != -1 || errno != EOPNOTSUPP) +			FAIL("%s: recv(MSG_OOB): expected EOPNOTSUPP: retval=%d errno=%d", +			     log_prefix, n, errno); + +		/* Remove peer1 from sockmap */ +		xbpf_map_delete_elem(sock_mapfd, &(int){ 1 }); + +		/* Check that OOB was dropped on redirect */ +		errno = 0; +		n = recv(peer1, &b, 1, MSG_OOB | MSG_DONTWAIT); +		if (n != -1 || errno != EINVAL) +			FAIL("%s: recv(MSG_OOB): expected EINVAL: retval=%d errno=%d", +			     log_prefix, n, errno); +	}  }  static void unix_redir_to_connected(int sotype, int sock_mapfd, @@ -1432,7 +1453,8 @@ static void unix_redir_to_connected(int sotype, int sock_mapfd,  		goto close0;  	c1 = sfd[0], p1 = sfd[1]; -	pairs_redir_to_connected(c0, p0, c1, p1, sock_mapfd, -1, verd_mapfd, mode); +	pairs_redir_to_connected(c0, p0, c1, p1, sock_mapfd, -1, verd_mapfd, +				 mode, NO_FLAGS);  	xclose(c1);  	xclose(p1); @@ -1722,7 +1744,8 @@ static void udp_redir_to_connected(int family, int sock_mapfd, int verd_mapfd,  	if (err)  		goto close_cli0; -	pairs_redir_to_connected(c0, p0, c1, p1, sock_mapfd, -1, verd_mapfd, mode); +	pairs_redir_to_connected(c0, p0, c1, p1, sock_mapfd, -1, verd_mapfd, +				 mode, NO_FLAGS);  	xclose(c1);  	xclose(p1); @@ -1780,7 +1803,8 @@ static void inet_unix_redir_to_connected(int family, int type, int sock_mapfd,  	if (err)  		goto close; -	pairs_redir_to_connected(c0, p0, c1, p1, sock_mapfd, -1, verd_mapfd, mode); +	pairs_redir_to_connected(c0, p0, c1, p1, sock_mapfd, -1, verd_mapfd, +				 mode, NO_FLAGS);  	xclose(c1);  	xclose(p1); @@ -1815,10 +1839,9 @@ static void inet_unix_skb_redir_to_connected(struct test_sockmap_listen *skel,  	xbpf_prog_detach2(verdict, sock_map, BPF_SK_SKB_VERDICT);  } -static void unix_inet_redir_to_connected(int family, int type, -					int sock_mapfd, int nop_mapfd, -					int verd_mapfd, -					enum redir_mode mode) +static void unix_inet_redir_to_connected(int family, int type, int sock_mapfd, +					 int nop_mapfd, int verd_mapfd, +					 enum redir_mode mode, int send_flags)  {  	int c0, c1, p0, p1;  	int sfd[2]; @@ -1828,19 +1851,18 @@ static void unix_inet_redir_to_connected(int family, int type,  	if (err)  		return; -	if (socketpair(AF_UNIX, SOCK_DGRAM | SOCK_NONBLOCK, 0, sfd)) +	if (socketpair(AF_UNIX, type | SOCK_NONBLOCK, 0, sfd))  		goto close_cli0;  	c1 = sfd[0], p1 = sfd[1]; -	pairs_redir_to_connected(c0, p0, c1, p1, -				 sock_mapfd, nop_mapfd, verd_mapfd, mode); +	pairs_redir_to_connected(c0, p0, c1, p1, sock_mapfd, nop_mapfd, +				 verd_mapfd, mode, send_flags);  	xclose(c1);  	xclose(p1);  close_cli0:  	xclose(c0);  	xclose(p0); -  }  static void unix_inet_skb_redir_to_connected(struct test_sockmap_listen *skel, @@ -1859,31 +1881,42 @@ static void unix_inet_skb_redir_to_connected(struct test_sockmap_listen *skel,  	skel->bss->test_ingress = false;  	unix_inet_redir_to_connected(family, SOCK_DGRAM,  				     sock_map, -1, verdict_map, -				     REDIR_EGRESS); +				     REDIR_EGRESS, NO_FLAGS);  	unix_inet_redir_to_connected(family, SOCK_DGRAM,  				     sock_map, -1, verdict_map, -				     REDIR_EGRESS); +				     REDIR_EGRESS, NO_FLAGS);  	unix_inet_redir_to_connected(family, SOCK_DGRAM,  				     sock_map, nop_map, verdict_map, -				     REDIR_EGRESS); +				     REDIR_EGRESS, NO_FLAGS); +	unix_inet_redir_to_connected(family, SOCK_STREAM, +				     sock_map, nop_map, verdict_map, +				     REDIR_EGRESS, NO_FLAGS); + +	/* MSG_OOB not supported by AF_UNIX SOCK_DGRAM */  	unix_inet_redir_to_connected(family, SOCK_STREAM,  				     sock_map, nop_map, verdict_map, -				     REDIR_EGRESS); +				     REDIR_EGRESS, MSG_OOB); +  	skel->bss->test_ingress = true;  	unix_inet_redir_to_connected(family, SOCK_DGRAM,  				     sock_map, -1, verdict_map, -				     REDIR_INGRESS); +				     REDIR_INGRESS, NO_FLAGS);  	unix_inet_redir_to_connected(family, SOCK_STREAM,  				     sock_map, -1, verdict_map, -				     REDIR_INGRESS); +				     REDIR_INGRESS, NO_FLAGS);  	unix_inet_redir_to_connected(family, SOCK_DGRAM,  				     sock_map, nop_map, verdict_map, -				     REDIR_INGRESS); +				     REDIR_INGRESS, NO_FLAGS); +	unix_inet_redir_to_connected(family, SOCK_STREAM, +				     sock_map, nop_map, verdict_map, +				     REDIR_INGRESS, NO_FLAGS); + +	/* MSG_OOB not supported by AF_UNIX SOCK_DGRAM */  	unix_inet_redir_to_connected(family, SOCK_STREAM,  				     sock_map, nop_map, verdict_map, -				     REDIR_INGRESS); +				     REDIR_INGRESS, MSG_OOB);  	xbpf_prog_detach2(verdict, sock_map, BPF_SK_SKB_VERDICT);  } diff --git a/tools/testing/selftests/bpf/prog_tests/sockopt_inherit.c b/tools/testing/selftests/bpf/prog_tests/sockopt_inherit.c index 1d3a20f01b60..7cd8be2780ca 100644 --- a/tools/testing/selftests/bpf/prog_tests/sockopt_inherit.c +++ b/tools/testing/selftests/bpf/prog_tests/sockopt_inherit.c @@ -70,7 +70,7 @@ static void *server_thread(void *arg)  	return (void *)(long)err;  } -static int custom_cb(int fd, const struct post_socket_opts *opts) +static int custom_cb(int fd, void *opts)  {  	char buf;  	int err; diff --git a/tools/testing/selftests/bpf/prog_tests/tc_redirect.c b/tools/testing/selftests/bpf/prog_tests/tc_redirect.c index b1073d36d77a..327d51f59142 100644 --- a/tools/testing/selftests/bpf/prog_tests/tc_redirect.c +++ b/tools/testing/selftests/bpf/prog_tests/tc_redirect.c @@ -890,9 +890,6 @@ static void test_udp_dtime(struct test_tc_dtime *skel, int family, bool bpf_fwd)  	ASSERT_EQ(dtimes[INGRESS_FWDNS_P100], 0,  		  dtime_cnt_str(t, INGRESS_FWDNS_P100)); -	/* non mono delivery time is not forwarded */ -	ASSERT_EQ(dtimes[INGRESS_FWDNS_P101], 0, -		  dtime_cnt_str(t, INGRESS_FWDNS_P101));  	for (i = EGRESS_FWDNS_P100; i < SET_DTIME; i++)  		ASSERT_GT(dtimes[i], 0, dtime_cnt_str(t, i)); diff --git a/tools/testing/selftests/bpf/prog_tests/test_skb_pkt_end.c b/tools/testing/selftests/bpf/prog_tests/test_skb_pkt_end.c index ae93411fd582..09ca13bdf6ca 100644 --- a/tools/testing/selftests/bpf/prog_tests/test_skb_pkt_end.c +++ b/tools/testing/selftests/bpf/prog_tests/test_skb_pkt_end.c @@ -11,6 +11,7 @@ static int sanity_run(struct bpf_program *prog)  		.data_in = &pkt_v4,  		.data_size_in = sizeof(pkt_v4),  		.repeat = 1, +		.flags = BPF_F_TEST_SKB_CHECKSUM_COMPLETE,  	);  	prog_fd = bpf_program__fd(prog); diff --git a/tools/testing/selftests/bpf/prog_tests/test_struct_ops_module.c b/tools/testing/selftests/bpf/prog_tests/test_struct_ops_module.c index 29e183a80f49..bbcf12696a6b 100644 --- a/tools/testing/selftests/bpf/prog_tests/test_struct_ops_module.c +++ b/tools/testing/selftests/bpf/prog_tests/test_struct_ops_module.c @@ -3,9 +3,12 @@  #include <test_progs.h>  #include <time.h> +#include <sys/epoll.h> +  #include "struct_ops_module.skel.h"  #include "struct_ops_nulled_out_cb.skel.h"  #include "struct_ops_forgotten_cb.skel.h" +#include "struct_ops_detach.skel.h"  static void check_map_info(struct bpf_map_info *info)  { @@ -242,6 +245,58 @@ cleanup:  	struct_ops_forgotten_cb__destroy(skel);  } +/* Detach a link from a user space program */ +static void test_detach_link(void) +{ +	struct epoll_event ev, events[2]; +	struct struct_ops_detach *skel; +	struct bpf_link *link = NULL; +	int fd, epollfd = -1, nfds; +	int err; + +	skel = struct_ops_detach__open_and_load(); +	if (!ASSERT_OK_PTR(skel, "struct_ops_detach__open_and_load")) +		return; + +	link = bpf_map__attach_struct_ops(skel->maps.testmod_do_detach); +	if (!ASSERT_OK_PTR(link, "attach_struct_ops")) +		goto cleanup; + +	fd = bpf_link__fd(link); +	if (!ASSERT_GE(fd, 0, "link_fd")) +		goto cleanup; + +	epollfd = epoll_create1(0); +	if (!ASSERT_GE(epollfd, 0, "epoll_create1")) +		goto cleanup; + +	ev.events = EPOLLHUP; +	ev.data.fd = fd; +	err = epoll_ctl(epollfd, EPOLL_CTL_ADD, fd, &ev); +	if (!ASSERT_OK(err, "epoll_ctl")) +		goto cleanup; + +	err = bpf_link__detach(link); +	if (!ASSERT_OK(err, "detach_link")) +		goto cleanup; + +	/* Wait for EPOLLHUP */ +	nfds = epoll_wait(epollfd, events, 2, 500); +	if (!ASSERT_EQ(nfds, 1, "epoll_wait")) +		goto cleanup; + +	if (!ASSERT_EQ(events[0].data.fd, fd, "epoll_wait_fd")) +		goto cleanup; +	if (!ASSERT_TRUE(events[0].events & EPOLLHUP, "events[0].events")) +		goto cleanup; + +cleanup: +	if (epollfd >= 0) +		close(epollfd); +	bpf_link__destroy(link); +	struct_ops_detach__destroy(skel); +} +  void serial_test_struct_ops_module(void)  {  	if (test__start_subtest("struct_ops_load")) @@ -254,5 +309,7 @@ void serial_test_struct_ops_module(void)  		test_struct_ops_nulled_out_cb();  	if (test__start_subtest("struct_ops_forgotten_cb"))  		test_struct_ops_forgotten_cb(); +	if (test__start_subtest("test_detach_link")) +		test_detach_link();  } diff --git a/tools/testing/selftests/bpf/prog_tests/tracing_struct.c b/tools/testing/selftests/bpf/prog_tests/tracing_struct.c index fe0fb0c9849a..19e68d4b3532 100644 --- a/tools/testing/selftests/bpf/prog_tests/tracing_struct.c +++ b/tools/testing/selftests/bpf/prog_tests/tracing_struct.c @@ -3,8 +3,9 @@  #include <test_progs.h>  #include "tracing_struct.skel.h" +#include "tracing_struct_many_args.skel.h" -static void test_fentry(void) +static void test_struct_args(void)  {  	struct tracing_struct *skel;  	int err; @@ -55,6 +56,25 @@ static void test_fentry(void)  	ASSERT_EQ(skel->bss->t6, 1, "t6 ret"); +destroy_skel: +	tracing_struct__destroy(skel); +} + +static void test_struct_many_args(void) +{ +	struct tracing_struct_many_args *skel; +	int err; + +	skel = tracing_struct_many_args__open_and_load(); +	if (!ASSERT_OK_PTR(skel, "tracing_struct_many_args__open_and_load")) +		return; + +	err = tracing_struct_many_args__attach(skel); +	if (!ASSERT_OK(err, "tracing_struct_many_args__attach")) +		goto destroy_skel; + +	ASSERT_OK(trigger_module_test_read(256), "trigger_read"); +  	ASSERT_EQ(skel->bss->t7_a, 16, "t7:a");  	ASSERT_EQ(skel->bss->t7_b, 17, "t7:b");  	ASSERT_EQ(skel->bss->t7_c, 18, "t7:c"); @@ -74,12 +94,28 @@ static void test_fentry(void)  	ASSERT_EQ(skel->bss->t8_g, 23, "t8:g");  	ASSERT_EQ(skel->bss->t8_ret, 156, "t8 ret"); -	tracing_struct__detach(skel); +	ASSERT_EQ(skel->bss->t9_a, 16, "t9:a"); +	ASSERT_EQ(skel->bss->t9_b, 17, "t9:b"); +	ASSERT_EQ(skel->bss->t9_c, 18, "t9:c"); +	ASSERT_EQ(skel->bss->t9_d, 19, "t9:d"); +	ASSERT_EQ(skel->bss->t9_e, 20, "t9:e"); +	ASSERT_EQ(skel->bss->t9_f, 21, "t9:f"); +	ASSERT_EQ(skel->bss->t9_g, 22, "t9:f"); +	ASSERT_EQ(skel->bss->t9_h_a, 23, "t9:h.a"); +	ASSERT_EQ(skel->bss->t9_h_b, 24, "t9:h.b"); +	ASSERT_EQ(skel->bss->t9_h_c, 25, "t9:h.c"); +	ASSERT_EQ(skel->bss->t9_h_d, 26, "t9:h.d"); +	ASSERT_EQ(skel->bss->t9_i, 27, "t9:i"); +	ASSERT_EQ(skel->bss->t9_ret, 258, "t9 ret"); +  destroy_skel: -	tracing_struct__destroy(skel); +	tracing_struct_many_args__destroy(skel);  }  void test_tracing_struct(void)  { -	test_fentry(); +	if (test__start_subtest("struct_args")) +		test_struct_args(); +	if (test__start_subtest("struct_many_args")) +		test_struct_many_args();  } diff --git a/tools/testing/selftests/bpf/prog_tests/uprobe_syscall.c b/tools/testing/selftests/bpf/prog_tests/uprobe_syscall.c new file mode 100644 index 000000000000..bd8c75b620c2 --- /dev/null +++ b/tools/testing/selftests/bpf/prog_tests/uprobe_syscall.c @@ -0,0 +1,385 @@ +// SPDX-License-Identifier: GPL-2.0 + +#include <test_progs.h> + +#ifdef __x86_64__ + +#include <unistd.h> +#include <asm/ptrace.h> +#include <linux/compiler.h> +#include <linux/stringify.h> +#include <sys/wait.h> +#include <sys/syscall.h> +#include <sys/prctl.h> +#include <asm/prctl.h> +#include "uprobe_syscall.skel.h" +#include "uprobe_syscall_executed.skel.h" + +__naked unsigned long uretprobe_regs_trigger(void) +{ +	asm volatile ( +		"movq $0xdeadbeef, %rax\n" +		"ret\n" +	); +} + +__naked void uretprobe_regs(struct pt_regs *before, struct pt_regs *after) +{ +	asm volatile ( +		"movq %r15,   0(%rdi)\n" +		"movq %r14,   8(%rdi)\n" +		"movq %r13,  16(%rdi)\n" +		"movq %r12,  24(%rdi)\n" +		"movq %rbp,  32(%rdi)\n" +		"movq %rbx,  40(%rdi)\n" +		"movq %r11,  48(%rdi)\n" +		"movq %r10,  56(%rdi)\n" +		"movq  %r9,  64(%rdi)\n" +		"movq  %r8,  72(%rdi)\n" +		"movq %rax,  80(%rdi)\n" +		"movq %rcx,  88(%rdi)\n" +		"movq %rdx,  96(%rdi)\n" +		"movq %rsi, 104(%rdi)\n" +		"movq %rdi, 112(%rdi)\n" +		"movq   $0, 120(%rdi)\n" /* orig_rax */ +		"movq   $0, 128(%rdi)\n" /* rip      */ +		"movq   $0, 136(%rdi)\n" /* cs       */ +		"pushf\n" +		"pop %rax\n" +		"movq %rax, 144(%rdi)\n" /* eflags   */ +		"movq %rsp, 152(%rdi)\n" /* rsp      */ +		"movq   $0, 160(%rdi)\n" /* ss       */ + +		/* save 2nd argument */ +		"pushq %rsi\n" +		"call uretprobe_regs_trigger\n" + +		/* save  return value and load 2nd argument pointer to rax */ +		"pushq %rax\n" +		"movq 8(%rsp), %rax\n" + +		"movq %r15,   0(%rax)\n" +		"movq %r14,   8(%rax)\n" +		"movq %r13,  16(%rax)\n" +		"movq %r12,  24(%rax)\n" +		"movq %rbp,  32(%rax)\n" +		"movq %rbx,  40(%rax)\n" +		"movq %r11,  48(%rax)\n" +		"movq %r10,  56(%rax)\n" +		"movq  %r9,  64(%rax)\n" +		"movq  %r8,  72(%rax)\n" +		"movq %rcx,  88(%rax)\n" +		"movq %rdx,  96(%rax)\n" +		"movq %rsi, 104(%rax)\n" +		"movq %rdi, 112(%rax)\n" +		"movq   $0, 120(%rax)\n" /* orig_rax */ +		"movq   $0, 128(%rax)\n" /* rip      */ +		"movq   $0, 136(%rax)\n" /* cs       */ + +		/* restore return value and 2nd argument */ +		"pop %rax\n" +		"pop %rsi\n" + +		"movq %rax,  80(%rsi)\n" + +		"pushf\n" +		"pop %rax\n" + +		"movq %rax, 144(%rsi)\n" /* eflags   */ +		"movq %rsp, 152(%rsi)\n" /* rsp      */ +		"movq   $0, 160(%rsi)\n" /* ss       */ +		"ret\n" +); +} + +static void test_uretprobe_regs_equal(void) +{ +	struct uprobe_syscall *skel = NULL; +	struct pt_regs before = {}, after = {}; +	unsigned long *pb = (unsigned long *) &before; +	unsigned long *pa = (unsigned long *) &after; +	unsigned long *pp; +	unsigned int i, cnt; +	int err; + +	skel = uprobe_syscall__open_and_load(); +	if (!ASSERT_OK_PTR(skel, "uprobe_syscall__open_and_load")) +		goto cleanup; + +	err = uprobe_syscall__attach(skel); +	if (!ASSERT_OK(err, "uprobe_syscall__attach")) +		goto cleanup; + +	uretprobe_regs(&before, &after); + +	pp = (unsigned long *) &skel->bss->regs; +	cnt = sizeof(before)/sizeof(*pb); + +	for (i = 0; i < cnt; i++) { +		unsigned int offset = i * sizeof(unsigned long); + +		/* +		 * Check register before and after uretprobe_regs_trigger call +		 * that triggers the uretprobe. +		 */ +		switch (offset) { +		case offsetof(struct pt_regs, rax): +			ASSERT_EQ(pa[i], 0xdeadbeef, "return value"); +			break; +		default: +			if (!ASSERT_EQ(pb[i], pa[i], "register before-after value check")) +				fprintf(stdout, "failed register offset %u\n", offset); +		} + +		/* +		 * Check register seen from bpf program and register after +		 * uretprobe_regs_trigger call +		 */ +		switch (offset) { +		/* +		 * These values will be different (not set in uretprobe_regs), +		 * we don't care. +		 */ +		case offsetof(struct pt_regs, orig_rax): +		case offsetof(struct pt_regs, rip): +		case offsetof(struct pt_regs, cs): +		case offsetof(struct pt_regs, rsp): +		case offsetof(struct pt_regs, ss): +			break; +		default: +			if (!ASSERT_EQ(pp[i], pa[i], "register prog-after value check")) +				fprintf(stdout, "failed register offset %u\n", offset); +		} +	} + +cleanup: +	uprobe_syscall__destroy(skel); +} + +#define BPF_TESTMOD_UPROBE_TEST_FILE "/sys/kernel/bpf_testmod_uprobe" + +static int write_bpf_testmod_uprobe(unsigned long offset) +{ +	size_t n, ret; +	char buf[30]; +	int fd; + +	n = sprintf(buf, "%lu", offset); + +	fd = open(BPF_TESTMOD_UPROBE_TEST_FILE, O_WRONLY); +	if (fd < 0) +		return -errno; + +	ret = write(fd, buf, n); +	close(fd); +	return ret != n ? (int) ret : 0; +} + +static void test_uretprobe_regs_change(void) +{ +	struct pt_regs before = {}, after = {}; +	unsigned long *pb = (unsigned long *) &before; +	unsigned long *pa = (unsigned long *) &after; +	unsigned long cnt = sizeof(before)/sizeof(*pb); +	unsigned int i, err, offset; + +	offset = get_uprobe_offset(uretprobe_regs_trigger); + +	err = write_bpf_testmod_uprobe(offset); +	if (!ASSERT_OK(err, "register_uprobe")) +		return; + +	uretprobe_regs(&before, &after); + +	err = write_bpf_testmod_uprobe(0); +	if (!ASSERT_OK(err, "unregister_uprobe")) +		return; + +	for (i = 0; i < cnt; i++) { +		unsigned int offset = i * sizeof(unsigned long); + +		switch (offset) { +		case offsetof(struct pt_regs, rax): +			ASSERT_EQ(pa[i], 0x12345678deadbeef, "rax"); +			break; +		case offsetof(struct pt_regs, rcx): +			ASSERT_EQ(pa[i], 0x87654321feebdaed, "rcx"); +			break; +		case offsetof(struct pt_regs, r11): +			ASSERT_EQ(pa[i], (__u64) -1, "r11"); +			break; +		default: +			if (!ASSERT_EQ(pa[i], pb[i], "register before-after value check")) +				fprintf(stdout, "failed register offset %u\n", offset); +		} +	} +} + +#ifndef __NR_uretprobe +#define __NR_uretprobe 467 +#endif + +__naked unsigned long uretprobe_syscall_call_1(void) +{ +	/* +	 * Pretend we are uretprobe trampoline to trigger the return +	 * probe invocation in order to verify we get SIGILL. +	 */ +	asm volatile ( +		"pushq %rax\n" +		"pushq %rcx\n" +		"pushq %r11\n" +		"movq $" __stringify(__NR_uretprobe) ", %rax\n" +		"syscall\n" +		"popq %r11\n" +		"popq %rcx\n" +		"retq\n" +	); +} + +__naked unsigned long uretprobe_syscall_call(void) +{ +	asm volatile ( +		"call uretprobe_syscall_call_1\n" +		"retq\n" +	); +} + +static void test_uretprobe_syscall_call(void) +{ +	LIBBPF_OPTS(bpf_uprobe_multi_opts, opts, +		.retprobe = true, +	); +	struct uprobe_syscall_executed *skel; +	int pid, status, err, go[2], c; + +	if (ASSERT_OK(pipe(go), "pipe")) +		return; + +	skel = uprobe_syscall_executed__open_and_load(); +	if (!ASSERT_OK_PTR(skel, "uprobe_syscall_executed__open_and_load")) +		goto cleanup; + +	pid = fork(); +	if (!ASSERT_GE(pid, 0, "fork")) +		goto cleanup; + +	/* child */ +	if (pid == 0) { +		close(go[1]); + +		/* wait for parent's kick */ +		err = read(go[0], &c, 1); +		if (err != 1) +			exit(-1); + +		uretprobe_syscall_call(); +		_exit(0); +	} + +	skel->links.test = bpf_program__attach_uprobe_multi(skel->progs.test, pid, +							    "/proc/self/exe", +							    "uretprobe_syscall_call", &opts); +	if (!ASSERT_OK_PTR(skel->links.test, "bpf_program__attach_uprobe_multi")) +		goto cleanup; + +	/* kick the child */ +	write(go[1], &c, 1); +	err = waitpid(pid, &status, 0); +	ASSERT_EQ(err, pid, "waitpid"); + +	/* verify the child got killed with SIGILL */ +	ASSERT_EQ(WIFSIGNALED(status), 1, "WIFSIGNALED"); +	ASSERT_EQ(WTERMSIG(status), SIGILL, "WTERMSIG"); + +	/* verify the uretprobe program wasn't called */ +	ASSERT_EQ(skel->bss->executed, 0, "executed"); + +cleanup: +	uprobe_syscall_executed__destroy(skel); +	close(go[1]); +	close(go[0]); +} + +/* + * Borrowed from tools/testing/selftests/x86/test_shadow_stack.c. + * + * For use in inline enablement of shadow stack. + * + * The program can't return from the point where shadow stack gets enabled + * because there will be no address on the shadow stack. So it can't use + * syscall() for enablement, since it is a function. + * + * Based on code from nolibc.h. Keep a copy here because this can't pull + * in all of nolibc.h. + */ +#define ARCH_PRCTL(arg1, arg2)					\ +({								\ +	long _ret;						\ +	register long _num  asm("eax") = __NR_arch_prctl;	\ +	register long _arg1 asm("rdi") = (long)(arg1);		\ +	register long _arg2 asm("rsi") = (long)(arg2);		\ +								\ +	asm volatile (						\ +		"syscall\n"					\ +		: "=a"(_ret)					\ +		: "r"(_arg1), "r"(_arg2),			\ +		  "0"(_num)					\ +		: "rcx", "r11", "memory", "cc"			\ +	);							\ +	_ret;							\ +}) + +#ifndef ARCH_SHSTK_ENABLE +#define ARCH_SHSTK_ENABLE	0x5001 +#define ARCH_SHSTK_DISABLE	0x5002 +#define ARCH_SHSTK_SHSTK	(1ULL <<  0) +#endif + +static void test_uretprobe_shadow_stack(void) +{ +	if (ARCH_PRCTL(ARCH_SHSTK_ENABLE, ARCH_SHSTK_SHSTK)) { +		test__skip(); +		return; +	} + +	/* Run all of the uretprobe tests. */ +	test_uretprobe_regs_equal(); +	test_uretprobe_regs_change(); +	test_uretprobe_syscall_call(); + +	ARCH_PRCTL(ARCH_SHSTK_DISABLE, ARCH_SHSTK_SHSTK); +} +#else +static void test_uretprobe_regs_equal(void) +{ +	test__skip(); +} + +static void test_uretprobe_regs_change(void) +{ +	test__skip(); +} + +static void test_uretprobe_syscall_call(void) +{ +	test__skip(); +} + +static void test_uretprobe_shadow_stack(void) +{ +	test__skip(); +} +#endif + +void test_uprobe_syscall(void) +{ +	if (test__start_subtest("uretprobe_regs_equal")) +		test_uretprobe_regs_equal(); +	if (test__start_subtest("uretprobe_regs_change")) +		test_uretprobe_regs_change(); +	if (test__start_subtest("uretprobe_syscall_call")) +		test_uretprobe_syscall_call(); +	if (test__start_subtest("uretprobe_shadow_stack")) +		test_uretprobe_shadow_stack(); +} diff --git a/tools/testing/selftests/bpf/prog_tests/uretprobe_stack.c b/tools/testing/selftests/bpf/prog_tests/uretprobe_stack.c new file mode 100644 index 000000000000..6deb8d560ddd --- /dev/null +++ b/tools/testing/selftests/bpf/prog_tests/uretprobe_stack.c @@ -0,0 +1,186 @@ +// SPDX-License-Identifier: GPL-2.0 +/* Copyright (c) 2024 Meta Platforms, Inc. and affiliates. */ + +#include <test_progs.h> +#include "uretprobe_stack.skel.h" +#include "../sdt.h" + +/* We set up target_1() -> target_2() -> target_3() -> target_4() -> USDT() + * call chain, each being traced by our BPF program. On entry or return from + * each target_*() we are capturing user stack trace and recording it in + * global variable, so that user space part of the test can validate it. + * + * Note, we put each target function into a custom section to get those + * __start_XXX/__stop_XXX symbols, generated by linker for us, which allow us + * to know address range of those functions + */ +__attribute__((section("uprobe__target_4"))) +__weak int target_4(void) +{ +	STAP_PROBE1(uretprobe_stack, target, 42); +	return 42; +} + +extern const void *__start_uprobe__target_4; +extern const void *__stop_uprobe__target_4; + +__attribute__((section("uprobe__target_3"))) +__weak int target_3(void) +{ +	return target_4(); +} + +extern const void *__start_uprobe__target_3; +extern const void *__stop_uprobe__target_3; + +__attribute__((section("uprobe__target_2"))) +__weak int target_2(void) +{ +	return target_3(); +} + +extern const void *__start_uprobe__target_2; +extern const void *__stop_uprobe__target_2; + +__attribute__((section("uprobe__target_1"))) +__weak int target_1(int depth) +{ +	if (depth < 1) +		return 1 + target_1(depth + 1); +	else +		return target_2(); +} + +extern const void *__start_uprobe__target_1; +extern const void *__stop_uprobe__target_1; + +extern const void *__start_uretprobe_stack_sec; +extern const void *__stop_uretprobe_stack_sec; + +struct range { +	long start; +	long stop; +}; + +static struct range targets[] = { +	{}, /* we want target_1 to map to target[1], so need 1-based indexing */ +	{ (long)&__start_uprobe__target_1, (long)&__stop_uprobe__target_1 }, +	{ (long)&__start_uprobe__target_2, (long)&__stop_uprobe__target_2 }, +	{ (long)&__start_uprobe__target_3, (long)&__stop_uprobe__target_3 }, +	{ (long)&__start_uprobe__target_4, (long)&__stop_uprobe__target_4 }, +}; + +static struct range caller = { +	(long)&__start_uretprobe_stack_sec, +	(long)&__stop_uretprobe_stack_sec, +}; + +static void validate_stack(__u64 *ips, int stack_len, int cnt, ...) +{ +	int i, j; +	va_list args; + +	if (!ASSERT_GT(stack_len, 0, "stack_len")) +		return; + +	stack_len /= 8; + +	/* check if we have enough entries to satisfy test expectations */ +	if (!ASSERT_GE(stack_len, cnt, "stack_len2")) +		return; + +	if (env.verbosity >= VERBOSE_NORMAL) { +		printf("caller: %#lx - %#lx\n", caller.start, caller.stop); +		for (i = 1; i < ARRAY_SIZE(targets); i++) +			printf("target_%d: %#lx - %#lx\n", i, targets[i].start, targets[i].stop); +		for (i = 0; i < stack_len; i++) { +			for (j = 1; j < ARRAY_SIZE(targets); j++) { +				if (ips[i] >= targets[j].start && ips[i] < targets[j].stop) +					break; +			} +			if (j < ARRAY_SIZE(targets)) { /* found target match */ +				printf("ENTRY #%d: %#lx (in target_%d)\n", i, (long)ips[i], j); +			} else if (ips[i] >= caller.start && ips[i] < caller.stop) { +				printf("ENTRY #%d: %#lx (in caller)\n", i, (long)ips[i]); +			} else { +				printf("ENTRY #%d: %#lx\n", i, (long)ips[i]); +			} +		} +	} + +	va_start(args, cnt); + +	for (i = cnt - 1; i >= 0; i--) { +		/* most recent entry is the deepest target function */ +		const struct range *t = va_arg(args, const struct range *); + +		ASSERT_GE(ips[i], t->start, "addr_start"); +		ASSERT_LT(ips[i], t->stop, "addr_stop"); +	} + +	va_end(args); +} + +/* __weak prevents inlining */ +__attribute__((section("uretprobe_stack_sec"))) +__weak void test_uretprobe_stack(void) +{ +	LIBBPF_OPTS(bpf_uprobe_opts, uprobe_opts); +	struct uretprobe_stack *skel; +	int err; + +	skel = uretprobe_stack__open_and_load(); +	if (!ASSERT_OK_PTR(skel, "skel_open")) +		return; + +	err = uretprobe_stack__attach(skel); +	if (!ASSERT_OK(err, "skel_attach")) +		goto cleanup; + +	/* trigger */ +	ASSERT_EQ(target_1(0), 42 + 1, "trigger_return"); + +	/* +	 * Stacks captured on ENTRY uprobes +	 */ + +	/* (uprobe 1) target_1 in stack trace*/ +	validate_stack(skel->bss->entry_stack1, skel->bss->entry1_len, +		       2, &caller, &targets[1]); +	/* (uprobe 1, recursed) */ +	validate_stack(skel->bss->entry_stack1_recur, skel->bss->entry1_recur_len, +		       3, &caller, &targets[1], &targets[1]); +	/* (uprobe 2) caller -> target_1 -> target_1 -> target_2 */ +	validate_stack(skel->bss->entry_stack2, skel->bss->entry2_len, +		       4, &caller, &targets[1], &targets[1], &targets[2]); +	/* (uprobe 3) */ +	validate_stack(skel->bss->entry_stack3, skel->bss->entry3_len, +		       5, &caller, &targets[1], &targets[1], &targets[2], &targets[3]); +	/* (uprobe 4) caller -> target_1 -> target_1 -> target_2 -> target_3 -> target_4 */ +	validate_stack(skel->bss->entry_stack4, skel->bss->entry4_len, +		       6, &caller, &targets[1], &targets[1], &targets[2], &targets[3], &targets[4]); + +	/* (USDT): full caller -> target_1 -> target_1 -> target_2 (uretprobed) +	 *              -> target_3 -> target_4 (uretprobes) chain +	 */ +	validate_stack(skel->bss->usdt_stack, skel->bss->usdt_len, +		       6, &caller, &targets[1], &targets[1], &targets[2], &targets[3], &targets[4]); + +	/* +	 * Now stacks captured on the way out in EXIT uprobes +	 */ + +	/* (uretprobe 4) everything up to target_4, but excluding it */ +	validate_stack(skel->bss->exit_stack4, skel->bss->exit4_len, +		       5, &caller, &targets[1], &targets[1], &targets[2], &targets[3]); +	/* we didn't install uretprobes on target_2 and target_3 */ +	/* (uretprobe 1, recur) first target_1 call only */ +	validate_stack(skel->bss->exit_stack1_recur, skel->bss->exit1_recur_len, +		       2, &caller, &targets[1]); +	/* (uretprobe 1) just a caller in the stack trace */ +	validate_stack(skel->bss->exit_stack1, skel->bss->exit1_len, +		       1, &caller); + +cleanup: +	uretprobe_stack__destroy(skel); +} diff --git a/tools/testing/selftests/bpf/prog_tests/verifier.c b/tools/testing/selftests/bpf/prog_tests/verifier.c index 98ef39efa77e..9dc3687bc406 100644 --- a/tools/testing/selftests/bpf/prog_tests/verifier.c +++ b/tools/testing/selftests/bpf/prog_tests/verifier.c @@ -87,6 +87,7 @@  #include "verifier_xadd.skel.h"  #include "verifier_xdp.skel.h"  #include "verifier_xdp_direct_packet_access.skel.h" +#include "verifier_bits_iter.skel.h"  #define MAX_ENTRIES 11 @@ -204,6 +205,7 @@ void test_verifier_var_off(void)              { RUN(verifier_var_off); }  void test_verifier_xadd(void)                 { RUN(verifier_xadd); }  void test_verifier_xdp(void)                  { RUN(verifier_xdp); }  void test_verifier_xdp_direct_packet_access(void) { RUN(verifier_xdp_direct_packet_access); } +void test_verifier_bits_iter(void) { RUN(verifier_bits_iter); }  static int init_test_val_map(struct bpf_object *obj, char *map_name)  { diff --git a/tools/testing/selftests/bpf/prog_tests/xdp_adjust_tail.c b/tools/testing/selftests/bpf/prog_tests/xdp_adjust_tail.c index f09505f8b038..53d6ad8c2257 100644 --- a/tools/testing/selftests/bpf/prog_tests/xdp_adjust_tail.c +++ b/tools/testing/selftests/bpf/prog_tests/xdp_adjust_tail.c @@ -222,7 +222,7 @@ static void test_xdp_adjust_frags_tail_grow(void)  	prog = bpf_object__next_program(obj, NULL);  	if (bpf_object__load(obj)) -		return; +		goto out;  	prog_fd = bpf_program__fd(prog); diff --git a/tools/testing/selftests/bpf/prog_tests/xdp_flowtable.c b/tools/testing/selftests/bpf/prog_tests/xdp_flowtable.c new file mode 100644 index 000000000000..e1bf141d3401 --- /dev/null +++ b/tools/testing/selftests/bpf/prog_tests/xdp_flowtable.c @@ -0,0 +1,168 @@ +// SPDX-License-Identifier: GPL-2.0 +#include <test_progs.h> +#include <network_helpers.h> +#include <bpf/btf.h> +#include <linux/if_link.h> +#include <linux/udp.h> +#include <net/if.h> +#include <unistd.h> + +#include "xdp_flowtable.skel.h" + +#define TX_NETNS_NAME	"ns0" +#define RX_NETNS_NAME	"ns1" + +#define TX_NAME		"v0" +#define FORWARD_NAME	"v1" +#define RX_NAME		"d0" + +#define TX_MAC		"00:00:00:00:00:01" +#define FORWARD_MAC	"00:00:00:00:00:02" +#define RX_MAC		"00:00:00:00:00:03" +#define DST_MAC		"00:00:00:00:00:04" + +#define TX_ADDR		"10.0.0.1" +#define FORWARD_ADDR	"10.0.0.2" +#define RX_ADDR		"20.0.0.1" +#define DST_ADDR	"20.0.0.2" + +#define PREFIX_LEN	"8" +#define N_PACKETS	10 +#define UDP_PORT	12345 +#define UDP_PORT_STR	"12345" + +static int send_udp_traffic(void) +{ +	struct sockaddr_storage addr; +	int i, sock; + +	if (make_sockaddr(AF_INET, DST_ADDR, UDP_PORT, &addr, NULL)) +		return -EINVAL; + +	sock = socket(AF_INET, SOCK_DGRAM, 0); +	if (sock < 0) +		return sock; + +	for (i = 0; i < N_PACKETS; i++) { +		unsigned char buf[] = { 0xaa, 0xbb, 0xcc }; +		int n; + +		n = sendto(sock, buf, sizeof(buf), MSG_NOSIGNAL | MSG_CONFIRM, +			   (struct sockaddr *)&addr, sizeof(addr)); +		if (n != sizeof(buf)) { +			close(sock); +			return -EINVAL; +		} + +		usleep(50000); /* 50ms */ +	} +	close(sock); + +	return 0; +} + +void test_xdp_flowtable(void) +{ +	struct xdp_flowtable *skel = NULL; +	struct nstoken *tok = NULL; +	int iifindex, stats_fd; +	__u32 value, key = 0; +	struct bpf_link *link; + +	if (SYS_NOFAIL("nft -v")) { +		fprintf(stdout, "Missing required nft tool\n"); +		test__skip(); +		return; +	} + +	SYS(out, "ip netns add " TX_NETNS_NAME); +	SYS(out, "ip netns add " RX_NETNS_NAME); + +	tok = open_netns(RX_NETNS_NAME); +	if (!ASSERT_OK_PTR(tok, "setns")) +		goto out; + +	SYS(out, "sysctl -qw net.ipv4.conf.all.forwarding=1"); + +	SYS(out, "ip link add " TX_NAME " type veth peer " FORWARD_NAME); +	SYS(out, "ip link set " TX_NAME " netns " TX_NETNS_NAME); +	SYS(out, "ip link set dev " FORWARD_NAME " address " FORWARD_MAC); +	SYS(out, +	    "ip addr add " FORWARD_ADDR "/" PREFIX_LEN " dev " FORWARD_NAME); +	SYS(out, "ip link set dev " FORWARD_NAME " up"); + +	SYS(out, "ip link add " RX_NAME " type dummy"); +	SYS(out, "ip link set dev " RX_NAME " address " RX_MAC); +	SYS(out, "ip addr add " RX_ADDR "/" PREFIX_LEN " dev " RX_NAME); +	SYS(out, "ip link set dev " RX_NAME " up"); + +	/* configure the flowtable */ +	SYS(out, "nft add table ip filter"); +	SYS(out, +	    "nft add flowtable ip filter f { hook ingress priority 0\\; " +	    "devices = { " FORWARD_NAME ", " RX_NAME " }\\; }"); +	SYS(out, +	    "nft add chain ip filter forward " +	    "{ type filter hook forward priority 0\\; }"); +	SYS(out, +	    "nft add rule ip filter forward ip protocol udp th dport " +	    UDP_PORT_STR " flow add @f"); + +	/* Avoid ARP calls */ +	SYS(out, +	    "ip -4 neigh add " DST_ADDR " lladdr " DST_MAC " dev " RX_NAME); + +	close_netns(tok); +	tok = open_netns(TX_NETNS_NAME); +	if (!ASSERT_OK_PTR(tok, "setns")) +		goto out; + +	SYS(out, "ip addr add " TX_ADDR "/" PREFIX_LEN " dev " TX_NAME); +	SYS(out, "ip link set dev " TX_NAME " address " TX_MAC); +	SYS(out, "ip link set dev " TX_NAME " up"); +	SYS(out, "ip route add default via " FORWARD_ADDR); + +	close_netns(tok); +	tok = open_netns(RX_NETNS_NAME); +	if (!ASSERT_OK_PTR(tok, "setns")) +		goto out; + +	iifindex = if_nametoindex(FORWARD_NAME); +	if (!ASSERT_NEQ(iifindex, 0, "iifindex")) +		goto out; + +	skel = xdp_flowtable__open_and_load(); +	if (!ASSERT_OK_PTR(skel, "skel")) +		goto out; + +	link = bpf_program__attach_xdp(skel->progs.xdp_flowtable_do_lookup, +				       iifindex); +	if (!ASSERT_OK_PTR(link, "prog_attach")) +		goto out; + +	close_netns(tok); +	tok = open_netns(TX_NETNS_NAME); +	if (!ASSERT_OK_PTR(tok, "setns")) +		goto out; + +	if (!ASSERT_OK(send_udp_traffic(), "send udp")) +		goto out; + +	close_netns(tok); +	tok = open_netns(RX_NETNS_NAME); +	if (!ASSERT_OK_PTR(tok, "setns")) +		goto out; + +	stats_fd = bpf_map__fd(skel->maps.stats); +	if (!ASSERT_OK(bpf_map_lookup_elem(stats_fd, &key, &value), +		       "bpf_map_update_elem stats")) +		goto out; + +	ASSERT_GE(value, N_PACKETS - 2, "bpf_xdp_flow_lookup failed"); +out: +	xdp_flowtable__destroy(skel); +	if (tok) +		close_netns(tok); +	SYS_NOFAIL("ip netns del " TX_NETNS_NAME); +	SYS_NOFAIL("ip netns del " RX_NETNS_NAME); +} diff --git a/tools/testing/selftests/bpf/prog_tests/xdp_metadata.c b/tools/testing/selftests/bpf/prog_tests/xdp_metadata.c index f76b5d67a3ee..c87ee2bf558c 100644 --- a/tools/testing/selftests/bpf/prog_tests/xdp_metadata.c +++ b/tools/testing/selftests/bpf/prog_tests/xdp_metadata.c @@ -68,7 +68,8 @@ static int open_xsk(int ifindex, struct xsk *xsk)  		.fill_size = XSK_RING_PROD__DEFAULT_NUM_DESCS,  		.comp_size = XSK_RING_CONS__DEFAULT_NUM_DESCS,  		.frame_size = XSK_UMEM__DEFAULT_FRAME_SIZE, -		.flags = XDP_UMEM_UNALIGNED_CHUNK_FLAG | XDP_UMEM_TX_SW_CSUM, +		.flags = XDP_UMEM_UNALIGNED_CHUNK_FLAG | XDP_UMEM_TX_SW_CSUM | +			 XDP_UMEM_TX_METADATA_LEN,  		.tx_metadata_len = sizeof(struct xsk_tx_metadata),  	};  	__u32 idx; |