Hello, this series aims to convert another test to the test_progs framework to make sure that it is executed in CI for series sent on the mailing list. test_tc_tunnel.sh tests a variety of tunnels based on BPF: packets are encapsulated by a BPF program on the client egress. We then check that those packets can be decapsulated on server ingress side, either thanks to kernel-based or BPF-based decapsulation. Those tests are run thanks to two veths in two dedicated namespaces.
- patches 1 to 3 are preparatory patches - patch 4 introduce tc_tunnel test into test_progs - patch 5 gets rid of the test_tc_tunnel.sh script
The new test has been executed both in some x86 local qemu machine, as well as in CI:
# ./test_progs -a tc_tunnel #454/1 tc_tunnel/ipip_none:OK #454/2 tc_tunnel/ipip6_none:OK #454/3 tc_tunnel/ip6tnl_none:OK #454/4 tc_tunnel/sit_none:OK #454/5 tc_tunnel/vxlan_eth:OK #454/6 tc_tunnel/ip6vxlan_eth:OK #454/7 tc_tunnel/gre_none:OK #454/8 tc_tunnel/gre_eth:OK #454/9 tc_tunnel/gre_mpls:OK #454/10 tc_tunnel/ip6gre_none:OK #454/11 tc_tunnel/ip6gre_eth:OK #454/12 tc_tunnel/ip6gre_mpls:OK #454/13 tc_tunnel/udp_none:OK #454/14 tc_tunnel/udp_eth:OK #454/15 tc_tunnel/udp_mpls:OK #454/16 tc_tunnel/ip6udp_none:OK #454/17 tc_tunnel/ip6udp_eth:OK #454/18 tc_tunnel/ip6udp_mpls:OK #454 tc_tunnel:OK Summary: 1/18 PASSED, 0 SKIPPED, 0 FAILED
Signed-off-by: Alexis Lothoré (eBPF Foundation) alexis.lothore@bootlin.com --- Alexis Lothoré (eBPF Foundation) (5): testing/selftests: rename tc_helpers.h to tcx_helpers.h selftests/bpf: add tc helpers selftests/bpf: make test_tc_tunnel.bpf.c compatible with big endian platforms selftests/bpf: integrate test_tc_tunnel.sh tests into test_progs selftests/bpf: remove test_tc_tunnel.sh
tools/testing/selftests/bpf/Makefile | 2 +- tools/testing/selftests/bpf/prog_tests/tc_links.c | 46 +- tools/testing/selftests/bpf/prog_tests/tc_netkit.c | 22 +- tools/testing/selftests/bpf/prog_tests/tc_opts.c | 40 +- .../bpf/prog_tests/{tc_helpers.h => tcx_helpers.h} | 6 +- .../selftests/bpf/prog_tests/test_tc_tunnel.c | 684 +++++++++++++++++++++ .../testing/selftests/bpf/prog_tests/test_tunnel.c | 80 +-- tools/testing/selftests/bpf/progs/test_tc_tunnel.c | 99 ++- tools/testing/selftests/bpf/tc_helpers.c | 87 +++ tools/testing/selftests/bpf/tc_helpers.h | 9 + tools/testing/selftests/bpf/test_tc_tunnel.sh | 320 ---------- 11 files changed, 884 insertions(+), 511 deletions(-) --- base-commit: 22267893b8c7f2773896e814800bbe693f206e0c change-id: 20250811-tc_tunnel-c61342683f18
Best regards,
The test_tunnel.c file defines some convenient functions to create a qdisc and attach bpf programs to it. It would be beneficial to new tests to get those functions exported as helpers in a tc_helpers.h. There is however already a tc_helpers.h file in the BPF selftests, used by a few tests. This header is quite coupled to some BPF programs (some exposed helpers consume a skeleton from some specific BPF programs), so rename it to something more related to its original purpose.
Signed-off-by: Alexis Lothoré (eBPF Foundation) alexis.lothore@bootlin.com --- tools/testing/selftests/bpf/prog_tests/tc_links.c | 46 +++++++++++----------- tools/testing/selftests/bpf/prog_tests/tc_netkit.c | 22 +++++------ tools/testing/selftests/bpf/prog_tests/tc_opts.c | 40 +++++++++---------- .../bpf/prog_tests/{tc_helpers.h => tcx_helpers.h} | 6 +-- 4 files changed, 57 insertions(+), 57 deletions(-)
diff --git a/tools/testing/selftests/bpf/prog_tests/tc_links.c b/tools/testing/selftests/bpf/prog_tests/tc_links.c index 2186a24e7d8a948840532f9bb6263f68985583c8..aa61d1d27dc9dcc5ba1e2ec628f0b12382528b4b 100644 --- a/tools/testing/selftests/bpf/prog_tests/tc_links.c +++ b/tools/testing/selftests/bpf/prog_tests/tc_links.c @@ -11,7 +11,7 @@ #include "test_tc_link.skel.h"
#include "netlink_helpers.h" -#include "tc_helpers.h" +#include "tcx_helpers.h"
void test_ns_tc_links_basic(void) { @@ -67,7 +67,7 @@ void test_ns_tc_links_basic(void) ASSERT_EQ(optq.prog_ids[1], 0, "prog_ids[1]"); ASSERT_EQ(optq.link_ids[1], 0, "link_ids[1]");
- tc_skel_reset_all_seen(skel); + tcx_skel_reset_all_seen(skel); ASSERT_OK(system(ping_cmd), ping_cmd);
ASSERT_EQ(skel->bss->seen_tc1, true, "seen_tc1"); @@ -100,7 +100,7 @@ void test_ns_tc_links_basic(void) ASSERT_EQ(optq.prog_ids[1], 0, "prog_ids[1]"); ASSERT_EQ(optq.link_ids[1], 0, "link_ids[1]");
- tc_skel_reset_all_seen(skel); + tcx_skel_reset_all_seen(skel); ASSERT_OK(system(ping_cmd), ping_cmd);
ASSERT_EQ(skel->bss->seen_tc1, true, "seen_tc1"); @@ -191,7 +191,7 @@ static void test_tc_links_before_target(int target) ASSERT_EQ(optq.prog_ids[2], 0, "prog_ids[2]"); ASSERT_EQ(optq.link_ids[2], 0, "link_ids[2]");
- tc_skel_reset_all_seen(skel); + tcx_skel_reset_all_seen(skel); ASSERT_OK(system(ping_cmd), ping_cmd);
ASSERT_EQ(skel->bss->seen_tc1, true, "seen_tc1"); @@ -248,7 +248,7 @@ static void test_tc_links_before_target(int target) ASSERT_EQ(optq.prog_ids[4], 0, "prog_ids[4]"); ASSERT_EQ(optq.link_ids[4], 0, "link_ids[4]");
- tc_skel_reset_all_seen(skel); + tcx_skel_reset_all_seen(skel); ASSERT_OK(system(ping_cmd), ping_cmd);
ASSERT_EQ(skel->bss->seen_tc1, true, "seen_tc1"); @@ -345,7 +345,7 @@ static void test_tc_links_after_target(int target) ASSERT_EQ(optq.prog_ids[2], 0, "prog_ids[2]"); ASSERT_EQ(optq.link_ids[2], 0, "link_ids[2]");
- tc_skel_reset_all_seen(skel); + tcx_skel_reset_all_seen(skel); ASSERT_OK(system(ping_cmd), ping_cmd);
ASSERT_EQ(skel->bss->seen_tc1, true, "seen_tc1"); @@ -402,7 +402,7 @@ static void test_tc_links_after_target(int target) ASSERT_EQ(optq.prog_ids[4], 0, "prog_ids[4]"); ASSERT_EQ(optq.link_ids[4], 0, "link_ids[4]");
- tc_skel_reset_all_seen(skel); + tcx_skel_reset_all_seen(skel); ASSERT_OK(system(ping_cmd), ping_cmd);
ASSERT_EQ(skel->bss->seen_tc1, true, "seen_tc1"); @@ -504,7 +504,7 @@ static void test_tc_links_revision_target(int target) ASSERT_EQ(optq.prog_ids[2], 0, "prog_ids[2]"); ASSERT_EQ(optq.link_ids[2], 0, "prog_ids[2]");
- tc_skel_reset_all_seen(skel); + tcx_skel_reset_all_seen(skel); ASSERT_OK(system(ping_cmd), ping_cmd);
ASSERT_EQ(skel->bss->seen_tc1, true, "seen_tc1"); @@ -584,7 +584,7 @@ static void test_tc_chain_classic(int target, bool chain_tc_old)
assert_mprog_count(target, 2);
- tc_skel_reset_all_seen(skel); + tcx_skel_reset_all_seen(skel); ASSERT_OK(system(ping_cmd), ping_cmd);
ASSERT_EQ(skel->bss->seen_tc1, true, "seen_tc1"); @@ -597,7 +597,7 @@ static void test_tc_chain_classic(int target, bool chain_tc_old)
assert_mprog_count(target, 1);
- tc_skel_reset_all_seen(skel); + tcx_skel_reset_all_seen(skel); ASSERT_OK(system(ping_cmd), ping_cmd);
ASSERT_EQ(skel->bss->seen_tc1, true, "seen_tc1"); @@ -708,7 +708,7 @@ static void test_tc_links_replace_target(int target) ASSERT_EQ(optq.prog_ids[2], 0, "prog_ids[2]"); ASSERT_EQ(optq.link_ids[2], 0, "link_ids[2]");
- tc_skel_reset_all_seen(skel); + tcx_skel_reset_all_seen(skel); ASSERT_OK(system(ping_cmd), ping_cmd);
ASSERT_EQ(skel->bss->seen_tc1, true, "seen_tc1"); @@ -779,7 +779,7 @@ static void test_tc_links_replace_target(int target) ASSERT_EQ(optq.prog_ids[2], 0, "prog_ids[2]"); ASSERT_EQ(optq.link_ids[2], 0, "link_ids[2]");
- tc_skel_reset_all_seen(skel); + tcx_skel_reset_all_seen(skel); ASSERT_OK(system(ping_cmd), ping_cmd);
ASSERT_EQ(skel->bss->seen_tc1, true, "seen_tc1"); @@ -807,7 +807,7 @@ static void test_tc_links_replace_target(int target) ASSERT_EQ(optq.prog_ids[1], 0, "prog_ids[1]"); ASSERT_EQ(optq.link_ids[1], 0, "link_ids[1]");
- tc_skel_reset_all_seen(skel); + tcx_skel_reset_all_seen(skel); ASSERT_OK(system(ping_cmd), ping_cmd);
ASSERT_EQ(skel->bss->seen_tc1, true, "seen_tc1"); @@ -835,7 +835,7 @@ static void test_tc_links_replace_target(int target) ASSERT_EQ(optq.prog_ids[1], 0, "prog_ids[1]"); ASSERT_EQ(optq.link_ids[1], 0, "link_ids[1]");
- tc_skel_reset_all_seen(skel); + tcx_skel_reset_all_seen(skel); ASSERT_OK(system(ping_cmd), ping_cmd);
ASSERT_EQ(skel->bss->seen_tc1, true, "seen_tc1"); @@ -1247,7 +1247,7 @@ static void test_tc_links_prepend_target(int target) ASSERT_EQ(optq.prog_ids[2], 0, "prog_ids[2]"); ASSERT_EQ(optq.link_ids[2], 0, "link_ids[2]");
- tc_skel_reset_all_seen(skel); + tcx_skel_reset_all_seen(skel); ASSERT_OK(system(ping_cmd), ping_cmd);
ASSERT_EQ(skel->bss->seen_tc1, true, "seen_tc1"); @@ -1302,7 +1302,7 @@ static void test_tc_links_prepend_target(int target) ASSERT_EQ(optq.prog_ids[4], 0, "prog_ids[4]"); ASSERT_EQ(optq.link_ids[4], 0, "link_ids[4]");
- tc_skel_reset_all_seen(skel); + tcx_skel_reset_all_seen(skel); ASSERT_OK(system(ping_cmd), ping_cmd);
ASSERT_EQ(skel->bss->seen_tc1, true, "seen_tc1"); @@ -1403,7 +1403,7 @@ static void test_tc_links_append_target(int target) ASSERT_EQ(optq.prog_ids[2], 0, "prog_ids[2]"); ASSERT_EQ(optq.link_ids[2], 0, "link_ids[2]");
- tc_skel_reset_all_seen(skel); + tcx_skel_reset_all_seen(skel); ASSERT_OK(system(ping_cmd), ping_cmd);
ASSERT_EQ(skel->bss->seen_tc1, true, "seen_tc1"); @@ -1458,7 +1458,7 @@ static void test_tc_links_append_target(int target) ASSERT_EQ(optq.prog_ids[4], 0, "prog_ids[4]"); ASSERT_EQ(optq.link_ids[4], 0, "link_ids[4]");
- tc_skel_reset_all_seen(skel); + tcx_skel_reset_all_seen(skel); ASSERT_OK(system(ping_cmd), ping_cmd);
ASSERT_EQ(skel->bss->seen_tc1, true, "seen_tc1"); @@ -1628,7 +1628,7 @@ static void test_tc_chain_mixed(int target)
assert_mprog_count(target, 1);
- tc_skel_reset_all_seen(skel); + tcx_skel_reset_all_seen(skel); ASSERT_OK(system(ping_cmd), ping_cmd);
ASSERT_EQ(skel->bss->seen_tc4, false, "seen_tc4"); @@ -1641,7 +1641,7 @@ static void test_tc_chain_mixed(int target)
assert_mprog_count(target, 1);
- tc_skel_reset_all_seen(skel); + tcx_skel_reset_all_seen(skel); ASSERT_OK(system(ping_cmd), ping_cmd);
ASSERT_EQ(skel->bss->seen_tc4, true, "seen_tc4"); @@ -1654,7 +1654,7 @@ static void test_tc_chain_mixed(int target)
assert_mprog_count(target, 0);
- tc_skel_reset_all_seen(skel); + tcx_skel_reset_all_seen(skel); ASSERT_OK(system(ping_cmd), ping_cmd);
ASSERT_EQ(skel->bss->seen_tc4, false, "seen_tc4"); @@ -1744,7 +1744,7 @@ static void test_tc_links_ingress(int target, bool chain_tc_old,
assert_mprog_count(target, 2);
- tc_skel_reset_all_seen(skel); + tcx_skel_reset_all_seen(skel); ASSERT_OK(system(ping_cmd), ping_cmd);
ASSERT_EQ(skel->bss->seen_tc1, true, "seen_tc1"); @@ -1757,7 +1757,7 @@ static void test_tc_links_ingress(int target, bool chain_tc_old,
assert_mprog_count(target, 1);
- tc_skel_reset_all_seen(skel); + tcx_skel_reset_all_seen(skel); ASSERT_OK(system(ping_cmd), ping_cmd);
ASSERT_EQ(skel->bss->seen_tc1, true, "seen_tc1"); diff --git a/tools/testing/selftests/bpf/prog_tests/tc_netkit.c b/tools/testing/selftests/bpf/prog_tests/tc_netkit.c index 2461d183dee584297f68df19b3c28cb52118bc59..ed52d1ece42ed8fe4edf4be36fd8558b6440f72c 100644 --- a/tools/testing/selftests/bpf/prog_tests/tc_netkit.c +++ b/tools/testing/selftests/bpf/prog_tests/tc_netkit.c @@ -12,7 +12,7 @@
#include "test_tc_link.skel.h" #include "netlink_helpers.h" -#include "tc_helpers.h" +#include "tcx_helpers.h"
#define NETKIT_HEADROOM 32 #define NETKIT_TAILROOM 8 @@ -252,7 +252,7 @@ void serial_test_tc_netkit_basic(void) ASSERT_EQ(optq.prog_ids[1], 0, "prog_ids[1]"); ASSERT_EQ(optq.link_ids[1], 0, "link_ids[1]");
- tc_skel_reset_all_seen(skel); + tcx_skel_reset_all_seen(skel); ASSERT_EQ(send_icmp(), 0, "icmp_pkt");
ASSERT_EQ(skel->bss->seen_tc1, true, "seen_tc1"); @@ -285,7 +285,7 @@ void serial_test_tc_netkit_basic(void) ASSERT_EQ(optq.prog_ids[1], 0, "prog_ids[1]"); ASSERT_EQ(optq.link_ids[1], 0, "link_ids[1]");
- tc_skel_reset_all_seen(skel); + tcx_skel_reset_all_seen(skel); ASSERT_EQ(send_icmp(), 0, "icmp_pkt");
ASSERT_EQ(skel->bss->seen_tc1, true, "seen_tc1"); @@ -366,7 +366,7 @@ static void serial_test_tc_netkit_multi_links_target(int mode, int target) ASSERT_EQ(optq.prog_ids[1], 0, "prog_ids[1]"); ASSERT_EQ(optq.link_ids[1], 0, "link_ids[1]");
- tc_skel_reset_all_seen(skel); + tcx_skel_reset_all_seen(skel); ASSERT_EQ(send_icmp(), 0, "icmp_pkt");
ASSERT_EQ(skel->bss->seen_tc1, true, "seen_tc1"); @@ -406,7 +406,7 @@ static void serial_test_tc_netkit_multi_links_target(int mode, int target) ASSERT_EQ(optq.prog_ids[2], 0, "prog_ids[2]"); ASSERT_EQ(optq.link_ids[2], 0, "link_ids[2]");
- tc_skel_reset_all_seen(skel); + tcx_skel_reset_all_seen(skel); ASSERT_EQ(send_icmp(), 0, "icmp_pkt");
ASSERT_EQ(skel->bss->seen_tc1, true, "seen_tc1"); @@ -481,7 +481,7 @@ static void serial_test_tc_netkit_multi_opts_target(int mode, int target) ASSERT_EQ(optq.prog_ids[0], pid1, "prog_ids[0]"); ASSERT_EQ(optq.prog_ids[1], 0, "prog_ids[1]");
- tc_skel_reset_all_seen(skel); + tcx_skel_reset_all_seen(skel); ASSERT_EQ(send_icmp(), 0, "icmp_pkt");
ASSERT_EQ(skel->bss->seen_tc1, true, "seen_tc1"); @@ -512,7 +512,7 @@ static void serial_test_tc_netkit_multi_opts_target(int mode, int target) ASSERT_EQ(optq.prog_ids[1], pid1, "prog_ids[1]"); ASSERT_EQ(optq.prog_ids[2], 0, "prog_ids[2]");
- tc_skel_reset_all_seen(skel); + tcx_skel_reset_all_seen(skel); ASSERT_EQ(send_icmp(), 0, "icmp_pkt");
ASSERT_EQ(skel->bss->seen_tc1, true, "seen_tc1"); @@ -616,7 +616,7 @@ void serial_test_tc_netkit_device(void) ASSERT_EQ(optq.prog_ids[1], 0, "prog_ids[1]"); ASSERT_EQ(optq.link_ids[1], 0, "link_ids[1]");
- tc_skel_reset_all_seen(skel); + tcx_skel_reset_all_seen(skel); ASSERT_EQ(send_icmp(), 0, "icmp_pkt");
ASSERT_EQ(skel->bss->seen_tc1, true, "seen_tc1"); @@ -716,7 +716,7 @@ static void serial_test_tc_netkit_neigh_links_target(int mode, int target) ASSERT_EQ(optq.prog_ids[1], 0, "prog_ids[1]"); ASSERT_EQ(optq.link_ids[1], 0, "link_ids[1]");
- tc_skel_reset_all_seen(skel); + tcx_skel_reset_all_seen(skel); ASSERT_EQ(__send_icmp(ping_addr_noneigh), 0, "icmp_pkt");
ASSERT_EQ(skel->bss->seen_tc1, true /* L2: ARP */, "seen_tc1"); @@ -787,7 +787,7 @@ static void serial_test_tc_netkit_pkt_type_mode(int mode)
move_netkit();
- tc_skel_reset_all_seen(skel); + tcx_skel_reset_all_seen(skel); skel->bss->set_type = true; ASSERT_EQ(send_icmp(), 0, "icmp_pkt");
@@ -847,7 +847,7 @@ static void serial_test_tc_netkit_scrub_type(int scrub, bool room) assert_mprog_count_ifindex(ifindex, BPF_NETKIT_PRIMARY, 1); assert_mprog_count_ifindex(ifindex, BPF_NETKIT_PEER, 0);
- tc_skel_reset_all_seen(skel); + tcx_skel_reset_all_seen(skel); ASSERT_EQ(send_icmp(), 0, "icmp_pkt");
ASSERT_EQ(skel->bss->seen_tc8, true, "seen_tc8"); diff --git a/tools/testing/selftests/bpf/prog_tests/tc_opts.c b/tools/testing/selftests/bpf/prog_tests/tc_opts.c index dd7a138d8c3dcef0db4d9e6831951db5306c0c04..bae33c8b5c06469405726aa9682c9be701c401f3 100644 --- a/tools/testing/selftests/bpf/prog_tests/tc_opts.c +++ b/tools/testing/selftests/bpf/prog_tests/tc_opts.c @@ -8,7 +8,7 @@ #define ping_cmd "ping -q -c1 -w1 127.0.0.1 > /dev/null"
#include "test_tc_link.skel.h" -#include "tc_helpers.h" +#include "tcx_helpers.h"
void test_ns_tc_opts_basic(void) { @@ -59,7 +59,7 @@ void test_ns_tc_opts_basic(void) ASSERT_EQ(optq.prog_ids[0], id1, "prog_ids[0]"); ASSERT_EQ(optq.prog_ids[1], 0, "prog_ids[1]");
- tc_skel_reset_all_seen(skel); + tcx_skel_reset_all_seen(skel); ASSERT_OK(system(ping_cmd), ping_cmd);
ASSERT_EQ(skel->bss->seen_tc1, true, "seen_tc1"); @@ -84,7 +84,7 @@ void test_ns_tc_opts_basic(void) ASSERT_EQ(optq.prog_ids[0], id2, "prog_ids[0]"); ASSERT_EQ(optq.prog_ids[1], 0, "prog_ids[1]");
- tc_skel_reset_all_seen(skel); + tcx_skel_reset_all_seen(skel); ASSERT_OK(system(ping_cmd), ping_cmd);
ASSERT_EQ(skel->bss->seen_tc1, true, "seen_tc1"); @@ -165,7 +165,7 @@ static void test_tc_opts_before_target(int target) ASSERT_EQ(optq.prog_ids[1], id2, "prog_ids[1]"); ASSERT_EQ(optq.prog_ids[2], 0, "prog_ids[2]");
- tc_skel_reset_all_seen(skel); + tcx_skel_reset_all_seen(skel); ASSERT_OK(system(ping_cmd), ping_cmd);
ASSERT_EQ(skel->bss->seen_tc1, true, "seen_tc1"); @@ -222,7 +222,7 @@ static void test_tc_opts_before_target(int target) ASSERT_EQ(optq.prog_ids[3], id2, "prog_ids[3]"); ASSERT_EQ(optq.prog_ids[4], 0, "prog_ids[4]");
- tc_skel_reset_all_seen(skel); + tcx_skel_reset_all_seen(skel); ASSERT_OK(system(ping_cmd), ping_cmd);
ASSERT_EQ(skel->bss->seen_tc1, true, "seen_tc1"); @@ -317,7 +317,7 @@ static void test_tc_opts_after_target(int target) ASSERT_EQ(optq.prog_ids[1], id2, "prog_ids[1]"); ASSERT_EQ(optq.prog_ids[2], 0, "prog_ids[2]");
- tc_skel_reset_all_seen(skel); + tcx_skel_reset_all_seen(skel); ASSERT_OK(system(ping_cmd), ping_cmd);
ASSERT_EQ(skel->bss->seen_tc1, true, "seen_tc1"); @@ -374,7 +374,7 @@ static void test_tc_opts_after_target(int target) ASSERT_EQ(optq.prog_ids[3], id4, "prog_ids[3]"); ASSERT_EQ(optq.prog_ids[4], 0, "prog_ids[4]");
- tc_skel_reset_all_seen(skel); + tcx_skel_reset_all_seen(skel); ASSERT_OK(system(ping_cmd), ping_cmd);
ASSERT_EQ(skel->bss->seen_tc1, true, "seen_tc1"); @@ -520,7 +520,7 @@ static void test_tc_opts_revision_target(int target) ASSERT_EQ(optq.prog_ids[1], id2, "prog_ids[1]"); ASSERT_EQ(optq.prog_ids[2], 0, "prog_ids[2]");
- tc_skel_reset_all_seen(skel); + tcx_skel_reset_all_seen(skel); ASSERT_OK(system(ping_cmd), ping_cmd);
ASSERT_EQ(skel->bss->seen_tc1, true, "seen_tc1"); @@ -615,7 +615,7 @@ static void test_tc_chain_classic(int target, bool chain_tc_old)
assert_mprog_count(target, 2);
- tc_skel_reset_all_seen(skel); + tcx_skel_reset_all_seen(skel); ASSERT_OK(system(ping_cmd), ping_cmd);
ASSERT_EQ(skel->bss->seen_tc1, true, "seen_tc1"); @@ -628,7 +628,7 @@ static void test_tc_chain_classic(int target, bool chain_tc_old)
assert_mprog_count(target, 1);
- tc_skel_reset_all_seen(skel); + tcx_skel_reset_all_seen(skel); ASSERT_OK(system(ping_cmd), ping_cmd);
ASSERT_EQ(skel->bss->seen_tc1, true, "seen_tc1"); @@ -735,7 +735,7 @@ static void test_tc_opts_replace_target(int target) ASSERT_EQ(optq.prog_attach_flags[1], 0, "prog_flags[1]"); ASSERT_EQ(optq.prog_attach_flags[2], 0, "prog_flags[2]");
- tc_skel_reset_all_seen(skel); + tcx_skel_reset_all_seen(skel); ASSERT_OK(system(ping_cmd), ping_cmd);
ASSERT_EQ(skel->bss->seen_tc1, true, "seen_tc1"); @@ -769,7 +769,7 @@ static void test_tc_opts_replace_target(int target) ASSERT_EQ(optq.prog_ids[1], id1, "prog_ids[1]"); ASSERT_EQ(optq.prog_ids[2], 0, "prog_ids[2]");
- tc_skel_reset_all_seen(skel); + tcx_skel_reset_all_seen(skel); ASSERT_OK(system(ping_cmd), ping_cmd);
ASSERT_EQ(skel->bss->seen_tc1, true, "seen_tc1"); @@ -804,7 +804,7 @@ static void test_tc_opts_replace_target(int target) ASSERT_EQ(optq.prog_ids[1], id1, "prog_ids[1]"); ASSERT_EQ(optq.prog_ids[2], 0, "prog_ids[2]");
- tc_skel_reset_all_seen(skel); + tcx_skel_reset_all_seen(skel); ASSERT_OK(system(ping_cmd), ping_cmd);
ASSERT_EQ(skel->bss->seen_tc1, true, "seen_tc1"); @@ -1084,7 +1084,7 @@ static void test_tc_opts_prepend_target(int target) ASSERT_EQ(optq.prog_ids[1], id1, "prog_ids[1]"); ASSERT_EQ(optq.prog_ids[2], 0, "prog_ids[2]");
- tc_skel_reset_all_seen(skel); + tcx_skel_reset_all_seen(skel); ASSERT_OK(system(ping_cmd), ping_cmd);
ASSERT_EQ(skel->bss->seen_tc1, true, "seen_tc1"); @@ -1125,7 +1125,7 @@ static void test_tc_opts_prepend_target(int target) ASSERT_EQ(optq.prog_ids[3], id1, "prog_ids[3]"); ASSERT_EQ(optq.prog_ids[4], 0, "prog_ids[4]");
- tc_skel_reset_all_seen(skel); + tcx_skel_reset_all_seen(skel); ASSERT_OK(system(ping_cmd), ping_cmd);
ASSERT_EQ(skel->bss->seen_tc1, true, "seen_tc1"); @@ -1224,7 +1224,7 @@ static void test_tc_opts_append_target(int target) ASSERT_EQ(optq.prog_ids[1], id2, "prog_ids[1]"); ASSERT_EQ(optq.prog_ids[2], 0, "prog_ids[2]");
- tc_skel_reset_all_seen(skel); + tcx_skel_reset_all_seen(skel); ASSERT_OK(system(ping_cmd), ping_cmd);
ASSERT_EQ(skel->bss->seen_tc1, true, "seen_tc1"); @@ -1265,7 +1265,7 @@ static void test_tc_opts_append_target(int target) ASSERT_EQ(optq.prog_ids[3], id4, "prog_ids[3]"); ASSERT_EQ(optq.prog_ids[4], 0, "prog_ids[4]");
- tc_skel_reset_all_seen(skel); + tcx_skel_reset_all_seen(skel); ASSERT_OK(system(ping_cmd), ping_cmd);
ASSERT_EQ(skel->bss->seen_tc1, true, "seen_tc1"); @@ -2320,7 +2320,7 @@ static void test_tc_chain_mixed(int target)
assert_mprog_count(target, 1);
- tc_skel_reset_all_seen(skel); + tcx_skel_reset_all_seen(skel); ASSERT_OK(system(ping_cmd), ping_cmd);
ASSERT_EQ(skel->bss->seen_tc4, false, "seen_tc4"); @@ -2340,7 +2340,7 @@ static void test_tc_chain_mixed(int target)
assert_mprog_count(target, 1);
- tc_skel_reset_all_seen(skel); + tcx_skel_reset_all_seen(skel); ASSERT_OK(system(ping_cmd), ping_cmd);
ASSERT_EQ(skel->bss->seen_tc4, true, "seen_tc4"); @@ -2352,7 +2352,7 @@ static void test_tc_chain_mixed(int target) ASSERT_OK(err, "prog_detach"); assert_mprog_count(target, 0);
- tc_skel_reset_all_seen(skel); + tcx_skel_reset_all_seen(skel); ASSERT_OK(system(ping_cmd), ping_cmd);
ASSERT_EQ(skel->bss->seen_tc4, false, "seen_tc4"); diff --git a/tools/testing/selftests/bpf/prog_tests/tc_helpers.h b/tools/testing/selftests/bpf/prog_tests/tcx_helpers.h similarity index 90% rename from tools/testing/selftests/bpf/prog_tests/tc_helpers.h rename to tools/testing/selftests/bpf/prog_tests/tcx_helpers.h index d52a62af77bff9e9bdb54c7bd1061fc162610680..05f5386f4f989f441016eed19bd01d25a5dfd2e0 100644 --- a/tools/testing/selftests/bpf/prog_tests/tc_helpers.h +++ b/tools/testing/selftests/bpf/prog_tests/tcx_helpers.h @@ -1,7 +1,7 @@ /* SPDX-License-Identifier: GPL-2.0 */ /* Copyright (c) 2023 Isovalent */ -#ifndef TC_HELPERS -#define TC_HELPERS +#ifndef TCX_HELPERS +#define TCX_HELPERS #include <test_progs.h>
#ifndef loopback @@ -42,7 +42,7 @@ static inline void assert_mprog_count_ifindex(int ifindex, int target, int expec __assert_mprog_count(target, expected, ifindex); }
-static inline void tc_skel_reset_all_seen(struct test_tc_link *skel) +static inline void tcx_skel_reset_all_seen(struct test_tc_link *skel) { memset(skel->bss, 0, sizeof(*skel->bss)); }
The test_tunnel.c file defines small fonctions to easily attach eBPF programs to tc hooks, either on egress, ingress or both.
Move those helpers in a dedicated file so that other tests can benefit from it.
Signed-off-by: Alexis Lothoré (eBPF Foundation) alexis.lothore@bootlin.com --- tools/testing/selftests/bpf/Makefile | 1 + .../testing/selftests/bpf/prog_tests/test_tunnel.c | 80 +------------------- tools/testing/selftests/bpf/tc_helpers.c | 87 ++++++++++++++++++++++ tools/testing/selftests/bpf/tc_helpers.h | 9 +++ 4 files changed, 98 insertions(+), 79 deletions(-)
diff --git a/tools/testing/selftests/bpf/Makefile b/tools/testing/selftests/bpf/Makefile index f00587d4ede68ef08419bdd545f0ce8e6c3fcfd2..2f248dab922f57e2c14053cb0bdfbb547efe1292 100644 --- a/tools/testing/selftests/bpf/Makefile +++ b/tools/testing/selftests/bpf/Makefile @@ -737,6 +737,7 @@ TRUNNER_EXTRA_SOURCES := test_progs.c \ netlink_helpers.c \ jit_disasm_helpers.c \ io_helpers.c \ + tc_helpers.c \ test_loader.c \ xsk.c \ disasm.c \ diff --git a/tools/testing/selftests/bpf/prog_tests/test_tunnel.c b/tools/testing/selftests/bpf/prog_tests/test_tunnel.c index bae0e9de277d24c34938de30b42f3418ecb6fba8..9e5577c74d60b02a7d39158967112805111b69d8 100644 --- a/tools/testing/selftests/bpf/prog_tests/test_tunnel.c +++ b/tools/testing/selftests/bpf/prog_tests/test_tunnel.c @@ -64,6 +64,7 @@
#include "test_progs.h" #include "network_helpers.h" +#include "tc_helpers.h" #include "test_tunnel_kern.skel.h"
#define IP4_ADDR_VETH0 "172.16.1.100" @@ -534,85 +535,6 @@ static void ping6_dev1(void) close_netns(nstoken); }
-static int attach_tc_prog(int ifindex, int igr_fd, int egr_fd) -{ - DECLARE_LIBBPF_OPTS(bpf_tc_hook, hook, .ifindex = ifindex, - .attach_point = BPF_TC_INGRESS | BPF_TC_EGRESS); - DECLARE_LIBBPF_OPTS(bpf_tc_opts, opts1, .handle = 1, - .priority = 1, .prog_fd = igr_fd); - DECLARE_LIBBPF_OPTS(bpf_tc_opts, opts2, .handle = 1, - .priority = 1, .prog_fd = egr_fd); - int ret; - - ret = bpf_tc_hook_create(&hook); - if (!ASSERT_OK(ret, "create tc hook")) - return ret; - - if (igr_fd >= 0) { - hook.attach_point = BPF_TC_INGRESS; - ret = bpf_tc_attach(&hook, &opts1); - if (!ASSERT_OK(ret, "bpf_tc_attach")) { - bpf_tc_hook_destroy(&hook); - return ret; - } - } - - if (egr_fd >= 0) { - hook.attach_point = BPF_TC_EGRESS; - ret = bpf_tc_attach(&hook, &opts2); - if (!ASSERT_OK(ret, "bpf_tc_attach")) { - bpf_tc_hook_destroy(&hook); - return ret; - } - } - - return 0; -} - -static int generic_attach(const char *dev, int igr_fd, int egr_fd) -{ - int ifindex; - - if (!ASSERT_OK_FD(igr_fd, "check ingress fd")) - return -1; - if (!ASSERT_OK_FD(egr_fd, "check egress fd")) - return -1; - - ifindex = if_nametoindex(dev); - if (!ASSERT_NEQ(ifindex, 0, "get ifindex")) - return -1; - - return attach_tc_prog(ifindex, igr_fd, egr_fd); -} - -static int generic_attach_igr(const char *dev, int igr_fd) -{ - int ifindex; - - if (!ASSERT_OK_FD(igr_fd, "check ingress fd")) - return -1; - - ifindex = if_nametoindex(dev); - if (!ASSERT_NEQ(ifindex, 0, "get ifindex")) - return -1; - - return attach_tc_prog(ifindex, igr_fd, -1); -} - -static int generic_attach_egr(const char *dev, int egr_fd) -{ - int ifindex; - - if (!ASSERT_OK_FD(egr_fd, "check egress fd")) - return -1; - - ifindex = if_nametoindex(dev); - if (!ASSERT_NEQ(ifindex, 0, "get ifindex")) - return -1; - - return attach_tc_prog(ifindex, -1, egr_fd); -} - static void test_vxlan_tunnel(void) { struct test_tunnel_kern *skel = NULL; diff --git a/tools/testing/selftests/bpf/tc_helpers.c b/tools/testing/selftests/bpf/tc_helpers.c new file mode 100644 index 0000000000000000000000000000000000000000..d668e10e3ebad8f8e04862f5c2b3ccd487fe8fa6 --- /dev/null +++ b/tools/testing/selftests/bpf/tc_helpers.c @@ -0,0 +1,87 @@ +// SPDX-License-Identifier: GPL-2.0-only +#define _GNU_SOURCE + +#include <net/if.h> +#include "tc_helpers.h" +#include "test_progs.h" + +static int attach_tc_prog(int ifindex, int igr_fd, int egr_fd) +{ + DECLARE_LIBBPF_OPTS(bpf_tc_hook, hook, .ifindex = ifindex, + .attach_point = BPF_TC_INGRESS | BPF_TC_EGRESS); + DECLARE_LIBBPF_OPTS(bpf_tc_opts, opts1, .handle = 1, + .priority = 1, .prog_fd = igr_fd); + DECLARE_LIBBPF_OPTS(bpf_tc_opts, opts2, .handle = 1, + .priority = 1, .prog_fd = egr_fd); + int ret; + + ret = bpf_tc_hook_create(&hook); + if (!ASSERT_OK(ret, "create tc hook")) + return ret; + + if (igr_fd >= 0) { + hook.attach_point = BPF_TC_INGRESS; + ret = bpf_tc_attach(&hook, &opts1); + if (!ASSERT_OK(ret, "bpf_tc_attach")) { + bpf_tc_hook_destroy(&hook); + return ret; + } + } + + if (egr_fd >= 0) { + hook.attach_point = BPF_TC_EGRESS; + ret = bpf_tc_attach(&hook, &opts2); + if (!ASSERT_OK(ret, "bpf_tc_attach")) { + bpf_tc_hook_destroy(&hook); + return ret; + } + } + + return 0; +} + +int generic_attach(const char *dev, int igr_fd, int egr_fd) +{ + int ifindex; + + if (!ASSERT_OK_FD(igr_fd, "check ingress fd")) + return -1; + if (!ASSERT_OK_FD(egr_fd, "check egress fd")) + return -1; + + ifindex = if_nametoindex(dev); + if (!ASSERT_NEQ(ifindex, 0, "get ifindex")) + return -1; + + return attach_tc_prog(ifindex, igr_fd, egr_fd); +} + +int generic_attach_igr(const char *dev, int igr_fd) +{ + int ifindex; + + if (!ASSERT_OK_FD(igr_fd, "check ingress fd")) + return -1; + + ifindex = if_nametoindex(dev); + if (!ASSERT_NEQ(ifindex, 0, "get ifindex")) + return -1; + + return attach_tc_prog(ifindex, igr_fd, -1); +} + +int generic_attach_egr(const char *dev, int egr_fd) +{ + int ifindex; + + if (!ASSERT_OK_FD(egr_fd, "check egress fd")) + return -1; + + ifindex = if_nametoindex(dev); + if (!ASSERT_NEQ(ifindex, 0, "get ifindex")) + return -1; + + return attach_tc_prog(ifindex, -1, egr_fd); +} + + diff --git a/tools/testing/selftests/bpf/tc_helpers.h b/tools/testing/selftests/bpf/tc_helpers.h new file mode 100644 index 0000000000000000000000000000000000000000..d31abe33f9d80dadd8f829bcf9a68cfd744c3b99 --- /dev/null +++ b/tools/testing/selftests/bpf/tc_helpers.h @@ -0,0 +1,9 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +#ifndef __TC_HELPERS_H +#define __TC_HELPERS_H + + +int generic_attach(const char *dev, int igr_fd, int egr_fd); +int generic_attach_igr(const char *dev, int igr_fd); +int generic_attach_egr(const char *dev, int egr_fd); +#endif
On 10/17/25 7:29 AM, Alexis Lothoré (eBPF Foundation) wrote:
diff --git a/tools/testing/selftests/bpf/tc_helpers.c b/tools/testing/selftests/bpf/tc_helpers.c new file mode 100644 index 0000000000000000000000000000000000000000..d668e10e3ebad8f8e04862f5c2b3ccd487fe8fa6 --- /dev/null +++ b/tools/testing/selftests/bpf/tc_helpers.c @@ -0,0 +1,87 @@ +// SPDX-License-Identifier: GPL-2.0-only +#define _GNU_SOURCE
+#include <net/if.h> +#include "tc_helpers.h" +#include "test_progs.h"
+static int attach_tc_prog(int ifindex, int igr_fd, int egr_fd)
This one looks good but change it to "int tc_prog_attach(const char *dev, int ingress_fd, int egress_fd)". Remove static. Take "const char *dev" as the arg. Add it to network_helpers.[ch] instead of creating a new source file.
+{
- DECLARE_LIBBPF_OPTS(bpf_tc_hook, hook, .ifindex = ifindex,
.attach_point = BPF_TC_INGRESS | BPF_TC_EGRESS);
- DECLARE_LIBBPF_OPTS(bpf_tc_opts, opts1, .handle = 1,
.priority = 1, .prog_fd = igr_fd);
- DECLARE_LIBBPF_OPTS(bpf_tc_opts, opts2, .handle = 1,
.priority = 1, .prog_fd = egr_fd);
- int ret;
- ret = bpf_tc_hook_create(&hook);
- if (!ASSERT_OK(ret, "create tc hook"))
return ret;
- if (igr_fd >= 0) {
hook.attach_point = BPF_TC_INGRESS;
ret = bpf_tc_attach(&hook, &opts1);
if (!ASSERT_OK(ret, "bpf_tc_attach")) {
bpf_tc_hook_destroy(&hook);
return ret;
}
- }
- if (egr_fd >= 0) {
hook.attach_point = BPF_TC_EGRESS;
ret = bpf_tc_attach(&hook, &opts2);
if (!ASSERT_OK(ret, "bpf_tc_attach")) {
bpf_tc_hook_destroy(&hook);
return ret;
}
- }
- return 0;
+}
+int generic_attach(const char *dev, int igr_fd, int egr_fd) +{
- int ifindex;
- if (!ASSERT_OK_FD(igr_fd, "check ingress fd"))
return -1;
- if (!ASSERT_OK_FD(egr_fd, "check egress fd"))
return -1;
- ifindex = if_nametoindex(dev);
- if (!ASSERT_NEQ(ifindex, 0, "get ifindex"))
return -1;
- return attach_tc_prog(ifindex, igr_fd, egr_fd);
+}
+int generic_attach_igr(const char *dev, int igr_fd) +{
- int ifindex;
- if (!ASSERT_OK_FD(igr_fd, "check ingress fd"))
return -1;
- ifindex = if_nametoindex(dev);
- if (!ASSERT_NEQ(ifindex, 0, "get ifindex"))
return -1;
- return attach_tc_prog(ifindex, igr_fd, -1);
+}
+int generic_attach_egr(const char *dev, int egr_fd) +{
- int ifindex;
- if (!ASSERT_OK_FD(egr_fd, "check egress fd"))
return -1;
- ifindex = if_nametoindex(dev);
- if (!ASSERT_NEQ(ifindex, 0, "get ifindex"))
return -1;
- return attach_tc_prog(ifindex, -1, egr_fd);
+}
These three generic_attach_* is a bit overkill for network_helpers.c.
Change test_tunnel.c to directly use tc_prog_attach().
diff --git a/tools/testing/selftests/bpf/tc_helpers.h b/tools/testing/selftests/bpf/tc_helpers.h new file mode 100644 index 0000000000000000000000000000000000000000..d31abe33f9d80dadd8f829bcf9a68cfd744c3b99 --- /dev/null +++ b/tools/testing/selftests/bpf/tc_helpers.h
This new file is not needed also. Use the network_helpers.h.
When trying to run bpf-based encapsulation in a s390x environment, some parts of test_tc_tunnel.bpf.o do not encapsulate correctly the traffic, leading to tests failures. Adding some logs shows for example that packets about to be sent on an interface with the ip6vxlan_eth program attached do not have the expected value 5 in the ip header ihl field, and so are ignored by the program.
This phenomenon appears when trying to cross-compile the selftests, rather than compiling it from a virtualized host: the selftests build system may then wrongly pick some host headers. If <asm/byteorder.h> ends up being picked on the host (and if the host has a endianness different from the target one), it will then expose wrong endianness defines (e.g __LITTLE_ENDIAN_BITFIELD instead of __BIT_ENDIAN_BITFIELD), and it will for example mess up the iphdr structure layout used in the ebpf program.
To prevent this, directly use the vmlinux.h header generated by the selftests build system rather than including directly specific kernel headers. As a consequence, add some missing definitions that are not exposed by vmlinux.h, and adapt the bitfield manipulations to allow building and using the program on both types of platforms.
Signed-off-by: Alexis Lothoré (eBPF Foundation) alexis.lothore@bootlin.com --- tools/testing/selftests/bpf/progs/test_tc_tunnel.c | 61 +++++++++------------- 1 file changed, 26 insertions(+), 35 deletions(-)
diff --git a/tools/testing/selftests/bpf/progs/test_tc_tunnel.c b/tools/testing/selftests/bpf/progs/test_tc_tunnel.c index 404124a9389278949e6952a6f1a50eea9a1bc473..c5a26f7e8ecfd0404960e75f07388fe609522bde 100644 --- a/tools/testing/selftests/bpf/progs/test_tc_tunnel.c +++ b/tools/testing/selftests/bpf/progs/test_tc_tunnel.c @@ -2,23 +2,10 @@
/* In-place tunneling */
-#include <stdbool.h> -#include <string.h> - -#include <linux/stddef.h> -#include <linux/bpf.h> -#include <linux/if_ether.h> -#include <linux/in.h> -#include <linux/ip.h> -#include <linux/ipv6.h> -#include <linux/mpls.h> -#include <linux/tcp.h> -#include <linux/udp.h> -#include <linux/pkt_cls.h> -#include <linux/types.h> +#include <vmlinux.h>
-#include <bpf/bpf_endian.h> #include <bpf/bpf_helpers.h> +#include <bpf/bpf_endian.h> #include "bpf_compiler.h"
#pragma GCC diagnostic ignored "-Waddress-of-packed-member" @@ -27,6 +14,19 @@ static const int cfg_port = 8000;
static const int cfg_udp_src = 20000;
+#define ETH_HLEN 14 +#define TC_ACT_OK 0 +#define TC_ACT_SHOT 2 +#define ETH_P_MPLS_UC 0x8847 +#define ETH_P_IP 0x0800 +#define ETH_P_IPV6 0x86DD +#define ETH_P_TEB 0x6558 + +#define MPLS_LS_S_MASK 0x00000100 +#define BPF_F_ADJ_ROOM_ENCAP_L2(len) \ + (((__u64)len & BPF_ADJ_ROOM_ENCAP_L2_MASK) \ + << BPF_ADJ_ROOM_ENCAP_L2_SHIFT) + #define L2_PAD_SZ (sizeof(struct vxlanhdr) + ETH_HLEN)
#define UDP_PORT 5555 @@ -36,10 +36,9 @@ static const int cfg_udp_src = 20000;
#define EXTPROTO_VXLAN 0x1
-#define VXLAN_N_VID (1u << 24) -#define VXLAN_VNI_MASK bpf_htonl((VXLAN_N_VID - 1) << 8) -#define VXLAN_FLAGS 0x8 -#define VXLAN_VNI 1 +#define VXLAN_FLAGS bpf_htonl(1<<27) +#define VNI_ID 1 +#define VXLAN_VNI bpf_htonl(VNI_ID << 8)
#ifndef NEXTHDR_DEST #define NEXTHDR_DEST 60 @@ -48,12 +47,6 @@ static const int cfg_udp_src = 20000; /* MPLS label 1000 with S bit (last label) set and ttl of 255. */ static const __u32 mpls_label = __bpf_constant_htonl(1000 << 12 | MPLS_LS_S_MASK | 0xff); - -struct vxlanhdr { - __be32 vx_flags; - __be32 vx_vni; -} __attribute__((packed)); - struct gre_hdr { __be16 flags; __be16 protocol; @@ -94,8 +87,8 @@ static __always_inline void set_ipv4_csum(struct iphdr *iph) static __always_inline int __encap_ipv4(struct __sk_buff *skb, __u8 encap_proto, __u16 l2_proto, __u16 ext_proto) { + struct iphdr iph_inner = {0}; __u16 udp_dst = UDP_PORT; - struct iphdr iph_inner; struct v4hdr h_outer; struct tcphdr tcph; int olen, l2_len; @@ -122,7 +115,6 @@ static __always_inline int __encap_ipv4(struct __sk_buff *skb, __u8 encap_proto, return TC_ACT_OK;
/* Derive the IPv4 header fields from the IPv6 header */ - memset(&iph_inner, 0, sizeof(iph_inner)); iph_inner.version = 4; iph_inner.ihl = 5; iph_inner.tot_len = bpf_htons(sizeof(iph6_inner) + @@ -210,7 +202,7 @@ static __always_inline int __encap_ipv4(struct __sk_buff *skb, __u8 encap_proto, struct vxlanhdr *vxlan_hdr = (struct vxlanhdr *)l2_hdr;
vxlan_hdr->vx_flags = VXLAN_FLAGS; - vxlan_hdr->vx_vni = bpf_htonl((VXLAN_VNI & VXLAN_VNI_MASK) << 8); + vxlan_hdr->vx_vni = VXLAN_VNI;
l2_hdr += sizeof(struct vxlanhdr); } @@ -340,7 +332,7 @@ static __always_inline int __encap_ipv6(struct __sk_buff *skb, __u8 encap_proto, struct vxlanhdr *vxlan_hdr = (struct vxlanhdr *)l2_hdr;
vxlan_hdr->vx_flags = VXLAN_FLAGS; - vxlan_hdr->vx_vni = bpf_htonl((VXLAN_VNI & VXLAN_VNI_MASK) << 8); + vxlan_hdr->vx_vni = VXLAN_VNI;
l2_hdr += sizeof(struct vxlanhdr); } @@ -372,8 +364,8 @@ static __always_inline int __encap_ipv6(struct __sk_buff *skb, __u8 encap_proto,
static int encap_ipv6_ipip6(struct __sk_buff *skb) { + struct v6hdr h_outer = {0}; struct iphdr iph_inner; - struct v6hdr h_outer; struct tcphdr tcph; struct ethhdr eth; __u64 flags; @@ -400,13 +392,12 @@ static int encap_ipv6_ipip6(struct __sk_buff *skb) return TC_ACT_SHOT;
/* prepare new outer network header */ - memset(&h_outer.ip, 0, sizeof(h_outer.ip)); h_outer.ip.version = 6; h_outer.ip.hop_limit = iph_inner.ttl; - h_outer.ip.saddr.s6_addr[1] = 0xfd; - h_outer.ip.saddr.s6_addr[15] = 1; - h_outer.ip.daddr.s6_addr[1] = 0xfd; - h_outer.ip.daddr.s6_addr[15] = 2; + h_outer.ip.saddr.in6_u.u6_addr8[1] = 0xfd; + h_outer.ip.saddr.in6_u.u6_addr8[15] = 1; + h_outer.ip.daddr.in6_u.u6_addr8[1] = 0xfd; + h_outer.ip.daddr.in6_u.u6_addr8[15] = 2; h_outer.ip.payload_len = iph_inner.tot_len; h_outer.ip.nexthdr = IPPROTO_IPIP;
On 10/17/25 7:29 AM, Alexis Lothoré (eBPF Foundation) wrote:
+#define ETH_HLEN 14 +#define TC_ACT_OK 0 +#define TC_ACT_SHOT 2 +#define ETH_P_MPLS_UC 0x8847 +#define ETH_P_IP 0x0800 +#define ETH_P_IPV6 0x86DD +#define ETH_P_TEB 0x6558
+#define MPLS_LS_S_MASK 0x00000100 +#define BPF_F_ADJ_ROOM_ENCAP_L2(len) \
- (((__u64)len & BPF_ADJ_ROOM_ENCAP_L2_MASK) \
<< BPF_ADJ_ROOM_ENCAP_L2_SHIFT)
Some of them (e.g. TC_ACT_OK) should be already in bpf_tracing_net.h, so include that header instead. Not sure the remaining ones (e.g. MPLS) will be very useful, so I would leave it here for now instead of adding them to bpf_tracing_net.h.
The test_tc_tunnel.sh script checks that a large variety of tunneling mechanisms handled by the kernel can be handled as well by eBPF programs. While this test shares similarities with test_tunnel.c (which is already integrated in test_progs), those are testing slightly different things: - test_tunnel.c creates a tunnel interface, and then get and set tunnel keys in packet metadata, from BPF programs. - test_tc_tunnels.sh manually parses/crafts packets content
Bring the tests covered by test_tc_tunnel.sh into the test_progs framework, by creating a dedicated test_tc_tunnel.sh. This new test defines a "generic" runner which, for each test configuration: - will bring the relevant veth pair, each of those isolated in a dedicated namespace - will check that traffic will fail if there is only an encapsulating program attached to one veth egress - will check that traffic succeed if we enable some decapsulation module on kernel side - will check that traffic still succeeds if we replace the kernel decapsulation with some eBPF ingress decapsulation.
Example of the new test execution:
# ./test_progs -a tc_tunnel #447/1 tc_tunnel/ipip_none:OK #447/2 tc_tunnel/ipip6_none:OK #447/3 tc_tunnel/ip6tnl_none:OK #447/4 tc_tunnel/sit_none:OK #447/5 tc_tunnel/vxlan_eth:OK #447/6 tc_tunnel/ip6vxlan_eth:OK #447/7 tc_tunnel/gre_none:OK #447/8 tc_tunnel/gre_eth:OK #447/9 tc_tunnel/gre_mpls:OK #447/10 tc_tunnel/ip6gre_none:OK #447/11 tc_tunnel/ip6gre_eth:OK #447/12 tc_tunnel/ip6gre_mpls:OK #447/13 tc_tunnel/udp_none:OK #447/14 tc_tunnel/udp_eth:OK #447/15 tc_tunnel/udp_mpls:OK #447/16 tc_tunnel/ip6udp_none:OK #447/17 tc_tunnel/ip6udp_eth:OK #447/18 tc_tunnel/ip6udp_mpls:OK #447 tc_tunnel:OK Summary: 1/18 PASSED, 0 SKIPPED, 0 FAILED
Signed-off-by: Alexis Lothoré (eBPF Foundation) alexis.lothore@bootlin.com --- .../selftests/bpf/prog_tests/test_tc_tunnel.c | 684 +++++++++++++++++++++ tools/testing/selftests/bpf/progs/test_tc_tunnel.c | 38 +- 2 files changed, 703 insertions(+), 19 deletions(-)
diff --git a/tools/testing/selftests/bpf/prog_tests/test_tc_tunnel.c b/tools/testing/selftests/bpf/prog_tests/test_tc_tunnel.c new file mode 100644 index 0000000000000000000000000000000000000000..294cf4fb1545e71a6ee8da8544fed4489fd8ff12 --- /dev/null +++ b/tools/testing/selftests/bpf/prog_tests/test_tc_tunnel.c @@ -0,0 +1,684 @@ +// SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause + +/* + * End-to-end eBPF tunnel test suite + * The file tests BPF network tunnels implementation. For each tunnel + * type, the test validates that: + * - basic communication can first be established between the two veths + * - when adding a BPF-based encapsulation on client egress, it now fails + * to communicate with the server + * - when adding a kernel-based decapsulation on server ingress, client + * can now connect + * - when replacing the kernel-based decapsulation with a BPF-based one, + * the client can still connect + */ + +#include <stdio.h> +#include <unistd.h> +#include <fcntl.h> +#include <sys/socket.h> +#include <bpf/libbpf.h> + +#include "test_progs.h" +#include "network_helpers.h" +#include "tc_helpers.h" +#include "test_tc_tunnel.skel.h" + +#define SERVER_NS "tc-tunnel-server-ns" +#define CLIENT_NS "tc-tunnel-client-ns" +#define MAC_ADDR_VETH1 "00:11:22:33:44:55" +#define IP4_ADDR_VETH1 "192.168.1.1" +#define IP6_ADDR_VETH1 "fd::1" +#define MAC_ADDR_VETH2 "66:77:88:99:AA:BB" +#define IP4_ADDR_VETH2 "192.168.1.2" +#define IP6_ADDR_VETH2 "fd::2" + +#define TEST_NAME_MAX_LEN 64 +#define PROG_NAME_MAX_LEN 64 +#define TUNNEL_ARGS_MAX_LEN 128 +#define BUFFER_LEN 2000 +#define DEFAULT_TEST_DATA_SIZE 100 +#define GSO_TEST_DATA_SIZE BUFFER_LEN + +#define TIMEOUT_MS 1000 +#define TEST_PORT 8000 +#define UDP_PORT 5555 +#define MPLS_UDP_PORT 6635 +#define FOU_MPLS_PROTO 137 +#define VXLAN_ID 1 +#define VXLAN_PORT 8472 +#define MPLS_TABLE_ENTRIES_COUNT 65536 + +static char tx_buffer[BUFFER_LEN], rx_buffer[BUFFER_LEN]; + +struct subtest_cfg { + char *ebpf_tun_type; + char *iproute_tun_type; + char *mac_tun_type; + int ipproto; + void (*extra_decap_mod_args_cb)(struct subtest_cfg *cfg, char *dst); + bool tunnel_need_veth_mac; + bool configure_fou_rx_port; + char *tmode; + bool expect_kern_decap_failure; + bool configure_mpls; + bool test_gso; + char *tunnel_client_addr; + char *tunnel_server_addr; + char name[TEST_NAME_MAX_LEN]; + char *server_addr; + int client_egress_prog_fd; + int server_ingress_prog_fd; + char extra_decap_mod_args[TUNNEL_ARGS_MAX_LEN]; + int *server_fd; +}; + +struct connection { + int client_fd; + int server_fd; +}; + +static int build_subtest_name(struct subtest_cfg *cfg, char *dst, size_t size) +{ + int ret; + + ret = snprintf(dst, size, "%s_%s", cfg->ebpf_tun_type, + cfg->mac_tun_type); + + return ret < 0 ? ret : 0; +} + +static int set_subtest_progs(struct subtest_cfg *cfg, struct test_tc_tunnel *skel) +{ + char prog_name[PROG_NAME_MAX_LEN]; + struct bpf_program *prog; + int ret; + + + ret = snprintf(prog_name, PROG_NAME_MAX_LEN, "__encap_"); + if (ret < 0) + return ret; + ret = build_subtest_name(cfg, prog_name + ret, PROG_NAME_MAX_LEN - ret); + if (ret < 0) + return ret; + prog = bpf_object__find_program_by_name(skel->obj, prog_name); + if (!prog) + return -1; + + cfg->client_egress_prog_fd = bpf_program__fd(prog); + cfg->server_ingress_prog_fd = bpf_program__fd(skel->progs.decap_f); + return 0; +} + +static void set_subtest_addresses(struct subtest_cfg *cfg) +{ + if (cfg->ipproto == 6) + cfg->server_addr = IP6_ADDR_VETH2; + else + cfg->server_addr = IP4_ADDR_VETH2; + + /* Some specific tunnel types need specific addressing, it then + * has been already set in the configuration table. Otherwise, + * deduce the relevant addressing from the ipproto + */ + if (cfg->tunnel_client_addr && cfg->tunnel_server_addr) + return; + + if (cfg->ipproto == 6) { + cfg->tunnel_client_addr = IP6_ADDR_VETH1; + cfg->tunnel_server_addr = IP6_ADDR_VETH2; + } else { + cfg->tunnel_client_addr = IP4_ADDR_VETH1; + cfg->tunnel_server_addr = IP4_ADDR_VETH2; + } +} + +static int run_server(struct subtest_cfg *cfg) +{ + struct nstoken *nstoken = open_netns(SERVER_NS); + int family = cfg->ipproto == 6 ? AF_INET6 : AF_INET; + + cfg->server_fd = start_reuseport_server(family, SOCK_STREAM, + cfg->server_addr, TEST_PORT, + TIMEOUT_MS, 1); + close_netns(nstoken); + if (!ASSERT_NEQ(cfg->server_fd, NULL, "start server")) + return -1; + + return 0; +} + +static void stop_server(struct subtest_cfg *cfg) +{ + struct nstoken *nstoken = open_netns(SERVER_NS); + + close(*cfg->server_fd); + cfg->server_fd = NULL; + close_netns(nstoken); +} + +static int check_server_rx_data(struct subtest_cfg *cfg, + struct connection *conn, int len) +{ + struct nstoken *nstoken = open_netns(SERVER_NS); + int err; + + memset(rx_buffer, 0, BUFFER_LEN); + err = recv(conn->server_fd, rx_buffer, len, 0); + close_netns(nstoken); + if (!ASSERT_EQ(err, len, "check rx data len")) + return 1; + if (!ASSERT_MEMEQ(tx_buffer, rx_buffer, len, "check received data")) + return 1; + return 0; +} + +static struct connection *connect_client_to_server(struct subtest_cfg *cfg) +{ + struct network_helper_opts opts = {.timeout_ms = 500}; + int family = cfg->ipproto == 6 ? AF_INET6 : AF_INET; + struct nstoken *nstoken = open_netns(CLIENT_NS); + struct connection *conn = NULL; + int client_fd, server_fd; + + client_fd = connect_to_addr_str(family, SOCK_STREAM, cfg->server_addr, + TEST_PORT, &opts); + close_netns(nstoken); + + if (client_fd < 0) + return NULL; + + nstoken = open_netns(SERVER_NS); + server_fd = accept(*cfg->server_fd, NULL, NULL); + close_netns(nstoken); + if (server_fd < 0) + return NULL; + + conn = malloc(sizeof(struct connection)); + if (conn) { + conn->server_fd = server_fd; + conn->client_fd = client_fd; + } + + return conn; +} + +static void disconnect_client_from_server(struct subtest_cfg *cfg, + struct connection *conn) +{ + struct nstoken *nstoken; + + nstoken = open_netns(SERVER_NS); + close(conn->server_fd); + close_netns(nstoken); + nstoken = open_netns(CLIENT_NS); + close(conn->client_fd); + close_netns(nstoken); + free(conn); +} + +static int send_and_test_data(struct subtest_cfg *cfg, bool must_succeed) +{ + struct nstoken *nstoken = NULL; + struct connection *conn; + int err, res = -1; + + conn = connect_client_to_server(cfg); + if (!must_succeed && !ASSERT_EQ(conn, NULL, "connection that must fail")) + goto end; + else if (!must_succeed) + return 0; + + if (!ASSERT_NEQ(conn, NULL, "connection that must succeed")) + return 1; + + nstoken = open_netns(CLIENT_NS); + err = send(conn->client_fd, tx_buffer, DEFAULT_TEST_DATA_SIZE, 0); + close_netns(nstoken); + if (!ASSERT_EQ(err, DEFAULT_TEST_DATA_SIZE, "send data from client")) + goto end; + if (check_server_rx_data(cfg, conn, DEFAULT_TEST_DATA_SIZE)) + goto end; + + if (!cfg->test_gso) { + res = 0; + goto end; + } + + nstoken = open_netns(CLIENT_NS); + err = send(conn->client_fd, tx_buffer, GSO_TEST_DATA_SIZE, 0); + close_netns(nstoken); + if (!ASSERT_EQ(err, GSO_TEST_DATA_SIZE, "send (large) data from client")) + goto end; + if (check_server_rx_data(cfg, conn, DEFAULT_TEST_DATA_SIZE)) + goto end; + + res = 0; +end: + disconnect_client_from_server(cfg, conn); + return res; +} +static void vxlan_decap_mod_args_cb(struct subtest_cfg *cfg, char *dst) +{ + snprintf(dst, TUNNEL_ARGS_MAX_LEN, "id %d dstport %d udp6zerocsumrx", + VXLAN_ID, VXLAN_PORT); +} + +static void udp_decap_mod_args_cb(struct subtest_cfg *cfg, char *dst) +{ + bool is_mpls = !strcmp(cfg->mac_tun_type, "mpls"); + + snprintf(dst, TUNNEL_ARGS_MAX_LEN, + "encap fou encap-sport auto encap-dport %d", + is_mpls ? MPLS_UDP_PORT : UDP_PORT); +} + +static int configure_fou_rx_port(struct subtest_cfg *cfg, bool add) +{ + bool is_mpls = strcmp(cfg->mac_tun_type, "mpls") == 0; + int fou_proto; + + if (is_mpls) + fou_proto = FOU_MPLS_PROTO; + else + fou_proto = cfg->ipproto == 6 ? 41 : 4; + + SYS(fail, "ip fou %s port %d ipproto %d%s", add ? "add" : "del", + is_mpls ? MPLS_UDP_PORT : UDP_PORT, fou_proto, + cfg->ipproto == 6 ? " -6" : ""); + + return 0; +fail: + return 1; +} + +static int add_fou_rx_port(struct subtest_cfg *cfg) +{ + return configure_fou_rx_port(cfg, true); +} + +static int del_fou_rx_port(struct subtest_cfg *cfg) +{ + return configure_fou_rx_port(cfg, false); +} + +static int update_tunnel_intf_addr(struct subtest_cfg *cfg) +{ + SYS(fail, "ip link set dev testtun0 address " MAC_ADDR_VETH2); + return 0; +fail: + return -1; +} + +static int configure_kernel_for_mpls(struct subtest_cfg *cfg) +{ + SYS(fail, "sysctl -qw net.mpls.platform_labels=%d", + MPLS_TABLE_ENTRIES_COUNT); + SYS(fail, "ip -f mpls route add 1000 dev lo"); + SYS(fail, "ip link set lo up"); + SYS(fail, "sysctl -qw net.mpls.conf.testtun0.input=1"); + SYS(fail, "sysctl -qw net.ipv4.conf.lo.rp_filter=0"); + return 0; +fail: + return -1; +} + +static int configure_encapsulation(struct subtest_cfg *cfg) +{ + struct nstoken *nstoken = open_netns(CLIENT_NS); + int ret; + + ret = generic_attach_egr("veth1", cfg->client_egress_prog_fd); + close_netns(nstoken); + + return ret; +} + +static int configure_kernel_decapsulation(struct subtest_cfg *cfg) +{ + struct nstoken *nstoken = open_netns(SERVER_NS); + + if (cfg->configure_fou_rx_port && !ASSERT_OK( + add_fou_rx_port(cfg), "configure FOU RX port")) + goto fail; + SYS(fail, "ip link add name testtun0 type %s %s remote %s local %s %s", + cfg->iproute_tun_type, cfg->tmode ? cfg->tmode : "", + cfg->tunnel_client_addr, cfg->tunnel_server_addr, + cfg->extra_decap_mod_args); + if (cfg->tunnel_need_veth_mac && + !ASSERT_OK(update_tunnel_intf_addr(cfg), "update testtun0 mac")) + goto fail; + if (cfg->configure_mpls && + (!ASSERT_OK(configure_kernel_for_mpls(cfg), + "configure MPLS decap"))) + goto fail; + SYS(fail, "sysctl -qw net.ipv4.conf.all.rp_filter=0"); + SYS(fail, "sysctl -qw net.ipv4.conf.testtun0.rp_filter=0"); + SYS(fail, "ip link set dev testtun0 up"); + close_netns(nstoken); + return 0; +fail: + close_netns(nstoken); + return -1; +} + +static int configure_ebpf_decapsulation(struct subtest_cfg *cfg) +{ + struct nstoken *nstoken = open_netns(SERVER_NS); + + SYS(fail, "ip link del testtun0"); + if (!ASSERT_OK(generic_attach_igr("veth2", cfg->server_ingress_prog_fd), + "attach_program")) + goto fail; + close_netns(nstoken); + return 0; +fail: + close_netns(nstoken); + return -1; + +} + +static void run_test(struct subtest_cfg *cfg) +{ + if (!ASSERT_OK(run_server(cfg), "run server")) + goto fail; + + // Basic communication must work + if (!ASSERT_OK(send_and_test_data(cfg, true), "connect without any encap")) + goto fail; + + // Attach encapsulation program to client, communication must fail + if (!ASSERT_OK(configure_encapsulation(cfg), "configure encapsulation")) + return; + if (!ASSERT_OK(send_and_test_data(cfg, false), "connect with encap prog only")) + goto fail; + + /* Insert kernel decap module, connection must succeed */ + if (!ASSERT_OK(configure_kernel_decapsulation(cfg), "configure kernel decapsulation")) + goto fail; + if (!ASSERT_OK(send_and_test_data(cfg, !cfg->expect_kern_decap_failure), + "connect with encap prog and kern decap")) + goto fail; + + // Replace kernel module with BPF decap, test must pass + if (!ASSERT_OK(configure_ebpf_decapsulation(cfg), "configure ebpf decapsulation")) + goto fail; + ASSERT_OK(send_and_test_data(cfg, true), "connect with encap and decap progs"); + +fail: + stop_server(cfg); +} + +static int setup(void) +{ + struct nstoken *nstoken = NULL; + int fd, err; + + fd = open("/dev/urandom", O_RDONLY); + if (!ASSERT_OK_FD(fd, "open urandom")) + goto fail; + err = read(fd, tx_buffer, BUFFER_LEN); + close(fd); + + if (!ASSERT_EQ(err, BUFFER_LEN, "read random bytes")) + goto fail; + + /* Configure the testing network */ + if (!ASSERT_OK(make_netns(CLIENT_NS), "create client ns") || + !ASSERT_OK(make_netns(SERVER_NS), "create server ns")) + goto fail; + + nstoken = open_netns(CLIENT_NS); + SYS(fail, "ip link add %s type veth peer name %s", + "veth1 mtu 1500 netns " CLIENT_NS " address " MAC_ADDR_VETH1, + "veth2 mtu 1500 netns " SERVER_NS " address " MAC_ADDR_VETH2); + SYS(fail, "ethtool -K veth1 tso off"); + SYS(fail, "ip link set veth1 up"); + close_netns(nstoken); + nstoken = open_netns(SERVER_NS); + SYS(fail, "ip link set veth2 up"); + close_netns(nstoken); + + return 0; +fail: + close_netns(nstoken); + return 1; +} +static int subtest_setup(struct test_tc_tunnel *skel, struct subtest_cfg *cfg) +{ + struct nstoken *nstoken; + + set_subtest_addresses(cfg); + if (!ASSERT_OK(set_subtest_progs(cfg, skel), + "find subtest progs")) + return -1; + if (cfg->extra_decap_mod_args_cb) + cfg->extra_decap_mod_args_cb(cfg, cfg->extra_decap_mod_args); + + nstoken = open_netns(CLIENT_NS); + SYS(fail, "ip -4 addr add " IP4_ADDR_VETH1 "/24 dev veth1"); + SYS(fail, "ip -4 route flush table main"); + SYS(fail, "ip -4 route add " IP4_ADDR_VETH2 " mtu 1450 dev veth1"); + SYS(fail, "ip -6 addr add " IP6_ADDR_VETH1 "/64 dev veth1 nodad"); + SYS(fail, "ip -6 route flush table main"); + SYS(fail, "ip -6 route add " IP6_ADDR_VETH2 " mtu 1430 dev veth1"); + close_netns(nstoken); + + + nstoken = open_netns(SERVER_NS); + SYS(fail, "ip -4 addr add " IP4_ADDR_VETH2 "/24 dev veth2"); + SYS(fail, "ip -6 addr add " IP6_ADDR_VETH2 "/64 dev veth2 nodad"); + close_netns(nstoken); + + return 0; +fail: + close_netns(nstoken); + return -1; +} + +static void subtest_cleanup(struct subtest_cfg *cfg) +{ + struct nstoken *nstoken; + + nstoken = open_netns(CLIENT_NS); + SYS_NOFAIL("tc qdisc delete dev veth1 parent ffff:fff1"); + SYS_NOFAIL("ip a flush veth1"); + close_netns(nstoken); + nstoken = open_netns(SERVER_NS); + SYS_NOFAIL("ip link del testtun0"); + SYS_NOFAIL("tc qdisc delete dev veth2 parent ffff:fff1"); + SYS_NOFAIL("ip a flush veth2"); + if (cfg->configure_mpls) + SYS_NOFAIL("ip -f mpls route del 1000 dev lo"); + if (cfg->configure_fou_rx_port) + del_fou_rx_port(cfg); + + close_netns(nstoken); +} + +static void cleanup(void) +{ + remove_netns(CLIENT_NS); + remove_netns(SERVER_NS); +} + +struct subtest_cfg subtests_cfg[] = { + { + .ebpf_tun_type = "ipip", + .mac_tun_type = "none", + .iproute_tun_type = "ipip", + .ipproto = 4, + }, + { + .ebpf_tun_type = "ipip6", + .mac_tun_type = "none", + .iproute_tun_type = "ip6tnl", + .ipproto = 4, + .tunnel_client_addr = IP6_ADDR_VETH1, + .tunnel_server_addr = IP6_ADDR_VETH2, + }, + { + .ebpf_tun_type = "ip6tnl", + .iproute_tun_type = "ip6tnl", + .mac_tun_type = "none", + .ipproto = 6, + }, + { + .mac_tun_type = "none", + .ebpf_tun_type = "sit", + .iproute_tun_type = "sit", + .ipproto = 6, + .tunnel_client_addr = IP4_ADDR_VETH1, + .tunnel_server_addr = IP4_ADDR_VETH2, + }, + { + .ebpf_tun_type = "vxlan", + .mac_tun_type = "eth", + .iproute_tun_type = "vxlan", + .ipproto = 4, + .extra_decap_mod_args_cb = vxlan_decap_mod_args_cb, + .tunnel_need_veth_mac = true + }, + { + .ebpf_tun_type = "ip6vxlan", + .mac_tun_type = "eth", + .iproute_tun_type = "vxlan", + .ipproto = 6, + .extra_decap_mod_args_cb = vxlan_decap_mod_args_cb, + .tunnel_need_veth_mac = true + }, + { + .ebpf_tun_type = "gre", + .mac_tun_type = "none", + .iproute_tun_type = "gre", + .ipproto = 4, + .test_gso = true + }, + { + .ebpf_tun_type = "gre", + .mac_tun_type = "eth", + .iproute_tun_type = "gretap", + .ipproto = 4, + .tunnel_need_veth_mac = true, + .test_gso = true + }, + { + .ebpf_tun_type = "gre", + .mac_tun_type = "mpls", + .iproute_tun_type = "gre", + .ipproto = 4, + .configure_mpls = true, + .test_gso = true + }, + { + .ebpf_tun_type = "ip6gre", + .mac_tun_type = "none", + .iproute_tun_type = "ip6gre", + .ipproto = 6, + .test_gso = true, + }, + { + .ebpf_tun_type = "ip6gre", + .mac_tun_type = "eth", + .iproute_tun_type = "ip6gretap", + .ipproto = 6, + .tunnel_need_veth_mac = true, + .test_gso = true + }, + { + .ebpf_tun_type = "ip6gre", + .mac_tun_type = "mpls", + .iproute_tun_type = "ip6gre", + .ipproto = 6, + .configure_mpls = true, + .test_gso = true + }, + { + .ebpf_tun_type = "udp", + .mac_tun_type = "none", + .iproute_tun_type = "ipip", + .ipproto = 4, + .extra_decap_mod_args_cb = udp_decap_mod_args_cb, + .configure_fou_rx_port = true, + .test_gso = true + }, + { + .ebpf_tun_type = "udp", + .mac_tun_type = "eth", + .iproute_tun_type = "ipip", + .ipproto = 4, + .extra_decap_mod_args_cb = udp_decap_mod_args_cb, + .configure_fou_rx_port = true, + .expect_kern_decap_failure = true, + .test_gso = true + }, + { + .ebpf_tun_type = "udp", + .mac_tun_type = "mpls", + .iproute_tun_type = "ipip", + .ipproto = 4, + .extra_decap_mod_args_cb = udp_decap_mod_args_cb, + .configure_fou_rx_port = true, + .tmode = "mode any ttl 255", + .configure_mpls = true, + .test_gso = true + }, + { + .ebpf_tun_type = "ip6udp", + .mac_tun_type = "none", + .iproute_tun_type = "ip6tnl", + .ipproto = 6, + .extra_decap_mod_args_cb = udp_decap_mod_args_cb, + .configure_fou_rx_port = true, + .test_gso = true + }, + { + .ebpf_tun_type = "ip6udp", + .mac_tun_type = "eth", + .iproute_tun_type = "ip6tnl", + .ipproto = 6, + .extra_decap_mod_args_cb = udp_decap_mod_args_cb, + .configure_fou_rx_port = true, + .expect_kern_decap_failure = true, + .test_gso = true + }, + { + .ebpf_tun_type = "ip6udp", + .mac_tun_type = "mpls", + .iproute_tun_type = "ip6tnl", + .ipproto = 6, + .extra_decap_mod_args_cb = udp_decap_mod_args_cb, + .configure_fou_rx_port = true, + .tmode = "mode any ttl 255", + .expect_kern_decap_failure = true, + .test_gso = true + }, +}; + +int subtests_count = sizeof(subtests_cfg)/sizeof(struct subtest_cfg); + + +void test_tc_tunnel(void) +{ + struct test_tc_tunnel *skel; + struct subtest_cfg *cfg; + int i, ret; + + skel = test_tc_tunnel__open_and_load(); + if (!ASSERT_OK_PTR(skel, "skel open and load")) + return; + + if (!ASSERT_OK(setup(), "global setup")) + return; + + for (i = 0; i < subtests_count; i++) { + cfg = &subtests_cfg[i]; + ret = build_subtest_name(cfg, cfg->name, TEST_NAME_MAX_LEN); + if (ret < 0 || !test__start_subtest(cfg->name)) + continue; + subtest_setup(skel, cfg); + run_test(cfg); + subtest_cleanup(cfg); + } + cleanup(); +} diff --git a/tools/testing/selftests/bpf/progs/test_tc_tunnel.c b/tools/testing/selftests/bpf/progs/test_tc_tunnel.c index c5a26f7e8ecfd0404960e75f07388fe609522bde..6f4736b90ee49b01968f50713dce4d3cace83a57 100644 --- a/tools/testing/selftests/bpf/progs/test_tc_tunnel.c +++ b/tools/testing/selftests/bpf/progs/test_tc_tunnel.c @@ -422,7 +422,7 @@ static __always_inline int encap_ipv6(struct __sk_buff *skb, __u8 encap_proto, return __encap_ipv6(skb, encap_proto, l2_proto, 0); }
-SEC("encap_ipip_none") +SEC("tc") int __encap_ipip_none(struct __sk_buff *skb) { if (skb->protocol == __bpf_constant_htons(ETH_P_IP)) @@ -431,7 +431,7 @@ int __encap_ipip_none(struct __sk_buff *skb) return TC_ACT_OK; }
-SEC("encap_gre_none") +SEC("tc") int __encap_gre_none(struct __sk_buff *skb) { if (skb->protocol == __bpf_constant_htons(ETH_P_IP)) @@ -440,7 +440,7 @@ int __encap_gre_none(struct __sk_buff *skb) return TC_ACT_OK; }
-SEC("encap_gre_mpls") +SEC("tc") int __encap_gre_mpls(struct __sk_buff *skb) { if (skb->protocol == __bpf_constant_htons(ETH_P_IP)) @@ -449,7 +449,7 @@ int __encap_gre_mpls(struct __sk_buff *skb) return TC_ACT_OK; }
-SEC("encap_gre_eth") +SEC("tc") int __encap_gre_eth(struct __sk_buff *skb) { if (skb->protocol == __bpf_constant_htons(ETH_P_IP)) @@ -458,7 +458,7 @@ int __encap_gre_eth(struct __sk_buff *skb) return TC_ACT_OK; }
-SEC("encap_udp_none") +SEC("tc") int __encap_udp_none(struct __sk_buff *skb) { if (skb->protocol == __bpf_constant_htons(ETH_P_IP)) @@ -467,7 +467,7 @@ int __encap_udp_none(struct __sk_buff *skb) return TC_ACT_OK; }
-SEC("encap_udp_mpls") +SEC("tc") int __encap_udp_mpls(struct __sk_buff *skb) { if (skb->protocol == __bpf_constant_htons(ETH_P_IP)) @@ -476,7 +476,7 @@ int __encap_udp_mpls(struct __sk_buff *skb) return TC_ACT_OK; }
-SEC("encap_udp_eth") +SEC("tc") int __encap_udp_eth(struct __sk_buff *skb) { if (skb->protocol == __bpf_constant_htons(ETH_P_IP)) @@ -485,7 +485,7 @@ int __encap_udp_eth(struct __sk_buff *skb) return TC_ACT_OK; }
-SEC("encap_vxlan_eth") +SEC("tc") int __encap_vxlan_eth(struct __sk_buff *skb) { if (skb->protocol == __bpf_constant_htons(ETH_P_IP)) @@ -496,7 +496,7 @@ int __encap_vxlan_eth(struct __sk_buff *skb) return TC_ACT_OK; }
-SEC("encap_sit_none") +SEC("tc") int __encap_sit_none(struct __sk_buff *skb) { if (skb->protocol == __bpf_constant_htons(ETH_P_IPV6)) @@ -505,7 +505,7 @@ int __encap_sit_none(struct __sk_buff *skb) return TC_ACT_OK; }
-SEC("encap_ip6tnl_none") +SEC("tc") int __encap_ip6tnl_none(struct __sk_buff *skb) { if (skb->protocol == __bpf_constant_htons(ETH_P_IPV6)) @@ -514,7 +514,7 @@ int __encap_ip6tnl_none(struct __sk_buff *skb) return TC_ACT_OK; }
-SEC("encap_ipip6_none") +SEC("tc") int __encap_ipip6_none(struct __sk_buff *skb) { if (skb->protocol == __bpf_constant_htons(ETH_P_IP)) @@ -523,7 +523,7 @@ int __encap_ipip6_none(struct __sk_buff *skb) return TC_ACT_OK; }
-SEC("encap_ip6gre_none") +SEC("tc") int __encap_ip6gre_none(struct __sk_buff *skb) { if (skb->protocol == __bpf_constant_htons(ETH_P_IPV6)) @@ -532,7 +532,7 @@ int __encap_ip6gre_none(struct __sk_buff *skb) return TC_ACT_OK; }
-SEC("encap_ip6gre_mpls") +SEC("tc") int __encap_ip6gre_mpls(struct __sk_buff *skb) { if (skb->protocol == __bpf_constant_htons(ETH_P_IPV6)) @@ -541,7 +541,7 @@ int __encap_ip6gre_mpls(struct __sk_buff *skb) return TC_ACT_OK; }
-SEC("encap_ip6gre_eth") +SEC("tc") int __encap_ip6gre_eth(struct __sk_buff *skb) { if (skb->protocol == __bpf_constant_htons(ETH_P_IPV6)) @@ -550,7 +550,7 @@ int __encap_ip6gre_eth(struct __sk_buff *skb) return TC_ACT_OK; }
-SEC("encap_ip6udp_none") +SEC("tc") int __encap_ip6udp_none(struct __sk_buff *skb) { if (skb->protocol == __bpf_constant_htons(ETH_P_IPV6)) @@ -559,7 +559,7 @@ int __encap_ip6udp_none(struct __sk_buff *skb) return TC_ACT_OK; }
-SEC("encap_ip6udp_mpls") +SEC("tc") int __encap_ip6udp_mpls(struct __sk_buff *skb) { if (skb->protocol == __bpf_constant_htons(ETH_P_IPV6)) @@ -568,7 +568,7 @@ int __encap_ip6udp_mpls(struct __sk_buff *skb) return TC_ACT_OK; }
-SEC("encap_ip6udp_eth") +SEC("tc") int __encap_ip6udp_eth(struct __sk_buff *skb) { if (skb->protocol == __bpf_constant_htons(ETH_P_IPV6)) @@ -577,7 +577,7 @@ int __encap_ip6udp_eth(struct __sk_buff *skb) return TC_ACT_OK; }
-SEC("encap_ip6vxlan_eth") +SEC("tc") int __encap_ip6vxlan_eth(struct __sk_buff *skb) { if (skb->protocol == __bpf_constant_htons(ETH_P_IPV6)) @@ -684,7 +684,7 @@ static int decap_ipv6(struct __sk_buff *skb) iph_outer.nexthdr); }
-SEC("decap") +SEC("tc") int decap_f(struct __sk_buff *skb) { switch (skb->protocol) {
On 10/17/25 7:29 AM, Alexis Lothoré (eBPF Foundation) wrote:
The test_tc_tunnel.sh script checks that a large variety of tunneling mechanisms handled by the kernel can be handled as well by eBPF programs. While this test shares similarities with test_tunnel.c (which is already integrated in test_progs), those are testing slightly different things:
- test_tunnel.c creates a tunnel interface, and then get and set tunnel keys in packet metadata, from BPF programs.
- test_tc_tunnels.sh manually parses/crafts packets content
Bring the tests covered by test_tc_tunnel.sh into the test_progs framework, by creating a dedicated test_tc_tunnel.sh. This new test defines a "generic" runner which, for each test configuration:
- will bring the relevant veth pair, each of those isolated in a dedicated namespace
- will check that traffic will fail if there is only an encapsulating program attached to one veth egress
- will check that traffic succeed if we enable some decapsulation module on kernel side
- will check that traffic still succeeds if we replace the kernel decapsulation with some eBPF ingress decapsulation.
Example of the new test execution:
# ./test_progs -a tc_tunnel #447/1 tc_tunnel/ipip_none:OK #447/2 tc_tunnel/ipip6_none:OK #447/3 tc_tunnel/ip6tnl_none:OK #447/4 tc_tunnel/sit_none:OK #447/5 tc_tunnel/vxlan_eth:OK #447/6 tc_tunnel/ip6vxlan_eth:OK #447/7 tc_tunnel/gre_none:OK #447/8 tc_tunnel/gre_eth:OK #447/9 tc_tunnel/gre_mpls:OK #447/10 tc_tunnel/ip6gre_none:OK #447/11 tc_tunnel/ip6gre_eth:OK #447/12 tc_tunnel/ip6gre_mpls:OK #447/13 tc_tunnel/udp_none:OK #447/14 tc_tunnel/udp_eth:OK #447/15 tc_tunnel/udp_mpls:OK #447/16 tc_tunnel/ip6udp_none:OK #447/17 tc_tunnel/ip6udp_eth:OK #447/18 tc_tunnel/ip6udp_mpls:OK #447 tc_tunnel:OK Summary: 1/18 PASSED, 0 SKIPPED, 0 FAILED
Thanks for working on this!
One high level comment is to minimize switching netns to make the test easier to follow.
Some ideas...
+static void stop_server(struct subtest_cfg *cfg) +{
- struct nstoken *nstoken = open_netns(SERVER_NS);
- close(*cfg->server_fd);
- cfg->server_fd = NULL;
- close_netns(nstoken);
+}
+static int check_server_rx_data(struct subtest_cfg *cfg,
struct connection *conn, int len)
+{
- struct nstoken *nstoken = open_netns(SERVER_NS);
- int err;
- memset(rx_buffer, 0, BUFFER_LEN);
- err = recv(conn->server_fd, rx_buffer, len, 0);
- close_netns(nstoken);
- if (!ASSERT_EQ(err, len, "check rx data len"))
return 1;
- if (!ASSERT_MEMEQ(tx_buffer, rx_buffer, len, "check received data"))
return 1;
- return 0;
+}
+static struct connection *connect_client_to_server(struct subtest_cfg *cfg) +{
- struct network_helper_opts opts = {.timeout_ms = 500};
- int family = cfg->ipproto == 6 ? AF_INET6 : AF_INET;
- struct nstoken *nstoken = open_netns(CLIENT_NS);
- struct connection *conn = NULL;
- int client_fd, server_fd;
- client_fd = connect_to_addr_str(family, SOCK_STREAM, cfg->server_addr,
TEST_PORT, &opts);
- close_netns(nstoken);
- if (client_fd < 0)
return NULL;
- nstoken = open_netns(SERVER_NS);
Understood that the server is in another netns but I don't think it needs to switch back to SERVER_NS to use its fd like accept(server_fd). It can be done in client_ns. Please check.
The same for the above check_server_rx_data and stop_server.
- server_fd = accept(*cfg->server_fd, NULL, NULL);
- close_netns(nstoken);
- if (server_fd < 0)
return NULL;
- conn = malloc(sizeof(struct connection));
- if (conn) {
conn->server_fd = server_fd;
conn->client_fd = client_fd;
- }
- return conn;
+}
+static void disconnect_client_from_server(struct subtest_cfg *cfg,
struct connection *conn)
+{
- struct nstoken *nstoken;
- nstoken = open_netns(SERVER_NS);
same here.
- close(conn->server_fd);
- close_netns(nstoken);
- nstoken = open_netns(CLIENT_NS);
and here.
- close(conn->client_fd);
- close_netns(nstoken);
- free(conn);
+}
+static int send_and_test_data(struct subtest_cfg *cfg, bool must_succeed)
See if this whole function can work in client_ns alone or may be the caller run_test() can stay with the CLIENT_NS instead of...
+{
- struct nstoken *nstoken = NULL;
- struct connection *conn;
- int err, res = -1;
- conn = connect_client_to_server(cfg);
- if (!must_succeed && !ASSERT_EQ(conn, NULL, "connection that must fail"))
goto end;
- else if (!must_succeed)
return 0;
- if (!ASSERT_NEQ(conn, NULL, "connection that must succeed"))
return 1;
- nstoken = open_netns(CLIENT_NS);
switching here...
- err = send(conn->client_fd, tx_buffer, DEFAULT_TEST_DATA_SIZE, 0);
- close_netns(nstoken);
- if (!ASSERT_EQ(err, DEFAULT_TEST_DATA_SIZE, "send data from client"))
goto end;
- if (check_server_rx_data(cfg, conn, DEFAULT_TEST_DATA_SIZE))
goto end;
- if (!cfg->test_gso) {
res = 0;
goto end;
- }
- nstoken = open_netns(CLIENT_NS);
and here.
+static void run_test(struct subtest_cfg *cfg) +{
See if it can open_netns(CLIENT_NS) once at the beginning.
- if (!ASSERT_OK(run_server(cfg), "run server"))
The run_server and configure_* can open/close SERVER_NS when needed. open_netns should have saved the previous netns (i.e. CLIENT_NS) such that it knows which one to restore during close_netns(). I don't think I have tried that though but should work. Please check.
goto fail;
- // Basic communication must work
Consistent comment style. Stay with /* */
- if (!ASSERT_OK(send_and_test_data(cfg, true), "connect without any encap"))
goto fail;
- // Attach encapsulation program to client, communication must fail
- if (!ASSERT_OK(configure_encapsulation(cfg), "configure encapsulation"))
return;
- if (!ASSERT_OK(send_and_test_data(cfg, false), "connect with encap prog only"))
goto fail;
- /* Insert kernel decap module, connection must succeed */
- if (!ASSERT_OK(configure_kernel_decapsulation(cfg), "configure kernel decapsulation"))
goto fail;
- if (!ASSERT_OK(send_and_test_data(cfg, !cfg->expect_kern_decap_failure),
"connect with encap prog and kern decap"))
goto fail;
- // Replace kernel module with BPF decap, test must pass
- if (!ASSERT_OK(configure_ebpf_decapsulation(cfg), "configure ebpf decapsulation"))
goto fail;
- ASSERT_OK(send_and_test_data(cfg, true), "connect with encap and decap progs");
+fail:
- stop_server(cfg);
+}
struct subtest_cfg subtests_cfg[] = {
static
+int subtests_count = sizeof(subtests_cfg)/sizeof(struct subtest_cfg);
ARRAY_SIZE(subtests_cfg)
pw-bot: cr
Hello Martin,
On Sat Oct 18, 2025 at 2:18 AM CEST, Martin KaFai Lau wrote:
On 10/17/25 7:29 AM, Alexis Lothoré (eBPF Foundation) wrote:
The test_tc_tunnel.sh script checks that a large variety of tunneling mechanisms handled by the kernel can be handled as well by eBPF programs. While this test shares similarities with test_tunnel.c (which is already integrated in test_progs), those are testing slightly different things:
- test_tunnel.c creates a tunnel interface, and then get and set tunnel keys in packet metadata, from BPF programs.
- test_tc_tunnels.sh manually parses/crafts packets content
Bring the tests covered by test_tc_tunnel.sh into the test_progs framework, by creating a dedicated test_tc_tunnel.sh. This new test defines a "generic" runner which, for each test configuration:
- will bring the relevant veth pair, each of those isolated in a dedicated namespace
- will check that traffic will fail if there is only an encapsulating program attached to one veth egress
- will check that traffic succeed if we enable some decapsulation module on kernel side
- will check that traffic still succeeds if we replace the kernel decapsulation with some eBPF ingress decapsulation.
Example of the new test execution:
# ./test_progs -a tc_tunnel #447/1 tc_tunnel/ipip_none:OK #447/2 tc_tunnel/ipip6_none:OK #447/3 tc_tunnel/ip6tnl_none:OK #447/4 tc_tunnel/sit_none:OK #447/5 tc_tunnel/vxlan_eth:OK #447/6 tc_tunnel/ip6vxlan_eth:OK #447/7 tc_tunnel/gre_none:OK #447/8 tc_tunnel/gre_eth:OK #447/9 tc_tunnel/gre_mpls:OK #447/10 tc_tunnel/ip6gre_none:OK #447/11 tc_tunnel/ip6gre_eth:OK #447/12 tc_tunnel/ip6gre_mpls:OK #447/13 tc_tunnel/udp_none:OK #447/14 tc_tunnel/udp_eth:OK #447/15 tc_tunnel/udp_mpls:OK #447/16 tc_tunnel/ip6udp_none:OK #447/17 tc_tunnel/ip6udp_eth:OK #447/18 tc_tunnel/ip6udp_mpls:OK #447 tc_tunnel:OK Summary: 1/18 PASSED, 0 SKIPPED, 0 FAILED
Thanks for working on this!
Thanks for the prompt and detailed review !
One high level comment is to minimize switching netns to make the test easier to follow.
Yeah, all the NS switches make the overall setup a bit tedious. I'll give a try to your suggestions and see if we can reduce the number of NS open/close pairs.
Alexis
Now that test_tc_tunnel.sh scope has been ported to the test_progs framework, remove it.
Signed-off-by: Alexis Lothoré (eBPF Foundation) alexis.lothore@bootlin.com --- tools/testing/selftests/bpf/Makefile | 1 - tools/testing/selftests/bpf/test_tc_tunnel.sh | 320 -------------------------- 2 files changed, 321 deletions(-)
diff --git a/tools/testing/selftests/bpf/Makefile b/tools/testing/selftests/bpf/Makefile index 2f248dab922f57e2c14053cb0bdfbb547efe1292..58c768d9da575d8bb9274d3287123eb214a8d14d 100644 --- a/tools/testing/selftests/bpf/Makefile +++ b/tools/testing/selftests/bpf/Makefile @@ -104,7 +104,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_lirc_mode2.sh \ - test_tc_tunnel.sh \ test_tc_edt.sh \ test_xdping.sh \ test_bpftool_build.sh \ diff --git a/tools/testing/selftests/bpf/test_tc_tunnel.sh b/tools/testing/selftests/bpf/test_tc_tunnel.sh deleted file mode 100755 index cb55a908bb0d70c251bc55c9c8994fc023d44f64..0000000000000000000000000000000000000000 --- a/tools/testing/selftests/bpf/test_tc_tunnel.sh +++ /dev/null @@ -1,320 +0,0 @@ -#!/bin/bash -# SPDX-License-Identifier: GPL-2.0 -# -# In-place tunneling - -BPF_FILE="test_tc_tunnel.bpf.o" -# must match the port that the bpf program filters on -readonly port=8000 - -readonly ns_prefix="ns-$$-" -readonly ns1="${ns_prefix}1" -readonly ns2="${ns_prefix}2" - -readonly ns1_v4=192.168.1.1 -readonly ns2_v4=192.168.1.2 -readonly ns1_v6=fd::1 -readonly ns2_v6=fd::2 - -# Must match port used by bpf program -readonly udpport=5555 -# MPLSoverUDP -readonly mplsudpport=6635 -readonly mplsproto=137 - -readonly infile="$(mktemp)" -readonly outfile="$(mktemp)" - -setup() { - ip netns add "${ns1}" - ip netns add "${ns2}" - - ip link add dev veth1 mtu 1500 netns "${ns1}" type veth \ - peer name veth2 mtu 1500 netns "${ns2}" - - ip netns exec "${ns1}" ethtool -K veth1 tso off - - ip -netns "${ns1}" link set veth1 up - ip -netns "${ns2}" link set veth2 up - - ip -netns "${ns1}" -4 addr add "${ns1_v4}/24" dev veth1 - ip -netns "${ns2}" -4 addr add "${ns2_v4}/24" dev veth2 - ip -netns "${ns1}" -6 addr add "${ns1_v6}/64" dev veth1 nodad - ip -netns "${ns2}" -6 addr add "${ns2_v6}/64" dev veth2 nodad - - # clamp route to reserve room for tunnel headers - ip -netns "${ns1}" -4 route flush table main - ip -netns "${ns1}" -6 route flush table main - ip -netns "${ns1}" -4 route add "${ns2_v4}" mtu 1450 dev veth1 - ip -netns "${ns1}" -6 route add "${ns2_v6}" mtu 1430 dev veth1 - - sleep 1 - - dd if=/dev/urandom of="${infile}" bs="${datalen}" count=1 status=none -} - -cleanup() { - ip netns del "${ns2}" - ip netns del "${ns1}" - - if [[ -f "${outfile}" ]]; then - rm "${outfile}" - fi - if [[ -f "${infile}" ]]; then - rm "${infile}" - fi - - if [[ -n $server_pid ]]; then - kill $server_pid 2> /dev/null - fi -} - -server_listen() { - ip netns exec "${ns2}" nc "${netcat_opt}" -l "${port}" > "${outfile}" & - server_pid=$! -} - -client_connect() { - ip netns exec "${ns1}" timeout 2 nc "${netcat_opt}" -w 1 "${addr2}" "${port}" < "${infile}" - echo $? -} - -verify_data() { - wait "${server_pid}" - server_pid= - # sha1sum returns two fields [sha1] [filepath] - # convert to bash array and access first elem - insum=($(sha1sum ${infile})) - outsum=($(sha1sum ${outfile})) - if [[ "${insum[0]}" != "${outsum[0]}" ]]; then - echo "data mismatch" - exit 1 - fi -} - -wait_for_port() { - for i in $(seq 20); do - if ip netns exec "${ns2}" ss ${2:--4}OHntl | grep -q "$1"; then - return 0 - fi - sleep 0.1 - done - return 1 -} - -set -e - -# no arguments: automated test, run all -if [[ "$#" -eq "0" ]]; then - echo "ipip" - $0 ipv4 ipip none 100 - - echo "ipip6" - $0 ipv4 ipip6 none 100 - - echo "ip6ip6" - $0 ipv6 ip6tnl none 100 - - echo "sit" - $0 ipv6 sit none 100 - - echo "ip4 vxlan" - $0 ipv4 vxlan eth 2000 - - echo "ip6 vxlan" - $0 ipv6 ip6vxlan eth 2000 - - for mac in none mpls eth ; do - echo "ip gre $mac" - $0 ipv4 gre $mac 100 - - echo "ip6 gre $mac" - $0 ipv6 ip6gre $mac 100 - - echo "ip gre $mac gso" - $0 ipv4 gre $mac 2000 - - echo "ip6 gre $mac gso" - $0 ipv6 ip6gre $mac 2000 - - echo "ip udp $mac" - $0 ipv4 udp $mac 100 - - echo "ip6 udp $mac" - $0 ipv6 ip6udp $mac 100 - - echo "ip udp $mac gso" - $0 ipv4 udp $mac 2000 - - echo "ip6 udp $mac gso" - $0 ipv6 ip6udp $mac 2000 - done - - echo "OK. All tests passed" - exit 0 -fi - -if [[ "$#" -ne "4" ]]; then - echo "Usage: $0" - echo " or: $0 <ipv4|ipv6> <tuntype> <none|mpls|eth> <data_len>" - exit 1 -fi - -case "$1" in -"ipv4") - readonly addr1="${ns1_v4}" - readonly addr2="${ns2_v4}" - readonly ipproto=4 - readonly netcat_opt=-${ipproto} - readonly foumod=fou - readonly foutype=ipip - readonly fouproto=4 - readonly fouproto_mpls=${mplsproto} - readonly gretaptype=gretap - ;; -"ipv6") - readonly addr1="${ns1_v6}" - readonly addr2="${ns2_v6}" - readonly ipproto=6 - readonly netcat_opt=-${ipproto} - readonly foumod=fou6 - readonly foutype=ip6tnl - readonly fouproto="41 -6" - readonly fouproto_mpls="${mplsproto} -6" - readonly gretaptype=ip6gretap - ;; -*) - echo "unknown arg: $1" - exit 1 - ;; -esac - -readonly tuntype=$2 -readonly mac=$3 -readonly datalen=$4 - -echo "encap ${addr1} to ${addr2}, type ${tuntype}, mac ${mac} len ${datalen}" - -trap cleanup EXIT - -setup - -# basic communication works -echo "test basic connectivity" -server_listen -wait_for_port ${port} ${netcat_opt} -client_connect -verify_data - -# clientside, insert bpf program to encap all TCP to port ${port} -# client can no longer connect -ip netns exec "${ns1}" tc qdisc add dev veth1 clsact -ip netns exec "${ns1}" tc filter add dev veth1 egress \ - bpf direct-action object-file ${BPF_FILE} \ - section "encap_${tuntype}_${mac}" -echo "test bpf encap without decap (expect failure)" -server_listen -wait_for_port ${port} ${netcat_opt} -! client_connect - -if [[ "$tuntype" =~ "udp" ]]; then - # Set up fou tunnel. - ttype="${foutype}" - targs="encap fou encap-sport auto encap-dport $udpport" - # fou may be a module; allow this to fail. - modprobe "${foumod}" ||true - if [[ "$mac" == "mpls" ]]; then - dport=${mplsudpport} - dproto=${fouproto_mpls} - tmode="mode any ttl 255" - else - dport=${udpport} - dproto=${fouproto} - fi - ip netns exec "${ns2}" ip fou add port $dport ipproto ${dproto} - targs="encap fou encap-sport auto encap-dport $dport" -elif [[ "$tuntype" =~ "gre" && "$mac" == "eth" ]]; then - ttype=$gretaptype -elif [[ "$tuntype" =~ "vxlan" && "$mac" == "eth" ]]; then - ttype="vxlan" - targs="id 1 dstport 8472 udp6zerocsumrx" -elif [[ "$tuntype" == "ipip6" ]]; then - ttype="ip6tnl" - targs="" -else - ttype=$tuntype - targs="" -fi - -# tunnel address family differs from inner for SIT -if [[ "${tuntype}" == "sit" ]]; then - link_addr1="${ns1_v4}" - link_addr2="${ns2_v4}" -elif [[ "${tuntype}" == "ipip6" ]]; then - link_addr1="${ns1_v6}" - link_addr2="${ns2_v6}" -else - link_addr1="${addr1}" - link_addr2="${addr2}" -fi - -# serverside, insert decap module -# server is still running -# client can connect again -ip netns exec "${ns2}" ip link add name testtun0 type "${ttype}" \ - ${tmode} remote "${link_addr1}" local "${link_addr2}" $targs - -expect_tun_fail=0 - -if [[ "$tuntype" == "ip6udp" && "$mac" == "mpls" ]]; then - # No support for MPLS IPv6 fou tunnel; expect failure. - expect_tun_fail=1 -elif [[ "$tuntype" =~ "udp" && "$mac" == "eth" ]]; then - # No support for TEB fou tunnel; expect failure. - expect_tun_fail=1 -elif [[ "$tuntype" =~ (gre|vxlan) && "$mac" == "eth" ]]; then - # Share ethernet address between tunnel/veth2 so L2 decap works. - ethaddr=$(ip netns exec "${ns2}" ip link show veth2 | \ - awk '/ether/ { print $2 }') - ip netns exec "${ns2}" ip link set testtun0 address $ethaddr -elif [[ "$mac" == "mpls" ]]; then - modprobe mpls_iptunnel ||true - modprobe mpls_gso ||true - ip netns exec "${ns2}" sysctl -qw net.mpls.platform_labels=65536 - ip netns exec "${ns2}" ip -f mpls route add 1000 dev lo - ip netns exec "${ns2}" ip link set lo up - ip netns exec "${ns2}" sysctl -qw net.mpls.conf.testtun0.input=1 - ip netns exec "${ns2}" sysctl -qw net.ipv4.conf.lo.rp_filter=0 -fi - -# Because packets are decapped by the tunnel they arrive on testtun0 from -# the IP stack perspective. Ensure reverse path filtering is disabled -# otherwise we drop the TCP SYN as arriving on testtun0 instead of the -# expected veth2 (veth2 is where 192.168.1.2 is configured). -ip netns exec "${ns2}" sysctl -qw net.ipv4.conf.all.rp_filter=0 -# rp needs to be disabled for both all and testtun0 as the rp value is -# selected as the max of the "all" and device-specific values. -ip netns exec "${ns2}" sysctl -qw net.ipv4.conf.testtun0.rp_filter=0 -ip netns exec "${ns2}" ip link set dev testtun0 up -if [[ "$expect_tun_fail" == 1 ]]; then - # This tunnel mode is not supported, so we expect failure. - echo "test bpf encap with tunnel device decap (expect failure)" - ! client_connect -else - echo "test bpf encap with tunnel device decap" - client_connect - verify_data - server_listen - wait_for_port ${port} ${netcat_opt} -fi - -# serverside, use BPF for decap -ip netns exec "${ns2}" ip link del dev testtun0 -ip netns exec "${ns2}" tc qdisc add dev veth2 clsact -ip netns exec "${ns2}" tc filter add dev veth2 ingress \ - bpf direct-action object-file ${BPF_FILE} section decap -echo "test bpf encap with bpf decap" -client_connect -verify_data - -echo OK
linux-kselftest-mirror@lists.linaro.org