Browse Source

fix: block redundant conn reinit on cross-connect INIT (reset_done flag)

Problem: when two peers connect simultaneously, each incoming
INIT_REQUEST(0x02) or INIT_RESPONSE(0x03) triggers etcp_conn_reinit
unconditionally, causing endless UP/DOWN flapping loop.

Fix: add reset_done flag to ETCP_CONN:
- 0 at creation and after explicit reinit (reinit allowed)
- set to 1 in etcp_conn_ready (connection stable, block reinit)

Three call sites guarded with !conn->reset_done:
- client: handle_init_response_client (INIT_RESPONSE 0x03)
- server: existing link INIT_REQUEST processing
- server: new link INIT_REQUEST processing

send_reset logic preserved unconditionally — only etcp_conn_reinit
itself is blocked when already stable.
topo_upd
Evgeny 3 months ago
parent
commit
abbb6de701
  1. 4
      src/etcp.c
  2. 1
      src/etcp.h
  3. 16
      src/etcp_connections.c

4
src/etcp.c

@ -215,6 +215,7 @@ struct ETCP_CONN* etcp_connection_create(struct UTUN_INSTANCE* instance, char* n
etcp->optimal_inflight=100000; etcp->optimal_inflight=100000;
etcp->initialized=0; etcp->initialized=0;
etcp->links_up=0; etcp->links_up=0;
etcp->reset_done=0;
etcp->last_rr_link=NULL; etcp->last_rr_link=NULL;
etcp->name = u_strdup(name); etcp->name = u_strdup(name);
// Initialize log_name with local node_id (peer will be updated later when known) // Initialize log_name with local node_id (peer will be updated later when known)
@ -503,7 +504,7 @@ void etcp_conn_reinit(struct ETCP_CONN* etcp) {// Если сбой в обме
etcp->log_name, etcp->initialized, etcp->links_up, etcp->reinit_count, etcp->tx_state); etcp->log_name, etcp->initialized, etcp->links_up, etcp->reinit_count, etcp->tx_state);
etcp->reinit_count++; etcp->reinit_count++;
etcp->reset_done = 0;
etcp->initialized = 0;// еще раз придёт conn_ready_callback etcp->initialized = 0;// еще раз придёт conn_ready_callback
// Отменяем висящие NAT-ping'и для этого соединения // Отменяем висящие NAT-ping'и для этого соединения
@ -530,6 +531,7 @@ void etcp_conn_ready(struct ETCP_CONN* conn) {
if (!conn) return; if (!conn) return;
conn->initialized = 1; conn->initialized = 1;
conn->reset_done = 1;
// Если tx_state не установлен (первое подключение без reinit), установим его // Если tx_state не установлен (первое подключение без reinit), установим его
if (conn->tx_state == 0) { if (conn->tx_state == 0) {
conn->tx_state = ETCP_TX_STATE_DATA_WAIT; conn->tx_state = ETCP_TX_STATE_DATA_WAIT;

1
src/etcp.h

@ -225,6 +225,7 @@ struct ETCP_CONN {
uint32_t session_id; // случайный ID сессии (генерируется клиентом) для защиты от ложного reinit 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 tx_state; // 0 - n/a, 1 - data_wait (queues empty), 2 - link_wait (link busy)
uint8_t links_up; // 0 - канал не готов для передачи, 1 - канал готов для передачи (хотя бы один линк не down) uint8_t links_up; // 0 - канал не готов для передачи, 1 - канал готов для передачи (хотя бы один линк не down)
uint8_t reset_done; // 0 - рукопожатие не завершено (реинит разрешён), 1 - соединение стабильно (реинит заблокирован)
// Callback chains for ready/up/down notifications // Callback chains for ready/up/down notifications
struct etcp_cbk_entry* ready_cbks; // цепочка callback'ов при готовности соединения struct etcp_cbk_entry* ready_cbks; // цепочка callback'ов при готовности соединения

16
src/etcp_connections.c

@ -1473,7 +1473,7 @@ static int handle_init_response_client(struct ETCP_SOCKET* e_sock, struct ETCP_D
link->init_timer = NULL; link->init_timer = NULL;
} }
if (pkt_code == ETCP_INIT_RESPONSE) { if (pkt_code == ETCP_INIT_RESPONSE && !link->etcp->reset_done) {
DEBUG_INFO(DEBUG_CATEGORY_DEBUG, "[%s] REINIT from client: INIT_RESPONSE(0x03) received, reinit conn=%p", DEBUG_INFO(DEBUG_CATEGORY_DEBUG, "[%s] REINIT from client: INIT_RESPONSE(0x03) received, reinit conn=%p",
link->etcp->log_name, link->etcp); link->etcp->log_name, link->etcp);
etcp_conn_reinit(link->etcp); etcp_conn_reinit(link->etcp);
@ -1748,10 +1748,11 @@ void etcp_connections_read_callback_socket(socket_t sock, void* arg) {
// Check session_id: if same - no reinit, if different - client restarted, do reinit // Check session_id: if same - no reinit, if different - client restarted, do reinit
if (code == ETCP_INIT_REQUEST || conn->session_id != session_id || !conn->got_initial_pkt) { if (code == ETCP_INIT_REQUEST || conn->session_id != session_id || !conn->got_initial_pkt) {
send_reset = 1; // Client explicitly requested reset, or new session, or server waiting for first packet send_reset = 1; // Client explicitly requested reset, or new session, or server waiting for first packet
DEBUG_INFO(DEBUG_CATEGORY_DEBUG, "[%s] REINIT existing link: code=0x%02x sess=%08x→%08x got_init=%d initialized=%d links_up=%d", DEBUG_INFO(DEBUG_CATEGORY_DEBUG, "[%s] REINIT existing link: code=0x%02x sess=%08x→%08x got_init=%d initialized=%d links_up=%d reset_done=%d",
conn->log_name, code, conn->session_id, session_id, conn->got_initial_pkt, conn->initialized, conn->links_up); conn->log_name, code, conn->session_id, session_id, conn->got_initial_pkt, conn->initialized, conn->links_up, conn->reset_done);
conn->session_id = session_id; conn->session_id = session_id;
etcp_conn_reinit(conn); if (!conn->reset_done) etcp_conn_reinit(conn);
else DEBUG_INFO(DEBUG_CATEGORY_DEBUG, "[%s] REINIT skipped — reset_done=1", conn->log_name);
} else { } else {
send_reset = 0; // Same session, no reinit needed send_reset = 0; // Same session, no reinit needed
DEBUG_TRACE(DEBUG_CATEGORY_CONNECTION, "same session_id, skip reinit"); DEBUG_TRACE(DEBUG_CATEGORY_CONNECTION, "same session_id, skip reinit");
@ -1775,10 +1776,11 @@ void etcp_connections_read_callback_socket(socket_t sock, void* arg) {
// For new links: reset if client requested or session changed // For new links: reset if client requested or session changed
if (code == ETCP_INIT_REQUEST || conn->session_id != session_id || !conn->got_initial_pkt) { if (code == ETCP_INIT_REQUEST || conn->session_id != session_id || !conn->got_initial_pkt) {
send_reset = 1; send_reset = 1;
DEBUG_INFO(DEBUG_CATEGORY_DEBUG, "[%s] REINIT new link: code=0x%02x sess=%08x→%08x got_init=%d initialized=%d links_up=%d", DEBUG_INFO(DEBUG_CATEGORY_DEBUG, "[%s] REINIT new link: code=0x%02x sess=%08x→%08x got_init=%d initialized=%d links_up=%d reset_done=%d",
conn->log_name, code, conn->session_id, session_id, conn->got_initial_pkt, conn->initialized, conn->links_up); conn->log_name, code, conn->session_id, session_id, conn->got_initial_pkt, conn->initialized, conn->links_up, conn->reset_done);
conn->session_id = session_id; conn->session_id = session_id;
etcp_conn_reinit(conn); if (!conn->reset_done) etcp_conn_reinit(conn);
else DEBUG_INFO(DEBUG_CATEGORY_DEBUG, "[%s] REINIT skipped — reset_done=1", conn->log_name);
} }
link->keepalive_interval=(req->keepalive[0]<<8) | req->keepalive[1]; link->keepalive_interval=(req->keepalive[0]<<8) | req->keepalive[1];
link->recovery_interval=((req->recovery[0]<<8) | req->recovery[1])*100;// timebase в link, timebase/100 в кодограмме link->recovery_interval=((req->recovery[0]<<8) | req->recovery[1])*100;// timebase в link, timebase/100 в кодограмме

Loading…
Cancel
Save