diff --git a/src/libknot/xdp/tcp.c b/src/libknot/xdp/tcp.c index 8604e749b6ed75e0bf3eddf827b058d9361a3f5f..724f5c73dac1dd90c29f96d7c7dbf5a165d56691 100644 --- a/src/libknot/xdp/tcp.c +++ b/src/libknot/xdp/tcp.c @@ -133,6 +133,7 @@ static void tcp_table_del(knot_tcp_conn_t **todel) static void tcp_table_del2(knot_tcp_conn_t **todel, knot_tcp_table_t *table) { assert(table->usage > 0); + table->inbufs_total -= (*todel)->inbuf.iov_len; tcp_table_del(todel); table->usage--; } @@ -253,7 +254,7 @@ int knot_xdp_tcp_relay(knot_xdp_socket_t *socket, knot_xdp_msg_t msgs[], uint32_ relay.action = XDP_TCP_DATA; struct iovec msg_payload = msg->payload, tofree; - ret = knot_tcp_input_buffers(&(*conn)->inbuf, &msg_payload, &tofree); + ret = knot_tcp_input_buffers(&(*conn)->inbuf, &msg_payload, &tofree, &tcp_table->inbufs_total); if (tofree.iov_len > 0 && ret == KNOT_EOK) { relay.data.iov_base = tofree.iov_base + sizeof(uint16_t); diff --git a/src/libknot/xdp/tcp.h b/src/libknot/xdp/tcp.h index 9f2c73b9cefc83a96dda91ec6d50b4283a03665e..06b9375228cb8ee39f12c285ff434f775dbe3c1d 100644 --- a/src/libknot/xdp/tcp.h +++ b/src/libknot/xdp/tcp.h @@ -73,6 +73,7 @@ typedef struct { size_t size; list_t timeout; size_t usage; + size_t inbufs_total; uint32_t hash_secret[4]; knot_tcp_conn_t *conns[]; } knot_tcp_table_t; diff --git a/src/libknot/xdp/tcp_iobuf.c b/src/libknot/xdp/tcp_iobuf.c index c1aaddcf8afe0c7336c6548dbb6b48d7117aae51..3698338ffe6ca1fdce5eca58a1da4b7b5903f7ab 100644 --- a/src/libknot/xdp/tcp_iobuf.c +++ b/src/libknot/xdp/tcp_iobuf.c @@ -36,7 +36,8 @@ size_t knot_tcp_pay_len(const struct iovec *payload) return req_len(payload->iov_base); } -int knot_tcp_input_buffers(struct iovec *buffer, struct iovec *data, struct iovec *data_tofree) +int knot_tcp_input_buffers(struct iovec *buffer, struct iovec *data, + struct iovec *data_tofree, size_t *buffers_total) { memset(data_tofree, 0, sizeof(*data_tofree)); if (data->iov_len < 1) { @@ -62,6 +63,7 @@ int knot_tcp_input_buffers(struct iovec *buffer, struct iovec *data, struct iove return KNOT_ENOMEM; } memcpy(data_tofree->iov_base + buffer->iov_len, data->iov_base, data_use); + *buffers_total -= buffer->iov_len; buffer->iov_base = NULL; buffer->iov_len = 0; data->iov_base += data_use; @@ -73,6 +75,7 @@ int knot_tcp_input_buffers(struct iovec *buffer, struct iovec *data, struct iove } buffer->iov_base = bufnew; memcpy(buffer->iov_base + buffer->iov_len, data->iov_base, data->iov_len); + *buffers_total += data->iov_len; buffer->iov_len += data->iov_len; data->iov_base += data->iov_len; data->iov_len = 0; @@ -96,6 +99,7 @@ int knot_tcp_input_buffers(struct iovec *buffer, struct iovec *data, struct iove memset(data_tofree, 0, sizeof(*data_tofree)); return KNOT_ENOMEM; } + *buffers_total += MAX(data_end.iov_len, 2); buffer->iov_len = data_end.iov_len; memcpy(buffer->iov_base, data_end.iov_base, data_end.iov_len); data->iov_len -= data_end.iov_len; diff --git a/src/libknot/xdp/tcp_iobuf.h b/src/libknot/xdp/tcp_iobuf.h index 174f44e192924d3ddd68b53931f74a1cc576b293..58d82a51bf0a506f931fc490638976d26709b310 100644 --- a/src/libknot/xdp/tcp_iobuf.h +++ b/src/libknot/xdp/tcp_iobuf.h @@ -38,9 +38,11 @@ size_t knot_tcp_pay_len(const struct iovec *payload); * \param buffer In/out: persistent buffer to store incomplete DNS payloads between receiving packets. * \param data In/out: momental DNS payloads in incomming packet. * \param data_tofree Out: once more DNS payload defragmented from multiple packets. + * \param buffers_total In/Out: total size of buffers (will be increased or decreased). * * \return KNOT_EOK, KNOT_ENOMEM */ -int knot_tcp_input_buffers(struct iovec *buffer, struct iovec *data, struct iovec *data_tofree); +int knot_tcp_input_buffers(struct iovec *buffer, struct iovec *data, + struct iovec *data_tofree, size_t *buffers_total); /*! @} */ diff --git a/tests/libknot/test_xdp_tcp.c b/tests/libknot/test_xdp_tcp.c index aeed4ee31b7df34d5a48858af4f538d0d4179bbf..75bdf8a45511b000d0264cb196b1f5b11725a0cf 100644 --- a/tests/libknot/test_xdp_tcp.c +++ b/tests/libknot/test_xdp_tcp.c @@ -136,6 +136,21 @@ static void prepare_data(knot_xdp_msg_t *msg, const char *bytes, size_t n) msg->payload.iov_base = (void *)bytes; } +static void fix_seqack(knot_xdp_msg_t *msg) +{ + knot_tcp_conn_t *conn = knot_tcp_table_find(test_table, msg); + assert(conn != NULL); + msg->seqno = conn->seqno; + msg->ackno = conn->ackno; +} + +static void fix_seqacks(knot_xdp_msg_t *msgs, size_t count) +{ + for (size_t i = 0; i < count; i++) { + fix_seqack(&msgs[i]); + } +} + void test_syn(void) { knot_xdp_msg_t msg; @@ -308,8 +323,7 @@ void test_many(void) 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; + fix_seqack(survive); 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"); @@ -334,6 +348,63 @@ void test_many(void) free(msgs); } +void test_ibufs_size(void) +{ + int CONNS = 4; + knot_xdp_msg_t msgs[CONNS]; + tcp_relay_dynarray_t relays = { 0 }; + + // just open connections + for (int i = 0; i < CONNS; i++) { + prepare_msg(&msgs[i], KNOT_XDP_MSG_SYN, i + 2000, 1); + } + int ret = knot_xdp_tcp_relay(test_sock, msgs, CONNS, test_table, NULL, &relays, NULL); + is_int(KNOT_EOK, ret, "ibufs: open 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; + } + + is_int(0, test_table->inbufs_total, "inbufs: initial total zero"); + + // first connection will start a fragment buf then finish it + fix_seqack(&msgs[0]); + prepare_data(&msgs[0], "\x00\x0a""lorem", 7); + ret = knot_xdp_tcp_relay(test_sock, &msgs[0], 1, test_table, NULL, &relays, NULL); + is_int(KNOT_EOK, ret, "ibufs: must be OK"); + check_sent(1, 0, 0, 0); + is_int(7, test_table->inbufs_total, "inbufs: first inbuf"); + knot_xdp_tcp_relay_free(&relays); + + // other connection will just store fragments + fix_seqacks(msgs, CONNS); + prepare_data(&msgs[0], "ipsum", 5); + 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_xdp_tcp_relay(test_sock, msgs, CONNS, test_table, NULL, &relays, NULL); + is_int(KNOT_EOK, ret, "inbufs: relay 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, tcp_relay_dynarray_arr(&relays)[0].data.iov_len, "inbufs: data length"); + knot_xdp_tcp_relay_free(&relays); + + // now free some + uint32_t reset_count = 0; + ret = knot_xdp_tcp_timeout(test_table, test_sock, UINT32_MAX, UINT32_MAX, UINT32_MAX, 0, 8, &reset_count); + is_int(KNOT_EOK, ret, "inbufs: timeout OK"); + check_sent(0, 2, 0, 0); + is_int(2, reset_count, "inbufs: reset count"); + is_int(7, test_table->inbufs_total, "inbufs: final state"); + ok(NULL != knot_tcp_table_find(test_table, &msgs[0]), "inbufs: first conn survived"); + ok(NULL == knot_tcp_table_find(test_table, &msgs[1]), "inbufs: second conn not survived"); + ok(NULL == knot_tcp_table_find(test_table, &msgs[2]), "inbufs: third conn not survived"); + ok(NULL != knot_tcp_table_find(test_table, &msgs[3]), "inbufs: fourth conn survived"); + + clean_table(); +} + int main(int argc, char *argv[]) { UNUSED(argc); @@ -353,6 +424,8 @@ int main(int argc, char *argv[]) test_data_fragments(); test_close(); + test_ibufs_size(); + knot_xdp_deinit(test_sock); ret = knot_xdp_init_mock(&test_sock, mock_send_nocheck); assert(ret == KNOT_EOK);