From: Joanne Koong <joannelkoong@gmail.com>
To: bpf@vger.kernel.org
Cc: andrii@kernel.org, daniel@iogearbox.net, ast@kernel.org,
Joanne Koong <joannelkoong@gmail.com>
Subject: [PATCH bpf-next v1 3/3] selftests/bpf: tests for using dynptrs to parse skb and xdp buffers
Date: Tue, 26 Jul 2022 11:47:06 -0700 [thread overview]
Message-ID: <20220726184706.954822-4-joannelkoong@gmail.com> (raw)
In-Reply-To: <20220726184706.954822-1-joannelkoong@gmail.com>
Test skb and xdp dynptr functionality in the following ways:
1) progs/test_xdp.c
* Change existing test to use dynptrs to parse xdp data
There were no noticeable diferences in user + system time between
the original version vs. using dynptrs. Averaging the time for 10
runs (run using "time ./test_progs -t xdp_bpf2bpf"):
original version: 0.0449 sec
with dynptrs: 0.0429 sec
2) progs/test_l4lb_noinline.c
* Change existing test to use dynptrs to parse skb data
There were no noticeable diferences in user + system time between
the original version vs. using dynptrs. Averaging the time for 10
runs (run using "time ./test_progs -t l4lb_all/l4lb_noinline"):
original version: 0.0502 sec
with dynptrs: 0.055 sec
For number of processed verifier instructions:
original version: 6284 insns
with dynptrs: 2538 insns
3) progs/test_dynptr_xdp.c
* Add sample code for parsing tcp hdr opt lookup using dynptrs.
This logic is lifted from a real-world use case of packet parsing in
katran [0], a layer 4 load balancer
4) progs/dynptr_success.c
* Add test case "test_skb_readonly" for testing attempts at writes /
data slices on a prog type with read-only skb ctx.
5) progs/dynptr_fail.c
* Add test cases "skb_invalid_data_slice" and
"xdp_invalid_data_slice" for testing that helpers that modify the
underlying packet buffer automatically invalidate the associated
data slice.
* Add test cases "skb_invalid_ctx" and "xdp_invalid_ctx" for testing
that prog types that do not support bpf_dynptr_from_skb/xdp don't
have access to the API.
[0] https://github.com/facebookincubator/katran/blob/main/katran/lib/bpf/pckt_parsing.h
Signed-off-by: Joanne Koong <joannelkoong@gmail.com>
---
.../testing/selftests/bpf/prog_tests/dynptr.c | 85 ++++++++++---
.../selftests/bpf/prog_tests/dynptr_xdp.c | 49 ++++++++
.../testing/selftests/bpf/progs/dynptr_fail.c | 76 ++++++++++++
.../selftests/bpf/progs/dynptr_success.c | 32 +++++
.../selftests/bpf/progs/test_dynptr_xdp.c | 115 ++++++++++++++++++
.../selftests/bpf/progs/test_l4lb_noinline.c | 71 +++++------
tools/testing/selftests/bpf/progs/test_xdp.c | 95 +++++++--------
.../selftests/bpf/test_tcp_hdr_options.h | 1 +
8 files changed, 416 insertions(+), 108 deletions(-)
create mode 100644 tools/testing/selftests/bpf/prog_tests/dynptr_xdp.c
create mode 100644 tools/testing/selftests/bpf/progs/test_dynptr_xdp.c
diff --git a/tools/testing/selftests/bpf/prog_tests/dynptr.c b/tools/testing/selftests/bpf/prog_tests/dynptr.c
index bcf80b9f7c27..c40631f33c7b 100644
--- a/tools/testing/selftests/bpf/prog_tests/dynptr.c
+++ b/tools/testing/selftests/bpf/prog_tests/dynptr.c
@@ -2,6 +2,7 @@
/* Copyright (c) 2022 Facebook */
#include <test_progs.h>
+#include <network_helpers.h>
#include "dynptr_fail.skel.h"
#include "dynptr_success.skel.h"
@@ -11,8 +12,8 @@ static char obj_log_buf[1048576];
static struct {
const char *prog_name;
const char *expected_err_msg;
-} dynptr_tests[] = {
- /* failure cases */
+} verifier_error_tests[] = {
+ /* these cases should trigger a verifier error */
{"ringbuf_missing_release1", "Unreleased reference id=1"},
{"ringbuf_missing_release2", "Unreleased reference id=2"},
{"ringbuf_missing_release_callback", "Unreleased reference id"},
@@ -42,11 +43,25 @@ static struct {
{"release_twice_callback", "arg 1 is an unacquired reference"},
{"dynptr_from_mem_invalid_api",
"Unsupported reg type fp for bpf_dynptr_from_mem data"},
+ {"skb_invalid_data_slice", "invalid mem access 'scalar'"},
+ {"xdp_invalid_data_slice", "invalid mem access 'scalar'"},
+ {"skb_invalid_ctx", "unknown func bpf_dynptr_from_skb"},
+ {"xdp_invalid_ctx", "unknown func bpf_dynptr_from_xdp"},
+};
+
+enum test_setup_type {
+ SETUP_SYSCALL_SLEEP,
+ SETUP_SKB_PROG,
+};
- /* success cases */
- {"test_read_write", NULL},
- {"test_data_slice", NULL},
- {"test_ringbuf", NULL},
+static struct {
+ const char *prog_name;
+ enum test_setup_type type;
+} runtime_tests[] = {
+ {"test_read_write", SETUP_SYSCALL_SLEEP},
+ {"test_data_slice", SETUP_SYSCALL_SLEEP},
+ {"test_ringbuf", SETUP_SYSCALL_SLEEP},
+ {"test_skb_readonly", SETUP_SKB_PROG},
};
static void verify_fail(const char *prog_name, const char *expected_err_msg)
@@ -85,7 +100,7 @@ static void verify_fail(const char *prog_name, const char *expected_err_msg)
dynptr_fail__destroy(skel);
}
-static void verify_success(const char *prog_name)
+static void run_tests(const char *prog_name, enum test_setup_type setup_type)
{
struct dynptr_success *skel;
struct bpf_program *prog;
@@ -107,15 +122,42 @@ static void verify_success(const char *prog_name)
if (!ASSERT_OK_PTR(prog, "bpf_object__find_program_by_name"))
goto cleanup;
- link = bpf_program__attach(prog);
- if (!ASSERT_OK_PTR(link, "bpf_program__attach"))
- goto cleanup;
+ switch (setup_type) {
+ case SETUP_SYSCALL_SLEEP:
+ link = bpf_program__attach(prog);
+ if (!ASSERT_OK_PTR(link, "bpf_program__attach"))
+ goto cleanup;
- usleep(1);
+ usleep(1);
- ASSERT_EQ(skel->bss->err, 0, "err");
+ bpf_link__destroy(link);
+ break;
+ case SETUP_SKB_PROG:
+ {
+ int prog_fd, err;
+ char buf[64];
+
+ prog_fd = bpf_program__fd(prog);
+ if (CHECK_FAIL(prog_fd < 0))
+ goto cleanup;
+
+ LIBBPF_OPTS(bpf_test_run_opts, topts,
+ .data_in = &pkt_v4,
+ .data_size_in = sizeof(pkt_v4),
+ .data_out = buf,
+ .data_size_out = sizeof(buf),
+ .repeat = 1,
+ );
- bpf_link__destroy(link);
+ err = bpf_prog_test_run_opts(prog_fd, &topts);
+
+ if (!ASSERT_OK(err, "test_run"))
+ goto cleanup;
+
+ break;
+ }
+ }
+ ASSERT_EQ(skel->bss->err, 0, "err");
cleanup:
dynptr_success__destroy(skel);
@@ -125,14 +167,17 @@ void test_dynptr(void)
{
int i;
- for (i = 0; i < ARRAY_SIZE(dynptr_tests); i++) {
- if (!test__start_subtest(dynptr_tests[i].prog_name))
+ for (i = 0; i < ARRAY_SIZE(verifier_error_tests); i++) {
+ if (!test__start_subtest(verifier_error_tests[i].prog_name))
+ continue;
+
+ verify_fail(verifier_error_tests[i].prog_name,
+ verifier_error_tests[i].expected_err_msg);
+ }
+ for (i = 0; i < ARRAY_SIZE(runtime_tests); i++) {
+ if (!test__start_subtest(runtime_tests[i].prog_name))
continue;
- if (dynptr_tests[i].expected_err_msg)
- verify_fail(dynptr_tests[i].prog_name,
- dynptr_tests[i].expected_err_msg);
- else
- verify_success(dynptr_tests[i].prog_name);
+ run_tests(runtime_tests[i].prog_name, runtime_tests[i].type);
}
}
diff --git a/tools/testing/selftests/bpf/prog_tests/dynptr_xdp.c b/tools/testing/selftests/bpf/prog_tests/dynptr_xdp.c
new file mode 100644
index 000000000000..ca775d126b60
--- /dev/null
+++ b/tools/testing/selftests/bpf/prog_tests/dynptr_xdp.c
@@ -0,0 +1,49 @@
+// SPDX-License-Identifier: GPL-2.0
+#include <test_progs.h>
+#include <network_helpers.h>
+#include "test_dynptr_xdp.skel.h"
+#include "test_tcp_hdr_options.h"
+
+struct test_pkt {
+ struct ipv6_packet pk6_v6;
+ u8 options[16];
+} __packed;
+
+void test_dynptr_xdp(void)
+{
+ struct test_dynptr_xdp *skel;
+ char buf[128];
+ int err;
+
+ skel = test_dynptr_xdp__open_and_load();
+ if (!ASSERT_OK_PTR(skel, "skel_open_and_load"))
+ return;
+
+ struct test_pkt pkt = {
+ .pk6_v6.eth.h_proto = __bpf_constant_htons(ETH_P_IPV6),
+ .pk6_v6.iph.nexthdr = IPPROTO_TCP,
+ .pk6_v6.iph.payload_len = __bpf_constant_htons(MAGIC_BYTES),
+ .pk6_v6.tcp.urg_ptr = 123,
+ .pk6_v6.tcp.doff = 9, /* 16 bytes of options */
+
+ .options = {
+ TCPOPT_MSS, 4, 0x05, 0xB4, TCPOPT_NOP, TCPOPT_NOP,
+ skel->rodata->tcp_hdr_opt_kind_tpr, 6, 0, 0, 0, 9, TCPOPT_EOL
+ },
+ };
+
+ LIBBPF_OPTS(bpf_test_run_opts, topts,
+ .data_in = &pkt,
+ .data_size_in = sizeof(pkt),
+ .data_out = buf,
+ .data_size_out = sizeof(buf),
+ .repeat = 3,
+ );
+
+ err = bpf_prog_test_run_opts(bpf_program__fd(skel->progs.xdp_ingress_v6), &topts);
+ ASSERT_OK(err, "ipv6 test_run");
+ ASSERT_EQ(skel->bss->server_id, 0x9000000, "server id");
+ ASSERT_EQ(topts.retval, XDP_PASS, "ipv6 test_run retval");
+
+ test_dynptr_xdp__destroy(skel);
+}
diff --git a/tools/testing/selftests/bpf/progs/dynptr_fail.c b/tools/testing/selftests/bpf/progs/dynptr_fail.c
index c1814938a5fd..4e3f853b2d02 100644
--- a/tools/testing/selftests/bpf/progs/dynptr_fail.c
+++ b/tools/testing/selftests/bpf/progs/dynptr_fail.c
@@ -5,6 +5,7 @@
#include <string.h>
#include <linux/bpf.h>
#include <bpf/bpf_helpers.h>
+#include <linux/if_ether.h>
#include "bpf_misc.h"
char _license[] SEC("license") = "GPL";
@@ -622,3 +623,78 @@ int dynptr_from_mem_invalid_api(void *ctx)
return 0;
}
+
+/* The data slice is invalidated whenever a helper changes packet data */
+SEC("?tc")
+int skb_invalid_data_slice(struct __sk_buff *skb)
+{
+ struct bpf_dynptr ptr;
+ struct ethhdr *hdr;
+
+ bpf_dynptr_from_skb(skb, 0, &ptr);
+ hdr = bpf_dynptr_data(&ptr, 0, sizeof(*hdr));
+ if (!hdr)
+ return SK_DROP;
+
+ hdr->h_proto = 12;
+
+ if (bpf_skb_pull_data(skb, skb->len))
+ return SK_DROP;
+
+ /* this should fail */
+ hdr->h_proto = 1;
+
+ return SK_PASS;
+}
+
+/* The data slice is invalidated whenever a helper changes packet data */
+SEC("?xdp")
+int xdp_invalid_data_slice(struct xdp_md *xdp)
+{
+ struct bpf_dynptr ptr;
+ struct ethhdr *hdr1, *hdr2;
+
+ bpf_dynptr_from_xdp(xdp, 0, &ptr);
+ hdr1 = bpf_dynptr_data(&ptr, 0, sizeof(*hdr1));
+ if (!hdr1)
+ return XDP_DROP;
+
+ hdr2 = bpf_dynptr_data(&ptr, 0, sizeof(*hdr2));
+ if (!hdr2)
+ return XDP_DROP;
+
+ hdr1->h_proto = 12;
+ hdr2->h_proto = 12;
+
+ if (bpf_xdp_adjust_head(xdp, 0 - (int)sizeof(*hdr1)))
+ return XDP_DROP;
+
+ /* this should fail */
+ hdr2->h_proto = 1;
+
+ return XDP_PASS;
+}
+
+/* Only supported prog type can create skb-type dynptrs */
+SEC("?raw_tp/sys_nanosleep")
+int skb_invalid_ctx(void *ctx)
+{
+ struct bpf_dynptr ptr;
+
+ /* this should fail */
+ bpf_dynptr_from_skb(ctx, 0, &ptr);
+
+ return 0;
+}
+
+/* Only supported prog type can create xdp-type dynptrs */
+SEC("?raw_tp/sys_nanosleep")
+int xdp_invalid_ctx(void *ctx)
+{
+ struct bpf_dynptr ptr;
+
+ /* this should fail */
+ bpf_dynptr_from_xdp(ctx, 0, &ptr);
+
+ return 0;
+}
diff --git a/tools/testing/selftests/bpf/progs/dynptr_success.c b/tools/testing/selftests/bpf/progs/dynptr_success.c
index a3a6103c8569..ffddd6ddc7fb 100644
--- a/tools/testing/selftests/bpf/progs/dynptr_success.c
+++ b/tools/testing/selftests/bpf/progs/dynptr_success.c
@@ -162,3 +162,35 @@ int test_ringbuf(void *ctx)
bpf_ringbuf_discard_dynptr(&ptr, 0);
return 0;
}
+
+SEC("cgroup_skb/egress")
+int test_skb_readonly(void *ctx)
+{
+ __u8 write_data[2] = {1, 2};
+ struct bpf_dynptr ptr;
+ void *data;
+ int ret;
+
+ err = 1;
+
+ if (bpf_dynptr_from_skb(ctx, 0, &ptr))
+ return 0;
+ err++;
+
+ data = bpf_dynptr_data(&ptr, 0, 1);
+ if (data)
+ /* it's an error if data is not NULL since cgroup skbs
+ * are read only
+ */
+ return 0;
+ err++;
+
+ ret = bpf_dynptr_write(&ptr, 0, write_data, sizeof(write_data), 0);
+ /* since cgroup skbs are read only, writes should fail */
+ if (ret != -EINVAL)
+ return 0;
+
+ err = 0;
+
+ return 0;
+}
diff --git a/tools/testing/selftests/bpf/progs/test_dynptr_xdp.c b/tools/testing/selftests/bpf/progs/test_dynptr_xdp.c
new file mode 100644
index 000000000000..c879dfb6370a
--- /dev/null
+++ b/tools/testing/selftests/bpf/progs/test_dynptr_xdp.c
@@ -0,0 +1,115 @@
+// SPDX-License-Identifier: GPL-2.0
+
+/* This logic is lifted from a real-world use case of packet parsing, used in
+ * the open source library katran, a layer 4 load balancer.
+ *
+ * This test demonstrates how to parse packet contents using dynptrs.
+ *
+ * https://github.com/facebookincubator/katran/blob/main/katran/lib/bpf/pckt_parsing.h
+ */
+
+#include <string.h>
+#include <linux/bpf.h>
+#include <bpf/bpf_helpers.h>
+#include <linux/tcp.h>
+#include <stdbool.h>
+#include <linux/ipv6.h>
+#include <linux/if_ether.h>
+#include "test_tcp_hdr_options.h"
+
+char _license[] SEC("license") = "GPL";
+
+/* Arbitrarily picked unused value from IANA TCP Option Kind Numbers */
+const __u32 tcp_hdr_opt_kind_tpr = 0xB7;
+/* Length of the tcp header option */
+const __u32 tcp_hdr_opt_len_tpr = 6;
+/* maximum number of header options to check to lookup server_id */
+const __u32 tcp_hdr_opt_max_opt_checks = 15;
+
+__u32 server_id;
+
+static int parse_hdr_opt(struct bpf_dynptr *ptr, __u32 *off, __u8 *hdr_bytes_remaining,
+ __u32 *server_id)
+{
+ __u8 *tcp_opt, kind, hdr_len;
+ __u8 *data;
+
+ data = bpf_dynptr_data(ptr, *off, sizeof(kind) + sizeof(hdr_len) +
+ sizeof(*server_id));
+ if (!data)
+ return -1;
+
+ kind = data[0];
+
+ if (kind == TCPOPT_EOL)
+ return -1;
+
+ if (kind == TCPOPT_NOP) {
+ *off += 1;
+ /* continue to the next option */
+ *hdr_bytes_remaining -= 1;
+
+ return 0;
+ }
+
+ if (*hdr_bytes_remaining < 2)
+ return -1;
+
+ hdr_len = data[1];
+ if (hdr_len > *hdr_bytes_remaining)
+ return -1;
+
+ if (kind == tcp_hdr_opt_kind_tpr) {
+ if (hdr_len != tcp_hdr_opt_len_tpr)
+ return -1;
+
+ memcpy(server_id, (__u32 *)(data + 2), sizeof(*server_id));
+ return 1;
+ }
+
+ *off += hdr_len;
+ *hdr_bytes_remaining -= hdr_len;
+
+ return 0;
+}
+
+SEC("xdp")
+int xdp_ingress_v6(struct xdp_md *xdp)
+{
+ __u8 hdr_bytes_remaining;
+ struct tcphdr *tcp_hdr;
+ __u8 tcp_hdr_opt_len;
+ int err = 0;
+ __u32 off;
+
+ struct bpf_dynptr ptr;
+
+ bpf_dynptr_from_xdp(xdp, 0, &ptr);
+
+ off = sizeof(struct ethhdr) + sizeof(struct ipv6hdr);
+
+ tcp_hdr = bpf_dynptr_data(&ptr, off, sizeof(*tcp_hdr));
+ if (!tcp_hdr)
+ return XDP_DROP;
+
+ tcp_hdr_opt_len = (tcp_hdr->doff * 4) - sizeof(struct tcphdr);
+ if (tcp_hdr_opt_len < tcp_hdr_opt_len_tpr)
+ return XDP_DROP;
+
+ hdr_bytes_remaining = tcp_hdr_opt_len;
+
+ off += sizeof(struct tcphdr);
+
+ /* max number of bytes of options in tcp header is 40 bytes */
+ for (int i = 0; i < tcp_hdr_opt_max_opt_checks; i++) {
+ err = parse_hdr_opt(&ptr, &off, &hdr_bytes_remaining, &server_id);
+
+ if (err || !hdr_bytes_remaining)
+ break;
+ }
+
+ if (!server_id)
+ return XDP_DROP;
+
+ return XDP_PASS;
+}
diff --git a/tools/testing/selftests/bpf/progs/test_l4lb_noinline.c b/tools/testing/selftests/bpf/progs/test_l4lb_noinline.c
index c8bc0c6947aa..1fef7868ea8b 100644
--- a/tools/testing/selftests/bpf/progs/test_l4lb_noinline.c
+++ b/tools/testing/selftests/bpf/progs/test_l4lb_noinline.c
@@ -230,21 +230,18 @@ static __noinline bool get_packet_dst(struct real_definition **real,
return true;
}
-static __noinline int parse_icmpv6(void *data, void *data_end, __u64 off,
+static __noinline int parse_icmpv6(struct bpf_dynptr *skb_ptr, __u64 off,
struct packet_description *pckt)
{
struct icmp6hdr *icmp_hdr;
struct ipv6hdr *ip6h;
- icmp_hdr = data + off;
- if (icmp_hdr + 1 > data_end)
+ icmp_hdr = bpf_dynptr_data(skb_ptr, off, sizeof(*icmp_hdr) + sizeof(*ip6h));
+ if (!icmp_hdr)
return TC_ACT_SHOT;
if (icmp_hdr->icmp6_type != ICMPV6_PKT_TOOBIG)
return TC_ACT_OK;
- off += sizeof(struct icmp6hdr);
- ip6h = data + off;
- if (ip6h + 1 > data_end)
- return TC_ACT_SHOT;
+ ip6h = (struct ipv6hdr *)(icmp_hdr + 1);
pckt->proto = ip6h->nexthdr;
pckt->flags |= F_ICMP;
memcpy(pckt->srcv6, ip6h->daddr.s6_addr32, 16);
@@ -252,22 +249,19 @@ static __noinline int parse_icmpv6(void *data, void *data_end, __u64 off,
return TC_ACT_UNSPEC;
}
-static __noinline int parse_icmp(void *data, void *data_end, __u64 off,
+static __noinline int parse_icmp(struct bpf_dynptr *skb_ptr, __u64 off,
struct packet_description *pckt)
{
struct icmphdr *icmp_hdr;
struct iphdr *iph;
- icmp_hdr = data + off;
- if (icmp_hdr + 1 > data_end)
+ icmp_hdr = bpf_dynptr_data(skb_ptr, off, sizeof(*icmp_hdr) + sizeof(*iph));
+ if (!icmp_hdr)
return TC_ACT_SHOT;
if (icmp_hdr->type != ICMP_DEST_UNREACH ||
icmp_hdr->code != ICMP_FRAG_NEEDED)
return TC_ACT_OK;
- off += sizeof(struct icmphdr);
- iph = data + off;
- if (iph + 1 > data_end)
- return TC_ACT_SHOT;
+ iph = (struct iphdr *)(icmp_hdr + 1);
if (iph->ihl != 5)
return TC_ACT_SHOT;
pckt->proto = iph->protocol;
@@ -277,13 +271,13 @@ static __noinline int parse_icmp(void *data, void *data_end, __u64 off,
return TC_ACT_UNSPEC;
}
-static __noinline bool parse_udp(void *data, __u64 off, void *data_end,
+static __noinline bool parse_udp(struct bpf_dynptr *skb_ptr, __u64 off,
struct packet_description *pckt)
{
struct udphdr *udp;
- udp = data + off;
- if (udp + 1 > data_end)
+ udp = bpf_dynptr_data(skb_ptr, off, sizeof(*udp));
+ if (!udp)
return false;
if (!(pckt->flags & F_ICMP)) {
@@ -296,13 +290,13 @@ static __noinline bool parse_udp(void *data, __u64 off, void *data_end,
return true;
}
-static __noinline bool parse_tcp(void *data, __u64 off, void *data_end,
+static __noinline bool parse_tcp(struct bpf_dynptr *skb_ptr, __u64 off,
struct packet_description *pckt)
{
struct tcphdr *tcp;
- tcp = data + off;
- if (tcp + 1 > data_end)
+ tcp = bpf_dynptr_data(skb_ptr, off, sizeof(*tcp));
+ if (!tcp)
return false;
if (tcp->syn)
@@ -318,12 +312,11 @@ static __noinline bool parse_tcp(void *data, __u64 off, void *data_end,
return true;
}
-static __noinline int process_packet(void *data, __u64 off, void *data_end,
+static __noinline int process_packet(struct bpf_dynptr *skb_ptr,
+ struct eth_hdr *eth, __u64 off,
bool is_ipv6, struct __sk_buff *skb)
{
- void *pkt_start = (void *)(long)skb->data;
struct packet_description pckt = {};
- struct eth_hdr *eth = pkt_start;
struct bpf_tunnel_key tkey = {};
struct vip_stats *data_stats;
struct real_definition *dst;
@@ -344,8 +337,8 @@ static __noinline int process_packet(void *data, __u64 off, void *data_end,
tkey.tunnel_ttl = 64;
if (is_ipv6) {
- ip6h = data + off;
- if (ip6h + 1 > data_end)
+ ip6h = bpf_dynptr_data(skb_ptr, off, sizeof(*ip6h));
+ if (!ip6h)
return TC_ACT_SHOT;
iph_len = sizeof(struct ipv6hdr);
@@ -356,7 +349,7 @@ static __noinline int process_packet(void *data, __u64 off, void *data_end,
if (protocol == IPPROTO_FRAGMENT) {
return TC_ACT_SHOT;
} else if (protocol == IPPROTO_ICMPV6) {
- action = parse_icmpv6(data, data_end, off, &pckt);
+ action = parse_icmpv6(skb_ptr, off, &pckt);
if (action >= 0)
return action;
off += IPV6_PLUS_ICMP_HDR;
@@ -365,10 +358,8 @@ static __noinline int process_packet(void *data, __u64 off, void *data_end,
memcpy(pckt.dstv6, ip6h->daddr.s6_addr32, 16);
}
} else {
- iph = data + off;
- if (iph + 1 > data_end)
- return TC_ACT_SHOT;
- if (iph->ihl != 5)
+ iph = bpf_dynptr_data(skb_ptr, off, sizeof(*iph));
+ if (!iph || iph->ihl != 5)
return TC_ACT_SHOT;
protocol = iph->protocol;
@@ -379,7 +370,7 @@ static __noinline int process_packet(void *data, __u64 off, void *data_end,
if (iph->frag_off & PCKT_FRAGMENTED)
return TC_ACT_SHOT;
if (protocol == IPPROTO_ICMP) {
- action = parse_icmp(data, data_end, off, &pckt);
+ action = parse_icmp(skb_ptr, off, &pckt);
if (action >= 0)
return action;
off += IPV4_PLUS_ICMP_HDR;
@@ -391,10 +382,10 @@ static __noinline int process_packet(void *data, __u64 off, void *data_end,
protocol = pckt.proto;
if (protocol == IPPROTO_TCP) {
- if (!parse_tcp(data, off, data_end, &pckt))
+ if (!parse_tcp(skb_ptr, off, &pckt))
return TC_ACT_SHOT;
} else if (protocol == IPPROTO_UDP) {
- if (!parse_udp(data, off, data_end, &pckt))
+ if (!parse_udp(skb_ptr, off, &pckt))
return TC_ACT_SHOT;
} else {
return TC_ACT_SHOT;
@@ -450,20 +441,22 @@ static __noinline int process_packet(void *data, __u64 off, void *data_end,
SEC("tc")
int balancer_ingress(struct __sk_buff *ctx)
{
- void *data_end = (void *)(long)ctx->data_end;
- void *data = (void *)(long)ctx->data;
- struct eth_hdr *eth = data;
+ struct bpf_dynptr ptr;
+ struct eth_hdr *eth;
__u32 eth_proto;
__u32 nh_off;
nh_off = sizeof(struct eth_hdr);
- if (data + nh_off > data_end)
+
+ bpf_dynptr_from_skb(ctx, 0, &ptr);
+ eth = bpf_dynptr_data(&ptr, 0, sizeof(*eth));
+ if (!eth)
return TC_ACT_SHOT;
eth_proto = eth->eth_proto;
if (eth_proto == bpf_htons(ETH_P_IP))
- return process_packet(data, nh_off, data_end, false, ctx);
+ return process_packet(&ptr, eth, nh_off, false, ctx);
else if (eth_proto == bpf_htons(ETH_P_IPV6))
- return process_packet(data, nh_off, data_end, true, ctx);
+ return process_packet(&ptr, eth, nh_off, true, ctx);
else
return TC_ACT_SHOT;
}
diff --git a/tools/testing/selftests/bpf/progs/test_xdp.c b/tools/testing/selftests/bpf/progs/test_xdp.c
index d7a9a74b7245..2272b56a8777 100644
--- a/tools/testing/selftests/bpf/progs/test_xdp.c
+++ b/tools/testing/selftests/bpf/progs/test_xdp.c
@@ -20,6 +20,12 @@
#include <bpf/bpf_endian.h>
#include "test_iptunnel_common.h"
+const size_t tcphdr_sz = sizeof(struct tcphdr);
+const size_t udphdr_sz = sizeof(struct udphdr);
+const size_t ethhdr_sz = sizeof(struct ethhdr);
+const size_t iphdr_sz = sizeof(struct iphdr);
+const size_t ipv6hdr_sz = sizeof(struct ipv6hdr);
+
struct {
__uint(type, BPF_MAP_TYPE_PERCPU_ARRAY);
__uint(max_entries, 256);
@@ -43,8 +49,7 @@ static __always_inline void count_tx(__u32 protocol)
*rxcnt_count += 1;
}
-static __always_inline int get_dport(void *trans_data, void *data_end,
- __u8 protocol)
+static __always_inline int get_dport(void *trans_data, __u8 protocol)
{
struct tcphdr *th;
struct udphdr *uh;
@@ -52,13 +57,9 @@ static __always_inline int get_dport(void *trans_data, void *data_end,
switch (protocol) {
case IPPROTO_TCP:
th = (struct tcphdr *)trans_data;
- if (th + 1 > data_end)
- return -1;
return th->dest;
case IPPROTO_UDP:
uh = (struct udphdr *)trans_data;
- if (uh + 1 > data_end)
- return -1;
return uh->dest;
default:
return 0;
@@ -75,14 +76,13 @@ static __always_inline void set_ethhdr(struct ethhdr *new_eth,
new_eth->h_proto = h_proto;
}
-static __always_inline int handle_ipv4(struct xdp_md *xdp)
+static __always_inline int handle_ipv4(struct xdp_md *xdp, struct bpf_dynptr *xdp_ptr)
{
- void *data_end = (void *)(long)xdp->data_end;
- void *data = (void *)(long)xdp->data;
+ struct bpf_dynptr new_xdp_ptr;
struct iptnl_info *tnl;
struct ethhdr *new_eth;
struct ethhdr *old_eth;
- struct iphdr *iph = data + sizeof(struct ethhdr);
+ struct iphdr *iph;
__u16 *next_iph;
__u16 payload_len;
struct vip vip = {};
@@ -90,10 +90,12 @@ static __always_inline int handle_ipv4(struct xdp_md *xdp)
__u32 csum = 0;
int i;
- if (iph + 1 > data_end)
+ iph = bpf_dynptr_data(xdp_ptr, ethhdr_sz,
+ iphdr_sz + (tcphdr_sz > udphdr_sz ? tcphdr_sz : udphdr_sz));
+ if (!iph)
return XDP_DROP;
- dport = get_dport(iph + 1, data_end, iph->protocol);
+ dport = get_dport(iph + 1, iph->protocol);
if (dport == -1)
return XDP_DROP;
@@ -108,37 +110,33 @@ static __always_inline int handle_ipv4(struct xdp_md *xdp)
if (!tnl || tnl->family != AF_INET)
return XDP_PASS;
- if (bpf_xdp_adjust_head(xdp, 0 - (int)sizeof(struct iphdr)))
+ if (bpf_xdp_adjust_head(xdp, 0 - (int)iphdr_sz))
return XDP_DROP;
- data = (void *)(long)xdp->data;
- data_end = (void *)(long)xdp->data_end;
-
- new_eth = data;
- iph = data + sizeof(*new_eth);
- old_eth = data + sizeof(*iph);
-
- if (new_eth + 1 > data_end ||
- old_eth + 1 > data_end ||
- iph + 1 > data_end)
+ bpf_dynptr_from_xdp(xdp, 0, &new_xdp_ptr);
+ new_eth = bpf_dynptr_data(&new_xdp_ptr, 0, ethhdr_sz + iphdr_sz + ethhdr_sz);
+ if (!new_eth)
return XDP_DROP;
+ iph = (struct iphdr *)(new_eth + 1);
+ old_eth = (struct ethhdr *)(iph + 1);
+
set_ethhdr(new_eth, old_eth, tnl, bpf_htons(ETH_P_IP));
iph->version = 4;
- iph->ihl = sizeof(*iph) >> 2;
+ iph->ihl = iphdr_sz >> 2;
iph->frag_off = 0;
iph->protocol = IPPROTO_IPIP;
iph->check = 0;
iph->tos = 0;
- iph->tot_len = bpf_htons(payload_len + sizeof(*iph));
+ iph->tot_len = bpf_htons(payload_len + iphdr_sz);
iph->daddr = tnl->daddr.v4;
iph->saddr = tnl->saddr.v4;
iph->ttl = 8;
next_iph = (__u16 *)iph;
#pragma clang loop unroll(full)
- for (i = 0; i < sizeof(*iph) >> 1; i++)
+ for (i = 0; i < iphdr_sz >> 1; i++)
csum += *next_iph++;
iph->check = ~((csum & 0xffff) + (csum >> 16));
@@ -148,22 +146,23 @@ static __always_inline int handle_ipv4(struct xdp_md *xdp)
return XDP_TX;
}
-static __always_inline int handle_ipv6(struct xdp_md *xdp)
+static __always_inline int handle_ipv6(struct xdp_md *xdp, struct bpf_dynptr *xdp_ptr)
{
- void *data_end = (void *)(long)xdp->data_end;
- void *data = (void *)(long)xdp->data;
+ struct bpf_dynptr new_xdp_ptr;
struct iptnl_info *tnl;
struct ethhdr *new_eth;
struct ethhdr *old_eth;
- struct ipv6hdr *ip6h = data + sizeof(struct ethhdr);
+ struct ipv6hdr *ip6h;
__u16 payload_len;
struct vip vip = {};
int dport;
- if (ip6h + 1 > data_end)
+ ip6h = bpf_dynptr_data(xdp_ptr, ethhdr_sz,
+ ipv6hdr_sz + (tcphdr_sz > udphdr_sz ? tcphdr_sz : udphdr_sz));
+ if (!ip6h)
return XDP_DROP;
- dport = get_dport(ip6h + 1, data_end, ip6h->nexthdr);
+ dport = get_dport(ip6h + 1, ip6h->nexthdr);
if (dport == -1)
return XDP_DROP;
@@ -178,26 +177,23 @@ static __always_inline int handle_ipv6(struct xdp_md *xdp)
if (!tnl || tnl->family != AF_INET6)
return XDP_PASS;
- if (bpf_xdp_adjust_head(xdp, 0 - (int)sizeof(struct ipv6hdr)))
+ if (bpf_xdp_adjust_head(xdp, 0 - (int)ipv6hdr_sz))
return XDP_DROP;
- data = (void *)(long)xdp->data;
- data_end = (void *)(long)xdp->data_end;
-
- new_eth = data;
- ip6h = data + sizeof(*new_eth);
- old_eth = data + sizeof(*ip6h);
-
- if (new_eth + 1 > data_end || old_eth + 1 > data_end ||
- ip6h + 1 > data_end)
+ bpf_dynptr_from_xdp(xdp, 0, &new_xdp_ptr);
+ new_eth = bpf_dynptr_data(&new_xdp_ptr, 0, ethhdr_sz + ipv6hdr_sz + ethhdr_sz);
+ if (!new_eth)
return XDP_DROP;
+ ip6h = (struct ipv6hdr *)(new_eth + 1);
+ old_eth = (struct ethhdr *)(ip6h + 1);
+
set_ethhdr(new_eth, old_eth, tnl, bpf_htons(ETH_P_IPV6));
ip6h->version = 6;
ip6h->priority = 0;
memset(ip6h->flow_lbl, 0, sizeof(ip6h->flow_lbl));
- ip6h->payload_len = bpf_htons(bpf_ntohs(payload_len) + sizeof(*ip6h));
+ ip6h->payload_len = bpf_htons(bpf_ntohs(payload_len) + ipv6hdr_sz);
ip6h->nexthdr = IPPROTO_IPV6;
ip6h->hop_limit = 8;
memcpy(ip6h->saddr.s6_addr32, tnl->saddr.v6, sizeof(tnl->saddr.v6));
@@ -211,21 +207,22 @@ static __always_inline int handle_ipv6(struct xdp_md *xdp)
SEC("xdp")
int _xdp_tx_iptunnel(struct xdp_md *xdp)
{
- void *data_end = (void *)(long)xdp->data_end;
- void *data = (void *)(long)xdp->data;
- struct ethhdr *eth = data;
+ struct bpf_dynptr ptr;
+ struct ethhdr *eth;
__u16 h_proto;
- if (eth + 1 > data_end)
+ bpf_dynptr_from_xdp(xdp, 0, &ptr);
+ eth = bpf_dynptr_data(&ptr, 0, ethhdr_sz);
+ if (!eth)
return XDP_DROP;
h_proto = eth->h_proto;
if (h_proto == bpf_htons(ETH_P_IP))
- return handle_ipv4(xdp);
+ return handle_ipv4(xdp, &ptr);
else if (h_proto == bpf_htons(ETH_P_IPV6))
- return handle_ipv6(xdp);
+ return handle_ipv6(xdp, &ptr);
else
return XDP_DROP;
}
diff --git a/tools/testing/selftests/bpf/test_tcp_hdr_options.h b/tools/testing/selftests/bpf/test_tcp_hdr_options.h
index 6118e3ab61fc..56c9f8a3ad3d 100644
--- a/tools/testing/selftests/bpf/test_tcp_hdr_options.h
+++ b/tools/testing/selftests/bpf/test_tcp_hdr_options.h
@@ -50,6 +50,7 @@ struct linum_err {
#define TCPOPT_EOL 0
#define TCPOPT_NOP 1
+#define TCPOPT_MSS 2
#define TCPOPT_WINDOW 3
#define TCPOPT_EXP 254
--
2.30.2
next prev parent reply other threads:[~2022-07-26 18:48 UTC|newest]
Thread overview: 52+ messages / expand[flat|nested] mbox.gz Atom feed top
2022-07-26 18:47 [PATCH bpf-next v1 0/3] Add skb + xdp dynptrs Joanne Koong
2022-07-26 18:47 ` [PATCH bpf-next v1 1/3] bpf: Add skb dynptrs Joanne Koong
2022-07-27 17:13 ` sdf
2022-07-28 16:49 ` Joanne Koong
2022-07-28 17:28 ` Stanislav Fomichev
2022-07-28 17:45 ` Hao Luo
2022-07-28 18:36 ` Joanne Koong
2022-07-28 23:39 ` Martin KaFai Lau
2022-07-29 20:26 ` Joanne Koong
2022-07-29 21:39 ` Martin KaFai Lau
2022-08-01 17:52 ` Joanne Koong
2022-08-01 19:38 ` Martin KaFai Lau
2022-08-01 21:16 ` Joanne Koong
2022-08-01 22:14 ` Andrii Nakryiko
2022-08-01 22:32 ` Martin KaFai Lau
2022-08-01 22:58 ` Andrii Nakryiko
2022-08-01 23:23 ` Martin KaFai Lau
2022-08-02 0:56 ` Martin KaFai Lau
2022-08-02 3:51 ` Andrii Nakryiko
2022-08-02 4:53 ` Joanne Koong
2022-08-02 5:14 ` Joanne Koong
2022-08-03 20:29 ` Joanne Koong
2022-08-03 20:36 ` Andrii Nakryiko
2022-08-03 20:56 ` Martin KaFai Lau
2022-08-03 23:25 ` Jakub Kicinski
2022-08-04 1:05 ` Joanne Koong
2022-08-04 1:34 ` Jakub Kicinski
2022-08-04 3:44 ` Joanne Koong
2022-08-04 1:27 ` Martin KaFai Lau
2022-08-04 1:44 ` Jakub Kicinski
2022-08-04 22:58 ` Kumar Kartikeya Dwivedi
2022-08-05 23:25 ` Jakub Kicinski
2022-08-01 22:11 ` Andrii Nakryiko
2022-08-02 0:15 ` Joanne Koong
2022-08-01 23:33 ` Jakub Kicinski
2022-08-02 2:12 ` Joanne Koong
2022-08-04 21:55 ` Joanne Koong
2022-08-05 23:22 ` Jakub Kicinski
2022-08-03 6:37 ` Martin KaFai Lau
2022-07-26 18:47 ` [PATCH bpf-next v1 2/3] bpf: Add xdp dynptrs Joanne Koong
2022-07-26 18:47 ` Joanne Koong [this message]
2022-07-26 19:44 ` [PATCH bpf-next v1 3/3] selftests/bpf: tests for using dynptrs to parse skb and xdp buffers Zvi Effron
2022-07-26 20:06 ` Joanne Koong
2022-08-01 17:58 ` Andrii Nakryiko
2022-08-02 22:56 ` Joanne Koong
2022-08-03 0:53 ` Andrii Nakryiko
2022-08-03 16:11 ` Joanne Koong
2022-08-04 18:45 ` Alexei Starovoitov
2022-08-05 16:29 ` Joanne Koong
2022-08-01 19:12 ` Alexei Starovoitov
2022-08-02 22:21 ` Joanne Koong
2022-08-04 21:46 ` Joanne Koong
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20220726184706.954822-4-joannelkoong@gmail.com \
--to=joannelkoong@gmail.com \
--cc=andrii@kernel.org \
--cc=ast@kernel.org \
--cc=bpf@vger.kernel.org \
--cc=daniel@iogearbox.net \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox