From a023cc05ce3f3f4ec6e8ef8b6deef724f896dc1d Mon Sep 17 00:00:00 2001
From: Libor Peltan <libor.peltan@nic.cz>
Date: Fri, 8 Oct 2021 11:52:46 +0200
Subject: [PATCH] xdp-tcp: allow configuring separate SYN table

---
 doc/reference.rst             | 11 +++++++++++
 src/knot/conf/base.c          |  3 +++
 src/knot/conf/base.h          |  1 +
 src/knot/conf/schema.c        |  1 +
 src/knot/conf/schema.h        |  1 +
 src/knot/server/xdp-handler.c | 27 ++++++++++++++++++++++++++-
 tests/knot/test_confio.c      |  1 +
 7 files changed, 44 insertions(+), 1 deletion(-)

diff --git a/doc/reference.rst b/doc/reference.rst
index c38a44b846..eb7f620db9 100644
--- a/doc/reference.rst
+++ b/doc/reference.rst
@@ -521,6 +521,7 @@ Various options related to XDP listening, especially TCP.
      listen: STR[@INT] | ADDR[@INT] ...
      tcp: BOOL
      tcp-max-clients: INT
+     tcp-max-syn-clients: INT
      tcp-inbuf-max-size: SIZE
      tcp-idle-close-timeout: TIME
      tcp-idle-reset-timeout: TIME
@@ -599,6 +600,16 @@ A maximum number of TCP clients connected in parallel.
 
 *Default:* 1000000 (one million)
 
+.. _xdp_tcp-max-syn-clients:
+
+tcp-max-syn-clients
+-------------------
+
+If set to non-zero, clients in SYN state are handled separately and
+this is the limit of their number in parallel.
+
+*Default:* 0
+
 .. _xdp_tcp-inbuf-max-size:
 
 tcp-inbuf-max-size
