Browse Source

Verify router ACK accounting across retries and epoch resets

master
evgeny 4 days ago
parent
commit
0bbd4d6c34
  1. 8
      src/routing_layer/route_tx_stats.h
  2. 39
      tests/test_etcp_router_unit.c

8
src/routing_layer/route_tx_stats.h

@ -6,6 +6,10 @@
#include <stdint.h>
#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

39
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();

Loading…
Cancel
Save