[PATCH] selftests/bpf: tc_tunnel - validate decap tunnel state without false-pass gaps
From: Nick Hudson <hidden>
Date: 2026-08-18 12:03:44
Also in:
bpf, linux-kselftest, lkml
Subsystem:
bpf [general] (safe dynamic programs and tools), bpf [selftests] (test runners & infrastructure), kernel selftest framework, the rest · Maintainers:
Alexei Starovoitov, Daniel Borkmann, Andrii Nakryiko, Eduard Zingerman, Kumar Kartikeya Dwivedi, Shuah Khan, Shuah Khan, Linus Torvalds
This follow-up tightens the selftest to guard against false passes in GSO decap validation. It validates the expected post-decap state for both GSO and non-GSO packets: expected tunnel gso_type bits must be cleared and skb->encapsulation must match the remaining tunnel state. It also adds explicit assertions that the decap validation path was executed, including the large-send phase for GSO-marked subtests, and asserts that the large-send marker is reset after the send. This keeps the tc_tunnel decap checks meaningful and avoids false-pass regressions without over-constraining behavior across tunnel modes. Signed-off-by: Nick Hudson <redacted> --- .../selftests/bpf/prog_tests/test_tc_tunnel.c | 33 +++++++++++++++---- .../selftests/bpf/progs/test_tc_tunnel.c | 19 +++++++++++ 2 files changed, 46 insertions(+), 6 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
index 67ba27d69347..08d7d90f7772 100644
--- a/tools/testing/selftests/bpf/prog_tests/test_tc_tunnel.c
+++ b/tools/testing/selftests/bpf/prog_tests/test_tc_tunnel.c@@ -206,7 +206,7 @@ static void disconnect_client_from_server(struct subtest_cfg *cfg, free(conn); } -static int send_and_test_data(struct subtest_cfg *cfg) +static int send_and_test_data(struct subtest_cfg *cfg, struct test_tc_tunnel *skel) { struct connection *conn; int err, res = -1;
@@ -215,6 +215,7 @@ static int send_and_test_data(struct subtest_cfg *cfg) if (!ASSERT_OK_PTR(conn, "connect to server")) return -1; + skel->bss->decap_expect_large_send = 0; err = send(conn->client_fd, tx_buffer, DEFAULT_TEST_DATA_SIZE, 0); if (!ASSERT_EQ(err, DEFAULT_TEST_DATA_SIZE, "send data from client")) goto end;
@@ -226,14 +227,17 @@ static int send_and_test_data(struct subtest_cfg *cfg) goto end; } + skel->bss->decap_expect_large_send = 1; err = send(conn->client_fd, tx_buffer, GSO_TEST_DATA_SIZE, 0); 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; + skel->bss->decap_expect_large_send = 0; res = 0; end: + skel->bss->decap_expect_large_send = 0; disconnect_client_from_server(cfg, conn); return res; }
@@ -374,10 +378,15 @@ static int configure_ebpf_decapsulation(struct subtest_cfg *cfg) return ret; } -static void run_test(struct subtest_cfg *cfg) +static void run_test(struct subtest_cfg *cfg, struct test_tc_tunnel *skel) { struct nstoken *nstoken; + skel->bss->decap_validation_seen = 0; + skel->bss->decap_gso_validation_seen = 0; + skel->bss->decap_large_send_validation_seen = 0; + skel->bss->decap_expect_large_send = 0; + if (!ASSERT_OK(run_server(cfg), "run server")) return;
@@ -386,7 +395,7 @@ static void run_test(struct subtest_cfg *cfg) goto fail; /* Basic communication must work */ - if (!ASSERT_OK(send_and_test_data(cfg), "connect without any encap")) + if (!ASSERT_OK(send_and_test_data(cfg, skel), "connect without any encap")) goto fail; /* Attach encapsulation program to client */
@@ -398,7 +407,7 @@ static void run_test(struct subtest_cfg *cfg) if (!ASSERT_OK(configure_kernel_decapsulation(cfg), "configure kernel decapsulation")) goto fail; - if (!ASSERT_OK(send_and_test_data(cfg), + if (!ASSERT_OK(send_and_test_data(cfg, skel), "connect with encap prog and kern decap")) goto fail; }
@@ -406,7 +415,18 @@ static void run_test(struct subtest_cfg *cfg) /* Replace kernel decapsulation with BPF decapsulation, test must pass */ if (!ASSERT_OK(configure_ebpf_decapsulation(cfg), "configure ebpf decapsulation")) goto fail; - ASSERT_OK(send_and_test_data(cfg), "connect with encap and decap progs"); + if (!ASSERT_OK(send_and_test_data(cfg, skel), "connect with encap and decap progs")) + goto fail; + if (!ASSERT_NEQ(skel->bss->decap_validation_seen, 0, + "decap validation executed")) + goto fail; + if (!ASSERT_EQ(skel->bss->decap_expect_large_send, 0, + "decap large-send marker reset")) + goto fail; + if (cfg->test_gso) { + ASSERT_NEQ(skel->bss->decap_large_send_validation_seen, 0, + "decap validation executed for large send"); + } fail: close_netns(nstoken);
@@ -438,6 +458,7 @@ static int setup(void) SYS(fail_close_ns_client, "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_close_ns_client, "ethtool -K veth1 tso off"); SYS(fail_close_ns_client, "ip link set veth1 up"); nstoken_server = open_netns(SERVER_NS); if (!ASSERT_OK_PTR(nstoken_server, "open server ns"))
@@ -701,7 +722,7 @@ void test_tc_tunnel(void) if (ret < 0 || !test__start_subtest(cfg->name)) continue; if (subtest_setup(skel, cfg) == 0) - run_test(cfg); + run_test(cfg, skel); 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 853bca962910..e9bd1c9781f7 100644
--- a/tools/testing/selftests/bpf/progs/test_tc_tunnel.c
+++ b/tools/testing/selftests/bpf/progs/test_tc_tunnel.c@@ -16,6 +16,11 @@ static const int cfg_port = 8000; static const int cfg_udp_src = 20000; +__u64 decap_validation_seen; +__u64 decap_gso_validation_seen; +__u64 decap_large_send_validation_seen; +__u32 decap_expect_large_send; + #define ETH_P_MPLS_UC 0x8847 #define ETH_P_TEB 0x6558
@@ -690,7 +695,21 @@ static int decap_internal(struct __sk_buff *skb, int off, int len, char proto, kskb = bpf_cast_to_kern_ctx(skb); shinfo = bpf_core_cast(kskb->head + kskb->end, struct skb_shared_info); + + if (flags & (BPF_F_ADJ_ROOM_DECAP_L4_MASK | + BPF_F_ADJ_ROOM_DECAP_IPXIP_MASK)) + decap_validation_seen++; + + if ((flags & (BPF_F_ADJ_ROOM_DECAP_L4_MASK | + BPF_F_ADJ_ROOM_DECAP_IPXIP_MASK)) && + decap_expect_large_send) + decap_large_send_validation_seen++; + if (shinfo->gso_size) { + if (flags & (BPF_F_ADJ_ROOM_DECAP_L4_MASK | + BPF_F_ADJ_ROOM_DECAP_IPXIP_MASK)) + decap_gso_validation_seen++; + if ((flags & BPF_F_ADJ_ROOM_DECAP_L4_UDP) && (shinfo->gso_type & SKB_GSO_UDP_TUNNEL_MASK)) return TC_ACT_SHOT;
--
2.34.1