On Tue Aug 18, 2026 at 8:03 AM EDT, Nick Hudson wrote:
> 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 <[email protected]>
> ---
>  .../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;

Sashiko points out correctly that gso_validation_seen is never checked.
Can we assert it with the others?

The CI bot's point on the other hand doesn't matter that much imo, the
assert for the config variable is good in case we ever add any other
subtests.

pw-bot: cr

> +     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;


Reply via email to