summaryrefslogtreecommitdiffstats
path: root/tests/libknot
diff options
context:
space:
mode:
authorLibor Peltan <libor.peltan@nic.cz>2021-09-10 16:56:33 +0200
committerDaniel Salzman <daniel.salzman@nic.cz>2022-03-16 13:41:23 +0100
commit302b6901998f3f8324c97e352fc0038ef3e30bf5 (patch)
treed87b502c33902b9d8ce757099ddfe37daba1510d /tests/libknot
parentxdp-tcp: bugfix: incomming RST has no ackno (diff)
downloadknot-302b6901998f3f8324c97e352fc0038ef3e30bf5.tar.xz
knot-302b6901998f3f8324c97e352fc0038ef3e30bf5.zip
xdp-tcp: major refactoring + store outgoing buffers
- both unsent and unacked buffers of outgoing payload stored - no longer uses dynarray - multiple in-buffers per relay - packets are only sent in knot_tcp_send()
Diffstat (limited to 'tests/libknot')
-rw-r--r--tests/libknot/test_xdp_tcp.c288
1 files changed, 197 insertions, 91 deletions
diff --git a/tests/libknot/test_xdp_tcp.c b/tests/libknot/test_xdp_tcp.c
index 130f6a47e..4d3f7303e 100644
--- a/tests/libknot/test_xdp_tcp.c
+++ b/tests/libknot/test_xdp_tcp.c
@@ -32,6 +32,8 @@ size_t sent_syns = 0;
size_t sent_fins = 0;
uint32_t sent_seqno = 0;
uint32_t sent_ackno = 0;
+size_t sent2_data = 0;
+size_t send2_mss = 0;
knot_xdp_socket_t *test_sock = NULL;
@@ -53,19 +55,15 @@ static size_t tcp_table_timeout_length(knot_tcp_table_t *table)
* \param tcp_table TCP connection table to clean up.
* \param timeout Remove connections older than this (usecs).
* \param at_least Remove at least this number of connections.
- * \param cleaned Optional: Out: number of removed connections.
*/
static void tcp_cleanup(knot_tcp_table_t *tcp_table, uint32_t timeout,
- uint32_t at_least, uint32_t *cleaned)
+ uint32_t at_least)
{
uint32_t now = get_timestamp(), i = 0;
knot_tcp_conn_t *conn, *next;
WALK_LIST_DELSAFE(conn, next, *tcp_table_timeout(tcp_table)) {
if (i++ < at_least || now - conn->last_active >= timeout) {
- tcp_table_del_lookup(conn, tcp_table);
- if (cleaned != NULL) {
- (*cleaned)++;
- }
+ tcp_table_del(tcp_table_re_lookup(conn, tcp_table), tcp_table);
}
}
}
@@ -129,9 +127,26 @@ static int mock_send_nocheck(_unused_ knot_xdp_socket_t *sock, const knot_xdp_ms
return KNOT_EOK;
}
+static int mock_send2(_unused_ knot_xdp_socket_t *sock, const knot_xdp_msg_t msgs[],
+ uint32_t n_msgs, _unused_ uint32_t *sent)
+{
+ ok(n_msgs <= 20, "send2: not too many at once");
+ for (uint32_t i = 0; i < n_msgs; i++) {
+ const knot_xdp_msg_t *msg = msgs + i;
+ ok(msg->flags & KNOT_XDP_MSG_TCP, "send2: is TCP message");
+ ok(msg->flags & KNOT_XDP_MSG_ACK, "send2: has ACK");
+ ok(msg->payload.iov_len <= send2_mss, "send2: fulfilled MSS");
+ sent2_data += msg->payload.iov_len;
+
+ sent_seqno = msg->seqno;
+ sent_ackno = msg->ackno;
+ }
+ return KNOT_EOK;
+}
+
static void clean_table(void)
{
- (void)tcp_cleanup(test_table, 0, UINT32_MAX, NULL);
+ (void)tcp_cleanup(test_table, 0, UINT32_MAX);
}
static void clean_sent(void)
@@ -190,71 +205,70 @@ static void fix_seqacks(knot_xdp_msg_t *msgs, size_t count)
void test_syn(void)
{
knot_xdp_msg_t msg;
- knot_tcp_relay_dynarray_t relays = { 0 };
+ knot_tcp_relay_t rl;
prepare_msg(&msg, KNOT_XDP_MSG_SYN, 1, 2);
- int ret = knot_tcp_relay(test_sock, &msg, 1, test_table, NULL, &relays, NULL);
+ int ret = knot_tcp_recv(&rl, &msg, 1, test_table, NULL);
is_int(KNOT_EOK, ret, "SYN: relay OK");
+ ret = knot_tcp_send(test_sock, &rl, 1, 1);
+ is_int(KNOT_EOK, ret, "SYN: send OK");
is_int(msg.seqno + 1, sent_ackno, "SYN: ackno");
check_sent(0, 0, 1, 0);
- is_int(1, relays.size, "SYN: one relay");
- knot_tcp_relay_t *rl = &knot_tcp_relay_dynarray_arr(&relays)[0];
- is_int(XDP_TCP_SYN, rl->action, "SYN: relay action");
- is_int(XDP_TCP_NOOP, rl->answer, "SYN: relay answer");
- is_int(0, rl->data.iov_len, "SYN: no payload");
+ is_int(XDP_TCP_SYN, rl.action, "SYN: relay action");
+ is_int(XDP_TCP_NOOP, rl.answer, "SYN: relay answer");
+ is_int(0, rl.inbufs_count, "SYN: no payload");
is_int(1, test_table->usage, "SYN: one connection in table");
knot_tcp_conn_t *conn = tcp_table_find(test_table, &msg);
ok(conn != NULL, "SYN: connection present");
- ok(conn == rl->conn, "SYN: relay points to connection");
+ ok(conn == rl.conn, "SYN: relay points to connection");
is_int(XDP_TCP_ESTABLISHING, conn->state, "SYN: connection state");
ok(memcmp(&conn->ip_rem, &msg.ip_from, sizeof(msg.ip_from)) == 0, "SYN: conn IP from");
ok(memcmp(&conn->ip_loc, &msg.ip_to, sizeof(msg.ip_to)) == 0, "SYN: conn IP to");
- knot_tcp_relay_free(&relays);
+ knot_tcp_cleanup(test_table, &rl, 1);
test_conn = conn;
}
void test_establish(void)
{
knot_xdp_msg_t msg;
- knot_tcp_relay_dynarray_t relays = { 0 };
+ knot_tcp_relay_t rl;
prepare_msg(&msg, KNOT_XDP_MSG_ACK, 1, 2);
prepare_seqack(&msg, 0, 1);
- int ret = knot_tcp_relay(test_sock, &msg, 1, test_table, NULL, &relays, NULL);
+ int ret = knot_tcp_recv(&rl, &msg, 1, test_table, NULL);
is_int(KNOT_EOK, ret, "establish: relay OK");
+ ret = knot_tcp_send(test_sock, &rl, 1, 1);
+ is_int(KNOT_EOK, ret, "establish: send OK");
check_sent(0, 0, 0, 0);
- is_int(0, relays.size, "establish: no relay");
- /*knot_tcp_relay_t *rl = &knot_tcp_relay_dynarray_arr(&relays)[0];
- is_int(XDP_TCP_ESTABLISH, rl->action, "establish: relay action");
- ok(rl->conn != NULL, "establish: connection present");
- ok(rl->conn == test_conn, "establish: same connection");
- is_int(XDP_TCP_NORMAL, rl->conn->state, "establish: connection state");*/
-
- knot_tcp_relay_free(&relays);
+ is_int(0, rl.auto_answer, "establish: no auto answer");
+
+ knot_tcp_cleanup(test_table, &rl, 1);
clean_table();
}
void test_syn_ack(void)
{
knot_xdp_msg_t msg;
- knot_tcp_relay_dynarray_t relays = { 0 };
+ knot_tcp_relay_t rl;
prepare_msg(&msg, KNOT_XDP_MSG_SYN | KNOT_XDP_MSG_ACK, 1000, 2000);
- int ret = knot_tcp_relay(test_sock, &msg, 1, test_table, NULL, &relays, NULL);
+ int ret = knot_tcp_recv(&rl, &msg, 1, test_table, NULL);
is_int(KNOT_EOK, ret, "SYN+ACK: relay OK");
+ ret = knot_tcp_send(test_sock, &rl, 1, 1);
+ is_int(KNOT_EOK, ret, "SYN+ACK: send OK");
is_int(msg.seqno + 1, sent_ackno, "SYN+ACK: ackno");
check_sent(1, 0, 0, 0);
- is_int(1, relays.size, "SYN+ACK: one relay");
- knot_tcp_relay_t *rl = &knot_tcp_relay_dynarray_arr(&relays)[0];
- is_int(XDP_TCP_ESTABLISH, rl->action, "SYN+ACK: relay action");
- ok(rl->conn != NULL, "SYN+ACK: connection present");
+ is_int(XDP_TCP_ESTABLISH, rl.action, "SYN+ACK: relay action");
+ ok(rl.conn != NULL, "SYN+ACK: connection present");
- test_conn = rl->conn;
- knot_tcp_relay_free(&relays);
+ test_conn = rl.conn;
+ knot_tcp_cleanup(test_table, &rl, 1);
}
void test_data_fragments(void)
{
- knot_xdp_msg_t msgs[4];
- knot_tcp_relay_dynarray_t relays = { 0 };
+ const size_t CONNS = 4;
+ knot_xdp_msg_t msgs[CONNS];
+ knot_tcp_relay_t rls[CONNS];
+ memset(rls, 0, CONNS * sizeof(*rls));
// first msg contains one whole payload and one fragment
prepare_msg(&msgs[0], KNOT_XDP_MSG_ACK, 1000, 2000);
@@ -276,37 +290,44 @@ void test_data_fragments(void)
prepare_seqack(&msgs[3], 15, 0);
prepare_data(&msgs[3], "\x02""AB""\xff\xff""abcdefghijklmnopqrstuvwxyz...", 34);
- int ret = knot_tcp_relay(test_sock, msgs, sizeof(msgs) / sizeof(msgs[0]),
- test_table, NULL, &relays, NULL);
+ int ret = knot_tcp_recv(rls, msgs, CONNS, test_table, NULL);
is_int(KNOT_EOK, ret, "fragments: relay OK");
+ ret = knot_tcp_send(test_sock, rls, CONNS, CONNS);
+ is_int(KNOT_EOK, ret, "fragments: send OK");
is_int(msgs[3].ackno, sent_seqno, "fragments: seqno");
is_int(msgs[3].seqno + msgs[3].payload.iov_len, sent_ackno, "fragments: ackno");
check_sent(4, 0, 0, 0);
- knot_tcp_relay_t *rls = knot_tcp_relay_dynarray_arr(&relays);
- is_int(XDP_TCP_DATA, rls[0].action, "fragments0: action");
- is_int(XDP_TCP_NOOP, rls[0].answer, "fragments0: answer");
- is_int(3, rls[0].data.iov_len, "fragments0: data length");
- ok(memcmp("xyz", rls[0].data.iov_base, rls[0].data.iov_len) == 0, "fragments0: data");
+ is_int(KNOT_XDP_MSG_ACK, rls[0].auto_answer, "fragments[0]: auto answer");
ok(rls[0].conn != NULL, "fragments0: connection present");
ok(rls[0].conn == test_conn, "fragments0: same connection");
-
- is_int(XDP_TCP_DATA, rls[1].action, "fragments1: action");
- is_int(4, rls[1].data.iov_len, "fragments1: data length");
- ok(memcmp("abcd", rls[1].data.iov_base, rls[1].data.iov_len) == 0, "fragments1: data");
- ok(rls[1].conn == test_conn, "fragments1: same connection");
-
- is_int(XDP_TCP_DATA, rls[2].action, "fragments2: action");
- is_int(1, rls[2].data.iov_len, "fragments2: data length");
- ok(memcmp("i", rls[2].data.iov_base, rls[2].data.iov_len) == 0, "fragments2: data");
- ok(rls[2].conn == test_conn, "fragments2: same connection");
-
- is_int(XDP_TCP_DATA, rls[3].action, "fragments3: action");
- is_int(2, rls[3].data.iov_len, "fragments3: data length");
- ok(memcmp("AB", rls[3].data.iov_base, rls[3].data.iov_len) == 0, "fragments3: data");
- ok(rls[3].conn == test_conn, "fragments3: same connection");
-
- knot_tcp_relay_free(&relays);
+ is_int(1, rls[0].inbufs_count, "fragments0: inbufs count");
+ is_int(3, rls[0].inbufs[0].iov_len, "fragments0: data length");
+ is_int(0, memcmp("xyz", rls[0].inbufs[0].iov_base, rls[0].inbufs[0].iov_len), "fragments0: data");
+
+ is_int(KNOT_XDP_MSG_ACK, rls[1].auto_answer, "fragments[1]: auto answer");
+ is_int(XDP_TCP_NOOP, rls[1].action, "fragments[1]: action"); // NOTE: NOOP
+ ok(rls[0].conn != NULL, "fragments1: connection present");
+ ok(rls[0].conn == test_conn, "fragments1: same connection");
+ is_int(0, rls[1].inbufs_count, "fragments1: inbufs count");
+
+ is_int(KNOT_XDP_MSG_ACK, rls[2].auto_answer, "fragments[2]: auto answer");
+ ok(rls[0].conn != NULL, "fragments2: connection present");
+ ok(rls[0].conn == test_conn, "fragments2: same connection");
+ is_int(2, rls[2].inbufs_count, "fragments2: inbufs count");
+ is_int(4, rls[2].inbufs[0].iov_len, "fragments2-0: data length");
+ is_int(0, memcmp("abcd", rls[2].inbufs[0].iov_base, rls[2].inbufs[0].iov_len), "fragments2-0: data");
+ is_int(1, rls[2].inbufs[1].iov_len, "fragments2-1: data length");
+ is_int(0, memcmp("i", rls[2].inbufs[1].iov_base, rls[2].inbufs[1].iov_len), "fragments2-1: data");
+
+ is_int(KNOT_XDP_MSG_ACK, rls[3].auto_answer, "fragments[3]: auto answer");
+ ok(rls[0].conn != NULL, "fragments3: connection present");
+ ok(rls[0].conn == test_conn, "fragments3: same connection");
+ is_int(1, rls[3].inbufs_count, "fragments3: inbufs count");
+ is_int(2, rls[3].inbufs[0].iov_len, "fragments3: data length");
+ is_int(0, memcmp("AB", rls[3].inbufs[0].iov_base, rls[3].inbufs[0].iov_len), "fragments3: data");
+
+ knot_tcp_cleanup(test_table, rls, 4);
}
void test_close(void)
@@ -314,25 +335,26 @@ void test_close(void)
size_t conns_pre = test_table->usage;
knot_xdp_msg_t msg;
- knot_tcp_relay_dynarray_t relays = { 0 };
+ knot_tcp_relay_t rl;
prepare_msg(&msg, KNOT_XDP_MSG_FIN | KNOT_XDP_MSG_ACK,
be16toh(test_conn->ip_rem.sin6_port),
be16toh(test_conn->ip_loc.sin6_port));
prepare_seqack(&msg, 0, 0);
- int ret = knot_tcp_relay(test_sock, &msg, 1, test_table, NULL, &relays, NULL);
+ int ret = knot_tcp_recv(&rl, &msg, 1, test_table, NULL);
is_int(KNOT_EOK, ret, "close: relay 1 OK");
+ ret = knot_tcp_send(test_sock, &rl, 1, 1);
+ is_int(KNOT_EOK, ret, "close: send OK");
check_sent(0, 0, 0, 1);
- is_int(1, relays.size, "close: one relay");
- knot_tcp_relay_t *rl = &knot_tcp_relay_dynarray_arr(&relays)[0];
- is_int(XDP_TCP_CLOSE, rl->action, "close: relay action");
- ok(rl->conn == test_conn, "close: same connection");
- is_int(XDP_TCP_CLOSING2, rl->conn->state, "close: conn state");
- knot_tcp_relay_free(&relays);
+ is_int(XDP_TCP_CLOSE, rl.action, "close: relay action");
+ ok(rl.conn == test_conn, "close: same connection");
+ is_int(XDP_TCP_CLOSING2, rl.conn->state, "close: conn state");
msg.flags &= ~KNOT_XDP_MSG_FIN;
prepare_seqack(&msg, 0, 0);
- ret = knot_tcp_relay(test_sock, &msg, 1, test_table, NULL, &relays, NULL);
+ ret = knot_tcp_recv(&rl, &msg, 1, test_table, NULL);
is_int(KNOT_EOK, ret, "close: relay 2 OK");
+ ret = knot_tcp_send(test_sock, &rl, 1, 1);
+ is_int(KNOT_EOK, ret, "close: send 2 OK");
check_sent(0, 0, 0, 0);
is_int(conns_pre - 1, test_table->usage, "close: connection removed");
is_int(conns_pre - 1, tcp_table_timeout_length(test_table), "close: timeout list size");
@@ -349,61 +371,71 @@ void test_many(void)
for (size_t i = 0; i < CONNS; i++) {
prepare_msg(&msgs[i], KNOT_XDP_MSG_SYN, i + 2, 1);
}
+ knot_tcp_relay_t *rls = malloc(CONNS * sizeof(*rls));
- knot_tcp_relay_dynarray_t relays = { 0 };
- int ret = knot_tcp_relay(test_sock, msgs, CONNS, test_table, NULL, &relays, NULL);
+ int ret = knot_tcp_recv(rls, msgs, CONNS, test_table, NULL);
is_int(KNOT_EOK, ret, "many: relay OK");
+ ret = knot_tcp_send(test_sock, rls, CONNS, CONNS);
+ is_int(KNOT_EOK, ret, "many: relay send OK");
check_sent(0, 0, CONNS, 0);
- is_int(CONNS, relays.size, "many: relays count");
is_int(CONNS, test_table->usage, "many: table usage");
- knot_tcp_relay_free(&relays);
+ knot_tcp_cleanup(test_table, rls, CONNS);
usleep(timeout_time);
knot_xdp_msg_t *survive = &msgs[i_survive];
+ knot_tcp_relay_t surv_rl;
survive->flags = (KNOT_XDP_MSG_TCP | KNOT_XDP_MSG_ACK);
knot_tcp_conn_t *surv_conn = tcp_table_find(test_table, survive);
fix_seqack(survive);
prepare_data(survive, "\x00\x00", 2);
- (void)knot_tcp_relay(test_sock, survive, 1, test_table, NULL, &relays, NULL);
- is_int(1, relays.size, "many/survivor: one relay");
- knot_tcp_relay_t *rl = &knot_tcp_relay_dynarray_arr(&relays)[0];
+ ret = knot_tcp_recv(&surv_rl, survive, 1, test_table, NULL);
+ is_int(KNOT_EOK, ret, "many/survivor: OK");
clean_sent();
uint32_t reset_count = 0, close_count = 0;
- ret = knot_tcp_sweep(test_table, test_sock, UINT32_MAX, timeout_time, UINT32_MAX,
- 0, 0, &close_count, &reset_count);
+ ret = knot_tcp_sweep(test_table, timeout_time, UINT32_MAX, UINT32_MAX, UINT32_MAX, UINT32_MAX,
+ UINT32_MAX, rls, CONNS, &close_count, &reset_count);
is_int(KNOT_EOK, ret, "many/timeout1: OK");
is_int(CONNS - 1, close_count, "many/timeout1: close count");
is_int(0, reset_count, "may/timeout1: reset count");
+ ret = knot_tcp_send(test_sock, rls, CONNS, CONNS);
+ is_int(KNOT_EOK, ret, "many/timeout1: send OK");
check_sent(0, 0, 0, CONNS - 1);
close_count = 0;
- ret = knot_tcp_sweep(test_table, test_sock, UINT32_MAX, UINT32_MAX, timeout_time,
- 0, 0, &close_count, &reset_count);
+ ret = knot_tcp_sweep(test_table, UINT32_MAX, timeout_time, UINT32_MAX, UINT32_MAX, UINT32_MAX,
+ UINT32_MAX, rls, CONNS, &close_count, &reset_count);
is_int(KNOT_EOK, ret, "many/timeout2: OK");
is_int(0, close_count, "many/timeout2: close count");
is_int(CONNS - 1, reset_count, "may/timeout2: reset count");
+ ret = knot_tcp_send(test_sock, rls, CONNS, CONNS);
+ is_int(KNOT_EOK, ret, "many/timeout2: send OK");
check_sent(0, CONNS - 1, 0, 0);
+ knot_tcp_cleanup(test_table, rls, CONNS);
is_int(1, test_table->usage, "many/timeout: one survivor");
is_int(1, tcp_table_timeout_length(test_table), "many/timeout: one survivor in timeout list");
ok(surv_conn != NULL, "many/timeout: survivor connection present");
- ok(surv_conn == rl->conn, "many/timeout: same connection");
+ ok(surv_conn == surv_rl.conn, "many/timeout: same connection");
+ knot_tcp_cleanup(test_table, &surv_rl, 1);
free(msgs);
+ free(rls);
}
void test_ibufs_size(void)
{
int CONNS = 4;
knot_xdp_msg_t msgs[CONNS];
- knot_tcp_relay_dynarray_t relays = { 0 };
+ knot_tcp_relay_t rls[CONNS];
// just open connections
for (int i = 0; i < CONNS; i++) {
prepare_msg(&msgs[i], KNOT_XDP_MSG_SYN, i + 2000, 1);
}
- int ret = knot_tcp_relay(test_sock, msgs, CONNS, test_table, NULL, &relays, NULL);
+ int ret = knot_tcp_recv(rls, msgs, CONNS, test_table, NULL);
is_int(KNOT_EOK, ret, "ibufs: open OK");
+ ret = knot_tcp_send(test_sock, rls, CONNS, CONNS);
+ is_int(KNOT_EOK, ret, "ibufs: first send OK");
check_sent(0, 0, CONNS, 0);
for (int i = 0; i < CONNS; i++) {
msgs[i].flags = KNOT_XDP_MSG_TCP | KNOT_XDP_MSG_ACK;
@@ -414,11 +446,13 @@ void test_ibufs_size(void)
// first connection will start a fragment buf then finish it
fix_seqack(&msgs[0]);
prepare_data(&msgs[0], "\x00\x0a""lorem", 7);
- ret = knot_tcp_relay(test_sock, &msgs[0], 1, test_table, NULL, &relays, NULL);
+ ret = knot_tcp_recv(&rls[0], &msgs[0], 1, test_table, NULL);
is_int(KNOT_EOK, ret, "ibufs: must be OK");
+ ret = knot_tcp_send(test_sock, &rls[0], 1, 1);
+ is_int(KNOT_EOK, ret, "ibufs: must send OK");
check_sent(1, 0, 0, 0);
is_int(7, test_table->inbufs_total, "inbufs: first inbuf");
- knot_tcp_relay_free(&relays);
+ knot_tcp_cleanup(test_table, &rls[0], 1);
// other connection will just store fragments
fix_seqacks(msgs, CONNS);
@@ -426,22 +460,28 @@ void test_ibufs_size(void)
prepare_data(&msgs[1], "\x00\xff""12345", 7);
prepare_data(&msgs[2], "\xff\xff""abcde", 7);
prepare_data(&msgs[3], "\xff\xff""abcde", 7);
- ret = knot_tcp_relay(test_sock, msgs, CONNS, test_table, NULL, &relays, NULL);
+ ret = knot_tcp_recv(rls, msgs, CONNS, test_table, NULL);
is_int(KNOT_EOK, ret, "inbufs: relay OK");
+ ret = knot_tcp_send(test_sock, rls, CONNS, CONNS);
+ is_int(KNOT_EOK, ret, "inbufs: send OK");
check_sent(CONNS, 0, 0, 0);
is_int(21, test_table->inbufs_total, "inbufs: after change");
- is_int(1, relays.size, "inbufs: one relay");
- is_int(10, knot_tcp_relay_dynarray_arr(&relays)[0].data.iov_len, "inbufs: data length");
- knot_tcp_relay_free(&relays);
+ is_int(0, rls[1].action, "inbufs: one relay");
+ is_int(10, rls[0].inbufs[0].iov_len, "inbufs: data length");
+ knot_tcp_cleanup(test_table, rls, CONNS);
// now free some
uint32_t reset_count = 0, close_count = 0;
- ret = knot_tcp_sweep(test_table, test_sock, UINT32_MAX, UINT32_MAX, UINT32_MAX,
- 0, 8, &close_count, &reset_count);
+ ret = knot_tcp_sweep(test_table, UINT32_MAX, UINT32_MAX, UINT32_MAX, UINT32_MAX,
+ test_table->inbufs_total - 8, UINT32_MAX, rls,
+ CONNS, &close_count, &reset_count);
is_int(KNOT_EOK, ret, "inbufs: timeout OK");
+ ret = knot_tcp_send(test_sock, rls, CONNS, CONNS);
+ is_int(KNOT_EOK, ret, "inbufs: timeout send OK");
check_sent(0, 2, 0, 0);
is_int(0, close_count, "inbufs: close count");
is_int(2, reset_count, "inbufs: reset count");
+ knot_tcp_cleanup(test_table, rls, CONNS);
is_int(7, test_table->inbufs_total, "inbufs: final state");
ok(NULL != tcp_table_find(test_table, &msgs[0]), "inbufs: first conn survived");
ok(NULL == tcp_table_find(test_table, &msgs[1]), "inbufs: second conn not survived");
@@ -451,6 +491,68 @@ void test_ibufs_size(void)
clean_table();
}
+void test_obufs(void)
+{
+ knot_xdp_msg_t msg;
+ knot_tcp_relay_t rl;
+
+ prepare_msg(&msg, KNOT_XDP_MSG_SYN, 1, 2);
+ (void)knot_tcp_recv(&rl, &msg, 1, test_table, NULL); // SYN
+ (void)knot_tcp_send(test_sock, &rl, 1, 1); // SYN+ACK
+ prepare_msg(&msg, KNOT_XDP_MSG_ACK, 1, 2);
+ prepare_seqack(&msg, 0, 1);
+ (void)knot_tcp_recv(&rl, &msg, 1, test_table, NULL); // ACK
+
+ size_t TEST_MSS = 1111;
+ 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;
+ send2_mss = TEST_MSS;
+
+ int ret = knot_tcp_reply_data(&rl, test_table, data, DATA_LEN), i = 0;
+ is_int(KNOT_EOK, ret, "obufs: fill with data");
+ for (struct tcp_outbuf *ob = rl.conn->outbufs.bufs; ob != NULL; ob = ob->next, i++) {
+ if (ob->next == NULL) {
+ ok(ob->len > 0, "init last ob[%d]: non-trivial", i);
+ ok(ob->len <= TEST_MSS, "init last ob[%d]: fulfills MSS", i);
+ } else {
+ is_int(TEST_MSS, ob->len, "init ob[%d]: exactly MSS", i);
+ }
+ ok(!ob->sent, "init ob[%d]: not sent", i);
+ }
+ ret = knot_tcp_send(test_sock, &rl, 1, 20), i = 0;
+ is_int(KNOT_EOK, ret, "obufs: send OK");
+ is_int((DATA_LEN + 2) / TEST_MSS * TEST_MSS, sent2_data, "obufs: sent all but one MSS");
+ for (struct tcp_outbuf *ob = rl.conn->outbufs.bufs; ob != NULL; ob = ob->next, i++) {
+ if (ob->next == NULL) {
+ ok(!ob->sent, "last ob[%d]: not sent", i);
+ } else {
+ ok(ob->sent, "ob[%d]: sent", i);
+ if (ob->next->next != NULL) {
+ is_int(ob->seqno + ob->len, ob->next->seqno, "init ob[%d+1]: seqno", i);
+ }
+ }
+ }
+ knot_tcp_cleanup(test_table, &rl, 1);
+
+ prepare_seqack(&msg, 0, TEST_MSS);
+ ret = knot_tcp_recv(&rl, &msg, 1, test_table, NULL);
+ is_int(KNOT_EOK, ret, "obufs: ACKed data");
+ 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");
+ ok(!surv_ob->sent, "obufs: survivor not sent");
+ ret = knot_tcp_send(test_sock, &rl, 1, 20);
+ is_int(KNOT_EOK, ret, "obufs: send rest OK");
+ is_int(DATA_LEN + 2, sent2_data, "obufs: sent all");
+ ok(surv_ob->sent, "obufs: survivor sent");
+ is_int(sent_seqno, surv_ob->seqno, "obufs: survivor seqno");
+
+ knot_tcp_cleanup(test_table, &rl, 1);
+ clean_table();
+ free(data);
+}
+
static void init_mock(knot_xdp_socket_t **socket, void *send_mock)
{
*socket = calloc(1, sizeof(**socket));
@@ -482,6 +584,10 @@ int main(int argc, char *argv[])
test_many();
knot_xdp_deinit(test_sock);
+ init_mock(&test_sock, mock_send2);
+ test_obufs();
+
+ knot_xdp_deinit(test_sock);
knot_tcp_table_free(test_table);
return 0;