[PATCH net-next 04/14] selftests/bpf: Add tests for bpf_dynptr_from_skb_ext
Jakub Sitnicki <[email protected]>
| Newsgroups | org.kernel.vger.bpf,org.kernel.vger.netdev |
|---|---|
| Message-ID | <20260814-bpf-meta-inside-skb-ext-v1-4-767edd862656@cloudflare.com> |
Cover the bpf_dynptr_from_skb_ext() kfunc and the dynptr interface to skb_ext with TC-to-TC tests on a tuntap device: - write/read via bpf_dynptr_read and bpf_dynptr_write - write/read via bpf_dynptr_slice and bpf_dynptr_slice_rdwr - clone read via bpf_dynptr_clone - error paths: no allocation without F_CREATE, invalid flags, read-only enforcement without F_CREATE - double allocation: data from first alloc survives second skb_ext_add Signed-off-by: Jakub Sitnicki <[email protected]> --- tools/testing/selftests/bpf/config | 1 + .../bpf/prog_tests/xdp_context_test_run.c | 52 ++++++ tools/testing/selftests/bpf/progs/test_xdp_meta.c | 185 +++++++++++++++++++++ 3 files changed, 238 insertions(+) diff --git a/tools/testing/selftests/bpf/config b/tools/testing/selftests/bpf/config index ea7044f30adc..502f4504bdba 100644 --- a/tools/testing/selftests/bpf/config +++ b/tools/testing/selftests/bpf/config @@ -7,6 +7,7 @@ CONFIG_BPF_JIT=y CONFIG_BPF_KPROBE_OVERRIDE=y CONFIG_BPF_LIRC_MODE2=y CONFIG_BPF_LSM=y +CONFIG_BPF_SKB_EXT=y CONFIG_BPF_STREAM_PARSER=y CONFIG_BPF_SYSCALL=y # CONFIG_BPF_UNPRIV_DEFAULT_OFF is not set 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 448807676176..fd340e5538a3 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 @@ -693,3 +693,55 @@ void test_xdp_context_lwt_encap(void) test_xdp_meta__destroy(skel); } + +void test_skb_ext_basic(void) +{ + struct test_xdp_meta *skel = NULL; + + skel = test_xdp_meta__open_and_load(); + if (!ASSERT_OK_PTR(skel, "open and load skeleton")) + return; + + if (test__start_subtest("tc_write_read")) + test_tuntap(NULL, /* xdp */ + skel->progs.tc_skb_ext_write, + skel->progs.tc_skb_ext_read, + &skel->bss->test_pass); + if (test__start_subtest("tc_write_clone_read")) + test_tuntap(NULL, /* xdp */ + skel->progs.tc_skb_ext_write, + skel->progs.tc_skb_ext_clone_read, + &skel->bss->test_pass); + if (test__start_subtest("tc_write_slice_read")) + test_tuntap(NULL, /* xdp */ + skel->progs.tc_skb_ext_write, + skel->progs.tc_skb_ext_slice_read, + &skel->bss->test_pass); + if (test__start_subtest("tc_slice_write_read")) + test_tuntap(NULL, /* xdp */ + skel->progs.tc_skb_ext_slice_write, + skel->progs.tc_skb_ext_read, + &skel->bss->test_pass); + if (test__start_subtest("tc_no_alloc")) + test_tuntap(NULL, /* xdp */ + skel->progs.tc_skb_ext_no_alloc, + NULL, /* tc prio 2 */ + &skel->bss->test_pass); + if (test__start_subtest("tc_invalid_flags")) + test_tuntap(NULL, /* xdp */ + skel->progs.tc_skb_ext_invalid_flags, + NULL, /* tc prio 2 */ + &skel->bss->test_pass); + if (test__start_subtest("tc_rdonly")) + test_tuntap(NULL, /* xdp */ + skel->progs.tc_skb_ext_rdonly, + NULL, /* tc prio 2 */ + &skel->bss->test_pass); + if (test__start_subtest("tc_double_alloc")) + test_tuntap(NULL, /* xdp */ + skel->progs.tc_skb_ext_double_alloc, + NULL, /* tc prio 2 */ + &skel->bss->test_pass); + + 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 08b03be0b891..43840ee32d35 100644 --- a/tools/testing/selftests/bpf/progs/test_xdp_meta.c +++ b/tools/testing/selftests/bpf/progs/test_xdp_meta.c @@ -6,6 +6,7 @@ #include <errno.h> #include "bpf_kfuncs.h" +#include "bpf_misc.h" #include "bpf_tracing_net.h" #define META_SIZE 32 @@ -689,4 +690,188 @@ int helper_skb_change_proto(struct __sk_buff *ctx) return TC_ACT_SHOT; } +/* Write to skb_ext using bpf_dynptr_write helper */ +SEC("tc") +int tc_skb_ext_write(struct __sk_buff *ctx) +{ + struct bpf_dynptr meta; + + if (!is_test_packet_tc(ctx)) + return TC_ACT_SHOT; + if (bpf_dynptr_from_skb_ext(ctx, 0, BPF_SKB_EXT_F_CREATE, &meta)) + return TC_ACT_SHOT; + if (bpf_dynptr_write(&meta, 0, (void *)meta_want, ARRAY_SIZE(meta_want), 0)) + return TC_ACT_SHOT; + + return TC_ACT_UNSPEC; +} + +/* Read from skb-ext metadata using bpf_dynptr_read helper */ +SEC("tc") +int tc_skb_ext_read(struct __sk_buff *ctx) +{ + __u8 meta_have[META_SIZE]; + struct bpf_dynptr meta; + + if (bpf_dynptr_from_skb_ext(ctx, 0, 0, &meta)) + return TC_ACT_SHOT; + if (bpf_dynptr_read(meta_have, ARRAY_SIZE(meta_have), &meta, 0, 0)) + return TC_ACT_SHOT; + if (!check_metadata(meta_have)) + return TC_ACT_SHOT; + + test_pass = true; + return TC_ACT_UNSPEC; +} + +/* Read from a cloned skb_ext dynptr */ +SEC("tc") +int tc_skb_ext_clone_read(struct __sk_buff *ctx) +{ + struct bpf_dynptr meta, clone; + __u8 meta_have[META_SIZE]; + + if (bpf_dynptr_from_skb_ext(ctx, 0, 0, &meta)) + return TC_ACT_SHOT; + if (bpf_dynptr_clone(&meta, &clone)) + return TC_ACT_SHOT; + if (bpf_dynptr_read(meta_have, ARRAY_SIZE(meta_have), &clone, 0, 0)) + return TC_ACT_SHOT; + if (!check_metadata(meta_have)) + return TC_ACT_SHOT; + + test_pass = true; + return TC_ACT_UNSPEC; +} + +/* Read from skb_ext using bpf_dynptr_slice */ +SEC("tc") +int tc_skb_ext_slice_read(struct __sk_buff *ctx) +{ + struct bpf_dynptr meta; + __u8 *meta_have; + + if (bpf_dynptr_from_skb_ext(ctx, 0, 0, &meta)) + return TC_ACT_SHOT; + meta_have = bpf_dynptr_slice(&meta, 0, NULL, META_SIZE); + if (!meta_have) + return TC_ACT_SHOT; + if (!check_metadata(meta_have)) + return TC_ACT_SHOT; + + test_pass = true; + return TC_ACT_UNSPEC; +} + +/* Write to skb_ext using bpf_dynptr_slice_rdwr */ +SEC("tc") +int tc_skb_ext_slice_write(struct __sk_buff *ctx) +{ + struct bpf_dynptr meta; + __u8 *dst; + + if (!is_test_packet_tc(ctx)) + return TC_ACT_SHOT; + if (bpf_dynptr_from_skb_ext(ctx, 0, BPF_SKB_EXT_F_CREATE, &meta)) + return TC_ACT_SHOT; + dst = bpf_dynptr_slice_rdwr(&meta, 0, NULL, META_SIZE); + if (!dst) + return TC_ACT_SHOT; + __builtin_memcpy(dst, meta_want, META_SIZE); + + return TC_ACT_UNSPEC; +} + +/* Opening skb_ext without F_CREATE on a fresh skb should fail */ +SEC("tc") +int tc_skb_ext_no_alloc(struct __sk_buff *ctx) +{ + struct bpf_dynptr meta; + + if (!is_test_packet_tc(ctx)) + return TC_ACT_SHOT; + if (bpf_dynptr_from_skb_ext(ctx, 0, 0, &meta) != -ENOENT) + return TC_ACT_SHOT; + + test_pass = true; + return TC_ACT_UNSPEC; +} + +/* Invalid flags are rejected */ +SEC("tc") +int tc_skb_ext_invalid_flags(struct __sk_buff *ctx) +{ + struct bpf_dynptr meta; + + if (!is_test_packet_tc(ctx)) + return TC_ACT_SHOT; + if (bpf_dynptr_from_skb_ext(ctx, 0, ~0ULL, &meta) != -EINVAL) + return TC_ACT_SHOT; + + test_pass = true; + return TC_ACT_UNSPEC; +} + +/* Without F_CREATE the dynptr is read-only */ +SEC("tc") +int tc_skb_ext_rdonly(struct __sk_buff *ctx) +{ + __u8 meta_have[META_SIZE]; + struct bpf_dynptr meta; + + if (!is_test_packet_tc(ctx)) + return TC_ACT_SHOT; + + /* Create and populate the ext */ + if (bpf_dynptr_from_skb_ext(ctx, 0, BPF_SKB_EXT_F_CREATE, &meta)) + return TC_ACT_SHOT; + if (bpf_dynptr_write(&meta, 0, (void *)meta_want, META_SIZE, 0)) + return TC_ACT_SHOT; + + /* Reopen without F_CREATE -- should be read-only */ + if (bpf_dynptr_from_skb_ext(ctx, 0, 0, &meta)) + return TC_ACT_SHOT; + + /* Verify read-only: writes must fail, reads must work */ + if (!bpf_dynptr_is_rdonly(&meta)) + return TC_ACT_SHOT; + if (!bpf_dynptr_write(&meta, 0, (void *)meta_want, META_SIZE, 0)) + return TC_ACT_SHOT; + if (bpf_dynptr_read(meta_have, META_SIZE, &meta, 0, 0)) + return TC_ACT_SHOT; + if (!check_metadata(meta_have)) + return TC_ACT_SHOT; + + test_pass = true; + return TC_ACT_UNSPEC; +} + +/* Double alloc: data from first alloc survives second skb_ext_add */ +SEC("tc") +int tc_skb_ext_double_alloc(struct __sk_buff *ctx) +{ + __u8 meta_have[META_SIZE]; + struct bpf_dynptr meta; + + if (!is_test_packet_tc(ctx)) + return TC_ACT_SHOT; + + /* First alloc + write */ + if (bpf_dynptr_from_skb_ext(ctx, 0, BPF_SKB_EXT_F_CREATE, &meta)) + return TC_ACT_SHOT; + if (bpf_dynptr_write(&meta, 0, (void *)meta_want, META_SIZE, 0)) + return TC_ACT_SHOT; + + /* Second alloc -- skb_ext_add returns existing ext */ + if (bpf_dynptr_from_skb_ext(ctx, 0, BPF_SKB_EXT_F_CREATE, &meta)) + return TC_ACT_SHOT; + if (bpf_dynptr_read(meta_have, META_SIZE, &meta, 0, 0)) + return TC_ACT_SHOT; + if (!check_metadata(meta_have)) + return TC_ACT_SHOT; + + test_pass = true; + return TC_ACT_UNSPEC; +} + char _license[] SEC("license") = "GPL"; -- 2.43.0