[PATCH net-next v2 13/14] selftests/bpf: Test skb_ext read from sk_skb stream verdict hook
flat view
COOLING6d
From: Jakub Sitnicki <jakub@cloudflare.com>
Date: 2026-09-10 14:03:05
Also in:
bpf
Subsystem:
bpf [general] (safe dynamic programs and tools), bpf [selftests] (test runners & infrastructure), kernel selftest framework, the rest, xdp (express data path) · Maintainers:
Alexei Starovoitov, Daniel Borkmann, Andrii Nakryiko, Eduard Zingerman, Kumar Kartikeya Dwivedi, Shuah Khan, Shuah Khan, Linus Torvalds, David S. Miller, Jakub Kicinski, Jesper Dangaard Brouer, John Fastabend
Test bpf_dynptr_from_skb_ext() from sk_skb context. Write to skb_ext from TC on loopback; read from skb_ext from the sockmap verdict program. Signed-off-by: Jakub Sitnicki <jakub@cloudflare.com> --- .../selftests/bpf/prog_tests/socket_helpers.h | 1 + .../bpf/prog_tests/xdp_context_test_run.c | 80 ++++++++++++++++++++++ tools/testing/selftests/bpf/progs/test_xdp_meta.c | 18 +++++ 3 files changed, 99 insertions(+)
diff --git a/tools/testing/selftests/bpf/prog_tests/socket_helpers.h b/tools/testing/selftests/bpf/prog_tests/socket_helpers.h
index 0d59503a0c73..eb114a6ea6f9 100644
--- a/tools/testing/selftests/bpf/prog_tests/socket_helpers.h
+++ b/tools/testing/selftests/bpf/prog_tests/socket_helpers.h@@ -3,6 +3,7 @@ #ifndef __SOCKET_HELPERS__ #define __SOCKET_HELPERS__ +#include <error.h> #include <sys/un.h> #include <linux/vm_sockets.h>
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 2814bfa0d6b5..01241276c4d3 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@@ -4,6 +4,7 @@ #include <linux/ipv6.h> #include <linux/netfilter.h> #include <arpa/inet.h> +#include "socket_helpers.h" #include "test_xdp_context_test_run.skel.h" #include "test_xdp_meta.skel.h"
@@ -1678,6 +1679,83 @@ static void test_skb_ext_seg6local(struct test_xdp_meta *skel) unlink(SEG6_PIN_PATH); } +/* + * Test skb_ext survival across TC ingress -> sk_skb verdict hook. TC ingress + * writes skb_ext on loopback; the verdict program reads it on skb delivery to a + * socket in the sockmap. + */ +static void test_skb_ext_sk_skb(struct test_xdp_meta *skel) +{ + LIBBPF_OPTS(bpf_tc_hook, tc_hook, + .ifindex = 1 /* IFINDEX_LO */, + .attach_point = BPF_TC_INGRESS); + LIBBPF_OPTS(bpf_tc_opts, tc_opts, .handle = 1, .priority = 1); + struct bpf_link *verdict_link = NULL; + struct sockaddr_in addr; + socklen_t addr_len = sizeof(addr); + struct netns_obj *ns = NULL; + char buf[TEST_PAYLOAD_LEN]; + int c1 = -1, p1 = -1; + int map = -1, zero = 0; + ssize_t n; + int ret; + + ns = netns_new("tc_to_sk_skb", true); + if (!ASSERT_OK_PTR(ns, "netns_new")) + return; + + skel->bss->test_pass = false; + + ret = bpf_tc_hook_create(&tc_hook); + if (!ASSERT_OK(ret, "bpf_tc_hook_create")) + goto cleanup; + + tc_opts.prog_fd = bpf_program__fd(skel->progs.tc_skb_ext_write_port); + ret = bpf_tc_attach(&tc_hook, &tc_opts); + if (!ASSERT_OK(ret, "bpf_tc_attach")) + goto cleanup; + + map = bpf_map_create(BPF_MAP_TYPE_SOCKMAP, NULL, sizeof(int), + sizeof(int), 1, NULL); + if (!ASSERT_GE(map, 0, "bpf_map_create")) + goto cleanup; + + verdict_link = bpf_program__attach_sockmap(skel->progs.sk_skb_skb_ext_read, + map); + if (!ASSERT_OK_PTR(verdict_link, "attach_sockmap")) + goto cleanup; + + if (!ASSERT_OK(create_pair(AF_INET, SOCK_STREAM, &c1, &p1), "create_pair")) + goto cleanup; + + if (!ASSERT_OK(bpf_map_update_elem(map, &zero, &c1, BPF_NOEXIST), "map_update")) + goto cleanup; + + if (!ASSERT_OK(getsockname(c1, (struct sockaddr *)&addr, &addr_len), + "getsockname")) + goto cleanup; + skel->bss->target_port = addr.sin_port; + + n = send(p1, test_payload, TEST_PAYLOAD_LEN, 0); + if (!ASSERT_EQ(n, TEST_PAYLOAD_LEN, "send")) + goto cleanup; + + n = recv_timeout(c1, buf, sizeof(buf), MSG_DONTWAIT, IO_TIMEOUT_SEC); + ASSERT_EQ(n, TEST_PAYLOAD_LEN, "recv"); + ASSERT_TRUE(skel->bss->test_pass, "test_pass"); + +cleanup: + if (c1 >= 0) + close(c1); + if (p1 >= 0) + close(p1); + bpf_link__destroy(verdict_link); + if (map >= 0) + close(map); + bpf_tc_hook_destroy(&tc_hook); + netns_free(ns); +} + void test_skb_ext_cross_hook(void) { struct test_xdp_meta *skel = NULL;
@@ -1709,6 +1787,8 @@ void test_skb_ext_cross_hook(void) skel->progs.lwt_xmit_skb_ext_write, "xmit", true); if (test__start_subtest("tc_to_seg6local")) test_skb_ext_seg6local(skel); + if (test__start_subtest("tc_to_sk_skb")) + test_skb_ext_sk_skb(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 00312a549ea9..5b1a5b9b610e 100644
--- a/tools/testing/selftests/bpf/progs/test_xdp_meta.c
+++ b/tools/testing/selftests/bpf/progs/test_xdp_meta.c@@ -1232,4 +1232,22 @@ int seg6local_skb_ext_read(struct __sk_buff *ctx) return BPF_OK; } +/* Read skb_ext from sk_skb stream verdict hook */ +SEC("sk_skb/stream_verdict") +int sk_skb_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 SK_PASS; + if (bpf_dynptr_read(meta_have, META_SIZE, &meta, 0, 0)) + return SK_PASS; + if (!check_metadata(meta_have)) + return SK_PASS; + + test_pass = true; + return SK_PASS; +} + char _license[] SEC("license") = "GPL";
--
2.43.0