diff --git a/src/knot/conf/base.c b/src/knot/conf/base.c
index ed43bfb3d5..8a7cfee9f6 100644
--- a/src/knot/conf/base.c
+++ b/src/knot/conf/base.c
@@ -192,6 +192,9 @@ static void init_cache(
 	val = conf_get(conf, C_XDP, C_TCP_MAX_CLIENTS);
 	conf->cache.xdp_tcp_max_clients = conf_int(&val);
 
+	val = conf_get(conf, C_XDP, C_TCP_SYN_CLIENTS);
+	conf->cache.xdp_tcp_syn_clients = conf_int(&val);
+
 	val = conf_get(conf, C_XDP, C_TCP_INBUF_MAX_SIZE);
 	conf->cache.xdp_tcp_inbuf_max_size = conf_int(&val);
 
diff --git a/src/knot/conf/base.h b/src/knot/conf/base.h
index 76bc613606..2d1967f971 100644
--- a/src/knot/conf/base.h
+++ b/src/knot/conf/base.h
@@ -133,6 +133,7 @@ typedef struct {
 		size_t srv_bg_threads;
 		size_t srv_tcp_max_clients;
 		size_t xdp_tcp_max_clients;
+		size_t xdp_tcp_syn_clients;
 		size_t xdp_tcp_inbuf_max_size;
 		uint32_t xdp_tcp_idle_close;
 		uint32_t xdp_tcp_idle_reset;
diff --git a/src/knot/conf/schema.c b/src/knot/conf/schema.c
index c27261d420..e1c490ab7e 100644
--- a/src/knot/conf/schema.c
+++ b/src/knot/conf/schema.c
@@ -243,6 +243,7 @@ static const yp_item_t desc_xdp[] = {
 	{ C_LISTEN,               YP_TADDR, YP_VADDR = { 53 }, YP_FMULTI, { check_xdp_listen } },
 	{ C_TCP,                  YP_TBOOL, YP_VNONE },
 	{ C_TCP_MAX_CLIENTS,      YP_TINT,  YP_VINT = { 0, INT32_MAX, 1000000 } },
+	{ C_TCP_SYN_CLIENTS,      YP_TINT,  YP_VINT = { 0, INT32_MAX, 0 } },
 	{ C_TCP_INBUF_MAX_SIZE,   YP_TINT,  YP_VINT = { MEGA(1), SSIZE_MAX, MEGA(100), YP_SSIZE } },
 	{ C_TCP_IDLE_CLOSE,       YP_TINT,  YP_VINT = { 1, INT32_MAX, 10, YP_STIME } },
 	{ C_TCP_IDLE_RESET,       YP_TINT,  YP_VINT = { 1, INT32_MAX, 20, YP_STIME } },
diff --git a/src/knot/conf/schema.h b/src/knot/conf/schema.h
index bee119a7ee..ded593b293 100644
--- a/src/knot/conf/schema.h
+++ b/src/knot/conf/schema.h
@@ -130,6 +130,7 @@
 #define C_TCP_INBUF_MAX_SIZE	"\x12""tcp-inbuf-max-size"
 #define C_TCP_IO_TIMEOUT	"\x0E""tcp-io-timeout"
 #define C_TCP_MAX_CLIENTS	"\x0F""tcp-max-clients"
+#define C_TCP_SYN_CLIENTS	"\x13""tcp-max-syn-clients"
 #define C_TCP_RESEND		"\x12""tcp-resend-timeout"
 #define C_TCP_REUSEPORT		"\x0D""tcp-reuseport"
 #define C_TCP_RMT_IO_TIMEOUT	"\x15""tcp-remote-io-timeout"
diff --git a/src/knot/server/xdp-handler.c b/src/knot/server/xdp-handler.c
index 9839963c2b..f6f3e51f1a 100644
--- a/src/knot/server/xdp-handler.c
+++ b/src/knot/server/xdp-handler.c
@@ -36,9 +36,11 @@ typedef struct xdp_handle_ctx {
 	uint32_t msg_recv_count;
 	uint32_t msg_udp_count;
 	knot_tcp_table_t *tcp_table;
+	knot_tcp_table_t *syn_table;
 
 	bool tcp;
 	size_t tcp_max_conns;
+	size_t tcp_syn_conns;
 	size_t tcp_max_inbufs;
 	size_t tcp_max_obufs;
 	uint32_t tcp_idle_close; // In microseconds.
@@ -67,6 +69,7 @@ void xdp_handle_reconfigure(xdp_handle_ctx_t *ctx)
 	conf_t *pconf = conf();
 	ctx->tcp            = pconf->cache.xdp_tcp;
 	ctx->tcp_max_conns  = pconf->cache.xdp_tcp_max_clients    / pconf->cache.srv_xdp_threads;
+	ctx->tcp_syn_conns  = pconf->cache.xdp_tcp_syn_clients    / pconf->cache.srv_xdp_threads;
 	ctx->tcp_max_inbufs = pconf->cache.xdp_tcp_inbuf_max_size / pconf->cache.srv_xdp_threads;
 	ctx->tcp_max_obufs  = pconf->cache.xdp_tcp_inbuf_max_size / pconf->cache.srv_xdp_threads; // FIXME another setting for outbuf!!
 	ctx->tcp_idle_close = pconf->cache.xdp_tcp_idle_close * 1000000;
@@ -78,6 +81,7 @@ void xdp_handle_reconfigure(xdp_handle_ctx_t *ctx)
 void xdp_handle_free(xdp_handle_ctx_t *ctx)
 {
 	knot_tcp_table_free(ctx->tcp_table);
+	knot_tcp_table_free(ctx->syn_table);
 	free(ctx);
 }
 
@@ -98,6 +102,13 @@ xdp_handle_ctx_t *xdp_handle_init(knot_xdp_socket_t *xdp_sock)
 			xdp_handle_free(ctx);
 			return NULL;
 		}
+		if (ctx->tcp_syn_conns > 0) {
+			ctx->syn_table = knot_tcp_table_new(ctx->tcp_syn_conns, ctx->tcp_table);
+			if (ctx->syn_table == NULL) {
+				xdp_handle_free(ctx);
+				return NULL;
+			}
+		}
 	}
 
 	return ctx;
@@ -185,7 +196,7 @@ static void handle_udp(xdp_handle_ctx_t *ctx, knot_layer_t *layer,
 static void handle_tcp(xdp_handle_ctx_t *ctx, knot_layer_t *layer,
                        knotd_qdata_params_t *params)
 {
-	int ret = knot_tcp_recv(ctx->relays, ctx->msg_recv, ctx->msg_recv_count, ctx->tcp_table, NULL);
+	int ret = knot_tcp_recv(ctx->relays, ctx->msg_recv, ctx->msg_recv_count, ctx->tcp_table, ctx->syn_table);
 	if (ret != KNOT_EOK) {
 		log_notice("TCP, failed to process some packets (%s)", knot_strerror(ret));
 		return;
@@ -278,6 +289,20 @@ void xdp_handle_sweep(xdp_handle_ctx_t *ctx)
 			ret = knot_tcp_send(ctx->sock, sweep_relays, XDP_BATCHLEN, XDP_BATCHLEN);
 		}
 		knot_tcp_cleanup(ctx->tcp_table, sweep_relays, XDP_BATCHLEN);
+		if (ret != KNOT_EOK) {
+			break;
+		}
+
+		if (ctx->syn_table == NULL) {
+			continue;
+		}
+		ret = knot_tcp_sweep(ctx->syn_table, ctx->tcp_idle_close, ctx->tcp_idle_reset,
+		                     UINT32_MAX, ctx->tcp_syn_conns, SIZE_MAX, SIZE_MAX,
+		                     sweep_relays, XDP_BATCHLEN, &total_close, &total_reset);
+		if (ret == KNOT_EOK) {
+			ret = knot_tcp_send(ctx->sock, sweep_relays, XDP_BATCHLEN, XDP_BATCHLEN);
+		}
+		knot_tcp_cleanup(ctx->syn_table, sweep_relays, XDP_BATCHLEN);
 	} while (ret == KNOT_EOK && prev_reset < total_reset);
 
 	if (total_close > 0 || total_reset > 0) {
diff --git a/tests/knot/test_confio.c b/tests/knot/test_confio.c
index 03ee7a0920..12a9db272c 100644
--- a/tests/knot/test_confio.c
+++ b/tests/knot/test_confio.c
@@ -991,6 +991,7 @@ static const yp_item_t desc_server[] = {
 static const yp_item_t desc_xdp[] = {
 	{ C_TCP,                YP_TBOOL, YP_VNONE },
 	{ C_TCP_MAX_CLIENTS,    YP_TINT,  YP_VNONE },
+	{ C_TCP_SYN_CLIENTS,    YP_TINT,  YP_VNONE },
 	{ C_TCP_INBUF_MAX_SIZE, YP_TINT,  YP_VNONE },
 	{ C_TCP_IDLE_CLOSE,     YP_TINT,  YP_VNONE },
 	{ C_TCP_IDLE_RESET,     YP_TINT,  YP_VNONE },
-- 
GitLab