Verify TCP retry timing, recovery and cancellation end to end

This commit is contained in:
evgeny committed 2026-10-23 21:58:27 +02:00
1 parent f5e4c78b8c
commit 03d69cb772
2 files changed
+54 -1

No files matched your search

+7 -1
View File
@@ -364,7 +364,7 @@ struct ETCP_LINK {
struct reality_client_config reality; // reality-параметры пира (для исходящего TCP)
struct stcp_link *tcp_link; // STCP линк (для TCP)
void *tcp_reconnect_timer; // таймер реконнекта (TCP клиент)
uint32_t tcp_reconnect_delay_ms; // задержка реконнекта (экспоненциальный backoff)
uint32_t tcp_reconnect_delay_ms; // 0 после READY; ошибки: 1000, 2000, 4000, 8000, 16000, затем 30000 мс
};
// ── Внутренняя отправка пакетов ──
@@ -414,6 +414,9 @@ struct ETCP_LINK* etcp_link_new(struct ETCP_CONN* etcp, struct ETCP_SOCKET* conn
// Минимум выбирает BBR; ноль отклоняется с логом без изменения окна. Неверный link игнорируется.
void etcp_link_update_inflight_lim(struct ETCP_LINK* link, uint32_t new_lim);
/* Снимает линк из списков, отменяет таймеры/STCP и освобождает память до возврата.
* Из callback его закрытия повторный close допустим; после возврата handle недействителен.
* Callback может закрыть ETCP_CONN или соседний линк: вызывающий не должен хранить next через уведомление. */
void etcp_link_close(struct ETCP_LINK* link);
//int etcp_input_cbk(struct packet_buffer* pkt, struct ETCP_SOCKET* conn);// получает расшифрованный пакет
@@ -445,10 +448,13 @@ void etcp_link_enter_init(struct ETCP_LINK *link);
void etcp_link_enter_reinit(struct ETCP_LINK *link);
/* Публикует готовность STCP и запускает согласование ETCP-сессии. Close из callback прекращает переход. */
void etcp_link_enter_ready_tcp(struct ETCP_LINK *link);
void etcp_tcp_link_start_connect(struct ETCP_LINK *link, struct sockaddr_storage *addr, uint16_t port);
/* Только исходящий TCP: закрывает текущий адаптер и ставит один backoff-таймер; повторный вызов его не сдвигает.
* Ошибка таймера закрывает ETCP_CONN. Close линка/соединения отменяет попытку; READY сбрасывает backoff. */
void etcp_tcp_link_start_reconnect(struct ETCP_LINK *link);
/* Принять plaintext установленного линка: session/формат проверяются до соответствующей обработки RX.
+47
View File
@@ -360,6 +360,52 @@ static void callback_close_case(int reality, int side, unsigned action, unsigned
reality, side, action, on_down);
}
static void reconnect_case(void) {
size_t allocated = u_get_allocated_count();
memset(endpoints, 0, sizeof(endpoints)); phase = 0; failed = 0;
ua = uasync_create(); CHECK(ua);
start_endpoint(0, 0); start_endpoint(1, 0);
struct TOPO_NODE *peer = import_peer(0);
CHECK(node_conn_direct_open_node(endpoints[0].inst, peer->node_id, NULL, NULL,
&endpoints[0].owner, peer, NULL) >= 0);
wait_pair(); exchange_messages();
struct ETCP_LINK *path = endpoints[0].conn->links;
CHECK(path && !path->is_server && !path->next && path->tcp_reconnect_delay_ms == 0);
stop_endpoint(1);
static const unsigned delays[] = {1000, 2000, 4000, 8000, 16000, 30000, 30000};
for (unsigned i = 0; i < sizeof(delays) / sizeof(delays[0]); i++) {
unsigned previous = i ? delays[i-1] : 0;
uint64_t started = get_time_tb(), deadline = started + (previous + 2000) * 10;
while ((!path->tcp_reconnect_timer || path->tcp_reconnect_delay_ms != delays[i] ||
(i && get_time_tb() - started < previous * 10)) && get_time_tb() < deadline)
test_clock_poll(ua, 100);
CHECK(path->tcp_reconnect_timer && path->tcp_reconnect_delay_ms == delays[i] && !path->tcp_link);
uint64_t elapsed = get_time_tb() - started;
if (i) CHECK(elapsed >= previous * 10 && elapsed < (previous + 1000) * 10);
void *timer = path->tcp_reconnect_timer;
etcp_tcp_link_start_reconnect(path);
CHECK(path->tcp_reconnect_timer == timer && path->tcp_reconnect_delay_ms == delays[i]);
DEBUG_INFO(DEBUG_CATEGORY_DEBUG, "PASS TCP retry stage=%u delay_ms=%u elapsed_tb=%llu single_timer=1",
i, delays[i], (unsigned long long)elapsed);
}
start_endpoint(1, 0);
uint64_t deadline = get_time_tb() + 350000;
while (!pair_ready() && get_time_tb() < deadline) test_clock_poll(ua, 100);
CHECK(pair_ready() && path->tcp_reconnect_delay_ms == 0 && !path->tcp_reconnect_timer);
exchange_messages();
stop_endpoint(1);
deadline = get_time_tb() + 20000;
while (!path->tcp_reconnect_timer && get_time_tb() < deadline) test_clock_poll(ua, 100);
CHECK(path->tcp_reconnect_timer && path->tcp_reconnect_delay_ms == 1000);
etcp_link_close(path); /* Отменённая попытка не должна ожить после истечения таймера. */
test_clock_run_for(ua, 20000);
CHECK(!endpoints[0].conn->links);
stop_endpoint(0); uasync_drain_immediate(ua); uasync_poll(ua, 0);
CHECK(ua->timer_alloc_count == ua->timer_free_count && ua->socket_alloc_count == ua->socket_free_count);
uasync_destroy(ua, 0); CHECK(u_get_allocated_count() == allocated);
printf("PASS TCP retry: capped backoff, recovery, reset after READY and cancellation\n");
}
int main(void) {
debug_config_init(); debug_set_level(DEBUG_LEVEL_WARN);
if (getenv("UTUN_TEST_DEBUG")) {
@@ -377,6 +423,7 @@ int main(void) {
if (action != 3) callback_close_case(reality, side, action, 1);
}
}
reconnect_case();
test_clock_finish();
return 0;
}