From 0bbd4d6c34141735d0e4effb6f86facfbec7712f Mon Sep 17 00:00:00 2001 From: evgeny Date: Wed, 14 Oct 2026 18:34:33 +0200 Subject: [PATCH] Verify router ACK accounting across retries and epoch resets --- src/routing_layer/route_tx_stats.h | 8 ++++++ tests/test_etcp_router_unit.c | 39 ++++++++++++++++++++++++++++++ 2 files changed, 47 insertions(+) diff --git a/src/routing_layer/route_tx_stats.h b/src/routing_layer/route_tx_stats.h index c7336117..157d8657 100644 --- a/src/routing_layer/route_tx_stats.h +++ b/src/routing_layer/route_tx_stats.h @@ -6,6 +6,10 @@ #include +#ifdef __cplusplus +extern "C" { +#endif + #define ROUTE_TX_SAMPLE_US 50000u #define ROUTE_TX_WINDOW_US 500000u #define ROUTE_TX_BUCKETS 11 @@ -22,4 +26,8 @@ int route_tx_meter_ack(struct route_tx_meter *meter, uint64_t now_us, uint64_t b /* Неполное окно после reset возвращает valid=0; текущая незавершённая ячейка исключается. */ int route_tx_meter_window(const struct route_tx_meter *meter, uint64_t now_us, struct route_tx_window *out); +#ifdef __cplusplus +} +#endif + #endif diff --git a/tests/test_etcp_router_unit.c b/tests/test_etcp_router_unit.c index 54d6916a..af696ef4 100644 --- a/tests/test_etcp_router_unit.c +++ b/tests/test_etcp_router_unit.c @@ -604,6 +604,44 @@ static int test_ack_receive(void) { return 0; } +/* Реальные ACK проходят проверку эпохи/диапазона; длины payload не равны размерам криптограмм. */ +static int test_delivery_stats(void) { + TEST("delivery stats: payload bytes, duplicate ACK, cancellation and isolated services"); + struct UASYNC* ua; struct UTUN_INSTANCE inst; etcp_recv_fn recv_cb; + SETUP(); + establish(recv_cb, &inst, TEST_REMOTE_NODE, TEST_SVC_ID, TEST_REMOTE_NODE); + struct ETCP_ROUTER_CONN *c = etcp_router_conn_get(&inst, TOPO_GROUP_UTUN, TEST_REMOTE_NODE, TEST_SVC_ID); + TESTASSERT(c); + for (unsigned i = 0; i < 3; i++) { + struct ROUTER_INFLIGHT *inf = u_calloc(1, sizeof(*inf)); TESTASSERT(inf); + inf->seq = i; inf->ll.size = 4; inf->ll.len = 100 + i * 10; inf->send_count = 3; + inf->dgram_len = inf->ll.len + 128; + inf->dgram = u_calloc(1, inf->dgram_len); TESTASSERT(inf->dgram); + TESTASSERT(queue_data_put_with_index(c->inflight_q, &inf->ll) == 0); + } + c->tx_seq = c->tx_sent = 3; + struct ETCP_ROUTER_TX_STATS stats; + TESTASSERT(etcp_router_tx_stats(&inst, TOPO_GROUP_UTUN, TEST_REMOTE_NODE, TEST_SVC_ID, get_time_us(), &stats) == 0); + TESTASSERT(stats.inflight_bytes == 330 && stats.inflight_packets == 3 && stats.acked_bytes == 0); + inject(recv_cb, &inst, TEST_REMOTE_NODE, TEST_SVC_ID, 2, NULL, 0, 1, 0); + inject(recv_cb, &inst, TEST_REMOTE_NODE, TEST_SVC_ID, 2, NULL, 0, 1, 0); + TESTASSERT(etcp_router_tx_stats(&inst, TOPO_GROUP_UTUN, TEST_REMOTE_NODE, TEST_SVC_ID, get_time_us(), &stats) == 0); + TESTASSERT(stats.inflight_bytes == 120 && stats.acked_bytes == 210 && stats.acked_packets == 2); + TESTASSERT(stats.inflight_packets == 1 && !stats.window.valid); + uint64_t old_epoch = stats.local_epoch; + struct ETCP_ROUTER_CONN *other = etcp_router_conn_get(&inst, TOPO_GROUP_UTUN, TEST_REMOTE_NODE, TEST_SVC_ID2); + TESTASSERT(other); + uint8_t body[23] = {0}; + TESTASSERT(etcp_router_conn_send(other, body, sizeof(body), 0) == 0); + TESTASSERT(etcp_router_tx_stats(&inst, TOPO_GROUP_UTUN, TEST_REMOTE_NODE, TEST_SVC_ID2, get_time_us(), &stats) == 0); + TESTASSERT(stats.queued_bytes == sizeof(body) && stats.queued_packets == 1 && !stats.acked_bytes); + etcp_router_conn_restart(&inst, TOPO_GROUP_UTUN, TEST_REMOTE_NODE, TEST_SVC_ID); + TESTASSERT(etcp_router_tx_stats(&inst, TOPO_GROUP_UTUN, TEST_REMOTE_NODE, TEST_SVC_ID, get_time_us(), &stats) == 0); + TESTASSERT(stats.local_epoch != old_epoch && !stats.acked_bytes && !stats.inflight_bytes && !stats.window.valid); + TESTASSERT(etcp_router_tx_stats(&inst, TOPO_GROUP_UTUN, TEST_REMOTE_NODE2, TEST_SVC_ID, get_time_us(), &stats) == 1); + TEARDOWN(); PASS(); return 0; +} + static int test_ack_timer(void) { TEST("ACK timer scheduled on data receive"); struct UASYNC* ua; struct UTUN_INSTANCE inst; etcp_recv_fn recv_cb; @@ -1343,6 +1381,7 @@ int main(void) { test_circular_wrap(); test_data_integrity(); test_ack_receive(); + test_delivery_stats(); test_ack_timer(); test_send_q(); test_loopback();