Browse Source

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
congestion
Evgeny 5 months ago
parent
commit
33fd5bc123
  1. 8
      src/etcp.c
  2. 1
      src/etcp.h
  3. 62
      src/etcp_connections.c
  4. 10
      src/utun_instance.c

8
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);
}

1
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)

62
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);

10
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) {

Loading…
Cancel
Save