diff options
author | Libor Peltan <libor.peltan@nic.cz> | 2021-09-10 16:56:33 +0200 |
---|---|---|
committer | Daniel Salzman <daniel.salzman@nic.cz> | 2022-03-16 13:41:23 +0100 |
commit | 302b6901998f3f8324c97e352fc0038ef3e30bf5 (patch) | |
tree | d87b502c33902b9d8ce757099ddfe37daba1510d /tests/libknot | |
parent | xdp-tcp: bugfix: incomming RST has no ackno (diff) | |
download | knot-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.c | 288 |
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; |