From 453308a6ca88d15b46c5fca2b6ee272d0f98eab0 Mon Sep 17 00:00:00 2001 From: Libor Peltan <libor.peltan@nic.cz> Date: Thu, 20 May 2021 17:28:40 +0200 Subject: [PATCH] tests: XDP-TCP test of connection handling, states and stress --- Knot.files | 1 + src/libknot/xdp/bpf-user.h | 5 +- src/libknot/xdp/tcp.c | 7 + src/libknot/xdp/tcp.h | 5 + src/libknot/xdp/xdp.c | 44 ++++- src/libknot/xdp/xdp.h | 20 +- tests/.gitignore | 1 + tests/Makefile.am | 5 + tests/libknot/test_xdp_tcp.c | 365 +++++++++++++++++++++++++++++++++++ 9 files changed, 447 insertions(+), 6 deletions(-) create mode 100644 tests/libknot/test_xdp_tcp.c diff --git a/Knot.files b/Knot.files index 8cfcf4aa8d..0b88e2c969 100644 --- a/Knot.files +++ b/Knot.files @@ -611,6 +611,7 @@ tests/libknot/test_rrset-wire.c tests/libknot/test_rrset.c tests/libknot/test_tsig.c tests/libknot/test_wire.c +tests/libknot/test_xdp_tcp.c tests/libknot/test_yparser.c tests/libknot/test_ypschema.c tests/libknot/test_yptrafo.c diff --git a/src/libknot/xdp/bpf-user.h b/src/libknot/xdp/bpf-user.h index 743e33568b..43964b75f9 100644 --- a/src/libknot/xdp/bpf-user.h +++ b/src/libknot/xdp/bpf-user.h @@ -1,4 +1,4 @@ -/* Copyright (C) 2020 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz> +/* Copyright (C) 2021 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz> This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -75,6 +75,9 @@ struct knot_xdp_socket { /*! Interface context. */ const struct kxsk_iface *iface; + /*! If non-NULL, it's a mocked socket with this send function. */ + void *send_mock; + /*! The kernel has to be woken up by a syscall indication. */ bool kernel_needs_wakeup; diff --git a/src/libknot/xdp/tcp.c b/src/libknot/xdp/tcp.c index 457553e86d..8604e749b6 100644 --- a/src/libknot/xdp/tcp.c +++ b/src/libknot/xdp/tcp.c @@ -112,6 +112,13 @@ static knot_tcp_conn_t **tcp_table_lookup(const struct sockaddr_in6 *rem, const return res; } +_public_ +knot_tcp_conn_t *knot_tcp_table_find(knot_tcp_table_t *table, knot_xdp_msg_t *msg_recv) +{ + uint64_t unused; + return *tcp_table_lookup(&msg_recv->ip_from, &msg_recv->ip_to, &unused, table); +} + static void tcp_table_del(knot_tcp_conn_t **todel) { knot_tcp_conn_t *conn = *todel; diff --git a/src/libknot/xdp/tcp.h b/src/libknot/xdp/tcp.h index 7db63b1174..9f2c73b9ce 100644 --- a/src/libknot/xdp/tcp.h +++ b/src/libknot/xdp/tcp.h @@ -118,6 +118,11 @@ knot_tcp_table_t *knot_tcp_table_new(size_t size); */ void knot_tcp_table_free(knot_tcp_table_t *t); +/*! + * \brief Find connection related to incomming message. + */ +knot_tcp_conn_t *knot_tcp_table_find(knot_tcp_table_t *table, knot_xdp_msg_t *msg_recv); + /*! * \brief Process received packets, send ACKs, pick incoming data. * diff --git a/src/libknot/xdp/xdp.c b/src/libknot/xdp/xdp.c index 24d420c9f6..9921e99023 100644 --- a/src/libknot/xdp/xdp.c +++ b/src/libknot/xdp/xdp.c @@ -199,12 +199,33 @@ int knot_xdp_init(knot_xdp_socket_t **socket, const char *if_name, int if_queue, return ret; } +_public_ +int knot_xdp_init_mock(knot_xdp_socket_t **socket, knot_xdp_send_mock_f send_mock) +{ + if (socket == NULL || send_mock == NULL) { + return KNOT_EINVAL; + } + + *socket = calloc(1, sizeof(**socket)); + if (*socket == NULL) { + return KNOT_ENOMEM; + } + + (*socket)->send_mock = send_mock; + + return KNOT_EOK; +} + _public_ void knot_xdp_deinit(knot_xdp_socket_t *socket) { if (socket == NULL) { return; } + if (unlikely(socket->send_mock != NULL)) { + free(socket); + return; + } kxsk_socket_stop(socket->iface); xsk_socket__delete(socket->xsk); @@ -235,7 +256,7 @@ static void tx_free_relative(struct kxsk_umem *umem, uint64_t addr_relative) _public_ void knot_xdp_send_prepare(knot_xdp_socket_t *socket) { - if (socket == NULL) { + if (socket == NULL || unlikely(socket->send_mock != NULL)) { return; } @@ -257,8 +278,11 @@ void knot_xdp_send_prepare(knot_xdp_socket_t *socket) xsk_ring_cons__release(cq, completed); } -static struct umem_frame *alloc_tx_frame(struct kxsk_umem *umem) +static struct umem_frame *alloc_tx_frame(struct kxsk_umem *umem, void *send_mock) { + if (unlikely(send_mock != NULL)) { + return malloc(sizeof(struct umem_frame)); + } if (unlikely(umem->tx_free_count == 0)) { return NULL; } @@ -282,7 +306,7 @@ int knot_xdp_send_alloc(knot_xdp_socket_t *socket, knot_xdp_msg_flag_t flags, return KNOT_EINVAL; } - struct umem_frame *uframe = alloc_tx_frame(socket->umem); + struct umem_frame *uframe = alloc_tx_frame(socket->umem, socket->send_mock); if (uframe == NULL) { return KNOT_ENOMEM; } @@ -301,7 +325,7 @@ int knot_xdp_reply_alloc(knot_xdp_socket_t *socket, const knot_xdp_msg_t *query, return KNOT_EINVAL; } - struct umem_frame *uframe = alloc_tx_frame(socket->umem); + struct umem_frame *uframe = alloc_tx_frame(socket->umem, socket->send_mock); if (uframe == NULL) { return KNOT_ENOMEM; } @@ -314,6 +338,10 @@ int knot_xdp_reply_alloc(knot_xdp_socket_t *socket, const knot_xdp_msg_t *query, static void free_unsent(knot_xdp_socket_t *socket, const knot_xdp_msg_t *msg) { + if (unlikely(socket->send_mock != NULL)) { + free(msg->payload.iov_base - prot_write_hdrs_len(msg)); + return; + } uint64_t addr_relative = (uint8_t *)msg->payload.iov_base - socket->umem->frames->bytes; tx_free_relative(socket->umem, addr_relative); @@ -326,6 +354,14 @@ int knot_xdp_send(knot_xdp_socket_t *socket, const knot_xdp_msg_t msgs[], if (socket == NULL || msgs == NULL || sent == NULL) { return KNOT_EINVAL; } + if (unlikely(socket->send_mock != NULL)) { + knot_xdp_send_mock_f send_mock = socket->send_mock; + int ret = send_mock(socket, msgs, count, sent); + for (uint32_t i = 0; i < count; ++i) { + free_unsent(socket, &msgs[i]); + } + return ret; + } /* Now we want to do something close to * xsk_ring_prod__reserve(&socket->tx, count, *idx) diff --git a/src/libknot/xdp/xdp.h b/src/libknot/xdp/xdp.h index aa9ad644ef..7bff7c1c76 100644 --- a/src/libknot/xdp/xdp.h +++ b/src/libknot/xdp/xdp.h @@ -55,10 +55,18 @@ typedef enum { /*! \brief Context structure for one XDP socket. */ typedef struct knot_xdp_socket knot_xdp_socket_t; +/*! + * \brief A mocked XDP send function. + * + * \note This must correspond to the prototype of knot_xdp_send(). + */ +typedef int (*knot_xdp_send_mock_f)(knot_xdp_socket_t *, const knot_xdp_msg_t[], + uint32_t, uint32_t *); + /*! * \brief Initialize XDP socket. * - * \param socket Socket ctx. + * \param socket XDP socket. * \param if_name Name of the net iface (e.g. eth0). * \param if_queue Network card queue to be used (normally 1 socket per each queue). * \param listen_port Port to listen on, or KNOT_XDP_LISTEN_PORT_* flag. @@ -69,6 +77,16 @@ typedef struct knot_xdp_socket knot_xdp_socket_t; int knot_xdp_init(knot_xdp_socket_t **socket, const char *if_name, int if_queue, uint32_t listen_port, knot_xdp_load_bpf_t load_bpf); +/*! + * \brief Initialize mocked XDP socket. + * + * \param socket XDP socket. + * \param send_mock Mocked send function. + * + * \return KNOT_E* + */ +int knot_xdp_init_mock(knot_xdp_socket_t **socket, knot_xdp_send_mock_f send_mock); + /*! * \brief De-init XDP socket. * diff --git a/tests/.gitignore b/tests/.gitignore index a3cff1a236..ef9af5cb69 100644 --- a/tests/.gitignore +++ b/tests/.gitignore @@ -81,6 +81,7 @@ /libknot/test_rrset /libknot/test_rrset-wire /libknot/test_tsig +/libknot/test_xdp_tcp /libknot/test_yparser /libknot/test_ypschema /libknot/test_yptrafo diff --git a/tests/Makefile.am b/tests/Makefile.am index eeeab755ae..60f025e02c 100644 --- a/tests/Makefile.am +++ b/tests/Makefile.am @@ -155,6 +155,11 @@ check_PROGRAMS += \ libknot/test_yptrafo \ libknot/test_wire +if ENABLE_XDP +check_PROGRAMS += \ + libknot/test_xdp_tcp +endif ENABLE_XDP + if HAVE_LIBUTILS check_PROGRAMS += \ utils/test_cert \ diff --git a/tests/libknot/test_xdp_tcp.c b/tests/libknot/test_xdp_tcp.c new file mode 100644 index 0000000000..aeed4ee31b --- /dev/null +++ b/tests/libknot/test_xdp_tcp.c @@ -0,0 +1,365 @@ +/* Copyright (C) 2021 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz> + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <https://www.gnu.org/licenses/>. + */ + +#include <unistd.h> + +#include "tap/basic.h" +#include "contrib/macros.h" +#include "libknot/error.h" +#include "libknot/xdp/msg_init.h" + +#include "libknot/xdp/tcp.h" + +knot_tcp_table_t *test_table = NULL; +#define TEST_TABLE_SIZE 100 + +size_t sent_acks = 0; +size_t sent_rsts = 0; +size_t sent_syns = 0; +size_t sent_fins = 0; +uint32_t sent_seqno = 0; +uint32_t sent_ackno = 0; + +knot_xdp_socket_t *test_sock = NULL; + +struct sockaddr_in test_addr = { AF_INET, 0, { 127 + (1 << 24) }, { 0 } }; + +knot_tcp_conn_t *test_conn = NULL; + +static int mock_send(knot_xdp_socket_t *sock, const knot_xdp_msg_t msgs[], + uint32_t n_msgs, uint32_t *sent) +{ + UNUSED(sock); + UNUSED(sent); + ok(n_msgs <= 20, "send: 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, "send: is TCP message"); + ok(msg->payload.iov_len == 0, "send: is empty payload"); + + if (msg->flags & KNOT_XDP_MSG_RST) { + ok(!(msg->flags & KNOT_XDP_MSG_ACK), "send: no RST+ACK"); + sent_rsts++; + } else if (msg->flags & KNOT_XDP_MSG_SYN) { + ok(msg->flags & KNOT_XDP_MSG_ACK, "send: is SYN+ACK"); + sent_syns++; + } else if (msg->flags & KNOT_XDP_MSG_FIN) { + ok(msg->flags & KNOT_XDP_MSG_ACK, "send: FIN has always ACK"); + sent_fins++; + } else { + ok(msg->flags & KNOT_XDP_MSG_ACK, "send: is ACK"); + sent_acks++; + } + + sent_seqno = msg->seqno; + sent_ackno = msg->ackno; + } + return KNOT_EOK; +} + +static int mock_send_nocheck(knot_xdp_socket_t *sock, const knot_xdp_msg_t msgs[], + uint32_t n_msgs, uint32_t *sent) +{ + UNUSED(sock); + UNUSED(sent); + for (uint32_t i = 0; i < n_msgs; i++) { + const knot_xdp_msg_t *msg = msgs + i; + if (msg->flags & KNOT_XDP_MSG_RST) { + sent_rsts++; + } else if (msg->flags & KNOT_XDP_MSG_SYN) { + sent_syns++; + } else if (msg->flags & KNOT_XDP_MSG_FIN) { + sent_fins++; + } else { + sent_acks++; + } + sent_seqno = msg->seqno; + sent_ackno = msg->ackno; + } + return KNOT_EOK; +} + +static void clean_table(void) +{ + (void)knot_xdp_tcp_cleanup(test_table, 0, UINT32_MAX, NULL); +} + +static void clean_sent(void) +{ + sent_acks = 0; + sent_rsts = 0; + sent_syns = 0; + sent_fins = 0; +} + +static void check_sent(size_t expect_acks, size_t expect_rsts, size_t expect_syns, size_t expect_fins) +{ + is_int(expect_acks, sent_acks, "sent ACKs"); + is_int(expect_rsts, sent_rsts, "sent RSTs"); + is_int(expect_syns, sent_syns, "sent SYNs"); + is_int(expect_fins, sent_fins, "sent FINs"); + clean_sent(); +} + +static void prepare_msg(knot_xdp_msg_t *msg, int flags, uint16_t sport, uint16_t dport) +{ + msg_init(msg, flags | KNOT_XDP_MSG_TCP); + memcpy(&msg->ip_from, &test_addr, sizeof(test_addr)); + memcpy(&msg->ip_to, &test_addr, sizeof(test_addr)); + msg->ip_from.sin6_port = htobe16(sport); + msg->ip_to.sin6_port = htobe16(dport); +} + +static void prepare_seqack(knot_xdp_msg_t *msg, int seq_shift, int ack_shift) +{ + msg->seqno = sent_ackno + seq_shift; + msg->ackno = sent_seqno + ack_shift; +} + +static void prepare_data(knot_xdp_msg_t *msg, const char *bytes, size_t n) +{ + msg->payload.iov_len = n; + msg->payload.iov_base = (void *)bytes; +} + +void test_syn(void) +{ + knot_xdp_msg_t msg; + tcp_relay_dynarray_t relays = { 0 }; + prepare_msg(&msg, KNOT_XDP_MSG_SYN, 1, 2); + int ret = knot_xdp_tcp_relay(test_sock, &msg, 1, test_table, NULL, &relays, NULL); + is_int(KNOT_EOK, ret, "SYN: relay 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 = &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(1, test_table->usage, "SYN: one connection in table"); + knot_tcp_conn_t *conn = knot_tcp_table_find(test_table, &msg); + ok(conn != NULL, "SYN: connection present"); + 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_xdp_tcp_relay_free(&relays); + test_conn = conn; +} + +void test_establish(void) +{ + knot_xdp_msg_t msg; + tcp_relay_dynarray_t relays = { 0 }; + prepare_msg(&msg, KNOT_XDP_MSG_ACK, 1, 2); + prepare_seqack(&msg, 0, 1); + int ret = knot_xdp_tcp_relay(test_sock, &msg, 1, test_table, NULL, &relays, NULL); + is_int(KNOT_EOK, ret, "establish: relay OK"); + check_sent(0, 0, 0, 0); + is_int(0, relays.size, "establish: no relay"); + /*knot_tcp_relay_t *rl = &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_xdp_tcp_relay_free(&relays); + clean_table(); +} + +void test_syn_ack(void) +{ + knot_xdp_msg_t msg; + tcp_relay_dynarray_t relays = { 0 }; + prepare_msg(&msg, KNOT_XDP_MSG_SYN | KNOT_XDP_MSG_ACK, 1000, 2000); + int ret = knot_xdp_tcp_relay(test_sock, &msg, 1, test_table, NULL, &relays, NULL); + is_int(KNOT_EOK, ret, "SYN+ACK: relay 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 = &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"); + + test_conn = rl->conn; + knot_xdp_tcp_relay_free(&relays); +} + +void test_data_fragments(void) +{ + knot_xdp_msg_t msgs[4]; + tcp_relay_dynarray_t relays = { 0 }; + + // first msg contains one whole payload and one fragment + prepare_msg(&msgs[0], KNOT_XDP_MSG_ACK, 1000, 2000); + prepare_seqack(&msgs[0], 0, 0); + prepare_data(&msgs[0], "\x00\x03""xyz""\x00\x04""ab", 9); + + // second msg contains just fragment not completing anything + prepare_msg(&msgs[1], KNOT_XDP_MSG_ACK, 1000, 2000); + prepare_seqack(&msgs[1], 9, 0); + prepare_data(&msgs[1], "c", 1); + + // third msg finishes fragment, contains one whole, and starts new fragment by just half of length info + prepare_msg(&msgs[2], KNOT_XDP_MSG_ACK, 1000, 2000); + prepare_seqack(&msgs[2], 10, 0); + prepare_data(&msgs[2], "d""\x00\x01""i""\x00", 5); + + // fourth msg completes fragment and starts never-finishing one + prepare_msg(&msgs[3], KNOT_XDP_MSG_ACK, 1000, 2000); + prepare_seqack(&msgs[3], 15, 0); + prepare_data(&msgs[3], "\x02""AB""\xff\xff""abcdefghijklmnopqrstuvwxyz...", 34); + + int ret = knot_xdp_tcp_relay(test_sock, msgs, sizeof(msgs) / sizeof(msgs[0]), test_table, NULL, &relays, NULL); + is_int(KNOT_EOK, ret, "fragments: relay 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 = 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"); + 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_xdp_tcp_relay_free(&relays); +} + +void test_close(void) +{ + size_t conns_pre = test_table->usage; + + knot_xdp_msg_t msg; + tcp_relay_dynarray_t relays = { 0 }; + 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_xdp_tcp_relay(test_sock, &msg, 1, test_table, NULL, &relays, NULL); + is_int(KNOT_EOK, ret, "close: relay 1 OK"); + check_sent(0, 0, 0, 1); + is_int(1, relays.size, "close: one relay"); + knot_tcp_relay_t *rl = &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_CLOSING, rl->conn->state, "close: conn state"); + knot_xdp_tcp_relay_free(&relays); + + msg.flags &= ~KNOT_XDP_MSG_FIN; + prepare_seqack(&msg, 0, 0); + ret = knot_xdp_tcp_relay(test_sock, &msg, 1, test_table, NULL, &relays, NULL); + is_int(KNOT_EOK, ret, "close: relay 2 OK"); + check_sent(0, 0, 0, 0); + is_int(conns_pre - 1, test_table->usage, "close: connection removed"); + is_int(conns_pre - 1, list_size(&test_table->timeout), "close: timeout list size"); +} + +void test_many(void) +{ + size_t CONNS = test_table->size * test_table->size; + size_t i_survive = CONNS / 2; + uint32_t timeout_time = 1000000; + + knot_xdp_msg_t *msgs = malloc(CONNS * sizeof(*msgs)); + assert(msgs != NULL); + for (size_t i = 0; i < CONNS; i++) { + prepare_msg(&msgs[i], KNOT_XDP_MSG_SYN, i + 2, 1); + } + + tcp_relay_dynarray_t relays = { 0 }; + int ret = knot_xdp_tcp_relay(test_sock, msgs, CONNS, test_table, NULL, &relays, NULL); + is_int(KNOT_EOK, ret, "many: relay 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_xdp_tcp_relay_free(&relays); + usleep(timeout_time); + knot_xdp_msg_t *survive = &msgs[i_survive]; + survive->flags = (KNOT_XDP_MSG_TCP | KNOT_XDP_MSG_ACK); + knot_tcp_conn_t *surv_conn = knot_tcp_table_find(test_table, survive); + survive->seqno = surv_conn->seqno; + survive->ackno = surv_conn->ackno; + prepare_data(survive, "\x00\x00", 2); + (void)knot_xdp_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 = &tcp_relay_dynarray_arr(&relays)[0]; + clean_sent(); + + uint32_t reset_count = 0; + ret = knot_xdp_tcp_timeout(test_table, test_sock, UINT32_MAX, timeout_time, UINT32_MAX, 0, 0, &reset_count); + is_int(KNOT_EOK, ret, "many/timeout1: OK"); + is_int(0, reset_count, "may/timeout1: reset count"); + check_sent(0, 0, 0, CONNS - 1); + + ret = knot_xdp_tcp_timeout(test_table, test_sock, UINT32_MAX, UINT32_MAX, timeout_time, 0, 0, &reset_count); + is_int(KNOT_EOK, ret, "many/timeout2: OK"); + is_int(CONNS - 1, reset_count, "may/timeout2: reset count"); + check_sent(0, CONNS - 1, 0, 0); + is_int(1, test_table->usage, "many/timeout: one survivor"); + is_int(1, list_size(&test_table->timeout), "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"); + + free(msgs); +} + +int main(int argc, char *argv[]) +{ + UNUSED(argc); + UNUSED(argv); + plan_lazy(); + + test_table = knot_tcp_table_new(TEST_TABLE_SIZE); + assert(test_table != NULL); + + int ret = knot_xdp_init_mock(&test_sock, mock_send); + assert(ret == KNOT_EOK); + + test_syn(); + test_establish(); + + test_syn_ack(); + test_data_fragments(); + test_close(); + + knot_xdp_deinit(test_sock); + ret = knot_xdp_init_mock(&test_sock, mock_send_nocheck); + assert(ret == KNOT_EOK); + test_many(); + + knot_xdp_deinit(test_sock); + knot_tcp_table_free(test_table); + + return 0; +} -- GitLab