[PATCH v1 bpf-next 11/11] selftest: bpf: Test BPF_SOCK_OPS_(GEN|CHECK)_SYNCOOKIE_CB.

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



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@xxxxxxxxxx>
---
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





[Index of Archives]     [Linux Samsung SoC]     [Linux Rockchip SoC]     [Linux Actions SoC]     [Linux for Synopsys ARC Processors]     [Linux NFS]     [Linux NILFS]     [Linux USB Devel]     [Video for Linux]     [Linux Audio Users]     [Yosemite News]     [Linux Kernel]     [Linux SCSI]


  Powered by Linux