From 33fd5bc123d81c24b31eeb1cce3c3c00cdabeef7 Mon Sep 17 00:00:00 2001 From: Evgeny Date: Wed, 22 Apr 2026 23:53:25 +0300 Subject: [PATCH] ETCP: add session_id to prevent false reinit on new link addition - Added 32-bit session_id to ETCP_CONN struct (random per client) - Session_id transmitted in INIT_REQUEST/RESPONSE after node_id - Server: skip reinit if session_id matches existing connection - Server: send INIT_RESPONSE (0x03) only when reinit actually done - Server: send INIT_RESPONSE_NOINIT (0x05) for same session - Client: ignore INIT_RESPONSE with wrong session_id - Fixed tx_state initialization in etcp_conn_ready for noinit case - All 26 tests passing --- src/etcp.c | 8 ++++-- src/etcp.h | 1 + src/etcp_connections.c | 62 ++++++++++++++++++++++++++++++++++-------- src/utun_instance.c | 10 +++++-- 4 files changed, 65 insertions(+), 16 deletions(-) diff --git a/src/etcp.c b/src/etcp.c index ce3daed4..6968e942 100644 --- a/src/etcp.c +++ b/src/etcp.c @@ -360,10 +360,14 @@ void etcp_conn_reinit(struct ETCP_CONN* etcp) {// Если сбой в обме // внутренняя функция. Вызывается один раз когда первый линк готов. void etcp_conn_ready(struct ETCP_CONN* conn) { if (!conn) return; - + conn->initialized = 1; + // Если tx_state не установлен (первое подключение без reinit), установим его + if (conn->tx_state == 0) { + conn->tx_state = ETCP_TX_STATE_DATA_WAIT; + } DEBUG_INFO(DEBUG_CATEGORY_ETCP, "[%s] Connection ready", conn->log_name); - + // Вызываем callback если установлен if (conn->ready_cbk) conn->ready_cbk(conn, conn->ready_arg); } diff --git a/src/etcp.h b/src/etcp.h index cd9743e3..8c8379a5 100644 --- a/src/etcp.h +++ b/src/etcp.h @@ -150,6 +150,7 @@ struct ETCP_CONN { uint8_t routing_exchange_active; // 0 - не активен, 1 - надо инициировать обмен маршрутами (клиент), 2 - обмен маршрутами активен uint8_t got_initial_pkt; // uint8_t initialized; // 0 - только созданный ETCP, 1 - хотя бы один линк проинициалзирован (обмен ключами произведен) + uint32_t session_id; // случайный ID сессии (генерируется клиентом) для защиты от ложного reinit uint8_t tx_state; // 0 - n/a, 1 - data_wait (queues empty), 2 - link_wait (link busy) uint8_t links_up; // 0 - канал не готов для передачи, 1 - канал готов для передачи (хотя бы один линк не down) diff --git a/src/etcp_connections.c b/src/etcp_connections.c index a394077c..e490c0d1 100644 --- a/src/etcp_connections.c +++ b/src/etcp_connections.c @@ -80,6 +80,13 @@ static void etcp_link_send_init(struct ETCP_LINK* link, uint8_t reset) { dgram->data[offset++] = (node_id >> 16) & 0xFF; dgram->data[offset++] = (node_id >> 8) & 0xFF; dgram->data[offset++] = node_id & 0xFF; + + // session_id (4 bytes) - для защиты от ложного reinit + uint32_t session_id = link->etcp->session_id; + dgram->data[offset++] = (session_id >> 24) & 0xFF; + dgram->data[offset++] = (session_id >> 16) & 0xFF; + dgram->data[offset++] = (session_id >> 8) & 0xFF; + dgram->data[offset++] = session_id & 0xFF; dgram->data[offset++] = (link->mtu_local >> 8) & 0xFF; dgram->data[offset++] = link->mtu_local & 0xFF; @@ -1200,6 +1207,7 @@ void etcp_connections_read_callback_socket(socket_t sock, void* arg) { struct { uint8_t code; uint8_t id[8]; + uint8_t session_id[4]; uint8_t mtu[2]; uint8_t keepalive[2]; uint8_t recovery[2]; @@ -1283,6 +1291,9 @@ void etcp_connections_read_callback_socket(socket_t sock, void* arg) { goto ec_fr; }// не init + uint32_t session_id = be32toh(*(uint32_t*)ack_hdr->session_id); + DEBUG_DEBUG(DEBUG_CATEGORY_CONNECTION, "INIT request session_id=%08x", session_id); + struct ETCP_CONN* conn=e_sock->instance->connections; while (conn) {// ищем есть ли подключение к этому пиру if (conn->peer_node_id==peer_id) break; @@ -1296,6 +1307,7 @@ void etcp_connections_read_callback_socket(socket_t sock, void* arg) { if (!conn) { errorcode=55; DEBUG_ERROR(DEBUG_CATEGORY_CONNECTION, "failed to create connection"); goto ec_fr; } memcpy(&conn->crypto_ctx, &sc, sizeof(sc)); conn->peer_node_id=peer_id; + conn->session_id = session_id; etcp_update_log_name(conn); DEBUG_INFO(DEBUG_CATEGORY_GENERAL, "New connection received on socket %s: log_name=%s peer_id=%lu peer:%s", e_sock->name, conn->log_name, (unsigned long)peer_id, sockaddr_storage_to_str(&addr).str); DEBUG_DEBUG(DEBUG_CATEGORY_CONNECTION, "New connection from %s peer_id=%ld etcp=%p", ip_to_str(&addr, addr.ss_family).str, peer_id, conn); @@ -1336,11 +1348,14 @@ void etcp_connections_read_callback_socket(socket_t sock, void* arg) { // For CHANNEL_INIT (0x04): if link already initialized - no reset, otherwise reset // For INIT_REQUEST (0x02): always reset - if (ack_hdr->code == ETCP_INIT_REQUEST_NOINIT && conn->initialized) { - send_reset = 0; // Link is up, respond without reset + // Check session_id: if same - no reinit, if different - client restarted, do reinit + if (conn->session_id == session_id) { + send_reset = 0; // Same session, no reinit needed + DEBUG_TRACE(DEBUG_CATEGORY_CONNECTION, "same session_id, skip reinit"); } else { - send_reset = 1; // INIT_REQUEST (0x02) or uninitialized link - send reset - DEBUG_TRACE(DEBUG_CATEGORY_CONNECTION, "do reinit"); + send_reset = 1; // New session or uninitialized link - send reset + DEBUG_TRACE(DEBUG_CATEGORY_CONNECTION, "new session_id %08x (was %08x), do reinit", session_id, conn->session_id); + conn->session_id = session_id; etcp_conn_reinit(conn); } @@ -1358,10 +1373,11 @@ void etcp_connections_read_callback_socket(socket_t sock, void* arg) { link->remote_socket_id = ack_hdr->remote_socket_id; link->remote_only_local = ack_hdr->only_local; - // For new links: INIT_REQUEST (0x02) causes reset, CHANNEL_INIT (0x04) does not - if (ack_hdr->code == ETCP_INIT_REQUEST || new_conn) { - send_reset = 1; // INIT_REQUEST (0x02) or uninitialized link - send reset - DEBUG_TRACE(DEBUG_CATEGORY_CONNECTION, "do reinit 2"); + // For new links: check session_id to avoid false reinit + if (conn->session_id != session_id) { + send_reset = 1; // New session or uninitialized link - send reset + DEBUG_TRACE(DEBUG_CATEGORY_CONNECTION, "new session_id %08x (was %08x) for new link, do reinit", session_id, conn->session_id); + conn->session_id = session_id; etcp_conn_reinit(conn); } link->keepalive_interval=(ack_hdr->keepalive[0]<<8) | ack_hdr->keepalive[1]; @@ -1376,6 +1392,7 @@ void etcp_connections_read_callback_socket(socket_t sock, void* arg) { struct { uint8_t code; uint8_t id[8]; + uint8_t session_id[4]; uint8_t mtu[2]; uint8_t link_id; uint8_t remote_socket_id; @@ -1385,7 +1402,8 @@ void etcp_connections_read_callback_socket(socket_t sock, void* arg) { } *ack_repl_hdr=(void*)&pkt->data[0]; // Set response code: 0x03 (with reset) or 0x05 (without reset) - if (send_reset != 0 || new_conn != 0 || ack_hdr->code == ETCP_INIT_REQUEST) { + // response with init (0x03) only if reinit was actually done on server side + if (send_reset != 0) { DEBUG_TRACE(DEBUG_CATEGORY_CONNECTION, "send init_response with reset"); ack_repl_hdr->code = ETCP_INIT_RESPONSE; // 0x03 - with reset } else { @@ -1393,6 +1411,7 @@ void etcp_connections_read_callback_socket(socket_t sock, void* arg) { ack_repl_hdr->code = ETCP_INIT_RESPONSE_NOINIT; // 0x05 - without reset } *(uint64_t*)ack_repl_hdr->id = htobe64(e_sock->instance->node_id); + *(uint32_t*)ack_repl_hdr->session_id = htobe32(conn->session_id); ack_repl_hdr->mtu[0]=link->mtu_local>>8; ack_repl_hdr->mtu[1]=link->mtu_local; @@ -1490,6 +1509,16 @@ process_decrypted: for (int i = 0; i < 8; i++) { server_node_id = (server_node_id << 8) | pkt->data[offset++]; } + uint32_t resp_session_id = (pkt->data[offset] << 24) | (pkt->data[offset+1] << 16) | (pkt->data[offset+2] << 8) | pkt->data[offset+3]; + offset += 4; + DEBUG_TRACE(DEBUG_CATEGORY_CONNECTION, "INIT_RESPONSE session_id=%08x", resp_session_id); + // Check session_id: ignore response if it doesn't match our session + if (resp_session_id != link->etcp->session_id) { + DEBUG_WARN(DEBUG_CATEGORY_CONNECTION, "[%s] INIT_RESPONSE session_id mismatch: got %08x, expected %08x, ignoring", + link->etcp->log_name, resp_session_id, link->etcp->session_id); + memory_pool_free(e_sock->instance->pkt_pool, pkt); + return; + } link->mtu_remote = (pkt->data[offset++] << 8) | pkt->data[offset++]; link->mtu = link->mtu_local < link->mtu_remote ? link->mtu_local : link->mtu_remote; link->remote_link_id = pkt->data[offset++]; @@ -1497,7 +1526,7 @@ process_decrypted: link->remote_only_local = pkt->data[offset++]; // Parse NAT IP:port from response (new format includes 4+2 bytes) - if (pkt_len >= 19) { + if (pkt_len >= 23) { uint32_t new_nat_ip; memcpy(&new_nat_ip, &pkt->data[offset], 4); offset += 4; @@ -1681,13 +1710,22 @@ int init_connections(struct UTUN_INSTANCE* instance) { // Create ETCP connection for this client struct ETCP_CONN* etcp_conn = etcp_connection_create(instance, client->name); - + if (!etcp_conn) { DEBUG_ERROR(DEBUG_CATEGORY_CONNECTION, "Failed to create ETCP connection for client %s", client->name); client = client->next; continue; } - + + // Generate session_id for this client connection + if (random_bytes((uint8_t*)&etcp_conn->session_id, sizeof(etcp_conn->session_id)) != 0) { + DEBUG_ERROR(DEBUG_CATEGORY_CRYPTO, "Failed to generate session_id for client %s", client->name); + etcp_connection_close(etcp_conn); + client = client->next; + continue; + } + DEBUG_INFO(DEBUG_CATEGORY_CONNECTION, "Client %s session_id=%08x", client->name, etcp_conn->session_id); + // Initialize crypto context for this connection if (sc_init_ctx(&etcp_conn->crypto_ctx, &instance->my_keys) != SC_OK) { DEBUG_ERROR(DEBUG_CATEGORY_CRYPTO, "init_connections: failed to initialize crypto context for client %s", client->name); diff --git a/src/utun_instance.c b/src/utun_instance.c index befabeeb..2b0c9e6a 100644 --- a/src/utun_instance.c +++ b/src/utun_instance.c @@ -569,8 +569,14 @@ struct UTUN_INSTANCE *utun_instance_reload(struct UTUN_INSTANCE *instance, struc } if (!conn) { conn = etcp_connection_create(instance, nc->name); - if (conn) sc_set_peer_public_key(&conn->crypto_ctx, nc->peer_public_key_hex, 1); - DEBUG_INFO(DEBUG_CATEGORY_CONNECTION, "Added new client %s", nc->name); + if (conn) { + sc_set_peer_public_key(&conn->crypto_ctx, nc->peer_public_key_hex, 1); + if (random_bytes((uint8_t*)&conn->session_id, sizeof(conn->session_id)) != 0) { + DEBUG_ERROR(DEBUG_CATEGORY_CRYPTO, "Failed to generate session_id for client %s", nc->name); + conn->session_id = 0; + } + DEBUG_INFO(DEBUG_CATEGORY_CONNECTION, "Added new client %s session_id=%08x", nc->name, conn->session_id); + } } // links for (struct CFG_CLIENT_LINK *nl = nc->links; nl; nl = nl->next) {