From 24c64967fdc53d9459f73f7a6fa17857b9ee106c Mon Sep 17 00:00:00 2001 From: evgeny Date: Mon, 12 Oct 2026 00:57:30 +0200 Subject: [PATCH] Propagate remote UDP link DOWN to the connection expiry timer --- doc/service_lifecycle.md | 5 +++-- src/transport_layer/etcp_connections.c | 9 +++++++++ tests/test_etcp_server_lifecycle.c | 14 ++++++++++++++ 3 files changed, 26 insertions(+), 2 deletions(-) diff --git a/doc/service_lifecycle.md b/doc/service_lifecycle.md index ae94ce4f..7f8e089e 100644 --- a/doc/service_lifecycle.md +++ b/doc/service_lifecycle.md @@ -39,7 +39,7 @@ SQLite открывается ядром один раз: `db_path/chats.db`, л не задан. Остановка чата сохраняет каналы и сообщения в этой БД. В варианте `:memory:` данные сохраняются только до уничтожения ядра. -Общий транспорт живёт, пока есть его владельцы. Закрытие запроса одной группы +Работающий общий транспорт сохраняется, пока он нужен хотя бы одному из пиров. Закрытие запроса одной группы не разрывает соединение, нужное другой группе. При остановке группы отменяются её планировщики, запросы, таймеры и ожидания очередей; отправляется LEAVE, закрываются логические каналы маршрутизатора и удаляется её ожидающий транзит @@ -62,7 +62,8 @@ KEEP_ALIVE полностью отменяет FIN_WAIT. Ожидание CLOSE Контроль RX независим от отправки keepalive. Объявленный сон не отключает проверку жизни: срок от последнего RX равен максимуму согласованного keepalive timeout и объявленного остатка ACTIVE+SLEEP с запасом normal timeout. Фазовые таймеры и наши -отправки срок жизни пира не продлевают. После истечения последнего линка транспорт +отправки срок жизни пира не продлевают. Пробуждение сразу сокращает deadline до normal timeout. +После истечения последнего линка транспорт становится DOWN, затем удаляется через 30 секунд. `test_etcp_server_lifecycle` проверяет сроки, несколько путей, восстановление, владение, сон и CLOSE/KEEP_ALIVE с управляемыми часами и контролем освобождения памяти/таймеров. diff --git a/src/transport_layer/etcp_connections.c b/src/transport_layer/etcp_connections.c index cd3a35b7..95b366d6 100644 --- a/src/transport_layer/etcp_connections.c +++ b/src/transport_layer/etcp_connections.c @@ -2610,6 +2610,15 @@ int etcp_packet_decrypted(struct ETCP_SOCKET* e_sock, struct ETCP_DGRAM* pkt, link->last_recv_timestamp=pkt->timestamp; link->last_recv_updated=1; link->total_decrypted += pkt->data_len; + if (was_up && !link->link_status) { + struct ETCP_CONN* conn = link->etcp; + DEBUG_INFO(DEBUG_CATEGORY_CONNECTION, "[%s] peer reports link DOWN id=%u remote_ka=%u", conn->log_name, + link->local_link_id, link->remote_keepalive); + etcp_on_link_down(conn, link); + int alive = 0; + for (struct ETCP_LINK* current = conn->links; current; current = current->next) if (current == link) { alive = 1; break; } + if (conn->close_requested || !alive) { memory_pool_free(conn->instance->pkt_pool, pkt); return 0; } + } if (pkt->data[0] != ETCP_KEEPALIVE) etcp_keepalive_watch_rx(link); uint8_t pkt_code = pkt->data[0]; diff --git a/tests/test_etcp_server_lifecycle.c b/tests/test_etcp_server_lifecycle.c index b4659d5b..9dac3a89 100644 --- a/tests/test_etcp_server_lifecycle.c +++ b/tests/test_etcp_server_lifecycle.c @@ -158,6 +158,19 @@ static void check_sleep(void) { teardown(); DEBUG_INFO(DEBUG_CATEGORY_DEBUG, "PASS sleeping peer deadline, FIN_WAIT and disappearance"); } +/* UDP flag_up=0 переводит весь транспорт в DOWN, обратный переход отменяет удаление. */ +static void check_remote_down(void) { + setup(); struct ETCP_CONN* conn = ready_conn(0); struct ETCP_LINK* link = conn->links; + for (int up = 0; up <= 1; up++) { + struct ETCP_DGRAM* pkt = memory_pool_alloc(inst.pkt_pool); CHECK(pkt); + memset(pkt, 0, sizeof(*pkt) + 4); pkt->flag_up = up; + pkt->data[0] = ETCP_KEEPALIVE; pkt->data[2] = 200; + CHECK(etcp_packet_decrypted(&sock, pkt, link, 7) == 0); + CHECK(conn->links_up == up && !!conn->down_timer == !up); + } + teardown(); DEBUG_INFO(DEBUG_CATEGORY_DEBUG, "PASS remote DOWN flag and recovered UDP transport"); +} + /* Пробуждение сокращает RX timeout сразу, не оставляя таймер прежнего длинного сна. */ static void check_wake_deadline(void) { setup(); struct ETCP_CONN* conn = ready_conn(1); struct ETCP_LINK* link = conn->links; @@ -205,6 +218,7 @@ int main(void) { check_silent_peer(1); CHECK(u_get_allocated_count() == baseline); check_sleep(); CHECK(u_get_allocated_count() == baseline); check_wake_deadline(); CHECK(u_get_allocated_count() == baseline); + check_remote_down(); CHECK(u_get_allocated_count() == baseline); check_graceful(); CHECK(u_get_allocated_count() == baseline); test_clock_finish(); return 0; }