diff --git a/src/routing_layer/etcp_router.c b/src/routing_layer/etcp_router.c index 4a4f852c..267049de 100644 --- a/src/routing_layer/etcp_router.c +++ b/src/routing_layer/etcp_router.c @@ -1192,7 +1192,8 @@ static void router_handle_ack(struct UTUN_INSTANCE* inst, struct ETCP_ROUTER_CON uint16_t new_rtt = get_current_timestamp() - ack_ts; int d_rtt = (int)new_rtt - (int)rconn->rtt; if (d_rtt < 0) d_rtt = -d_rtt; - rconn->rtt_jitter += (int32_t)(((int64_t)d_rtt * 65536 - rconn->rtt_jitter) / 32); + int64_t jitter = rconn->rtt_jitter; + rconn->rtt_jitter = (uint32_t)(jitter + ((int64_t)d_rtt * 65536 - jitter) / 32); rconn->rtt = new_rtt; } rconn->tx_acked = seq; diff --git a/src/routing_layer/etcp_router.h b/src/routing_layer/etcp_router.h index b2bf3d34..80be1a3e 100644 --- a/src/routing_layer/etcp_router.h +++ b/src/routing_layer/etcp_router.h @@ -109,7 +109,7 @@ struct ETCP_ROUTER_CONN { uint8_t svc_id; // = data[16] uint16_t last_dgram_ts; // последний timestamp датаграммы (rx или tx, timebase 0.1ms) uint16_t rtt; // измеренный RTT (0.1ms) - int32_t rtt_jitter; // EMA jitter, fixed-point *65536 (как в ETCP) + uint32_t rtt_jitter; // EWMA |ΔRTT|, беззнаковый fixed-point *65536; монитор показывает удвоенный EWMA uint16_t max_inflight; // настраиваемый рабочий максимум (default=ROUTER_MAX_INFLIGHT) uint16_t inflight_limit; // текущий эффективный лимит (=max_inflight или MINRTT_PROBE_MAX_INFLIGHT) uint16_t minrtt; // скользящее среднее minRTT (0.1ms), default=100 (10ms) diff --git a/tests/test_etcp_router_unit.c b/tests/test_etcp_router_unit.c index b08076ef..01902c34 100644 --- a/tests/test_etcp_router_unit.c +++ b/tests/test_etcp_router_unit.c @@ -642,6 +642,28 @@ static int test_delivery_stats(void) { TEARDOWN(); PASS(); return 0; } +/* Высокий RTT не должен переполнять fixed-point EWMA jitter в отрицательное значение. */ +static int test_jitter_range(void) { + TEST("ACK jitter keeps the full unsigned 16.16 range"); + struct UASYNC* ua; struct UTUN_INSTANCE inst; etcp_recv_fn recv_cb; + SETUP(); debug_set_category_level(DEBUG_CATEGORY_ETCPROUTE, DEBUG_LEVEL_INFO); + 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); + c->tx_seq = c->tx_sent = 1; c->rtt = 0; c->rtt_jitter = INT32_MAX; + struct SVC_ROUTE_HDR ack = {.cmd = ETCP_ID_SVC_ROUTE, .group_id = TOPO_GROUP_UTUN, + .src_node_id = TEST_REMOTE_NODE, .dst_node_id = inst.node_id, .svc_id = TEST_SVC_ID, .seq = 1, + .reset_id = c->peer_reset_id, .peer_reset_id = c->reset_id, .timestamp = (uint16_t)(get_current_timestamp() - 60000)}; + if (!ack.timestamp) ack.timestamp = 1; + struct ll_entry* e = ll_alloc_lldgram(sizeof(ack)); TESTASSERT(e); + memcpy(e->dgram, &ack, sizeof(ack)); e->len = sizeof(ack); recv_cb(&fake_conn, e); + uint64_t expected = INT32_MAX + (((int64_t)c->rtt * 65536 - INT32_MAX) / 32); + uint64_t measured = (uint64_t)c->rtt_jitter; + DEBUG_INFO(DEBUG_CATEGORY_ETCPROUTE, "[regression] RTT=%u jitter=%llu expected=%llu", c->rtt, + (unsigned long long)measured, (unsigned long long)expected); + TEARDOWN(); + TESTASSERT(measured == expected && measured > INT32_MAX); 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; @@ -1574,6 +1596,7 @@ int main(void) { test_data_integrity(); test_ack_receive(); test_delivery_stats(); + test_jitter_range(); test_ack_timer(); test_send_q(); test_loopback();