From 3a4daf6e77712cf808b43f0956b6a051c8357d6e Mon Sep 17 00:00:00 2001 From: evgeny Date: Fri, 25 Sep 2026 19:13:38 +0300 Subject: [PATCH] =?UTF-8?q?etcp:=20=D1=81=D1=82=D1=80=D0=BE=D0=B3=D0=B0?= =?UTF-8?q?=D1=8F=20=D0=BF=D0=BE=D1=81=D0=BB=D0=B5=D0=B4=D0=BE=D0=B2=D0=B0?= =?UTF-8?q?=D1=82=D0=B5=D0=BB=D1=8C=D0=BD=D0=BE=D1=81=D1=82=D1=8C=20=D0=BA?= =?UTF-8?q?=D0=BE=D0=BB=D0=BB=D0=B1=D1=8D=D0=BA=D0=BE=D0=B2=20=D0=BF=D1=80?= =?UTF-8?q?=D0=B8=20=D0=B7=D0=B0=D0=BA=D1=80=D1=8B=D1=82=D0=B8=D0=B8=20(NE?= =?UTF-8?q?W=E2=86=92UP=E2=86=94DOWN=E2=86=92DELETE)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - close: финальный per-connection DOWN только если был UP, до state=2 - etcp_cbk_fire / etcp_fire_conn_status / etcp_link_ready_callback / etcp_on_up: guard state==2 - документация контракта в etcp_api.h / etcp.h --- src/transport_layer/etcp.c | 13 +++++++++++-- src/transport_layer/etcp.h | 15 +++++++++++++++ src/transport_layer/etcp_api.h | 20 ++++++++++++++++---- 3 files changed, 42 insertions(+), 6 deletions(-) diff --git a/src/transport_layer/etcp.c b/src/transport_layer/etcp.c index 25571c28..5e785232 100644 --- a/src/transport_layer/etcp.c +++ b/src/transport_layer/etcp.c @@ -291,6 +291,7 @@ struct ETCP_CONN* etcp_connection_create(struct UTUN_INSTANCE* instance, char* n void etcp_fire_conn_status(struct ETCP_CONN* etcp, int status) { if (!etcp || !etcp->instance) return; + if (etcp->state == 2 && status != ETCP_CONN_STATUS_DELETE) return; // при state==2 допустим только DELETE static const char* names[] = { "NEW", "UP", "DOWN", "DELETE" }; DEBUG_INFO(DEBUG_CATEGORY_GENERAL, "[%s] Connection status changed to %s", etcp->log_name, (status >= 0 && status < (int)(sizeof(names)/sizeof(names[0]))) ? names[status] : "?"); @@ -300,6 +301,7 @@ void etcp_fire_conn_status(struct ETCP_CONN* etcp, int status) { void etcp_cbk_fire(struct ETCP_CONN* conn, int event) { if (!conn) return; + if (conn->state == 2) return; // после DELETE per-connection события не идут static const char* names[] = { "INIT", "REINIT", "UP", "DOWN", "NODE_CHANGED" }; int idx = 0, e = event; while (e >>= 1) idx++; DEBUG_INFO(DEBUG_CATEGORY_ETCP, "[%s] callback event: %s", conn->log_name, (idx >= 0 && idx < (int)(sizeof(names)/sizeof(names[0]))) ? names[idx] : "?"); @@ -310,6 +312,7 @@ void etcp_cbk_fire(struct ETCP_CONN* conn, int event) { } static void etcp_on_up(struct ETCP_CONN* etcp) { + if (etcp->state == 2) return; // defensive: после DELETE UP не стреляет int total_links = 0; for (struct ETCP_LINK* l = etcp->links; l; l = l->next) total_links++; uint64_t elapsed_ms = (get_time_tb() - etcp->setup_start_tb) / 10; struct strbuf sb = strbuf_new(); int any = 0; @@ -400,8 +403,14 @@ void etcp_connection_close(struct ETCP_CONN* etcp) { // === PHASE 1: detach from external world === + /* Финальный DOWN если соединение ещё UP (переход UP→DOWN до state=2): + строгая последовательность ... → UP → DOWN → DELETE, без дублей. */ + if (etcp->links_up != 0) { + etcp->links_up = 0; + etcp_cbk_fire(etcp, ETCP_CBK_EVENT_DOWN); + } + etcp->state = 2; // deleted — blocks ref_take and etcp_send immediately - if (etcp->links_up != 0) etcp->links_up = 0; if (etcp->peer_sleep_phase) { etcp->peer_sleep_phase = 0; @@ -411,7 +420,6 @@ void etcp_connection_close(struct ETCP_CONN* etcp) { etcp_fire_conn_status(etcp, ETCP_CONN_STATUS_DELETE); DEBUG_DEBUG(DEBUG_CATEGORY_CONNECTION, "[%s] close: state=2 conn=%p peer=%016llx (DELETE fired)", etcp->log_name, (void*)etcp, (unsigned long long)etcp->peer_node_id); - etcp_cbk_fire(etcp, ETCP_CBK_EVENT_DOWN); // Cancel active timers if (etcp->retrans_timer) { uasync_cancel_timeout(etcp->instance->ua, etcp->retrans_timer); etcp->retrans_timer = NULL; } @@ -1066,6 +1074,7 @@ static void ack_response_timer_cb(void* arg) {// проверяем неотпр static void etcp_link_ready_callback(struct ETCP_CONN* etcp) { DEBUG_TRACE(DEBUG_CATEGORY_ETCP, ""); if (!etcp) return; + if (etcp->state == 2) return; // удалённый conn не поднимается if (etcp->tx_state==0) { DEBUG_WARN(DEBUG_CATEGORY_ETCP, "ETCP not ready, skip link state change"); diff --git a/src/transport_layer/etcp.h b/src/transport_layer/etcp.h index d8f4c517..638329bc 100644 --- a/src/transport_layer/etcp.h +++ b/src/transport_layer/etcp.h @@ -230,6 +230,21 @@ struct ETCP_CONN { // Functions struct ETCP_CONN* etcp_connection_create(struct UTUN_INSTANCE* instance, char* name); + +/** + * @brief Закрыть соединение: фаза-1 detach + отложенная фаза-2 очистка. + * + * Порядок коллбэков при закрытии (строгая последовательность): + * 1) если соединение ещё UP (links_up != 0) — финальный per-connection DOWN + * (ETCP_CBK_EVENT_DOWN), затем links_up = 0; + * 2) state = 2; + * 3) conn-status DELETE (ETCP_CONN_STATUS_DELETE) — единственный статус при + * state==2 (по нему подписчики снимают conn из своих списков); + * 4) cleanup: таймеры, routing, линки, очередь, deferred u_free. + * + * После state==2 per-connection события (etcp_cbk_fire) и conn-status + * NEW/UP/DOWN не вызываются. + */ void etcp_connection_close(struct ETCP_CONN* etcp); void etcp_conn_queue_set_ready(struct ETCP_CONN* conn); // move pending->connections, fire ready cbks diff --git a/src/transport_layer/etcp_api.h b/src/transport_layer/etcp_api.h index 448ce426..0ba54eaf 100644 --- a/src/transport_layer/etcp_api.h +++ b/src/transport_layer/etcp_api.h @@ -85,7 +85,12 @@ extern "C" { #define ETCP_RT_ID_DM_MAILBOX 0x35 // dm_mailbox — offline-хранение #define ETCP_RT_ID_CALL 0x36 // call — P2P аудио-звонок -// Connection status events (instance-level callback) +// Connection status events (instance-level callback). +// Строгая последовательность: NEW -> UP <-> DOWN -> DELETE. +// NEW — один раз при создании соединения. +// UP — переход links_up 0→1 (UP после UP не вызывается). +// DOWN — переход links_up 1→0 (DOWN после DOWN не вызывается). +// DELETE — один раз при закрытии, финальный; единственный статус, допустимый при state==2. #define ETCP_CONN_STATUS_NEW 0 // соединение создано #define ETCP_CONN_STATUS_UP 1 // соединение поднялось #define ETCP_CONN_STATUS_DOWN 2 // соединение упало @@ -127,7 +132,10 @@ struct ETCP_CONN; struct UTUN_INSTANCE; struct TOPO_GROUP_NODE; -// Per-connection callback event types (bitmask) +// Per-connection callback event types (bitmask). +// Вызываются только при state != 2 (после DELETE события не идут). +// UP/DOWN зеркалят conn-status UP/DOWN; при закрытии (если соединение было UP) +// отправляется один финальный DOWN до state=2. #define ETCP_CBK_EVENT_INIT (1 << 0) #define ETCP_CBK_EVENT_REINIT (1 << 1) #define ETCP_CBK_EVENT_UP (1 << 2) @@ -149,7 +157,9 @@ struct etcp_cbk_entry { * @param event Битовая маска события (ETCP_CBK_EVENT_*) * * @note Вызываются все коллбэки соединения, у которых event_mask - * пересекается с переданным event + * пересекается с переданным event. + * @note Не вызывается при state==2 (соединение удалено) — после DELETE + * per-connection события не рассылаются. */ void etcp_cbk_fire(struct ETCP_CONN* conn, int event); @@ -329,7 +339,9 @@ int etcp_unbind(struct UTUN_INSTANCE* inst, uint8_t id); * @param fn Коллбэк (сигнатура etcp_conn_status_fn) * @param arg Пользовательский аргумент * - * @note status = ETCP_CONN_STATUS_NEW / UP / DOWN / DELETE + * @note status = ETCP_CONN_STATUS_NEW / UP / DOWN / DELETE. + * Строгая последовательность NEW -> UP <-> DOWN -> DELETE; DELETE — + * единственный статус, отправляемый при state==2 (при закрытии). */ void etcp_add_conn_status_cbk(struct UTUN_INSTANCE* inst, etcp_conn_status_fn fn, void* arg);