Browse Source

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)
topo_upd
Evgeny 3 months ago
parent
commit
a7ecee01f5
  1. 30
      src/control_server.c
  2. 47
      src/db_sync.c
  3. 218
      src/etcp.c
  4. 33
      src/etcp.h
  5. 13
      src/etcp_connect.c
  6. 47
      src/etcp_connections.c
  7. 11
      src/etcp_dump.c
  8. 7
      src/ntp_node_time.c
  9. 6
      src/topo_group.c
  10. 47
      src/utun_instance.c
  11. 12
      src/utun_instance.h
  12. 55
      tests/bbr_integration/test_bbr_integration.c
  13. 23
      tests/test_bgp_route_exchange.c
  14. 22
      tests/test_bgp_triangle.c
  15. 7
      tests/test_conn_mgr.c
  16. 7
      tests/test_db_sync.c
  17. 18
      tests/test_etcp_100_packets.c
  18. 14
      tests/test_etcp_api.c
  19. 16
      tests/test_etcp_congestion.c
  20. 7
      tests/test_etcp_connect.c
  21. 8
      tests/test_etcp_dummynet.c
  22. 18
      tests/test_etcp_ping.c
  23. 29
      tests/test_etcp_reconnect.c
  24. 26
      tests/test_etcp_reinit_inflight.c
  25. 8
      tests/test_etcp_router.c
  26. 7
      tests/test_etcp_router_reconnect.c
  27. 14
      tests/test_etcp_simple_traffic.c
  28. 16
      tests/test_etcp_two_instances.c
  29. 10
      tests/test_icmp_proxy.c
  30. 10
      tests/test_ipv6_sockets.c
  31. 41
      tests/test_nat_detection.c
  32. 13
      tests/test_nat_transport.c
  33. 20
      tests/test_pkt_normalizer_etcp.c
  34. 20
      tests/test_route_ping.c
  35. 9
      tests/test_socks_http_proxy.c
  36. 7
      tests/test_stcp_traffic.c
  37. 10
      tests/test_tcp_proxy_remote.c
  38. 10
      tests/test_udp_proxy.c
  39. 17
      tools/chatgui/transport/chat_core.c
  40. 74
      tools/chatgui/transport/chat_sync.c

30
src/control_server.c

