Hi all,
This patch series continues the work to migrate the *.sh tests into prog_tests framework.
test_xdp_redirect_multi.sh tests the XDP redirections done through bpf_redirect_map().
This is already partly covered by test_xdp_veth.c that already tests map redirections at XDP level. What isn't covered yet by test_xdp_veth is the use of the broadcast flags (BPF_F_BROADCAST or BPF_F_EXCLUDE_INGRESS) and XDP egress programs.
Hence, this patch series add test cases to test_xdp_veth.c to get rid of the test_xdp_redirect_multi.sh: - PATCH 1 to 5 rework test_xdp_veth to make it more generic and allow to configure different test cases - PATCH 6 adds test cases for 'classic' bpf_redirect_map() - PATCH 7 & 8 covers the broadcast flags - PATCH 9 covers the XDP egress programs - PATCH 10 removes test_xdp_redirect_multi.sh
Signed-off-by: Bastien Curutchet (eBPF Foundation) bastien.curutchet@bootlin.com --- Bastien Curutchet (eBPF Foundation) (10): selftests/bpf: test_xdp_veth: Split network configuration selftests/bpf: Remove unused argument selftests/bpf: test_xdp_veth: Rename config[] selftests/bpf: test_xdp_veth: Add prog_config[] table selftests/bpf: test_xdp_veth: Add XDP flags to prog_configuration selftests/bpf: test_xdp_veth: Add new test cases for XDP flags selftests/bpf: Optionally select broadcasting flags selftests/bpf: test_xdp_veth: Add XDP broadcast redirection tests selftests/bpf: test_xdp_veth: Add XDP program on egress test selftests/bpf: Remove test_xdp_redirect_multi.sh
tools/testing/selftests/bpf/Makefile | 2 - .../selftests/bpf/prog_tests/test_xdp_veth.c | 534 +++++++++++++++++---- .../testing/selftests/bpf/progs/xdp_redirect_map.c | 89 ++++ .../selftests/bpf/progs/xdp_redirect_multi_kern.c | 41 +- .../selftests/bpf/test_xdp_redirect_multi.sh | 214 --------- tools/testing/selftests/bpf/xdp_redirect_multi.c | 226 --------- 6 files changed, 553 insertions(+), 553 deletions(-) --- base-commit: 075d2f15ddfc67f27601c8f861dd5d3b189af401 change-id: 20250103-redirect-multi-245d6eafb5d1
Best regards,
configure_network() does two things : it first creates the network topology and then configures the BPF maps to fit the test needs. This isn't convenient if we want to re-use the same network topology for different test cases.
Rename configure_network() create_network(). Move the BPF configuration to the test itself. Split the test description in two parts, first the description of the network topology, then the description of the test case. Remove the veth indexes from the ASCII art as dynamic ones are used
Signed-off-by: Bastien Curutchet (eBPF Foundation) bastien.curutchet@bootlin.com --- .../selftests/bpf/prog_tests/test_xdp_veth.c | 78 +++++++++++++--------- 1 file changed, 46 insertions(+), 32 deletions(-)
diff --git a/tools/testing/selftests/bpf/prog_tests/test_xdp_veth.c b/tools/testing/selftests/bpf/prog_tests/test_xdp_veth.c index 8d75424fe6bc8b2d4eeabe3ec49b883284c834e9..8dc28274a6e8fc75b05781d827a04f01e03a6ebb 100644 --- a/tools/testing/selftests/bpf/prog_tests/test_xdp_veth.c +++ b/tools/testing/selftests/bpf/prog_tests/test_xdp_veth.c @@ -3,17 +3,27 @@ /* Create 3 namespaces with 3 veth peers, and forward packets in-between using * native XDP * - * XDP_TX - * NS1(veth11) NS2(veth22) NS3(veth33) - * | | | - * | | | - * (veth1, (veth2, (veth3, - * id:111) id:122) id:133) - * ^ | ^ | ^ | - * | | XDP_REDIRECT | | XDP_REDIRECT | | - * | ------------------ ------------------ | - * ----------------------------------------- - * XDP_REDIRECT + * Network topology: + * ---------- ---------- ---------- + * | NS1 | | NS2 | | NS3 | + * | veth11 | | veth22 | | veth33 | + * ----|----- -----|---- -----|---- + * | | | + * veth1 veth2 veth3 + * + * Test cases: + * - [test_xdp_veth_redirect] : ping veth33 from veth11 + * + * veth11 veth22 veth33 + * (XDP_PASS) (XDP_TX) (XDP_PASS) + * | | | + * | | | + * veth1 veth2 veth3 + * (XDP_REDIRECT) (XDP_REDIRECT) (XDP_REDIRECT) + * ^ | ^ | ^ | + * | | | | | | + * | ------------------ ------------------ | + * ----------------------------------------- */
#define _GNU_SOURCE @@ -121,12 +131,9 @@ static int attach_programs_to_veth_pair(struct skeletons *skeletons, int index) return 0; }
-static int configure_network(struct skeletons *skeletons) +static int create_network(void) { - int interface_id; - int map_fd; - int err; - int i = 0; + int i;
/* First create and configure all interfaces */ for (i = 0; i < VETH_PAIRS_COUNT; i++) { @@ -141,25 +148,11 @@ static int configure_network(struct skeletons *skeletons) config[i].remote_veth); }
- /* Then configure the redirect map and attach programs to interfaces */ - map_fd = bpf_map__fd(skeletons->xdp_redirect_maps->maps.tx_port); - if (!ASSERT_GE(map_fd, 0, "open redirect map")) - goto fail; - for (i = 0; i < VETH_PAIRS_COUNT; i++) { - interface_id = if_nametoindex(config[i].next_veth); - if (!ASSERT_NEQ(interface_id, 0, "non zero interface index")) - goto fail; - err = bpf_map_update_elem(map_fd, &i, &interface_id, BPF_ANY); - if (!ASSERT_OK(err, "configure interface redirection through map")) - goto fail; - if (attach_programs_to_veth_pair(skeletons, i)) - goto fail; - } - return 0;
fail: return -1; + }
static void cleanup_network(void) @@ -184,6 +177,8 @@ static int check_ping(struct skeletons *skeletons) void test_xdp_veth_redirect(void) { struct skeletons skeletons = {}; + int map_fd; + int i;
skeletons.xdp_dummy = xdp_dummy__open_and_load(); if (!ASSERT_OK_PTR(skeletons.xdp_dummy, "xdp_dummy__open_and_load")) @@ -197,9 +192,28 @@ void test_xdp_veth_redirect(void) if (!ASSERT_OK_PTR(skeletons.xdp_redirect_maps, "xdp_redirect_map__open_and_load")) goto destroy_xdp_tx;
- if (configure_network(&skeletons)) + if (create_network()) goto destroy_xdp_redirect_map;
+ /* Then configure the redirect map and attach programs to interfaces */ + map_fd = bpf_map__fd(skeletons.xdp_redirect_maps->maps.tx_port); + if (!ASSERT_OK_FD(map_fd, "open redirect map")) + goto destroy_xdp_redirect_map; + + for (i = 0; i < VETH_PAIRS_COUNT; i++) { + int interface_id; + int err; + + interface_id = if_nametoindex(config[i].next_veth); + if (!ASSERT_NEQ(interface_id, 0, "non zero interface index")) + goto destroy_xdp_redirect_map; + err = bpf_map_update_elem(map_fd, &i, &interface_id, BPF_ANY); + if (!ASSERT_OK(err, "configure interface redirection through map")) + goto destroy_xdp_redirect_map; + if (attach_programs_to_veth_pair(&skeletons, i)) + goto destroy_xdp_redirect_map; + } + ASSERT_OK(check_ping(&skeletons), "ping");
destroy_xdp_redirect_map:
check_ping() doesn't use the struct skeletons it has as input.
Remove the unused input.
Signed-off-by: Bastien Curutchet (eBPF Foundation) bastien.curutchet@bootlin.com --- tools/testing/selftests/bpf/prog_tests/test_xdp_veth.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/tools/testing/selftests/bpf/prog_tests/test_xdp_veth.c b/tools/testing/selftests/bpf/prog_tests/test_xdp_veth.c index 8dc28274a6e8fc75b05781d827a04f01e03a6ebb..8507863e61bbea99c906c60ed4535e23d530588c 100644 --- a/tools/testing/selftests/bpf/prog_tests/test_xdp_veth.c +++ b/tools/testing/selftests/bpf/prog_tests/test_xdp_veth.c @@ -165,7 +165,7 @@ static void cleanup_network(void) SYS_NOFAIL("ip netns del %s", config[i].namespace); }
-static int check_ping(struct skeletons *skeletons) +static int check_ping(void) { /* Test: if all interfaces are properly configured, we must be able to ping * veth33 from veth11 @@ -214,7 +214,7 @@ void test_xdp_veth_redirect(void) goto destroy_xdp_redirect_map; }
- ASSERT_OK(check_ping(&skeletons), "ping"); + ASSERT_OK(check_ping(), "ping");
destroy_xdp_redirect_map: xdp_redirect_map__destroy(skeletons.xdp_redirect_maps);
The network topology is held by the config[] table. This 'config' name is a bit too generic if we want to add other configuration variable.
Rename config[] to net_config[].
Signed-off-by: Bastien Curutchet (eBPF Foundation) bastien.curutchet@bootlin.com --- .../selftests/bpf/prog_tests/test_xdp_veth.c | 30 +++++++++++----------- 1 file changed, 15 insertions(+), 15 deletions(-)
diff --git a/tools/testing/selftests/bpf/prog_tests/test_xdp_veth.c b/tools/testing/selftests/bpf/prog_tests/test_xdp_veth.c index 8507863e61bbea99c906c60ed4535e23d530588c..e84ff834736ddd35a787e5b7a7f20a48efba7649 100644 --- a/tools/testing/selftests/bpf/prog_tests/test_xdp_veth.c +++ b/tools/testing/selftests/bpf/prog_tests/test_xdp_veth.c @@ -55,7 +55,7 @@ struct veth_configuration { char *remote_addr; /* IP address of the remote veth */ };
-static struct veth_configuration config[VETH_PAIRS_COUNT] = { +static struct veth_configuration net_config[VETH_PAIRS_COUNT] = { { .local_veth = "veth1", .remote_veth = "veth11", @@ -107,17 +107,17 @@ static int attach_programs_to_veth_pair(struct skeletons *skeletons, int index) remote_link = &skeletons->xdp_dummy->links.xdp_dummy_prog; break; } - interface = if_nametoindex(config[index].local_veth); + interface = if_nametoindex(net_config[index].local_veth); if (!ASSERT_NEQ(interface, 0, "non zero interface index")) return -1; link = bpf_program__attach_xdp(local_prog, interface); if (!ASSERT_OK_PTR(link, "attach xdp program to local veth")) return -1; *local_link = link; - nstoken = open_netns(config[index].namespace); + nstoken = open_netns(net_config[index].namespace); if (!ASSERT_OK_PTR(nstoken, "switch to remote veth namespace")) return -1; - interface = if_nametoindex(config[index].remote_veth); + interface = if_nametoindex(net_config[index].remote_veth); if (!ASSERT_NEQ(interface, 0, "non zero interface index")) { close_netns(nstoken); return -1; @@ -137,15 +137,15 @@ static int create_network(void)
/* First create and configure all interfaces */ for (i = 0; i < VETH_PAIRS_COUNT; i++) { - SYS(fail, "ip netns add %s", config[i].namespace); + SYS(fail, "ip netns add %s", net_config[i].namespace); SYS(fail, "ip link add %s type veth peer name %s netns %s", - config[i].local_veth, config[i].remote_veth, config[i].namespace); - SYS(fail, "ip link set dev %s up", config[i].local_veth); - if (config[i].remote_addr) - SYS(fail, "ip -n %s addr add %s/24 dev %s", config[i].namespace, - config[i].remote_addr, config[i].remote_veth); - SYS(fail, "ip -n %s link set dev %s up", config[i].namespace, - config[i].remote_veth); + net_config[i].local_veth, net_config[i].remote_veth, net_config[i].namespace); + SYS(fail, "ip link set dev %s up", net_config[i].local_veth); + if (net_config[i].remote_addr) + SYS(fail, "ip -n %s addr add %s/24 dev %s", net_config[i].namespace, + net_config[i].remote_addr, net_config[i].remote_veth); + SYS(fail, "ip -n %s link set dev %s up", net_config[i].namespace, + net_config[i].remote_veth); }
return 0; @@ -162,7 +162,7 @@ static void cleanup_network(void) /* Deleting namespaces is enough to automatically remove veth pairs as well */ for (i = 0; i < VETH_PAIRS_COUNT; i++) - SYS_NOFAIL("ip netns del %s", config[i].namespace); + SYS_NOFAIL("ip netns del %s", net_config[i].namespace); }
static int check_ping(void) @@ -171,7 +171,7 @@ static int check_ping(void) * veth33 from veth11 */ return SYS_NOFAIL("ip netns exec %s ping -c 1 -W 1 %s > /dev/null", - config[0].namespace, IP_DST); + net_config[0].namespace, IP_DST); }
void test_xdp_veth_redirect(void) @@ -204,7 +204,7 @@ void test_xdp_veth_redirect(void) int interface_id; int err;
- interface_id = if_nametoindex(config[i].next_veth); + interface_id = if_nametoindex(net_config[i].next_veth); if (!ASSERT_NEQ(interface_id, 0, "non zero interface index")) goto destroy_xdp_redirect_map; err = bpf_map_update_elem(map_fd, &i, &interface_id, BPF_ANY);
The BPF program attached to each veth is hardcoded through the use of the struct skeletons. It prevents from re-using the initialization code in new test cases.
Replace the struct skeletons by a bpf_object table. Add a struct prog_configuration that holds the name of BPF program to load on a given veth pair. Use bpf_object__find_program_by_name() / bpf_xdp_attach() API instead of bpf_program__attach_xdp() to retrieve the BPF programs from their names. Detach BPF progs in the cleanup() as it's not automatically done by this API.
Signed-off-by: Bastien Curutchet (eBPF Foundation) bastien.curutchet@bootlin.com --- .../selftests/bpf/prog_tests/test_xdp_veth.c | 133 +++++++++++++-------- 1 file changed, 80 insertions(+), 53 deletions(-)
diff --git a/tools/testing/selftests/bpf/prog_tests/test_xdp_veth.c b/tools/testing/selftests/bpf/prog_tests/test_xdp_veth.c index e84ff834736ddd35a787e5b7a7f20a48efba7649..b2b4f6958940f7bce66953568d18fb10e12130e5 100644 --- a/tools/testing/selftests/bpf/prog_tests/test_xdp_veth.c +++ b/tools/testing/selftests/bpf/prog_tests/test_xdp_veth.c @@ -40,12 +40,7 @@ #define IP_SRC "10.1.1.11" #define IP_DST "10.1.1.33" #define IP_CMD_MAX_LEN 128 - -struct skeletons { - struct xdp_dummy *xdp_dummy; - struct xdp_tx *xdp_tx; - struct xdp_redirect_map *xdp_redirect_maps; -}; +#define PROG_NAME_MAX_LEN 128
struct veth_configuration { char local_veth[VETH_NAME_MAX_LEN]; /* Interface in main namespace */ @@ -79,55 +74,59 @@ static struct veth_configuration net_config[VETH_PAIRS_COUNT] = { } };
-static int attach_programs_to_veth_pair(struct skeletons *skeletons, int index) +struct prog_configuration { + char local_name[PROG_NAME_MAX_LEN]; /* BPF prog to attach to local_veth */ + char remote_name[PROG_NAME_MAX_LEN]; /* BPF prog to attach to remote_veth */ +}; + +static int attach_programs_to_veth_pair(struct bpf_object **objs, size_t nb_obj, + struct prog_configuration *prog, int index) { struct bpf_program *local_prog, *remote_prog; - struct bpf_link **local_link, **remote_link; struct nstoken *nstoken; - struct bpf_link *link; - int interface; - - switch (index) { - case 0: - local_prog = skeletons->xdp_redirect_maps->progs.xdp_redirect_map_0; - local_link = &skeletons->xdp_redirect_maps->links.xdp_redirect_map_0; - remote_prog = skeletons->xdp_dummy->progs.xdp_dummy_prog; - remote_link = &skeletons->xdp_dummy->links.xdp_dummy_prog; - break; - case 1: - local_prog = skeletons->xdp_redirect_maps->progs.xdp_redirect_map_1; - local_link = &skeletons->xdp_redirect_maps->links.xdp_redirect_map_1; - remote_prog = skeletons->xdp_tx->progs.xdp_tx; - remote_link = &skeletons->xdp_tx->links.xdp_tx; - break; - case 2: - local_prog = skeletons->xdp_redirect_maps->progs.xdp_redirect_map_2; - local_link = &skeletons->xdp_redirect_maps->links.xdp_redirect_map_2; - remote_prog = skeletons->xdp_dummy->progs.xdp_dummy_prog; - remote_link = &skeletons->xdp_dummy->links.xdp_dummy_prog; - break; + int interface, ret, i; + + for (i = 0; i < nb_obj; i++) { + local_prog = bpf_object__find_program_by_name(objs[i], prog[index].local_name); + if (local_prog) + break; } + if (!ASSERT_OK_PTR(local_prog, "find local program")) + return -1; + + for (i = 0; i < nb_obj; i++) { + remote_prog = bpf_object__find_program_by_name(objs[i], prog[index].remote_name); + if (remote_prog) + break; + } + if (!ASSERT_OK_PTR(remote_prog, "find remote program")) + return -1; + interface = if_nametoindex(net_config[index].local_veth); if (!ASSERT_NEQ(interface, 0, "non zero interface index")) return -1; - link = bpf_program__attach_xdp(local_prog, interface); - if (!ASSERT_OK_PTR(link, "attach xdp program to local veth")) + + ret = bpf_xdp_attach(interface, bpf_program__fd(local_prog), 0, NULL); + if (!ASSERT_OK(ret, "attach xdp program to local veth")) return -1; - *local_link = link; + nstoken = open_netns(net_config[index].namespace); if (!ASSERT_OK_PTR(nstoken, "switch to remote veth namespace")) return -1; + interface = if_nametoindex(net_config[index].remote_veth); if (!ASSERT_NEQ(interface, 0, "non zero interface index")) { close_netns(nstoken); return -1; } - link = bpf_program__attach_xdp(remote_prog, interface); - *remote_link = link; - close_netns(nstoken); - if (!ASSERT_OK_PTR(link, "attach xdp program to remote veth")) + + ret = bpf_xdp_attach(interface, bpf_program__fd(remote_prog), 0, NULL); + if (!ASSERT_OK(ret, "attach xdp program to remote veth")) { + close_netns(nstoken); return -1; + }
+ close_netns(nstoken); return 0; }
@@ -152,17 +151,24 @@ static int create_network(void)
fail: return -1; - }
static void cleanup_network(void) { + struct nstoken *nstoken; int i;
- /* Deleting namespaces is enough to automatically remove veth pairs as well - */ - for (i = 0; i < VETH_PAIRS_COUNT; i++) + for (i = 0; i < VETH_PAIRS_COUNT; i++) { + bpf_xdp_detach(if_nametoindex(net_config[i].local_veth), 0, NULL); + nstoken = open_netns(net_config[i].namespace); + if (nstoken) { + bpf_xdp_detach(if_nametoindex(net_config[i].remote_veth), 0, NULL); + close_netns(nstoken); + } + /* in case the detach failed */ + SYS_NOFAIL("ip link del %s", net_config[i].local_veth); SYS_NOFAIL("ip netns del %s", net_config[i].namespace); + } }
static int check_ping(void) @@ -174,32 +180,53 @@ static int check_ping(void) net_config[0].namespace, IP_DST); }
+#define VETH_REDIRECT_SKEL_NB 3 void test_xdp_veth_redirect(void) { - struct skeletons skeletons = {}; + struct prog_configuration ping_config[VETH_PAIRS_COUNT] = { + { + .local_name = "xdp_redirect_map_0", + .remote_name = "xdp_dummy_prog", + }, + { + .local_name = "xdp_redirect_map_1", + .remote_name = "xdp_tx", + }, + { + .local_name = "xdp_redirect_map_2", + .remote_name = "xdp_dummy_prog", + } + }; + struct bpf_object *bpf_objs[VETH_REDIRECT_SKEL_NB]; + struct xdp_redirect_map *xdp_redirect_map; + struct xdp_dummy *xdp_dummy; + struct xdp_tx *xdp_tx; int map_fd; int i;
- skeletons.xdp_dummy = xdp_dummy__open_and_load(); - if (!ASSERT_OK_PTR(skeletons.xdp_dummy, "xdp_dummy__open_and_load")) + xdp_dummy = xdp_dummy__open_and_load(); + if (!ASSERT_OK_PTR(xdp_dummy, "xdp_dummy__open_and_load")) return;
- skeletons.xdp_tx = xdp_tx__open_and_load(); - if (!ASSERT_OK_PTR(skeletons.xdp_tx, "xdp_tx__open_and_load")) + xdp_tx = xdp_tx__open_and_load(); + if (!ASSERT_OK_PTR(xdp_tx, "xdp_tx__open_and_load")) goto destroy_xdp_dummy;
- skeletons.xdp_redirect_maps = xdp_redirect_map__open_and_load(); - if (!ASSERT_OK_PTR(skeletons.xdp_redirect_maps, "xdp_redirect_map__open_and_load")) + xdp_redirect_map = xdp_redirect_map__open_and_load(); + if (!ASSERT_OK_PTR(xdp_redirect_map, "xdp_redirect_map__open_and_load")) goto destroy_xdp_tx;
if (create_network()) goto destroy_xdp_redirect_map;
/* Then configure the redirect map and attach programs to interfaces */ - map_fd = bpf_map__fd(skeletons.xdp_redirect_maps->maps.tx_port); + map_fd = bpf_map__fd(xdp_redirect_map->maps.tx_port); if (!ASSERT_OK_FD(map_fd, "open redirect map")) goto destroy_xdp_redirect_map;
+ bpf_objs[0] = xdp_dummy->obj; + bpf_objs[1] = xdp_tx->obj; + bpf_objs[2] = xdp_redirect_map->obj; for (i = 0; i < VETH_PAIRS_COUNT; i++) { int interface_id; int err; @@ -210,18 +237,18 @@ void test_xdp_veth_redirect(void) err = bpf_map_update_elem(map_fd, &i, &interface_id, BPF_ANY); if (!ASSERT_OK(err, "configure interface redirection through map")) goto destroy_xdp_redirect_map; - if (attach_programs_to_veth_pair(&skeletons, i)) + if (attach_programs_to_veth_pair(bpf_objs, VETH_REDIRECT_SKEL_NB, ping_config, i)) goto destroy_xdp_redirect_map; }
ASSERT_OK(check_ping(), "ping");
destroy_xdp_redirect_map: - xdp_redirect_map__destroy(skeletons.xdp_redirect_maps); + xdp_redirect_map__destroy(xdp_redirect_map); destroy_xdp_tx: - xdp_tx__destroy(skeletons.xdp_tx); + xdp_tx__destroy(xdp_tx); destroy_xdp_dummy: - xdp_dummy__destroy(skeletons.xdp_dummy); + xdp_dummy__destroy(xdp_dummy);
cleanup_network(); }
XDP flags are hardcoded to 0 at attachment.
Add flags attributes to the struct prog_configuration to allow flag modifications for each test case.
Signed-off-by: Bastien Curutchet (eBPF Foundation) bastien.curutchet@bootlin.com --- tools/testing/selftests/bpf/prog_tests/test_xdp_veth.c | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-)
diff --git a/tools/testing/selftests/bpf/prog_tests/test_xdp_veth.c b/tools/testing/selftests/bpf/prog_tests/test_xdp_veth.c index b2b4f6958940f7bce66953568d18fb10e12130e5..0aa1583b741b15f573bbb979b2047a0109070544 100644 --- a/tools/testing/selftests/bpf/prog_tests/test_xdp_veth.c +++ b/tools/testing/selftests/bpf/prog_tests/test_xdp_veth.c @@ -77,6 +77,8 @@ static struct veth_configuration net_config[VETH_PAIRS_COUNT] = { struct prog_configuration { char local_name[PROG_NAME_MAX_LEN]; /* BPF prog to attach to local_veth */ char remote_name[PROG_NAME_MAX_LEN]; /* BPF prog to attach to remote_veth */ + u32 local_flags; /* XDP flags to use on local_veth */ + u32 remote_flags; /* XDP flags to use on remote_veth */ };
static int attach_programs_to_veth_pair(struct bpf_object **objs, size_t nb_obj, @@ -106,7 +108,8 @@ static int attach_programs_to_veth_pair(struct bpf_object **objs, size_t nb_obj, if (!ASSERT_NEQ(interface, 0, "non zero interface index")) return -1;
- ret = bpf_xdp_attach(interface, bpf_program__fd(local_prog), 0, NULL); + ret = bpf_xdp_attach(interface, bpf_program__fd(local_prog), + prog[index].local_flags, NULL); if (!ASSERT_OK(ret, "attach xdp program to local veth")) return -1;
@@ -120,7 +123,8 @@ static int attach_programs_to_veth_pair(struct bpf_object **objs, size_t nb_obj, return -1; }
- ret = bpf_xdp_attach(interface, bpf_program__fd(remote_prog), 0, NULL); + ret = bpf_xdp_attach(interface, bpf_program__fd(remote_prog), + prog[index].remote_flags, NULL); if (!ASSERT_OK(ret, "attach xdp program to remote veth")) { close_netns(nstoken); return -1; @@ -187,14 +191,20 @@ void test_xdp_veth_redirect(void) { .local_name = "xdp_redirect_map_0", .remote_name = "xdp_dummy_prog", + .local_flags = 0, + .remote_flags = 0, }, { .local_name = "xdp_redirect_map_1", .remote_name = "xdp_tx", + .local_flags = 0, + .remote_flags = 0, }, { .local_name = "xdp_redirect_map_2", .remote_name = "xdp_dummy_prog", + .local_flags = 0, + .remote_flags = 0, } }; struct bpf_object *bpf_objs[VETH_REDIRECT_SKEL_NB];
The XDP redirection is tested without any flag provided to the xdp_attach() function.
Add two subtests that check the correct behaviour with XDP_FLAGS_{DRV/SKB}_MODE flags
Signed-off-by: Bastien Curutchet (eBPF Foundation) bastien.curutchet@bootlin.com --- .../selftests/bpf/prog_tests/test_xdp_veth.c | 27 ++++++++++++++++------ 1 file changed, 20 insertions(+), 7 deletions(-)
diff --git a/tools/testing/selftests/bpf/prog_tests/test_xdp_veth.c b/tools/testing/selftests/bpf/prog_tests/test_xdp_veth.c index 0aa1583b741b15f573bbb979b2047a0109070544..62a315d01e51739a7f13dddda3d5a569eab24ceb 100644 --- a/tools/testing/selftests/bpf/prog_tests/test_xdp_veth.c +++ b/tools/testing/selftests/bpf/prog_tests/test_xdp_veth.c @@ -33,6 +33,7 @@ #include "xdp_dummy.skel.h" #include "xdp_redirect_map.skel.h" #include "xdp_tx.skel.h" +#include <uapi/linux/if_link.h>
#define VETH_PAIRS_COUNT 3 #define NS_SUFFIX_LEN 6 @@ -185,26 +186,26 @@ static int check_ping(void) }
#define VETH_REDIRECT_SKEL_NB 3 -void test_xdp_veth_redirect(void) +static void xdp_veth_redirect(u32 flags) { struct prog_configuration ping_config[VETH_PAIRS_COUNT] = { { .local_name = "xdp_redirect_map_0", .remote_name = "xdp_dummy_prog", - .local_flags = 0, - .remote_flags = 0, + .local_flags = flags, + .remote_flags = flags, }, { .local_name = "xdp_redirect_map_1", .remote_name = "xdp_tx", - .local_flags = 0, - .remote_flags = 0, + .local_flags = flags, + .remote_flags = flags, }, { .local_name = "xdp_redirect_map_2", .remote_name = "xdp_dummy_prog", - .local_flags = 0, - .remote_flags = 0, + .local_flags = flags, + .remote_flags = flags, } }; struct bpf_object *bpf_objs[VETH_REDIRECT_SKEL_NB]; @@ -262,3 +263,15 @@ void test_xdp_veth_redirect(void)
cleanup_network(); } + +void test_xdp_veth_redirect(void) +{ + if (test__start_subtest("0")) + xdp_veth_redirect(0); + + if (test__start_subtest("DRV_MODE")) + xdp_veth_redirect(XDP_FLAGS_DRV_MODE); + + if (test__start_subtest("SKB_MODE")) + xdp_veth_redirect(XDP_FLAGS_SKB_MODE); +}
Broadcasting flags are hardcoded for each kind for protocol.
Create a redirect_flags map that allows to select the broadcasting flags to use in the bpf_redirect_map(). The protocol ID is used as a key. Set the old hardcoded values as default if the map isn't filled by the BPF caller.
Signed-off-by: Bastien Curutchet (eBPF Foundation) bastien.curutchet@bootlin.com --- .../selftests/bpf/progs/xdp_redirect_multi_kern.c | 41 +++++++++++++++------- 1 file changed, 29 insertions(+), 12 deletions(-)
diff --git a/tools/testing/selftests/bpf/progs/xdp_redirect_multi_kern.c b/tools/testing/selftests/bpf/progs/xdp_redirect_multi_kern.c index 97b26a30b59a758f5a5f2152af509acef80031ce..bc2945ed8a8017021c2792671b4de9aa4928a3e4 100644 --- a/tools/testing/selftests/bpf/progs/xdp_redirect_multi_kern.c +++ b/tools/testing/selftests/bpf/progs/xdp_redirect_multi_kern.c @@ -34,6 +34,14 @@ struct { __uint(max_entries, 128); } mac_map SEC(".maps");
+/* map to store redirect flags for each protocol*/ +struct { + __uint(type, BPF_MAP_TYPE_HASH); + __type(key, __u16); + __type(value, __u64); + __uint(max_entries, 16); +} redirect_flags SEC(".maps"); + SEC("xdp") int xdp_redirect_map_multi_prog(struct xdp_md *ctx) { @@ -41,25 +49,34 @@ int xdp_redirect_map_multi_prog(struct xdp_md *ctx) void *data = (void *)(long)ctx->data; int if_index = ctx->ingress_ifindex; struct ethhdr *eth = data; + __u64 *flags_from_map; __u16 h_proto; __u64 nh_off; + __u64 flags;
nh_off = sizeof(*eth); if (data + nh_off > data_end) return XDP_DROP;
- h_proto = eth->h_proto; - - /* Using IPv4 for (BPF_F_BROADCAST | BPF_F_EXCLUDE_INGRESS) testing */ - if (h_proto == bpf_htons(ETH_P_IP)) - return bpf_redirect_map(&map_all, 0, - BPF_F_BROADCAST | BPF_F_EXCLUDE_INGRESS); - /* Using IPv6 for none flag testing */ - else if (h_proto == bpf_htons(ETH_P_IPV6)) - return bpf_redirect_map(&map_all, if_index, 0); - /* All others for BPF_F_BROADCAST testing */ - else - return bpf_redirect_map(&map_all, 0, BPF_F_BROADCAST); + h_proto = bpf_htons(eth->h_proto); + + flags_from_map = bpf_map_lookup_elem(&redirect_flags, &h_proto); + + /* Default flags for IPv4 : (BPF_F_BROADCAST | BPF_F_EXCLUDE_INGRESS) */ + if (h_proto == ETH_P_IP) { + flags = flags_from_map ? *flags_from_map : BPF_F_BROADCAST | BPF_F_EXCLUDE_INGRESS; + return bpf_redirect_map(&map_all, 0, flags); + } + /* Default flags for IPv6 : 0 */ + if (h_proto == ETH_P_IPV6) { + flags = flags_from_map ? *flags_from_map : 0; + return bpf_redirect_map(&map_all, if_index, flags); + } + /* Default flags for others BPF_F_BROADCAST : 0 */ + else { + flags = flags_from_map ? *flags_from_map : BPF_F_BROADCAST; + return bpf_redirect_map(&map_all, 0, flags); + } }
/* The following 2 progs are for 2nd devmap prog testing */
XDP redirections with BPF_F_BROADCAST and BPF_F_EXCLUDE_INGRESS flags are tested by test_xdp_redirect_multi.sh but not within the test_progs framework.
Add a broadcast test case in test_xdp_veth.c to test them. Use the same BPF programs than the one used by test_xdp_redirect_multi.sh. Use a BPF map to select the broadcast flags. Use a BPF map with an entry per veth to check whether packets are received or not
Signed-off-by: Bastien Curutchet (eBPF Foundation) bastien.curutchet@bootlin.com --- .../selftests/bpf/prog_tests/test_xdp_veth.c | 149 +++++++++++++++++++++ .../testing/selftests/bpf/progs/xdp_redirect_map.c | 49 +++++++ 2 files changed, 198 insertions(+)
diff --git a/tools/testing/selftests/bpf/prog_tests/test_xdp_veth.c b/tools/testing/selftests/bpf/prog_tests/test_xdp_veth.c index 62a315d01e51739a7f13dddda3d5a569eab24ceb..9caa5755f0a2471e091bd8d5a701087a64565902 100644 --- a/tools/testing/selftests/bpf/prog_tests/test_xdp_veth.c +++ b/tools/testing/selftests/bpf/prog_tests/test_xdp_veth.c @@ -24,6 +24,23 @@ * | | | | | | * | ------------------ ------------------ | * ----------------------------------------- + * + * - [test_xdp_veth_broadcast_redirect]: broadcast from veth11 + * - IPv4 ping : BPF_F_BROADCAST | BPF_F_EXCLUDE_INGRESS + * -> echo request received by all except veth11 + * - IPv4 ping : BPF_F_BROADCAST + * -> echo request received by all veth + * + * veth11 veth22 veth33 + * (XDP_PASS) (XDP_PASS) (XDP_PASS) + * | | | + * | | | + * veth1 veth2 veth3 + * (XDP_REDIRECT) (XDP_REDIRECT) (XDP_REDIRECT) + * ^ ^ ^ + * | | | + * ---------------------------------------- + * */
#define _GNU_SOURCE @@ -32,6 +49,7 @@ #include "network_helpers.h" #include "xdp_dummy.skel.h" #include "xdp_redirect_map.skel.h" +#include "xdp_redirect_multi_kern.skel.h" #include "xdp_tx.skel.h" #include <uapi/linux/if_link.h>
@@ -40,6 +58,7 @@ #define VETH_NAME_MAX_LEN 16 #define IP_SRC "10.1.1.11" #define IP_DST "10.1.1.33" +#define IP_NEIGH "10.1.1.253" #define IP_CMD_MAX_LEN 128 #define PROG_NAME_MAX_LEN 128
@@ -275,3 +294,133 @@ void test_xdp_veth_redirect(void) if (test__start_subtest("SKB_MODE")) xdp_veth_redirect(XDP_FLAGS_SKB_MODE); } + +#define BROADCAST_REDIRECT_SKEL_NB 2 +static void xdp_veth_broadcast_redirect(u32 attach_flags, u64 redirect_flags) +{ + struct prog_configuration prog_cfg[VETH_PAIRS_COUNT] = { + { + .local_name = "xdp_redirect_map_multi_prog", + .remote_name = "xdp_count_0", + .local_flags = attach_flags, + .remote_flags = attach_flags, + }, + { + .local_name = "xdp_redirect_map_multi_prog", + .remote_name = "xdp_count_1", + .local_flags = attach_flags, + .remote_flags = attach_flags, + }, + { + .local_name = "xdp_redirect_map_multi_prog", + .remote_name = "xdp_count_2", + .local_flags = attach_flags, + .remote_flags = attach_flags, + } + }; + struct bpf_object *bpf_objs[BROADCAST_REDIRECT_SKEL_NB]; + struct xdp_redirect_multi_kern *xdp_redirect_multi_kern; + struct xdp_redirect_map *xdp_redirect_map; + struct bpf_devmap_val devmap_val = {}; + u16 protocol = ETH_P_IP; + int group_map; + int flags_map; + int cnt_map; + u64 cnt = 0; + int i, err; + + xdp_redirect_multi_kern = xdp_redirect_multi_kern__open_and_load(); + if (!ASSERT_OK_PTR(xdp_redirect_multi_kern, "xdp_redirect_multi_kern__open_and_load")) + return; + + xdp_redirect_map = xdp_redirect_map__open_and_load(); + if (!ASSERT_OK_PTR(xdp_redirect_map, "xdp_redirect_map__open_and_load")) + goto destroy_xdp_redirect_multi_kern; + + if (create_network()) + goto destroy_xdp_redirect_map; + + group_map = bpf_map__fd(xdp_redirect_multi_kern->maps.map_all); + if (!ASSERT_OK_FD(group_map, "open map_all")) + goto destroy_xdp_redirect_map; + + flags_map = bpf_map__fd(xdp_redirect_multi_kern->maps.redirect_flags); + if (!ASSERT_OK_FD(group_map, "open map_all")) + goto destroy_xdp_redirect_map; + + err = bpf_map_update_elem(flags_map, &protocol, &redirect_flags, BPF_NOEXIST); + if (!ASSERT_OK(err, "init IP count")) + goto destroy_xdp_redirect_map; + + cnt_map = bpf_map__fd(xdp_redirect_map->maps.rxcnt); + if (!ASSERT_OK_FD(cnt_map, "open rxcnt map")) + goto destroy_xdp_redirect_map; + + bpf_objs[0] = xdp_redirect_multi_kern->obj; + bpf_objs[1] = xdp_redirect_map->obj; + for (i = 0; i < VETH_PAIRS_COUNT; i++) { + int ifindex = if_nametoindex(net_config[i].local_veth); + + if (attach_programs_to_veth_pair(bpf_objs, BROADCAST_REDIRECT_SKEL_NB, prog_cfg, i)) + goto destroy_xdp_redirect_map; + + SYS(destroy_xdp_redirect_map, + "ip -n %s neigh add %s lladdr 00:00:00:00:00:01 dev %s", + net_config[i].namespace, IP_NEIGH, net_config[i].remote_veth); + + devmap_val.ifindex = ifindex; + err = bpf_map_update_elem(group_map, &ifindex, &devmap_val, 0); + if (!ASSERT_OK(err, "bpf_map_update_elem")) + goto destroy_xdp_redirect_map; + + } + + SYS_NOFAIL("ip netns exec %s ping %s -i 0.1 -c 4 -W1 > /dev/null ", + net_config[0].namespace, IP_NEIGH); + + for (i = 0; i < VETH_PAIRS_COUNT; i++) { + err = bpf_map_lookup_elem(cnt_map, &i, &cnt); + if (!ASSERT_OK(err, "get IP cnt")) + goto destroy_xdp_redirect_map; + + if (redirect_flags & BPF_F_EXCLUDE_INGRESS) + /* veth11 shouldn't receive the ICMP requests; + * others should + */ + ASSERT_EQ(cnt, i ? 4 : 0, "compare IP cnt"); + else + /* All remote veth should receive the ICMP requests */ + ASSERT_EQ(cnt, 4, "compare IP cnt"); + } + +destroy_xdp_redirect_map: + xdp_redirect_map__destroy(xdp_redirect_map); +destroy_xdp_redirect_multi_kern: + xdp_redirect_multi_kern__destroy(xdp_redirect_multi_kern); + + cleanup_network(); +} + +void test_xdp_veth_broadcast_redirect(void) +{ + if (test__start_subtest("0/BROADCAST")) + xdp_veth_broadcast_redirect(0, BPF_F_BROADCAST); + + if (test__start_subtest("0/(BROADCAST | EXCLUDE_INGRESS)")) + xdp_veth_broadcast_redirect(0, BPF_F_BROADCAST | BPF_F_EXCLUDE_INGRESS); + + if (test__start_subtest("DRV_MODE/BROADCAST")) + xdp_veth_broadcast_redirect(XDP_FLAGS_DRV_MODE, BPF_F_BROADCAST); + + if (test__start_subtest("DRV_MODE/(BROADCAST | EXCLUDE_INGRESS)")) + xdp_veth_broadcast_redirect(XDP_FLAGS_DRV_MODE, + BPF_F_BROADCAST | BPF_F_EXCLUDE_INGRESS); + + if (test__start_subtest("SKB_MODE/BROADCAST")) + xdp_veth_broadcast_redirect(XDP_FLAGS_SKB_MODE, BPF_F_BROADCAST); + + if (test__start_subtest("SKB_MODE/(BROADCAST | EXCLUDE_INGRESS)")) + xdp_veth_broadcast_redirect(XDP_FLAGS_SKB_MODE, + BPF_F_BROADCAST | BPF_F_EXCLUDE_INGRESS); +} + diff --git a/tools/testing/selftests/bpf/progs/xdp_redirect_map.c b/tools/testing/selftests/bpf/progs/xdp_redirect_map.c index 682dda8dabbc9abbb5d1b0b22dd5f81124142e79..14385df71d7fc40c3b0ee5c6ea0760d0e7336d71 100644 --- a/tools/testing/selftests/bpf/progs/xdp_redirect_map.c +++ b/tools/testing/selftests/bpf/progs/xdp_redirect_map.c @@ -1,7 +1,11 @@ // SPDX-License-Identifier: GPL-2.0
+#include <linux/if_ether.h> +#include <linux/ip.h> + #include <linux/bpf.h> #include <bpf/bpf_helpers.h> +#include <bpf/bpf_endian.h>
struct { __uint(type, BPF_MAP_TYPE_DEVMAP); @@ -28,4 +32,49 @@ int xdp_redirect_map_2(struct xdp_md *xdp) return bpf_redirect_map(&tx_port, 2, 0); }
+struct { + __uint(type, BPF_MAP_TYPE_ARRAY); + __uint(max_entries, 3); + __type(key, __u32); + __type(value, __u64); +} rxcnt SEC(".maps"); + +static int xdp_count(struct xdp_md *xdp, __u32 key) +{ + void *data_end = (void *)(long)xdp->data_end; + void *data = (void *)(long)xdp->data; + struct ethhdr *eth = data; + __u64 *count; + + if (data + sizeof(*eth) > data_end) + return XDP_DROP; + + if (bpf_htons(eth->h_proto) == ETH_P_IP) { + /* We only count IPv4 packets */ + count = bpf_map_lookup_elem(&rxcnt, &key); + if (count) + *count += 1; + } + + return XDP_PASS; +} + +SEC("xdp") +int xdp_count_0(struct xdp_md *xdp) +{ + return xdp_count(xdp, 0); +} + +SEC("xdp") +int xdp_count_1(struct xdp_md *xdp) +{ + return xdp_count(xdp, 1); +} + +SEC("xdp") +int xdp_count_2(struct xdp_md *xdp) +{ + return xdp_count(xdp, 2); +} + char _license[] SEC("license") = "GPL";
XDP programs loaded on egress is tested by test_xdp_redirect_multi.sh but not by the test_progs framework.
Add a test case in test_xdp_veth.c to test the XDP program on egress. Use the same BPF program than test_xdp_redirect_multi.sh that replaces the source MAC address by one provided through a BPF map. Use a BPF program that stores the source MAC of received packets in a map to check the test results.
Signed-off-by: Bastien Curutchet (eBPF Foundation) bastien.curutchet@bootlin.com --- .../selftests/bpf/prog_tests/test_xdp_veth.c | 123 +++++++++++++++++++++ .../testing/selftests/bpf/progs/xdp_redirect_map.c | 40 +++++++ 2 files changed, 163 insertions(+)
diff --git a/tools/testing/selftests/bpf/prog_tests/test_xdp_veth.c b/tools/testing/selftests/bpf/prog_tests/test_xdp_veth.c index 9caa5755f0a2471e091bd8d5a701087a64565902..c7bed7e906410630581c596014f3c8824d9fe0c3 100644 --- a/tools/testing/selftests/bpf/prog_tests/test_xdp_veth.c +++ b/tools/testing/selftests/bpf/prog_tests/test_xdp_veth.c @@ -30,6 +30,8 @@ * -> echo request received by all except veth11 * - IPv4 ping : BPF_F_BROADCAST * -> echo request received by all veth + * - [test_xdp_veth_egress]: + * - all src mac should be the magic mac * * veth11 veth22 veth33 * (XDP_PASS) (XDP_PASS) (XDP_PASS) @@ -424,3 +426,124 @@ void test_xdp_veth_broadcast_redirect(void) BPF_F_BROADCAST | BPF_F_EXCLUDE_INGRESS); }
+#define VETH_EGRESS_SKEL_NB 3 +static void xdp_veth_egress(u32 flags) +{ + struct prog_configuration prog_cfg[VETH_PAIRS_COUNT] = { + { + .local_name = "xdp_redirect_map_all_prog", + .remote_name = "xdp_dummy_prog", + .local_flags = flags, + .remote_flags = flags, + }, + { + .local_name = "xdp_redirect_map_all_prog", + .remote_name = "store_mac_1", + .local_flags = flags, + .remote_flags = flags, + }, + { + .local_name = "xdp_redirect_map_all_prog", + .remote_name = "store_mac_2", + .local_flags = flags, + .remote_flags = flags, + } + }; + const char magic_mac[6] = { 0xAA, 0xBB, 0xCC, 0xDD, 0xEE, 0xFF}; + struct xdp_redirect_multi_kern *xdp_redirect_multi_kern; + struct bpf_object *bpf_objs[VETH_EGRESS_SKEL_NB]; + struct xdp_redirect_map *xdp_redirect_map; + struct bpf_devmap_val devmap_val = {}; + int mac_map, egress_map, res_map; + struct xdp_dummy *xdp_dummy; + int err; + int i; + + xdp_dummy = xdp_dummy__open_and_load(); + if (!ASSERT_OK_PTR(xdp_dummy, "xdp_dummy__open_and_load")) + return; + + xdp_redirect_multi_kern = xdp_redirect_multi_kern__open_and_load(); + if (!ASSERT_OK_PTR(xdp_redirect_multi_kern, "xdp_redirect_multi_kern__open_and_load")) + goto destroy_xdp_dummy; + + xdp_redirect_map = xdp_redirect_map__open_and_load(); + if (!ASSERT_OK_PTR(xdp_redirect_map, "xdp_redirect_map__open_and_load")) + goto destroy_xdp_redirect_multi_kern; + + if (create_network()) + goto destroy_xdp_redirect_map; + + mac_map = bpf_map__fd(xdp_redirect_multi_kern->maps.mac_map); + if (!ASSERT_OK_FD(mac_map, "open mac_map")) + goto destroy_xdp_redirect_map; + + egress_map = bpf_map__fd(xdp_redirect_multi_kern->maps.map_egress); + if (!ASSERT_OK_FD(egress_map, "open map_egress")) + goto destroy_xdp_redirect_map; + + devmap_val.bpf_prog.fd = bpf_program__fd(xdp_redirect_multi_kern->progs.xdp_devmap_prog); + + bpf_objs[0] = xdp_dummy->obj; + bpf_objs[1] = xdp_redirect_multi_kern->obj; + bpf_objs[2] = xdp_redirect_map->obj; + for (i = 0; i < VETH_PAIRS_COUNT; i++) { + int ifindex = if_nametoindex(net_config[i].local_veth); + + SYS(destroy_xdp_redirect_map, + "ip -n %s neigh add %s lladdr 00:00:00:00:00:01 dev %s", + net_config[i].namespace, IP_NEIGH, net_config[i].remote_veth); + + if (attach_programs_to_veth_pair(bpf_objs, VETH_REDIRECT_SKEL_NB, prog_cfg, i)) + goto destroy_xdp_redirect_map; + + err = bpf_map_update_elem(mac_map, &ifindex, magic_mac, 0); + if (!ASSERT_OK(err, "bpf_map_update_elem")) + goto destroy_xdp_redirect_map; + + devmap_val.ifindex = ifindex; + err = bpf_map_update_elem(egress_map, &ifindex, &devmap_val, 0); + if (!ASSERT_OK(err, "bpf_map_update_elem")) + goto destroy_xdp_redirect_map; + } + + SYS_NOFAIL("ip netns exec %s ping %s -i 0.1 -c 4 -W1 > /dev/null ", + net_config[0].namespace, IP_NEIGH); + + res_map = bpf_map__fd(xdp_redirect_map->maps.rx_mac); + if (!ASSERT_OK_FD(res_map, "open rx_map")) + goto destroy_xdp_redirect_map; + + for (i = 0; i < 2; i++) { + u32 key = i; + u64 res; + + err = bpf_map_lookup_elem(res_map, &key, &res); + if (!ASSERT_OK(err, "get MAC res")) + goto destroy_xdp_redirect_map; + + ASSERT_STRNEQ((const char *)&res, magic_mac, ETH_ALEN, "compare mac"); + } + +destroy_xdp_redirect_map: + xdp_redirect_map__destroy(xdp_redirect_map); +destroy_xdp_redirect_multi_kern: + xdp_redirect_multi_kern__destroy(xdp_redirect_multi_kern); +destroy_xdp_dummy: + xdp_dummy__destroy(xdp_dummy); + + cleanup_network(); +} + +void test_xdp_veth_egress(void) +{ + if (test__start_subtest("0")) + xdp_veth_egress(0); + + if (test__start_subtest("DRV_MODE")) + xdp_veth_egress(XDP_FLAGS_DRV_MODE); + + if (test__start_subtest("SKB_MODE")) + xdp_veth_egress(XDP_FLAGS_SKB_MODE); +} + diff --git a/tools/testing/selftests/bpf/progs/xdp_redirect_map.c b/tools/testing/selftests/bpf/progs/xdp_redirect_map.c index 14385df71d7fc40c3b0ee5c6ea0760d0e7336d71..3b8b4e0cdd071b0ce885a1f42d55da5257cf38c2 100644 --- a/tools/testing/selftests/bpf/progs/xdp_redirect_map.c +++ b/tools/testing/selftests/bpf/progs/xdp_redirect_map.c @@ -77,4 +77,44 @@ int xdp_count_2(struct xdp_md *xdp) return xdp_count(xdp, 2); }
+struct { + __uint(type, BPF_MAP_TYPE_ARRAY); + __uint(max_entries, 2); + __type(key, __u32); + __type(value, __be64); +} rx_mac SEC(".maps"); + +static int store_mac(struct xdp_md *xdp, __u32 id) +{ + void *data_end = (void *)(long)xdp->data_end; + void *data = (void *)(long)xdp->data; + struct ethhdr *eth = data; + __u32 key = id; + __be64 mac = 0; + + if (data + sizeof(*eth) > data_end) + return XDP_DROP; + + /* Only store IPv4 MAC to avoid being polluted by IPv6 packets */ + if (eth->h_proto == bpf_htons(ETH_P_IP)) { + __builtin_memcpy(&mac, eth->h_source, ETH_ALEN); + bpf_map_update_elem(&rx_mac, &key, &mac, 0); + bpf_printk("%s - %x", __func__, mac); + } + + return XDP_PASS; +} + +SEC("xdp") +int store_mac_1(struct xdp_md *xdp) +{ + return store_mac(xdp, 0); +} + +SEC("xdp") +int store_mac_2(struct xdp_md *xdp) +{ + return store_mac(xdp, 1); +} + char _license[] SEC("license") = "GPL";
The tests done by test_xdp_redirect_multi.sh are now fully covered by the CI through test_xdp_veth.c.
Remove test_xdp_redirect_multi.sh Remove xdp_redirect_multi.c that was used by the script to load and attach the BPF programs. Remove their entries in the Makefile
Signed-off-by: Bastien Curutchet (eBPF Foundation) bastien.curutchet@bootlin.com --- tools/testing/selftests/bpf/Makefile | 2 - .../selftests/bpf/test_xdp_redirect_multi.sh | 214 ------------------- tools/testing/selftests/bpf/xdp_redirect_multi.c | 226 --------------------- 3 files changed, 442 deletions(-)
diff --git a/tools/testing/selftests/bpf/Makefile b/tools/testing/selftests/bpf/Makefile index b59aa621b06b8aecce62f7d0c7a6b5c4feb0e4fd..3498322689efb19d98a3cda7393d27134aa5a6fc 100644 --- a/tools/testing/selftests/bpf/Makefile +++ b/tools/testing/selftests/bpf/Makefile @@ -100,7 +100,6 @@ TEST_FILES = xsk_prereqs.sh $(wildcard progs/btf_dump_test_case_*.c)
# Order correspond to 'make run_tests' order TEST_PROGS := test_kmod.sh \ - test_xdp_redirect_multi.sh \ test_xdp_meta.sh \ test_tunnel.sh \ test_lwt_seg6local.sh \ @@ -136,7 +135,6 @@ TEST_GEN_PROGS_EXTENDED = \ veristat \ xdp_features \ xdp_hw_metadata \ - xdp_redirect_multi \ xdp_synproxy \ xdping \ xskxceiver diff --git a/tools/testing/selftests/bpf/test_xdp_redirect_multi.sh b/tools/testing/selftests/bpf/test_xdp_redirect_multi.sh deleted file mode 100755 index 4c3c3fdd2d7304cbe71abbea69f1c20601108b2d..0000000000000000000000000000000000000000 --- a/tools/testing/selftests/bpf/test_xdp_redirect_multi.sh +++ /dev/null @@ -1,214 +0,0 @@ -#!/bin/bash -# SPDX-License-Identifier: GPL-2.0 -# -# Test topology: -# - - - - - - - - - - - - - - - - - - - -# | veth1 veth2 veth3 | ns0 -# - -| - - - - - - | - - - - - - | - - -# --------- --------- --------- -# | veth0 | | veth0 | | veth0 | -# --------- --------- --------- -# ns1 ns2 ns3 -# -# Test modules: -# XDP modes: generic, native, native + egress_prog -# -# Test cases: -# ARP: Testing BPF_F_BROADCAST, the ingress interface also should receive -# the redirects. -# ns1 -> gw: ns1, ns2, ns3, should receive the arp request -# IPv4: Testing BPF_F_BROADCAST | BPF_F_EXCLUDE_INGRESS, the ingress -# interface should not receive the redirects. -# ns1 -> gw: ns1 should not receive, ns2, ns3 should receive redirects. -# IPv6: Testing none flag, all the pkts should be redirected back -# ping test: ns1 -> ns2 (block), echo requests will be redirect back -# egress_prog: -# all src mac should be egress interface's mac - -# netns numbers -NUM=3 -IFACES="" -DRV_MODE="xdpgeneric xdpdrv xdpegress" -PASS=0 -FAIL=0 -LOG_DIR=$(mktemp -d) -declare -a NS -NS[0]="ns0-$(mktemp -u XXXXXX)" -NS[1]="ns1-$(mktemp -u XXXXXX)" -NS[2]="ns2-$(mktemp -u XXXXXX)" -NS[3]="ns3-$(mktemp -u XXXXXX)" - -test_pass() -{ - echo "Pass: $@" - PASS=$((PASS + 1)) -} - -test_fail() -{ - echo "fail: $@" - FAIL=$((FAIL + 1)) -} - -clean_up() -{ - for i in $(seq 0 $NUM); do - ip netns del ${NS[$i]} 2> /dev/null - done -} - -# Kselftest framework requirement - SKIP code is 4. -check_env() -{ - ip link set dev lo xdpgeneric off &>/dev/null - if [ $? -ne 0 ];then - echo "selftests: [SKIP] Could not run test without the ip xdpgeneric support" - exit 4 - fi - - which tcpdump &>/dev/null - if [ $? -ne 0 ];then - echo "selftests: [SKIP] Could not run test without tcpdump" - exit 4 - fi -} - -setup_ns() -{ - local mode=$1 - IFACES="" - - if [ "$mode" = "xdpegress" ]; then - mode="xdpdrv" - fi - - ip netns add ${NS[0]} - for i in $(seq $NUM); do - ip netns add ${NS[$i]} - ip -n ${NS[$i]} link add veth0 type veth peer name veth$i netns ${NS[0]} - ip -n ${NS[$i]} link set veth0 up - ip -n ${NS[0]} link set veth$i up - - ip -n ${NS[$i]} addr add 192.0.2.$i/24 dev veth0 - ip -n ${NS[$i]} addr add 2001:db8::$i/64 dev veth0 - # Add a neigh entry for IPv4 ping test - ip -n ${NS[$i]} neigh add 192.0.2.253 lladdr 00:00:00:00:00:01 dev veth0 - ip -n ${NS[$i]} link set veth0 $mode obj \ - xdp_dummy.bpf.o sec xdp &> /dev/null || \ - { test_fail "Unable to load dummy xdp" && exit 1; } - IFACES="$IFACES veth$i" - veth_mac[$i]=$(ip -n ${NS[0]} link show veth$i | awk '/link/ether/ {print $2}') - done -} - -do_egress_tests() -{ - local mode=$1 - - # mac test - ip netns exec ${NS[2]} tcpdump -e -i veth0 -nn -l -e &> ${LOG_DIR}/mac_ns1-2_${mode}.log & - ip netns exec ${NS[3]} tcpdump -e -i veth0 -nn -l -e &> ${LOG_DIR}/mac_ns1-3_${mode}.log & - sleep 0.5 - ip netns exec ${NS[1]} ping 192.0.2.254 -i 0.1 -c 4 &> /dev/null - sleep 0.5 - pkill tcpdump - - # mac check - grep -q "${veth_mac[2]} > ff:ff:ff:ff:ff:ff" ${LOG_DIR}/mac_ns1-2_${mode}.log && \ - test_pass "$mode mac ns1-2" || test_fail "$mode mac ns1-2" - grep -q "${veth_mac[3]} > ff:ff:ff:ff:ff:ff" ${LOG_DIR}/mac_ns1-3_${mode}.log && \ - test_pass "$mode mac ns1-3" || test_fail "$mode mac ns1-3" -} - -do_ping_tests() -{ - local mode=$1 - - # ping6 test: echo request should be redirect back to itself, not others - ip netns exec ${NS[1]} ip neigh add 2001:db8::2 dev veth0 lladdr 00:00:00:00:00:02 - - ip netns exec ${NS[1]} tcpdump -i veth0 -nn -l -e &> ${LOG_DIR}/ns1-1_${mode}.log & - ip netns exec ${NS[2]} tcpdump -i veth0 -nn -l -e &> ${LOG_DIR}/ns1-2_${mode}.log & - ip netns exec ${NS[3]} tcpdump -i veth0 -nn -l -e &> ${LOG_DIR}/ns1-3_${mode}.log & - sleep 0.5 - # ARP test - ip netns exec ${NS[1]} arping -q -c 2 -I veth0 192.0.2.254 - # IPv4 test - ip netns exec ${NS[1]} ping 192.0.2.253 -i 0.1 -c 4 &> /dev/null - # IPv6 test - ip netns exec ${NS[1]} ping6 2001:db8::2 -i 0.1 -c 2 &> /dev/null - sleep 0.5 - pkill tcpdump - - # All netns should receive the redirect arp requests - [ $(grep -cF "who-has 192.0.2.254" ${LOG_DIR}/ns1-1_${mode}.log) -eq 4 ] && \ - test_pass "$mode arp(F_BROADCAST) ns1-1" || \ - test_fail "$mode arp(F_BROADCAST) ns1-1" - [ $(grep -cF "who-has 192.0.2.254" ${LOG_DIR}/ns1-2_${mode}.log) -eq 2 ] && \ - test_pass "$mode arp(F_BROADCAST) ns1-2" || \ - test_fail "$mode arp(F_BROADCAST) ns1-2" - [ $(grep -cF "who-has 192.0.2.254" ${LOG_DIR}/ns1-3_${mode}.log) -eq 2 ] && \ - test_pass "$mode arp(F_BROADCAST) ns1-3" || \ - test_fail "$mode arp(F_BROADCAST) ns1-3" - - # ns1 should not receive the redirect echo request, others should - [ $(grep -c "ICMP echo request" ${LOG_DIR}/ns1-1_${mode}.log) -eq 4 ] && \ - test_pass "$mode IPv4 (F_BROADCAST|F_EXCLUDE_INGRESS) ns1-1" || \ - test_fail "$mode IPv4 (F_BROADCAST|F_EXCLUDE_INGRESS) ns1-1" - [ $(grep -c "ICMP echo request" ${LOG_DIR}/ns1-2_${mode}.log) -eq 4 ] && \ - test_pass "$mode IPv4 (F_BROADCAST|F_EXCLUDE_INGRESS) ns1-2" || \ - test_fail "$mode IPv4 (F_BROADCAST|F_EXCLUDE_INGRESS) ns1-2" - [ $(grep -c "ICMP echo request" ${LOG_DIR}/ns1-3_${mode}.log) -eq 4 ] && \ - test_pass "$mode IPv4 (F_BROADCAST|F_EXCLUDE_INGRESS) ns1-3" || \ - test_fail "$mode IPv4 (F_BROADCAST|F_EXCLUDE_INGRESS) ns1-3" - - # ns1 should receive the echo request, ns2 should not - [ $(grep -c "ICMP6, echo request" ${LOG_DIR}/ns1-1_${mode}.log) -eq 4 ] && \ - test_pass "$mode IPv6 (no flags) ns1-1" || \ - test_fail "$mode IPv6 (no flags) ns1-1" - [ $(grep -c "ICMP6, echo request" ${LOG_DIR}/ns1-2_${mode}.log) -eq 0 ] && \ - test_pass "$mode IPv6 (no flags) ns1-2" || \ - test_fail "$mode IPv6 (no flags) ns1-2" -} - -do_tests() -{ - local mode=$1 - local drv_p - - case ${mode} in - xdpdrv) drv_p="-N";; - xdpegress) drv_p="-X";; - xdpgeneric) drv_p="-S";; - esac - - ip netns exec ${NS[0]} ./xdp_redirect_multi $drv_p $IFACES &> ${LOG_DIR}/xdp_redirect_${mode}.log & - xdp_pid=$! - sleep 1 - if ! ps -p $xdp_pid > /dev/null; then - test_fail "$mode xdp_redirect_multi start failed" - return 1 - fi - - if [ "$mode" = "xdpegress" ]; then - do_egress_tests $mode - else - do_ping_tests $mode - fi - - kill $xdp_pid -} - -check_env - -trap clean_up EXIT - -for mode in ${DRV_MODE}; do - setup_ns $mode - do_tests $mode - clean_up -done -rm -rf ${LOG_DIR} - -echo "Summary: PASS $PASS, FAIL $FAIL" -[ $FAIL -eq 0 ] && exit 0 || exit 1 diff --git a/tools/testing/selftests/bpf/xdp_redirect_multi.c b/tools/testing/selftests/bpf/xdp_redirect_multi.c deleted file mode 100644 index c1fc44c87c300c72df65a2fb00f9293c3b4f2ffc..0000000000000000000000000000000000000000 --- a/tools/testing/selftests/bpf/xdp_redirect_multi.c +++ /dev/null @@ -1,226 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 -#include <linux/bpf.h> -#include <linux/if_link.h> -#include <assert.h> -#include <errno.h> -#include <signal.h> -#include <stdio.h> -#include <stdlib.h> -#include <string.h> -#include <net/if.h> -#include <unistd.h> -#include <libgen.h> -#include <sys/ioctl.h> -#include <sys/types.h> -#include <sys/socket.h> -#include <netinet/in.h> - -#include "bpf_util.h" -#include <bpf/bpf.h> -#include <bpf/libbpf.h> - -#define MAX_IFACE_NUM 32 -#define MAX_INDEX_NUM 1024 - -static __u32 xdp_flags = XDP_FLAGS_UPDATE_IF_NOEXIST; -static int ifaces[MAX_IFACE_NUM] = {}; - -static void int_exit(int sig) -{ - __u32 prog_id = 0; - int i; - - for (i = 0; ifaces[i] > 0; i++) { - if (bpf_xdp_query_id(ifaces[i], xdp_flags, &prog_id)) { - printf("bpf_xdp_query_id failed\n"); - exit(1); - } - if (prog_id) - bpf_xdp_detach(ifaces[i], xdp_flags, NULL); - } - - exit(0); -} - -static int get_mac_addr(unsigned int ifindex, void *mac_addr) -{ - char ifname[IF_NAMESIZE]; - struct ifreq ifr; - int fd, ret = -1; - - fd = socket(AF_INET, SOCK_DGRAM, 0); - if (fd < 0) - return ret; - - if (!if_indextoname(ifindex, ifname)) - goto err_out; - - strcpy(ifr.ifr_name, ifname); - - if (ioctl(fd, SIOCGIFHWADDR, &ifr) != 0) - goto err_out; - - memcpy(mac_addr, ifr.ifr_hwaddr.sa_data, 6 * sizeof(char)); - ret = 0; - -err_out: - close(fd); - return ret; -} - -static void usage(const char *prog) -{ - fprintf(stderr, - "usage: %s [OPTS] <IFNAME|IFINDEX> <IFNAME|IFINDEX> ...\n" - "OPTS:\n" - " -S use skb-mode\n" - " -N enforce native mode\n" - " -F force loading prog\n" - " -X load xdp program on egress\n", - prog); -} - -int main(int argc, char **argv) -{ - int prog_fd, group_all, mac_map; - struct bpf_program *ingress_prog, *egress_prog; - int i, err, ret, opt, egress_prog_fd = 0; - struct bpf_devmap_val devmap_val; - bool attach_egress_prog = false; - unsigned char mac_addr[6]; - char ifname[IF_NAMESIZE]; - struct bpf_object *obj; - unsigned int ifindex; - char filename[256]; - - while ((opt = getopt(argc, argv, "SNFX")) != -1) { - switch (opt) { - case 'S': - xdp_flags |= XDP_FLAGS_SKB_MODE; - break; - case 'N': - /* default, set below */ - break; - case 'F': - xdp_flags &= ~XDP_FLAGS_UPDATE_IF_NOEXIST; - break; - case 'X': - attach_egress_prog = true; - break; - default: - usage(basename(argv[0])); - return 1; - } - } - - if (!(xdp_flags & XDP_FLAGS_SKB_MODE)) { - xdp_flags |= XDP_FLAGS_DRV_MODE; - } else if (attach_egress_prog) { - printf("Load xdp program on egress with SKB mode not supported yet\n"); - goto err_out; - } - - if (optind == argc) { - printf("usage: %s <IFNAME|IFINDEX> <IFNAME|IFINDEX> ...\n", argv[0]); - goto err_out; - } - - printf("Get interfaces:"); - for (i = 0; i < MAX_IFACE_NUM && argv[optind + i]; i++) { - ifaces[i] = if_nametoindex(argv[optind + i]); - if (!ifaces[i]) - ifaces[i] = strtoul(argv[optind + i], NULL, 0); - if (!if_indextoname(ifaces[i], ifname)) { - perror("Invalid interface name or i"); - goto err_out; - } - if (ifaces[i] > MAX_INDEX_NUM) { - printf(" interface index too large\n"); - goto err_out; - } - printf(" %d", ifaces[i]); - } - printf("\n"); - - snprintf(filename, sizeof(filename), "%s_kern.bpf.o", argv[0]); - obj = bpf_object__open_file(filename, NULL); - err = libbpf_get_error(obj); - if (err) - goto err_out; - err = bpf_object__load(obj); - if (err) - goto err_out; - prog_fd = bpf_program__fd(bpf_object__next_program(obj, NULL)); - - if (attach_egress_prog) - group_all = bpf_object__find_map_fd_by_name(obj, "map_egress"); - else - group_all = bpf_object__find_map_fd_by_name(obj, "map_all"); - mac_map = bpf_object__find_map_fd_by_name(obj, "mac_map"); - - if (group_all < 0 || mac_map < 0) { - printf("bpf_object__find_map_fd_by_name failed\n"); - goto err_out; - } - - if (attach_egress_prog) { - /* Find ingress/egress prog for 2nd xdp prog */ - ingress_prog = bpf_object__find_program_by_name(obj, "xdp_redirect_map_all_prog"); - egress_prog = bpf_object__find_program_by_name(obj, "xdp_devmap_prog"); - if (!ingress_prog || !egress_prog) { - printf("finding ingress/egress_prog in obj file failed\n"); - goto err_out; - } - prog_fd = bpf_program__fd(ingress_prog); - egress_prog_fd = bpf_program__fd(egress_prog); - if (prog_fd < 0 || egress_prog_fd < 0) { - printf("find egress_prog fd failed\n"); - goto err_out; - } - } - - signal(SIGINT, int_exit); - signal(SIGTERM, int_exit); - - /* Init forward multicast groups and exclude group */ - for (i = 0; ifaces[i] > 0; i++) { - ifindex = ifaces[i]; - - if (attach_egress_prog) { - ret = get_mac_addr(ifindex, mac_addr); - if (ret < 0) { - printf("get interface %d mac failed\n", ifindex); - goto err_out; - } - ret = bpf_map_update_elem(mac_map, &ifindex, mac_addr, 0); - if (ret) { - perror("bpf_update_elem mac_map failed\n"); - goto err_out; - } - } - - /* Add all the interfaces to group all */ - devmap_val.ifindex = ifindex; - devmap_val.bpf_prog.fd = egress_prog_fd; - ret = bpf_map_update_elem(group_all, &ifindex, &devmap_val, 0); - if (ret) { - perror("bpf_map_update_elem"); - goto err_out; - } - - /* bind prog_fd to each interface */ - ret = bpf_xdp_attach(ifindex, prog_fd, xdp_flags, NULL); - if (ret) { - printf("Set xdp fd failed on %d\n", ifindex); - goto err_out; - } - } - - /* sleep some time for testing */ - sleep(999); - - return 0; - -err_out: - return 1; -}
linux-kselftest-mirror@lists.linaro.org