Extend skb_ext test coverage to hook on TCP ingress path: attach a TC ingress program on loopback that writes the metadata to packets, and read it back from a sock_ops program (BPF_SOCK_OPS_PASSIVE_ESTABLISHED_CB, reaching the skb via bpf_sock_ops_kern->skb) and from the LSM inet_conn_established hook. Signed-off-by: Jakub Sitnicki --- .../bpf/prog_tests/xdp_context_test_run.c | 92 ++++++++++++++++++++++ tools/testing/selftests/bpf/progs/test_xdp_meta.c | 75 ++++++++++++++++++ 2 files changed, 167 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 808eb158fe0f..e0ab0fccf9d7 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 @@ -1098,6 +1098,94 @@ static void test_skb_ext_udp(struct test_xdp_meta *skel, const char *name, netns_free(ns); } +enum tcp_reader_type { + READER_SKOPS, + READER_LSM, +}; + +/* Test skb_ext survival across TC ingress -> TCP reader hook */ +static void test_skb_ext_tcp(struct test_xdp_meta *skel, const char *name, + enum tcp_reader_type reader) +{ + 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 *reader_link = NULL; + struct netns_obj *ns = NULL; + int server_fd = -1; + int cgroup_fd = -1; + int client_fd = -1; + int conn_fd = -1; + __be16 port; + int ret; + + ns = netns_new(name, true); + if (!ASSERT_OK_PTR(ns, "netns_new")) + return; + + cgroup_fd = test__join_cgroup(name); + if (!ASSERT_GE(cgroup_fd, 0, "join_cgroup")) + goto cleanup; + + server_fd = start_server(AF_INET, SOCK_STREAM, "127.0.0.1", 0, 0); + if (!ASSERT_GE(server_fd, 0, "start_server")) + goto cleanup; + + port = get_socket_local_port(server_fd); + if (!ASSERT_GE(port, 0, "get_port")) + goto cleanup; + + skel->bss->target_port = port; + 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; + + switch (reader) { + case READER_SKOPS: + reader_link = bpf_program__attach_cgroup(skel->progs.skops_skb_ext_read, + cgroup_fd); + if (!ASSERT_OK_PTR(reader_link, "attach_skops")) + goto cleanup; + break; + case READER_LSM: + reader_link = bpf_program__attach_lsm(skel->progs.lsm_skb_ext_read); + if (!ASSERT_OK_PTR(reader_link, "attach_lsm")) + goto cleanup; + break; + } + + client_fd = connect_to_fd(server_fd, 0); + if (!ASSERT_GE(client_fd, 0, "connect")) + goto cleanup; + + conn_fd = accept(server_fd, NULL, NULL); + if (!ASSERT_GE(conn_fd, 0, "accept")) + goto cleanup; + + ASSERT_TRUE(skel->bss->test_pass, "test_pass"); + +cleanup: + if (conn_fd >= 0) + close(conn_fd); + if (client_fd >= 0) + close(client_fd); + bpf_link__destroy(reader_link); + bpf_tc_hook_destroy(&tc_hook); + if (server_fd >= 0) + close(server_fd); + if (cgroup_fd >= 0) + close(cgroup_fd); + netns_free(ns); +} + void test_skb_ext_cross_hook(void) { struct test_xdp_meta *skel = NULL; @@ -1110,6 +1198,10 @@ void test_skb_ext_cross_hook(void) test_skb_ext_udp(skel, "tc_to_cgrp_ingress", READER_CGRP_SKB); if (test__start_subtest("tc_to_sk_filter")) test_skb_ext_udp(skel, "tc_to_sk_filter", READER_SK_FILTER); + if (test__start_subtest("tc_to_lsm")) + 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); 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 0a3afb5c3461..8f0a0fea5d84 100644 --- a/tools/testing/selftests/bpf/progs/test_xdp_meta.c +++ b/tools/testing/selftests/bpf/progs/test_xdp_meta.c @@ -938,6 +938,81 @@ int cgrp_skb_ext_read(struct __sk_buff *ctx) return 1; } +volatile __be16 target_port; + +#define TCPV4_HDR_OFF (sizeof(struct ethhdr) + sizeof(struct iphdr)) +#define TCPV4_SPORT_OFF (TCPV4_HDR_OFF + offsetof(struct tcphdr, source)) +#define TCPV4_DPORT_OFF (TCPV4_HDR_OFF + offsetof(struct tcphdr, dest)) + +/* Write skb_ext on TCP packets to/from target_port */ +SEC("tc") +int tc_skb_ext_write_port(struct __sk_buff *ctx) +{ + struct bpf_dynptr meta; + __be16 sport, dport; + + if (ctx->protocol != __bpf_constant_htons(ETH_P_IP)) + return TC_ACT_UNSPEC; + if (bpf_skb_load_bytes(ctx, TCPV4_SPORT_OFF, &sport, sizeof(sport))) + return TC_ACT_UNSPEC; + if (bpf_skb_load_bytes(ctx, TCPV4_DPORT_OFF, &dport, sizeof(dport))) + return TC_ACT_UNSPEC; + if (sport != target_port && dport != target_port) + return TC_ACT_UNSPEC; + + if (bpf_dynptr_from_skb_ext(ctx, 0, BPF_SKB_EXT_F_CREATE, &meta)) + return TC_ACT_UNSPEC; + bpf_dynptr_write(&meta, 0, (void *)meta_want, META_SIZE, 0); + + return TC_ACT_UNSPEC; +} + +/* Read skb_ext from sock_ops passive established -- tests TC -> sock_ops path */ +SEC("sockops") +int skops_skb_ext_read(struct bpf_sock_ops *ctx) +{ + struct bpf_sock_ops_kern *kctx; + __u8 meta_have[META_SIZE]; + struct bpf_dynptr meta; + struct sk_buff *skb; + + if (ctx->op != BPF_SOCK_OPS_PASSIVE_ESTABLISHED_CB) + return 1; + + kctx = bpf_cast_to_kern_ctx(ctx); + skb = kctx->skb; + if (!skb) + return 1; + + if (bpf_dynptr_from_skb_ext((struct __sk_buff *)skb, 0, 0, &meta)) + return 1; + if (bpf_dynptr_read(meta_have, META_SIZE, &meta, 0, 0)) + return 1; + if (!check_metadata(meta_have)) + return 1; + + test_pass = true; + return 1; +} + +/* Read skb_ext from LSM inet_conn_established -- tests TC -> LSM path */ +SEC("lsm/inet_conn_established") +int BPF_PROG(lsm_skb_ext_read, struct sock *sk, 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; +} + /* Read skb_ext from socket filter -- tests TC -> sk_filter path */ SEC("socket") int sk_filter_skb_ext_read(struct __sk_buff *ctx) -- 2.43.0