Write skb_ext at cgroup/skb egress, send UDP to a closed loopback port so the packet is dropped, and read the extension back from a tp_btf/kfree_skb program. Signed-off-by: Jakub Sitnicki --- .../bpf/prog_tests/xdp_context_test_run.c | 65 ++++++++++++++++++++++ tools/testing/selftests/bpf/progs/test_xdp_meta.c | 34 +++++++++++ 2 files changed, 99 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 e0ab0fccf9d7..1e4e12d2b041 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 @@ -1186,6 +1186,69 @@ static void test_skb_ext_tcp(struct test_xdp_meta *skel, const char *name, netns_free(ns); } +/* Test skb_ext survival until skb free: cgroup/skb egress -> kfree_skb. + * Send UDP to a closed port. The packet is dropped, triggering kfree_skb. + */ +static void test_cgrp_egress_to_kfree_skb(struct test_xdp_meta *skel) +{ + struct sockaddr_in addr = { + .sin_family = AF_INET, + .sin_port = htons(4321), + .sin_addr.s_addr = htonl(INADDR_LOOPBACK), + }; + struct bpf_link *cg_link = NULL; + struct bpf_link *tp_link = NULL; + struct netns_obj *ns = NULL; + int cgroup_fd = -1; + char buf[1]; + int fd = -1; + int ret; + + cgroup_fd = test__join_cgroup("/cgrp_to_kfree"); + if (!ASSERT_GE(cgroup_fd, 0, "join_cgroup")) + return; + + ns = netns_new("cgrp_to_kfree", true); + if (!ASSERT_OK_PTR(ns, "netns_new")) + goto cleanup; + + skel->bss->test_pass = false; + + cg_link = bpf_program__attach_cgroup(skel->progs.cgrp_skb_ext_write, + cgroup_fd); + if (!ASSERT_OK_PTR(cg_link, "attach_cgroup")) + goto cleanup; + + tp_link = bpf_program__attach_trace(skel->progs.tp_kfree_skb_ext_read); + if (!ASSERT_OK_PTR(tp_link, "attach_tp")) + goto cleanup; + + fd = socket(AF_INET, SOCK_DGRAM, 0); + if (!ASSERT_GE(fd, 0, "socket")) + goto cleanup; + + ret = connect(fd, (void *)&addr, sizeof(addr)); + if (!ASSERT_OK(ret, "connect")) + goto cleanup; + + send(fd, test_payload, TEST_PAYLOAD_LEN, 0); + + /* Wait for ICMP error -- confirms the packet was freed */ + ret = recv(fd, buf, sizeof(buf), 0); + ASSERT_EQ(errno, ECONNREFUSED, "recv_econnrefused"); + + ASSERT_TRUE(skel->bss->test_pass, "test_pass"); + +cleanup: + if (fd >= 0) + close(fd); + bpf_link__destroy(tp_link); + bpf_link__destroy(cg_link); + netns_free(ns); + if (cgroup_fd >= 0) + close(cgroup_fd); +} + void test_skb_ext_cross_hook(void) { struct test_xdp_meta *skel = NULL; @@ -1202,6 +1265,8 @@ void test_skb_ext_cross_hook(void) test_skb_ext_tcp(skel, "tc_to_lsm", READER_LSM); if (test__start_subtest("tc_to_skops")) test_skb_ext_tcp(skel, "tc_to_skops", READER_SKOPS); + if (test__start_subtest("cgrp_egress_to_kfree_skb")) + test_cgrp_egress_to_kfree_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 8f0a0fea5d84..415b33b32ef5 100644 --- a/tools/testing/selftests/bpf/progs/test_xdp_meta.c +++ b/tools/testing/selftests/bpf/progs/test_xdp_meta.c @@ -1032,4 +1032,38 @@ int sk_filter_skb_ext_read(struct __sk_buff *ctx) return ctx->len; } +/* Write skb_ext from cgroup/skb egress */ +SEC("cgroup_skb/egress") +int cgrp_skb_ext_write(struct __sk_buff *ctx) +{ + struct bpf_dynptr meta; + + if (!is_test_packet_tc(ctx)) + return 1; + + if (bpf_dynptr_from_skb_ext(ctx, 0, BPF_SKB_EXT_F_CREATE, &meta)) + return 1; + bpf_dynptr_write(&meta, 0, (void *)meta_want, META_SIZE, 0); + + return 1; +} + +/* Read skb_ext from tp_btf/kfree_skb -- tests survival until skb free */ +SEC("tp_btf/kfree_skb") +int BPF_PROG(tp_kfree_skb_ext_read, struct sk_buff *skb) +{ + __u8 meta_have[META_SIZE]; + struct bpf_dynptr meta; + + 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