[PATCH net-next 05/14] selftests/bpf: Test skb_ext on cloned skbs
Jakub Sitnicki <[email protected]>
| Newsgroups | org.kernel.vger.bpf,org.kernel.vger.netdev |
|---|---|
| Message-ID | <20260814-bpf-meta-inside-skb-ext-v1-5-767edd862656@cloudflare.com> |
Cover skb_ext behavior when an skb is cloned by TC mirred (mirror to a dummy device): - clone_ext_read: the extension written at tap ingress is readable from the mirred clone at dummy ingress -- the clone shares the extension with the original - clone_ext_cow: opening the extension with F_CREATE on the clone triggers copy-on-write, so overwriting the clone's data does not affect the original -- verified by a tp_btf/kfree_skb probe that checks the original skb still carries meta_want Signed-off-by: Jakub Sitnicki <[email protected]> --- .../bpf/prog_tests/xdp_context_test_run.c | 93 ++++++++++++++++++++++ tools/testing/selftests/bpf/progs/test_xdp_meta.c | 46 +++++++++++ 2 files changed, 139 insertions(+) diff --git a/tools/testing/selftests/bpf/prog_tests/xdp_context_test_run.c b/tools/testing/selftests/bpf/prog_tests/xdp_context_test_run.c index fd340e5538a3..58b793cf14e5 100644 --- a/tools/testing/selftests/bpf/prog_tests/xdp_context_test_run.c +++ b/tools/testing/selftests/bpf/prog_tests/xdp_context_test_run.c @@ -694,6 +694,95 @@ void test_xdp_context_lwt_encap(void) test_xdp_meta__destroy(skel); } +/* Test if skb_ext survives skb clone (via tc mirred). + * dummy_prog runs on the clone (dummy ingress). + */ +static void test_mirred_clone_ext(struct test_xdp_meta *skel, + struct bpf_program *dummy_prog) +{ + LIBBPF_OPTS(bpf_tc_hook, tc_hook, .attach_point = BPF_TC_INGRESS); + LIBBPF_OPTS(bpf_tc_opts, tc_opts, .handle = 1, .priority = 1); + struct netns_obj *ns = NULL; + int dummy_ifindex; + int tap_ifindex; + int tap_fd = -1; + int ret; + + skel->bss->test_pass = false; + + ns = netns_new("mirred_clone", true); + if (!ASSERT_OK_PTR(ns, "netns_new")) + return; + + /* Dummy dev: attach reader */ + SYS(close, "ip link add name " DUMMY_NAME " type dummy"); + SYS(close, "ip link set dev " DUMMY_NAME " up"); + + dummy_ifindex = if_nametoindex(DUMMY_NAME); + if (!ASSERT_GE(dummy_ifindex, 0, "dummy_ifindex")) + goto close; + + tc_hook.ifindex = dummy_ifindex; + ret = bpf_tc_hook_create(&tc_hook); + if (!ASSERT_OK(ret, "dummy_hook_create")) + goto close; + + tc_opts.prog_fd = bpf_program__fd(dummy_prog); + ret = bpf_tc_attach(&tc_hook, &tc_opts); + if (!ASSERT_OK(ret, "dummy_attach")) + goto close; + + /* TAP dev: attach writer + mirred to dummy */ + tap_fd = open_tuntap(TAP_NAME, true); + if (!ASSERT_GE(tap_fd, 0, "open_tuntap")) + goto close; + + SYS(close, "ip link set dev " TAP_NAME " up"); + + tap_ifindex = if_nametoindex(TAP_NAME); + if (!ASSERT_GE(tap_ifindex, 0, "tap_ifindex")) + goto close; + + tc_hook.ifindex = tap_ifindex; + ret = bpf_tc_hook_create(&tc_hook); + if (!ASSERT_OK(ret, "tap_hook_create")) + goto close; + + tc_opts.prog_id = 0; + tc_opts.prog_fd = bpf_program__fd(skel->progs.tc_skb_ext_write); + ret = bpf_tc_attach(&tc_hook, &tc_opts); + if (!ASSERT_OK(ret, "tap_attach")) + goto close; + + SYS(close, "tc filter add dev " TAP_NAME " ingress " + "protocol all matchall " + "action mirred ingress mirror dev " DUMMY_NAME); + + ret = write_test_packet(tap_fd); + if (!ASSERT_OK(ret, "write_test_packet")) + goto close; + + ASSERT_TRUE(skel->bss->test_pass, "test_pass"); + +close: + if (tap_fd >= 0) + close(tap_fd); + netns_free(ns); +} + +static void test_mirred_clone_ext_cow(struct test_xdp_meta *skel) +{ + struct bpf_link *tp_link; + + skel->bss->clone_cow_done = false; + tp_link = bpf_program__attach(skel->progs.tp_kfree_skb_cow_check); + if (!ASSERT_OK_PTR(tp_link, "attach_tp")) + return; + + test_mirred_clone_ext(skel, skel->progs.tc_skb_ext_clone_redir_cow); + bpf_link__destroy(tp_link); +} + void test_skb_ext_basic(void) { struct test_xdp_meta *skel = NULL; @@ -742,6 +831,10 @@ void test_skb_ext_basic(void) skel->progs.tc_skb_ext_double_alloc, NULL, /* tc prio 2 */ &skel->bss->test_pass); + if (test__start_subtest("clone_ext_read")) + test_mirred_clone_ext(skel, skel->progs.tc_skb_ext_read); + if (test__start_subtest("clone_ext_cow")) + test_mirred_clone_ext_cow(skel); test_xdp_meta__destroy(skel); } diff --git a/tools/testing/selftests/bpf/progs/test_xdp_meta.c b/tools/testing/selftests/bpf/progs/test_xdp_meta.c index 43840ee32d35..1b6bb20ee7e8 100644 --- a/tools/testing/selftests/bpf/progs/test_xdp_meta.c +++ b/tools/testing/selftests/bpf/progs/test_xdp_meta.c @@ -3,6 +3,7 @@ #include <bpf/bpf_endian.h> #include <bpf/bpf_helpers.h> +#include <bpf/bpf_tracing.h> #include <errno.h> #include "bpf_kfuncs.h" @@ -874,4 +875,49 @@ int tc_skb_ext_double_alloc(struct __sk_buff *ctx) return TC_ACT_UNSPEC; } +static const __u8 meta_zero[META_SIZE] = {}; + +volatile bool clone_cow_done; + +/* Overwrite skb_ext on the clone via F_CREATE (COW) -- must not affect original. + * Runs on the dummy ingress (clone side), synchronously during tc mirred. + */ +SEC("tc") +int tc_skb_ext_clone_redir_cow(struct __sk_buff *ctx) +{ + struct bpf_dynptr meta; + + if (bpf_dynptr_from_skb_ext(ctx, 0, BPF_SKB_EXT_F_CREATE, &meta)) + return TC_ACT_SHOT; + + /* Zero out the clone's ext -- must not affect original */ + bpf_dynptr_write(&meta, 0, (void *)meta_zero, META_SIZE, 0); + + clone_cow_done = true; + return TC_ACT_SHOT; +} + +/* Verify COW isolation at kfree_skb time: once clone_cow_done is set, + * check that the original skb still has meta_want. + */ +SEC("tp_btf/kfree_skb") +int BPF_PROG(tp_kfree_skb_cow_check, struct sk_buff *skb) +{ + __u8 meta_have[META_SIZE]; + struct bpf_dynptr meta; + + if (!clone_cow_done) + return 0; + + if (bpf_dynptr_from_skb_ext((struct __sk_buff *)skb, 0, 0, &meta)) + return 0; + if (bpf_dynptr_read(meta_have, META_SIZE, &meta, 0, 0)) + return 0; + if (!check_metadata(meta_have)) + return 0; + + test_pass = true; + return 0; +} + char _license[] SEC("license") = "GPL"; -- 2.43.0