From d95b63b8c177b77f804278b4d8e8f8757e11b0fb Mon Sep 17 00:00:00 2001
From: Libor Peltan <libor.peltan@nic.cz>
Date: Mon, 20 Sep 2021 18:50:24 +0200
Subject: [PATCH] xdp-tcp: honor foreign TCP window size

---
 src/libknot/xdp/msg.h        | 2 ++
 src/libknot/xdp/protocols.h  | 5 +++++
 src/libknot/xdp/tcp.c        | 4 ++--
 src/libknot/xdp/tcp.h        | 1 +
 tests/libknot/test_xdp_tcp.c | 2 ++
 5 files changed, 12 insertions(+), 2 deletions(-)

diff --git a/src/libknot/xdp/msg.h b/src/libknot/xdp/msg.h
index 83652f9866..d972738dc3 100644
--- a/src/libknot/xdp/msg.h
+++ b/src/libknot/xdp/msg.h
@@ -52,6 +52,8 @@ typedef struct knot_xdp_msg {
 	uint32_t seqno;
 	uint32_t ackno;
 	uint16_t mss;
+	uint16_t win;
+	uint8_t win_scale;
 } knot_xdp_msg_t;
 
 /*! @} */
diff --git a/src/libknot/xdp/protocols.h b/src/libknot/xdp/protocols.h
index 7b9829e1df..5f4bcb3990 100644
--- a/src/libknot/xdp/protocols.h
+++ b/src/libknot/xdp/protocols.h
@@ -76,6 +76,7 @@ inline static void *prot_read_tcp(void *data, knot_xdp_msg_t *msg, uint16_t *src
 
 	msg->seqno = be32toh(tcp->seq);
 	msg->ackno = be32toh(tcp->ack_seq);
+	msg->win = be16toh(tcp->window);
 
 	*src_port = tcp->source;
 	*dst_port = tcp->dest;
@@ -98,6 +99,10 @@ inline static void *prot_read_tcp(void *data, knot_xdp_msg_t *msg, uint16_t *src
 			msg->mss = be16toh(msg->mss);
 		}
 
+		if (opts[0] == PROT_TCP_OPT_WSC && opts[1] == PROT_TCP_OPT_LEN_WSC) {
+			msg->win_scale = opts[2];
+		}
+
 		opts += opts[1];
 	}
 
diff --git a/src/libknot/xdp/tcp.c b/src/libknot/xdp/tcp.c
index 94e78c47e6..1bf31106ac 100644
--- a/src/libknot/xdp/tcp.c
+++ b/src/libknot/xdp/tcp.c
@@ -167,8 +167,6 @@ static int tcp_table_add(knot_xdp_msg_t *msg, uint64_t hash, knot_tcp_table_t *t
 	c->ackno = msg->ackno;
 	c->acked = msg->ackno;
 
-	c->window_size = 65536; // FIXME
-
 	c->last_active = get_timestamp();
 	add_tail(tcp_table_timeout(table), tcp_conn_node(c));
 
@@ -227,6 +225,7 @@ int knot_tcp_recv(knot_tcp_relay_t *relays, knot_xdp_msg_t *msgs, uint32_t count
 			conn->seqno = knot_tcp_next_seqno(msg);
 			memcpy(conn->last_eth_rem, msg->eth_from, sizeof(conn->last_eth_rem));
 			memcpy(conn->last_eth_loc, msg->eth_to, sizeof(conn->last_eth_loc));
+			conn->window_size = (uint32_t)msg->win * (1LU << conn->window_scale);
 
 			conn->last_active = get_timestamp();
 			rem_node(tcp_conn_node(conn));
@@ -270,6 +269,7 @@ int knot_tcp_recv(knot_tcp_relay_t *relays, knot_xdp_msg_t *msgs, uint32_t count
 					conn->state = XDP_TCP_ESTABLISHING;
 					conn->seqno++;
 					conn->mss = MAX(msg->mss, 536); // minimal MSS, most importantly not zero!
+					conn->window_scale = msg->win_scale;
 					if (!synack) {
 						conn->acked = dnssec_random_uint32_t();
 						conn->ackno = conn->acked;
diff --git a/src/libknot/xdp/tcp.h b/src/libknot/xdp/tcp.h
index 8836470543..a8d8fc5fc8 100644
--- a/src/libknot/xdp/tcp.h
+++ b/src/libknot/xdp/tcp.h
@@ -65,6 +65,7 @@ typedef struct knot_tcp_conn {
 	uint8_t last_eth_rem[ETH_ALEN];
 	uint8_t last_eth_loc[ETH_ALEN];
 	uint16_t mss;
+	uint8_t window_scale;
 	uint32_t seqno;
 	uint32_t ackno;
 	uint32_t acked;
diff --git a/tests/libknot/test_xdp_tcp.c b/tests/libknot/test_xdp_tcp.c
index 4d3f7303e1..09a1525b31 100644
--- a/tests/libknot/test_xdp_tcp.c
+++ b/tests/libknot/test_xdp_tcp.c
@@ -507,6 +507,7 @@ void test_obufs(void)
 	size_t DATA_LEN = 65535; // with 2-byte len prefix, this is > 64k == window_size
 	uint8_t *data = calloc(DATA_LEN, 1);
 	rl.conn->mss = TEST_MSS;
+	rl.conn->window_size = 65536;
 	send2_mss = TEST_MSS;
 
 	int ret = knot_tcp_reply_data(&rl, test_table, data, DATA_LEN), i = 0;
@@ -538,6 +539,7 @@ void test_obufs(void)
 	prepare_seqack(&msg, 0, TEST_MSS);
 	ret = knot_tcp_recv(&rl, &msg, 1, test_table, NULL);
 	is_int(KNOT_EOK, ret, "obufs: ACKed data");
+	rl.conn->window_size = 65536;
 	struct tcp_outbuf *surv_ob = rl.conn->outbufs.bufs;
 	ok(surv_ob != NULL, "obufs: unACKed survived");
 	ok(surv_ob->next == NULL, "obufs: just one survived");
-- 
GitLab