* [PATCH bpf-next v2 2/3] selftests/bpf: Test for IPv6 ext header parsing
[not found] <20211207225635.113904-1-mathjadin@gmail.com>
@ 2021-12-07 22:56 ` Mathieu Jadin
2021-12-13 21:44 ` Andrii Nakryiko
2021-12-07 22:56 ` [PATCH bpf-next v2 3/3] selftests/bpf: Improve test tcpbpf_user robustness Mathieu Jadin
1 sibling, 1 reply; 3+ messages in thread
From: Mathieu Jadin @ 2021-12-07 22:56 UTC (permalink / raw)
To: bpf
Cc: Mathieu Jadin, John Fastabend, Yonghong Song, linux-kselftest,
Martin KaFai Lau, netdev, Shuah Khan, KP Singh, Daniel Borkmann,
Alexei Starovoitov, Song Liu, Andrii Nakryiko
This test creates a client and a server exchanging a single byte
with a Segment Routing Header and the eBPF program saves
the inner segment in a sk_storage. The test program checks that
the segment is correct.
Signed-off-by: Mathieu Jadin <mathjadin@gmail.com>
---
.../bpf/prog_tests/tcp_ipv6_exthdr_srh.c | 171 ++++++++++++++++++
.../selftests/bpf/progs/tcp_ipv6_exthdr_srh.c | 78 ++++++++
2 files changed, 249 insertions(+)
create mode 100644 tools/testing/selftests/bpf/prog_tests/tcp_ipv6_exthdr_srh.c
create mode 100644 tools/testing/selftests/bpf/progs/tcp_ipv6_exthdr_srh.c
diff --git a/tools/testing/selftests/bpf/prog_tests/tcp_ipv6_exthdr_srh.c b/tools/testing/selftests/bpf/prog_tests/tcp_ipv6_exthdr_srh.c
new file mode 100644
index 000000000000..70f7ee230975
--- /dev/null
+++ b/tools/testing/selftests/bpf/prog_tests/tcp_ipv6_exthdr_srh.c
@@ -0,0 +1,171 @@
+// SPDX-License-Identifier: GPL-2.0
+#include <test_progs.h>
+#include <linux/seg6.h>
+#include "cgroup_helpers.h"
+#include "network_helpers.h"
+
+struct tcp_srh_storage {
+ struct in6_addr inner_segment;
+};
+
+static void send_byte(int fd)
+{
+ char b = 0x55;
+
+ if (CHECK_FAIL(send(fd, &b, sizeof(b), 0) != 1))
+ perror("Failed to send single byte");
+}
+
+static int verify_srh(int map_fd, int server_fd, struct ipv6_sr_hdr *client_srh)
+{
+ int err = 0;
+ struct tcp_srh_storage val;
+
+ if (CHECK_FAIL(bpf_map_lookup_elem(map_fd, &server_fd, &val) < 0)) {
+ perror("Failed to read socket storage");
+ return -1;
+ }
+
+ if (memcmp(&val.inner_segment, &client_srh->segments[1],
+ sizeof(struct in6_addr))) {
+ log_err("The inner segment of the received SRH differs from the sent one");
+ err++;
+ }
+
+ return err;
+}
+
+static int run_test(int cgroup_fd, int listen_fd)
+{
+ struct bpf_prog_load_attr attr = {
+ .prog_type = BPF_PROG_TYPE_SOCK_OPS,
+ .file = "./tcp_ipv6_exthdr_srh.o",
+ .expected_attach_type = BPF_CGROUP_SOCK_OPS,
+ };
+ size_t srh_size = sizeof(struct ipv6_sr_hdr) +
+ 2 * sizeof(struct in6_addr);
+ struct ipv6_sr_hdr *client_srh;
+ struct bpf_object *obj;
+ struct bpf_map *map;
+ struct timeval tv;
+ int client_fd;
+ int server_fd;
+ int prog_fd;
+ int map_fd;
+ char byte;
+ int err;
+
+ err = bpf_prog_load_xattr(&attr, &obj, &prog_fd);
+ if (err) {
+ log_err("Failed to load BPF object");
+ return -1;
+ }
+
+ map = bpf_object__next_map(obj, NULL);
+ map_fd = bpf_map__fd(map);
+
+ err = bpf_prog_attach(prog_fd, cgroup_fd, BPF_CGROUP_SOCK_OPS, 0);
+ if (err) {
+ log_err("Failed to attach BPF program");
+ goto close_bpf_object;
+ }
+
+ client_fd = connect_to_fd(listen_fd, 0);
+ if (client_fd < 0) {
+ err = -1;
+ goto close_bpf_object;
+ }
+
+ server_fd = accept(listen_fd, NULL, 0);
+ if (server_fd < 0) {
+ err = -1;
+ goto close_client_fd;
+ }
+
+ /* Set an SRH with ::1 as an intermediate segment on the client */
+
+ client_srh = calloc(1, srh_size);
+ if (!client_srh) {
+ log_err("Failed to create the SRH to send");
+ goto close_server_fd;
+ }
+ client_srh->type = IPV6_SRCRT_TYPE_4;
+ // We do not count the first 8 bytes (RFC 8200 Section 4.4)
+ client_srh->hdrlen = (2 * sizeof(struct in6_addr)) >> 3;
+ client_srh->segments_left = 1;
+ client_srh->first_segment = 1;
+ // client_srh->segments[0] is set by the kernel
+ memcpy(&client_srh->segments[1], &in6addr_loopback,
+ sizeof(struct in6_addr));
+
+ if (setsockopt(client_fd, SOL_IPV6, IPV6_RTHDR, client_srh,
+ srh_size)) {
+ log_err("Failed to set the SRH on the client");
+ goto free_srh;
+ }
+
+ /* Send traffic with this SRH
+ * and check its parsing on the server side
+ */
+
+ tv.tv_sec = 1;
+ tv.tv_usec = 0;
+ if (setsockopt(server_fd, SOL_SOCKET, SO_RCVTIMEO, (const char *)&tv,
+ sizeof(tv))) {
+ log_err("Failed to set the receive timeout on the server");
+ err = -1;
+ goto free_srh;
+ }
+
+ send_byte(client_fd);
+ if (recv(server_fd, &byte, 1, 0) != 1) {
+ log_err("Failed to get the byte under one second on the server 2");
+ err = -1;
+ goto free_srh;
+ }
+
+ err += verify_srh(map_fd, server_fd, client_srh);
+
+free_srh:
+ free(client_srh);
+close_server_fd:
+ close(server_fd);
+close_client_fd:
+ close(client_fd);
+close_bpf_object:
+ bpf_object__close(obj);
+ return err;
+}
+
+void test_tcp_ipv6_exthdr_srh(void)
+{
+ int server_fd, cgroup_fd;
+
+ cgroup_fd = test__join_cgroup("/tcp_ipv6_exthdr_srh");
+ if (CHECK_FAIL(cgroup_fd < 0))
+ return;
+
+ server_fd = start_server(AF_INET6, SOCK_STREAM, "::1", 0, 0);
+ if (CHECK_FAIL(server_fd < 0))
+ goto close_cgroup_fd;
+
+ if (CHECK_FAIL(system("sysctl net.ipv6.conf.all.seg6_enabled=1")))
+ goto close_server;
+
+ if (CHECK_FAIL(system("sysctl net.ipv6.conf.lo.seg6_enabled=1")))
+ goto reset_sysctl;
+
+ CHECK_FAIL(run_test(cgroup_fd, server_fd));
+
+ if (CHECK_FAIL(system("sysctl net.ipv6.conf.lo.seg6_enabled=0")))
+ log_err("Cannot reset sysctl net.ipv6.conf.lo.seg6_enabled to 0");
+
+reset_sysctl:
+ if (CHECK_FAIL(system("sysctl net.ipv6.conf.all.seg6_enabled=0")))
+ log_err("Cannot reset sysctl net.ipv6.conf.all.seg6_enabled to 0");
+
+close_server:
+ close(server_fd);
+close_cgroup_fd:
+ close(cgroup_fd);
+}
diff --git a/tools/testing/selftests/bpf/progs/tcp_ipv6_exthdr_srh.c b/tools/testing/selftests/bpf/progs/tcp_ipv6_exthdr_srh.c
new file mode 100644
index 000000000000..cb4b4e23e337
--- /dev/null
+++ b/tools/testing/selftests/bpf/progs/tcp_ipv6_exthdr_srh.c
@@ -0,0 +1,78 @@
+// SPDX-License-Identifier: GPL-2.0
+#include <linux/types.h>
+#include <bpf/bpf_helpers.h>
+#include <linux/in6.h>
+#include <linux/ipv6.h>
+#include <linux/seg6.h>
+#include <linux/bpf.h>
+
+char _license[] SEC("license") = "GPL";
+
+#define NEXTHDR_ROUTING 43
+
+struct tcp_srh_storage {
+ struct in6_addr inner_segment;
+};
+
+struct {
+ __uint(type, BPF_MAP_TYPE_SK_STORAGE);
+ __uint(map_flags, BPF_F_NO_PREALLOC);
+ __type(key, int);
+ __type(value, struct tcp_srh_storage);
+} socket_storage_map SEC(".maps");
+
+/* Check the header received from the active side */
+static int read_incoming_srh(struct bpf_sock_ops *skops,
+ struct tcp_srh_storage *storage)
+{
+ __u32 seg_size = 2 * sizeof(struct in6_addr);
+ struct ipv6_sr_hdr *srh;
+ struct ipv6hdr *ip6;
+ void *seg_list;
+ int ret = 1;
+
+ ip6 = (struct ipv6hdr *)skops->skb_data;
+ if (ip6 + 1 <= skops->skb_data_end && ip6->nexthdr == NEXTHDR_ROUTING) {
+ srh = (struct ipv6_sr_hdr *)(ip6 + 1);
+ if (srh + 1 <= skops->skb_data_end) {
+ if (srh->type != IPV6_SRCRT_TYPE_4)
+ return ret;
+
+ seg_list = (void *)(srh + 1);
+ if (seg_list + seg_size <= skops->skb_data_end) {
+ // This is an SRH with at least 2 segments
+ storage->inner_segment = srh->segments[1];
+ ret = 0;
+ }
+ }
+ }
+
+ return ret;
+}
+
+SEC("sockops")
+int srh_read(struct bpf_sock_ops *skops)
+{
+ struct tcp_srh_storage *storage;
+ int true_val = 1;
+
+ if (!skops->sk)
+ return 1;
+
+ storage = bpf_sk_storage_get(&socket_storage_map, skops->sk, 0,
+ BPF_SK_STORAGE_GET_F_CREATE);
+ if (!storage)
+ return 1;
+
+ switch (skops->op) {
+ case BPF_SOCK_OPS_ACTIVE_ESTABLISHED_CB:
+ case BPF_SOCK_OPS_PASSIVE_ESTABLISHED_CB:
+ bpf_sock_ops_cb_flags_set(skops, skops->bpf_sock_ops_cb_flags |
+ BPF_SOCK_OPS_PARSE_IPV6_HDR_CB_FLAG);
+ break;
+ case BPF_SOCK_OPS_PARSE_IPV6_HDR_CB:
+ return read_incoming_srh(skops, storage);
+ }
+
+ return 0;
+}
--
2.32.0
^ permalink raw reply related [flat|nested] 3+ messages in thread
* [PATCH bpf-next v2 3/3] selftests/bpf: Improve test tcpbpf_user robustness
[not found] <20211207225635.113904-1-mathjadin@gmail.com>
2021-12-07 22:56 ` [PATCH bpf-next v2 2/3] selftests/bpf: Test for IPv6 ext header parsing Mathieu Jadin
@ 2021-12-07 22:56 ` Mathieu Jadin
1 sibling, 0 replies; 3+ messages in thread
From: Mathieu Jadin @ 2021-12-07 22:56 UTC (permalink / raw)
To: bpf
Cc: Mathieu Jadin, Song Liu, linux-kselftest, Dave Marchevsky,
Andrii Nakryiko, Shuah Khan, KP Singh, Yonghong Song,
Martin KaFai Lau, John Fastabend, netdev, Daniel Borkmann,
Alexei Starovoitov
Allow the test to support any number of supported callback flags.
Provided that BPF_SOCK_OPS_ALL_CB_FLAGS is correctly updated when new
flags are added, left shifting it always leads to a non existing flag.
Signed-off-by: Mathieu Jadin <mathjadin@gmail.com>
---
tools/testing/selftests/bpf/prog_tests/tcpbpf_user.c | 4 +++-
tools/testing/selftests/bpf/progs/test_tcpbpf_kern.c | 9 +++++++--
2 files changed, 10 insertions(+), 3 deletions(-)
diff --git a/tools/testing/selftests/bpf/prog_tests/tcpbpf_user.c b/tools/testing/selftests/bpf/prog_tests/tcpbpf_user.c
index 87923d2865b7..56d007bf4011 100644
--- a/tools/testing/selftests/bpf/prog_tests/tcpbpf_user.c
+++ b/tools/testing/selftests/bpf/prog_tests/tcpbpf_user.c
@@ -12,6 +12,8 @@ static __u32 duration;
static void verify_result(struct tcpbpf_globals *result)
{
+ __u32 non_existing_flag = (BPF_SOCK_OPS_ALL_CB_FLAGS << 1) &
+ ~BPF_SOCK_OPS_ALL_CB_FLAGS;
__u32 expected_events = ((1 << BPF_SOCK_OPS_TIMEOUT_INIT) |
(1 << BPF_SOCK_OPS_RWND_INIT) |
(1 << BPF_SOCK_OPS_TCP_CONNECT_CB) |
@@ -30,7 +32,7 @@ static void verify_result(struct tcpbpf_globals *result)
ASSERT_EQ(result->bytes_acked, 1002, "bytes_acked");
ASSERT_EQ(result->data_segs_in, 1, "data_segs_in");
ASSERT_EQ(result->data_segs_out, 1, "data_segs_out");
- ASSERT_EQ(result->bad_cb_test_rv, 0x80, "bad_cb_test_rv");
+ ASSERT_EQ(result->bad_cb_test_rv, non_existing_flag, "bad_cb_test_rv");
ASSERT_EQ(result->good_cb_test_rv, 0, "good_cb_test_rv");
ASSERT_EQ(result->num_listen, 1, "num_listen");
diff --git a/tools/testing/selftests/bpf/progs/test_tcpbpf_kern.c b/tools/testing/selftests/bpf/progs/test_tcpbpf_kern.c
index 3ded05280757..c37ba5940e3d 100644
--- a/tools/testing/selftests/bpf/progs/test_tcpbpf_kern.c
+++ b/tools/testing/selftests/bpf/progs/test_tcpbpf_kern.c
@@ -43,6 +43,8 @@ SEC("sockops")
int bpf_testcb(struct bpf_sock_ops *skops)
{
char header[sizeof(struct ipv6hdr) + sizeof(struct tcphdr)];
+ __u32 non_existing_flag = (BPF_SOCK_OPS_ALL_CB_FLAGS << 1) &
+ ~BPF_SOCK_OPS_ALL_CB_FLAGS;
struct bpf_sock_ops *reuse = skops;
struct tcphdr *thdr;
int window_clamp = 9216;
@@ -104,8 +106,11 @@ int bpf_testcb(struct bpf_sock_ops *skops)
global.window_clamp_client = get_tp_window_clamp(skops);
break;
case BPF_SOCK_OPS_ACTIVE_ESTABLISHED_CB:
- /* Test failure to set largest cb flag (assumes not defined) */
- global.bad_cb_test_rv = bpf_sock_ops_cb_flags_set(skops, 0x80);
+ /* Test failure to set largest cb flag
+ * (assumes that BPF_SOCK_OPS_ALL_CB_FLAGS masks all cb flags)
+ */
+ global.bad_cb_test_rv = bpf_sock_ops_cb_flags_set(skops,
+ non_existing_flag);
/* Set callback */
global.good_cb_test_rv = bpf_sock_ops_cb_flags_set(skops,
BPF_SOCK_OPS_STATE_CB_FLAG);
--
2.32.0
^ permalink raw reply related [flat|nested] 3+ messages in thread
* Re: [PATCH bpf-next v2 2/3] selftests/bpf: Test for IPv6 ext header parsing
2021-12-07 22:56 ` [PATCH bpf-next v2 2/3] selftests/bpf: Test for IPv6 ext header parsing Mathieu Jadin
@ 2021-12-13 21:44 ` Andrii Nakryiko
0 siblings, 0 replies; 3+ messages in thread
From: Andrii Nakryiko @ 2021-12-13 21:44 UTC (permalink / raw)
To: Mathieu Jadin
Cc: bpf, John Fastabend, Yonghong Song,
open list:KERNEL SELFTEST FRAMEWORK, Martin KaFai Lau,
Networking, Shuah Khan, KP Singh, Daniel Borkmann,
Alexei Starovoitov, Song Liu, Andrii Nakryiko
On Tue, Dec 7, 2021 at 2:58 PM Mathieu Jadin <mathjadin@gmail.com> wrote:
>
> This test creates a client and a server exchanging a single byte
> with a Segment Routing Header and the eBPF program saves
> the inner segment in a sk_storage. The test program checks that
> the segment is correct.
>
> Signed-off-by: Mathieu Jadin <mathjadin@gmail.com>
> ---
> .../bpf/prog_tests/tcp_ipv6_exthdr_srh.c | 171 ++++++++++++++++++
> .../selftests/bpf/progs/tcp_ipv6_exthdr_srh.c | 78 ++++++++
> 2 files changed, 249 insertions(+)
> create mode 100644 tools/testing/selftests/bpf/prog_tests/tcp_ipv6_exthdr_srh.c
> create mode 100644 tools/testing/selftests/bpf/progs/tcp_ipv6_exthdr_srh.c
>
> diff --git a/tools/testing/selftests/bpf/prog_tests/tcp_ipv6_exthdr_srh.c b/tools/testing/selftests/bpf/prog_tests/tcp_ipv6_exthdr_srh.c
> new file mode 100644
> index 000000000000..70f7ee230975
> --- /dev/null
> +++ b/tools/testing/selftests/bpf/prog_tests/tcp_ipv6_exthdr_srh.c
> @@ -0,0 +1,171 @@
> +// SPDX-License-Identifier: GPL-2.0
> +#include <test_progs.h>
> +#include <linux/seg6.h>
> +#include "cgroup_helpers.h"
> +#include "network_helpers.h"
> +
> +struct tcp_srh_storage {
> + struct in6_addr inner_segment;
> +};
> +
> +static void send_byte(int fd)
> +{
> + char b = 0x55;
> +
> + if (CHECK_FAIL(send(fd, &b, sizeof(b), 0) != 1))
> + perror("Failed to send single byte");
> +}
> +
> +static int verify_srh(int map_fd, int server_fd, struct ipv6_sr_hdr *client_srh)
> +{
> + int err = 0;
> + struct tcp_srh_storage val;
> +
> + if (CHECK_FAIL(bpf_map_lookup_elem(map_fd, &server_fd, &val) < 0)) {
please use ASSERT_XXX() macros instead of CHECK and especially instead
of CHECK_FAIL
> + perror("Failed to read socket storage");
> + return -1;
> + }
> +
> + if (memcmp(&val.inner_segment, &client_srh->segments[1],
> + sizeof(struct in6_addr))) {
> + log_err("The inner segment of the received SRH differs from the sent one");
> + err++;
> + }
> +
> + return err;
> +}
> +
> +static int run_test(int cgroup_fd, int listen_fd)
> +{
> + struct bpf_prog_load_attr attr = {
> + .prog_type = BPF_PROG_TYPE_SOCK_OPS,
> + .file = "./tcp_ipv6_exthdr_srh.o",
> + .expected_attach_type = BPF_CGROUP_SOCK_OPS,
> + };
> + size_t srh_size = sizeof(struct ipv6_sr_hdr) +
> + 2 * sizeof(struct in6_addr);
> + struct ipv6_sr_hdr *client_srh;
> + struct bpf_object *obj;
> + struct bpf_map *map;
> + struct timeval tv;
> + int client_fd;
> + int server_fd;
> + int prog_fd;
> + int map_fd;
> + char byte;
> + int err;
> +
> + err = bpf_prog_load_xattr(&attr, &obj, &prog_fd);
bpf_prog_load_xattr() is deprecated, please use BPF skeleton for the test
> + if (err) {
> + log_err("Failed to load BPF object");
> + return -1;
> + }
> +
> + map = bpf_object__next_map(obj, NULL);
> + map_fd = bpf_map__fd(map);
> +
[...]
> +
> +void test_tcp_ipv6_exthdr_srh(void)
> +{
> + int server_fd, cgroup_fd;
> +
> + cgroup_fd = test__join_cgroup("/tcp_ipv6_exthdr_srh");
> + if (CHECK_FAIL(cgroup_fd < 0))
> + return;
> +
> + server_fd = start_server(AF_INET6, SOCK_STREAM, "::1", 0, 0);
> + if (CHECK_FAIL(server_fd < 0))
> + goto close_cgroup_fd;
> +
> + if (CHECK_FAIL(system("sysctl net.ipv6.conf.all.seg6_enabled=1")))
> + goto close_server;
> +
> + if (CHECK_FAIL(system("sysctl net.ipv6.conf.lo.seg6_enabled=1")))
> + goto reset_sysctl;
> +
> + CHECK_FAIL(run_test(cgroup_fd, server_fd));
> +
> + if (CHECK_FAIL(system("sysctl net.ipv6.conf.lo.seg6_enabled=0")))
> + log_err("Cannot reset sysctl net.ipv6.conf.lo.seg6_enabled to 0");
> +
> +reset_sysctl:
> + if (CHECK_FAIL(system("sysctl net.ipv6.conf.all.seg6_enabled=0")))
> + log_err("Cannot reset sysctl net.ipv6.conf.all.seg6_enabled to 0");
> +
same here, please no CHECK_FAIL()s
> +close_server:
> + close(server_fd);
> +close_cgroup_fd:
> + close(cgroup_fd);
> +}
[...]
^ permalink raw reply [flat|nested] 3+ messages in thread
end of thread, other threads:[~2021-12-13 21:44 UTC | newest]
Thread overview: 3+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
[not found] <20211207225635.113904-1-mathjadin@gmail.com>
2021-12-07 22:56 ` [PATCH bpf-next v2 2/3] selftests/bpf: Test for IPv6 ext header parsing Mathieu Jadin
2021-12-13 21:44 ` Andrii Nakryiko
2021-12-07 22:56 ` [PATCH bpf-next v2 3/3] selftests/bpf: Improve test tcpbpf_user robustness Mathieu Jadin
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).