bpf.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
From: Kuniyuki Iwashima <kuniyu@amazon.com>
To: "David S. Miller" <davem@davemloft.net>,
	Eric Dumazet <edumazet@google.com>,
	Jakub Kicinski <kuba@kernel.org>, Paolo Abeni <pabeni@redhat.com>,
	David Ahern <dsahern@kernel.org>,
	Alexei Starovoitov <ast@kernel.org>,
	Daniel Borkmann <daniel@iogearbox.net>,
	Andrii Nakryiko <andrii@kernel.org>,
	Martin KaFai Lau <martin.lau@linux.dev>,
	Song Liu <song@kernel.org>,
	Yonghong Song <yonghong.song@linux.dev>,
	John Fastabend <john.fastabend@gmail.com>,
	KP Singh <kpsingh@kernel.org>,
	Stanislav Fomichev <sdf@google.com>, Hao Luo <haoluo@google.com>,
	Jiri Olsa <jolsa@kernel.org>, Mykola Lysenko <mykolal@fb.com>
Cc: Kuniyuki Iwashima <kuniyu@amazon.com>,
	Kuniyuki Iwashima <kuni1840@gmail.com>, <bpf@vger.kernel.org>,
	<netdev@vger.kernel.org>
Subject: [PATCH v1 bpf-next 11/11] selftest: bpf: Test BPF_SOCK_OPS_(GEN|CHECK)_SYNCOOKIE_CB.
Date: Fri, 13 Oct 2023 15:04:33 -0700	[thread overview]
Message-ID: <20231013220433.70792-12-kuniyu@amazon.com> (raw)
In-Reply-To: <20231013220433.70792-1-kuniyu@amazon.com>

This patch adds a test for BPF_SOCK_OPS_(GEN|CHECK)_SYNCOOKIE_CB hooks.

BPF_SOCK_OPS_GEN_SYNCOOKIE_CB hook generates a hash using SipHash from
based on 4-tuple.  The hash is split into ISN and TS.  MSS, ECN, SACK,
and WScale are encoded into the lower 8-bits of ISN.

  ISN:
    MSB                                   LSB
    | 31 ... 8 | 7 6 | 5   | 4    | 3 2 1 0 |
    | Hash_1   | MSS | ECN | SACK | WScale  |

  TS:
    MSB                LSB
    | 31 ... 8 | 7 ... 0 |
    | Random   | Hash_2  |

BPF_SOCK_OPS_CHECK_SYNCOOKIE_CB hook re-calculates the hash and validates
the cookie.

Signed-off-by: Kuniyuki Iwashima <kuniyu@amazon.com>
---
Currently, the validator is incomplete...

If this line is changed

    skops->replylong[0] = msstab[3];

to
    skops->replylong[0] = msstab[mssind];

, we will get the error below during make:

    GEN-SKEL [test_progs] test_tcp_syncookie.skel.h
  ...
  Error: failed to open BPF object file: No such file or directory
    GEN-SKEL [test_progs-no_alu32] test_tcp_syncookie.skel.h
  make: *** [Makefile:603: /home/ec2-user/kernel/bpf_syncookie/tools/testing/selftests/bpf/test_tcp_syncookie.skel.h] Error 254
  make: *** Deleting file '/home/ec2-user/kernel/bpf_syncookie/tools/testing/selftests/bpf/test_tcp_syncookie.skel.h'
  make: *** Waiting for unfinished jobs....
---
 .../selftests/bpf/prog_tests/tcp_syncookie.c  |  84 +++++++++
 .../selftests/bpf/progs/test_siphash.h        |  65 +++++++
 .../selftests/bpf/progs/test_tcp_syncookie.c  | 170 ++++++++++++++++++
 .../selftests/bpf/test_tcp_hdr_options.h      |   8 +-
 4 files changed, 326 insertions(+), 1 deletion(-)
 create mode 100644 tools/testing/selftests/bpf/prog_tests/tcp_syncookie.c
 create mode 100644 tools/testing/selftests/bpf/progs/test_siphash.h
 create mode 100644 tools/testing/selftests/bpf/progs/test_tcp_syncookie.c

