Add cross-hook tests for bpf_dynptr_from_skb_ext() covering all generic LWT program types. For lwt_in, a TC ingress program writes skb_ext on loopback and the LWT program reads it back. For lwt_out and lwt_xmit, the LWT programs write skb_ext on the output path and a TC ingress program reads it back after loopback. Signed-off-by: Jakub Sitnicki --- .../bpf/prog_tests/xdp_context_test_run.c | 98 ++++++++++++++++++++++ tools/testing/selftests/bpf/progs/test_xdp_meta.c | 45 ++++++++++ 2 files changed, 143 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 82c57ea3957e..7a7537ef80e7 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 @@ -1303,6 +1303,95 @@ static void test_skb_ext_nf(struct test_xdp_meta *skel, const char *name) netns_free(ns); } +#define LWT_EXT_PIN_PATH "/sys/fs/bpf/skb_ext_lwt" + +/* Test skb_ext across LWT hooks on loopback. + * + * @lwt_prog: BPF program to pin and attach via ip route encap + * @encap_dir: "in", "out", or "xmit" + * @writer: true if lwt_prog writes skb_ext (reader on TC ingress), + * false if lwt_prog reads skb_ext (writer on TC ingress) + */ +static void test_skb_ext_lwt(struct test_xdp_meta *skel, const char *name, + struct bpf_program *lwt_prog, + const char *encap_dir, bool writer) +{ + 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 sockaddr_in addr = { + .sin_family = AF_INET, + }; + struct bpf_program *tc_prog; + char buf[TEST_PAYLOAD_LEN]; + struct netns_obj *ns = NULL; + bool pinned = false; + int server_fd = -1; + int fd = -1; + __be16 port; + int ret; + + unlink(LWT_EXT_PIN_PATH); + ret = bpf_program__pin(lwt_prog, LWT_EXT_PIN_PATH); + if (!ASSERT_OK(ret, "pin lwt")) + return; + pinned = true; + + ns = netns_new(name, true); + if (!ASSERT_OK_PTR(ns, "netns_new")) + goto cleanup; + + SYS(cleanup, "ip addr add 10.0.0.1/32 dev lo"); + SYS(cleanup, "ip route replace table local local 10.0.0.1 " + "encap bpf %s pinned " LWT_EXT_PIN_PATH " dev lo", + encap_dir); + + server_fd = start_server(AF_INET, SOCK_DGRAM, "10.0.0.1", 0, 0); + if (!ASSERT_GE(server_fd, 0, "start_server")) + goto cleanup; + + skel->bss->test_pass = false; + + ret = bpf_tc_hook_create(&tc_hook); + if (!ASSERT_OK(ret, "bpf_tc_hook_create")) + goto cleanup; + + /* When LWT writes, TC ingress reads; when LWT reads, TC ingress writes */ + tc_prog = writer ? skel->progs.tc_skb_ext_read + : skel->progs.tc_skb_ext_write; + tc_opts.prog_fd = bpf_program__fd(tc_prog); + ret = bpf_tc_attach(&tc_hook, &tc_opts); + if (!ASSERT_OK(ret, "bpf_tc_attach")) + goto cleanup; + + port = get_socket_local_port(server_fd); + if (!ASSERT_GE(port, 0, "get_port")) + goto cleanup; + + fd = socket(AF_INET, SOCK_DGRAM, 0); + if (!ASSERT_GE(fd, 0, "socket")) + goto cleanup; + + inet_pton(AF_INET, "10.0.0.1", &addr.sin_addr); + addr.sin_port = port; + sendto(fd, test_payload, TEST_PAYLOAD_LEN, 0, + (void *)&addr, sizeof(addr)); + recvfrom(server_fd, buf, sizeof(buf), 0, NULL, NULL); + + ASSERT_TRUE(skel->bss->test_pass, "test_pass"); + +cleanup: + if (fd >= 0) + close(fd); + bpf_tc_hook_destroy(&tc_hook); + if (server_fd >= 0) + close(server_fd); + netns_free(ns); + if (pinned) + unlink(LWT_EXT_PIN_PATH); +} + void test_skb_ext_cross_hook(void) { struct test_xdp_meta *skel = NULL; @@ -1323,6 +1412,15 @@ void test_skb_ext_cross_hook(void) test_cgrp_egress_to_kfree_skb(skel); if (test__start_subtest("tc_to_nf")) test_skb_ext_nf(skel, "tc_to_nf"); + if (test__start_subtest("tc_to_lwt_in")) + test_skb_ext_lwt(skel, "tc_to_lwt_in", + skel->progs.lwt_in_skb_ext_read, "in", false); + if (test__start_subtest("lwt_out_to_tc")) + test_skb_ext_lwt(skel, "lwt_out_to_tc", + skel->progs.lwt_out_skb_ext_write, "out", true); + if (test__start_subtest("lwt_xmit_to_tc")) + test_skb_ext_lwt(skel, "lwt_xmit_to_tc", + skel->progs.lwt_xmit_skb_ext_write, "xmit", true); 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 0688120f3d40..97c1760fbd6d 100644 --- a/tools/testing/selftests/bpf/progs/test_xdp_meta.c +++ b/tools/testing/selftests/bpf/progs/test_xdp_meta.c @@ -1089,4 +1089,49 @@ int nf_skb_ext_read(struct bpf_nf_ctx *ctx) return NF_ACCEPT; } +SEC("lwt_in") +int lwt_in_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 BPF_OK; + if (bpf_dynptr_read(meta_have, META_SIZE, &meta, 0, 0)) + return BPF_OK; + if (!check_metadata(meta_have)) + return BPF_OK; + + test_pass = true; + return BPF_OK; +} + +SEC("lwt_out") +int lwt_out_skb_ext_write(struct __sk_buff *ctx) +{ + struct bpf_dynptr meta; + + if (!is_test_packet_tc(ctx)) + return BPF_OK; + if (bpf_dynptr_from_skb_ext(ctx, 0, BPF_SKB_EXT_F_CREATE, &meta)) + return BPF_OK; + bpf_dynptr_write(&meta, 0, (void *)meta_want, ARRAY_SIZE(meta_want), 0); + + return BPF_OK; +} + +SEC("lwt_xmit") +int lwt_xmit_skb_ext_write(struct __sk_buff *ctx) +{ + struct bpf_dynptr meta; + + if (!is_test_packet_tc(ctx)) + return BPF_OK; + if (bpf_dynptr_from_skb_ext(ctx, 0, BPF_SKB_EXT_F_CREATE, &meta)) + return BPF_OK; + bpf_dynptr_write(&meta, 0, (void *)meta_want, ARRAY_SIZE(meta_want), 0); + + return BPF_OK; +} + char _license[] SEC("license") = "GPL"; -- 2.43.0