etcp: replace connections linked-list with indexed ll_queue, add state/ref_count lifecycle

- Remove ETCP_CONN.next linked-list, UTUN_INSTANCE.connections list/count
- Unified connections in single ll_queue indexed by peer_node_id (0=pending)
- struct conn_queue_entry { peer_node_id, conn* } for uniform queue data
- Connection lifecycle via etcp.c: create→pending, conn_ready→indexed, close→detach+deferred free
- Close split: phase1 detach from external world, phase2 deferred via uasync_call_soon
- etcp_conn_set_peer_node_id: reindex on ID change (WARN)
- etcp_conn_ref_take/free: blocking close until refcount returns to 0
- Remove chat_connections — use unified connections queue directly
- Update all iteration/search patterns across 40 files (src + tests + chatgui)
This commit is contained in:
Evgeny committed 2026-07-15 19:56:47 +03:00
1 parent 6902f5fd95
commit a7ecee01f5
40 files changed
+650 -449

No files matched your search

+14 -16
View File
@@ -885,10 +885,10 @@ static void send_conn_list(struct control_server* server, struct control_client*
/* Count connections */
uint8_t count = 0;
struct ETCP_CONN* conn = instance->connections;
while (conn && count < ETCPMON_MAX_CONNECTIONS) {
struct ll_entry* entry = instance->connections->head;
while (entry && count < ETCPMON_MAX_CONNECTIONS) {
count++;
conn = conn->next;
entry = entry->next;
}
/* Build response */
@@ -909,12 +909,13 @@ static void send_conn_list(struct control_server* server, struct control_client*
rsp->count = count;
struct etcpmon_conn_info* info = (struct etcpmon_conn_info*)(buffer + sizeof(*hdr) + sizeof(*rsp));
conn = instance->connections;
for (uint8_t i = 0; i < count && conn; i++) {
info[i].peer_node_id = conn->peer_node_id;
strncpy(info[i].name, conn->log_name, ETCPMON_MAX_CONN_NAME - 1);
entry = instance->connections->head;
for (uint8_t i = 0; i < count && entry; i++) {
struct conn_queue_entry* ce = (struct conn_queue_entry*)entry->data;
info[i].peer_node_id = ce->conn->peer_node_id;
strncpy(info[i].name, ce->conn->log_name, ETCPMON_MAX_CONN_NAME - 1);
info[i].name[ETCPMON_MAX_CONN_NAME - 1] = '\0';
conn = conn->next;
entry = entry->next;
}
/* Log and send response */
@@ -1372,14 +1373,11 @@ static void send_debug_config(struct control_server* server, struct control_clie
static struct ETCP_CONN* find_connection_by_peer_id(struct UTUN_INSTANCE* instance,
uint64_t peer_id) {
struct ETCP_CONN* conn = instance->connections;
while (conn) {
if (conn->peer_node_id == peer_id) {
return conn;
}
conn = conn->next;
}
return NULL;
if (!instance || !instance->connections) return NULL;
struct ll_entry* e = queue_find_data_by_index(instance->connections, (const uint8_t*)&peer_id);
if (!e) return NULL;
struct conn_queue_entry* ce = (struct conn_queue_entry*)e->data;
return ce->conn;
}
/* ============================================================================
+29 -18
View File
@@ -488,9 +488,9 @@ static int db_sync_send_hash(struct DB_SYNC* db, uint64_t node_id,
entry->len = plen + 9;
struct ETCP_CONN* conn = NULL;
if (db->inst->chat_connections) {
struct ll_entry* e = queue_find_data_by_index(db->inst->chat_connections, (const uint8_t*)&node_id);
if (e) { struct { uint64_t node_id; struct ETCP_CONN* conn; }* ce = e->data; if (ce->conn->links_up) conn = ce->conn; }
{
struct ll_entry* e = queue_find_data_by_index(db->inst->connections, (const uint8_t*)&node_id);
if (e) { struct conn_queue_entry* ce = (struct conn_queue_entry*)e->data; if (ce->conn->links_up) conn = ce->conn; }
}
if (!conn) {
DEBUG_WARN(DEBUG_CATEGORY_DB_SYNC,
@@ -1430,11 +1430,16 @@ int db_sync_init(struct UTUN_INSTANCE* inst)
etcp_set_new_conn_cbk(inst, db_sync_on_new_conn, NULL);
// Attach to existing connections
struct ETCP_CONN* conn = inst->connections;
while (conn) {
etcp_conn_add_up_cbk(conn, db_sync_on_conn_up, NULL);
etcp_conn_add_down_cbk(conn, db_sync_on_conn_down, NULL);
conn = conn->next;
{
struct ll_entry* entry = inst->connections->head;
while (entry) {
struct conn_queue_entry* ce = (struct conn_queue_entry*)entry->data;
if (ce && ce->conn) {
etcp_conn_add_up_cbk(ce->conn, db_sync_on_conn_up, NULL);
etcp_conn_add_down_cbk(ce->conn, db_sync_on_conn_down, NULL);
}
entry = entry->next;
}
}
if (db->enabled)
@@ -1474,11 +1479,16 @@ void db_sync_destroy(struct UTUN_INSTANCE* inst)
}
// Detach from existing connections
struct ETCP_CONN* conn = inst->connections;
while (conn) {
etcp_conn_remove_up_cbk(conn, db_sync_on_conn_up, NULL);
etcp_conn_remove_down_cbk(conn, db_sync_on_conn_down, NULL);
conn = conn->next;
{
struct ll_entry* entry = inst->connections->head;
while (entry) {
struct conn_queue_entry* ce = (struct conn_queue_entry*)entry->data;
if (ce && ce->conn) {
etcp_conn_remove_up_cbk(ce->conn, db_sync_on_conn_up, NULL);
etcp_conn_remove_down_cbk(ce->conn, db_sync_on_conn_down, NULL);
}
entry = entry->next;
}
}
db_sqlite_close(db);
@@ -1579,11 +1589,12 @@ struct DB_SYNC_INSTANCE* db_sync_instance_add(struct UTUN_INSTANCE* inst,
// Initiate sync with already connected peers
{
struct ETCP_CONN* conn = inst->connections;
while (conn) {
uint64_t pid = conn->peer_node_id;
struct ll_entry* entry = inst->connections->head;
while (entry) {
struct conn_queue_entry* ce = (struct conn_queue_entry*)entry->data;
uint64_t pid = ce->conn->peer_node_id;
if (pid != 0 && pid != inst->node_id
&& conn->links_up > 0 && conn->initialized)
&& ce->conn->links_up > 0 && ce->conn->initialized)
{
struct SI_PEER* p = si_peer_add(si, pid);
if (p && p->synced == 0) {
@@ -1591,7 +1602,7 @@ struct DB_SYNC_INSTANCE* db_sync_instance_add(struct UTUN_INSTANCE* inst,
db_sync_initiate_sync(si, pid);
}
}
conn = conn->next;
entry = entry->next;
}
}
+151 -75
View File
@@ -44,6 +44,7 @@ static void input_send_q_cb(struct ll_queue* q, void* arg);
static void wait_ack_cb(struct ll_queue* q, void* arg);
static void send_ack_req_cb(struct ll_queue* q, void* arg);
static void etcp_conn_process_send_queue(struct ETCP_CONN* etcp);
static void etcp_connection_free_deferred(void* arg);
struct ETCP_DGRAM* etcp_request_pkt(struct ETCP_CONN* etcp);
static void clear_queue(struct ll_queue* q) {
@@ -217,6 +218,7 @@ struct ETCP_CONN* etcp_connection_create(struct UTUN_INSTANCE* instance, char* n
etcp->links_up=0;
etcp->reset_done=0;
etcp->callbacks_running=0;
etcp->ref_count=0;
etcp->last_rr_link=NULL;
etcp->name = u_strdup(name);
// Initialize log_name with local node_id (peer will be updated later when known)
@@ -256,7 +258,19 @@ struct ETCP_CONN* etcp_connection_create(struct UTUN_INSTANCE* instance, char* n
etcp->link_ready_for_send_fn = etcp_link_ready_callback;
DEBUG_DEBUG(DEBUG_CATEGORY_ETCP, "[%s] connection initialized. ETCP=%p mtu=%d, next_tx_id=%u",
// Add to instance's connections queue (peer_node_id=0 for pending, reindexed later)
{
struct ll_entry* qe = queue_entry_new(sizeof(struct conn_queue_entry));
if (!qe) { etcp_connection_close(etcp); return NULL; }
struct conn_queue_entry* ce = (struct conn_queue_entry*)qe->data;
ce->peer_node_id = 0;
ce->conn = etcp;
etcp->conn_queue_entry = qe;
etcp->conn_queue = instance->connections;
queue_data_put_with_index(instance->connections, qe);
}
DEBUG_DEBUG(DEBUG_CATEGORY_ETCP, "[%s] connection initialized. ETCP=%p mtu=%d, next_tx_id=%u state=pending",
etcp->log_name, etcp, etcp->mtu, etcp->next_tx_id);
// Вызываем callback для нового соединения если установлен
@@ -286,57 +300,15 @@ static void etcp_on_down(struct ETCP_CONN* etcp) {
}
// Close connection with NULL pointer safety (prevents double free)
void etcp_connection_close(struct ETCP_CONN* etcp) {
DEBUG_TRACE(DEBUG_CATEGORY_ETCP, "");
// Phase 2 resources cleanup (callable both sync and async via call_soon)
static void etcp_connection_free_resources(struct ETCP_CONN* etcp) {
if (!etcp) return;
DEBUG_DEBUG(DEBUG_CATEGORY_ETCP, "[%s] freeing resources phase 2", etcp->log_name);
if (etcp->callbacks_running) {
DEBUG_ERROR(DEBUG_CATEGORY_DEBUG, "[%s] FATAL: etcp_connection_close called from inside callback chain — SEGFAULTING to show backtrace",
etcp->log_name);
*(volatile int*)0 = 0;
}
if (etcp->links_up!=0) {
etcp->links_up=0;
etcp_on_down(etcp);
}
// Cancel active timers to prevent memory leaks
if (etcp->retrans_timer) {
uasync_cancel_timeout(etcp->instance->ua, etcp->retrans_timer);
etcp->retrans_timer = NULL;
}
if (etcp->ack_resp_timer) {
uasync_cancel_timeout(etcp->instance->ua, etcp->ack_resp_timer);
etcp->ack_resp_timer = NULL;
}
etcp_metrics_stop_timer(etcp);
routing_del_conn(etcp);
// NOTE: topo_bgp_remove_conn is already called via down_cbk in etcp_on_down() above
// DO NOT call it again here to avoid double-processing and ref_count corruption
// Очистить транзитные очереди (до pn_deinit/stcp_link_close — waiter'ы на normalizer->input/tx_queue)
etcp_router_transit_queues_destroy(etcp);
// Deinitialize packet normalizer (this will call routing_del_conn)
if (etcp->normalizer) {
pn_deinit((struct PKTNORM*)etcp->normalizer);
etcp->normalizer = NULL;
}
// Clear links BEFORE draining queues: etcp_link_close calls
// etcp_conn_on_inflight_lim_changed -> input_queue_try_resume
// which accesses input_send_q, input_queue, input_wait_ack.
// Close links
if (etcp->links) {
struct ETCP_LINK* link = etcp->links;
while (link) {
struct ETCP_LINK* next = link->next;
etcp_link_close(link);
link = next;
}
while (link) { struct ETCP_LINK* next = link->next; etcp_link_close(link); link = next; }
etcp->links = NULL;
}
@@ -350,35 +322,86 @@ void etcp_connection_close(struct ETCP_CONN* etcp) {
// Free callback chains
{ struct etcp_cbk_entry* cbe = etcp->ready_cbks; while (cbe) { struct etcp_cbk_entry* n = cbe->next; u_free(cbe); cbe = n; } etcp->ready_cbks = NULL; }
{ struct etcp_cbk_entry* cbe = etcp->up_cbks; while (cbe) { struct etcp_cbk_entry* n = cbe->next; u_free(cbe); cbe = n; } etcp->up_cbks = NULL; }
{ struct etcp_cbk_entry* cbe = etcp->down_cbks; while (cbe) { struct etcp_cbk_entry* n = cbe->next; u_free(cbe); cbe = n; } etcp->down_cbks = NULL; }
{ struct etcp_cbk_entry* cbe = etcp->up_cbks; while (cbe) { struct etcp_cbk_entry* n = cbe->next; u_free(cbe); cbe = n; } etcp->up_cbks = NULL; }
{ struct etcp_cbk_entry* cbe = etcp->down_cbks; while (cbe) { struct etcp_cbk_entry* n = cbe->next; u_free(cbe); cbe = n; } etcp->down_cbks = NULL; }
// Free memory pools after all elements are returned
if (etcp->inflight_pool) {
memory_pool_destroy(etcp->inflight_pool);
etcp->inflight_pool = NULL;
}
if (etcp->io_pool) {
memory_pool_destroy(etcp->io_pool);
etcp->io_pool = NULL;
}
if (etcp->inflight_pool) { memory_pool_destroy(etcp->inflight_pool); etcp->inflight_pool = NULL; }
if (etcp->io_pool) { memory_pool_destroy(etcp->io_pool); etcp->io_pool = NULL; }
u_free(etcp->name);
u_free(etcp);
}
{ struct UTUN_INSTANCE* inst = etcp->instance;
if (inst && inst->connections) {
struct ETCP_CONN** pp = &inst->connections;
while (*pp && *pp != etcp) pp = &(*pp)->next;
if (*pp) { *pp = etcp->next; if (inst->connections_count) inst->connections_count--; }
}
static void etcp_connection_free_deferred(void* arg) {
etcp_connection_free_resources((struct ETCP_CONN*)arg);
}
// Close connection: phase 1 detach + deferred phase 2 cleanup
void etcp_connection_close(struct ETCP_CONN* etcp) {
DEBUG_TRACE(DEBUG_CATEGORY_ETCP, "");
if (!etcp) return;
if (etcp->state == 2) { DEBUG_WARN(DEBUG_CATEGORY_ETCP, "[%s] already deleted", etcp->log_name); return; }
if (etcp->callbacks_running) {
DEBUG_ERROR(DEBUG_CATEGORY_DEBUG, "[%s] FATAL: etcp_connection_close called from inside callback chain — SEGFAULTING to show backtrace",
etcp->log_name);
*(volatile int*)0 = 0;
}
// Clear next pointer to prevent dangling references
etcp->next = NULL;
// === PHASE 1: detach from external world ===
// TODO: Free rx_list, etc.
u_free(etcp);
if (etcp->links_up != 0) { etcp->links_up = 0; etcp_on_down(etcp); }
// Cancel active timers
if (etcp->retrans_timer) { uasync_cancel_timeout(etcp->instance->ua, etcp->retrans_timer); etcp->retrans_timer = NULL; }
if (etcp->ack_resp_timer) { uasync_cancel_timeout(etcp->instance->ua, etcp->ack_resp_timer); etcp->ack_resp_timer = NULL; }
etcp_metrics_stop_timer(etcp);
routing_del_conn(etcp);
etcp_router_transit_queues_destroy(etcp);
if (etcp->normalizer) { pn_deinit((struct PKTNORM*)etcp->normalizer); etcp->normalizer = NULL; }
// Close links (detach from socket scan queues)
if (etcp->links) {
struct ETCP_LINK* link = etcp->links;
while (link) { struct ETCP_LINK* next = link->next; etcp_link_close(link); link = next; }
etcp->links = NULL;
}
// Remove from instance queue
if (etcp->conn_queue && etcp->conn_queue_entry) {
queue_remove_data(etcp->conn_queue, etcp->conn_queue_entry);
queue_entry_free(etcp->conn_queue_entry);
etcp->conn_queue_entry = NULL;
etcp->conn_queue = NULL;
}
etcp->state = 2; // deleted
// === PHASE 2: deferred resource cleanup (only if no outstanding refs) ===
if (etcp->ref_count == 0)
uasync_call_soon(etcp->instance->ua, etcp, etcp_connection_free_deferred);
// else: cleanup deferred until last etcp_conn_ref_free()
}
// Take a reference on connection. Returns -1 if already deleted (state==2).
int etcp_conn_ref_take(struct ETCP_CONN* conn) {
if (!conn) return -1;
if (conn->state == 2) { DEBUG_WARN(DEBUG_CATEGORY_ETCP, "[%s] ref_take on deleted conn", conn->log_name); return -1; }
conn->ref_count++;
DEBUG_DEBUG(DEBUG_CATEGORY_ETCP, "[%s] ref_take ref_count=%d", conn->log_name, conn->ref_count);
return 0;
}
// Release a reference. If last ref and conn is deleted, schedule deferred free.
void etcp_conn_ref_free(struct ETCP_CONN* conn) {
if (!conn) return;
if (conn->ref_count <= 0) { DEBUG_WARN(DEBUG_CATEGORY_ETCP, "[%s] ref_free underflow ref_count=%d", conn->log_name, conn->ref_count); return; }
conn->ref_count--;
DEBUG_DEBUG(DEBUG_CATEGORY_ETCP, "[%s] ref_free ref_count=%d state=%d", conn->log_name, conn->ref_count, conn->state);
if (conn->ref_count == 0 && conn->state == 2)
uasync_call_soon(conn->instance->ua, conn, etcp_connection_free_deferred);
}
// Reset connection
@@ -543,24 +566,77 @@ void etcp_conn_reinit(struct ETCP_CONN* etcp) {// Если сбой в обме
// внутренняя функция. Вызывается один раз когда первый линк готов.
void etcp_conn_ready(struct ETCP_CONN* conn) {
if (!conn) return;
if (conn->initialized) return; // already ready
conn->initialized = 1;
conn->reset_done = 1;
// Если tx_state не установлен (первое подключение без reinit), установим его
if (conn->tx_state == 0) {
conn->tx_state = ETCP_TX_STATE_DATA_WAIT;
}
if (conn->tx_state == 0) { conn->tx_state = ETCP_TX_STATE_DATA_WAIT; }
DEBUG_INFO(DEBUG_CATEGORY_ETCP, "[%s] Connection ready", conn->log_name);
etcp_conn_queue_set_ready(conn);
}
// Move from pending to indexed connections, fire ready callbacks
void etcp_conn_queue_set_ready(struct ETCP_CONN* conn) {
if (!conn || conn->state != 0) return;
// Reindex: remove old entry (key=0), add new entry with real peer_node_id
if (conn->conn_queue && conn->conn_queue_entry) {
queue_remove_data(conn->conn_queue, conn->conn_queue_entry);
queue_entry_free(conn->conn_queue_entry);
}
struct ll_entry* qe = queue_entry_new(sizeof(struct conn_queue_entry));
if (!qe) { DEBUG_ERROR(DEBUG_CATEGORY_ETCP, "[%s] failed to alloc queue entry for ready", conn->log_name); return; }
struct conn_queue_entry* ce = (struct conn_queue_entry*)qe->data;
ce->peer_node_id = conn->peer_node_id;
ce->conn = conn;
conn->conn_queue_entry = qe;
conn->conn_queue = conn->instance->connections;
conn->state = 1;
queue_data_put_with_index(conn->instance->connections, qe);
DEBUG_INFO(DEBUG_CATEGORY_ETCP, "[%s] moved to ready queue, state=%d peer_node_id=0x%llx",
conn->log_name, conn->state, (unsigned long long)conn->peer_node_id);
etcp_metrics_start_timer(conn);
// Вызываем callback если установлен
conn->callbacks_running = 1;
{ struct etcp_cbk_entry* cbe = conn->ready_cbks; while (cbe) { struct etcp_cbk_entry* n = cbe->next; cbe->fn(conn, cbe->arg); cbe = n; } }
if (conn->links_up) etcp_on_up(conn);
conn->callbacks_running = 0;
}
// Set peer_node_id, reindex if already in connections queue
void etcp_conn_set_peer_node_id(struct ETCP_CONN* conn, uint64_t peer_node_id) {
if (!conn) return;
if (conn->state == 2) { DEBUG_WARN(DEBUG_CATEGORY_ETCP, "[%s] set_peer_node_id on deleted conn", conn->log_name); return; }
if (conn->peer_node_id == peer_node_id) return;
if (conn->state == 1 && conn->peer_node_id != 0) {
// Already in indexed queue with old ID — reindex
DEBUG_WARN(DEBUG_CATEGORY_ETCP, "[%s] peer_node_id changed 0x%llx -> 0x%llx, reindexing",
conn->log_name, (unsigned long long)conn->peer_node_id, (unsigned long long)peer_node_id);
if (conn->conn_queue && conn->conn_queue_entry) {
queue_remove_data(conn->conn_queue, conn->conn_queue_entry);
queue_entry_free(conn->conn_queue_entry);
}
struct ll_entry* qe = queue_entry_new(sizeof(struct conn_queue_entry));
if (!qe) { DEBUG_ERROR(DEBUG_CATEGORY_ETCP, "[%s] reindex alloc failed", conn->log_name); return; }
struct conn_queue_entry* ce = (struct conn_queue_entry*)qe->data;
ce->peer_node_id = peer_node_id;
ce->conn = conn;
conn->conn_queue_entry = qe;
conn->conn_queue = conn->instance->connections;
queue_data_put_with_index(conn->instance->connections, qe);
}
conn->peer_node_id = peer_node_id;
etcp_update_log_name(conn);
DEBUG_INFO(DEBUG_CATEGORY_ETCP, "[%s] peer_node_id set to 0x%llx", conn->log_name, (unsigned long long)peer_node_id);
}
// Update log_name when peer_node_id becomes known
void etcp_update_log_name(struct ETCP_CONN* etcp) {
+32 -1
View File
@@ -138,11 +138,19 @@ struct ACK_PACKET {
// ETCP connection structure (refactored)
struct ETCP_CONN {
struct ETCP_CONN* next;
// State: 0=not ready, 1=ready (indexed in instance->connections), 2=deleted
int state; // 0=pending, 1=ready, 2=deleted (phase 1 of close done)
int ref_count; // External reference count. >0 blocks deferred resource free.
// Take/free via etcp_conn_ref_take()/etcp_conn_ref_free().
int mtu;
struct UTUN_INSTANCE* instance;
// Queue entries in instance->connections or instance->pending_connections
struct ll_entry* conn_queue_entry; // entry в очереди instance
struct ll_queue* conn_queue; // указатель на очередь где лежим (pending или connections)
// Links (channels) - linked list
struct ETCP_LINK* links;
struct ETCP_LINK* last_rr_link; // последний линк, выбранный round-robin
@@ -252,6 +260,29 @@ struct ETCP_CONN {
// Functions
struct ETCP_CONN* etcp_connection_create(struct UTUN_INSTANCE* instance, char* name);
void etcp_connection_close(struct ETCP_CONN* etcp);
void etcp_conn_set_peer_node_id(struct ETCP_CONN* conn, uint64_t peer_node_id);
void etcp_conn_queue_set_ready(struct ETCP_CONN* conn); // move pending->connections, fire ready cbks
/**
* @brief Take a reference on the connection (blocks deferred resource free).
* @param conn connection
* @return 0 on success, -1 if conn is NULL or already in state 2 (deleted).
*
* Increments ref_count. While ref_count > 0, etcp_connection_close()
* will detach but defer resource cleanup until all references are released.
* Always pair with etcp_conn_ref_free().
*/
int etcp_conn_ref_take(struct ETCP_CONN* conn);
/**
* @brief Release a reference. Triggers deferred resource free if last ref and state==2.
* @param conn connection
*
* Decrements ref_count. If ref_count reaches 0 and the connection has been closed
* (state == 2), schedules deferred resource cleanup via uasync_call_soon.
* Safe to call on NULL.
*/
void etcp_conn_ref_free(struct ETCP_CONN* conn);
void etcp_conn_reset(struct ETCP_CONN* etcp);
void etcp_links_reset(struct ETCP_CONN* etcp);
+18 -15
View File
@@ -210,22 +210,26 @@ int etcp_connect(struct UTUN_INSTANCE* inst, struct TOPO_NODEQ* node,
if (!inst || !node || !cb) return -1;
uint64_t node_id = node->node->node_id;
struct ETCP_CONN* existing = inst->connections;
while (existing) {
if (existing->peer_node_id == node_id) {
struct ETCP_LINK* l = existing->links;
while (l) { if (l->link_state == 3) break; l = l->next; }
if (l) {
DEBUG_INFO(DEBUG_CATEGORY_ETCP_CONNECT, "[etcp_connect] node 0x%016llx already connected, delivering immediately",
(unsigned long long)node_id);
if (flags & ETCP_CONNECT_EARLY) cb(arg, existing, ETCP_CONNECT_EARLY);
if (flags & ETCP_CONNECT_LATE) cb(arg, existing, ETCP_CONNECT_LATE);
if ((flags & ETCP_CONNECT_BGP_READY) && existing->routing_exchange_active >= 3)
cb(arg, existing, ETCP_CONNECT_BGP_READY);
return 0;
// Check if already connected via indexed queue
{
struct ll_entry* e = queue_find_data_by_index(inst->connections, (const uint8_t*)&node_id);
if (e) {
struct conn_queue_entry* ce = (struct conn_queue_entry*)e->data;
struct ETCP_CONN* existing = ce->conn;
if (existing && existing->peer_node_id == node_id) {
struct ETCP_LINK* l = existing->links;
while (l) { if (l->link_state == 3) break; l = l->next; }
if (l) {
DEBUG_INFO(DEBUG_CATEGORY_ETCP_CONNECT, "[etcp_connect] node 0x%016llx already connected, delivering immediately",
(unsigned long long)node_id);
if (flags & ETCP_CONNECT_EARLY) cb(arg, existing, ETCP_CONNECT_EARLY);
if (flags & ETCP_CONNECT_LATE) cb(arg, existing, ETCP_CONNECT_LATE);
if ((flags & ETCP_CONNECT_BGP_READY) && existing->routing_exchange_active >= 3)
cb(arg, existing, ETCP_CONNECT_BGP_READY);
return 0;
}
}
}
existing = existing->next;
}
struct ETCP_CONNECT* ctx = connect_find(inst, node_id);
@@ -241,7 +245,6 @@ int etcp_connect(struct UTUN_INSTANCE* inst, struct TOPO_NODEQ* node,
struct ETCP_CONN* conn = etcp_connection_create(inst, NULL);
if (!conn) { DEBUG_ERROR(DEBUG_CATEGORY_ETCP_CONNECT, "[etcp_connect] failed to create ETCP_CONN for node 0x%016llx",
(unsigned long long)node_id); cb(arg, NULL, 0); return -1; }
conn->next = inst->connections; inst->connections = conn; inst->connections_count++;
if (sc_init_ctx(&conn->crypto_ctx, &inst->my_keys) != SC_OK) {
DEBUG_ERROR(DEBUG_CATEGORY_ETCP_CONNECT, "[etcp_connect] sc_init_ctx failed for node 0x%016llx", (unsigned long long)node_id);
etcp_connection_close(conn); cb(arg, NULL, 0); return -1;
+20 -29
View File
@@ -32,18 +32,17 @@
// TCP server: on new incoming connection → create minimal ETCP_CONN
static void tcp_server_on_link(struct stcp_link *link, void *arg) {
struct UTUN_INSTANCE *inst = (struct UTUN_INSTANCE *)arg;
struct ETCP_CONN *conn = u_calloc(1, sizeof(struct ETCP_CONN));
struct ETCP_CONN *conn = etcp_connection_create(inst, NULL);
if (!conn) return;
conn->instance = inst;
conn->transport_link = link;
snprintf(conn->log_name, sizeof(conn->log_name), "tcp-[%p]", (void*)link);
conn->next = inst->connections;
inst->connections = conn;
inst->connections_count++;
struct etcp_cbk_entry* cbe = inst->new_conn_cbks;
while (cbe) { struct etcp_cbk_entry* n = cbe->next; cbe->fn(conn, cbe->arg); cbe = n; }
{ struct etcp_cbk_entry* rcb = conn->ready_cbks; while (rcb) { struct etcp_cbk_entry* n = rcb->next; rcb->fn(conn, rcb->arg); rcb = n; } }
DEBUG_INFO(DEBUG_CATEGORY_ETCP, "TCP server new conn=%p total=%d", (void*)conn, inst->connections_count);
DEBUG_INFO(DEBUG_CATEGORY_ETCP, "TCP server new conn=%p total=%d pending=%d",
(void*)conn,
queue_entry_count(inst->connections),
queue_entry_count(inst->connections));
}
// Forward declaration
@@ -1464,8 +1463,7 @@ static int handle_init_response_client(struct ETCP_SOCKET* e_sock, struct ETCP_D
// DEBUG_INFO(DEBUG_CATEGORY_CONNECTION, "Received INIT_RESPONSE from server_node_id=%llu, mtu=%d", (unsigned long long)server_node_id, link->mtu);
link->etcp->peer_node_id = server_node_id; // If not set
etcp_update_log_name(link->etcp); // Update log_name with peer_node_id
etcp_conn_set_peer_node_id(link->etcp, server_node_id);
link->initialized = 1;// получен init response (client)
link->link_state = 3; // connected
@@ -1678,11 +1676,11 @@ void etcp_connections_read_callback_socket(socket_t sock, void* arg) {
req->src_ipv4[0], req->src_ipv4[1], req->src_ipv4[2], req->src_ipv4[3],
src_port, session_id, sockaddr_storage_to_str(&addr).str);
struct ETCP_CONN* conn=e_sock->instance->connections;
while (conn) {// ищем есть ли подключение к этому пиру
if (conn->peer_node_id==peer_id) break;
conn=conn->next;
}
struct ETCP_CONN* conn = NULL;
{
struct ll_entry* e = queue_find_data_by_index(e_sock->instance->connections, (const uint8_t*)&peer_id);
if (e) { struct conn_queue_entry* ce = (struct conn_queue_entry*)e->data; conn = ce->conn; }
}
int new_conn=0;
if (!conn || conn->peer_node_id!=peer_id) {// создаём новое подключение [new etcp]
@@ -1690,14 +1688,11 @@ void etcp_connections_read_callback_socket(socket_t sock, void* arg) {
conn=etcp_connection_create(e_sock->instance,"");
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;
etcp_update_log_name(conn);
etcp_conn_set_peer_node_id(conn, peer_id);
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);
conn->next = e_sock->instance->connections;
e_sock->instance->connections = conn;
e_sock->instance->connections_count++;
DEBUG_INFO(DEBUG_CATEGORY_CONNECTION, "Added incoming connection %p to instance, total count: %d", conn, e_sock->instance->connections_count);
DEBUG_DEBUG(DEBUG_CATEGORY_CONNECTION, "New connection from %s peer_id=%ld etcp=%p total=%d",
ip_to_str(&addr, addr.ss_family).str, peer_id, conn,
queue_entry_count(e_sock->instance->connections));
}
else {// check keys если существующее подключение
if (memcmp(conn->crypto_ctx.peer_public_key, sc.peer_public_key, SC_PUBKEY_SIZE)) { errorcode=5; DEBUG_ERROR(DEBUG_CATEGORY_CRYPTO, "peer key mismatch for node %016llx", (unsigned long long)peer_id); goto ec_fr; }// коллизия - peer id совпал а ключи разные.
@@ -2065,8 +2060,8 @@ int init_connections(struct UTUN_INSTANCE* instance) {
// Initialize clients - create outgoing connections
struct CFG_CLIENT* client = config->clients;
DEBUG_DEBUG(DEBUG_CATEGORY_CONNECTION, "init_connections called, instance=%p, config=%p, clients=%p, connections_count=%d",
instance, config, config ? config->clients : NULL, instance ? instance->connections_count : -1);
DEBUG_DEBUG(DEBUG_CATEGORY_CONNECTION, "init_connections called, instance=%p, config=%p, clients=%p, total_conns=%d",
instance, config, config ? config->clients : NULL, instance ? queue_entry_count(instance->connections) : -1);
while (client) {
// Check if client has required configuration
DEBUG_INFO(DEBUG_CATEGORY_CONNECTION, "Client %s - keepalive=%d, links=%p, peer_key_len=%zu",
@@ -2196,22 +2191,18 @@ int init_connections(struct UTUN_INSTANCE* instance) {
client_link = client_link->next;
}
etcp_conn->next = instance->connections;
instance->connections = etcp_conn;
instance->connections_count++;
DEBUG_INFO(DEBUG_CATEGORY_CONNECTION, "Added connection %p to instance, total count: %d", etcp_conn, instance->connections_count);
client = client->next;
}
// If there are clients configured but no connections created, that's an error
// If there are no clients (server-only mode), 0 connections is OK (server will accept incoming)
if (instance->connections_count == 0 && config->clients != NULL) {
int total_conns = queue_entry_count(instance->connections);
if (total_conns == 0 && config->clients != NULL) {
DEBUG_ERROR(DEBUG_CATEGORY_ETCP, "Clients configured but no connections initialized");
return -1;
}
DEBUG_INFO(DEBUG_CATEGORY_CONNECTION, "Initialized %d connections", instance->connections_count);
DEBUG_INFO(DEBUG_CATEGORY_CONNECTION, "Initialized %d connections", total_conns);
// Return 1 if there was a partial socket initialization error
if (socket_result == 1) return 1;
return 0;
+9 -6
View File
@@ -233,13 +233,16 @@ void etcp_dump_all_conns(struct UTUN_INSTANCE* instance) {
}
DUMP_HDR("=== DUMP ALL CONNS for instance node=0x%llx ===",
(unsigned long long)instance->node_id);
struct ETCP_CONN* conn = instance->connections;
struct ll_entry* entry = instance->connections->head;
int idx = 0;
while (conn) {
DUMP_HDR("--- CONN %d ---", idx);
etcp_dump_conn_state(conn);
conn = conn->next;
idx++;
while (entry) {
struct conn_queue_entry* ce = (struct conn_queue_entry*)entry->data;
if (ce && ce->conn) {
DUMP_HDR("--- CONN %d ---", idx);
etcp_dump_conn_state(ce->conn);
idx++;
}
entry = entry->next;
}
if (idx == 0) DUMP_HDR("--- NO CONNECTIONS ---");
DUMP_HDR("=== END DUMP ALL ===");
+4 -3
View File
@@ -152,9 +152,10 @@ void ntp_node_sync_peers(struct UTUN_INSTANCE* inst) {
if (!inst || !ntp_time_is_synced(inst)) return;
int sent = 0;
for (struct ETCP_CONN* conn = inst->connections; conn; conn = conn->next) {
if (!conn->initialized || !conn->links_up) continue;
if (send_time_sync(inst, conn->peer_node_id) == 0) sent++;
for (struct ll_entry* entry = inst->connections->head; entry; entry = entry->next) {
struct conn_queue_entry* ce = (struct conn_queue_entry*)entry->data;
if (!ce->conn->initialized || !ce->conn->links_up) continue;
if (send_time_sync(inst, ce->conn->peer_node_id) == 0) sent++;
}
DEBUG_INFO(DEBUG_CATEGORY_GENERAL, "NTP_node: broadcast TIME_SYNC to %d peers", sent);
+3 -3
View File
@@ -366,8 +366,8 @@ void topo_group_new_conn(struct TOPO_GROUP* group, struct ETCP_CONN* conn) {
topo_group_add_to_senders(group, conn);
if (peer_nq && peer_nq->alien) { DEBUG_INFO(DEBUG_CATEGORY_BGP, "peer 0x%016llx is alien, skipping route exchange", (unsigned long long)conn->peer_node_id); return; }
struct ETCP_CONN* c = conn->instance->connections;
while (c) { struct ETCP_LINK* l = c->links; while (l) { if (l->initialized && l->conn && l->nat_check_status < NAT_CHECK_IN_PROGRESS) topo_group_start_link_nat_check(group, l); l = l->next; } c = c->next; }
struct ll_entry* entry = conn->instance->connections->head;
while (entry) { struct conn_queue_entry* ce = (struct conn_queue_entry*)entry->data; struct ETCP_LINK* l = ce->conn->links; while (l) { if (l->initialized && l->conn && l->nat_check_status < NAT_CHECK_IN_PROGRESS) topo_group_start_link_nat_check(group, l); l = l->next; } entry = entry->next; }
topo_group_send_table_request(group, conn);
}
@@ -839,7 +839,7 @@ void topo_group_send_nat_check_req(struct ETCP_CONN* conn, uint8_t socket_id) {
void topo_group_request_nat_check_all(struct TOPO_GROUP* group) {
if (!group || !group->instance) return;
int count = 0;
struct ETCP_CONN* conn = group->instance->connections; while (conn) { struct ETCP_LINK* link = conn->links; while (link) { if (link->nat_check_status < NAT_CHECK_IN_PROGRESS) topo_group_start_link_nat_check(group, link); count++; link = link->next; } conn = conn->next; }
struct ll_entry* entry = group->instance->connections->head; while (entry) { struct conn_queue_entry* ce = (struct conn_queue_entry*)entry->data; struct ETCP_LINK* link = ce->conn->links; while (link) { if (link->nat_check_status < NAT_CHECK_IN_PROGRESS) topo_group_start_link_nat_check(group, link); count++; link = link->next; } entry = entry->next; }
}
void topo_group_send_withdraw(struct TOPO_GROUP* group, uint64_t node_id) {
+27 -22
View File
@@ -106,6 +106,9 @@ static int instance_init_common(struct UTUN_INSTANCE* instance, struct UASYNC* u
DEBUG_ERROR(DEBUG_CATEGORY_CONFIG, "Failed to create networks queue");
return -1;
}
// Initialize connection queue
instance->connections = queue_new(ua, 256, 0, 8, "connections");
if (!instance->connections) { DEBUG_ERROR(DEBUG_CATEGORY_ETCP, "Failed to create connections queue"); return -1; }
struct CFG_NETWORK *net = config->networks;
while (net) {
struct ll_entry *entry = queue_entry_new(sizeof(struct NETWORK_ENTRY));
@@ -397,17 +400,17 @@ void utun_instance_destroy(struct UTUN_INSTANCE *instance) {
instance->etcp_sockets = NULL;
DEBUG_INFO(DEBUG_CATEGORY_MEMORY, "[INSTANCE_DESTROY] ETCP sockets cleanup complete");
// Safe cleanup of ETCP connections with NULL pointer checks
struct ETCP_CONN* conn = instance->connections;
while (conn) {
struct ETCP_CONN* next = conn->next;
DEBUG_INFO(DEBUG_CATEGORY_MEMORY, "[INSTANCE_DESTROY] Closing connection %p", conn);
if (conn) { // Дополнительная проверка на случай поврежденного списка
etcp_connection_close(conn); // Закрыть соединение (с проверкой NULL внутри)
// Cleanup ETCP connections (phase 1 detach + deferred phase 2 via call_soon)
{
struct ll_entry* entry = instance->connections->head;
while (entry) {
struct ll_entry* next = entry->next;
struct conn_queue_entry* ce = (struct conn_queue_entry*)entry->data;
if (ce && ce->conn) etcp_connection_close(ce->conn);
entry = next;
}
conn = next;
}
instance->connections = NULL;
queue_free(instance->connections); instance->connections = NULL;
DEBUG_INFO(DEBUG_CATEGORY_MEMORY, "[INSTANCE_DESTROY] ETCP connections cleanup complete");
struct PING_CONTEXT* p = instance->pending_pings;
@@ -578,7 +581,7 @@ int utun_instance_init(struct UTUN_INSTANCE *instance) {
return -1;
}
DEBUG_DEBUG(DEBUG_CATEGORY_ETCP, "Connections initialized successfully, count=%d", instance->connections_count);
DEBUG_DEBUG(DEBUG_CATEGORY_ETCP, "Connections initialized successfully, count=%d", queue_entry_count(instance->connections));
// Initialize control server if configured
if (instance->config->global.control_sock.ss_family != 0) {
@@ -654,16 +657,15 @@ void utun_instance_diagnose_leaks(struct UTUN_INSTANCE *instance, const char *ph
}
// Подсчёт ETCP соединений
struct ETCP_CONN *conn = instance->connections;
while (conn) {
report.etcp_connections_count++;
// Подсчёт линков в соединениях
struct ETCP_LINK *link = conn->links;
while (link) {
report.etcp_links_count++;
link = link->next;
{
struct ll_entry* entry = instance->connections->head;
while (entry) {
struct conn_queue_entry* ce = (struct conn_queue_entry*)entry->data;
report.etcp_connections_count++;
struct ETCP_LINK *link = ce->conn->links;
while (link) { report.etcp_links_count++; link = link->next; }
entry = entry->next;
}
conn = conn->next;
}
DEBUG_INFO(DEBUG_CATEGORY_MEMORY, "[LEAK DIAGNOSIS] Phase: %s", phase);
@@ -788,9 +790,12 @@ struct UTUN_INSTANCE *utun_instance_reload(struct UTUN_INSTANCE *instance, struc
// Clients [client:] and links (link=)
for (struct CFG_CLIENT *nc = new_config->clients; nc; nc = nc->next) {
struct ETCP_CONN *conn = NULL;
for (struct ETCP_CONN *c = instance->connections; c; c = c->next) {
if (strcmp(nc->name, c->name) == 0) {
conn = c; break;
{
struct ll_entry* entry = instance->connections->head;
while (entry) {
struct conn_queue_entry* ce = (struct conn_queue_entry*)entry->data;
if (strcmp(nc->name, ce->conn->name) == 0) { conn = ce->conn; break; }
entry = entry->next;
}
}
if (!conn) {
+8 -4
View File
@@ -52,6 +52,12 @@ struct NETWORK_ENTRY {
char name[64];
};
// Queue entry for instance->connections (indexed by peer_node_id)
struct conn_queue_entry {
uint64_t peer_node_id;
struct ETCP_CONN* conn;
};
// uTun instance configuration
struct UTUN_INSTANCE {
// Identification
@@ -87,9 +93,8 @@ struct UTUN_INSTANCE {
// State
int running;
// Connections (список всех подключений для instance)
struct ETCP_CONN* connections;// linked-list
int connections_count; // Number of connections
// Connections (очередь всех подключений для instance)
struct ll_queue* connections; // indexed by peer_node_id (0=pending), data=conn_queue_entry
// Callback chain for new ETCP connections
struct etcp_cbk_entry* new_conn_cbks;
@@ -135,7 +140,6 @@ struct UTUN_INSTANCE {
// etcp_router bindings и seq-connections (per-instance service routing)
struct ETCP_ROUTER_BINDINGS router_bindings;
struct ll_queue* router_conns;
struct ll_queue* chat_connections; // chat P2P connections indexed by node_id
struct CONN_MGR* conn_mgr; // Connection Manager (может быть NULL)
struct DB_SYNC* db_sync; // Distributed DB sync (может быть NULL)