diff --git a/tools/testing/selftests/bpf/prog_tests/tcp_syncookie.c b/tools/testing/selftests/bpf/prog_tests/tcp_syncookie.c
new file mode 100644
index 000000000000..53af1434fc2c
--- /dev/null
+++ b/tools/testing/selftests/bpf/prog_tests/tcp_syncookie.c
@@ -0,0 +1,84 @@
+// SPDX-License-Identifier: GPL-2.0
+/* Copyright Amazon.com Inc. or its affiliates. */
+
+#define _GNU_SOURCE
+#include <sched.h>
+#include <stdlib.h>
+
+#include "test_progs.h"
+#include "cgroup_helpers.h"
+#include "network_helpers.h"
+#include "test_tcp_syncookie.skel.h"
+
+static int setup_netns(void)
+{
+	if (!ASSERT_OK(unshare(CLONE_NEWNET), "create netns"))
+		return -1;
+
+	if (!ASSERT_OK(system("ip link set dev lo up"), "system"))
+		return -1;
+
+	if (!ASSERT_OK(write_sysctl("/proc/sys/net/ipv4/tcp_syncookies", "2"),
+		       "write_sysctl(tcp_syncookies)"))
+		return -1;
+
+	if (!ASSERT_OK(write_sysctl("/proc/sys/net/ipv4/tcp_ecn", "1"),
+		       "write_sysctl(tcp_ecn)"))
+		return -1;
+
+	return 0;
+}
+
+static void create_connection(void)
+{
+	int server, client, child;
+
+	server = start_server(AF_INET, SOCK_STREAM, "127.0.0.1", 0, 0);
+	if (!ASSERT_NEQ(server, -1, "start_server"))
+		return;
+
+	client = connect_to_fd(server, 0);
+	if (!ASSERT_NEQ(client, -1, "connect_to_fd"))
+		goto close_server;
+
+	child = accept(server, NULL, 0);
+	if (!ASSERT_NEQ(child, -1, "accept"))
+		goto close_client;
+
+	close(child);
+close_client:
+	close(client);
+close_server:
+	close(server);
+}
+
+void test_tcp_syncookie(void)
+{
+	struct test_tcp_syncookie *skel;
+	struct bpf_link *link;
+	int cgroup;
+
+	if (setup_netns())
+		return;
+
+	skel = test_tcp_syncookie__open_and_load();
+	if (!ASSERT_OK_PTR(skel, "open_and_load"))
+		return;
+
+	cgroup = test__join_cgroup("/tcp_syncookie");
+	if (!ASSERT_GE(cgroup, 0, "join_cgroup"))
+		goto destroy_skel;
+
+	link = bpf_program__attach_cgroup(skel->progs.syncookie, cgroup);
+	if (!ASSERT_OK_PTR(link, "attach_cgroup"))
+		goto close_cgroup;
+
+	create_connection();
+
+	bpf_link__destroy(link);
+
+close_cgroup:
+	close(cgroup);
+destroy_skel:
+	test_tcp_syncookie__destroy(skel);
+}
diff --git a/tools/testing/selftests/bpf/progs/test_siphash.h b/tools/testing/selftests/bpf/progs/test_siphash.h
new file mode 100644
index 000000000000..e36de63fdbaa
--- /dev/null
+++ b/tools/testing/selftests/bpf/progs/test_siphash.h
@@ -0,0 +1,65 @@
+// SPDX-License-Identifier: GPL-2.0
+/* Copyright Amazon.com Inc. or its affiliates. */
+
+/* include/linux/bitops.h */
+static __always_inline __u64 rol64(__u64 word, unsigned int shift)
+{
+	return (word << (shift & 63)) | (word >> ((-shift) & 63));
+}
+
+/* include/linux/siphash.h */
+typedef struct {
+	__u64 key[2];
+} siphash_key_t;
+
+#define SIPHASH_PERMUTATION(a, b, c, d) ( \
+	(a) += (b), (b) = rol64((b), 13), (b) ^= (a), (a) = rol64((a), 32), \
+	(c) += (d), (d) = rol64((d), 16), (d) ^= (c), \
+	(a) += (d), (d) = rol64((d), 21), (d) ^= (a), \
+	(c) += (b), (b) = rol64((b), 17), (b) ^= (c), (c) = rol64((c), 32))
+
+#define SIPHASH_CONST_0 0x736f6d6570736575ULL
+#define SIPHASH_CONST_1 0x646f72616e646f6dULL
+#define SIPHASH_CONST_2 0x6c7967656e657261ULL
+#define SIPHASH_CONST_3 0x7465646279746573ULL
+
+/* lib/siphash.c */
+#define SIPROUND SIPHASH_PERMUTATION(v0, v1, v2, v3)
+
+#define PREAMBLE(len) \
+	__u64 v0 = SIPHASH_CONST_0; \
+	__u64 v1 = SIPHASH_CONST_1; \
+	__u64 v2 = SIPHASH_CONST_2; \
+	__u64 v3 = SIPHASH_CONST_3; \
+	__u64 b = ((__u64)(len)) << 56; \
+	v3 ^= key->key[1]; \
+	v2 ^= key->key[0]; \
+	v1 ^= key->key[1]; \
+	v0 ^= key->key[0];
+
+#define POSTAMBLE \
+	v3 ^= b; \
+	SIPROUND; \
+	SIPROUND; \
+	v0 ^= b; \
+	v2 ^= 0xff; \
+	SIPROUND; \
+	SIPROUND; \
+	SIPROUND; \
+	SIPROUND; \
+	return (v0 ^ v1) ^ (v2 ^ v3);
+
+static __always_inline __u64 siphash_2u64(const __u64 first, const __u64 second,
+					  const siphash_key_t *key)
+{
+	PREAMBLE(16)
+	v3 ^= first;
+	SIPROUND;
+	SIPROUND;
+	v0 ^= first;
+	v3 ^= second;
+	SIPROUND;
+	SIPROUND;
+	v0 ^= second;
+	POSTAMBLE
+}
diff --git a/tools/testing/selftests/bpf/progs/test_tcp_syncookie.c b/tools/testing/selftests/bpf/progs/test_tcp_syncookie.c
new file mode 100644
index 000000000000..5d1fc928602b
--- /dev/null
+++ b/tools/testing/selftests/bpf/progs/test_tcp_syncookie.c
@@ -0,0 +1,170 @@
+// SPDX-License-Identifier: GPL-2.0
+/* Copyright Amazon.com Inc. or its affiliates. */
+
+#include <stdbool.h>
+#include <linux/bpf.h>
+#include <linux/tcp.h>
+#include <linux/types.h>
+#include <bpf/bpf_helpers.h>
+#define BPF_PROG_TEST_TCP_HDR_OPTIONS
+#include "test_tcp_hdr_options.h"
+#include "test_siphash.h"
+
+#define ARRAY_SIZE(arr)	(sizeof(arr) / sizeof((arr)[0]))
+
+static int assert_gen_syncookie_cb(struct bpf_sock_ops *skops)
+{
+	struct tcp_opt tcp_opt;
+	int ret;
+
+	tcp_opt.kind = TCPOPT_WINDOW;
+	tcp_opt.len = 0;
+
+	ret = bpf_load_hdr_opt(skops, &tcp_opt, TCPOLEN_WINDOW, 0);
+	if (ret != TCPOLEN_WINDOW ||
+	    tcp_opt.data[0] != (skops->args[1] & BPF_SYNCOOKIE_WSCALE_MASK))
+		goto err;
+
+	tcp_opt.kind = TCPOPT_SACK_PERM;
+	tcp_opt.len = 0;
+
+	ret = bpf_load_hdr_opt(skops, &tcp_opt, TCPOLEN_SACK_PERM, 0);
+	if (ret != TCPOLEN_SACK_PERM ||
+	    !(skops->args[1] & BPF_SYNCOOKIE_SACK))
+		goto err;
+
+	tcp_opt.kind = TCPOPT_TIMESTAMP;
+	tcp_opt.len = 0;
+
+	ret = bpf_load_hdr_opt(skops, &tcp_opt, TCPOLEN_TIMESTAMP, 0);
+	if (ret != TCPOLEN_TIMESTAMP ||
+	    !(skops->args[1] & BPF_SYNCOOKIE_TS))
+		goto err;
+
+	if (((skops->skb_tcp_flags & (TCPHDR_ECE | TCPHDR_CWR)) !=
+	     (TCPHDR_ECE | TCPHDR_CWR)) ||
+	    !(skops->args[1] & BPF_SYNCOOKIE_ECN))
+		goto err;
+
+	return CG_OK;
+
+err:
+	return CG_ERR;
+}
+
+static siphash_key_t test_key_siphash = {
+	{ 0x0706050403020100ULL, 0x0f0e0d0c0b0a0908ULL }
+};
+
+static __u32 cookie_hash(struct bpf_sock_ops *skops)
+{
+	return siphash_2u64((__u64)skops->remote_ip4 << 32 | skops->local_ip4,
+			    (__u64)skops->remote_port << 32 | skops->local_port,
+			    &test_key_siphash);
+}
+
+static const __u16 msstab[] = {
+	536,
+	1300,
+	1440,
+	1460,
+};
+
+#define COOKIE_BITS	8
+#define COOKIE_MASK	(((__u32)1 << COOKIE_BITS) - 1)
+
+/* Hash is calculated for each client and split into
+ * ISN and TS.
+ *
+ * ISN:
+ *
+ * MSB                                   LSB
+ * | 31 ... 8 | 7 6 | 5   | 4    | 3 2 1 0 |
+ * | Hash_1   | MSS | ECN | SACK | WScale  |
+ *
+ * TS:
+ *
+ * MSB                LSB
+ * | 31 ... 8 | 7 ... 0 |
+ * | Random   | Hash_2  |
+ */
+static void gen_syncookie(struct bpf_sock_ops *skops)
+{
+	__u16 mss = skops->args[0];
+	__u32 tstamp = 0;
+	__u32 cookie;
+	int mssind;
+
+	for (mssind = ARRAY_SIZE(msstab) - 1; mssind; mssind--)
+		if (mss > msstab[mssind])
+			break;
+
+	cookie = cookie_hash(skops);
+
+	if (skops->args[1] & BPF_SYNCOOKIE_TS) {
+		tstamp = bpf_get_prandom_u32();
+		tstamp &= ~COOKIE_MASK;
+		tstamp |= cookie & COOKIE_MASK;
+	}
+
+	cookie &= ~COOKIE_MASK;
+	cookie |= mssind << 6;
+	cookie |= skops->args[1] & (BPF_SYNCOOKIE_ECN |
+				    BPF_SYNCOOKIE_SACK |
+				    BPF_SYNCOOKIE_WSCALE_MASK);
+
+	skops->replylong[0] = cookie;
+	skops->replylong[1] = tstamp;
+}
+
+static int check_syncookie(struct bpf_sock_ops *skops)
+{
+	__u32 cookie = cookie_hash(skops);
+	__u32 tstamp = skops->args[1];
+	__u8 mssind;
+
+	if (tstamp)
+		cookie -= tstamp & COOKIE_MASK;
+	else
+		cookie &= ~COOKIE_MASK;
+
+	cookie -= skops->args[0] & ~COOKIE_MASK;
+	if (cookie)
+		return CG_ERR;
+
+	mssind = (skops->args[0] & (3 << 6)) >> 6;
+	if (mssind > ARRAY_SIZE(msstab))
+		return CG_ERR;
+
+	/* msstab[mssind]; does not compile ... */
+	skops->replylong[0] = msstab[3];
+	skops->replylong[1] = skops->args[0] & (BPF_SYNCOOKIE_ECN |
+						BPF_SYNCOOKIE_SACK |
+						BPF_SYNCOOKIE_WSCALE_MASK);
+
+	return CG_OK;
+}
+
+SEC("sockops")
+int syncookie(struct bpf_sock_ops *skops)
+{
+	int ret = CG_OK;
+
+	switch (skops->op) {
+	case BPF_SOCK_OPS_TCP_LISTEN_CB:
+		bpf_sock_ops_cb_flags_set(skops, BPF_SOCK_OPS_SYNCOOKIE_CB_FLAG);
+		break;
+	case BPF_SOCK_OPS_GEN_SYNCOOKIE_CB:
+		ret = assert_gen_syncookie_cb(skops);
+		if (ret)
+			gen_syncookie(skops);
+		break;
+	case BPF_SOCK_OPS_CHECK_SYNCOOKIE_CB:
+		ret = check_syncookie(skops);
+		break;
+	}
+
+	return ret;
+}
+
+char _license[] SEC("license") = "GPL";
diff --git a/tools/testing/selftests/bpf/test_tcp_hdr_options.h b/tools/testing/selftests/bpf/test_tcp_hdr_options.h
index 56c9f8a3ad3d..3efca29a1394 100644
--- a/tools/testing/selftests/bpf/test_tcp_hdr_options.h
+++ b/tools/testing/selftests/bpf/test_tcp_hdr_options.h
@@ -52,8 +52,14 @@ struct linum_err {
 #define TCPOPT_NOP		1
 #define TCPOPT_MSS		2
 #define TCPOPT_WINDOW		3
+#define TCPOPT_SACK_PERM	4
+#define TCPOPT_TIMESTAMP	8
 #define TCPOPT_EXP		254
 
+#define TCPOLEN_WINDOW		3
+#define TCPOLEN_SACK_PERM	2
+#define TCPOLEN_TIMESTAMP	10
+
 #define TCP_BPF_EXPOPT_BASE_LEN 4
 #define MAX_TCP_HDR_LEN		60
 #define MAX_TCP_OPTION_SPACE	40
@@ -81,7 +87,7 @@ struct tcp_opt {
 	__u8 kind;
 	__u8 len;
 	union {
-		__u8 data[4];
+		__u8 data[8];
 		__u32 data32;
 	};
 } __attribute__((packed));
-- 
2.30.2


  parent reply	other threads:[~2023-10-13 22:10 UTC|newest]

Thread overview: 44+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2023-10-13 22:04 [PATCH v1 bpf-next 00/11] bpf: tcp: Add SYN Cookie generation/validation SOCK_OPS hooks Kuniyuki Iwashima
2023-10-13 22:04 ` [PATCH v1 bpf-next 01/11] tcp: Clean up reverse xmas tree in cookie_v[46]_check() Kuniyuki Iwashima
2023-10-13 22:04 ` [PATCH v1 bpf-next 02/11] tcp: Cache sock_net(sk) " Kuniyuki Iwashima
2023-10-13 22:04 ` [PATCH v1 bpf-next 03/11] tcp: Clean up goto labels " Kuniyuki Iwashima
2023-10-17  0:00   ` Kui-Feng Lee
2023-10-17  0:30     ` Kuniyuki Iwashima
2023-10-13 22:04 ` [PATCH v1 bpf-next 04/11] tcp: Don't initialise tp->tsoffset in tcp_get_cookie_sock() Kuniyuki Iwashima
2023-10-13 22:04 ` [PATCH v1 bpf-next 05/11] bpf: tcp: Add SYN Cookie generation SOCK_OPS hook Kuniyuki Iwashima
2023-10-18  0:54   ` Martin KaFai Lau
2023-10-18 17:00     ` Kuniyuki Iwashima
2023-10-13 22:04 ` [PATCH v1 bpf-next 06/11] bpf: tcp: Add SYN Cookie validation " Kuniyuki Iwashima
2023-10-16 20:38   ` Stanislav Fomichev
2023-10-16 22:02     ` Kuniyuki Iwashima
2023-10-17 16:52   ` Kuniyuki Iwashima
2023-10-13 22:04 ` [PATCH v1 bpf-next 07/11] bpf: Make bpf_sock_ops.replylong[1] writable Kuniyuki Iwashima
2023-10-13 22:04 ` [PATCH v1 bpf-next 08/11] bpf: tcp: Make TS available for SYN Cookie storage Kuniyuki Iwashima
2023-10-13 22:04 ` [PATCH v1 bpf-next 09/11] tcp: Split cookie_ecn_ok() Kuniyuki Iwashima
2023-10-13 22:04 ` [PATCH v1 bpf-next 10/11] bpf: tcp: Make WS, SACK, ECN configurable from BPF SYN Cookie Kuniyuki Iwashima
2023-10-18  1:08   ` Martin KaFai Lau
2023-10-18 17:02     ` Kuniyuki Iwashima
2023-10-13 22:04 ` Kuniyuki Iwashima [this message]
2023-10-17  5:50   ` [PATCH v1 bpf-next 11/11] selftest: bpf: Test BPF_SOCK_OPS_(GEN|CHECK)_SYNCOOKIE_CB Martin KaFai Lau
2023-10-17 16:29     ` Kuniyuki Iwashima
2023-10-16 13:05 ` [PATCH v1 bpf-next 00/11] bpf: tcp: Add SYN Cookie generation/validation SOCK_OPS hooks Daniel Borkmann
2023-10-16 16:11   ` Kuniyuki Iwashima
2023-10-16 14:19 ` Willem de Bruijn
2023-10-16 16:46   ` Kuniyuki Iwashima
2023-10-16 18:41     ` Willem de Bruijn
2023-10-17  5:53 ` Martin KaFai Lau
2023-10-17 16:48   ` Kuniyuki Iwashima
2023-10-18  6:19     ` Martin KaFai Lau
2023-10-18  8:02       ` Eric Dumazet
2023-10-18 17:20         ` Kuniyuki Iwashima
2023-10-18 21:47           ` Kui-Feng Lee
2023-10-18 22:31             ` Kuniyuki Iwashima
2023-10-19  7:25               ` Martin KaFai Lau
2023-10-19 18:01                 ` Kuniyuki Iwashima
2023-10-20 19:59                   ` Martin KaFai Lau
2023-10-20 23:10                     ` Kuniyuki Iwashima
2023-10-21  6:48                       ` Kuniyuki Iwashima
2023-10-23 21:35                         ` Martin KaFai Lau
2023-10-24  0:37                           ` Kui-Feng Lee
2023-10-24  1:22                             ` Kuniyuki Iwashima
2023-10-24 17:55                               ` Kui-Feng Lee

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=20231013220433.70792-12-kuniyu@amazon.com \
    --to=kuniyu@amazon.com \
    --cc=andrii@kernel.org \
    --cc=ast@kernel.org \
    --cc=bpf@vger.kernel.org \
    --cc=daniel@iogearbox.net \
    --cc=davem@davemloft.net \
    --cc=dsahern@kernel.org \
    --cc=edumazet@google.com \
    --cc=haoluo@google.com \
    --cc=john.fastabend@gmail.com \
    --cc=jolsa@kernel.org \
    --cc=kpsingh@kernel.org \
    --cc=kuba@kernel.org \
    --cc=kuni1840@gmail.com \
    --cc=martin.lau@linux.dev \
    --cc=mykolal@fb.com \
    --cc=netdev@vger.kernel.org \
    --cc=pabeni@redhat.com \
    --cc=sdf@google.com \
    --cc=song@kernel.org \
    --cc=yonghong.song@linux.dev \
    /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;
as well as URLs for NNTP newsgroup(s).