[<prev] [next>] [<thread-prev] [thread-next>] [day] [month] [year] [list]
Message-ID: <20211006230543.3928580-4-joannekoong@fb.com>
Date: Wed, 6 Oct 2021 16:05:43 -0700
From: Joanne Koong <joannekoong@...com>
To: <bpf@...r.kernel.org>
CC: <kafai@...com>, <netdev@...r.kernel.org>, <Kernel-team@...com>,
Joanne Koong <joannekoong@...com>
Subject: [PATCH bpf-next v2 3/3] bpf/selftests: Add xdp bpf_load_tcp_hdr_options tests
This patch adds tests for bpf_load_tcp_hdr_options used by xdp
programs.
test_xdp_tcp_hdr_options.c:
- Tests ipv4 and ipv6 packets with TCPOPT_EXP and non-TCPOPT_EXP
tcp options set. Verify that options can be parsed and loaded
successfully.
- Tests error paths: TCPOPT_EXP with invalid magic, option with
invalid kind_len, non-existent option, invalid flags, option size
smaller than kind_len, invalid packet
Signed-off-by: Joanne Koong <joannekoong@...com>
---
.../bpf/prog_tests/xdp_tcp_hdr_options.c | 144 +++++++++++++
.../bpf/progs/test_xdp_tcp_hdr_options.c | 198 ++++++++++++++++++
2 files changed, 342 insertions(+)
create mode 100644 tools/testing/selftests/bpf/prog_tests/xdp_tcp_hdr_options.c
create mode 100644 tools/testing/selftests/bpf/progs/test_xdp_tcp_hdr_options.c
diff --git a/tools/testing/selftests/bpf/prog_tests/xdp_tcp_hdr_options.c b/tools/testing/selftests/bpf/prog_tests/xdp_tcp_hdr_options.c
new file mode 100644
index 000000000000..2148199f2fcc
--- /dev/null
+++ b/tools/testing/selftests/bpf/prog_tests/xdp_tcp_hdr_options.c
@@ -0,0 +1,144 @@
+// SPDX-License-Identifier: GPL-2.0
+/* Copyright (c) 2021 Facebook */
+
+#include "test_progs.h"
+#include "network_helpers.h"
+#include "test_tcp_hdr_options.h"
+#include "test_xdp_tcp_hdr_options.skel.h"
+
+struct xdp_exprm_opt {
+ __u8 kind;
+ __u8 len;
+ __u16 magic;
+ struct bpf_test_option data;
+} __packed;
+
+struct xdp_regular_opt {
+ __u8 kind;
+ __u8 len;
+ struct bpf_test_option data;
+} __packed;
+
+struct xdp_test_opt {
+ struct xdp_exprm_opt exprm_opt;
+ struct xdp_regular_opt regular_opt;
+} __packed;
+
+struct xdp_ipv4_packet {
+ struct ipv4_packet pkt_v4;
+ struct xdp_test_opt test_opt;
+} __packed;
+
+struct xdp_ipv6_packet {
+ struct ipv6_packet pkt_v6;
+ struct xdp_test_opt test_opt;
+} __packed;
+
+static __u8 opt_flags = OPTION_MAX_DELACK_MS | OPTION_RAND;
+static __u8 exprm_max_delack_ms = 12;
+static __u8 regular_max_delack_ms = 21;
+static __u8 exprm_rand = 0xfa;
+static __u8 regular_rand = 0xce;
+
+static void init_test_opt(struct xdp_test_opt *test_opt,
+ struct test_xdp_tcp_hdr_options *skel)
+{
+ test_opt->exprm_opt.kind = TCPOPT_EXP;
+ /* +1 for kind, +1 for kind-len, +2 for magic, +1 for flags, +1 for
+ * OPTION_MAX_DELACK_MAX, +1 FOR OPTION_RAND
+ */
+ test_opt->exprm_opt.len = 3 + TCP_BPF_EXPOPT_BASE_LEN;
+ test_opt->exprm_opt.magic = __bpf_htons(skel->rodata->test_magic);
+ test_opt->exprm_opt.data.flags = opt_flags;
+ test_opt->exprm_opt.data.max_delack_ms = exprm_max_delack_ms;
+ test_opt->exprm_opt.data.rand = exprm_rand;
+
+ test_opt->regular_opt.kind = skel->rodata->test_kind;
+ /* +1 for kind, +1 for kind-len, +1 for flags, +1 FOR
+ * OPTION_MAX_DELACK_MS, +1 FOR OPTION_RAND
+ */
+ test_opt->regular_opt.len = 5;
+ test_opt->regular_opt.data.flags = opt_flags;
+ test_opt->regular_opt.data.max_delack_ms = regular_max_delack_ms;
+ test_opt->regular_opt.data.rand = regular_rand;
+}
+
+static void check_opt_out(struct test_xdp_tcp_hdr_options *skel)
+{
+ struct bpf_test_option *opt_out;
+
+ opt_out = &skel->bss->exprm_opt_out;
+ ASSERT_EQ(opt_out->flags, opt_flags, "check exprm flags");
+ ASSERT_EQ(opt_out->max_delack_ms, exprm_max_delack_ms,
+ "check exprm max_delack_ms");
+ ASSERT_EQ(opt_out->rand, exprm_rand, "check exprm rand");
+
+ opt_out = &skel->bss->regular_opt_out;
+ ASSERT_EQ(opt_out->flags, opt_flags, "check regular flags");
+ ASSERT_EQ(opt_out->max_delack_ms, regular_max_delack_ms,
+ "check regular max_delack_ms");
+ ASSERT_EQ(opt_out->rand, regular_rand, "check regular rand");
+}
+
+void test_xdp_tcp_hdr_options(void)
+{
+ int err, prog_fd, prog_err_path_fd, prog_invalid_pkt_fd;
+ struct xdp_ipv6_packet ipv6_pkt, invalid_pkt;
+ struct test_xdp_tcp_hdr_options *skel;
+ struct xdp_ipv4_packet ipv4_pkt;
+ struct xdp_test_opt test_opt;
+ __u32 duration, retval, size;
+ char buf[128];
+
+ /* Load XDP program to introspect */
+ skel = test_xdp_tcp_hdr_options__open_and_load();
+ if (!ASSERT_OK_PTR(skel, "skel open and load"))
+ return;
+
+ prog_fd = bpf_program__fd(skel->progs._xdp_load_hdr_opt);
+
+ init_test_opt(&test_opt, skel);
+
+ /* Init the packets */
+ ipv4_pkt.pkt_v4 = pkt_v4;
+ ipv4_pkt.pkt_v4.tcp.doff += 3;
+ ipv4_pkt.test_opt = test_opt;
+
+ ipv6_pkt.pkt_v6 = pkt_v6;
+ ipv6_pkt.pkt_v6.tcp.doff += 3;
+ ipv6_pkt.test_opt = test_opt;
+
+ invalid_pkt.pkt_v6 = pkt_v6;
+ /* Set to an offset that will exceed the xdp data_end */
+ invalid_pkt.pkt_v6.tcp.doff += 4;
+ invalid_pkt.test_opt = test_opt;
+
+ /* Test on ipv4 packet */
+ err = bpf_prog_test_run(prog_fd, 1, &ipv4_pkt, sizeof(ipv4_pkt),
+ buf, &size, &retval, &duration);
+ if (ASSERT_TRUE(!err && retval == XDP_PASS, "xdp_tcp_hdr_options ipv4"))
+ check_opt_out(skel);
+
+ /* Test on ipv6 packet */
+ err = bpf_prog_test_run(prog_fd, 1, &ipv6_pkt, sizeof(ipv6_pkt),
+ buf, &size, &retval, &duration);
+ if (ASSERT_TRUE(!err && retval == XDP_PASS, "xdp_tcp_hdr_options ipv6"))
+ check_opt_out(skel);
+
+ /* Test error paths */
+ prog_err_path_fd =
+ bpf_program__fd(skel->progs._xdp_load_hdr_opt_err_paths);
+ err = bpf_prog_test_run(prog_err_path_fd, 1, &ipv6_pkt, sizeof(ipv6_pkt),
+ buf, &size, &retval, &duration);
+ ASSERT_TRUE(!err && retval == XDP_PASS, "xdp_tcp_hdr_options err_path");
+
+ /* Test invalid packet */
+ prog_invalid_pkt_fd =
+ bpf_program__fd(skel->progs._xdp_load_hdr_opt_invalid_pkt);
+ err = bpf_prog_test_run(prog_invalid_pkt_fd, 1, &invalid_pkt,
+ sizeof(invalid_pkt), buf, &size, &retval,
+ &duration);
+ ASSERT_TRUE(!err && retval == XDP_PASS, "xdp_tcp_hdr_options invalid_pkt");
+
+ test_xdp_tcp_hdr_options__destroy(skel);
+}
diff --git a/tools/testing/selftests/bpf/progs/test_xdp_tcp_hdr_options.c b/tools/testing/selftests/bpf/progs/test_xdp_tcp_hdr_options.c
new file mode 100644
index 000000000000..3fe6e1ebd78a
--- /dev/null
+++ b/tools/testing/selftests/bpf/progs/test_xdp_tcp_hdr_options.c
@@ -0,0 +1,198 @@
+// SPDX-License-Identifier: GPL-2.0
+/* Copyright (c) 2021 Facebook */
+
+#include <errno.h>
+#include <stdbool.h>
+#include <string.h>
+#include <linux/bpf.h>
+#include <linux/if_ether.h>
+#include <linux/ip.h>
+#include <linux/ipv6.h>
+#include <linux/tcp.h>
+#include <bpf/bpf_helpers.h>
+#include <bpf/bpf_endian.h>
+#define BPF_PROG_TEST_TCP_HDR_OPTIONS
+#include "test_tcp_hdr_options.h"
+
+struct bpf_test_option regular_opt_out;
+struct bpf_test_option exprm_opt_out;
+
+const __u16 test_magic = 0xeB9F;
+const __u8 test_kind = 0xB9;
+
+int err_val = 0;
+
+static void copy_opt_to_out(struct bpf_test_option *test_option, __u8 *data)
+{
+ test_option->flags = data[0];
+ test_option->max_delack_ms = data[1];
+ test_option->rand = data[2];
+}
+
+static int parse_xdp(struct xdp_md *xdp, __u64 *out_flags)
+{
+ void *data_end = (void *)(long)xdp->data_end;
+ __u64 tcphdr_offset = 0, nh_off;
+ void *data = (void *)(long)xdp->data;
+ struct ethhdr *eth = data;
+ int ret;
+
+ nh_off = sizeof(*eth);
+ if (data + nh_off > data_end) {
+ err_val = 1;
+ return XDP_DROP;
+ }
+
+ /* Calculate the offset to the tcp hdr */
+ if (eth->h_proto == __bpf_constant_htons(ETH_P_IPV6)) {
+ tcphdr_offset = sizeof(struct ethhdr) +
+ sizeof(struct ipv6hdr);
+ } else if (eth->h_proto == bpf_htons(ETH_P_IP)) {
+ tcphdr_offset = sizeof(struct ethhdr) +
+ sizeof(struct iphdr);
+ } else {
+ err_val = 2;
+ return XDP_DROP;
+ }
+
+ *out_flags = tcphdr_offset << BPF_LOAD_HDR_OPT_TCP_OFFSET_SHIFT;
+
+ return XDP_PASS;
+}
+
+SEC("xdp")
+int _xdp_load_hdr_opt(struct xdp_md *xdp)
+{
+ struct tcp_exprm_opt exprm_opt = { 0 };
+ struct tcp_opt regular_opt = { 0 };
+ __u64 flags = 0;
+ int ret;
+
+ ret = parse_xdp(xdp, &flags);
+ if (ret != XDP_PASS)
+ return ret;
+
+ /* Test TCPOPT_EXP */
+ exprm_opt.kind = TCPOPT_EXP;
+ exprm_opt.len = 4;
+ exprm_opt.magic = __bpf_htons(test_magic);
+ ret = bpf_load_hdr_opt(xdp, &exprm_opt,
+ sizeof(exprm_opt), flags);
+ if (ret < 0) {
+ err_val = 3;
+ return XDP_DROP;
+ }
+
+ copy_opt_to_out(&exprm_opt_out, exprm_opt.data);
+
+ /* Test non-TCP_OPT_EXP */
+ regular_opt.kind = test_kind;
+ ret = bpf_load_hdr_opt(xdp, ®ular_opt,
+ sizeof(regular_opt), flags);
+ if (ret < 0) {
+ err_val = 4;
+ return XDP_DROP;
+ }
+
+ copy_opt_to_out(®ular_opt_out, regular_opt.data);
+
+ return XDP_PASS;
+}
+
+SEC("xdp")
+int _xdp_load_hdr_opt_err_paths(struct xdp_md *xdp)
+{
+ struct tcp_exprm_opt exprm_opt = { 0 };
+ struct tcp_opt regular_opt = { 0 };
+ __u64 flags = 0;
+ int ret;
+
+ ret = parse_xdp(xdp, &flags);
+ if (ret != XDP_PASS)
+ return ret;
+
+ /* Test TCPOPT_EXP with invalid magic */
+ exprm_opt.kind = TCPOPT_EXP;
+ exprm_opt.len = 4;
+ exprm_opt.magic = __bpf_htons(test_magic + 1);
+ ret = bpf_load_hdr_opt(xdp, &exprm_opt,
+ sizeof(exprm_opt), flags);
+ if (ret != -ENOMSG) {
+ err_val = 3;
+ return XDP_DROP;
+ }
+
+ /* Test TCPOPT_EXP with 0 magic */
+ exprm_opt.magic = 0;
+ ret = bpf_load_hdr_opt(xdp, &exprm_opt,
+ sizeof(exprm_opt), flags);
+ if (ret != -ENOMSG) {
+ err_val = 4;
+ return XDP_DROP;
+ }
+
+ exprm_opt.magic = __bpf_htons(test_magic);
+
+ /* Test TCPOPT_EXP with invalid kind length */
+ exprm_opt.len = 5;
+ ret = bpf_load_hdr_opt(xdp, &exprm_opt,
+ sizeof(exprm_opt), flags);
+ if (ret != -EINVAL) {
+ err_val = 5;
+ return XDP_DROP;
+ }
+
+ /* Test that non-existent option is not found */
+ regular_opt.kind = test_kind + 1;
+ ret = bpf_load_hdr_opt(xdp, ®ular_opt,
+ sizeof(regular_opt), flags);
+ if (ret != -ENOMSG) {
+ err_val = 6;
+ return XDP_DROP;
+ }
+
+ /* Test invalid flags */
+ regular_opt.kind = test_kind;
+ ret = bpf_load_hdr_opt(xdp, ®ular_opt, sizeof(regular_opt),
+ flags | BPF_LOAD_HDR_OPT_TCP_SYN);
+ if (ret != -EINVAL) {
+ err_val = 7;
+ return XDP_DROP;
+ }
+
+ /* Test non-TCP_OPT_EXP with option size smaller than kind len */
+ ret = bpf_load_hdr_opt(xdp, ®ular_opt,
+ sizeof(regular_opt) - 2, flags);
+ if (ret != -ENOSPC) {
+ err_val = 8;
+ return XDP_DROP;
+ }
+
+ return XDP_PASS;
+}
+
+SEC("xdp")
+int _xdp_load_hdr_opt_invalid_pkt(struct xdp_md *xdp)
+{
+ struct tcp_exprm_opt exprm_opt = { 0 };
+ __u64 flags = 0;
+ int ret;
+
+ ret = parse_xdp(xdp, &flags);
+ if (ret != XDP_PASS)
+ return ret;
+
+ exprm_opt.kind = TCPOPT_EXP;
+ exprm_opt.len = 4;
+ exprm_opt.magic = __bpf_htons(test_magic);
+ ret = bpf_load_hdr_opt(xdp, &exprm_opt,
+ sizeof(exprm_opt), flags);
+ if (ret != -EINVAL) {
+ err_val = 3;
+ return XDP_DROP;
+ }
+
+ return XDP_PASS;
+}
+
+char _license[] SEC("license") = "GPL";
--
2.30.2
Powered by blists - more mailing lists