@ -885,10 +885,10 @@ static void send_conn_list(struct control_server* server, struct control_client*
/* Count connections */ /* Count connections */
uint8_t count = 0; uint8_t count = 0;
struct ETCP_CONN* conn = instance->connections; struct ll_entry* entry = instance->connections->head;
while (conn && count < ETCPMON_MAX_CONNECTIONS) { while (entry && count < ETCPMON_MAX_CONNECTIONS) {
count++; count++;
conn = conn->next; entry = entry->next;
} }
/* Build response */ /* Build response */
@ -909,12 +909,13 @@ static void send_conn_list(struct control_server* server, struct control_client*
rsp->count = count; rsp->count = count;
struct etcpmon_conn_info* info = (struct etcpmon_conn_info*)(buffer + sizeof(*hdr) + sizeof(*rsp)); struct etcpmon_conn_info* info = (struct etcpmon_conn_info*)(buffer + sizeof(*hdr) + sizeof(*rsp));
conn = instance->connections; entry = instance->connections->head;
for (uint8_t i = 0; i < count && conn; i++) { for (uint8_t i = 0; i < count && entry; i++) {
info[i].peer_node_id = conn->peer_node_id; struct conn_queue_entry* ce = (struct conn_queue_entry*)entry->data;
strncpy(info[i].name, conn->log_name, ETCPMON_MAX_CONN_NAME - 1); 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'; info[i].name[ETCPMON_MAX_CONN_NAME - 1] = '\0';
conn = conn->next; entry = entry->next;
} }
/* Log and send response */ /* 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, static struct ETCP_CONN* find_connection_by_peer_id(struct UTUN_INSTANCE* instance,
uint64_t peer_id) { uint64_t peer_id) {
struct ETCP_CONN* conn = instance->connections; if (!instance || !instance->connections) return NULL;
while (conn) { struct ll_entry* e = queue_find_data_by_index(instance->connections, (const uint8_t*)&peer_id);
if (conn->peer_node_id == peer_id) { if (!e) return NULL;
return conn; struct conn_queue_entry* ce = (struct conn_queue_entry*)e->data;
} return ce->conn;
conn = conn->next;
}
return NULL;
} }
/* ============================================================================ /* ============================================================================

47
src/db_sync.c

@ -488,9 +488,9 @@ static int db_sync_send_hash(struct DB_SYNC* db, uint64_t node_id,
entry->len = plen + 9; entry->len = plen + 9;
struct ETCP_CONN* conn = NULL; 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); struct ll_entry* e = queue_find_data_by_index(db->inst->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; } if (e) { struct conn_queue_entry* ce = (struct conn_queue_entry*)e->data; if (ce->conn->links_up) conn = ce->conn; }
} }
if (!conn) { if (!conn) {
DEBUG_WARN(DEBUG_CATEGORY_DB_SYNC, 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); etcp_set_new_conn_cbk(inst, db_sync_on_new_conn, NULL);
// Attach to existing connections // Attach to existing connections
struct ETCP_CONN* conn = inst->connections; {
while (conn) { struct ll_entry* entry = inst->connections->head;
etcp_conn_add_up_cbk(conn, db_sync_on_conn_up, NULL); while (entry) {
etcp_conn_add_down_cbk(conn, db_sync_on_conn_down, NULL); struct conn_queue_entry* ce = (struct conn_queue_entry*)entry->data;
conn = conn->next; 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) if (db->enabled)
@ -1474,11 +1479,16 @@ void db_sync_destroy(struct UTUN_INSTANCE* inst)
} }
// Detach from existing connections // Detach from existing connections
struct ETCP_CONN* conn = inst->connections; {
while (conn) { struct ll_entry* entry = inst->connections->head;
etcp_conn_remove_up_cbk(conn, db_sync_on_conn_up, NULL); while (entry) {
etcp_conn_remove_down_cbk(conn, db_sync_on_conn_down, NULL); struct conn_queue_entry* ce = (struct conn_queue_entry*)entry->data;
conn = conn->next; 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); 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 // Initiate sync with already connected peers
{ {
struct ETCP_CONN* conn = inst->connections; struct ll_entry* entry = inst->connections->head;
while (conn) { while (entry) {
uint64_t pid = conn->peer_node_id; 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 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); struct SI_PEER* p = si_peer_add(si, pid);
if (p && p->synced == 0) { 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); db_sync_initiate_sync(si, pid);
} }
} }
conn = conn->next; entry = entry->next;
} }
} }

218
src/etcp.c

@ -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 wait_ack_cb(struct ll_queue* q, void* arg);
static void send_ack_req_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_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); struct ETCP_DGRAM* etcp_request_pkt(struct ETCP_CONN* etcp);
static void clear_queue(struct ll_queue* q) { 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->links_up=0;
etcp->reset_done=0; etcp->reset_done=0;
etcp->callbacks_running=0; etcp->callbacks_running=0;
etcp->ref_count=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)
@ -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; 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); etcp->log_name, etcp, etcp->mtu, etcp->next_tx_id);
// Вызываем callback для нового соединения если установлен // Вызываем callback для нового соединения если установлен
@ -286,57 +300,15 @@ static void etcp_on_down(struct ETCP_CONN* etcp) {
} }
// Close connection with NULL pointer safety (prevents double free) // Phase 2 resources cleanup (callable both sync and async via call_soon)
void etcp_connection_close(struct ETCP_CONN* etcp) { static void etcp_connection_free_resources(struct ETCP_CONN* etcp) {
DEBUG_TRACE(DEBUG_CATEGORY_ETCP, "");
if (!etcp) return; if (!etcp) return;
DEBUG_DEBUG(DEBUG_CATEGORY_ETCP, "[%s] freeing resources phase 2", etcp->log_name);
if (etcp->callbacks_running) { // Close links
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.
if (etcp->links) { if (etcp->links) {
struct ETCP_LINK* link = etcp->links; struct ETCP_LINK* link = etcp->links;
while (link) { while (link) { struct ETCP_LINK* next = link->next; etcp_link_close(link); link = next; }
struct ETCP_LINK* next = link->next;
etcp_link_close(link);
link = next;
}
etcp->links = NULL; etcp->links = NULL;
} }
@ -353,32 +325,83 @@ void etcp_connection_close(struct ETCP_CONN* etcp) {
{ 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->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->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->inflight_pool) { if (etcp->io_pool) { memory_pool_destroy(etcp->io_pool); etcp->io_pool = NULL; }
memory_pool_destroy(etcp->inflight_pool);
etcp->inflight_pool = NULL; u_free(etcp->name);
} u_free(etcp);
}
if (etcp->io_pool) { static void etcp_connection_free_deferred(void* arg) {
memory_pool_destroy(etcp->io_pool); etcp_connection_free_resources((struct ETCP_CONN*)arg);
etcp->io_pool = NULL; }
// 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;
} }
u_free(etcp->name); // === PHASE 1: detach from external world ===
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; }
{ struct UTUN_INSTANCE* inst = etcp->instance; // Close links (detach from socket scan queues)
if (inst && inst->connections) { if (etcp->links) {
struct ETCP_CONN** pp = &inst->connections; struct ETCP_LINK* link = etcp->links;
while (*pp && *pp != etcp) pp = &(*pp)->next; while (link) { struct ETCP_LINK* next = link->next; etcp_link_close(link); link = next; }
if (*pp) { *pp = etcp->next; if (inst->connections_count) inst->connections_count--; } 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;
} }
// Clear next pointer to prevent dangling references etcp->state = 2; // deleted
etcp->next = NULL;
// TODO: Free rx_list, etc. // === PHASE 2: deferred resource cleanup (only if no outstanding refs) ===
u_free(etcp); 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 // Reset connection
@ -543,24 +566,77 @@ void etcp_conn_reinit(struct ETCP_CONN* etcp) {// Если сбой в обме
// внутренняя функция. Вызывается один раз когда первый линк готов. // внутренняя функция. Вызывается один раз когда первый линк готов.
void etcp_conn_ready(struct ETCP_CONN* conn) { void etcp_conn_ready(struct ETCP_CONN* conn) {
if (!conn) return; if (!conn) return;
if (conn->initialized) return; // already ready
conn->initialized = 1; conn->initialized = 1;
conn->reset_done = 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); 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); etcp_metrics_start_timer(conn);
// Вызываем callback если установлен
conn->callbacks_running = 1; 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; } } { 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); if (conn->links_up) etcp_on_up(conn);
conn->callbacks_running = 0; 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 // Update log_name when peer_node_id becomes known
void etcp_update_log_name(struct ETCP_CONN* etcp) { void etcp_update_log_name(struct ETCP_CONN* etcp) {

33
src/etcp.h

@ -138,11 +138,19 @@ struct ACK_PACKET {
// ETCP connection structure (refactored) // ETCP connection structure (refactored)
struct ETCP_CONN { 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; int mtu;
struct UTUN_INSTANCE* instance; 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 // Links (channels) - linked list
struct ETCP_LINK* links; struct ETCP_LINK* links;
struct ETCP_LINK* last_rr_link; // последний линк, выбранный round-robin struct ETCP_LINK* last_rr_link; // последний линк, выбранный round-robin
@ -252,6 +260,29 @@ struct ETCP_CONN {
// Functions // Functions
struct ETCP_CONN* etcp_connection_create(struct UTUN_INSTANCE* instance, char* name); struct ETCP_CONN* etcp_connection_create(struct UTUN_INSTANCE* instance, char* name);
void etcp_connection_close(struct ETCP_CONN* etcp); 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_conn_reset(struct ETCP_CONN* etcp);
void etcp_links_reset(struct ETCP_CONN* etcp); void etcp_links_reset(struct ETCP_CONN* etcp);

13
src/etcp_connect.c

@ -210,9 +210,13 @@ int etcp_connect(struct UTUN_INSTANCE* inst, struct TOPO_NODEQ* node,
if (!inst || !node || !cb) return -1; if (!inst || !node || !cb) return -1;
uint64_t node_id = node->node->node_id; uint64_t node_id = node->node->node_id;
struct ETCP_CONN* existing = inst->connections; // Check if already connected via indexed queue
while (existing) { {
if (existing->peer_node_id == node_id) { 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; struct ETCP_LINK* l = existing->links;
while (l) { if (l->link_state == 3) break; l = l->next; } while (l) { if (l->link_state == 3) break; l = l->next; }
if (l) { if (l) {
@ -225,7 +229,7 @@ int etcp_connect(struct UTUN_INSTANCE* inst, struct TOPO_NODEQ* node,
return 0; return 0;
} }
} }
existing = existing->next; }
} }
struct ETCP_CONNECT* ctx = connect_find(inst, node_id); 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); 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", 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; } (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) { 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); 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; etcp_connection_close(conn); cb(arg, NULL, 0); return -1;

47
src/etcp_connections.c

@ -32,18 +32,17 @@
// TCP server: on new incoming connection → create minimal ETCP_CONN // TCP server: on new incoming connection → create minimal ETCP_CONN
static void tcp_server_on_link(struct stcp_link *link, void *arg) { static void tcp_server_on_link(struct stcp_link *link, void *arg) {
struct UTUN_INSTANCE *inst = (struct UTUN_INSTANCE *)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; if (!conn) return;
conn->instance = inst;
conn->transport_link = link; conn->transport_link = link;
snprintf(conn->log_name, sizeof(conn->log_name), "tcp-[%p]", (void*)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; 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; } 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; } } { 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 // 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); // 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_conn_set_peer_node_id(link->etcp, server_node_id);
etcp_update_log_name(link->etcp); // Update log_name with peer_node_id
link->initialized = 1;// получен init response (client) link->initialized = 1;// получен init response (client)
link->link_state = 3; // connected link->link_state = 3; // connected
@ -1678,10 +1676,10 @@ 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], 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); src_port, session_id, sockaddr_storage_to_str(&addr).str);
struct ETCP_CONN* conn=e_sock->instance->connections; struct ETCP_CONN* conn = NULL;
while (conn) {// ищем есть ли подключение к этому пиру {
if (conn->peer_node_id==peer_id) break; struct ll_entry* e = queue_find_data_by_index(e_sock->instance->connections, (const uint8_t*)&peer_id);
conn=conn->next; if (e) { struct conn_queue_entry* ce = (struct conn_queue_entry*)e->data; conn = ce->conn; }
} }
int new_conn=0; int new_conn=0;
@ -1690,14 +1688,11 @@ void etcp_connections_read_callback_socket(socket_t sock, void* arg) {
conn=etcp_connection_create(e_sock->instance,""); conn=etcp_connection_create(e_sock->instance,"");
if (!conn) { errorcode=55; DEBUG_ERROR(DEBUG_CATEGORY_CONNECTION, "failed to create connection"); goto ec_fr; } if (!conn) { errorcode=55; DEBUG_ERROR(DEBUG_CATEGORY_CONNECTION, "failed to create connection"); goto ec_fr; }
memcpy(&conn->crypto_ctx, &sc, sizeof(sc)); memcpy(&conn->crypto_ctx, &sc, sizeof(sc));
conn->peer_node_id=peer_id; etcp_conn_set_peer_node_id(conn, peer_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_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); DEBUG_DEBUG(DEBUG_CATEGORY_CONNECTION, "New connection from %s peer_id=%ld etcp=%p total=%d",
conn->next = e_sock->instance->connections; ip_to_str(&addr, addr.ss_family).str, peer_id, conn,
e_sock->instance->connections = conn; queue_entry_count(e_sock->instance->connections));
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);
} }
else {// check keys если существующее подключение 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 совпал а ключи разные. 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 // Initialize clients - create outgoing connections
struct CFG_CLIENT* client = config->clients; struct CFG_CLIENT* client = config->clients;
DEBUG_DEBUG(DEBUG_CATEGORY_CONNECTION, "init_connections called, instance=%p, config=%p, clients=%p, connections_count=%d", DEBUG_DEBUG(DEBUG_CATEGORY_CONNECTION, "init_connections called, instance=%p, config=%p, clients=%p, total_conns=%d",
instance, config, config ? config->clients : NULL, instance ? instance->connections_count : -1); instance, config, config ? config->clients : NULL, instance ? queue_entry_count(instance->connections) : -1);
while (client) { while (client) {
// Check if client has required configuration // Check if client has required configuration
DEBUG_INFO(DEBUG_CATEGORY_CONNECTION, "Client %s - keepalive=%d, links=%p, peer_key_len=%zu", 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; 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; client = client->next;
} }
// If there are clients configured but no connections created, that's an error // 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 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"); DEBUG_ERROR(DEBUG_CATEGORY_ETCP, "Clients configured but no connections initialized");
return -1; 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 // Return 1 if there was a partial socket initialization error
if (socket_result == 1) return 1; if (socket_result == 1) return 1;
return 0; return 0;

11
src/etcp_dump.c

@ -233,14 +233,17 @@ void etcp_dump_all_conns(struct UTUN_INSTANCE* instance) {
} }
DUMP_HDR("=== DUMP ALL CONNS for instance node=0x%llx ===", DUMP_HDR("=== DUMP ALL CONNS for instance node=0x%llx ===",
(unsigned long long)instance->node_id); (unsigned long long)instance->node_id);
struct ETCP_CONN* conn = instance->connections; struct ll_entry* entry = instance->connections->head;
int idx = 0; int idx = 0;
while (conn) { while (entry) {
struct conn_queue_entry* ce = (struct conn_queue_entry*)entry->data;
if (ce && ce->conn) {
DUMP_HDR("--- CONN %d ---", idx); DUMP_HDR("--- CONN %d ---", idx);
etcp_dump_conn_state(conn); etcp_dump_conn_state(ce->conn);
conn = conn->next;
idx++; idx++;
} }
entry = entry->next;
}
if (idx == 0) DUMP_HDR("--- NO CONNECTIONS ---"); if (idx == 0) DUMP_HDR("--- NO CONNECTIONS ---");
DUMP_HDR("=== END DUMP ALL ==="); DUMP_HDR("=== END DUMP ALL ===");
} }

7
src/ntp_node_time.c

@ -152,9 +152,10 @@ void ntp_node_sync_peers(struct UTUN_INSTANCE* inst) {
if (!inst || !ntp_time_is_synced(inst)) return; if (!inst || !ntp_time_is_synced(inst)) return;
int sent = 0; int sent = 0;
for (struct ETCP_CONN* conn = inst->connections; conn; conn = conn->next) { for (struct ll_entry* entry = inst->connections->head; entry; entry = entry->next) {
if (!conn->initialized || !conn->links_up) continue; struct conn_queue_entry* ce = (struct conn_queue_entry*)entry->data;
if (send_time_sync(inst, conn->peer_node_id) == 0) sent++; 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); DEBUG_INFO(DEBUG_CATEGORY_GENERAL, "NTP_node: broadcast TIME_SYNC to %d peers", sent);

6
src/topo_group.c

@ -366,8 +366,8 @@ void topo_group_new_conn(struct TOPO_GROUP* group, struct ETCP_CONN* conn) {
topo_group_add_to_senders(group, 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; } 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; struct ll_entry* entry = conn->instance->connections->head;
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; } 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); 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) { void topo_group_request_nat_check_all(struct TOPO_GROUP* group) {
if (!group || !group->instance) return; if (!group || !group->instance) return;
int count = 0; 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) { void topo_group_send_withdraw(struct TOPO_GROUP* group, uint64_t node_id) {

47
src/utun_instance.c

@ -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"); DEBUG_ERROR(DEBUG_CATEGORY_CONFIG, "Failed to create networks queue");
return -1; 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; struct CFG_NETWORK *net = config->networks;
while (net) { while (net) {
struct ll_entry *entry = queue_entry_new(sizeof(struct NETWORK_ENTRY)); 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; instance->etcp_sockets = NULL;
DEBUG_INFO(DEBUG_CATEGORY_MEMORY, "[INSTANCE_DESTROY] ETCP sockets cleanup complete"); DEBUG_INFO(DEBUG_CATEGORY_MEMORY, "[INSTANCE_DESTROY] ETCP sockets cleanup complete");
// Safe cleanup of ETCP connections with NULL pointer checks // Cleanup ETCP connections (phase 1 detach + deferred phase 2 via call_soon)
struct ETCP_CONN* conn = instance->connections; {
while (conn) { struct ll_entry* entry = instance->connections->head;
struct ETCP_CONN* next = conn->next; while (entry) {
DEBUG_INFO(DEBUG_CATEGORY_MEMORY, "[INSTANCE_DESTROY] Closing connection %p", conn); struct ll_entry* next = entry->next;
if (conn) { // Дополнительная проверка на случай поврежденного списка struct conn_queue_entry* ce = (struct conn_queue_entry*)entry->data;
etcp_connection_close(conn); // Закрыть соединение (с проверкой NULL внутри) 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"); DEBUG_INFO(DEBUG_CATEGORY_MEMORY, "[INSTANCE_DESTROY] ETCP connections cleanup complete");
struct PING_CONTEXT* p = instance->pending_pings; struct PING_CONTEXT* p = instance->pending_pings;
@ -578,7 +581,7 @@ int utun_instance_init(struct UTUN_INSTANCE *instance) {
return -1; 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 // Initialize control server if configured
if (instance->config->global.control_sock.ss_family != 0) { 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 соединений // Подсчёт ETCP соединений
struct ETCP_CONN *conn = instance->connections; {
while (conn) { struct ll_entry* entry = instance->connections->head;
while (entry) {
struct conn_queue_entry* ce = (struct conn_queue_entry*)entry->data;
report.etcp_connections_count++; report.etcp_connections_count++;
// Подсчёт линков в соединениях struct ETCP_LINK *link = ce->conn->links;
struct ETCP_LINK *link = conn->links; while (link) { report.etcp_links_count++; link = link->next; }
while (link) { entry = entry->next;
report.etcp_links_count++;
link = link->next;
} }
conn = conn->next;
} }
DEBUG_INFO(DEBUG_CATEGORY_MEMORY, "[LEAK DIAGNOSIS] Phase: %s", phase); 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=) // Clients [client:] and links (link=)
for (struct CFG_CLIENT *nc = new_config->clients; nc; nc = nc->next) { for (struct CFG_CLIENT *nc = new_config->clients; nc; nc = nc->next) {
struct ETCP_CONN *conn = NULL; struct ETCP_CONN *conn = NULL;
for (struct ETCP_CONN *c = instance->connections; c; c = c->next) { {
if (strcmp(nc->name, c->name) == 0) { struct ll_entry* entry = instance->connections->head;
conn = c; break; 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) { if (!conn) {

12
src/utun_instance.h

@ -52,6 +52,12 @@ struct NETWORK_ENTRY {
char name[64]; 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 // uTun instance configuration
struct UTUN_INSTANCE { struct UTUN_INSTANCE {
// Identification // Identification
@ -87,9 +93,8 @@ struct UTUN_INSTANCE {
// State // State
int running; int running;
// Connections (список всех подключений для instance) // Connections (очередь всех подключений для instance)
struct ETCP_CONN* connections;// linked-list struct ll_queue* connections; // indexed by peer_node_id (0=pending), data=conn_queue_entry
int connections_count; // Number of connections
// Callback chain for new ETCP connections // Callback chain for new ETCP connections
struct etcp_cbk_entry* new_conn_cbks; struct etcp_cbk_entry* new_conn_cbks;
@ -135,7 +140,6 @@ struct UTUN_INSTANCE {
// etcp_router bindings и seq-connections (per-instance service routing) // etcp_router bindings и seq-connections (per-instance service routing)
struct ETCP_ROUTER_BINDINGS router_bindings; struct ETCP_ROUTER_BINDINGS router_bindings;
struct ll_queue* router_conns; 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 CONN_MGR* conn_mgr; // Connection Manager (может быть NULL)
struct DB_SYNC* db_sync; // Distributed DB sync (может быть NULL) struct DB_SYNC* db_sync; // Distributed DB sync (может быть NULL)

55
tests/bbr_integration/test_bbr_integration.c

@ -187,7 +187,9 @@ static void send_wtr_cb(struct ll_queue* q, void* arg) {
static void send_burst(struct test_ctx* ctx) { static void send_burst(struct test_ctx* ctx) {
if (ctx->test_done) return; if (ctx->test_done) return;
struct ETCP_CONN* conn = ctx->sender->connections; struct ETCP_CONN* conn = NULL;
if (ctx->sender->connections && ctx->sender->connections->head)
conn = ((struct conn_queue_entry*)ctx->sender->connections->head->data)->conn;
if (!conn || !conn->initialized) return; if (!conn || !conn->initialized) return;
if (conn->normalizer && conn->normalizer->input) { if (conn->normalizer && conn->normalizer->input) {
@ -241,9 +243,11 @@ static void print_metrics(struct test_ctx* ctx) {
double pace_kbps = 0; double pace_kbps = 0;
double rtt_ms = 0; double rtt_ms = 0;
if (ctx->sender->connections && ctx->sender->connections->links) { struct ETCP_CONN* c = NULL;
struct ETCP_LINK* l = ctx->sender->connections->links; if (ctx->sender->connections && ctx->sender->connections->head)
struct ETCP_CONN* c = ctx->sender->connections; c = ((struct conn_queue_entry*)ctx->sender->connections->head->data)->conn;
if (c && c->links) {
struct ETCP_LINK* l = c->links;
lk_status = l->link_status ? " UP" : " DN"; lk_status = l->link_status ? " UP" : " DN";
lk_reinit = c->reinit_count; lk_reinit = c->reinit_count;
lk_rtrns = l->total_retransmissions; lk_rtrns = l->total_retransmissions;
@ -376,11 +380,14 @@ int main(void) {
/* Wait for connection */ /* Wait for connection */
printf("Waiting for connection...\n"); printf("Waiting for connection...\n");
struct ETCP_CONN* conn = NULL;
uint64_t t0 = now_us(); uint64_t t0 = now_us();
while ((now_us() - t0) < 10000000ULL) { while ((now_us() - t0) < 10000000ULL) {
uasync_poll(ctx.ua, 1); uasync_poll(ctx.ua, 1);
if (ctx.sender->connections && ctx.sender->connections->links) { if (ctx.sender->connections && ctx.sender->connections->head)
struct ETCP_LINK* l = ctx.sender->connections->links; conn = ((struct conn_queue_entry*)ctx.sender->connections->head->data)->conn;
if (conn && conn->links) {
struct ETCP_LINK* l = conn->links;
if (l->initialized && l->link_status == 1) break; if (l->initialized && l->link_status == 1) break;
} }
} }
@ -388,9 +395,9 @@ int main(void) {
t0 = now_us(); t0 = now_us();
while ((now_us() - t0) < 500000ULL) uasync_poll(ctx.ua, 1); while ((now_us() - t0) < 500000ULL) uasync_poll(ctx.ua, 1);
if (ctx.sender->connections && ctx.sender->connections->normalizer && ctx.sender->connections->normalizer->input) { if (conn && conn->normalizer && conn->normalizer->input) {
queue_set_threshold(ctx.sender->connections->normalizer->input, 0, 0); queue_set_threshold(conn->normalizer->input, 0, 0);
queue_set_waiter_defer(ctx.sender->connections->normalizer->input, 1); queue_set_waiter_defer(conn->normalizer->input, 1);
} }
/* Start test */ /* Start test */
@ -416,10 +423,15 @@ int main(void) {
const struct dummynet_stats* ds = dummynet_get_stats(ctx.dn, DUMMYNET_FORWARD); const struct dummynet_stats* ds = dummynet_get_stats(ctx.dn, DUMMYNET_FORWARD);
const char* lks = "?"; const char* lks = "?";
uint32_t rein = 0, rtrns = 0; uint32_t rein = 0, rtrns = 0;
if (ctx.sender->connections && ctx.sender->connections->links) { {
lks = ctx.sender->connections->links->link_status ? "UP" : "DN"; struct ETCP_CONN* c = NULL;
rein = ctx.sender->connections->reinit_count; if (ctx.sender->connections && ctx.sender->connections->head)
rtrns = ctx.sender->connections->links->total_retransmissions; c = ((struct conn_queue_entry*)ctx.sender->connections->head->data)->conn;
if (c && c->links) {
lks = c->links->link_status ? "UP" : "DN";
rein = c->reinit_count;
rtrns = c->links->total_retransmissions;
}
} }
printf(" t=%lus Lk=%s Rein=%u Rtrns=%u recv=%lu dn_tx=%llu dn_lost=%llu\n", printf(" t=%lus Lk=%s Rein=%u Rtrns=%u recv=%lu dn_tx=%llu dn_lost=%llu\n",
(unsigned long)elapsed, lks, rein, rtrns, (unsigned long)ctx.bytes_received, (unsigned long)elapsed, lks, rein, rtrns, (unsigned long)ctx.bytes_received,
@ -455,8 +467,11 @@ int main(void) {
(unsigned long long)ds_bk->recv, (unsigned long long)ds_bk->sent, (unsigned long long)ds_bk->recv, (unsigned long long)ds_bk->sent,
(unsigned long long)ds_bk->lost, (unsigned long long)ds_bk->dropped); (unsigned long long)ds_bk->lost, (unsigned long long)ds_bk->dropped);
if (ctx.sender->connections) { {
struct ETCP_CONN* c = ctx.sender->connections; struct ETCP_CONN* c = NULL;
if (ctx.sender->connections && ctx.sender->connections->head)
c = ((struct conn_queue_entry*)ctx.sender->connections->head->data)->conn;
if (c) {
printf("ETCP: reinit=%u reset=%u links_up=%u\n", printf("ETCP: reinit=%u reset=%u links_up=%u\n",
c->reinit_count, c->reset_count, c->links_up); c->reinit_count, c->reset_count, c->links_up);
if (c->links) { if (c->links) {
@ -483,6 +498,7 @@ int main(void) {
} }
} }
} }
}
int pass = (ctx.bytes_received > 50000); int pass = (ctx.bytes_received > 50000);
printf("\n[%s]\n", pass ? "PASS" : "FAIL"); printf("\n[%s]\n", pass ? "PASS" : "FAIL");
@ -505,8 +521,13 @@ int main(void) {
} }
/* Cleanup */ /* Cleanup */
if (ctx.sender->connections && ctx.sender->connections->normalizer && ctx.sender->connections->normalizer->input) {
queue_waiter_cancel(ctx.sender->connections->normalizer->input, &ctx.waiter); struct ETCP_CONN* cc = NULL;
if (ctx.sender->connections && ctx.sender->connections->head)
cc = ((struct conn_queue_entry*)ctx.sender->connections->head->data)->conn;
if (cc && cc->normalizer && cc->normalizer->input)
queue_waiter_cancel(cc->normalizer->input, &ctx.waiter);
}
dummynet_destroy(ctx.dn); dummynet_destroy(ctx.dn);
utun_instance_destroy(ctx.sender); utun_instance_destroy(ctx.sender);
utun_instance_destroy(ctx.receiver); utun_instance_destroy(ctx.receiver);

23
tests/test_bgp_route_exchange.c

@ -195,17 +195,17 @@ static void cleanup_temp_configs(void) {
} }
static int has_initialized_link(struct UTUN_INSTANCE* inst) { static int has_initialized_link(struct UTUN_INSTANCE* inst) {
if (!inst) return 0; if (!inst || !inst->connections) return 0;
struct ETCP_CONN* conn = inst->connections; struct ll_entry* entry = inst->connections->head;
while (conn) { struct ETCP_LINK* l = conn->links; while (l) { if (l->initialized) return 1; l = l->next; } conn = conn->next; } 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) return 1; l = l->next; } entry = entry->next; }
return 0; return 0;
} }
static int count_initialized_links(struct UTUN_INSTANCE* inst) { static int count_initialized_links(struct UTUN_INSTANCE* inst) {
int n = 0; int n = 0;
if (!inst) return 0; if (!inst || !inst->connections) return 0;
struct ETCP_CONN* conn = inst->connections; struct ll_entry* entry = inst->connections->head;
while (conn) { struct ETCP_LINK* l = conn->links; while (l) { if (l->initialized) n++; l = l->next; } conn = conn->next; } 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) n++; l = l->next; } entry = entry->next; }
return n; return n;
} }
@ -221,10 +221,11 @@ static int check_learned_route(struct UTUN_INSTANCE* inst, uint32_t network, uin
} }
static struct ETCP_LINK* find_client_link(struct UTUN_INSTANCE* inst, int idx) { static struct ETCP_LINK* find_client_link(struct UTUN_INSTANCE* inst, int idx) {
if (!inst) return NULL; if (!inst || !inst->connections) return NULL;
struct ETCP_CONN* conn = inst->connections; struct ll_entry* entry = inst->connections->head;
while (conn) { while (entry) {
struct ETCP_LINK* l = conn->links; struct conn_queue_entry* ce = (struct conn_queue_entry*)entry->data;
struct ETCP_LINK* l = ce->conn->links;
while (l) { while (l) {
if (l->is_server == 0) { if (l->is_server == 0) {
if (idx == 0) return l; if (idx == 0) return l;
@ -232,7 +233,7 @@ static struct ETCP_LINK* find_client_link(struct UTUN_INSTANCE* inst, int idx) {
} }
l = l->next; l = l->next;
} }
conn = conn->next; entry = entry->next;
} }
return NULL; return NULL;
} }

22
tests/test_bgp_triangle.c

@ -162,26 +162,28 @@ static int route_count_for_node(struct UTUN_INSTANCE* inst, uint64_t node_id) {
static int count_initialized_links(struct UTUN_INSTANCE* inst) { static int count_initialized_links(struct UTUN_INSTANCE* inst) {
int n = 0; int n = 0;
if (!inst) return 0; if (!inst || !inst->connections) return 0;
struct ETCP_CONN* conn = inst->connections; struct ll_entry* entry = inst->connections->head;
while (conn) { while (entry) {
struct ETCP_LINK* l = conn->links; struct conn_queue_entry* ce = (struct conn_queue_entry*)entry->data;
struct ETCP_LINK* l = ce->conn->links;
while (l) { if (l->initialized) n++; l = l->next; } while (l) { if (l->initialized) n++; l = l->next; }
conn = conn->next; entry = entry->next;
} }
return n; return n;
} }
static struct ETCP_LINK* find_client_link(struct UTUN_INSTANCE* inst, int idx) { static struct ETCP_LINK* find_client_link(struct UTUN_INSTANCE* inst, int idx) {
if (!inst) return NULL; if (!inst || !inst->connections) return NULL;
struct ETCP_CONN* conn = inst->connections; struct ll_entry* entry = inst->connections->head;
while (conn) { while (entry) {
struct ETCP_LINK* l = conn->links; struct conn_queue_entry* ce = (struct conn_queue_entry*)entry->data;
struct ETCP_LINK* l = ce->conn->links;
while (l) { while (l) {
if (l->is_server == 0) { if (idx == 0) return l; idx--; } if (l->is_server == 0) { if (idx == 0) return l; idx--; }
l = l->next; l = l->next;
} }
conn = conn->next; entry = entry->next;
} }
return NULL; return NULL;
} }

7
tests/test_conn_mgr.c

@ -56,8 +56,11 @@ static char* gv(const char* p, const char* k) {
free_config(c); return r; free_config(c); return r;
} }
static int lks(struct UTUN_INSTANCE* i) { static int lks(struct UTUN_INSTANCE* i) {
int n = 0; struct ETCP_CONN* c = i->connections; int n = 0; struct ll_entry* entry = i->connections->head;
while (c) { struct ETCP_LINK* l = c->links; while (l) { if (l->initialized && l->link_status) n++; l = l->next; } c = c->next; } while (entry) { struct conn_queue_entry* ce = (struct conn_queue_entry*)entry->data;
struct ETCP_CONN* c = ce->conn;
struct ETCP_LINK* l = c->links; while (l) { if (l->initialized && l->link_status) n++; l = l->next; }
entry = entry->next; }
return n; return n;
} }
static void fail(const char* msg) { fprintf(stderr, "FAIL: %s\n", msg); fflush(stderr); result = 2; } static void fail(const char* msg) { fprintf(stderr, "FAIL: %s\n", msg); fflush(stderr); result = 2; }

7
tests/test_db_sync.c

@ -151,8 +151,11 @@ static void sleep_tb(int tb) {
// ---- Condition functions ---- // ---- Condition functions ----
static int cond_links_init(void) { static int cond_links_init(void) {
if (!inst_a || !inst_b) return 0; if (!inst_a || !inst_b) return 0;
struct ETCP_CONN* ca = inst_a->connections; struct ll_entry* entry = inst_a->connections->head;
while (ca) { struct ETCP_LINK* l = ca->links; while (l) { if (l->initialized) return 1; l = l->next; } ca = ca->next; } while (entry) { struct conn_queue_entry* ce = (struct conn_queue_entry*)entry->data;
struct ETCP_CONN* ca = ce->conn;
struct ETCP_LINK* l = ca->links; while (l) { if (l->initialized) return 1; l = l->next; }
entry = entry->next; }
return 0; return 0;
} }

18
tests/test_etcp_100_packets.c

@ -139,15 +139,17 @@ static void generate_packet_data(int seq, uint8_t* buffer, int size) {
// Check if connection is established // Check if connection is established
static int is_connection_established(struct UTUN_INSTANCE* inst) { static int is_connection_established(struct UTUN_INSTANCE* inst) {
if (!inst) return 0; if (!inst || !inst->connections) return 0;
struct ETCP_CONN* conn = inst->connections; struct ll_entry* entry = inst->connections->head;
while (conn) { while (entry) {
struct conn_queue_entry* ce = (struct conn_queue_entry*)entry->data;
struct ETCP_CONN* conn = ce->conn;
struct ETCP_LINK* link = conn->links; struct ETCP_LINK* link = conn->links;
while (link) { while (link) {
if (link->initialized) return 1; if (link->initialized) return 1;
link = link->next; link = link->next;
} }
conn = conn->next; entry = entry->next;
} }
return 0; return 0;
} }
@ -156,7 +158,7 @@ static int is_connection_established(struct UTUN_INSTANCE* inst) {
static void send_packets_fwd(void) { static void send_packets_fwd(void) {
if (!client_instance || packets_sent_fwd >= TOTAL_PACKETS) return; if (!client_instance || packets_sent_fwd >= TOTAL_PACKETS) return;
struct ETCP_CONN* conn = client_instance->connections; struct ETCP_CONN* conn = (client_instance->connections && client_instance->connections->head) ? ((struct conn_queue_entry*)client_instance->connections->head->data)->conn : NULL;
if (!conn || !conn->input_queue) return; if (!conn || !conn->input_queue) return;
// Start timing on first packet // Start timing on first packet
@ -193,7 +195,7 @@ static void send_packets_fwd(void) {
static void send_packets_back(void) { static void send_packets_back(void) {
if (!server_instance || packets_sent_back >= TOTAL_PACKETS) return; if (!server_instance || packets_sent_back >= TOTAL_PACKETS) return;
struct ETCP_CONN* conn = server_instance->connections; struct ETCP_CONN* conn = (server_instance->connections && server_instance->connections->head) ? ((struct conn_queue_entry*)server_instance->connections->head->data)->conn : NULL;
if (!conn || !conn->input_queue) return; if (!conn || !conn->input_queue) return;
// Start timing on first packet // Start timing on first packet
@ -230,7 +232,7 @@ static void send_packets_back(void) {
static void check_received_packets_fwd(void) { static void check_received_packets_fwd(void) {
if (!server_instance) return; if (!server_instance) return;
struct ETCP_CONN* conn = server_instance->connections; struct ETCP_CONN* conn = (server_instance->connections && server_instance->connections->head) ? ((struct conn_queue_entry*)server_instance->connections->head->data)->conn : NULL;
if (!conn || !conn->output_queue) return; if (!conn || !conn->output_queue) return;
// Disable routing callback to keep packets in output_queue for test verification // Disable routing callback to keep packets in output_queue for test verification
@ -263,7 +265,7 @@ static void check_received_packets_fwd(void) {
static void check_received_packets_back(void) { static void check_received_packets_back(void) {
if (!client_instance) return; if (!client_instance) return;
struct ETCP_CONN* conn = client_instance->connections; struct ETCP_CONN* conn = (client_instance->connections && client_instance->connections->head) ? ((struct conn_queue_entry*)client_instance->connections->head->data)->conn : NULL;
if (!conn || !conn->output_queue) return; if (!conn || !conn->output_queue) return;
// Disable routing callback to keep packets in output_queue for test verification // Disable routing callback to keep packets in output_queue for test verification

14
tests/test_etcp_api.c

@ -280,8 +280,10 @@ static void client_recv_callback(struct ETCP_CONN* conn, struct ll_entry* entry)
// Check if connection is established and crypto session key is ready // Check if connection is established and crypto session key is ready
static int is_connection_established(struct UTUN_INSTANCE* inst) { static int is_connection_established(struct UTUN_INSTANCE* inst) {
if (!inst) return 0; if (!inst) return 0;
struct ETCP_CONN* conn = inst->connections; struct ll_entry* entry = inst->connections->head;
while (conn) { while (entry) {
struct conn_queue_entry* ce = (struct conn_queue_entry*)entry->data;
struct ETCP_CONN* conn = ce->conn;
struct ETCP_LINK* link = conn->links; struct ETCP_LINK* link = conn->links;
while (link) { while (link) {
// Проверяем что линк инициализирован И session_key установлен (не нулевой) // Проверяем что линк инициализирован И session_key установлен (не нулевой)
@ -300,15 +302,17 @@ static int is_connection_established(struct UTUN_INSTANCE* inst) {
} }
link = link->next; link = link->next;
} }
conn = conn->next; entry = entry->next;
} }
return 0; return 0;
} }
// Get first connection from instance // Get first connection from instance
static struct ETCP_CONN* get_first_connection(struct UTUN_INSTANCE* inst) { static struct ETCP_CONN* get_first_connection(struct UTUN_INSTANCE* inst) {
if (!inst) return NULL; if (!inst || !inst->connections) return NULL;
return inst->connections; if (!inst->connections->head) return NULL;
struct conn_queue_entry* ce = (struct conn_queue_entry*)inst->connections->head->data;
return ce->conn;
} }
// Send packets from client to server (forward direction) via etcp_send // Send packets from client to server (forward direction) via etcp_send

16
tests/test_etcp_congestion.c

@ -187,8 +187,8 @@ static void send_timer_cb(void* arg) {
static void send_burst(struct test_ctx* ctx) { static void send_burst(struct test_ctx* ctx) {
if (ctx->test_done) return; if (ctx->test_done) return;
if (!ctx->sender || !ctx->sender->connections) return; if (!ctx->sender || !ctx->sender->connections) return;
struct ETCP_CONN* conn = ctx->sender->connections; struct ETCP_CONN* conn = (ctx->sender->connections && ctx->sender->connections->head) ? ((struct conn_queue_entry*)ctx->sender->connections->head->data)->conn : NULL;
if (!conn->initialized) return; if (!conn || !conn->initialized) return;
int sent = 0; int sent = 0;
while (sent < 64) { while (sent < 64) {
@ -232,7 +232,8 @@ static void log_metrics(struct test_ctx* ctx) {
ctx->last_bytes_recv = ctx->bytes_received; ctx->last_bytes_recv = ctx->bytes_received;
if (!ctx->sender || !ctx->sender->connections) return; if (!ctx->sender || !ctx->sender->connections) return;
struct ETCP_CONN* sconn = ctx->sender->connections; struct ETCP_CONN* sconn = (ctx->sender->connections && ctx->sender->connections->head) ? ((struct conn_queue_entry*)ctx->sender->connections->head->data)->conn : NULL;
if (!sconn) return;
struct ETCP_LINK* link = sconn->links; struct ETCP_LINK* link = sconn->links;
int idx = 0; int idx = 0;
while (link && idx < 2) { while (link && idx < 2) {
@ -343,10 +344,11 @@ int main(void) {
int links_ready = 0; int links_ready = 0;
while ((now_us() - t0) < 10000000ULL) { while ((now_us() - t0) < 10000000ULL) {
uasync_poll(ctx.ua, 1); uasync_poll(ctx.ua, 1);
if (ctx.sender->connections && ctx.sender->connections->links) { { struct ETCP_CONN* c = (ctx.sender->connections && ctx.sender->connections->head) ? ((struct conn_queue_entry*)ctx.sender->connections->head->data)->conn : NULL;
struct ETCP_LINK* l = ctx.sender->connections->links; if (c && c->links) {
struct ETCP_LINK* l = c->links;
if (l->initialized && l->link_status == 1) { links_ready = 1; break; } if (l->initialized && l->link_status == 1) { links_ready = 1; break; }
} } }
} }
printf("Links ready, waiting stabilize...\n"); printf("Links ready, waiting stabilize...\n");
t0 = now_us(); t0 = now_us();
@ -383,7 +385,7 @@ int main(void) {
if (elapsed >= TEST_DURATION_MS) ctx.test_done = 1; if (elapsed >= TEST_DURATION_MS) ctx.test_done = 1;
if (elapsed - last_progress >= 1000) { if (elapsed - last_progress >= 1000) {
last_progress = elapsed; last_progress = elapsed;
int l1 = ctx.sender->connections && ctx.sender->connections->links ? 1 : 0; int l1 = (ctx.sender->connections && ctx.sender->connections->head) ? 1 : 0;
printf(" t=%lus sent=%lu recv=%lu links=%d\n", (unsigned long)elapsed, (unsigned long)ctx.bytes_sent, (unsigned long)ctx.bytes_received, l1); printf(" t=%lus sent=%lu recv=%lu links=%d\n", (unsigned long)elapsed, (unsigned long)ctx.bytes_sent, (unsigned long)ctx.bytes_received, l1);
} }
} }

7
tests/test_etcp_connect.c

@ -45,8 +45,11 @@ static char* gv(const char* p, const char* k) {
free_config(c); return r; free_config(c); return r;
} }
static int lks(struct UTUN_INSTANCE* i) { static int lks(struct UTUN_INSTANCE* i) {
int n = 0; struct ETCP_CONN* c = i->connections; int n = 0; struct ll_entry* entry = i->connections->head;
while (c) { struct ETCP_LINK* l = c->links; while (l) { if (l->initialized && l->link_status) n++; l = l->next; } c = c->next; } while (entry) { struct conn_queue_entry* ce = (struct conn_queue_entry*)entry->data;
struct ETCP_CONN* c = ce->conn;
struct ETCP_LINK* l = c->links; while (l) { if (l->initialized && l->link_status) n++; l = l->next; }
entry = entry->next; }
return n; return n;
} }
static void fail(const char* msg) { fprintf(stderr, "FAIL: %s\n", msg); fflush(stderr); result = 1; } static void fail(const char* msg) { fprintf(stderr, "FAIL: %s\n", msg); fflush(stderr); result = 1; }

8
tests/test_etcp_dummynet.c

@ -81,7 +81,9 @@ static void send_pkt(void* arg) {
if (sending_done || (now_us() - start_us)/1000 >= SEND_MS) { if (sending_done || (now_us() - start_us)/1000 >= SEND_MS) {
sending_done = 1; return; sending_done = 1; return;
} }
if (!client || !client->connections) return; if (!client || !client->connections || !client->connections->head) return;
struct ETCP_CONN* conn = ((struct conn_queue_entry*)client->connections->head->data)->conn;
if (!conn) return;
struct ll_entry* e = ll_alloc_lldgram(sizeof(struct test_pkt)); struct ll_entry* e = ll_alloc_lldgram(sizeof(struct test_pkt));
if (!e) return; if (!e) return;
@ -91,7 +93,7 @@ static void send_pkt(void* arg) {
pkt->crc = crc32_calc(pkt->data, PAYLOAD_SIZE); pkt->crc = crc32_calc(pkt->data, PAYLOAD_SIZE);
e->len = sizeof(struct test_pkt); e->len = sizeof(struct test_pkt);
if (etcp_send(client->connections, e) == 0) pkts_sent++; if (etcp_send(conn, e) == 0) pkts_sent++;
else queue_entry_free(e); else queue_entry_free(e);
/* Schedule next packet with 5ms delay for rate limiting (~200 pkt/sec) /* Schedule next packet with 5ms delay for rate limiting (~200 pkt/sec)
@ -229,7 +231,7 @@ int main(void) {
/* Check if client has an established connection with ready links */ /* Check if client has an established connection with ready links */
if (client && client->connections) { if (client && client->connections) {
struct ETCP_CONN* conn = client->connections; struct ETCP_CONN* conn = (client->connections && client->connections->head) ? ((struct conn_queue_entry*)client->connections->head->data)->conn : NULL;
/* Check if peer_node_id is set (non-zero) and has links */ /* Check if peer_node_id is set (non-zero) and has links */
if (conn->peer_node_id != 0 && conn->links != NULL) { if (conn->peer_node_id != 0 && conn->links != NULL) {
struct ETCP_LINK* link = conn->links; struct ETCP_LINK* link = conn->links;

18
tests/test_etcp_ping.c

@ -97,27 +97,31 @@ static void monitor_connections(void* arg) {
int server_links = 0; int server_links = 0;
int client_links = 0; int client_links = 0;
if (server_instance) { if (server_instance) {
struct ETCP_CONN* conn = server_instance->connections; struct ll_entry* e = server_instance->connections->head;
while (conn) { while (e) {
struct conn_queue_entry* ce = (struct conn_queue_entry*)e->data;
struct ETCP_CONN* conn = ce->conn;
struct ETCP_LINK* link = conn->links; struct ETCP_LINK* link = conn->links;
while (link) { while (link) {
server_links++; server_links++;
if (link->initialized) test_completed = 1; if (link->initialized) test_completed = 1;
link = link->next; link = link->next;
} }
conn = conn->next; e = e->next;
} }
} }
if (client_instance) { if (client_instance) {
struct ETCP_CONN* conn = client_instance->connections; struct ll_entry* e = client_instance->connections->head;
while (conn) { while (e) {
struct conn_queue_entry* ce = (struct conn_queue_entry*)e->data;
struct ETCP_CONN* conn = ce->conn;
struct ETCP_LINK* link = conn->links; struct ETCP_LINK* link = conn->links;
while (link) { while (link) {
client_links++; client_links++;
if (link->initialized) test_completed = 1; if (link->initialized) test_completed = 1;
link = link->next; link = link->next;
} }
conn = conn->next; e = e->next;
} }
} }
if (server_links > 0 && client_links > 0) test_completed = 1; if (server_links > 0 && client_links > 0) test_completed = 1;
@ -182,7 +186,7 @@ int main(void) {
goto cleanup; goto cleanup;
} }
DEBUG_INFO(DEBUG_CATEGORY_ETCP, "Links established, running ping test"); DEBUG_INFO(DEBUG_CATEGORY_ETCP, "Links established, running ping test");
struct ETCP_CONN* conn = client_instance->connections; struct ETCP_CONN* conn = (client_instance->connections && client_instance->connections->head) ? ((struct conn_queue_entry*)client_instance->connections->head->data)->conn : NULL;
if (conn && conn->links) { if (conn && conn->links) {
struct ETCP_LINK* link = conn->links; struct ETCP_LINK* link = conn->links;
uint8_t peer_pubkey[SC_PUBKEY_SIZE]; uint8_t peer_pubkey[SC_PUBKEY_SIZE];

29
tests/test_etcp_reconnect.c

@ -114,19 +114,22 @@ static void cleanup_temp_configs(void) {
} }
static int is_connection_established(struct UTUN_INSTANCE* inst) { static int is_connection_established(struct UTUN_INSTANCE* inst) {
if (!inst) return 0; if (!inst || !inst->connections) return 0;
struct ETCP_CONN* conn = inst->connections; struct ll_entry* entry = inst->connections->head;
while (conn) { while (entry) {
if (conn->initialized) return 1; struct conn_queue_entry* ce = (struct conn_queue_entry*)entry->data;
conn = conn->next; if (ce->conn->initialized) return 1;
entry = entry->next;
} }
return 0; return 0;
} }
static void drain_received(int count_flag) { static void drain_received(int count_flag) {
if (!server_instance) return; if (!server_instance || !server_instance->connections) return;
struct ETCP_CONN* conn = server_instance->connections; struct ll_entry* entry = server_instance->connections->head;
while (conn) { while (entry) {
struct conn_queue_entry* ce = (struct conn_queue_entry*)entry->data;
struct ETCP_CONN* conn = ce->conn;
if (conn->output_queue) { if (conn->output_queue) {
queue_set_callback(conn->output_queue, NULL, NULL); queue_set_callback(conn->output_queue, NULL, NULL);
struct ETCP_FRAGMENT* pkt; struct ETCP_FRAGMENT* pkt;
@ -136,13 +139,13 @@ static void drain_received(int count_flag) {
queue_entry_free((struct ll_entry*)pkt); queue_entry_free((struct ll_entry*)pkt);
} }
} }
conn = conn->next; entry = entry->next;
} }
} }
static void send_packets(void) { static void send_packets(void) {
if (!client_instance || packets_sent >= TOTAL_PACKETS) return; if (!client_instance || packets_sent >= TOTAL_PACKETS) return;
struct ETCP_CONN* conn = client_instance->connections; struct ETCP_CONN* conn = (client_instance->connections && client_instance->connections->head) ? ((struct conn_queue_entry*)client_instance->connections->head->data)->conn : NULL;
if (!conn || !conn->input_queue) return; if (!conn || !conn->input_queue) return;
while (packets_sent < TOTAL_PACKETS) { while (packets_sent < TOTAL_PACKETS) {
if (queue_entry_count(conn->input_queue) >= MAX_QUEUE_SIZE) break; if (queue_entry_count(conn->input_queue) >= MAX_QUEUE_SIZE) break;
@ -160,7 +163,7 @@ static void monitor(void* arg) {
int conn_ok = is_connection_established(client_instance); int conn_ok = is_connection_established(client_instance);
static int last_reinit_count = -1; static int last_reinit_count = -1;
int cur_reinit = 0; int cur_reinit = 0;
struct ETCP_CONN* conn = client_instance ? client_instance->connections : NULL; struct ETCP_CONN* conn = (client_instance && client_instance->connections && client_instance->connections->head) ? ((struct conn_queue_entry*)client_instance->connections->head->data)->conn : NULL;
if (conn) cur_reinit = conn->reinit_count; if (conn) cur_reinit = conn->reinit_count;
switch (phase) { switch (phase) {
@ -208,8 +211,8 @@ static void monitor(void* arg) {
} }
printf("Server recreated (node_id=%llx)\n", (unsigned long long)server_instance->node_id); printf("Server recreated (node_id=%llx)\n", (unsigned long long)server_instance->node_id);
// Force client to reinit — otherwise conn_ok stays true on old initialized=1 // Force client to reinit — otherwise conn_ok stays true on old initialized=1
{ struct ETCP_CONN* c = client_instance->connections; { struct ll_entry* e = client_instance->connections->head;
while (c) { etcp_conn_reinit(c); c = c->next; } } while (e) { struct conn_queue_entry* ce = (struct conn_queue_entry*)e->data; etcp_conn_reinit(ce->conn); e = e->next; } }
restart_action_done = 1; restart_action_done = 1;
} }
drain_received(0); drain_received(0);

26
tests/test_etcp_reinit_inflight.c

@ -133,17 +133,20 @@ static void on_recv(struct ETCP_CONN* conn, struct ll_entry* entry) {
} }
static int links_initialized(struct UTUN_INSTANCE* inst) { static int links_initialized(struct UTUN_INSTANCE* inst) {
struct ETCP_CONN* conn = inst->connections; if (!inst->connections) return 0;
while (conn) { struct ll_entry* entry = inst->connections->head;
while (entry) {
struct conn_queue_entry* ce = (struct conn_queue_entry*)entry->data;
struct ETCP_CONN* conn = ce->conn;
struct ETCP_LINK* link = conn->links; struct ETCP_LINK* link = conn->links;
while (link) { if (link->initialized) return 1; link = link->next; } while (link) { if (link->initialized) return 1; link = link->next; }
conn = conn->next; entry = entry->next;
} }
return 0; return 0;
} }
static int buffers_full(struct test_ctx* ctx) { static int buffers_full(struct test_ctx* ctx) {
struct ETCP_CONN* conn = ctx->sender->connections; struct ETCP_CONN* conn = (ctx->sender->connections && ctx->sender->connections->head) ? ((struct conn_queue_entry*)ctx->sender->connections->head->data)->conn : NULL;
if (!conn) return 0; if (!conn) return 0;
struct ETCP_LINK* link = conn->links; struct ETCP_LINK* link = conn->links;
return link && link->inflight_bytes >= link->inflight_lim_bytes return link && link->inflight_bytes >= link->inflight_lim_bytes
@ -152,7 +155,7 @@ static int buffers_full(struct test_ctx* ctx) {
} }
static void send_one_packet(struct test_ctx* ctx) { static void send_one_packet(struct test_ctx* ctx) {
struct ETCP_CONN* conn = ctx->sender->connections; struct ETCP_CONN* conn = (ctx->sender->connections && ctx->sender->connections->head) ? ((struct conn_queue_entry*)ctx->sender->connections->head->data)->conn : NULL;
if (!conn || !conn->initialized) return; if (!conn || !conn->initialized) return;
if (ctx->total_packets_sent >= TOTAL_PACKETS) return; if (ctx->total_packets_sent >= TOTAL_PACKETS) return;
if (conn->input_queue && queue_entry_count(conn->input_queue) > 200) return; if (conn->input_queue && queue_entry_count(conn->input_queue) > 200) return;
@ -192,11 +195,12 @@ static void monitor(void* arg) {
break; break;
case 2: // fill buffers case 2: // fill buffers
if (buffers_full(ctx)) { if (buffers_full(ctx)) {
{ struct ETCP_CONN* c = (ctx->sender->connections && ctx->sender->connections->head) ? ((struct conn_queue_entry*)ctx->sender->connections->head->data)->conn : NULL;
printf(" buffers full: inflight=%u/%u wait_ack=%d sent=%d\n", printf(" buffers full: inflight=%u/%u wait_ack=%d sent=%d\n",
ctx->sender->connections->links->inflight_bytes, c ? c->links->inflight_bytes : 0,
ctx->sender->connections->links->inflight_lim_bytes, c ? c->links->inflight_lim_bytes : 0,
ctx->sender->connections->input_wait_ack->count, c ? c->input_wait_ack->count : 0,
ctx->total_packets_sent); ctx->total_packets_sent); }
ctx->phase = 3; ctx->phase = 3;
} }
break; break;
@ -221,7 +225,7 @@ static void monitor(void* arg) {
ctx->phase = 5; ctx->phase = 5;
break; break;
case 5: // wait delivery case 5: // wait delivery
conn = ctx->sender->connections; conn = (ctx->sender->connections && ctx->sender->connections->head) ? ((struct conn_queue_entry*)ctx->sender->connections->head->data)->conn : NULL;
if (conn && conn->reinit_count > 0 && !ctx->sender_reinit_detected) { if (conn && conn->reinit_count > 0 && !ctx->sender_reinit_detected) {
ctx->sender_reinit_detected = 1; ctx->sender_reinit_detected = 1;
printf(" reinit detected (count=%u)\n", conn->reinit_count); printf(" reinit detected (count=%u)\n", conn->reinit_count);
@ -232,7 +236,7 @@ static void monitor(void* arg) {
} }
break; break;
case 6: // verify case 6: // verify
conn = ctx->sender->connections; conn = (ctx->sender->connections && ctx->sender->connections->head) ? ((struct conn_queue_entry*)ctx->sender->connections->head->data)->conn : NULL;
if (conn && conn->reinit_count < 1) { if (conn && conn->reinit_count < 1) {
printf("\n[FAIL] Reinit not triggered (reinit_count=%u)\n", conn->reinit_count); printf("\n[FAIL] Reinit not triggered (reinit_count=%u)\n", conn->reinit_count);
ctx->test_done = 2; ctx->test_done = 2;

8
tests/test_etcp_router.c

@ -205,8 +205,11 @@ static void write_configs(void) {
// ======================== Helpers ======================== // ======================== Helpers ========================
static int conn_established(struct UTUN_INSTANCE* inst) { static int conn_established(struct UTUN_INSTANCE* inst) {
if (!inst) return 0; if (!inst || !inst->connections) return 0;
for (struct ETCP_CONN* c = inst->connections; c; c = c->next) { struct ll_entry* entry = inst->connections->head;
while (entry) {
struct conn_queue_entry* ce = (struct conn_queue_entry*)entry->data;
struct ETCP_CONN* c = ce->conn;
for (struct ETCP_LINK* l = c->links; l; l = l->next) { for (struct ETCP_LINK* l = c->links; l; l = l->next) {
if (l->initialized && c->crypto_ctx.initialized) { if (l->initialized && c->crypto_ctx.initialized) {
int ok = 0; int ok = 0;
@ -215,6 +218,7 @@ static int conn_established(struct UTUN_INSTANCE* inst) {
if (ok) return 1; if (ok) return 1;
} }
} }
entry = entry->next;
} }
return 0; return 0;
} }

7
tests/test_etcp_router_reconnect.c

@ -186,9 +186,10 @@ static void state_step(void) {
break; break;
case ST_B_KILL: case ST_B_KILL:
g_b->running=0; utun_instance_destroy(g_b); g_b=NULL; g_b->running=0; utun_instance_destroy(g_b); g_b=NULL;
{ struct ETCP_CONN *c,*n; { struct ll_entry* e = queue_find_data_by_index(g_a->connections, (const uint8_t*)&node_b);
for(c=g_a->connections;c;c=n){n=c->next;if(c->peer_node_id==node_b)etcp_connection_close(c);} if (e) etcp_connection_close(((struct conn_queue_entry*)e->data)->conn);
for(c=g_c->connections;c;c=n){n=c->next;if(c->peer_node_id==node_b)etcp_connection_close(c);} } e = queue_find_data_by_index(g_c->connections, (const uint8_t*)&node_b);
if (e) etcp_connection_close(((struct conn_queue_entry*)e->data)->conn); }
g_phase_sent=0; g_bgp_ready_mask=0; g_state=ST_B_RESTART; g_phase_sent=0; g_bgp_ready_mask=0; g_state=ST_B_RESTART;
break; break;
case ST_B_RESTART: case ST_B_RESTART:

14
tests/test_etcp_simple_traffic.c

@ -107,22 +107,24 @@ static void cleanup_temp_configs(void) {
} }
static int is_connection_established(struct UTUN_INSTANCE* inst) { static int is_connection_established(struct UTUN_INSTANCE* inst) {
if (!inst) return 0; if (!inst || !inst->connections) return 0;
struct ETCP_CONN* conn = inst->connections; struct ll_entry* entry = inst->connections->head;
while (conn) { while (entry) {
struct conn_queue_entry* ce = (struct conn_queue_entry*)entry->data;
struct ETCP_CONN* conn = ce->conn;
struct ETCP_LINK* link = conn->links; struct ETCP_LINK* link = conn->links;
while (link) { while (link) {
if (link->initialized) return 1; if (link->initialized) return 1;
link = link->next; link = link->next;
} }
conn = conn->next; entry = entry->next;
} }
return 0; return 0;
} }
static void send_test_packet(void) { static void send_test_packet(void) {
if (!client_instance || packet_sent) return; if (!client_instance || packet_sent) return;
struct ETCP_CONN* conn = client_instance->connections; struct ETCP_CONN* conn = (client_instance->connections && client_instance->connections->head) ? ((struct conn_queue_entry*)client_instance->connections->head->data)->conn : NULL;
if (!conn || !conn->input_queue) { if (!conn || !conn->input_queue) {
DEBUG_ERROR(DEBUG_CATEGORY_ETCP, "send_test_packet: no connection or input_queue"); DEBUG_ERROR(DEBUG_CATEGORY_ETCP, "send_test_packet: no connection or input_queue");
return; return;
@ -138,7 +140,7 @@ static void send_test_packet(void) {
static void check_packet_received(void) { static void check_packet_received(void) {
if (!server_instance || packet_received) return; if (!server_instance || packet_received) return;
struct ETCP_CONN* conn = server_instance->connections; struct ETCP_CONN* conn = (server_instance->connections && server_instance->connections->head) ? ((struct conn_queue_entry*)server_instance->connections->head->data)->conn : NULL;
if (!conn || !conn->output_queue) return; if (!conn || !conn->output_queue) return;
queue_set_callback(conn->output_queue, NULL, NULL); queue_set_callback(conn->output_queue, NULL, NULL);

16
tests/test_etcp_two_instances.c

@ -125,8 +125,10 @@ static void monitor_connections(void* arg) {
// Check server // Check server
if (server_instance) { if (server_instance) {
struct ETCP_CONN* conn = server_instance->connections; struct ll_entry* entry = server_instance->connections->head;
while (conn) { while (entry) {
struct conn_queue_entry* ce = (struct conn_queue_entry*)entry->data;
struct ETCP_CONN* conn = ce->conn;
struct ETCP_LINK* link = conn->links; struct ETCP_LINK* link = conn->links;
while (link) { while (link) {
server_links++; server_links++;
@ -136,14 +138,16 @@ static void monitor_connections(void* arg) {
link->is_server ? "server" : "client"); link->is_server ? "server" : "client");
link = link->next; link = link->next;
} }
conn = conn->next; entry = entry->next;
} }
} }
// Check client // Check client
if (client_instance) { if (client_instance) {
struct ETCP_CONN* conn = client_instance->connections; struct ll_entry* entry = client_instance->connections->head;
while (conn) { while (entry) {
struct conn_queue_entry* ce = (struct conn_queue_entry*)entry->data;
struct ETCP_CONN* conn = ce->conn;
struct ETCP_LINK* link = conn->links; struct ETCP_LINK* link = conn->links;
while (link) { while (link) {
client_links++; client_links++;
@ -203,7 +207,7 @@ static void monitor_connections(void* arg) {
} }
link = link->next; link = link->next;
} }
conn = conn->next; entry = entry->next;
} }
} }

10
tests/test_icmp_proxy.c

@ -114,12 +114,18 @@ static void monitor(void* arg) {
if (g_test_phase == 0) { if (g_test_phase == 0) {
int cli_ok = 0, exit_ok = 0; int cli_ok = 0, exit_ok = 0;
for (struct ETCP_CONN* c = cli->connections; c; c = c->next) { struct ll_entry* e = cli->connections->head;
while (e) { struct conn_queue_entry* ce = (struct conn_queue_entry*)e->data;
struct ETCP_CONN* c = ce->conn;
for (struct ETCP_LINK* l = c->links; l; l = l->next) for (struct ETCP_LINK* l = c->links; l; l = l->next)
if (l->initialized && c->crypto_ctx.initialized) cli_ok = 1; if (l->initialized && c->crypto_ctx.initialized) cli_ok = 1;
for (struct ETCP_CONN* c = exit_node->connections; c; c = c->next) e = e->next; } }
{ struct ll_entry* e = exit_node->connections->head;
while (e) { struct conn_queue_entry* ce = (struct conn_queue_entry*)e->data;
struct ETCP_CONN* c = ce->conn;
for (struct ETCP_LINK* l = c->links; l; l = l->next) for (struct ETCP_LINK* l = c->links; l; l = l->next)
if (l->initialized && c->crypto_ctx.initialized) exit_ok = 1; if (l->initialized && c->crypto_ctx.initialized) exit_ok = 1;
e = e->next; } }
if (cli_ok && exit_ok) { if (cli_ok && exit_ok) {
g_test_phase = 1; g_test_phase = 1;
// Override client handler for test verification // Override client handler for test verification

10
tests/test_ipv6_sockets.c

@ -121,15 +121,17 @@ static void cleanup_temp_configs(void) {
} }
static int is_connection_established(struct UTUN_INSTANCE* inst) { static int is_connection_established(struct UTUN_INSTANCE* inst) {
if (!inst) return 0; if (!inst || !inst->connections) return 0;
struct ETCP_CONN* conn = inst->connections; struct ll_entry* entry = inst->connections->head;
while (conn) { while (entry) {
struct conn_queue_entry* ce = (struct conn_queue_entry*)entry->data;
struct ETCP_CONN* conn = ce->conn;
struct ETCP_LINK* link = conn->links; struct ETCP_LINK* link = conn->links;
while (link) { while (link) {
if (link->initialized) return 1; if (link->initialized) return 1;
link = link->next; link = link->next;
} }
conn = conn->next; entry = entry->next;
} }
return 0; return 0;
} }

41
tests/test_nat_detection.c

@ -149,27 +149,25 @@ static void cleanup_temp_configs(void) {
} }
static int has_initialized_link(struct UTUN_INSTANCE* inst) { static int has_initialized_link(struct UTUN_INSTANCE* inst) {
if (!inst) return 0; if (!inst || !inst->connections) return 0;
struct ETCP_CONN* conn = inst->connections; struct ll_entry* entry = inst->connections->head;
while (conn) { while (entry) {
struct conn_queue_entry* ce = (struct conn_queue_entry*)entry->data;
struct ETCP_CONN* conn = ce->conn;
struct ETCP_LINK* link = conn->links; struct ETCP_LINK* link = conn->links;
while (link) { while (link) {
if (link->initialized) return 1; if (link->initialized) return 1;
link = link->next; link = link->next;
} }
conn = conn->next; entry = entry->next;
} }
return 0; return 0;
} }
static struct ETCP_LINK* find_link_to_node(struct UTUN_INSTANCE* inst, uint64_t node_id) { static struct ETCP_LINK* find_link_to_node(struct UTUN_INSTANCE* inst, uint64_t node_id) {
if (!inst) return NULL; if (!inst || !inst->connections) return NULL;
struct ETCP_CONN* conn = inst->connections; struct ll_entry* entry = queue_find_data_by_index(inst->connections, (const uint8_t*)&node_id);
while (conn) { return entry ? ((struct conn_queue_entry*)entry->data)->conn->links : NULL;
if (conn->peer_node_id == node_id) return conn->links;
conn = conn->next;
}
return NULL;
} }
static void test_timeout_cb(void* arg) { static void test_timeout_cb(void* arg) {
@ -335,11 +333,13 @@ int main(void) {
} }
// Verify link nat_type on server // Verify link nat_type on server
struct ETCP_CONN* conn_sc1 = inst_s->connections; struct ETCP_CONN* conn_sc1 = NULL;
while (conn_sc1) { { struct ll_entry* e = inst_s->connections->head;
if (conn_sc1->peer_node_id == NODE_ID_C1) break; while (e) {
conn_sc1 = conn_sc1->next; struct conn_queue_entry* ce = (struct conn_queue_entry*)e->data;
} if (ce->conn->peer_node_id == NODE_ID_C1) { conn_sc1 = ce->conn; break; }
e = e->next;
} }
if (!link_sc1 || link_sc1->nat_type != NAT_TYPE_EIM) { if (!link_sc1 || link_sc1->nat_type != NAT_TYPE_EIM) {
DEBUG_ERROR(DEBUG_CATEGORY_BGP, "FAIL: link nat_type not EIM on server"); DEBUG_ERROR(DEBUG_CATEGORY_BGP, "FAIL: link nat_type not EIM on server");
goto cleanup; goto cleanup;
@ -414,10 +414,11 @@ int main(void) {
// 6. Separate STUN check via explicit route_ping_send_req_addr // 6. Separate STUN check via explicit route_ping_send_req_addr
DEBUG_INFO(DEBUG_CATEGORY_BGP, "Performing explicit STUN ping from S via C2 to C1..."); DEBUG_INFO(DEBUG_CATEGORY_BGP, "Performing explicit STUN ping from S via C2 to C1...");
struct ETCP_CONN* conn_sc2 = NULL; struct ETCP_CONN* conn_sc2 = NULL;
struct ETCP_CONN* conn = inst_s->connections; struct ll_entry* entry = inst_s->connections->head;
while (conn) { while (entry) {
if (conn->peer_node_id == NODE_ID_C2) { conn_sc2 = conn; break; } struct conn_queue_entry* ce = (struct conn_queue_entry*)entry->data;
conn = conn->next; if (ce->conn->peer_node_id == NODE_ID_C2) { conn_sc2 = ce->conn; break; }
entry = entry->next;
} }
if (!conn_sc2) { if (!conn_sc2) {
DEBUG_ERROR(DEBUG_CATEGORY_BGP, "FAIL: S has no connection to C2"); DEBUG_ERROR(DEBUG_CATEGORY_BGP, "FAIL: S has no connection to C2");

13
tests/test_nat_transport.c

@ -157,11 +157,12 @@ static void test_timeout_cb(void* arg) {
static struct ETCP_LINK* first_initialized_link(struct UTUN_INSTANCE* inst) { static struct ETCP_LINK* first_initialized_link(struct UTUN_INSTANCE* inst) {
if (!inst || !inst->connections) return NULL; if (!inst || !inst->connections) return NULL;
struct ETCP_LINK* link = inst->connections->links; struct ll_entry* entry = inst->connections->head;
while (link) { while (entry) { struct conn_queue_entry* ce = (struct conn_queue_entry*)entry->data;
if (link->initialized) return link; struct ETCP_CONN* conn = ce->conn;
link = link->next; struct ETCP_LINK* link = conn->links;
} while (link) { if (link->initialized) return link; link = link->next; }
entry = entry->next; }
return NULL; return NULL;
} }
@ -278,7 +279,7 @@ static int test_provider_egress(void) {
DEBUG_INFO(DEBUG_CATEGORY_NAT, "=== test_provider_egress ==="); DEBUG_INFO(DEBUG_CATEGORY_NAT, "=== test_provider_egress ===");
// Get the connection from client to provider (for debug info only) // Get the connection from client to provider (for debug info only)
struct ETCP_CONN* client_conn = inst_client->connections; struct ETCP_CONN* client_conn = (inst_client->connections && inst_client->connections->head) ? ((struct conn_queue_entry*)inst_client->connections->head->data)->conn : NULL;
if (!client_conn) { if (!client_conn) {
DEBUG_ERROR(DEBUG_CATEGORY_NAT, "No client connections"); DEBUG_ERROR(DEBUG_CATEGORY_NAT, "No client connections");
return 0; return 0;

20
tests/test_pkt_normalizer_etcp.c

@ -127,9 +127,9 @@ static int verify_packet_data(uint8_t* buffer, int size, int expected_seq) {
} }
static int is_connection_established(struct UTUN_INSTANCE* inst) { static int is_connection_established(struct UTUN_INSTANCE* inst) {
if (!inst) return 0; if (!inst || !inst->connections) return 0;
struct ETCP_CONN* conn = inst->connections; struct ll_entry* entry = inst->connections->head;
while (conn) { struct ETCP_LINK* link = conn->links; while (link) { if (link->initialized) return 1; link = link->next; } conn = conn->next; } while (entry) { struct conn_queue_entry* ce = (struct conn_queue_entry*)entry->data; struct ETCP_CONN* conn = ce->conn; struct ETCP_LINK* link = conn->links; while (link) { if (link->initialized) return 1; link = link->next; } entry = entry->next; }
return 0; return 0;
} }
@ -189,16 +189,18 @@ static void monitor_and_send(void* arg) {
if (!connection_checked) { if (!connection_checked) {
if (is_connection_established(client_instance)) { if (is_connection_established(client_instance)) {
connection_checked = 1; connection_checked = 1;
if (!client_pn && client_instance->connections) { { struct ETCP_CONN* c = (client_instance->connections && client_instance->connections->head) ? ((struct conn_queue_entry*)client_instance->connections->head->data)->conn : NULL;
client_pn = pn_init(client_instance->connections); if (!client_pn && c) {
client_pn = pn_init(c);
if (!client_pn) { test_completed = 2; return; } if (!client_pn) { test_completed = 2; return; }
queue_set_callback(client_pn->output, NULL, NULL); queue_set_callback(client_pn->output, NULL, NULL);
} } }
if (!server_pn && server_instance->connections) { { struct ETCP_CONN* c = (server_instance->connections && server_instance->connections->head) ? ((struct conn_queue_entry*)server_instance->connections->head->data)->conn : NULL;
server_pn = pn_init(server_instance->connections); if (!server_pn && c) {
server_pn = pn_init(c);
if (!server_pn) { test_completed = 2; return; } if (!server_pn) { test_completed = 2; return; }
queue_set_callback(server_pn->output, NULL, NULL); queue_set_callback(server_pn->output, NULL, NULL);
} } }
} }
} }

20
tests/test_route_ping.c

@ -157,27 +157,25 @@ static void cleanup_temp_configs(void) {
} }
static int has_initialized_link(struct UTUN_INSTANCE* inst) { static int has_initialized_link(struct UTUN_INSTANCE* inst) {
if (!inst) return 0; if (!inst || !inst->connections) return 0;
struct ETCP_CONN* conn = inst->connections; struct ll_entry* entry = inst->connections->head;
while (conn) { while (entry) {
struct conn_queue_entry* ce = (struct conn_queue_entry*)entry->data;
struct ETCP_CONN* conn = ce->conn;
struct ETCP_LINK* link = conn->links; struct ETCP_LINK* link = conn->links;
while (link) { while (link) {
if (link->initialized) return 1; if (link->initialized) return 1;
link = link->next; link = link->next;
} }
conn = conn->next; entry = entry->next;
} }
return 0; return 0;
} }
static struct ETCP_CONN* find_conn_to_peer(struct UTUN_INSTANCE* inst, uint64_t peer_node_id) { static struct ETCP_CONN* find_conn_to_peer(struct UTUN_INSTANCE* inst, uint64_t peer_node_id) {
if (!inst) return NULL; if (!inst || !inst->connections) return NULL;
struct ETCP_CONN* conn = inst->connections; struct ll_entry* entry = queue_find_data_by_index(inst->connections, (const uint8_t*)&peer_node_id);
while (conn) { return entry ? ((struct conn_queue_entry*)entry->data)->conn : NULL;
if (conn->peer_node_id == peer_node_id) return conn;
conn = conn->next;
}
return NULL;
} }
static void test_timeout_cb(void* arg) { static void test_timeout_cb(void* arg) {

9
tests/test_socks_http_proxy.c

@ -215,10 +215,15 @@ static char* make_cfg_client(void) {
} }
static int conn_ready(struct UTUN_INSTANCE* inst) { static int conn_ready(struct UTUN_INSTANCE* inst) {
if (!inst) return 0; if (!inst || !inst->connections) return 0;
for (struct ETCP_CONN* c = inst->connections; c; c = c->next) struct ll_entry* entry = inst->connections->head;
while (entry) {
struct conn_queue_entry* ce = (struct conn_queue_entry*)entry->data;
struct ETCP_CONN* c = ce->conn;
for (struct ETCP_LINK* l = c->links; l; l = l->next) for (struct ETCP_LINK* l = c->links; l; l = l->next)
if (l->initialized && c->crypto_ctx.initialized) return 1; if (l->initialized && c->crypto_ctx.initialized) return 1;
entry = entry->next;
}
return 0; return 0;
} }

7
tests/test_stcp_traffic.c

@ -178,8 +178,11 @@ int main(void) {
TASSERT(cli_send_ready); TASSERT(cli_send_ready);
TASSERT(cli_link); TASSERT(cli_link);
{ struct ETCP_CONN *c = srv_inst->connections; { struct ll_entry* e = srv_inst->connections->head;
while (c) { if (c->transport_link) { srv_link = (struct stcp_link *)c->transport_link; break; } c = c->next; } while (e) { struct conn_queue_entry* ce = (struct conn_queue_entry*)e->data;
struct ETCP_CONN *c = ce->conn;
if (c->transport_link) { srv_link = (struct stcp_link *)c->transport_link; break; }
e = e->next; }
} }
TASSERT(srv_link); TASSERT(srv_link);

10
tests/test_tcp_proxy_remote.c

@ -118,10 +118,16 @@ static void monitor(void* arg) {
if (g_done) return; if (g_done) return;
if (g_phase == 0 && !g_conn_up) { if (g_phase == 0 && !g_conn_up) {
int bup=0, eup=0; int bup=0, eup=0;
if (g_b) for (struct ETCP_CONN*c=g_b->connections;c;c=c->next) if (g_b) { struct ll_entry* e=g_b->connections->head;
while (e) { struct conn_queue_entry* ce=(struct conn_queue_entry*)e->data;
struct ETCP_CONN*c=ce->conn;
for (struct ETCP_LINK*l=c->links;l;l=l->next) if (l->initialized&&c->crypto_ctx.initialized) bup=1; for (struct ETCP_LINK*l=c->links;l;l=l->next) if (l->initialized&&c->crypto_ctx.initialized) bup=1;
if (g_exit) for (struct ETCP_CONN*c=g_exit->connections;c;c=c->next) e=e->next; } }
if (g_exit) { struct ll_entry* e=g_exit->connections->head;
while (e) { struct conn_queue_entry* ce=(struct conn_queue_entry*)e->data;
struct ETCP_CONN*c=ce->conn;
for (struct ETCP_LINK*l=c->links;l;l=l->next) if (l->initialized&&c->crypto_ctx.initialized) eup=1; for (struct ETCP_LINK*l=c->links;l;l=l->next) if (l->initialized&&c->crypto_ctx.initialized) eup=1;
e=e->next; } }
if (bup && eup) { g_conn_up=1; start_test(); } if (bup && eup) { g_conn_up=1; start_test(); }
} }
if (g_conn_up && !g_done) poll_test(); if (g_conn_up && !g_done) poll_test();

10
tests/test_udp_proxy.c

@ -118,12 +118,18 @@ static void monitor(void* arg) {
if (g_test_phase == 0) { if (g_test_phase == 0) {
// Wait for ETCP connection // Wait for ETCP connection
int cli_ok = 0, exit_ok = 0; int cli_ok = 0, exit_ok = 0;
for (struct ETCP_CONN* c = cli->connections; c; c = c->next) { struct ll_entry* e = cli->connections->head;
while (e) { struct conn_queue_entry* ce = (struct conn_queue_entry*)e->data;
struct ETCP_CONN* c = ce->conn;
for (struct ETCP_LINK* l = c->links; l; l = l->next) for (struct ETCP_LINK* l = c->links; l; l = l->next)
if (l->initialized && c->crypto_ctx.initialized) cli_ok = 1; if (l->initialized && c->crypto_ctx.initialized) cli_ok = 1;
for (struct ETCP_CONN* c = exit_node->connections; c; c = c->next) e = e->next; } }
{ struct ll_entry* e = exit_node->connections->head;
while (e) { struct conn_queue_entry* ce = (struct conn_queue_entry*)e->data;
struct ETCP_CONN* c = ce->conn;
for (struct ETCP_LINK* l = c->links; l; l = l->next) for (struct ETCP_LINK* l = c->links; l; l = l->next)
if (l->initialized && c->crypto_ctx.initialized) exit_ok = 1; if (l->initialized && c->crypto_ctx.initialized) exit_ok = 1;
e = e->next; } }
if (cli_ok && exit_ok) { if (cli_ok && exit_ok) {
g_test_phase = 1; g_test_phase = 1;
// Bind client handler for UDP replies (overrides what tcp_proxy_client_create set, for test verification) // Bind client handler for UDP replies (overrides what tcp_proxy_client_create set, for test verification)

17
tools/chatgui/transport/chat_core.c

@ -564,10 +564,6 @@ struct cc_parallel_ctx {
static void cc_parallel_cleanup(struct cc_parallel_state* st) { static void cc_parallel_cleanup(struct cc_parallel_state* st) {
if (!st) return; if (!st) return;
if (st->inst && st->inst->chat_connections) {
struct ll_entry* e = queue_find_data_by_index(st->inst->chat_connections, (const uint8_t*)&st->node_id);
if (e) { queue_remove_data(st->inst->chat_connections, e); queue_entry_free(e); }
}
u_free(st->conns); u_free(st->conns);
u_free(st->timers); u_free(st->timers);
u_free(st); u_free(st);
@ -764,8 +760,7 @@ void chat_core_connect_from_invite(struct chat_invite* inv) {
pst->conns[idx] = NULL; pst->timers[idx] = NULL; pst->pending_count--; continue; pst->conns[idx] = NULL; pst->timers[idx] = NULL; pst->pending_count--; continue;
} }
pst->conns[idx] = conn; pst->conns[idx] = conn;
conn->peer_node_id = inv->node_id; etcp_conn_set_peer_node_id(conn, inv->node_id);
{ struct ll_entry* qe = queue_entry_new(16); if (qe) { *(uint64_t*)qe->data = inv->node_id; *(struct ETCP_CONN**)(qe->data+8) = conn; queue_data_put_with_index(g_cc.inst->chat_connections, qe); } }
pst->timers[idx] = uasync_set_timeout(g_cc.inst->ua, CC_PARALLEL_CONNECT_TIMEOUT_MS * 10, pst->timers[idx] = uasync_set_timeout(g_cc.inst->ua, CC_PARALLEL_CONNECT_TIMEOUT_MS * 10,
pctx, cc_parallel_timeout_cb, "cc_parallel"); pctx, cc_parallel_timeout_cb, "cc_parallel");
DEBUG_INFO(DEBUG_CATEGORY_CONNECTIVITY, "%s: parallel connect attempt %d/%d to %d.%d.%d.%d:%d", DEBUG_INFO(DEBUG_CATEGORY_CONNECTIVITY, "%s: parallel connect attempt %d/%d to %d.%d.%d.%d:%d",
@ -810,10 +805,6 @@ struct ca_state {
static void ca_cleanup(struct ca_state* st) { static void ca_cleanup(struct ca_state* st) {
if (!st) return; if (!st) return;
if (st->inst && st->inst->chat_connections) {
struct ll_entry* e = queue_find_data_by_index(st->inst->chat_connections, (const uint8_t*)&st->node_id);
if (e) { queue_remove_data(st->inst->chat_connections, e); queue_entry_free(e); }
}
if (st->ctxs) { for (int i = 0; i < st->addr_count; i++) u_free(st->ctxs[i]); u_free(st->ctxs); } if (st->ctxs) { for (int i = 0; i < st->addr_count; i++) u_free(st->ctxs[i]); u_free(st->ctxs); }
u_free(st->conns); u_free(st->conns);
u_free(st->timers); u_free(st->timers);
@ -962,10 +953,7 @@ void chat_core_connect_auto(uint64_t node_id,
} }
sc_init_ctx(&conn->crypto_ctx, &g_cc.inst->my_keys); sc_init_ctx(&conn->crypto_ctx, &g_cc.inst->my_keys);
sc_set_peer_public_key(&conn->crypto_ctx, pubkey, 0); sc_set_peer_public_key(&conn->crypto_ctx, pubkey, 0);
conn->peer_node_id = node_id; etcp_conn_set_peer_node_id(conn, node_id);
conn->next = g_cc.inst->connections;
g_cc.inst->connections = conn;
g_cc.inst->connections_count++;
struct ca_ctx* pctx = u_calloc(1, sizeof(struct ca_ctx)); struct ca_ctx* pctx = u_calloc(1, sizeof(struct ca_ctx));
if (!pctx) { etcp_connection_close(conn); pst->conns[i] = NULL; pst->timers[i] = NULL; pst->ctxs[i] = NULL; pst->pending_count--; continue; } if (!pctx) { etcp_connection_close(conn); pst->conns[i] = NULL; pst->timers[i] = NULL; pst->ctxs[i] = NULL; pst->pending_count--; continue; }
@ -980,7 +968,6 @@ void chat_core_connect_auto(uint64_t node_id,
pst->conns[i] = NULL; pst->timers[i] = NULL; pst->ctxs[i] = NULL; pst->pending_count--; continue; pst->conns[i] = NULL; pst->timers[i] = NULL; pst->ctxs[i] = NULL; pst->pending_count--; continue;
} }
pst->conns[i] = conn; pst->conns[i] = conn;
{ struct ll_entry* qe = queue_entry_new(16); if (qe) { *(uint64_t*)qe->data = node_id; *(struct ETCP_CONN**)(qe->data+8) = conn; queue_data_put_with_index(g_cc.inst->chat_connections, qe); } }
pst->timers[i] = uasync_set_timeout(g_cc.inst->ua, CA_CONNECT_TIMEOUT_MS * 10, pst->timers[i] = uasync_set_timeout(g_cc.inst->ua, CA_CONNECT_TIMEOUT_MS * 10,
pctx, ca_timeout_cb, "ca_timeout"); pctx, ca_timeout_cb, "ca_timeout");
DEBUG_INFO(DEBUG_CATEGORY_CONNECTIVITY, "%s: auto_connect attempt %d/%d to %d.%d.%d.%d:%d", DEBUG_INFO(DEBUG_CATEGORY_CONNECTIVITY, "%s: auto_connect attempt %d/%d to %d.%d.%d.%d:%d",

74
tools/chatgui/transport/chat_sync.c

@ -90,15 +90,18 @@ static void ac_gc(struct auto_connect* ac) {
if (now - ac->flights[i].created_tb < deadline) continue; if (now - ac->flights[i].created_tb < deadline) continue;
uint64_t nid = ac->flights[i].node_id; uint64_t nid = ac->flights[i].node_id;
/* check if link already UP — if so, just free slot (already connected) */ /* check if link already UP — if so, just free slot (already connected) */
struct ETCP_CONN* c = ac->inst->connections;
int found_up = 0; int found_up = 0;
while (c) { {
if (c->peer_node_id == nid) { struct ll_entry* entry = ac->inst->connections->head;
struct ETCP_LINK* l = c->links; while (entry) {
struct conn_queue_entry* ce = (struct conn_queue_entry*)entry->data;
if (ce->conn->peer_node_id == nid) {
struct ETCP_LINK* l = ce->conn->links;
while (l) { if (l->initialized && l->link_status) { found_up = 1; break; } l = l->next; } while (l) { if (l->initialized && l->link_status) { found_up = 1; break; } l = l->next; }
if (found_up) break; if (found_up) break;
} }
c = c->next; entry = entry->next;
}
} }
if (found_up) { if (found_up) {
chat_core_connect_auto_cancel(ac->flights[i].ca_state); chat_core_connect_auto_cancel(ac->flights[i].ca_state);
@ -130,12 +133,9 @@ static int ac_find_free_slot(struct auto_connect* ac) {
/* ── check if node already has active ETCP connection ── */ /* ── check if node already has active ETCP connection ── */
static int ac_node_has_conn(struct UTUN_INSTANCE* inst, uint64_t nid) { static int ac_node_has_conn(struct UTUN_INSTANCE* inst, uint64_t nid) {
struct ETCP_CONN* c = inst->connections; if (!inst->connections) return 0;
while (c) { struct ll_entry* e = queue_find_data_by_index(inst->connections, (const uint8_t*)&nid);
if (c->peer_node_id == nid) return 1; return e != NULL;
c = c->next;
}
return 0;
} }
/* ── fill up to AC_MAX_FLIGHTS by advancing cursor ── */ /* ── fill up to AC_MAX_FLIGHTS by advancing cursor ── */
@ -433,25 +433,23 @@ static void cs_join_timeout_cb(void* arg) {
/* ── helper: find active ETCP_CONN for node ── */ /* ── helper: find active ETCP_CONN for node ── */
struct CHAT_CONN_ENTRY { uint64_t node_id; struct ETCP_CONN* conn; };
static struct ETCP_CONN* cs_find_conn_for_node(struct UTUN_INSTANCE* inst, uint64_t node_id) { static struct ETCP_CONN* cs_find_conn_for_node(struct UTUN_INSTANCE* inst, uint64_t node_id) {
struct ll_entry* e = queue_find_data_by_index(inst->chat_connections, (const uint8_t*)&node_id); if (!inst->connections) return NULL;
if (e) { struct CHAT_CONN_ENTRY* ce = (struct CHAT_CONN_ENTRY*)e->data; if (ce->conn->initialized && ce->conn->links_up) return ce->conn; } 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; if (ce->conn->initialized && ce->conn->links_up) return ce->conn; }
return NULL; return NULL;
} }
/* ── helper: check if peer has active ETCP link ── */ /* ── helper: check if peer has active ETCP link ── */
static int cs_is_peer_online(struct UTUN_INSTANCE* inst, uint64_t peer_id) { static int cs_is_peer_online(struct UTUN_INSTANCE* inst, uint64_t peer_id) {
struct ETCP_CONN* c = inst->connections; if (!inst->connections) return 0;
while (c) { struct ll_entry* e = queue_find_data_by_index(inst->connections, (const uint8_t*)&peer_id);
if (c->peer_node_id == peer_id) { if (e) {
struct ETCP_LINK* l = c->links; struct conn_queue_entry* ce = (struct conn_queue_entry*)e->data;
struct ETCP_LINK* l = ce->conn->links;
while (l) { if (l->initialized && l->link_status) return 1; l = l->next; } while (l) { if (l->initialized && l->link_status) return 1; l = l->next; }
} }
c = c->next;
}
return 0; return 0;
} }
@ -509,10 +507,6 @@ static void cs_on_conn_down(struct ETCP_CONN* conn, void* arg) {
(void)arg; (void)arg;
if (!conn || !g_cs) return; if (!conn || !g_cs) return;
uint64_t peer = conn->peer_node_id; uint64_t peer = conn->peer_node_id;
if (g_cs->inst->chat_connections) {
struct ll_entry* e = queue_find_data_by_index(g_cs->inst->chat_connections, (const uint8_t*)&peer);
if (e) { queue_remove_data(g_cs->inst->chat_connections, e); queue_entry_free(e); }
}
uint16_t rtt = conn->rtt_avg_100; uint16_t rtt = conn->rtt_avg_100;
if (rtt > 0 && peer != 0 && g_cs->inst->topo_groups && g_cs->inst->topo_groups->topo_sqlite_db) { if (rtt > 0 && peer != 0 && g_cs->inst->topo_groups && g_cs->inst->topo_groups->topo_sqlite_db) {
sqlite3* db = g_cs->inst->topo_groups->topo_sqlite_db; sqlite3* db = g_cs->inst->topo_groups->topo_sqlite_db;
@ -617,13 +611,14 @@ int chat_sync_init(struct UTUN_INSTANCE* inst,
etcp_bind(inst, ETCP_RT_ID_CHAT_SYNC, chat_sync_recv_cb); etcp_bind(inst, ETCP_RT_ID_CHAT_SYNC, chat_sync_recv_cb);
etcp_add_new_conn_cbk(inst, cs_on_new_conn, NULL); etcp_add_new_conn_cbk(inst, cs_on_new_conn, NULL);
inst->chat_connections = queue_new(inst->ua, 16, 0, 8, "chat_conns"); {
struct ll_entry* entry = inst->connections->head;
struct ETCP_CONN* c = inst->connections; while (entry) {
while (c) { struct conn_queue_entry* ce = (struct conn_queue_entry*)entry->data;
etcp_conn_add_up_cbk(c, cs_on_conn_up, NULL); etcp_conn_add_up_cbk(ce->conn, cs_on_conn_up, NULL);
etcp_conn_add_down_cbk(c, cs_on_conn_down, NULL); etcp_conn_add_down_cbk(ce->conn, cs_on_conn_down, NULL);
c = c->next; entry = entry->next;
}
} }
cs_refresh_channels(cs); cs_refresh_channels(cs);
@ -646,17 +641,18 @@ void chat_sync_destroy(struct UTUN_INSTANCE* inst) {
member_sync_destroy(inst); member_sync_destroy(inst);
cs->initialized = 0; g_cs = NULL; cs->initialized = 0; g_cs = NULL;
queue_free(inst->chat_connections); inst->chat_connections = NULL;
etcp_unbind(inst, ETCP_RT_ID_CHAT_SYNC); etcp_unbind(inst, ETCP_RT_ID_CHAT_SYNC);
if (cs->refresh_timer) { uasync_cancel_timeout(inst->ua, cs->refresh_timer); cs->refresh_timer = NULL; } if (cs->refresh_timer) { uasync_cancel_timeout(inst->ua, cs->refresh_timer); cs->refresh_timer = NULL; }
cs_cancel_proto_timers(cs); cs_cancel_proto_timers(cs);
struct ETCP_CONN* c = inst->connections; {
while (c) { struct ll_entry* entry = inst->connections->head;
etcp_conn_remove_up_cbk(c, cs_on_conn_up, NULL); while (entry) {
etcp_conn_remove_down_cbk(c, cs_on_conn_down, NULL); struct conn_queue_entry* ce = (struct conn_queue_entry*)entry->data;
c = c->next; etcp_conn_remove_up_cbk(ce->conn, cs_on_conn_up, NULL);
etcp_conn_remove_down_cbk(ce->conn, cs_on_conn_down, NULL);
entry = entry->next;
}
} }
for (int i = 0; i < cs->channel_count; i++) for (int i = 0; i < cs->channel_count; i++)

Loading…
Cancel
Save