From d310bccf082c1fdb26bddfb089686aa08bf268b3 Mon Sep 17 00:00:00 2001 From: evgeny Date: Fri, 31 Jul 2026 21:44:44 +0300 Subject: [PATCH] fix: memory pool alloc mismatch in tests, db_sync SEND_DATA buffer overflow MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - test_etcp_router_reconnect, test_etcp_connect, test_stcp_traffic: replace u_calloc with memory_pool_alloc for TOPO_SOCKMETA4/TOPO_ADDR4 - db_sync.c: fix req[14] -> req[15] — SEND_DATA request header is 15 bytes (type:1 + from:4 + count:2 + vp:4 + want_from:4) --- src/Makefile.am | 2 + src/chat/chat_sync.c | 12 +- src/chat/db_sync.c | 4 +- src/control_server.c | 14 +- src/control_server.h | 4 +- src/media_delivery/media_delivery.c | 5 +- src/ntp_node_time.c | 6 +- src/routing_layer/conn_mgr.c | 336 ++++++------ src/routing_layer/conn_mgr.h | 121 +++-- src/routing_layer/etcp_router.c | 12 +- src/routing_layer/nat_detection.c | 30 +- src/routing_layer/route6_lib.c | 8 +- src/routing_layer/route6_lib.h | 6 +- src/routing_layer/route_connectivity.c | 38 +- src/routing_layer/route_connectivity.h | 4 +- src/routing_layer/route_lib.c | 10 +- src/routing_layer/route_lib.h | 8 +- src/routing_layer/route_ping.c | 2 +- src/routing_layer/routing.c | 9 +- src/routing_layer/topo_group.c | 97 ++-- src/routing_layer/topo_group.h | 8 +- src/routing_layer/topo_group_connect.c | 4 +- src/routing_layer/topo_node.c | 100 ++-- src/routing_layer/topo_node_sqlite.c | 9 +- src/routing_layer/topo_node_sqlite.h | 6 +- src/routing_layer/topo_recovery.c | 45 +- src/routing_layer/topo_recovery.h | 5 +- src/transport_layer/etcp.c | 38 +- src/transport_layer/etcp.h | 19 +- src/transport_layer/etcp_api.c | 45 +- src/transport_layer/etcp_api.h | 52 +- src/transport_layer/etcp_connect.c | 63 ++- src/transport_layer/etcp_connect.h | 8 +- src/transport_layer/etcp_connections.c | 26 +- src/transport_layer/node_conn_direct.c | 670 ++++++++++++++++++++++++ src/transport_layer/node_conn_direct.h | 67 +++ src/utun_instance.h | 2 +- tests/Makefile.am | 5 + tests/test_bgp_route_exchange.c | 2 +- tests/test_bgp_triangle.c | 8 +- tests/test_conn_mgr.c | 4 +- tests/test_conn_mgr_already_connected.c | 7 +- tests/test_etcp_connect.c | 27 +- tests/test_etcp_router_reconnect.c | 24 +- tests/test_etcp_router_unit.c | 12 +- tests/test_ipv6_sockets.c | 29 +- tests/test_nat_detection.c | 28 +- tests/test_node_conn_direct.c | 251 +++++++++ tests/test_route6_lib.c | 41 +- tests/test_route_lib.c | 28 +- tests/test_route_ping.c | 9 +- tests/test_stcp_traffic.c | 11 +- 52 files changed, 1796 insertions(+), 585 deletions(-) create mode 100644 src/transport_layer/node_conn_direct.c create mode 100644 src/transport_layer/node_conn_direct.h create mode 100644 tests/test_node_conn_direct.c diff --git a/src/Makefile.am b/src/Makefile.am index 70b3f5d4..cff65a72 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -41,6 +41,7 @@ utun_CORE_SOURCES = \ transport_layer/packet_dump.c \ transport_layer/etcp_api.c \ transport_layer/etcp_connect.c \ + transport_layer/node_conn_direct.c \ control_server.c \ firewall.c \ eim_nat.c \ @@ -112,6 +113,7 @@ libutun_a_SOURCES = \ transport_layer/packet_dump.c \ transport_layer/etcp_api.c \ transport_layer/etcp_connect.c \ + transport_layer/node_conn_direct.c \ control_server.c \ firewall.c \ eim_nat.c \ diff --git a/src/chat/chat_sync.c b/src/chat/chat_sync.c index a8b2f437..9cadec04 100644 --- a/src/chat/chat_sync.c +++ b/src/chat/chat_sync.c @@ -415,7 +415,7 @@ static void cs_start_channel_join(struct chat_sync* cs, uint64_t peer) { /* ── Connection callbacks ── */ -static void cs_on_conn_up(struct ETCP_CONN* conn, void* arg) { +static void cs_on_conn_up(struct ETCP_CONN* conn, int event, void* arg) { (void)event; (void)arg; if (!conn || !g_cs) return; uint64_t peer = conn->peer_node_id; @@ -438,7 +438,7 @@ static void cs_on_conn_up(struct ETCP_CONN* conn, void* arg) { cs_on_peer_status_changed(peer, 1); } -static void cs_on_conn_down(struct ETCP_CONN* conn, void* arg) { +static void cs_on_conn_down(struct ETCP_CONN* conn, int event, void* arg) { (void)event; (void)arg; if (!conn || !g_cs) return; uint64_t peer = conn->peer_node_id; @@ -467,8 +467,8 @@ static void cs_on_conn_down(struct ETCP_CONN* conn, void* arg) { static void cs_on_conn_status(struct ETCP_CONN* conn, int status, void* arg) { switch (status) { - case ETCP_CONN_STATUS_UP: cs_on_conn_up(conn, arg); break; - case ETCP_CONN_STATUS_DOWN: cs_on_conn_down(conn, arg); break; + case ETCP_CONN_STATUS_UP: cs_on_conn_up(conn, ETCP_CBK_EVENT_UP, arg); break; + case ETCP_CONN_STATUS_DOWN: cs_on_conn_down(conn, ETCP_CBK_EVENT_DOWN, arg); break; default: break; } } @@ -478,8 +478,8 @@ static void cs_on_new_conn(struct ETCP_CONN* conn, void* arg) { (void)arg; if (!conn) return; DEBUG_TRACE(DEBUG_CATEGORY_DB_SYNC, "%s: new_conn conn=%p", CS_ID, (void*)conn); - etcp_conn_add_up_cbk(conn, cs_on_conn_up, NULL); - etcp_conn_add_down_cbk(conn, cs_on_conn_down, NULL); + etcp_conn_add_cbk(conn, cs_on_conn_up, NULL, ETCP_CBK_EVENT_UP); + etcp_conn_add_cbk(conn, cs_on_conn_down, NULL, ETCP_CBK_EVENT_DOWN); } #endif diff --git a/src/chat/db_sync.c b/src/chat/db_sync.c index 4b2ea6ae..14389c9f 100644 --- a/src/chat/db_sync.c +++ b/src/chat/db_sync.c @@ -1089,10 +1089,10 @@ static void db_handle_sync_done(struct DB_SYNC_INSTANCE* si, uint64_t src, const } else if (mc < pc) { // Request tail uint32_t vp_send = mc > 0 ? mc - 1 : (uint32_t)-1; - uint8_t req[14]; req[0] = DB_MSG_SEND_DATA; + uint8_t req[15]; req[0] = DB_MSG_SEND_DATA; memcpy(req + 1, &mc, 4); uint16_t z = 0; memcpy(req + 5, &z, 2); memcpy(req + 7, &vp_send, 4); memcpy(req + 11, &mc, 4); - db_sync_send(si, src, req, 14); + db_sync_send(si, src, req, 15); if (sp) sp->sync_state = 1; DEBUG_INFO(DEBUG_CATEGORY_DB_SYNC, "sync [%s:%04llX] ← SYNC_DONE: mc=%u < pc=%u → request tail from=%u", SI_SHRT(si), (unsigned long long)(src >> 16), mc, pc, mc); diff --git a/src/control_server.c b/src/control_server.c index a06368b8..d214ed65 100644 --- a/src/control_server.c +++ b/src/control_server.c @@ -101,7 +101,7 @@ static void send_debug_config(struct control_server* server, struct control_clie static void send_error(struct control_client* client, uint8_t error_code, const char* msg, uint8_t seq_id); static void control_server_send_node_list(struct control_server* server, struct control_client* client, uint8_t seq_id); -static void send_single_node_info(struct control_server* server, struct control_client* client, struct TOPO_NODEQ* nq); +static void send_single_node_info(struct control_server* server, struct control_client* client, struct TOPO_GROUP_NODE* nq); static int control_client_send(struct control_server* server, struct control_client* client, uint8_t* dgram, uint16_t len); static struct ETCP_CONN* find_connection_by_peer_id(struct UTUN_INSTANCE* instance, uint64_t peer_id); @@ -1385,10 +1385,12 @@ static struct ETCP_CONN* find_connection_by_peer_id(struct UTUN_INSTANCE* instan * Node Info Helpers (send_single_node_info, send_node_list, notify_change) * ============================================================================ */ -static void send_single_node_info(struct control_server* server, struct control_client* client, struct TOPO_NODEQ* nq) { - if (!server || !nq || !nq->node) return; +static void send_single_node_info(struct control_server* server, struct control_client* client, struct TOPO_GROUP_NODE* nq) { + if (!server || !nq) return; + struct TOPO_NODE* ni = topo_node_registry_find(server->instance->topo_groups, nq->node_id); + if (!ni) return; uint8_t buf[8192]; - int ser_len = topo_node_serialize(topo_groups_get_default(server->instance->topo_groups), nq, buf + 2, sizeof(buf) - 2, 0); + int ser_len = topo_node_serialize(ni, nq, buf + 2, sizeof(buf) - 2, 0); if (ser_len < 0) { DEBUG_ERROR(DEBUG_CATEGORY_CONTROL, "Failed to serialize node info"); return; } buf[0] = CTRL_BGP_CMD; buf[1] = CTRL_BGP_SUBCMD; size_t data_size = 2 + (size_t)ser_len; @@ -1414,7 +1416,7 @@ static void control_server_send_node_list(struct control_server* server, struct /* Send all known remote nodes */ struct ll_entry* e = group->nodes ? group->nodes->head : NULL; while (e) { - struct TOPO_NODEQ* nq = (struct TOPO_NODEQ*)e; + struct TOPO_GROUP_NODE* nq = (struct TOPO_GROUP_NODE*)e; send_single_node_info(server, client, nq); e = e->next; } @@ -1429,7 +1431,7 @@ static void control_server_send_node_list(struct control_server* server, struct DEBUG_INFO(DEBUG_CATEGORY_CONTROL, "Sent node list to client (seq=%d)", seq_id); } -void control_server_notify_node_change(struct control_server* server, struct TOPO_NODEQ* node) { +void control_server_notify_node_change(struct control_server* server, struct TOPO_GROUP_NODE* node) { if (!server || !node) return; struct control_client* client = server->clients; while (client) { diff --git a/src/control_server.h b/src/control_server.h index 3edfc8ca..c12b375d 100644 --- a/src/control_server.h +++ b/src/control_server.h @@ -19,7 +19,7 @@ extern "C" { /* Forward declarations */ struct UTUN_INSTANCE; struct UASYNC; -struct TOPO_NODEQ; +struct TOPO_GROUP_NODE; struct ll_queue; /* Forward declaration */ @@ -117,7 +117,7 @@ void control_server_process_updates(struct control_server* server); */ uint32_t control_server_get_client_count(const struct control_server* server); -void control_server_notify_node_change(struct control_server* server, struct TOPO_NODEQ* node); +void control_server_notify_node_change(struct control_server* server, struct TOPO_GROUP_NODE* node); void control_server_notify_node_removed(struct control_server* server, uint64_t node_id); #ifdef __cplusplus diff --git a/src/media_delivery/media_delivery.c b/src/media_delivery/media_delivery.c index fa9caae1..136a6c35 100644 --- a/src/media_delivery/media_delivery.c +++ b/src/media_delivery/media_delivery.c @@ -802,8 +802,9 @@ static void md_on_bgp_node(struct TOPO_GROUP* group, uint64_t node_id, int event } /* check if this node is a supernode */ - struct TOPO_NODEQ* nq = topo_node_find_by_id(group, node_id); - if (!nq || !nq->node) return; + struct TOPO_GROUP_NODE* nq = topo_node_find_by_id(group, node_id); + struct TOPO_NODE* ni = nq ? topo_node_registry_find(group->instance->topo_groups, nq->node_id) : NULL; + if (!nq || !ni) return; /* for CHAT groups, check node_type via DB */ if (group->group_type == TOPO_GROUP_TYPE_CHAT && group->channel_id[0]) { diff --git a/src/ntp_node_time.c b/src/ntp_node_time.c index 771562f7..eab6584b 100644 --- a/src/ntp_node_time.c +++ b/src/ntp_node_time.c @@ -23,7 +23,7 @@ _Static_assert(sizeof(struct time_sync_msg) == 9, "time_sync_msg size mismatch") #define TIME_SYNC_PKT_SIZE (1 + sizeof(struct time_sync_msg)) // cmd(1) + msg(9) = 10 -static void ntp_node_on_conn_init(struct ETCP_CONN* conn, void* arg); +static void ntp_node_on_conn_init(struct ETCP_CONN* conn, int event, void* arg); static void ntp_node_on_new_conn(struct ETCP_CONN* conn, void* arg); static void ntp_node_recv_cb(struct ETCP_CONN* conn, struct ll_entry* entry); @@ -103,7 +103,7 @@ static void check_drift(uint64_t node_id, int64_t offset_delta_us) { } } -static void ntp_node_on_conn_init(struct ETCP_CONN* conn, void* arg) { +static void ntp_node_on_conn_init(struct ETCP_CONN* conn, int event, void* arg) { (void)event; struct UTUN_INSTANCE* inst = (struct UTUN_INSTANCE*)arg; if (!inst || !conn) return; @@ -117,7 +117,7 @@ static void ntp_node_on_new_conn(struct ETCP_CONN* conn, void* arg) { if (!conn) return; struct UTUN_INSTANCE* inst = (struct UTUN_INSTANCE*)arg; if (!inst) return; - etcp_conn_add_init_cbk(conn, ntp_node_on_conn_init, inst); + etcp_conn_add_cbk(conn, ntp_node_on_conn_init, inst, ETCP_CBK_EVENT_INIT); } static void ntp_node_recv_cb(struct ETCP_CONN* conn, struct ll_entry* entry) { diff --git a/src/routing_layer/conn_mgr.c b/src/routing_layer/conn_mgr.c index 5932624c..fee03c1f 100644 --- a/src/routing_layer/conn_mgr.c +++ b/src/routing_layer/conn_mgr.c @@ -24,6 +24,7 @@ #include "route_ping.h" #include "route_connectivity.h" #include "conn_mgr.h" +#include "../transport_layer/node_conn_direct.h" static struct CONN_MGR_ENTRY* cm_find_entry(struct CONN_MGR* mgr, uint64_t node_id); static void cm_start_phase_direct(struct CONN_MGR_ENTRY* entry); @@ -34,28 +35,40 @@ static void cm_idle_timer_cb(void* arg); static void cm_deliver_result(struct CONN_MGR_ENTRY* entry, int result); static void cm_bg_ping_timer_cb(void* arg); static int cm_nat_compatible(struct ETCP_SOCKET* our, uint8_t target_config_type, uint8_t target_nat_type); -static uint16_t cm_get_node_min_rtt(struct TOPO_NODEQ* nq); -static int cm_has_direct_ip(struct TOPO_NODEQ* nq); -static int cm_has_local_addr(struct TOPO_NODEQ* nq); +static uint16_t cm_get_node_min_rtt(struct TOPO_GROUP_NODE* nq); +static int cm_has_direct_ip(struct CONN_MGR* mgr, struct TOPO_GROUP_NODE* nq); +static int cm_has_local_addr(struct CONN_MGR* mgr, struct TOPO_GROUP_NODE* nq); static void cm_entry_destroy(struct CONN_MGR_ENTRY* entry); static void cm_exchange_probe_retry_cb(void* arg); -static uint64_t cm_get_node_max_probe_time(struct TOPO_NODEQ* nq); -static int cm_is_rtt_fresh(struct TOPO_NODEQ* nq, uint64_t now_tb); +static uint64_t cm_get_node_max_probe_time(struct TOPO_GROUP_NODE* nq); +static int cm_is_rtt_fresh(struct TOPO_GROUP_NODE* nq, uint64_t now_tb); static void cm_clear_nodeinfo(struct CONN_MGR* mgr, uint64_t node_id); +static struct CONN_MGR_HANDLE* cm_handle_new(struct CONN_MGR_ENTRY* entry, + conn_mgr_connect_callback_t cb, void* cb_arg) { + struct CONN_MGR_HANDLE* h = u_calloc(1, sizeof(*h)); + if (!h) return NULL; + h->target_node_id = entry->node_id; + h->conn_type = CONN_TYPE_NONE; + h->result_cb = cb; h->result_arg = cb_arg; + h->entry = entry; + h->next = entry->handles; entry->handles = h; + return h; +} + static void conn_mgr_router_recv_handler(struct ETCP_CONN* conn, struct ll_entry* entry); static void cm_handle_direct_req(struct ETCP_CONN* conn, const uint8_t* data, size_t len); static void cm_handle_interm_exchange_req(struct ETCP_CONN* conn, struct CONN_MGR_INTERM_EXCHANGE_REQ* req); static void cm_handle_interm_exchange_resp(struct CONN_MGR* mgr, const uint8_t* data, size_t len); static void cm_handle_interm_selected(struct CONN_MGR* mgr, const uint8_t* data, size_t len); static void cm_handle_disconnect(struct CONN_MGR* mgr, uint64_t node_id); -static void cm_direct_init_cb(struct ETCP_CONN* conn, void* arg); -static void cm_reverse_init_cb(struct ETCP_CONN* conn, void* arg); +static void cm_direct_init_cb(struct ETCP_CONN* conn, int event, void* arg); +static void cm_reverse_init_cb(struct ETCP_CONN* conn, int event, void* arg); static void cm_reverse_timeout_cb(void* arg); static void cm_exchange_timeout_cb(void* arg); static void cm_candidate_ping_timer_cb(void* arg); -static void cm_invite_init_cb(struct ETCP_CONN* conn, void* arg); +static void cm_invite_init_cb(struct ETCP_CONN* conn, int event, void* arg); static void cm_invite_overall_timeout(void* arg); static void cm_invite_fail(struct cm_invite_pending* inv, int result); static void cm_invite_cleanup(struct cm_invite_pending* inv); @@ -101,7 +114,7 @@ struct cm_invite_pending { void* overall_timer; conn_mgr_connect_callback_t cb; void* cb_arg; - struct TOPO_NODEQ* temp_nq; + struct TOPO_GROUP_NODE* temp_nq; }; struct CONN_MGR* conn_mgr_init(struct TOPO_GROUP* group) { @@ -191,23 +204,23 @@ static struct CONN_MGR_ENTRY* cm_find_entry(struct CONN_MGR* mgr, uint64_t node_ } static void cm_deliver_result(struct CONN_MGR_ENTRY* entry, int result) { - struct cm_cb_node* node = entry->cb_list; - entry->cb_list = NULL; - int delivered = 0; - while (node) { - struct cm_cb_node* next = node->next; - if (!node->cancelled) { node->cb(result, entry->node_id, node->arg); delivered++; } - u_free(node); - node = next; - } - if (delivered == 0 && entry->state == CONN_MGR_STATE_CONNECTING) + struct CONN_MGR_HANDLE* h = entry->handles; + entry->handles = NULL; + while (h) { + struct CONN_MGR_HANDLE* next = h->next; + if (h->result_cb) h->result_cb(result, entry->node_id, h->result_arg); + h->nd_handle = NULL; h->entry = NULL; + u_free(h); + h = next; + } + if (entry->state == CONN_MGR_STATE_CONNECTING) cm_clear_nodeinfo(entry->mgr, entry->node_id); } static void cm_update_nodeinfo(struct CONN_MGR_ENTRY* entry) { struct TOPO_GROUP* group = entry->mgr->group; if (!group) return; - struct TOPO_NODEQ* nq = topo_node_find_by_id(group, entry->node_id); + struct TOPO_GROUP_NODE* nq = topo_node_find_by_id(group, entry->node_id); if (!nq) return; nq->conn_mgr_type = entry->conn_type; memcpy(nq->conn_mgr_intermediaries, entry->intermediaries, sizeof(entry->intermediaries)); @@ -215,7 +228,7 @@ static void cm_update_nodeinfo(struct CONN_MGR_ENTRY* entry) { } static void cm_clear_nodeinfo(struct CONN_MGR* mgr, uint64_t node_id) { - struct TOPO_NODEQ* nq = topo_node_find_by_id(mgr->group, node_id); + struct TOPO_GROUP_NODE* nq = topo_node_find_by_id(mgr->group, node_id); if (!nq) return; nq->conn_mgr_type = CONN_TYPE_NONE; nq->conn_mgr_intermediariy_count = 0; @@ -224,7 +237,7 @@ static void cm_clear_nodeinfo(struct CONN_MGR* mgr, uint64_t node_id) { static void cm_cleanup_db_node(struct CONN_MGR_ENTRY* entry) { if (!entry->db_loaded) return; struct TOPO_GROUP* group = entry->mgr->group; - struct TOPO_NODEQ* nq = topo_node_find_by_id(group, entry->node_id); + struct TOPO_GROUP_NODE* nq = topo_node_find_by_id(group, entry->node_id); if (!nq) return; if (nq->paths && queue_entry_count(nq->paths) > 0) { DEBUG_INFO(DEBUG_CATEGORY_GENERAL, "conn_mgr: db_node 0x%016llx got paths, keep in group", (unsigned long long)entry->node_id); @@ -232,48 +245,44 @@ static void cm_cleanup_db_node(struct CONN_MGR_ENTRY* entry) { } DEBUG_INFO(DEBUG_CATEGORY_GENERAL, "conn_mgr: removing db_node 0x%016llx from group (no paths)", (unsigned long long)entry->node_id); queue_remove_data(group->nodes, &nq->ll); - topo_node_free_lists(group, nq); + topo_nodeq_free_group_fields(entry->mgr->instance->topo_groups, nq); queue_entry_free(&nq->ll); } -int conn_mgr_connect_node(struct CONN_MGR* mgr, uint64_t node_id, uint32_t idle_timeout_ms, +struct CONN_MGR_HANDLE* conn_mgr_connect_node(struct CONN_MGR* mgr, uint64_t node_id, uint32_t idle_timeout_ms, conn_mgr_connect_callback_t cb, void* cb_arg) { - if (!mgr) return CONN_MGR_ERR_INTERNAL; + if (!mgr) { if (cb) cb(CONN_MGR_ERR_INTERNAL, node_id, cb_arg); return NULL; } struct TOPO_GROUP* group = mgr->group; - if (!group) { DEBUG_ERROR(DEBUG_CATEGORY_GENERAL, "BGP not initialized"); return CONN_MGR_ERR_INTERNAL; } - struct TOPO_NODEQ* target = topo_node_find_by_id(group, node_id); + if (!group) { DEBUG_ERROR(DEBUG_CATEGORY_GENERAL, "BGP not initialized"); if (cb) cb(CONN_MGR_ERR_INTERNAL, node_id, cb_arg); return NULL; } + struct TOPO_GROUP_NODE* target = topo_node_find_by_id(group, node_id); int loaded_from_db = 0; if (!target && mgr->instance->topo_sqlite_db && mgr->instance->topo_groups) { struct TOPO_NODE* ni = topo_node_sqlite_node_load(mgr->instance->topo_sqlite_db, mgr->instance->topo_groups, node_id); if (ni) { - ni = topo_node_registry_acquire(mgr->instance->topo_groups, ni); - struct ll_entry* qe = queue_entry_new(sizeof(struct TOPO_NODEQ)); + ni = topo_node_registry_store(mgr->instance->topo_groups, ni); + struct ll_entry* qe = queue_entry_new(sizeof(struct TOPO_GROUP_NODE)); if (qe) { - target = (struct TOPO_NODEQ*)qe; + target = (struct TOPO_GROUP_NODE*)qe; memset((uint8_t*)target + sizeof(struct ll_entry), 0, sizeof(*target) - sizeof(struct ll_entry)); - target->node = ni; target->hash_node_id = node_id; + target->node_id = node_id; target->conn_mgr_type = CONN_TYPE_NONE; queue_data_put_with_index(group->nodes, &target->ll); loaded_from_db = 1; DEBUG_INFO(DEBUG_CATEGORY_GENERAL, "conn_mgr: node 0x%016llx loaded from DB", (unsigned long long)node_id); - } else { DEBUG_ERROR(DEBUG_CATEGORY_GENERAL, "conn_mgr: failed to alloc TOPO_NODEQ for DB node 0x%016llx", (unsigned long long)node_id); } + } else { DEBUG_ERROR(DEBUG_CATEGORY_GENERAL, "conn_mgr: failed to alloc TOPO_GROUP_NODE for DB node 0x%016llx", (unsigned long long)node_id); } } } - if (!target) { DEBUG_ERROR(DEBUG_CATEGORY_GENERAL, "node 0x%016llx not found", (unsigned long long)node_id); return CONN_MGR_ERR_NOT_FOUND; } + if (!target) { DEBUG_ERROR(DEBUG_CATEGORY_GENERAL, "node 0x%016llx not found", (unsigned long long)node_id); return NULL; } struct CONN_MGR_ENTRY* entry = cm_find_entry(mgr, node_id); + struct CONN_MGR_HANDLE* h = NULL; if (entry && entry->state == CONN_MGR_STATE_CONNECTED) { - if (cb) { - struct cm_cb_node* cn = u_calloc(1, sizeof(struct cm_cb_node)); - if (cn) { cn->cb = cb; cn->arg = cb_arg; entry->cb_list = cn; } - cb(CONN_MGR_OK, node_id, cb_arg); - } - return CONN_MGR_OK; + h = cm_handle_new(entry, cb, cb_arg); + if (cb) cb(CONN_MGR_OK, node_id, cb_arg); + return h; } if (entry && entry->state == CONN_MGR_STATE_CONNECTING) { - DEBUG_WARN(DEBUG_CATEGORY_GENERAL, "node 0x%016llx already connecting, adding cb", (unsigned long long)node_id); - struct cm_cb_node* cn = u_calloc(1, sizeof(struct cm_cb_node)); - if (cn) { cn->cb = cb; cn->arg = cb_arg; cn->next = entry->cb_list; entry->cb_list = cn; } - return CONN_MGR_OK; + DEBUG_WARN(DEBUG_CATEGORY_GENERAL, "node 0x%016llx already connecting, adding handle", (unsigned long long)node_id); + return cm_handle_new(entry, cb, cb_arg); } struct ETCP_CONN* existing = topo_group_find_conn_for_node(group, node_id); if (!existing) existing = instance_find_conn(mgr->instance, node_id); @@ -282,23 +291,21 @@ int conn_mgr_connect_node(struct CONN_MGR* mgr, uint64_t node_id, uint32_t idle_ while (l) { if (l->link_state == 3 && l->initialized) break; l = l->next; } if (l) { entry = cm_ensure_entry(mgr, node_id); - if (!entry) return CONN_MGR_ERR_INTERNAL; + if (!entry) { if (cb) cb(CONN_MGR_ERR_INTERNAL, node_id, cb_arg); return NULL; } entry->state = CONN_MGR_STATE_CONNECTED; entry->conn_type = CONN_TYPE_DIRECT; entry->idle_timeout_ms = idle_timeout_ms; entry->last_traffic_tb = get_time_tb(); - { struct cm_cb_node* cn = u_calloc(1, sizeof(struct cm_cb_node)); - if (cn) { cn->cb = cb; cn->arg = cb_arg; entry->cb_list = cn; } } - cm_update_nodeinfo(entry); + h = cm_handle_new(entry, cb, cb_arg); + cm_update_nodeinfo(entry); cm_deliver_result(entry, CONN_MGR_OK); - return CONN_MGR_OK; + return h; } } entry = cm_ensure_entry(mgr, node_id); - if (!entry) return CONN_MGR_ERR_INTERNAL; + if (!entry) { if (cb) cb(CONN_MGR_ERR_INTERNAL, node_id, cb_arg); return NULL; } entry->state = CONN_MGR_STATE_CONNECTING; - { struct cm_cb_node* cn = u_calloc(1, sizeof(struct cm_cb_node)); - if (cn) { cn->cb = cb; cn->arg = cb_arg; cn->next = entry->cb_list; entry->cb_list = cn; } } + h = cm_handle_new(entry, cb, cb_arg); entry->idle_timeout_ms = idle_timeout_ms; entry->last_traffic_tb = get_time_tb(); entry->local_scan_state = CM_TRY_NONE; @@ -307,12 +314,12 @@ int conn_mgr_connect_node(struct CONN_MGR* mgr, uint64_t node_id, uint32_t idle_ entry->main.timer = NULL; entry->main.request_id = 0; entry->db_loaded = (uint8_t)loaded_from_db; - int has_our_local = cm_has_local_addr(group->local_node); - int has_target_local = cm_has_local_addr(target); + int has_our_local = cm_has_local_addr(mgr, group->local_node); + int has_target_local = cm_has_local_addr(mgr, target); if (has_our_local && has_target_local) { entry->local_scan_state = CM_TRY_PENDING; cm_start_local_scan(entry); } entry->main_connect_state = CM_TRY_PENDING; cm_start_phase_direct(entry); - return CONN_MGR_OK; + return h; } int conn_mgr_disconnect_node(struct CONN_MGR* mgr, uint64_t node_id) { @@ -334,32 +341,46 @@ int conn_mgr_disconnect_node(struct CONN_MGR* mgr, uint64_t node_id) { } int conn_mgr_cancel_callback(struct CONN_MGR* mgr, uint64_t node_id, - conn_mgr_connect_callback_t cb, void* cb_arg) { + struct CONN_MGR_HANDLE* h) { if (!mgr) return CONN_MGR_ERR_INTERNAL; struct CONN_MGR_ENTRY* entry = cm_find_entry(mgr, node_id); if (!entry) return CONN_MGR_ERR_NOT_FOUND; int cb_count = 0; - struct cm_cb_node* target = NULL; - for (struct cm_cb_node* n = entry->cb_list; n; n = n->next) { - cb_count++; - if (n->cb == cb && n->arg == cb_arg) target = n; + for (struct CONN_MGR_HANDLE* n = entry->handles; n; n = n->next) cb_count++; + if (cb_count == 0) return CONN_MGR_ERR_NOT_FOUND; + + if (h) { + struct CONN_MGR_HANDLE** pp = &entry->handles; + while (*pp) { if (*pp == h) { *pp = h->next; break; } pp = &(*pp)->next; } + h->nd_handle = NULL; h->entry = NULL; u_free(h); + cb_count--; } - if (!target) return CONN_MGR_ERR_NOT_FOUND; - if (cb_count == 1) { + if (cb_count <= 1 || !h) { cm_entry_destroy(entry); - u_free(target); - entry->cb_list = NULL; cm_clear_nodeinfo(mgr, node_id); - DEBUG_INFO(DEBUG_CATEGORY_GENERAL, "conn_mgr: cancel connect to 0x%016llx (single cb)", (unsigned long long)node_id); - } else { - target->cancelled = 1; - DEBUG_INFO(DEBUG_CATEGORY_GENERAL, "conn_mgr: cancel cb for 0x%016llx (cbs=%d)", (unsigned long long)node_id, cb_count); + struct CONN_MGR_HANDLE* rem = h ? entry->handles : NULL; + while (rem) { struct CONN_MGR_HANDLE* nx = rem->next; rem->nd_handle = NULL; u_free(rem); rem = nx; } + entry->handles = NULL; } return CONN_MGR_OK; } +void conn_mgr_release_handle(struct CONN_MGR_HANDLE* h) { + if (!h) return; + struct CONN_MGR_ENTRY* entry = h->entry; + if (!entry) { u_free(h); return; } + + struct CONN_MGR_HANDLE** pp = &entry->handles; + while (*pp) { if (*pp == h) { *pp = h->next; break; } pp = &(*pp)->next; } + + if (h->nd_handle) { node_conn_direct_close(h->nd_handle); h->nd_handle = NULL; } + + if (!entry->handles) { cm_entry_destroy(entry); cm_clear_nodeinfo(entry->mgr, entry->node_id); } + h->entry = NULL; u_free(h); +} + int conn_mgr_set_idle_timeout(struct CONN_MGR* mgr, uint64_t node_id, uint32_t timeout_ms) { if (!mgr) return CONN_MGR_ERR_INTERNAL; struct CONN_MGR_ENTRY* entry = cm_find_entry(mgr, node_id); @@ -455,7 +476,7 @@ static int cm_nat_compatible(struct ETCP_SOCKET* our, uint8_t target_config_type return 0; } -static uint16_t cm_get_node_min_rtt(struct TOPO_NODEQ* nq) { +static uint16_t cm_get_node_min_rtt(struct TOPO_GROUP_NODE* nq) { uint16_t rtt = 0xFFFF; if (nq->connectivity.interface_status == PROBE_RESULT_REACHABLE && nq->connectivity.interface_min_rtt < rtt) rtt = nq->connectivity.interface_min_rtt; @@ -466,14 +487,14 @@ static uint16_t cm_get_node_min_rtt(struct TOPO_NODEQ* nq) { return rtt; } -static uint64_t cm_get_node_max_probe_time(struct TOPO_NODEQ* nq) { +static uint64_t cm_get_node_max_probe_time(struct TOPO_GROUP_NODE* nq) { uint64_t t = nq->connectivity.interface_probe_time; if (nq->connectivity.nat_probe_time > t) t = nq->connectivity.nat_probe_time; if (nq->connectivity.real_probe_time > t) t = nq->connectivity.real_probe_time; return t; } -static int cm_is_rtt_fresh(struct TOPO_NODEQ* nq, uint64_t now_tb) { +static int cm_is_rtt_fresh(struct TOPO_GROUP_NODE* nq, uint64_t now_tb) { uint64_t last = cm_get_node_max_probe_time(nq); return (last > 0 && (now_tb - last) < (uint64_t)(CONN_MGR_CANDIDATE_CACHE_MS * 10)); } @@ -497,20 +518,22 @@ static uint8_t cm_sock_v6_classify(const struct ETCP_SOCKET* s) { return cm_classify_v6_addr(((const struct sockaddr_in6*)&s->local_addr)->sin6_addr.s6_addr); } -static int cm_has_direct_ip(struct TOPO_NODEQ* nq) { - if (!nq || !nq->node) return 0; - for (const struct TOPO_ADDR4* a = nq->node->v4_addrs; a; a = a->next) { +static int cm_has_direct_ip(struct CONN_MGR* mgr, struct TOPO_GROUP_NODE* nq) { + if (!nq) return 0; + struct TOPO_NODE* ni = topo_node_registry_find(mgr->instance->topo_groups, nq->node_id); + if (!ni) return 0; + for (const struct TOPO_ADDR4* a = ni->v4_addrs; a; a = a->next) { if (a->type == TOPO_ADDR_NAT || a->type == TOPO_ADDR_INTERFACE) { - for (const struct TOPO_SOCKMETA4* m = nq->node->v4_sock_meta; m; m = m->next) { + for (const struct TOPO_SOCKMETA4* m = ni->v4_sock_meta; m; m = m->next) { if (m->id == a->socket_id && (m->config_type == CFG_SERVER_TYPE_PUBLIC || m->config_type == CFG_SERVER_TYPE_UNKNOWN || m->nat_type == NAT_VERIFIED_EIM || m->nat_type == NAT_VERIFIED_DIRECT)) return 1; } } } - for (const struct TOPO_ADDR6* a6 = nq->node->v6_addrs; a6; a6 = a6->next) { + for (const struct TOPO_ADDR6* a6 = ni->v6_addrs; a6; a6 = a6->next) { if ((a6->type != TOPO_ADDR_NAT && a6->type != TOPO_ADDR_INTERFACE) || cm_classify_v6_addr(a6->addr) != CM_V6_DIRECT) continue; - for (const struct TOPO_SOCKMETA6* m = nq->node->v6_sock_meta; m; m = m->next) + for (const struct TOPO_SOCKMETA6* m = ni->v6_sock_meta; m; m = m->next) if (m->id == a6->socket_id && (m->config_type == CFG_SERVER_TYPE_PUBLIC || m->config_type == CFG_SERVER_TYPE_UNKNOWN || m->nat_type == NAT_VERIFIED_EIM || m->nat_type == NAT_VERIFIED_DIRECT)) return 1; @@ -518,21 +541,23 @@ static int cm_has_direct_ip(struct TOPO_NODEQ* nq) { return 0; } -static int cm_has_local_addr(struct TOPO_NODEQ* nq) { - if (!nq || !nq->node) return 0; - for (const struct TOPO_ADDR4* a = nq->node->v4_addrs; a; a = a->next) { +static int cm_has_local_addr(struct CONN_MGR* mgr, struct TOPO_GROUP_NODE* nq) { + if (!nq) return 0; + struct TOPO_NODE* ni = topo_node_registry_find(mgr->instance->topo_groups, nq->node_id); + if (!ni) return 0; + for (const struct TOPO_ADDR4* a = ni->v4_addrs; a; a = a->next) { if (a->type == TOPO_ADDR_INTERFACE) { - for (const struct TOPO_SOCKMETA4* m = nq->node->v4_sock_meta; m; m = m->next) { + for (const struct TOPO_SOCKMETA4* m = ni->v4_sock_meta; m; m = m->next) { if (m->id == a->socket_id && (m->config_type == CFG_SERVER_TYPE_PRIVATE || m->config_type == CFG_SERVER_TYPE_LOCAL)) return 1; } } } - for (const struct TOPO_ADDR6* a6 = nq->node->v6_addrs; a6; a6 = a6->next) { + for (const struct TOPO_ADDR6* a6 = ni->v6_addrs; a6; a6 = a6->next) { if (a6->type != TOPO_ADDR_INTERFACE) continue; uint8_t cls = cm_classify_v6_addr(a6->addr); if (cls != CM_V6_LINK_LOCAL && cls != CM_V6_LOCAL) continue; - for (const struct TOPO_SOCKMETA6* m = nq->node->v6_sock_meta; m; m = m->next) + for (const struct TOPO_SOCKMETA6* m = ni->v6_sock_meta; m; m = m->next) if (m->id == a6->socket_id && (m->config_type == CFG_SERVER_TYPE_PRIVATE || m->config_type == CFG_SERVER_TYPE_LOCAL)) return 1; } @@ -553,11 +578,12 @@ struct cm_ping_ctx { static void cm_start_local_scan(struct CONN_MGR_ENTRY* entry) { struct TOPO_GROUP* group = entry->mgr->group; - struct TOPO_NODEQ* target = topo_node_find_by_id(group, entry->node_id); - if (!target || !target->node) { entry->local_scan_state = CM_TRY_FAILED; return; } - for (const struct TOPO_ADDR4* a = target->node->v4_addrs; a; a = a->next) { + struct TOPO_GROUP_NODE* target = topo_node_find_by_id(group, entry->node_id); + struct TOPO_NODE* ni = target ? topo_node_registry_find(entry->mgr->instance->topo_groups, target->node_id) : NULL; + if (!target || !ni) { entry->local_scan_state = CM_TRY_FAILED; return; } + for (const struct TOPO_ADDR4* a = ni->v4_addrs; a; a = a->next) { if (a->type != TOPO_ADDR_INTERFACE) continue; - for (const struct TOPO_SOCKMETA4* m = target->node->v4_sock_meta; m; m = m->next) { + for (const struct TOPO_SOCKMETA4* m = ni->v4_sock_meta; m; m = m->next) { if (m->id != a->socket_id || (m->config_type != CFG_SERVER_TYPE_PRIVATE && m->config_type != CFG_SERVER_TYPE_LOCAL)) continue; struct ETCP_SOCKET* s = entry->mgr->instance->etcp_sockets; while (s) { @@ -567,7 +593,7 @@ static void cm_start_local_scan(struct CONN_MGR_ENTRY* entry) { struct sockaddr_storage sa; memset(&sa, 0, sizeof(sa)); memcpy(&sa, &sin, sizeof(sin)); struct cm_ping_ctx* ctx = u_calloc(1, sizeof(struct cm_ping_ctx)); if (!ctx) continue; ctx->entry = entry; ctx->addr = sa; ctx->sock = s; ctx->phase = 0; ctx->attempt = 0; - etcp_send_ping_to_socket(entry->mgr->instance, s, target->node->public_key, &sa, CONN_MGR_LOCAL_SCAN_TIMEOUT_MS, cm_ping_cb, ctx, NULL, 0, 0); + etcp_send_ping_to_socket(entry->mgr->instance, s, ni->public_key, &sa, CONN_MGR_LOCAL_SCAN_TIMEOUT_MS, cm_ping_cb, ctx, NULL, 0, 0); return; } s = s->next; @@ -577,21 +603,22 @@ static void cm_start_local_scan(struct CONN_MGR_ENTRY* entry) { entry->local_scan_state = CM_TRY_FAILED; } -static struct ETCP_CONN* cm_get_or_create_conn(struct CONN_MGR_ENTRY* entry, const struct TOPO_NODEQ* target) { +static struct ETCP_CONN* cm_get_or_create_conn(struct CONN_MGR_ENTRY* entry, const struct TOPO_GROUP_NODE* target) { struct ETCP_CONN* conn = topo_group_find_conn_for_node(entry->mgr->group, entry->node_id); if (!conn) conn = instance_find_conn(entry->mgr->instance, entry->node_id); if (conn) { - if (conn->peer_node_id == entry->node_id) { struct etcp_cbk_entry* cbe = conn->init_cbks; while (cbe) { struct etcp_cbk_entry* n = cbe->next; u_free(cbe); cbe = n; } conn->init_cbks = NULL; etcp_conn_add_init_cbk(conn, cm_direct_init_cb, entry); return conn; } + if (conn->peer_node_id == entry->node_id) { etcp_conn_remove_cbk(conn, cm_direct_init_cb, entry); etcp_conn_add_cbk(conn, cm_direct_init_cb, entry, ETCP_CBK_EVENT_INIT); return conn; } DEBUG_WARN(DEBUG_CATEGORY_GENERAL, "conn_mgr: existing conn peer_id mismatch 0x%016llx != 0x%016llx, ignoring", (unsigned long long)conn->peer_node_id, (unsigned long long)entry->node_id); } conn = etcp_connection_create(entry->mgr->instance, NULL); if (!conn) return NULL; sc_init_ctx(&conn->crypto_ctx, &entry->mgr->instance->my_keys); - sc_set_peer_public_key(&conn->crypto_ctx, target->node->public_key, 0); + { struct TOPO_NODE* tni = topo_node_registry_find(entry->mgr->instance->topo_groups, target->node_id); + sc_set_peer_public_key(&conn->crypto_ctx, tni ? tni->public_key : NULL, 0); } conn->peer_node_id = entry->node_id; etcp_update_log_name(conn); - etcp_conn_add_init_cbk(conn, cm_direct_init_cb, entry); + etcp_conn_add_cbk(conn, cm_direct_init_cb, entry, ETCP_CBK_EVENT_INIT); return conn; } @@ -606,8 +633,9 @@ static void cm_add_v6_link(struct ETCP_CONN* conn, const uint8_t addr[16], uint1 static void cm_start_phase_direct(struct CONN_MGR_ENTRY* entry) { if (entry->main_connect_state == CM_TRY_OK || entry->local_scan_state == CM_TRY_OK) return; struct TOPO_GROUP* group = entry->mgr->group; - struct TOPO_NODEQ* target = topo_node_find_by_id(group, entry->node_id); - if (!target || !target->node) { cm_deliver_result(entry, CONN_MGR_ERR_NOT_FOUND); return; } + struct TOPO_GROUP_NODE* target = topo_node_find_by_id(group, entry->node_id); + struct TOPO_NODE* ni = target ? topo_node_registry_find(entry->mgr->instance->topo_groups, target->node_id) : NULL; + if (!target || !ni) { cm_deliver_result(entry, CONN_MGR_ERR_NOT_FOUND); return; } struct ETCP_CONN* conn = cm_get_or_create_conn(entry, target); if (!conn) { cm_deliver_result(entry, CONN_MGR_ERR_INTERNAL); return; } @@ -616,7 +644,7 @@ static void cm_start_phase_direct(struct CONN_MGR_ENTRY* entry) { const char* timer_name = entry->db_loaded ? "conn_mgr_db_direct" : "conn_mgr_direct"; if (entry->db_loaded) { - for (const struct TOPO_ADDR4* a = target->node->v4_addrs; a; a = a->next) { + for (const struct TOPO_ADDR4* a = ni->v4_addrs; a; a = a->next) { if (!(a->protocol & TOPO_PROTO_UDP)) continue; struct ETCP_SOCKET* s = entry->mgr->instance->etcp_sockets; while (s) { @@ -629,7 +657,7 @@ static void cm_start_phase_direct(struct CONN_MGR_ENTRY* entry) { s = s->next; } } - for (const struct TOPO_ADDR6* a6 = target->node->v6_addrs; a6; a6 = a6->next) { + for (const struct TOPO_ADDR6* a6 = ni->v6_addrs; a6; a6 = a6->next) { if (!(a6->protocol & TOPO_PROTO_UDP)) continue; uint8_t tclass = cm_classify_v6_addr(a6->addr); if (tclass == CM_V6_OTHER) continue; struct ETCP_SOCKET* s = entry->mgr->instance->etcp_sockets; @@ -647,9 +675,9 @@ static void cm_start_phase_direct(struct CONN_MGR_ENTRY* entry) { uint8_t priority_order[] = { TOPO_ADDR_NAT, TOPO_ADDR_INTERFACE }; for (int pri = 0; pri < 2; pri++) { - for (const struct TOPO_ADDR4* a = target->node->v4_addrs; a; a = a->next) { + for (const struct TOPO_ADDR4* a = ni->v4_addrs; a; a = a->next) { if (a->type != priority_order[pri]) continue; - for (const struct TOPO_SOCKMETA4* m = target->node->v4_sock_meta; m; m = m->next) { + for (const struct TOPO_SOCKMETA4* m = ni->v4_sock_meta; m; m = m->next) { if (m->id != a->socket_id) continue; struct ETCP_SOCKET* s = entry->mgr->instance->etcp_sockets; while (s) { @@ -663,7 +691,7 @@ static void cm_start_phase_direct(struct CONN_MGR_ENTRY* entry) { } } } - for (const struct TOPO_ADDR6* a6 = target->node->v6_addrs; a6; a6 = a6->next) { + for (const struct TOPO_ADDR6* a6 = ni->v6_addrs; a6; a6 = a6->next) { if (a6->type != priority_order[pri]) continue; uint8_t tclass = cm_classify_v6_addr(a6->addr); if (tclass == CM_V6_OTHER) continue; struct ETCP_SOCKET* s = entry->mgr->instance->etcp_sockets; @@ -676,8 +704,8 @@ static void cm_start_phase_direct(struct CONN_MGR_ENTRY* entry) { return; } etcp_connection_close(conn); - int has_our = cm_has_direct_ip(group->local_node); - int has_target = cm_has_direct_ip(target); + int has_our = cm_has_direct_ip(entry->mgr, group->local_node); + int has_target = cm_has_direct_ip(entry->mgr, target); if (has_our && !has_target) { cm_start_phase_reverse(entry); return; } cm_start_phase_indirect(entry); } @@ -692,9 +720,9 @@ static void cm_direct_timeout_cb(void* arg) { return; } struct TOPO_GROUP* group = entry->mgr->group; - struct TOPO_NODEQ* target = topo_node_find_by_id(group, entry->node_id); - int has_our = cm_has_direct_ip(group->local_node); - int has_target = target ? cm_has_direct_ip(target) : 0; + struct TOPO_GROUP_NODE* target = topo_node_find_by_id(group, entry->node_id); + int has_our = cm_has_direct_ip(entry->mgr, group->local_node); + int has_target = target ? cm_has_direct_ip(entry->mgr, target) : 0; if (entry->local_scan_state != CM_TRY_OK) { if (has_our && !has_target) { cm_start_phase_reverse(entry); return; } cm_start_phase_indirect(entry); @@ -704,7 +732,7 @@ static void cm_direct_timeout_cb(void* arg) { cm_deliver_result(entry, CONN_MGR_ERR_UNREACHABLE); } -static void cm_direct_init_cb(struct ETCP_CONN* conn, void* arg) { +static void cm_direct_init_cb(struct ETCP_CONN* conn, int event, void* arg) { (void)event; struct CONN_MGR_ENTRY* entry = (struct CONN_MGR_ENTRY*)arg; if (!conn || conn->peer_node_id != entry->node_id) { DEBUG_WARN(DEBUG_CATEGORY_GENERAL, "conn_mgr: direct ready cb peer mismatch conn=%p peer=0x%llx entry=0x%llx", @@ -739,8 +767,10 @@ static void cm_ping_cb(int success, uint16_t rtt, void* arg, uint64_t nonce, } ctx->attempt++; if (ctx->attempt < CONN_MGR_LOCAL_SCAN_ATTEMPTS && ctx->phase == 0) { + struct TOPO_GROUP_NODE* nq = topo_node_find_by_id(entry->mgr->group, entry->node_id); + struct TOPO_NODE* pub_ni = nq ? topo_node_registry_find(entry->mgr->instance->topo_groups, nq->node_id) : NULL; etcp_send_ping_to_socket(entry->mgr->instance, ctx->sock, - topo_node_find_by_id(entry->mgr->group, entry->node_id)->node->public_key, + pub_ni ? pub_ni->public_key : NULL, &ctx->addr, CONN_MGR_LOCAL_SCAN_TIMEOUT_MS, cm_ping_cb, ctx, NULL, 0, 0); return; } @@ -752,15 +782,16 @@ static void cm_ping_cb(int success, uint16_t rtt, void* arg, uint64_t nonce, static void cm_start_phase_reverse(struct CONN_MGR_ENTRY* entry) { struct TOPO_GROUP* group = entry->mgr->group; - struct TOPO_NODEQ* local = group->local_node; - if (!local || !local->node) { cm_deliver_result(entry, CONN_MGR_ERR_INTERNAL); return; } + struct TOPO_GROUP_NODE* local = group->local_node; + struct TOPO_NODE* lni = local ? topo_node_registry_find(entry->mgr->instance->topo_groups, local->node_id) : NULL; + if (!local || !lni) { cm_deliver_result(entry, CONN_MGR_ERR_INTERNAL); return; } uint32_t req_id = ++entry->mgr->next_request_id; entry->main.request_id = req_id; entry->main.phase = 2; uint8_t direct_count = 0; struct { uint8_t type; uint8_t ip[4]; uint16_t port; uint8_t socket_id; } out_addrs[8]; - for (const struct TOPO_ADDR4* a = local->node->v4_addrs; a && direct_count < 8; a = a->next) { + for (const struct TOPO_ADDR4* a = lni->v4_addrs; a && direct_count < 8; a = a->next) { if (a->type != TOPO_ADDR_NAT && a->type != TOPO_ADDR_INTERFACE) continue; - for (const struct TOPO_SOCKMETA4* m = local->node->v4_sock_meta; m; m = m->next) { + for (const struct TOPO_SOCKMETA4* m = lni->v4_sock_meta; m; m = m->next) { if (m->id == a->socket_id && (m->config_type == CFG_SERVER_TYPE_PUBLIC || m->config_type == CFG_SERVER_TYPE_UNKNOWN || m->nat_type == NAT_VERIFIED_EIM || m->nat_type == NAT_VERIFIED_DIRECT)) { out_addrs[direct_count].type = a->type; memcpy(out_addrs[direct_count].ip, a->addr, 4); @@ -808,7 +839,7 @@ static void cm_reverse_timeout_cb(void* arg) { cm_start_phase_indirect(entry); } -static void cm_reverse_init_cb(struct ETCP_CONN* conn, void* arg) { +static void cm_reverse_init_cb(struct ETCP_CONN* conn, int event, void* arg) { (void)event; struct cm_reverse_pending* rp = (struct cm_reverse_pending*)arg; struct CONN_MGR_ENTRY* entry = rp->entry; if (!conn || conn->peer_node_id != entry->node_id) { u_free(rp); return; } @@ -886,7 +917,7 @@ static void cm_compute_intermediaries(struct CONN_MGR_ENTRY* entry, struct CONN_ } for (uint8_t i = 0; i < resp->my_count && count < 8; i++) { uint64_t id = resp->my_candidates[i].node_id; - struct TOPO_NODEQ* nq = topo_node_find_by_id(entry->mgr->group, id); + struct TOPO_GROUP_NODE* nq = topo_node_find_by_id(entry->mgr->group, id); uint16_t our_rtt = nq ? cm_get_node_min_rtt(nq) : 0; if (our_rtt == 0xFFFF) our_rtt = 0; all[count].node_id = id; @@ -912,7 +943,7 @@ static void cm_compute_intermediaries(struct CONN_MGR_ENTRY* entry, struct CONN_ for (uint8_t i = 0; i < sel; i++) { pkt.selected[i].node_id = all[i].node_id; pkt.selected[i].rtt = 0; - struct TOPO_NODEQ* nq = topo_node_find_by_id(entry->mgr->group, all[i].node_id); + struct TOPO_GROUP_NODE* nq = topo_node_find_by_id(entry->mgr->group, all[i].node_id); if (nq) pkt.selected[i].rtt = cm_get_node_min_rtt(nq); } struct ll_entry* qe = queue_entry_new(0); @@ -933,7 +964,7 @@ static void cm_exchange_probe_retry_cb(void* arg) { uint8_t need_probe = 0; uint64_t now_tb = get_time_tb(); for (uint8_t i = 0; i < ep->cached_resp.my_count && i < 4; i++) { - struct TOPO_NODEQ* nq = topo_node_find_by_id(mgr->group, ep->cached_resp.my_candidates[i].node_id); + struct TOPO_GROUP_NODE* nq = topo_node_find_by_id(mgr->group, ep->cached_resp.my_candidates[i].node_id); if (!nq || !cm_is_rtt_fresh(nq, now_tb)) { need_probe++; break; } } if (need_probe == 0) { @@ -963,7 +994,7 @@ static void cm_handle_interm_exchange_resp(struct CONN_MGR* mgr, const uint8_t* uint8_t need_probe = 0; uint64_t now_tb = get_time_tb(); for (uint8_t i = 0; i < ep->cached_resp.my_count && i < 4; i++) { - struct TOPO_NODEQ* nq = topo_node_find_by_id(mgr->group, ep->cached_resp.my_candidates[i].node_id); + struct TOPO_GROUP_NODE* nq = topo_node_find_by_id(mgr->group, ep->cached_resp.my_candidates[i].node_id); if (!nq || !cm_is_rtt_fresh(nq, now_tb)) { need_probe++; route_connectivity_probe_node(mgr->instance, nq); } } if (need_probe == 0) { ep->probes_done = 1; cm_compute_intermediaries(ep->entry, &ep->cached_resp); @@ -1059,12 +1090,13 @@ static void cm_handle_direct_req(struct ETCP_CONN* conn, const uint8_t* data, si memset(&sa, 0, sizeof(sa)); memcpy(&sa, &sin, sizeof(sin)); struct ETCP_CONN* new_conn = etcp_connection_create(conn->instance, NULL); if (new_conn) { - struct TOPO_NODEQ* nq = topo_node_find_by_id(mgr->group, src_node_id); + struct TOPO_GROUP_NODE* nq = topo_node_find_by_id(mgr->group, src_node_id); if (nq) { sc_init_ctx(&new_conn->crypto_ctx, &conn->instance->my_keys); - sc_set_peer_public_key(&new_conn->crypto_ctx, nq->node->public_key, 0); } + { struct TOPO_NODE* dni = topo_node_registry_find(mgr->instance->topo_groups, nq->node_id); + sc_set_peer_public_key(&new_conn->crypto_ctx, dni ? dni->public_key : NULL, 0); } } struct cm_reverse_pending* rp = u_calloc(1, sizeof(struct cm_reverse_pending)); if (rp) { rp->request_id = req->request_id; rp->entry = NULL; rp->next = mgr->reverse_pending; - etcp_conn_add_init_cbk(new_conn, cm_reverse_init_cb, rp); mgr->reverse_pending = rp; } + etcp_conn_add_cbk(new_conn, cm_reverse_init_cb, rp, ETCP_CBK_EVENT_INIT); mgr->reverse_pending = rp; } etcp_link_new(new_conn, s, &sa, 0); } break; @@ -1081,7 +1113,7 @@ static void cm_handle_interm_exchange_req(struct ETCP_CONN* conn, uint64_t now_tb = get_time_tb(); for (uint8_t i = 0; i < req->candidate_count && i < 4; i++) { uint64_t cand_id = req->candidates[i].node_id; - struct TOPO_NODEQ* nq = topo_node_find_by_id(mgr->group, cand_id); + struct TOPO_GROUP_NODE* nq = topo_node_find_by_id(mgr->group, cand_id); if (nq && cm_is_rtt_fresh(nq, now_tb)) continue; route_connectivity_probe_node(mgr->instance, nq); } @@ -1096,7 +1128,7 @@ static void cm_handle_interm_exchange_req(struct ETCP_CONN* conn, resp.your_count = req->candidate_count > 4 ? 4 : req->candidate_count; for (uint8_t i = 0; i < resp.your_count; i++) { resp.your_candidates[i].node_id = req->candidates[i].node_id; - struct TOPO_NODEQ* nq2 = topo_node_find_by_id(mgr->group, req->candidates[i].node_id); + struct TOPO_GROUP_NODE* nq2 = topo_node_find_by_id(mgr->group, req->candidates[i].node_id); uint16_t rtt = nq2 ? cm_get_node_min_rtt(nq2) : 0; resp.your_candidates[i].rtt = rtt; } @@ -1154,8 +1186,9 @@ static void cm_bg_ping_timer_cb(void* arg) { e = group->nodes->head; while (e && cursor < mgr->bg_ping_cursor) { cursor++; e = e->next; } if (e) { - struct TOPO_NODEQ* nq = (struct TOPO_NODEQ*)e; - uint64_t node_id = nq->node->node_id; + struct TOPO_GROUP_NODE* nq = (struct TOPO_GROUP_NODE*)e; + struct TOPO_NODE* bg_ni = topo_node_registry_find(mgr->instance->topo_groups, nq->node_id); + uint64_t node_id = nq->node_id; if (node_id != mgr->instance->node_id) { struct ETCP_CONN* dconn = topo_group_find_conn_for_node(group, node_id); int has_active = 0; @@ -1163,9 +1196,9 @@ static void cm_bg_ping_timer_cb(void* arg) { struct ETCP_LINK* l = dconn->links; while (l) { if (l->initialized && l->link_status) { has_active = 1; break; } l = l->next; } } - if (!has_active) { + if (!has_active && bg_ni) { int sent_v4 = 0; - for (const struct TOPO_ADDR4* a = nq->node->v4_addrs; a; a = a->next) { + for (const struct TOPO_ADDR4* a = bg_ni->v4_addrs; a; a = a->next) { if (a->type != TOPO_ADDR_NAT && a->type != TOPO_ADDR_INTERFACE) continue; struct sockaddr_in sin; memset(&sin, 0, sizeof(sin)); sin.sin_family = AF_INET; memcpy(&sin.sin_addr.s_addr, a->addr, 4); sin.sin_port = htons(a->port); @@ -1173,12 +1206,12 @@ static void cm_bg_ping_timer_cb(void* arg) { struct ETCP_SOCKET* s = mgr->instance->etcp_sockets; while (s) { if (s->local_addr.ss_family == AF_INET) break; s = s->next; } if (!s) break; - etcp_send_ping_to_socket(mgr->instance, s, nq->node->public_key, &sa, + etcp_send_ping_to_socket(mgr->instance, s, bg_ni->public_key, &sa, CONN_PROBE_TIMEOUT_MS, cm_bg_ping_noop_cb, NULL, NULL, 0, 0); sent_v4 = 1; break; } if (!sent_v4) { - for (const struct TOPO_ADDR6* a6 = nq->node->v6_addrs; a6; a6 = a6->next) { + for (const struct TOPO_ADDR6* a6 = bg_ni->v6_addrs; a6; a6 = a6->next) { if (a6->type != TOPO_ADDR_NAT && a6->type != TOPO_ADDR_INTERFACE) continue; struct sockaddr_in6 sin6; memset(&sin6, 0, sizeof(sin6)); sin6.sin6_family = AF_INET6; memcpy(&sin6.sin6_addr, a6->addr, 16); sin6.sin6_port = htons(a6->port); @@ -1186,7 +1219,7 @@ static void cm_bg_ping_timer_cb(void* arg) { struct ETCP_SOCKET* s = mgr->instance->etcp_sockets; while (s) { if (s->local_addr.ss_family == AF_INET6) break; s = s->next; } if (!s) break; - etcp_send_ping_to_socket(mgr->instance, s, nq->node->public_key, &sa, + etcp_send_ping_to_socket(mgr->instance, s, bg_ni->public_key, &sa, CONN_PROBE_TIMEOUT_MS, cm_bg_ping_noop_cb, NULL, NULL, 0, 0); break; } @@ -1203,7 +1236,7 @@ static void cm_candidate_ping_timer_cb(void* arg) { uint64_t now_tb = get_time_tb(); for (uint8_t i = 0; i < mgr->best_candidate_count; i++) { uint64_t cand_id = mgr->best_candidates[i].node_id; - struct TOPO_NODEQ* nq = topo_node_find_by_id(mgr->group, cand_id); + struct TOPO_GROUP_NODE* nq = topo_node_find_by_id(mgr->group, cand_id); if (!nq) { memmove(&mgr->best_candidates[i], &mgr->best_candidates[i+1], (mgr->best_candidate_count - i - 1) * sizeof(mgr->best_candidates[0])); mgr->best_candidate_count--; i--; continue; @@ -1220,8 +1253,10 @@ static void cm_candidate_ping_timer_cb(void* arg) { struct ETCP_LINK* l = dconn->links; while (l) { if (l->initialized && l->link_status) { has_active = 1; break; } l = l->next; } } - if (has_active) - etcp_send_ping(mgr->instance, nq->node->public_key, &dconn->links->remote_addr, CONN_PROBE_TIMEOUT_MS, NULL, NULL, NULL, 0); + if (has_active) { + struct TOPO_NODE* cp_ni = topo_node_registry_find(mgr->instance->topo_groups, nq->node_id); + if (cp_ni) etcp_send_ping(mgr->instance, cp_ni->public_key, &dconn->links->remote_addr, CONN_PROBE_TIMEOUT_MS, NULL, NULL, NULL, 0); + } else route_connectivity_probe_node(mgr->instance, nq); } @@ -1278,8 +1313,8 @@ static void cm_handle_invite_info_req(struct CONN_MGR* mgr, struct ETCP_CONN* co struct TOPO_GROUP* group = topo_groups_find(mgr->instance->topo_groups, req->group_id); struct TOPO_GROUP* src_group = group ? group : mgr->group; const uint8_t* ed_pubkey = src_group->ed25519_public_key; - const char* node_name = (src_group->local_node && src_group->local_node->node) - ? src_group->local_node->node->node_name : NULL; + struct TOPO_NODE* ln = (src_group->local_node) ? topo_node_registry_find(src_group->instance->topo_groups, src_group->local_node->node_id) : NULL; + const char* node_name = ln ? ln->node_name : NULL; size_t name_len = node_name ? strlen(node_name) : 0; if (name_len > 255) name_len = 255; @@ -1335,13 +1370,16 @@ static void cm_handle_invite_info_resp(struct CONN_MGR* mgr, const uint8_t* data if (inv->group_id != 0 && !resp->is_member) { cm_invite_fail(inv, CONN_MGR_ERR_NOT_MEMBER); return; } - struct TOPO_NODEQ* nq = inv->temp_nq; - if (nq && nq->node) { - memcpy(nq->node->ed25519_public_key, resp->ed25519_pubkey, SC_PUBKEY_SIZE); - if (nq->node->node_name) { u_free(nq->node->node_name); nq->node->node_name = NULL; } - if (resp->node_name_len) { nq->node->node_name = u_strdup((const char*)resp->node_name); } + struct TOPO_GROUP_NODE* nq = inv->temp_nq; + if (nq) { + struct TOPO_NODE* inv_ni = topo_node_registry_find(inv->mgr->instance->topo_groups, nq->node_id); + if (inv_ni) { + memcpy(inv_ni->ed25519_public_key, resp->ed25519_pubkey, SC_PUBKEY_SIZE); + if (inv_ni->node_name) { u_free(inv_ni->node_name); inv_ni->node_name = NULL; } + if (resp->node_name_len) { inv_ni->node_name = u_strdup((const char*)resp->node_name); } + } sqlite3* db = inv->mgr->instance->topo_sqlite_db; - if (db) topo_node_sqlite_node_put(db, nq, (time_t)(get_time_tb() / 10000)); + if (db) topo_node_sqlite_node_put(db, inv->mgr->instance->topo_groups, nq, (time_t)(get_time_tb() / 10000)); } if (inv->overall_timer) { uasync_cancel_timeout(inv->mgr->instance->ua, inv->overall_timer); inv->overall_timer = NULL; } @@ -1349,7 +1387,7 @@ static void cm_handle_invite_info_resp(struct CONN_MGR* mgr, const uint8_t* data cm_invite_cleanup(inv); } -static void cm_invite_init_cb(struct ETCP_CONN* conn, void* arg) { +static void cm_invite_init_cb(struct ETCP_CONN* conn, int event, void* arg) { (void)event; struct cm_invite_pending* inv = (struct cm_invite_pending*)arg; if (inv->state != CM_INVITE_CONNECTING) return; if (!conn || conn->peer_node_id != inv->node_id) { @@ -1399,7 +1437,7 @@ static void cm_tcp_ready_cb(struct stcp_link* link, void* arg) { inv->conn = etcp; DEBUG_INFO(DEBUG_CATEGORY_GENERAL, "conn_mgr: TCP invite ready node=0x%016llx", (unsigned long long)inv->node_id); - cm_invite_init_cb(etcp, inv); + cm_invite_init_cb(etcp, ETCP_CBK_EVENT_INIT, inv); } static void cm_tcp_close_cb(struct stcp_link* link, int err, void* arg) { @@ -1424,7 +1462,7 @@ static void cm_invite_fail(struct cm_invite_pending* inv, int result) { if (inv->temp_nq) { struct TOPO_GROUP* group = mgr->group; queue_remove_data(group->nodes, &inv->temp_nq->ll); - topo_node_free_lists(group, inv->temp_nq); + topo_nodeq_free_group_fields(mgr->instance->topo_groups, inv->temp_nq); queue_entry_free(&inv->temp_nq->ll); inv->temp_nq = NULL; } struct cm_invite_pending** pp = &mgr->invite_list; @@ -1454,7 +1492,7 @@ int conn_mgr_connect_from_invite(struct CONN_MGR* mgr, struct TOPO_NODE* ni, struct TOPO_NODE* group_ni = u_calloc(1, sizeof(struct TOPO_NODE)); if (!group_ni) { if (cb) cb(CONN_MGR_ERR_INTERNAL, node_id, cb_arg); return CONN_MGR_ERR_INTERNAL; } - group_ni->node_id = node_id; group_ni->ver = 1; + group_ni->node_id = node_id; group_ni->ver = 1; group_ni->group_id = mgr->group->group_id; memcpy(group_ni->public_key, ni->public_key, SC_PUBKEY_SIZE); for (const struct TOPO_ADDR4* src = ni->v4_addrs; src; src = src->next) { struct TOPO_ADDR4* a4 = memory_pool_alloc(groups->v4_addr_pool); @@ -1472,17 +1510,17 @@ int conn_mgr_connect_from_invite(struct CONN_MGR* mgr, struct TOPO_NODE* ni, } if (!group_ni->v4_addrs && !group_ni->v6_addrs) { u_free(group_ni); if (cb) cb(CONN_MGR_ERR_NO_ADDRESSES, node_id, cb_arg); return CONN_MGR_ERR_NO_ADDRESSES; } - group_ni = topo_node_registry_acquire(groups, group_ni); /* group_ni now owned by registry (ref=1) */ + group_ni = topo_node_registry_store(groups, group_ni); /* group_ni now owned by registry (ref=1) */ if (!group_ni) { if (cb) cb(CONN_MGR_ERR_INTERNAL, node_id, cb_arg); return CONN_MGR_ERR_INTERNAL; } - struct ll_entry* qe = queue_entry_new(sizeof(struct TOPO_NODEQ)); - if (!qe) { topo_node_registry_release(groups, group_ni); if (cb) cb(CONN_MGR_ERR_INTERNAL, node_id, cb_arg); return CONN_MGR_ERR_INTERNAL; } - struct TOPO_NODEQ* nq = (struct TOPO_NODEQ*)qe; + struct ll_entry* qe = queue_entry_new(sizeof(struct TOPO_GROUP_NODE)); + if (!qe) { topo_node_registry_unref(groups, group_ni->node_id); if (cb) cb(CONN_MGR_ERR_INTERNAL, node_id, cb_arg); return CONN_MGR_ERR_INTERNAL; } + struct TOPO_GROUP_NODE* nq = (struct TOPO_GROUP_NODE*)qe; memset((uint8_t*)nq + sizeof(struct ll_entry), 0, sizeof(*nq) - sizeof(struct ll_entry)); - nq->node = group_ni; nq->hash_node_id = node_id; + nq->node_id = node_id; struct cm_invite_pending* inv = u_calloc(1, sizeof(struct cm_invite_pending)); - if (!inv) { queue_entry_free(qe); topo_node_registry_release(groups, group_ni); if (cb) cb(CONN_MGR_ERR_INTERNAL, node_id, cb_arg); return CONN_MGR_ERR_INTERNAL; } + if (!inv) { queue_entry_free(qe); topo_node_registry_unref(groups, group_ni->node_id); if (cb) cb(CONN_MGR_ERR_INTERNAL, node_id, cb_arg); return CONN_MGR_ERR_INTERNAL; } inv->mgr = mgr; inv->node_id = node_id; inv->group_id = group_id; inv->timeout_ms = timeout_ms; inv->state = CM_INVITE_CONNECTING; inv->temp_nq = nq; inv->cb = cb; inv->cb_arg = cb_arg; @@ -1505,7 +1543,7 @@ int conn_mgr_connect_from_invite(struct CONN_MGR* mgr, struct TOPO_NODE* ni, if (!udp_conn) { udp_conn = etcp_connection_create(mgr->instance, NULL); if (udp_conn) { - etcp_conn_add_init_cbk(udp_conn, cm_invite_init_cb, inv); + etcp_conn_add_cbk(udp_conn, cm_invite_init_cb, inv, ETCP_CBK_EVENT_INIT); sc_init_ctx(&udp_conn->crypto_ctx, &mgr->instance->my_keys); sc_set_peer_public_key(&udp_conn->crypto_ctx, group_ni->public_key, 0); } @@ -1526,7 +1564,7 @@ int conn_mgr_connect_from_invite(struct CONN_MGR* mgr, struct TOPO_NODE* ni, if (!udp_conn) { udp_conn = etcp_connection_create(mgr->instance, NULL); if (udp_conn) { - etcp_conn_add_init_cbk(udp_conn, cm_invite_init_cb, inv); + etcp_conn_add_cbk(udp_conn, cm_invite_init_cb, inv, ETCP_CBK_EVENT_INIT); sc_init_ctx(&udp_conn->crypto_ctx, &mgr->instance->my_keys); sc_set_peer_public_key(&udp_conn->crypto_ctx, group_ni->public_key, 0); } diff --git a/src/routing_layer/conn_mgr.h b/src/routing_layer/conn_mgr.h index 100fa9eb..76eee6ca 100644 --- a/src/routing_layer/conn_mgr.h +++ b/src/routing_layer/conn_mgr.h @@ -15,7 +15,7 @@ extern "C" { struct UTUN_INSTANCE; struct ETCP_CONN; -struct TOPO_NODEQ; +struct TOPO_GROUP_NODE; struct TOPO_GROUP; /** @@ -120,6 +120,10 @@ enum CM_TRY_STATE { #define CONN_MGR_SUBCMD_INTERM_EXCHANGE_RESP 0x04 ///< Ответ с нашими кандидатами + замеры RTT до кандидатов инициатора #define CONN_MGR_SUBCMD_INTERM_SELECTED 0x05 ///< Выбранные посредники (финальный шаг INDIRECT-фазы) #define CONN_MGR_SUBCMD_DISCONNECT 0x06 ///< Уведомление о разрыве соединения +#define CONN_MGR_SUBCMD_BRIDGE_REQUEST 0x10 ///< A→X: запрос моста к B (payload: target_node_id) +#define CONN_MGR_SUBCMD_BRIDGE_RESPONSE 0x11 ///< X→A: ответ с session_id +#define CONN_MGR_SUBCMD_BRIDGE_CLOSE 0x12 ///< A→X: закрыть мост (payload: session_id + target_node_id) +#define CONN_MGR_SUBCMD_BRIDGE_CLOSED 0x13 ///< X→A: мост закрыт (payload: session_id) #define CONN_MGR_SUBCMD_INVITE_INFO_REQ 0x0A ///< Запрос проверки членства в группе при invite-подключении #define CONN_MGR_SUBCMD_INVITE_INFO_RESP 0x0B ///< Ответ с членством, ed25519_pubkey, именем узла /** @} */ @@ -221,6 +225,37 @@ struct CONN_MGR_DISCONNECT { uint64_t node_id; ///< Идентификатор ноды, с которой разрываем соединение }; +struct CONN_MGR_BRIDGE_REQUEST { + uint8_t cmd; ///< ETCP_RT_ID_CONN_MGR + uint8_t subcmd; ///< CONN_MGR_SUBCMD_BRIDGE_REQUEST + uint64_t target_node_id; ///< К какой ноде нужен мост +}; + +struct CONN_MGR_BRIDGE_RESPONSE { + uint8_t cmd; ///< ETCP_RT_ID_CONN_MGR + uint8_t subcmd; ///< CONN_MGR_SUBCMD_BRIDGE_RESPONSE + uint64_t session_id; ///< ID bridge-сессии + uint64_t target_node_id; ///< Эхо target_node_id +}; + +struct CONN_MGR_BRIDGE_CLOSE { + uint8_t cmd; ///< ETCP_RT_ID_CONN_MGR + uint8_t subcmd; ///< CONN_MGR_SUBCMD_BRIDGE_CLOSE + uint64_t session_id; ///< ID bridge-сессии + uint64_t target_node_id; ///< ID цели +}; + +struct CONN_MGR_BRIDGE_CLOSED { + uint8_t cmd; ///< ETCP_RT_ID_CONN_MGR + uint8_t subcmd; ///< CONN_MGR_SUBCMD_BRIDGE_CLOSED + uint64_t session_id; ///< ID bridge-сессии +}; + +#define CONN_MGR_BRIDGE_REQUEST_SIZE sizeof(struct CONN_MGR_BRIDGE_REQUEST) +#define CONN_MGR_BRIDGE_RESPONSE_SIZE sizeof(struct CONN_MGR_BRIDGE_RESPONSE) +#define CONN_MGR_BRIDGE_CLOSE_SIZE sizeof(struct CONN_MGR_BRIDGE_CLOSE) +#define CONN_MGR_BRIDGE_CLOSED_SIZE sizeof(struct CONN_MGR_BRIDGE_CLOSED) + #pragma pack(pop) /** @@ -276,18 +311,36 @@ struct CONN_MGR_INVITE_INFO_RESP { */ typedef void (*conn_mgr_connect_callback_t)(int result, uint64_t node_id, void* arg); +/* forward declaration для handle */ +struct NODE_CONN_DIRECT; + /** - * @struct cm_cb_node - * @brief Узел связного списка callback'ов для одной ноды. - * - * Позволяет регистрировать несколько callback'ов на одно подключение. - * Новые callback'и добавляются в начало списка. + * @struct CONN_MGR_HANDLE + * @brief Универсальный handle подключения (один на каждый conn_mgr_connect_node). + */ +struct CONN_MGR_HANDLE { + uint64_t target_node_id; + uint8_t conn_type; + uint64_t intermediary_node_id; + uint64_t bridge_session_id; + struct NODE_CONN_DIRECT* nd_handle; + conn_mgr_connect_callback_t result_cb; + void* result_arg; + struct CONN_MGR_HANDLE* next; + struct CONN_MGR_ENTRY* entry; +}; + +/** + * @struct cm_bridge_session + * @brief Bridge-сессия на посреднике X (INDIRECT-прокси). */ -struct cm_cb_node { - conn_mgr_connect_callback_t cb; ///< Функция-callback - void* arg; ///< Пользовательский аргумент - struct cm_cb_node* next; ///< Следующий callback в списке - uint8_t cancelled; ///< 1 = callback помечен как недействительный +struct cm_bridge_session { + uint64_t session_id; + uint64_t initiator_id; + uint64_t target_id; + struct CONN_MGR_HANDLE* handle_to_target; + struct CONN_MGR_HANDLE* handle_to_initiator; + struct cm_bridge_session* next; }; /** @@ -305,7 +358,7 @@ struct CONN_MGR_ENTRY { uint64_t last_traffic_tb; ///< Время последнего трафика (tb) для idle-проверки void* idle_timer; ///< Хендл таймера idle-проверки - struct cm_cb_node* cb_list; ///< Связный список callback'ов результата + struct CONN_MGR_HANDLE* handles; ///< Связный список handle'ов (один на conn_mgr_connect_node) uint64_t intermediaries[CONN_MGR_MAX_INTERMEDIARIES]; ///< Выбранные посредники (node_id) uint8_t intermediariy_count; ///< Количество выбранных посредников @@ -356,6 +409,8 @@ struct CONN_MGR { struct cm_reverse_pending* reverse_pending; ///< Связный список ожидающих REVERSE-подключений struct cm_exchange_pending* exchange_pending; ///< Связный список ожидающих INDIRECT-обменов struct cm_invite_pending* invite_list; ///< Связный список ожидающих invite-подключений + struct cm_bridge_session* bridge_sessions; ///< Связный список активных bridge-сессий (на посреднике X) + uint64_t next_bridge_session_id; ///< Счётчик session_id для новых bridge-сессий }; /* ==================== Жизненный цикл ==================== */ @@ -383,46 +438,32 @@ void conn_mgr_destroy(struct CONN_MGR* mgr); /** * @brief Запускает асинхронное подключение к ноде. - * @param mgr менеджер - * @param node_id идентификатор целевой ноды - * @param idle_timeout_ms таймаут неактивности (мс), 0 = без таймаута - * @param cb callback результата (CONN_MGR_OK, CONN_MGR_ERR_TIMEOUT, CONN_MGR_ERR_UNREACHABLE и т.д.) - * @param cb_arg пользовательский аргумент для callback - * @return CONN_MGR_OK если процесс запущен, CONN_MGR_ERR_* при ошибке - * - * @note Если нода уже подключена — сразу вызывает callback с CONN_MGR_OK и возвращает CONN_MGR_OK. - * @note Если нода в процессе подключения — добавляет callback в список ожидания. - * @note Автоматически определяет наличие BGP-соединения (уже существующий ETCP-линк). + * @return CONN_MGR_HANDLE* при успехе, NULL при ошибке (cb вызван с CONN_MGR_ERR_*) */ -int conn_mgr_connect_node(struct CONN_MGR* mgr, uint64_t node_id, +struct CONN_MGR_HANDLE* conn_mgr_connect_node(struct CONN_MGR* mgr, uint64_t node_id, uint32_t idle_timeout_ms, conn_mgr_connect_callback_t cb, void* cb_arg); +/** + * @brief Освобождает ОДИН handle (не разрывает соединение если есть другие handles). + */ +void conn_mgr_release_handle(struct CONN_MGR_HANDLE* h); + /** * @brief Разрывает подключение к ноде. - * @param mgr менеджер - * @param node_id идентификатор ноды - * @return CONN_MGR_OK или CONN_MGR_ERR_NOT_FOUND - * - * Отправляет DISCONNECT-пакет целевой ноде, сбрасывает conn_mgr_type в NODEINFO_Q, - * уничтожает запись entry. + * Отправляет DISCONNECT-пакет целевой ноде (через etcp_router). + * Для INDIRECT — также отправляет BRIDGE_CLOSE каждому посреднику. + * Закрывает все handles, чистит entry. */ int conn_mgr_disconnect_node(struct CONN_MGR* mgr, uint64_t node_id); /** - * @brief Отменяет ожидающий callback подключения к ноде. - * @param mgr менеджер - * @param node_id идентификатор ноды - * @param cb callback для отмены - * @param cb_arg аргумент callback для точного сопоставления - * @return CONN_MGR_OK или CONN_MGR_ERR_NOT_FOUND/INTERNAL - * - * Если callback единственный в списке — полностью отменяет попытку подключения - * (уничтожает entry, таймеры, pending-структуры). Если есть другие callback'и — - * только помечает этот callback как cancelled, он будет пропущен при доставке. + * @brief Отменяет ожидающий handle подключения к ноде. + * Если handle единственный — полностью отменяет подключение. + * Если h == NULL — отменяет все handles для node_id. */ int conn_mgr_cancel_callback(struct CONN_MGR* mgr, uint64_t node_id, - conn_mgr_connect_callback_t cb, void* cb_arg); + struct CONN_MGR_HANDLE* h); /* ==================== Управление idle-таймаутом ==================== */ diff --git a/src/routing_layer/etcp_router.c b/src/routing_layer/etcp_router.c index 6d104a16..c4b5b2c8 100644 --- a/src/routing_layer/etcp_router.c +++ b/src/routing_layer/etcp_router.c @@ -190,7 +190,7 @@ static int router_send_one_flags(struct ETCP_ROUTER_CONN* rconn, const uint8_t* struct ETCP_CONN* conn = NULL; struct TOPO_GROUP* group = topo_groups_find(inst->topo_groups, rconn->group_id); if (group) { - struct TOPO_NODEQ* nq = topo_node_find_by_id(group, rconn->remote_node_id); + struct TOPO_GROUP_NODE* nq = topo_node_find_by_id(group, rconn->remote_node_id); if (nq && nq->conn_mgr_type == CONN_TYPE_INDIRECT && nq->conn_mgr_intermediariy_count > 0) { for (uint8_t i = 0; i < nq->conn_mgr_intermediariy_count; i++) { conn = topo_group_find_conn_for_node(group, nq->conn_mgr_intermediaries[i]); @@ -434,7 +434,7 @@ static void router_retransmit_one(struct ETCP_ROUTER_CONN* rconn, struct ROUTER_ struct ETCP_CONN* conn = NULL; struct TOPO_GROUP* group = topo_groups_find(inst->topo_groups, rconn->group_id); if (group) { - struct TOPO_NODEQ* nq = topo_node_find_by_id(group, rconn->remote_node_id); + struct TOPO_GROUP_NODE* nq = topo_node_find_by_id(group, rconn->remote_node_id); if (nq && nq->conn_mgr_type == CONN_TYPE_INDIRECT && nq->conn_mgr_intermediariy_count > 0) { for (uint8_t i = 0; i < nq->conn_mgr_intermediariy_count; i++) { conn = topo_group_find_conn_for_node(group, nq->conn_mgr_intermediaries[i]); @@ -802,22 +802,24 @@ static int router_verify_signature(struct UTUN_INSTANCE* inst, struct ll_entry* size_t actual_payload = *pl_len - SC_SIGN_SIZE; uint8_t* sig = (uint8_t*)entry->dgram + SVC_ROUTE_HDR_SIZE + actual_payload; struct TOPO_GROUP* group = topo_groups_find(inst->topo_groups, hdr->group_id); - struct TOPO_NODEQ* nq = group ? topo_node_find_by_id(group, hdr->src_node_id) : NULL; + struct TOPO_GROUP_NODE* nq = group ? topo_node_find_by_id(group, hdr->src_node_id) : NULL; if (!nq) { DEBUG_WARN(DEBUG_CATEGORY_ETCPROUTE, "router: SIGNED packet from unknown node %016llx — dropping", (unsigned long long)hdr->src_node_id); free_entry(entry); return -1; } + struct TOPO_NODE* sni = topo_node_registry_find(inst->topo_groups, nq->node_id); + if (!sni) { free_entry(entry); return -1; } { uint8_t zero[SC_PUBKEY_SIZE] = {0}; - if (memcmp(nq->node->ed25519_public_key, zero, SC_PUBKEY_SIZE) == 0) { + if (memcmp(sni->ed25519_public_key, zero, SC_PUBKEY_SIZE) == 0) { DEBUG_WARN(DEBUG_CATEGORY_ETCPROUTE, "router: no Ed25519 pubkey for node %016llx — dropping", (unsigned long long)hdr->src_node_id); free_entry(entry); return -1; } } struct sc_stream_sign_state vfy_state; - if (sc_stream_sign_verify_init(&vfy_state, nq->node->ed25519_public_key) != SC_OK) { + if (sc_stream_sign_verify_init(&vfy_state, sni->ed25519_public_key) != SC_OK) { DEBUG_ERROR(DEBUG_CATEGORY_ETCPROUTE, "router: sign_verify_init failed for node %016llx", (unsigned long long)hdr->src_node_id); free_entry(entry); return -1; diff --git a/src/routing_layer/nat_detection.c b/src/routing_layer/nat_detection.c index 37bbbeee..093fb7da 100644 --- a/src/routing_layer/nat_detection.c +++ b/src/routing_layer/nat_detection.c @@ -128,9 +128,11 @@ static void nat_detection_handle_nat_info(struct NAT_DETECTION* nd, struct TOPO_ else if (info->nat_type == NAT_TYPE_DIRECT) verified_type = NAT_VERIFIED_DIRECT; else verified_type = NAT_VERIFIED_UNKNOWN; - if (group && group->local_node && group->local_node->node) { + if (group && group->local_node) { + struct TOPO_NODE* ni = topo_node_registry_find(group->instance->topo_groups, group->local_node->node_id); + if (!ni) return; int data_changed = 0; - struct TOPO_SOCKMETA4* sm = group->local_node->node->v4_sock_meta; + struct TOPO_SOCKMETA4* sm = ni->v4_sock_meta; while (sm) { if (sm->id == socket_id) { if (sm->nat_type != verified_type) { sm->nat_type = verified_type; data_changed = 1; } @@ -164,23 +166,23 @@ static void nat_detection_handle_nat_info(struct NAT_DETECTION* nd, struct TOPO_ } if (data_changed) { - int prev_v4a = topo_list_count((struct _topo_head*)group->local_node->node->v4_addrs); + int prev_v4a = topo_list_count((struct _topo_head*)ni->v4_addrs); topo_group_update_my_nodeinfo(group->instance, group); - if (topo_list_count((struct _topo_head*)group->local_node->node->v4_addrs) != prev_v4a) { + if (topo_list_count((struct _topo_head*)ni->v4_addrs) != prev_v4a) { group->local_node->dirty = 1; - group->local_node->node->ver = (group->local_node->node->ver % 255) + 1; - group->local_node->last_ver = group->local_node->node->ver; + ni->ver = (ni->ver % 255) + 1; + group->local_node->last_ver = ni->ver; } - if (topo_list_count((struct _topo_head*)group->local_node->node->v4_addrs) == prev_v4a) { - struct TOPO_ADDR4* a = group->local_node->node->v4_addrs; + if (topo_list_count((struct _topo_head*)ni->v4_addrs) == prev_v4a) { + struct TOPO_ADDR4* a = ni->v4_addrs; while (a) { if (a->type == TOPO_ADDR_INTERFACE && a->socket_id == socket_id) { uint32_t a_ip; memcpy(&a_ip, a->addr, 4); if (a_ip == nat_ip && a->port == nat_port) { a->type = TOPO_ADDR_NAT; a->socket_id = socket_id | 1; group->local_node->dirty = 1; - group->local_node->node->ver = (group->local_node->node->ver % 255) + 1; - group->local_node->last_ver = group->local_node->node->ver; + ni->ver = (ni->ver % 255) + 1; + group->local_node->last_ver = ni->ver; DEBUG_INFO(DEBUG_CATEGORY_BGP, "NAT_INFO matched interface addr, updated entry type to NAT sock=%d", socket_id); } break; @@ -243,13 +245,13 @@ static void nat_detection_handle_nat_check_req(struct NAT_DETECTION* nd, struct static void nat_detection_trigger_checks(struct NAT_DETECTION* nd); -static void nat_detection_on_conn_up(struct ETCP_CONN* conn, void* arg) { +static void nat_detection_on_conn_up(struct ETCP_CONN* conn, int event, void* arg) { (void)event; struct NAT_DETECTION* nd = (struct NAT_DETECTION*)arg; if (!nd) return; nat_detection_trigger_checks(nd); } -static void nat_detection_on_conn_down(struct ETCP_CONN* conn, void* arg) { +static void nat_detection_on_conn_down(struct ETCP_CONN* conn, int event, void* arg) { (void)event; struct NAT_DETECTION* nd = (struct NAT_DETECTION*)arg; if (!nd || !conn) return; route_ping_cancel_for_conn(nd, conn); @@ -264,8 +266,8 @@ static void nat_detection_new_conn_cbk(struct ETCP_CONN* conn, void* arg) { if (!conn || !conn->instance) return; struct NAT_DETECTION* nd = (struct NAT_DETECTION*)arg; DEBUG_DEBUG(DEBUG_CATEGORY_NAT, "NAT detection set callbacks: %s", conn->log_name); - etcp_conn_add_up_cbk(conn, nat_detection_on_conn_up, nd); - etcp_conn_add_down_cbk(conn, nat_detection_on_conn_down, nd); + etcp_conn_add_cbk(conn, nat_detection_on_conn_up, nd, ETCP_CBK_EVENT_UP); + etcp_conn_add_cbk(conn, nat_detection_on_conn_down, nd, ETCP_CBK_EVENT_DOWN); } diff --git a/src/routing_layer/route6_lib.c b/src/routing_layer/route6_lib.c index 60c28b68..9f276d62 100644 --- a/src/routing_layer/route6_lib.c +++ b/src/routing_layer/route6_lib.c @@ -63,7 +63,7 @@ void route6_table_destroy(struct ROUTE6_TABLE *table) { u_free(table); } -bool route6_insert(struct ROUTE6_TABLE *table, struct TOPO_NODEQ *node) { +bool route6_insert(struct ROUTE6_TABLE *table, struct TOPO_GROUP_NODE *node) { if (!table || !node || !node->subnets || !node->subnets->v6_subnets) return false; for (const struct TOPO_SUBNET6* s = node->subnets->v6_subnets; s; s = s->next) { struct ll_entry *entry = queue_entry_new(ROUTE6_DATA_SIZE); @@ -72,7 +72,7 @@ bool route6_insert(struct ROUTE6_TABLE *table, struct TOPO_NODEQ *node) { make_key6(rd->key, s->addr); make_mask6(rd->mask, s->prefix_length); rd->v_node_info = node; - rd->node_id = node->node->node_id; + rd->node_id = node->node_id; rd->prefix_length = s->prefix_length; struct radix_node *rn = rn_addroute(rd->key, rd->mask, &table->rnh->rh, rd->rn_nodes); if (!rn) { queue_entry_free(entry); continue; } @@ -81,9 +81,9 @@ bool route6_insert(struct ROUTE6_TABLE *table, struct TOPO_NODEQ *node) { return true; } -void route6_delete(struct ROUTE6_TABLE *table, struct TOPO_NODEQ *node) { +void route6_delete(struct ROUTE6_TABLE *table, struct TOPO_GROUP_NODE *node) { if (!table || !node) return; - uint64_t node_id = node->node->node_id; + uint64_t node_id = node->node_id; struct ll_entry *entry = queue_find_data_by_index(table->queue, &node_id); while (entry) { struct ROUTE6_DATA *rd = (struct ROUTE6_DATA *)entry->data; diff --git a/src/routing_layer/route6_lib.h b/src/routing_layer/route6_lib.h index 508caa9a..0f685c85 100644 --- a/src/routing_layer/route6_lib.h +++ b/src/routing_layer/route6_lib.h @@ -48,7 +48,7 @@ FDxx/8 - опциональные ipv6 подсети узлов /64 (если struct ROUTE6_DATA { struct radix_node rn_nodes[2]; // scratch-буфер для rn_addroute - struct TOPO_NODEQ *v_node_info; // узел-владелец + struct TOPO_GROUP_NODE *v_node_info; // узел-владелец uint64_t node_id; // ключ для hash-поиска в ll_queue uint8_t prefix_length; // 0..128 uint8_t key[17]; // sockaddr-like: {len=17, addr[16]} @@ -66,8 +66,8 @@ struct ROUTE6_TABLE { struct ROUTE6_TABLE *route6_table_create(struct UASYNC *ua); void route6_table_destroy(struct ROUTE6_TABLE *table); -bool route6_insert(struct ROUTE6_TABLE *table, struct TOPO_NODEQ *node); -void route6_delete(struct ROUTE6_TABLE *table, struct TOPO_NODEQ *node); +bool route6_insert(struct ROUTE6_TABLE *table, struct TOPO_GROUP_NODE *node); +void route6_delete(struct ROUTE6_TABLE *table, struct TOPO_GROUP_NODE *node); const struct ROUTE6_DATA *route6_lookup(struct ROUTE6_TABLE *table, const uint8_t addr[16]); diff --git a/src/routing_layer/route_connectivity.c b/src/routing_layer/route_connectivity.c index 456ad258..9243a9a4 100644 --- a/src/routing_layer/route_connectivity.c +++ b/src/routing_layer/route_connectivity.c @@ -23,7 +23,7 @@ struct conn_probe_ctx { struct conn_probe_ctx* next; /* linked list in nq->connectivity.probe_list */ struct UTUN_INSTANCE* instance; - struct TOPO_NODEQ* nq; + struct TOPO_GROUP_NODE* nq; uint8_t addr_type; // ADDR_TYPE_* struct sockaddr_storage target_addr; uint8_t peer_pubkey[SC_PUBKEY_SIZE]; @@ -279,10 +279,10 @@ static void conn_probe_finish(struct conn_probe_ctx* ctx, int ok) { if (c->pending_count == 0) { c->probe_status = PROBE_STATUS_DONE; DEBUG_INFO(DEBUG_CATEGORY_BGP, "connectivity probe DONE for node %016llx: intf=%d nat=%d real=%d", - (unsigned long long)ctx->nq->node->node_id, + (unsigned long long)ctx->nq->node_id, c->interface_status, c->nat_status, c->real_status); if (ctx->instance && ctx->instance->topo_sqlite_db) - topo_node_sqlite_nodeinfo_updated(ctx->instance->topo_sqlite_db, ctx->nq->node->node_id); + topo_node_sqlite_nodeinfo_updated(ctx->instance->topo_sqlite_db, ctx->nq->node_id); } } @@ -291,11 +291,13 @@ static void conn_probe_finish(struct conn_probe_ctx* ctx, int ok) { // ---- public API ---- -void route_connectivity_probe_node(struct UTUN_INSTANCE* instance, struct TOPO_NODEQ* nq) { +void route_connectivity_probe_node(struct UTUN_INSTANCE* instance, struct TOPO_GROUP_NODE* nq) { if (!instance || !nq) return; - if (nq->node->node_id == instance->node_id) return; // не пингуем себя + struct TOPO_NODE* ni = topo_node_registry_find(instance->topo_groups, nq->node_id); + if (!ni) return; + if (nq->node_id == instance->node_id) return; // не пингуем себя if (nq->connectivity.probe_status == PROBE_STATUS_IN_PROGRESS) { - DEBUG_INFO(DEBUG_CATEGORY_BGP, "probe already in progress for node %016llx", (unsigned long long)nq->node->node_id); + DEBUG_INFO(DEBUG_CATEGORY_BGP, "probe already in progress for node %016llx", (unsigned long long)nq->node_id); return; } @@ -312,9 +314,9 @@ void route_connectivity_probe_node(struct UTUN_INSTANCE* instance, struct TOPO_N int pend = 0; // ---- IPv4 addresses ---- - if (nq->node && nq->node->v4_addrs) { + if (ni->v4_addrs) { uint32_t seen_ips[16]; uint16_t seen_ports[16]; int seen_count = 0; - for (const struct TOPO_ADDR4* a = nq->node->v4_addrs; a; a = a->next) { + for (const struct TOPO_ADDR4* a = ni->v4_addrs; a; a = a->next) { uint32_t ip; memcpy(&ip, a->addr, 4); uint16_t port = a->port; if (ip == 0 || port == 0) continue; int dup = 0; for (int j = 0; j < seen_count; j++) { if (seen_ips[j] == ip && seen_ports[j] == port) { dup = 1; break; } } @@ -331,7 +333,7 @@ void route_connectivity_probe_node(struct UTUN_INSTANCE* instance, struct TOPO_N struct conn_probe_ctx* ctx = u_calloc(1, sizeof(struct conn_probe_ctx)); if (!ctx) continue; ctx->instance = instance; ctx->nq = nq; ctx->addr_type = a->type; ctx->target_addr = target; - memcpy(ctx->peer_pubkey, nq->node->public_key, SC_PUBKEY_SIZE); + memcpy(ctx->peer_pubkey, ni->public_key, SC_PUBKEY_SIZE); memcpy(ctx->candidate_sockets, candidates, cand_count * sizeof(struct ETCP_SOCKET*)); ctx->candidate_count = cand_count; ctx->candidate_index = 0; ctx->count_total = CONN_PROBE_COUNT; ctx->timeout_ms = CONN_PROBE_TIMEOUT_MS; ctx->best_across_sockets = 65535; ctx->next = (struct conn_probe_ctx*)nq->connectivity.probe_list; nq->connectivity.probe_list = ctx; pend++; @@ -340,9 +342,9 @@ void route_connectivity_probe_node(struct UTUN_INSTANCE* instance, struct TOPO_N } // ---- IPv6 addresses ---- - if (nq->node && nq->node->v6_addrs) { + if (ni->v6_addrs) { uint8_t seen_ip6s[16][16]; uint16_t seen_ports6[16]; int seen6_count = 0; - for (const struct TOPO_ADDR6* a6 = nq->node->v6_addrs; a6; a6 = a6->next) { + for (const struct TOPO_ADDR6* a6 = ni->v6_addrs; a6; a6 = a6->next) { if (a6->port == 0) continue; int zero = 1; for (int z = 0; z < 16; z++) if (a6->addr[z] != 0) { zero = 0; break; } if (zero) continue; int dup = 0; for (int j = 0; j < seen6_count; j++) { if (memcmp(seen_ip6s[j], a6->addr, 16) == 0 && seen_ports6[j] == a6->port) { dup = 1; break; } } @@ -360,7 +362,7 @@ void route_connectivity_probe_node(struct UTUN_INSTANCE* instance, struct TOPO_N struct conn_probe_ctx* ctx = u_calloc(1, sizeof(struct conn_probe_ctx)); if (!ctx) continue; ctx->instance = instance; ctx->nq = nq; ctx->addr_type = a6->type; ctx->target_addr = target; - memcpy(ctx->peer_pubkey, nq->node->public_key, SC_PUBKEY_SIZE); + memcpy(ctx->peer_pubkey, ni->public_key, SC_PUBKEY_SIZE); memcpy(ctx->candidate_sockets, candidates, cand_count * sizeof(struct ETCP_SOCKET*)); ctx->candidate_count = cand_count; ctx->candidate_index = 0; ctx->count_total = CONN_PROBE_COUNT; ctx->timeout_ms = CONN_PROBE_TIMEOUT_MS; ctx->best_across_sockets = 65535; ctx->next = (struct conn_probe_ctx*)nq->connectivity.probe_list; nq->connectivity.probe_list = ctx; pend++; @@ -370,24 +372,24 @@ void route_connectivity_probe_node(struct UTUN_INSTANCE* instance, struct TOPO_N if (pend == 0) { nq->connectivity.probe_status = PROBE_STATUS_DONE; - if (!nq->node->v4_addrs && !nq->node->v6_addrs) { - DEBUG_INFO(DEBUG_CATEGORY_BGP, "no addresses to probe for node %016llx", (unsigned long long)nq->node->node_id); + if (!ni->v4_addrs && !ni->v6_addrs) { + DEBUG_INFO(DEBUG_CATEGORY_BGP, "no addresses to probe for node %016llx", (unsigned long long)ni->node_id); } } else { nq->connectivity.pending_count = pend; DEBUG_INFO(DEBUG_CATEGORY_BGP, "connectivity probe started for node %016llx: %d series pending", - (unsigned long long)nq->node->node_id, pend); + (unsigned long long)ni->node_id, pend); } } -void route_connectivity_cancel_node(struct UTUN_INSTANCE* instance, struct TOPO_NODEQ* nq) { +void route_connectivity_cancel_node(struct UTUN_INSTANCE* instance, struct TOPO_GROUP_NODE* nq) { if (!instance || !nq) return; nq->connectivity.probe_status = PROBE_STATUS_NONE; nq->connectivity.pending_count = 0; struct conn_probe_ctx* ctx = (struct conn_probe_ctx*)nq->connectivity.probe_list; while (ctx) { ctx->nq = NULL; ctx = ctx->next; } nq->connectivity.probe_list = NULL; - DEBUG_INFO(DEBUG_CATEGORY_BGP, "connectivity probe cancelled for node %016llx", (unsigned long long)nq->node->node_id); + DEBUG_INFO(DEBUG_CATEGORY_BGP, "connectivity probe cancelled for node %016llx", (unsigned long long)nq->node_id); } void route_connectivity_cancel_all(struct UTUN_INSTANCE* instance) { @@ -396,7 +398,7 @@ void route_connectivity_cancel_all(struct UTUN_INSTANCE* instance) { int count = 0; struct ll_entry* e = group->nodes->head; while (e) { - struct TOPO_NODEQ* nq = (struct TOPO_NODEQ*)e; + struct TOPO_GROUP_NODE* nq = (struct TOPO_GROUP_NODE*)e; nq->connectivity.probe_status = PROBE_STATUS_NONE; nq->connectivity.pending_count = 0; e = e->next; diff --git a/src/routing_layer/route_connectivity.h b/src/routing_layer/route_connectivity.h index a16d967a..6bcb9372 100644 --- a/src/routing_layer/route_connectivity.h +++ b/src/routing_layer/route_connectivity.h @@ -13,11 +13,11 @@ struct UTUN_INSTANCE; // Запускает зондирование связности ко всем адресам удалённого узла void route_connectivity_probe_node(struct UTUN_INSTANCE* instance, - struct TOPO_NODEQ* nq); + struct TOPO_GROUP_NODE* nq); // Отменяет все pending пробы для узла (при удалении / withdraw) void route_connectivity_cancel_node(struct UTUN_INSTANCE* instance, - struct TOPO_NODEQ* nq); + struct TOPO_GROUP_NODE* nq); // Отменяет все pending пробы для всех узлов (при destroy) void route_connectivity_cancel_all(struct UTUN_INSTANCE* instance); diff --git a/src/routing_layer/route_lib.c b/src/routing_layer/route_lib.c index 749b533d..a97c99d2 100644 --- a/src/routing_layer/route_lib.c +++ b/src/routing_layer/route_lib.c @@ -136,7 +136,7 @@ void route_table_destroy(struct ROUTE_TABLE *table) { DEBUG_TRACE(DEBUG_CATEGORY_ROUTING, "count=%zu", table->count); - // v_node_info — внешний объект (TOPO_NODEQ), память им не управляем + // v_node_info — внешний объект (TOPO_GROUP_NODE), память им не управляем u_free(table->dynamic_subnets); u_free(table->local_subnets); u_free(table->entries); @@ -145,7 +145,7 @@ void route_table_destroy(struct ROUTE_TABLE *table) { DEBUG_INFO(DEBUG_CATEGORY_ROUTING, "Routing table destroyed"); } -bool route_insert(struct ROUTE_TABLE *table, struct TOPO_NODEQ *node) { +bool route_insert(struct ROUTE_TABLE *table, struct TOPO_GROUP_NODE *node) { if (!table || !node) { DEBUG_ERROR(DEBUG_CATEGORY_ROUTING, "invalid arguments"); return false; } if (!node->subnets || !node->subnets->v4_subnets) { @@ -184,13 +184,13 @@ bool route_insert(struct ROUTE_TABLE *table, struct TOPO_NODEQ *node) { } DEBUG_INFO(DEBUG_CATEGORY_ROUTING, "added %zu route(s) for node=%016llx", - count, (unsigned long long)node->node->node_id); + count, (unsigned long long)node->node_id); return true; } -void route_delete(struct ROUTE_TABLE *table, struct TOPO_NODEQ *node) { +void route_delete(struct ROUTE_TABLE *table, struct TOPO_GROUP_NODE *node) { if (!table || !node) return; - DEBUG_INFO(DEBUG_CATEGORY_ROUTING, "removing all routes for node=%016llx", (unsigned long long)node->node->node_id); + DEBUG_INFO(DEBUG_CATEGORY_ROUTING, "removing all routes for node=%016llx", (unsigned long long)node->node_id); size_t i = 0, removed = 0; while (i < table->count) { if (table->entries[i].v_node_info == node) { diff --git a/src/routing_layer/route_lib.h b/src/routing_layer/route_lib.h index 010845ee..657e7038 100644 --- a/src/routing_layer/route_lib.h +++ b/src/routing_layer/route_lib.h @@ -15,7 +15,7 @@ extern "C" { struct ETCP_CONNECTIONS; struct ROUTE_TABLE; struct ROUTE_ENTRY; -struct TOPO_NODEQ; +struct TOPO_GROUP_NODE; /** * @brief Флаги узла @@ -34,7 +34,7 @@ typedef enum { struct ROUTE_ENTRY { uint32_t network; // Сетевой адрес (big-endian) uint8_t prefix_length; // Длина префикса подсети - struct TOPO_NODEQ* v_node_info; // узел владелец этих маршрутов. null если - локальный маршрут. + struct TOPO_GROUP_NODE* v_node_info; // узел владелец этих маршрутов. null если - локальный маршрут. }; /** @@ -79,7 +79,7 @@ void route_table_destroy(struct ROUTE_TABLE *table); * * @return true если вставка/обновление успешно */ -bool route_insert(struct ROUTE_TABLE *table, struct TOPO_NODEQ *node); +bool route_insert(struct ROUTE_TABLE *table, struct TOPO_GROUP_NODE *node); /** * @brief Удаляет все записи из таблицы маршрутизации для указанного узла @@ -87,7 +87,7 @@ bool route_insert(struct ROUTE_TABLE *table, struct TOPO_NODEQ *node); * @param table Указатель на таблицу маршрутизации * @param node узел, все маршруты которого нужно удалить */ -void route_delete(struct ROUTE_TABLE *table, struct TOPO_NODEQ *node); +void route_delete(struct ROUTE_TABLE *table, struct TOPO_GROUP_NODE *node); /** * @brief Выполняет поиск маршрута для заданного IP-адреса diff --git a/src/routing_layer/route_ping.c b/src/routing_layer/route_ping.c index dad1e81c..51fb6207 100644 --- a/src/routing_layer/route_ping.c +++ b/src/routing_layer/route_ping.c @@ -349,7 +349,7 @@ void route_ping_handle_req(struct NAT_DETECTION* nd, struct TOPO_GROUP* group, { if (group) { - struct TOPO_NODEQ* req_nq = topo_node_find_by_id(group, from_conn->peer_node_id); + struct TOPO_GROUP_NODE* req_nq = topo_node_find_by_id(group, from_conn->peer_node_id); if (req_nq) req_nq->connectivity.ping_req_time = get_time_tb(); } } diff --git a/src/routing_layer/routing.c b/src/routing_layer/routing.c index 29c0443b..5211d609 100644 --- a/src/routing_layer/routing.c +++ b/src/routing_layer/routing.c @@ -138,13 +138,14 @@ void route_pkt(struct UTUN_INSTANCE* instance, struct ll_entry* entry, uint64_t return; } - struct TOPO_NODEQ* nq = route->v_node_info; - if (!nq || !nq->node || nq->hop_count == 0) { + struct TOPO_GROUP_NODE* nq = route->v_node_info; + struct TOPO_NODE* rni = nq ? topo_node_registry_find(instance->topo_groups, nq->node_id) : NULL; + if (!nq || !rni || nq->hop_count == 0) { DEBUG_TRACE(DEBUG_CATEGORY_ROUTING, "Local route to %s", ip_to_str(&addr, AF_INET).str); } else { DEBUG_TRACE(DEBUG_CATEGORY_ROUTING, "route_pkt: sending %zu bytes to node %016llx dst=%s", - ip_len, (unsigned long long)nq->node->node_id, ip_to_str(&addr, AF_INET).str); - int send_err = etcp_route_send(instance, nq->node->group_id, nq->node->node_id, entry, 1); + ip_len, (unsigned long long)nq->node_id, ip_to_str(&addr, AF_INET).str); + int send_err = etcp_route_send(instance, rni->group_id, nq->node_id, entry, 1); if (send_err != 0) { DEBUG_WARN(DEBUG_CATEGORY_ROUTING, "route_pkt: etcp_route_send failed: dst=%s err=%d", ip_to_str(&addr, AF_INET).str, send_err); diff --git a/src/routing_layer/topo_group.c b/src/routing_layer/topo_group.c index 9d47a10b..30f46e89 100644 --- a/src/routing_layer/topo_group.c +++ b/src/routing_layer/topo_group.c @@ -62,7 +62,7 @@ static void topo_group_send_table_complete(struct TOPO_GROUP* group, struct ETCP } static void topo_group_add_to_senders(struct TOPO_GROUP* group, struct ETCP_CONN* conn); -static bool topo_group_should_send_to(const struct TOPO_NODEQ* nq, uint64_t target_id); +static bool topo_group_should_send_to(const struct TOPO_GROUP_NODE* nq, uint64_t target_id); static void topo_group_send_full_table(struct TOPO_GROUP* group, struct ETCP_CONN* conn); static void topo_group_handle_request_table(struct TOPO_GROUP* group, struct ETCP_CONN* conn); @@ -173,6 +173,7 @@ static void topo_group_receive_cbk(struct ETCP_CONN* from_conn, struct ll_entry* } DEBUG_INFO(DEBUG_CATEGORY_BGP, "BGP recv %s from %s len=%zu group=%016llx", group_subcmd_name(subcmd), from_conn->log_name, entry->len, (unsigned long long)pkt_group_id); + if (subcmd == TOPO_SUBCMD_NODEINFO) { DEBUG_DEBUG(DEBUG_CATEGORY_DEBUG, "BGP recv NODEINFO from=%s nid=%016llx ver=%d grp=%016llx", from_conn->log_name, (unsigned long long)((struct TOPOMSG_NODEINFO_PKT*)data)->node.node_id, ((struct TOPOMSG_NODEINFO_PKT*)data)->node.ver, (unsigned long long)((struct TOPOMSG_NODEINFO_PKT*)data)->node.group_id); } if (subcmd == TOPO_SUBCMD_NODEINFO) { nodeinfo_dump_log(data, entry->len); topo_group_process_nodeinfo(group, from_conn, data, entry->len); } else if (subcmd == TOPO_SUBCMD_WITHDRAW) topo_group_process_withdraw(group, from_conn, data, entry->len); @@ -219,7 +220,7 @@ static struct TOPO_GROUP* topo_group_create(struct UTUN_INSTANCE* instance, uint sc_derive_ed25519_pubkey(instance->my_keys.private_key, group->ed25519_public_key); DEBUG_INFO(DEBUG_CATEGORY_BGP, "Ed25519 pubkey derived from X25519 private key"); - group->nodes = queue_new(instance->ua, BGP_NODES_HASH_SIZE, offsetof(struct TOPO_NODEQ, hash_node_id) - sizeof(struct ll_entry), 8, "group_nodes"); + group->nodes = queue_new(instance->ua, BGP_NODES_HASH_SIZE, offsetof(struct TOPO_GROUP_NODE, node_id) - sizeof(struct ll_entry), 8, "group_nodes"); if (!group->nodes) { queue_entry_free(qe); DEBUG_ERROR(DEBUG_CATEGORY_BGP, "nodes queue creation failed"); return NULL; } group->senders_list = queue_new(instance->ua, 0, 0, 0, "group_senders"); @@ -245,7 +246,7 @@ static void topo_group_destroy(struct TOPO_GROUP* group) { while ((e = queue_data_get(group->senders_list)) != NULL) queue_entry_free(e); queue_free(group->senders_list); - if (group->local_node) { topo_node_free_lists(group, group->local_node); u_free(group->local_node); } + if (group->local_node) { topo_nodeq_free_group_fields(group->instance->topo_groups, group->local_node); u_free(group->local_node); } if (group->nodes) queue_free(group->nodes); group->conn_mgr = NULL; /* destroyed externally in utun_instance_destroy before etcp_router_destroy */ @@ -334,7 +335,7 @@ void topo_groups_destroy(struct UTUN_INSTANCE* instance) { while ((re = queue_data_get(g->node_registry)) != NULL) { struct TOPO_NODE* node; memcpy(&node, re->data + 8, sizeof(node)); - topo_node_unref(node); + topo_node_registry_unref(g, node->node_id); queue_entry_free(re); } queue_free(g->node_registry); @@ -404,7 +405,7 @@ void topo_group_new_conn(struct TOPO_GROUP* group, struct ETCP_CONN* conn) { topo_recovery_cancel_for_node(group, conn->peer_node_id); - struct TOPO_NODEQ* peer_nq = topo_node_find_by_id(group, conn->peer_node_id); + struct TOPO_GROUP_NODE* peer_nq = topo_node_find_by_id(group, conn->peer_node_id); topo_group_add_to_senders(group, conn); DEBUG_DEBUG(DEBUG_CATEGORY_DEBUG, "topo_group_new_conn: peer=%016llx group=%016llx type=%d ch=%s", (unsigned long long)conn->peer_node_id, (unsigned long long)group->group_id, group->group_type, group->channel_id); @@ -430,16 +431,16 @@ void topo_group_remove_conn(struct TOPO_GROUP* group, struct ETCP_CONN* conn) { struct ll_entry* node_entry = group->nodes ? group->nodes->head : NULL; while (node_entry) { struct ll_entry* next = node_entry->next; - struct TOPO_NODEQ* nq = (struct TOPO_NODEQ*)node_entry; + struct TOPO_GROUP_NODE* nq = (struct TOPO_GROUP_NODE*)node_entry; if (topo_group_remove_path(nq, conn) == 1) { - uint64_t key = nq->hash_node_id; + uint64_t key = nq->node_id; if (key != conn->peer_node_id) { topo_recovery_add_node(group, nq, conn->peer_node_id); cascaded++; } if (group->group_type != TOPO_GROUP_TYPE_CHAT && rt) route_delete(rt, nq); if (conn->instance && conn->instance->control_srv) control_server_notify_node_removed(conn->instance->control_srv, key); nq->dirty = 1; route_connectivity_cancel_node(conn->instance, nq); if (nq->paths) { queue_free(nq->paths); nq->paths = NULL; } - topo_node_free_lists(group, nq); + topo_nodeq_free_group_fields(group->instance->topo_groups, nq); struct ll_entry* entry = node_entry; if (entry) { queue_remove_data(group->nodes, entry); queue_entry_free(entry); } nodes_removed++; @@ -468,7 +469,9 @@ void topo_group_remove_conn(struct TOPO_GROUP* group, struct ETCP_CONN* conn) { struct ETCP_CONN* topo_group_find_conn_for_node(struct TOPO_GROUP* group, uint64_t node_id) { if (!group) return NULL; - struct TOPO_NODEQ* nq = topo_node_find_by_id(group, node_id); + struct TOPO_GROUP_NODE* nq = topo_node_find_by_id(group, node_id); + DEBUG_DEBUG(DEBUG_CATEGORY_DEBUG, "find_conn_for_node %016llx: nq=%p paths=%p head=%p", + (unsigned long long)node_id, (void*)nq, nq ? (void*)nq->paths : NULL, (nq && nq->paths) ? (void*)nq->paths->head : NULL); if (!nq || !nq->paths || !nq->paths->head) return NULL; struct ll_entry* e = nq->paths->head; struct TOPO_NODEPATH* best_live = NULL; uint8_t best_live_hops = 255; @@ -482,7 +485,7 @@ struct ETCP_CONN* topo_group_find_conn_for_node(struct TOPO_GROUP* group, uint64 return best_live ? best_live->conn : (best_any ? best_any->conn : NULL); } -int topo_group_add_path(struct TOPO_NODEQ* nq, struct ETCP_CONN* conn, uint64_t* hop_list, uint8_t hop_count, uint16_t cumulative_rtt) { +int topo_group_add_path(struct TOPO_GROUP_NODE* nq, struct ETCP_CONN* conn, uint64_t* hop_list, uint8_t hop_count, uint16_t cumulative_rtt) { if (!nq || !conn || hop_count > MAX_HOPS || !hop_list) { DEBUG_ERROR(DEBUG_CATEGORY_BGP, "add_path: invalid args"); return -1; } if (!nq->paths) { nq->paths = queue_new(conn->instance->ua, 0, 0, 0, "node_paths"); if (!nq->paths) return -1; } size_t path_size = sizeof(struct TOPO_NODEPATH) - sizeof(struct ll_entry) + hop_count * 8; @@ -494,7 +497,7 @@ int topo_group_add_path(struct TOPO_NODEQ* nq, struct ETCP_CONN* conn, uint64_t* return 0; } -static int topo_group_remove_path_by_hop(struct TOPO_NODEQ* nq, uint64_t wd_source) { +static int topo_group_remove_path_by_hop(struct TOPO_GROUP_NODE* nq, uint64_t wd_source) { if (!nq || !nq->paths) return 0; int removed = 0; struct ll_entry* e = nq->paths->head; @@ -509,7 +512,7 @@ static int topo_group_remove_path_by_hop(struct TOPO_NODEQ* nq, uint64_t wd_sour return removed; } -int topo_group_remove_path(struct TOPO_NODEQ* nq, struct ETCP_CONN* conn) { +int topo_group_remove_path(struct TOPO_GROUP_NODE* nq, struct ETCP_CONN* conn) { if (!nq || !conn || !nq->paths) return -1; struct ll_entry* e = nq->paths->head; while (e) { struct TOPO_NODEPATH* path = (struct TOPO_NODEPATH*)e; if (path->conn == conn) { queue_remove_data(nq->paths, e); queue_entry_free(e); break; } e = e->next; } @@ -540,8 +543,8 @@ int topo_group_process_nodeinfo(struct TOPO_GROUP* group, struct ETCP_CONN* from struct TOPOMSG_NODE* ni = &pkt->node; if (ni->group_id != group->group_id) { - DEBUG_ERROR(DEBUG_CATEGORY_BGP, "NODEINFO group_id mismatch from %s: expected=%016llx got=%016llx", - from->log_name, (unsigned long long)group->group_id, (unsigned long long)ni->group_id); + DEBUG_ERROR(DEBUG_CATEGORY_DEBUG, "NODEINFO group_id mismatch from %s: expected=%016llx got=%016llx nid=%016llx", + from->log_name, (unsigned long long)group->group_id, (unsigned long long)ni->group_id, (unsigned long long)ni->node_id); return -1; } @@ -561,8 +564,10 @@ int topo_group_process_nodeinfo(struct TOPO_GROUP* group, struct ETCP_CONN* from if (ni->hop_count >= MAX_HOPS) { DEBUG_WARN(DEBUG_CATEGORY_BGP, "NODEINFO from %s dropped: too many hops (%d)", from->log_name, ni->hop_count); return -1; } if (node_id == group->instance->node_id) return 0; - struct TOPO_NODEQ* nodeinfo1 = topo_node_find_by_id(group, node_id); + struct TOPO_GROUP_NODE* nodeinfo1 = topo_node_find_by_id(group, node_id); uint8_t new_ver = ni->ver; + DEBUG_DEBUG(DEBUG_CATEGORY_DEBUG, "process_nodeinfo: nid=%016llx ver=%d hops=%d nodeinfo1=%p from=%s", + (unsigned long long)node_id, new_ver, ni->hop_count, (void*)nodeinfo1, from->log_name); if (nodeinfo1 && (int8_t)(nodeinfo1->last_ver - new_ver) >= 0) { DEBUG_DEBUG(DEBUG_CATEGORY_DEBUG, "NODEINFO skip (stale ver): node=%016llx cur_ver=%d new_ver=%d from=%s", (unsigned long long)node_id, nodeinfo1->last_ver, new_ver, from->log_name); @@ -585,7 +590,7 @@ int topo_group_process_nodeinfo(struct TOPO_GROUP* group, struct ETCP_CONN* from struct ll_queue* paths = NULL; int is_new_node = 0; if (nodeinfo1) { - topo_node_free_lists(group, nodeinfo1); + topo_nodeq_free_group_fields(group->instance->topo_groups, nodeinfo1); paths = nodeinfo1->paths; nodeinfo1->paths = NULL; } else { is_new_node = 1; } @@ -597,10 +602,12 @@ int topo_group_process_nodeinfo(struct TOPO_GROUP* group, struct ETCP_CONN* from uint64_t* new_hop_list = NULL; uint8_t new_hop_count = 0; uint16_t incoming_cumulative_rtt = 0; if (topo_node_deserialize(group, ser_data, ser_len, &new_ni, &new_subnets, &new_tranzit, &new_tranzit_count, &new_hop_list, &new_hop_count, &incoming_cumulative_rtt) != 0) { - DEBUG_ERROR(DEBUG_CATEGORY_BGP, "NODEINFO deserialize failed from %s", from->log_name); + DEBUG_ERROR(DEBUG_CATEGORY_DEBUG, "NODEINFO deserialize failed from %s nid=%016llx", from->log_name, (unsigned long long)node_id); if (nodeinfo1) { queue_remove_data(group->nodes, &nodeinfo1->ll); queue_free(paths); queue_entry_free(&nodeinfo1->ll); } return -1; } + DEBUG_DEBUG(DEBUG_CATEGORY_DEBUG, "NODEINFO deserialized: nid=%016llx new_ver=%d flags=0x%02x ed=%016llx", + (unsigned long long)node_id, new_ni->ver, new_ni->flags, *(uint64_t*)new_ni->ed25519_public_key); /* verify Ed25519 self-signature: Ed25519_pubkey must sign X25519_pubkey */ { @@ -611,7 +618,7 @@ int topo_group_process_nodeinfo(struct TOPO_GROUP* group, struct ETCP_CONN* from } else if (sc_ed25519_verify(new_ni->ed25519_public_key, new_ni->public_key, SC_PUBKEY_SIZE, new_ni->x25519_self_sig) != SC_OK) { DEBUG_ERROR(DEBUG_CATEGORY_BGP, "NODEINFO x25519_self_sig VERIFY FAIL node=%016llx ed_pubkey=%016llx... from=%s — rejecting as forgery", (unsigned long long)node_id, ekchk, from->log_name); - topo_node_free_raw(group->instance->topo_groups, new_ni); + topo_node_destroy(group->instance->topo_groups, new_ni); u_free(new_subnets); u_free(new_tranzit); u_free(new_hop_list); if (nodeinfo1) { queue_remove_data(group->nodes, &nodeinfo1->ll); queue_free(paths); queue_entry_free(&nodeinfo1->ll); } return -1; @@ -622,8 +629,13 @@ int topo_group_process_nodeinfo(struct TOPO_GROUP* group, struct ETCP_CONN* from } if (nodeinfo1) { - nodeinfo1->node = topo_node_registry_acquire(group->instance->topo_groups, new_ni); - nodeinfo1->hash_node_id = nodeinfo1->node ? nodeinfo1->node->node_id : 0; + { struct TOPO_NODE* stored = topo_node_registry_store(group->instance->topo_groups, new_ni); + DEBUG_DEBUG(DEBUG_CATEGORY_DEBUG, "NODEINFO update: stored=%p new_ni=%p same=%d nid=%016llx", + (void*)stored, (void*)new_ni, stored == new_ni, (unsigned long long)(stored ? stored->node_id : 0)); + if (stored != new_ni) new_ni = stored; } + new_ni->group_id = group->group_id; + new_ni->flags = pkt->node.flags; + nodeinfo1->node_id = new_ni ? new_ni->node_id : 0; nodeinfo1->subnets = new_subnets; nodeinfo1->cumulative_rtt = incoming_cumulative_rtt; u_free(nodeinfo1->tranzit_data); nodeinfo1->tranzit_data = new_tranzit; @@ -631,12 +643,17 @@ int topo_group_process_nodeinfo(struct TOPO_GROUP* group, struct ETCP_CONN* from u_free(nodeinfo1->hop_list); nodeinfo1->hop_list = new_hop_list; nodeinfo1->hop_count = new_hop_count; } else { - struct ll_entry* qe = queue_entry_new(sizeof(struct TOPO_NODEQ)); - if (!qe) { topo_node_free_raw(group->instance->topo_groups, new_ni); u_free(new_tranzit); u_free(new_hop_list); u_free(new_subnets); return -1; } - nodeinfo1 = (struct TOPO_NODEQ*)qe; + struct ll_entry* qe = queue_entry_new(sizeof(struct TOPO_GROUP_NODE)); + if (!qe) { topo_node_destroy(group->instance->topo_groups, new_ni); u_free(new_tranzit); u_free(new_hop_list); u_free(new_subnets); return -1; } + nodeinfo1 = (struct TOPO_GROUP_NODE*)qe; memset((uint8_t*)nodeinfo1 + sizeof(struct ll_entry), 0, sizeof(*nodeinfo1) - sizeof(struct ll_entry)); - nodeinfo1->node = topo_node_registry_acquire(group->instance->topo_groups, new_ni); - nodeinfo1->hash_node_id = nodeinfo1->node ? nodeinfo1->node->node_id : 0; + { struct TOPO_NODE* stored = topo_node_registry_store(group->instance->topo_groups, new_ni); + DEBUG_DEBUG(DEBUG_CATEGORY_DEBUG, "NODEINFO new: stored=%p new_ni=%p same=%d nid=%016llx", + (void*)stored, (void*)new_ni, stored == new_ni, (unsigned long long)(stored ? stored->node_id : 0)); + if (stored != new_ni) new_ni = stored; } + new_ni->group_id = group->group_id; + new_ni->flags = pkt->node.flags; + nodeinfo1->node_id = new_ni ? new_ni->node_id : 0; nodeinfo1->subnets = new_subnets; nodeinfo1->cumulative_rtt = incoming_cumulative_rtt; nodeinfo1->tranzit_data = new_tranzit; nodeinfo1->tranzit_count = new_tranzit_count; @@ -667,7 +684,7 @@ int topo_group_process_nodeinfo(struct TOPO_GROUP* group, struct ETCP_CONN* from if (node_id != group->instance->node_id) { sqlite3* sdb = group->instance->topo_sqlite_db; if (sdb) { - topo_node_sqlite_node_put(sdb, nodeinfo1, ntp_time_get_seconds(group->instance)); + topo_node_sqlite_node_put(sdb, group->instance->topo_groups, nodeinfo1, ntp_time_get_seconds(group->instance)); if (group->group_type == TOPO_GROUP_TYPE_CHAT && group->channel_id[0]) { topo_node_sqlite_member_put(sdb, group->channel_id, node_id, NULL, 0, NULL, 0, NULL, NULL, NULL, NULL, NULL, NULL, 0); DEBUG_DEBUG(DEBUG_CATEGORY_DEBUG, "SQLite member_put: ch=%s node=%016llx", group->channel_id, (unsigned long long)node_id); @@ -708,8 +725,9 @@ int topo_group_process_nodeinfo(struct TOPO_GROUP* group, struct ETCP_CONN* from if (!is_new_node) socks_changed = 1; if (node_id != group->instance->node_id && (is_new_node || socks_changed)) { - int v4a = topo_list_count((struct _topo_head*)nodeinfo1->node->v4_addrs); - int v6a = topo_list_count((struct _topo_head*)nodeinfo1->node->v6_addrs); + struct TOPO_NODE* tni = topo_node_registry_find(group->instance->topo_groups, nodeinfo1->node_id); + int v4a = tni ? topo_list_count((struct _topo_head*)tni->v4_addrs) : 0; + int v6a = tni ? topo_list_count((struct _topo_head*)tni->v6_addrs) : 0; if (v4a > 0 || v6a > 0) route_connectivity_probe_node(group->instance, nodeinfo1); } @@ -721,7 +739,7 @@ int topo_group_process_withdraw(struct TOPO_GROUP* group, struct ETCP_CONN* send struct TOPOMSG_WITHDRAW_PKT* wp = (struct TOPOMSG_WITHDRAW_PKT*)data; uint64_t node_id = wp->node_id, wd_source = wp->wd_source; - struct TOPO_NODEQ* nq = topo_node_find_by_id(group, node_id); + struct TOPO_GROUP_NODE* nq = topo_node_find_by_id(group, node_id); if (!nq) { DEBUG_INFO(DEBUG_CATEGORY_BGP, "node not found"); return 0; } int ret = topo_group_remove_path_by_hop(nq, wd_source); if (ret == 1) { @@ -730,7 +748,7 @@ int topo_group_process_withdraw(struct TOPO_GROUP* group, struct ETCP_CONN* send nq->dirty = 1; route_connectivity_cancel_node(group->instance, nq); if (nq->paths) { queue_free(nq->paths); nq->paths = NULL; } - topo_node_free_lists(group, nq); + topo_nodeq_free_group_fields(group->instance->topo_groups, nq); struct ll_entry* entry = queue_find_data_by_index(group->nodes, &node_id); if (entry) { queue_remove_data(group->nodes, entry); queue_entry_free(entry); } topo_group_broadcast_withdraw(group, node_id, wd_source, sender); @@ -744,7 +762,7 @@ int topo_group_process_withdraw(struct TOPO_GROUP* group, struct ETCP_CONN* send return 0; } -void topo_group_send_nodeinfo(struct TOPO_GROUP* group, struct TOPO_NODEQ* node, struct ETCP_CONN* conn, uint16_t cumulative_rtt) { +void topo_group_send_nodeinfo(struct TOPO_GROUP* group, struct TOPO_GROUP_NODE* node, struct ETCP_CONN* conn, uint16_t cumulative_rtt) { if (!group || !node || !conn) return; size_t max_sz = sizeof(struct TOPOMSG_NODEINFO_PKT) + 4096; @@ -753,14 +771,17 @@ void topo_group_send_nodeinfo(struct TOPO_GROUP* group, struct TOPO_NODEQ* node, p[0] = ETCP_ID_TOPO_ENTRY; p[1] = TOPO_SUBCMD_NODEINFO; - int ser_len = topo_node_serialize(group, node, p + 2, max_sz - 2, cumulative_rtt); - if (ser_len < 0) { u_free(p); return; } + struct TOPO_NODE* sni = topo_node_registry_find(group->instance->topo_groups, node->node_id); + if (!sni) { DEBUG_ERROR(DEBUG_CATEGORY_DEBUG, "send_nodeinfo: node %016llx NOT in registry — skip forward to %s", (unsigned long long)node->node_id, conn->log_name); u_free(p); return; } + DEBUG_DEBUG(DEBUG_CATEGORY_DEBUG, "send_nodeinfo: node %016llx ver=%d grp=%016llx to conn=%s", (unsigned long long)node->node_id, sni->ver, (unsigned long long)sni->group_id, conn->log_name); + int ser_len = topo_node_serialize(sni, node, p + 2, max_sz - 2, cumulative_rtt); + if (ser_len < 0) { DEBUG_ERROR(DEBUG_CATEGORY_DEBUG, "send_nodeinfo: serialize failed for node %016llx", (unsigned long long)node->node_id); u_free(p); return; } struct ll_entry* e = queue_entry_new(0); if (!e) { u_free(p); return; } e->dgram = p; e->len = (size_t)ser_len + 2; - if (etcp_send(conn, e) != 0) { DEBUG_ERROR(DEBUG_CATEGORY_BGP, "etcp_send NODEINFO failed"); u_free(p); queue_entry_free(e); } + if (etcp_send(conn, e) != 0) { DEBUG_ERROR(DEBUG_CATEGORY_DEBUG, "send_nodeinfo: etcp_send FAILED for node %016llx to %s", (unsigned long long)node->node_id, conn->log_name); u_free(p); queue_entry_free(e); } } static void topo_group_add_to_senders(struct TOPO_GROUP* group, struct ETCP_CONN* conn) { @@ -774,9 +795,9 @@ static void topo_group_add_to_senders(struct TOPO_GROUP* group, struct ETCP_CONN } } -static bool topo_group_should_send_to(const struct TOPO_NODEQ* nq, uint64_t target_id) { - if (!nq || !nq->node || !nq->paths) return false; - if (target_id == nq->node->node_id) return false; +static bool topo_group_should_send_to(const struct TOPO_GROUP_NODE* nq, uint64_t target_id) { + if (!nq || !nq->paths) return false; + if (target_id == nq->node_id) return false; struct ll_entry* e = nq->paths->head; while (e) { struct TOPO_NODEPATH* path = (struct TOPO_NODEPATH*)e; @@ -793,7 +814,7 @@ static void topo_group_send_full_table(struct TOPO_GROUP* group, struct ETCP_CON uint64_t target = conn->peer_node_id; struct ll_entry* e = group->nodes ? group->nodes->head : NULL; while (e) { - struct TOPO_NODEQ* nq = (struct TOPO_NODEQ*)e; + struct TOPO_GROUP_NODE* nq = (struct TOPO_GROUP_NODE*)e; if (nq->hop_count == 0) { e = e->next; continue; } if (!nq->paths) { DEBUG_WARN(DEBUG_CATEGORY_BGP, "node has no paths"); e = e->next; continue; } if (topo_group_should_send_to(nq, target)) topo_group_send_nodeinfo(group, nq, conn, (uint16_t)((uint32_t)conn->rtt_last + nq->cumulative_rtt)); diff --git a/src/routing_layer/topo_group.h b/src/routing_layer/topo_group.h index c809b0eb..64144967 100644 --- a/src/routing_layer/topo_group.h +++ b/src/routing_layer/topo_group.h @@ -148,7 +148,7 @@ struct TOPO_GROUP { struct UTUN_INSTANCE* instance; struct ll_queue* senders_list; struct ll_queue* nodes; - struct TOPO_NODEQ* local_node; + struct TOPO_GROUP_NODE* local_node; uint8_t ed25519_public_key[SC_PUBKEY_SIZE]; char channel_id[64]; // channel_id для групп типа CHAT struct CONN_MGR* conn_mgr; // менеджер соединений для этой группы @@ -245,7 +245,7 @@ int topo_group_process_withdraw(struct TOPO_GROUP* group, struct ETCP_CONN* send /** * @brief Отправляет NODEINFO пакет одному conn (всегда local_node). */ -void topo_group_send_nodeinfo(struct TOPO_GROUP* group, struct TOPO_NODEQ* node, struct ETCP_CONN* conn, uint16_t cumulative_rtt); +void topo_group_send_nodeinfo(struct TOPO_GROUP* group, struct TOPO_GROUP_NODE* node, struct ETCP_CONN* conn, uint16_t cumulative_rtt); /** * @brief Отправляет WITHDRAW для node_id (вызывает broadcast_withdraw). @@ -268,14 +268,14 @@ struct ETCP_CONN* topo_group_find_conn_for_node(struct TOPO_GROUP* group, uint64 * * @return 0 при успехе */ -int topo_group_add_path(struct TOPO_NODEQ* nq, struct ETCP_CONN* conn, uint64_t* hop_list, uint8_t hop_count, uint16_t cumulative_rtt); +int topo_group_add_path(struct TOPO_GROUP_NODE* nq, struct ETCP_CONN* conn, uint64_t* hop_list, uint8_t hop_count, uint16_t cumulative_rtt); /** * @brief Удаляет conn из paths узла. * * @return 1 если путей не осталось (unreachable) */ -int topo_group_remove_path(struct TOPO_NODEQ* nq, struct ETCP_CONN* conn); +int topo_group_remove_path(struct TOPO_GROUP_NODE* nq, struct ETCP_CONN* conn); void topo_groups_set_node_updated_cb(struct TOPO_GROUPS* groups, topo_node_updated_fn fn); diff --git a/src/routing_layer/topo_group_connect.c b/src/routing_layer/topo_group_connect.c index 892e8269..3cce813d 100644 --- a/src/routing_layer/topo_group_connect.c +++ b/src/routing_layer/topo_group_connect.c @@ -164,7 +164,7 @@ void topo_group_connect_on_down(struct TOPO_GROUP* group, struct ETCP_CONN* conn } /* есть ли indirect-пути в nodeinfo? */ - struct TOPO_NODEQ* nq = topo_node_find_by_id(group, peer); + struct TOPO_GROUP_NODE* nq = topo_node_find_by_id(group, peer); if (nq && nq->paths) { struct ll_entry* pe = nq->paths->head; while (pe) { @@ -216,7 +216,7 @@ static void tgc_phase1_timeout(void* arg) { uint8_t status; uint8_t conn_type; conn_mgr_get_status(gc->group->conn_mgr, ids[i], &status, &conn_type); if (status != CONN_MGR_STATE_CONNECTED) { - conn_mgr_cancel_callback(gc->group->conn_mgr, ids[i], tgc_phase1_result, gc); + conn_mgr_cancel_callback(gc->group->conn_mgr, ids[i], NULL); topo_node_sqlite_set_connected(db, gc->group->channel_id, ids[i], 0); } } diff --git a/src/routing_layer/topo_node.c b/src/routing_layer/topo_node.c index b769baef..a74c0016 100644 --- a/src/routing_layer/topo_node.c +++ b/src/routing_layer/topo_node.c @@ -18,12 +18,17 @@ void topo_node_ref(struct TOPO_NODE* ni) { if (!ni) return; - ni->ref_count++; + ni->group_ref_count++; +} + +void topo_node_registry_ref(struct TOPO_GROUPS* groups, uint64_t node_id) { + struct TOPO_NODE* ni = topo_node_registry_find(groups, node_id); + if (ni) ni->group_ref_count++; } void topo_node_unref(struct TOPO_NODE* ni) { if (!ni) return; - if (--ni->ref_count == 0) { + if (--ni->group_ref_count == 0) { u_free(ni->node_name); u_free(ni); } @@ -60,21 +65,25 @@ void topo_node_free_raw(struct TOPO_GROUPS* groups, struct TOPO_NODE* ni) { u_free(ni); } -void topo_node_free_lists(struct TOPO_GROUP* group, struct TOPO_NODEQ* nq) { - if (!group || !nq) return; - if (nq->node) { - free_v4_sock_list(group->instance->topo_groups->v4_sock_meta_pool, nq->node->v4_sock_meta); - free_v4_addr_list(group->instance->topo_groups->v4_addr_pool, nq->node->v4_addrs); - free_v6_sock_list(group->instance->topo_groups->v6_sock_meta_pool, nq->node->v6_sock_meta); - free_v6_addr_list(group->instance->topo_groups->v6_addr_pool, nq->node->v6_addrs); - nq->node->v4_sock_meta = NULL; nq->node->v4_addrs = NULL; - nq->node->v6_sock_meta = NULL; nq->node->v6_addrs = NULL; - topo_node_registry_release(group->instance->topo_groups, nq->node); - nq->node = NULL; +void topo_node_destroy(struct TOPO_GROUPS* groups, struct TOPO_NODE* ni) { + topo_node_free_raw(groups, ni); +} + +void topo_nodeq_free_group_fields(struct TOPO_GROUPS* groups, struct TOPO_GROUP_NODE* nq) { + if (!groups || !nq) return; + struct TOPO_NODE* ni = topo_node_registry_find(groups, nq->node_id); + if (ni) { + free_v4_sock_list(groups->v4_sock_meta_pool, ni->v4_sock_meta); + free_v4_addr_list(groups->v4_addr_pool, ni->v4_addrs); + free_v6_sock_list(groups->v6_sock_meta_pool, ni->v6_sock_meta); + free_v6_addr_list(groups->v6_addr_pool, ni->v6_addrs); + ni->v4_sock_meta = NULL; ni->v4_addrs = NULL; + ni->v6_sock_meta = NULL; ni->v6_addrs = NULL; + topo_node_registry_unref(groups, nq->node_id); } if (nq->subnets) { - free_v4_sub_list(group->instance->topo_groups->v4_subnet_pool, nq->subnets->v4_subnets); - free_v6_sub_list(group->instance->topo_groups->v6_subnet_pool, nq->subnets->v6_subnets); + free_v4_sub_list(groups->v4_subnet_pool, nq->subnets->v4_subnets); + free_v6_sub_list(groups->v6_subnet_pool, nq->subnets->v6_subnets); u_free(nq->subnets); nq->subnets = NULL; } @@ -84,7 +93,7 @@ void topo_node_free_lists(struct TOPO_GROUP* group, struct TOPO_NODEQ* nq) { // ===== глобальный реестр TOPO_NODE ===== -static struct TOPO_NODE* node_registry_find(struct TOPO_GROUPS* groups, uint64_t node_id) { +struct TOPO_NODE* topo_node_registry_find(struct TOPO_GROUPS* groups, uint64_t node_id) { if (!groups || !groups->node_registry) return NULL; struct ll_entry* e = queue_find_data_by_index(groups->node_registry, &node_id); if (!e) return NULL; @@ -93,9 +102,9 @@ static struct TOPO_NODE* node_registry_find(struct TOPO_GROUPS* groups, uint64_t return node; } -struct TOPO_NODE* topo_node_registry_acquire(struct TOPO_GROUPS* groups, struct TOPO_NODE* ni) { +struct TOPO_NODE* topo_node_registry_store(struct TOPO_GROUPS* groups, struct TOPO_NODE* ni) { if (!groups || !ni) return NULL; - struct TOPO_NODE* existing = node_registry_find(groups, ni->node_id); + struct TOPO_NODE* existing = topo_node_registry_find(groups, ni->node_id); if (existing) { topo_node_ref(existing); topo_node_free_raw(groups, ni); @@ -113,10 +122,11 @@ struct TOPO_NODE* topo_node_registry_acquire(struct TOPO_GROUPS* groups, struct return ni; } -void topo_node_registry_release(struct TOPO_GROUPS* groups, struct TOPO_NODE* ni) { - if (!groups || !ni) return; - uint64_t node_id = ni->node_id; - uint32_t prev_ref = ni->ref_count; +void topo_node_registry_unref(struct TOPO_GROUPS* groups, uint64_t node_id) { + if (!groups) return; + struct TOPO_NODE* ni = topo_node_registry_find(groups, node_id); + if (!ni) return; + uint32_t prev_ref = ni->group_ref_count; topo_node_unref(ni); if (prev_ref == 1) { struct ll_entry* e = groups->node_registry ? queue_find_data_by_index(groups->node_registry, &node_id) : NULL; @@ -139,9 +149,8 @@ int topo_node_dyn_size(const struct TOPOMSG_NODE* msg) { + msg->hop_count * 8; } -int topo_node_serialize(struct TOPO_GROUP* group, struct TOPO_NODEQ* nq, uint8_t* out, size_t out_max, uint16_t cumulative_rtt) { - if (!group || !nq || !nq->node || !out) return -1; - struct TOPO_NODE* ni = nq->node; +int topo_node_serialize(struct TOPO_NODE* ni, struct TOPO_GROUP_NODE* nq, uint8_t* out, size_t out_max, uint16_t cumulative_rtt) { + if (!ni || !nq || !out) return -1; struct TOPOMSG_NODE msg; memset(&msg, 0, sizeof(msg)); @@ -224,7 +233,7 @@ int topo_node_deserialize(struct TOPO_GROUP* group, const uint8_t* data, size_t struct TOPO_NODE* ni = u_calloc(1, sizeof(struct TOPO_NODE)); if (!ni) { DEBUG_ERROR(DEBUG_CATEGORY_BGP, "alloc TOPO_NODE failed"); return -1; } - ni->ref_count = 0; + ni->group_ref_count = 0; ni->flags = msg->flags; ni->group_id = msg->group_id; ni->node_id = msg->node_id; @@ -316,11 +325,11 @@ int topo_node_deserialize(struct TOPO_GROUP* group, const uint8_t* data, size_t return 0; } -struct TOPO_NODEQ* topo_node_find_by_id(struct TOPO_GROUP* group, uint64_t node_id) { +struct TOPO_GROUP_NODE* topo_node_find_by_id(struct TOPO_GROUP* group, uint64_t node_id) { if (!group || !group->nodes) return NULL; uint64_t key = node_id; struct ll_entry* e = queue_find_data_by_index(group->nodes, &key); - return e ? (struct TOPO_NODEQ*)e : NULL; + return e ? (struct TOPO_GROUP_NODE*)e : NULL; } int topo_node_ping_request_cbk(struct TOPO_GROUPS* groups, uint64_t node_id) { @@ -329,7 +338,7 @@ int topo_node_ping_request_cbk(struct TOPO_GROUPS* groups, uint64_t node_id) { struct ll_entry* ge = groups->group_list->head; while (ge) { struct TOPO_GROUP* g = (struct TOPO_GROUP*)ge; - struct TOPO_NODEQ* nq = topo_node_find_by_id(g, node_id); + struct TOPO_GROUP_NODE* nq = topo_node_find_by_id(g, node_id); if (nq) { struct TOPO_CONNECTIVITY* c = &nq->connectivity; if (c->last_ping_time == 0 || (now - c->last_ping_time) > PING_TTL_TB) { @@ -354,7 +363,7 @@ void topo_node_ping_update_rtt(struct TOPO_GROUPS* groups, uint64_t node_id, uin struct ll_entry* ge = groups->group_list->head; while (ge) { struct TOPO_GROUP* g = (struct TOPO_GROUP*)ge; - struct TOPO_NODEQ* nq = topo_node_find_by_id(g, node_id); + struct TOPO_GROUP_NODE* nq = topo_node_find_by_id(g, node_id); if (nq) { nq->connectivity.last_ping_time = now; if (nq->connectivity.interface_min_rtt == 0 || rtt < nq->connectivity.interface_min_rtt) @@ -402,8 +411,8 @@ void topo_node_dump_all(struct TOPO_GROUP* group) { int idx = 0; struct ll_entry* e = group->nodes->head; while (e) { - struct TOPO_NODEQ* nq = (struct TOPO_NODEQ*)e; - struct TOPO_NODE* ni = nq->node; + struct TOPO_GROUP_NODE* nq = (struct TOPO_GROUP_NODE*)e; + struct TOPO_NODE* ni = topo_node_registry_find(group->instance->topo_groups, nq->node_id); if (!ni) { e = e->next; continue; } int is_local = (group->local_node == nq); const char* name_s = ni->node_name ? ni->node_name : ""; @@ -498,8 +507,8 @@ int topo_node_format_all(struct TOPO_GROUP* group, char* buf, size_t buf_size) { int idx = 0; struct ll_entry* e = group->nodes->head; while (e && (size_t)pos < buf_size) { - struct TOPO_NODEQ* nq = (struct TOPO_NODEQ*)e; - struct TOPO_NODE* ni = nq->node; + struct TOPO_GROUP_NODE* nq = (struct TOPO_GROUP_NODE*)e; + struct TOPO_NODE* ni = topo_node_registry_find(group->instance->topo_groups, nq->node_id); if (!ni) { e = e->next; continue; } int is_local = (group->local_node == nq); const char* name_s = ni->node_name ? ni->node_name : ""; @@ -620,9 +629,10 @@ int topo_group_update_my_nodeinfo(struct UTUN_INSTANCE* instance, struct TOPO_GR } int changed = 1; uint8_t old_ver = 0; - if (group->local_node && group->local_node->node) { - struct TOPO_NODE* oni = group->local_node->node; - old_ver = oni->ver; + if (group->local_node) { + struct TOPO_NODE* oni = topo_node_registry_find(instance->topo_groups, group->local_node->node_id); + if (oni) { + old_ver = oni->ver; changed = (vc != (group->local_node->subnets ? topo_list_count((struct _topo_head*)group->local_node->subnets->v4_subnets) : 0)) || (sock_count != topo_list_count((struct _topo_head*)oni->v4_sock_meta)) || (addr_count != topo_list_count((struct _topo_head*)oni->v4_addrs)) @@ -643,23 +653,24 @@ int topo_group_update_my_nodeinfo(struct UTUN_INSTANCE* instance, struct TOPO_GR } if (group->local_node && group->instance && group->instance->topo_groups) if (memcmp(oni->ed25519_public_key, group->ed25519_public_key, SC_PUBKEY_SIZE) != 0) changed = 1; + } } if (changed) { if (group->local_node) { if (instance->rt) route_delete(instance->rt, group->local_node); - topo_node_free_lists(group, group->local_node); + topo_nodeq_free_group_fields(instance->topo_groups, group->local_node); u_free(group->local_node); group->local_node = NULL; } - group->local_node = u_calloc(1, sizeof(struct TOPO_NODEQ)); + group->local_node = u_calloc(1, sizeof(struct TOPO_GROUP_NODE)); if (!group->local_node) return -1; - struct TOPO_NODEQ* lq = group->local_node; + struct TOPO_GROUP_NODE* lq = group->local_node; struct TOPO_NODE* ni = u_calloc(1, sizeof(struct TOPO_NODE)); if (!ni) { u_free(lq); group->local_node = NULL; return -1; } - ni->ref_count = 0; + ni->group_ref_count = 0; ni->flags = (group->group_type == TOPO_GROUP_TYPE_CHAT) ? 0 : TOPO_FLAG_SEND_SUBNETS; ni->group_id = group->group_id; ni->node_id = instance->node_id; @@ -670,8 +681,8 @@ int topo_group_update_my_nodeinfo(struct UTUN_INSTANCE* instance, struct TOPO_GR DEBUG_ERROR(DEBUG_CATEGORY_BGP, "Ed25519 self-sign FAILED for my node=%016llx", (unsigned long long)ni->node_id); if (name_len) { ni->node_name = u_malloc(name_len + 1); if (ni->node_name) { memcpy(ni->node_name, instance->name, name_len); ni->node_name[name_len] = 0; } } - lq->node = topo_node_registry_acquire(instance->topo_groups, ni); - lq->hash_node_id = ni->node_id; + topo_node_registry_store(instance->topo_groups, ni); + lq->node_id = ni->node_id; lq->hop_count = 0; lq->dirty = 1; lq->last_ver = ni->ver; @@ -757,7 +768,8 @@ int topo_group_update_my_nodeinfo(struct UTUN_INSTANCE* instance, struct TOPO_GR if (group->group_type != TOPO_GROUP_TYPE_CHAT && instance->rt && !route_insert(instance->rt, lq)) DEBUG_WARN(DEBUG_CATEGORY_ROUTING, "failed to insert local routes"); } else { - group->local_node->last_ver = group->local_node->node->ver; + struct TOPO_NODE* lni = topo_node_registry_find(instance->topo_groups, group->local_node->node_id); + if (lni) group->local_node->last_ver = lni->ver; } return vc; } diff --git a/src/routing_layer/topo_node_sqlite.c b/src/routing_layer/topo_node_sqlite.c index 852260f5..55ba3b4d 100644 --- a/src/routing_layer/topo_node_sqlite.c +++ b/src/routing_layer/topo_node_sqlite.c @@ -124,9 +124,10 @@ static uint8_t collect_socket_ids(struct TOPO_NODE* ni, uint8_t* sock_ids) { return count; } -int topo_node_sqlite_node_put(sqlite3* db, struct TOPO_NODEQ* nq, time_t now_sec) { - if (!db || !nq || !nq->node) return -1; - struct TOPO_NODE* ni = nq->node; +int topo_node_sqlite_node_put(sqlite3* db, struct TOPO_GROUPS* groups, struct TOPO_GROUP_NODE* nq, time_t now_sec) { + if (!db || !nq) return -1; + struct TOPO_NODE* ni = topo_node_registry_find(groups, nq->node_id); + if (!ni) return -1; sqlite3_exec(db, "BEGIN", NULL, NULL, NULL); @@ -760,7 +761,7 @@ struct TOPO_NODE* topo_node_sqlite_node_load(sqlite3* db, struct TOPO_GROUPS* gr struct TOPO_NODE* ni = u_calloc(1, sizeof(struct TOPO_NODE)); if (!ni) { u_free(name); return NULL; } - ni->node_id = node_id; ni->ver = 1; ni->ref_count = 0; + ni->node_id = node_id; ni->ver = 1; ni->group_ref_count = 0; memcpy(ni->public_key, pubkey, 32); memcpy(ni->ed25519_public_key, ed_pubkey, 32); ni->node_name = name; ni->v4_addrs = v4_head; diff --git a/src/routing_layer/topo_node_sqlite.h b/src/routing_layer/topo_node_sqlite.h index b4c21d2a..3464437e 100644 --- a/src/routing_layer/topo_node_sqlite.h +++ b/src/routing_layer/topo_node_sqlite.h @@ -5,9 +5,7 @@ #include #include #include - -struct TOPO_NODEQ; -struct TOPO_GROUPS; +#include "topo_node.h" #define PEERS_JOIN_SIG_SIZE 64 #define PEERS_JOIN_TS_SIZE 8 @@ -18,7 +16,7 @@ struct TOPO_GROUPS; int topo_node_sqlite_init(sqlite3* db); -int topo_node_sqlite_node_put(sqlite3* db, struct TOPO_NODEQ* nq, time_t now_sec); +int topo_node_sqlite_node_put(sqlite3* db, struct TOPO_GROUPS* groups, struct TOPO_GROUP_NODE* nq, time_t now_sec); int topo_node_sqlite_channel_put(sqlite3* db, const char* channel_id, const char* name, uint64_t owner_node_id, diff --git a/src/routing_layer/topo_recovery.c b/src/routing_layer/topo_recovery.c index 08ecb131..72a03efd 100644 --- a/src/routing_layer/topo_recovery.c +++ b/src/routing_layer/topo_recovery.c @@ -44,11 +44,13 @@ static void topo_recovery_ctx_free(struct TOPO_RECOVERY_CTX* ctx) { } /* Аналог cm_has_direct_ip: есть ли адреса с прямым доступом (PUBLIC/UNKNOWN/EIM/DIRECT) */ -static int node_has_direct_ip(const struct TOPO_NODEQ* nq) { - if (!nq || !nq->node) return 0; - for (const struct TOPO_ADDR4* a = nq->node->v4_addrs; a; a = a->next) { +static int node_has_direct_ip(struct TOPO_GROUP* group, const struct TOPO_GROUP_NODE* nq) { + if (!nq) return 0; + struct TOPO_NODE* ni = topo_node_registry_find(group->instance->topo_groups, nq->node_id); + if (!ni) return 0; + for (const struct TOPO_ADDR4* a = ni->v4_addrs; a; a = a->next) { if (a->type != TOPO_ADDR_NAT && a->type != TOPO_ADDR_INTERFACE) continue; - for (const struct TOPO_SOCKMETA4* m = nq->node->v4_sock_meta; m; m = m->next) { + for (const struct TOPO_SOCKMETA4* m = ni->v4_sock_meta; m; m = m->next) { if (m->id != a->socket_id) continue; if (m->config_type == CFG_SERVER_TYPE_PUBLIC || m->config_type == CFG_SERVER_TYPE_UNKNOWN || m->nat_type == NAT_VERIFIED_EIM || m->nat_type == NAT_VERIFIED_DIRECT) @@ -59,7 +61,7 @@ static int node_has_direct_ip(const struct TOPO_NODEQ* nq) { } /* Минимальный RTT из данных connectivity-проб (interface/nat/real). 0xFFFF если неизвестен */ -static uint16_t node_get_min_rtt(const struct TOPO_NODEQ* nq) { +static uint16_t node_get_min_rtt(const struct TOPO_GROUP_NODE* nq) { uint16_t rtt = 0xFFFF; if (nq->connectivity.interface_status == PROBE_RESULT_REACHABLE && nq->connectivity.interface_min_rtt < rtt) rtt = nq->connectivity.interface_min_rtt; @@ -74,10 +76,10 @@ static uint16_t node_get_min_rtt(const struct TOPO_NODEQ* nq) { * hop_list = [...узлы пути...], последний элемент = failed_peer (форвардил NODEINFO нам). * next_hop = элемент перед failed_peer (hop_list[hop_count-2]). * Если failed_peer не найден в конце — fallback: node_id сам себе next_hop. */ -static uint64_t find_next_hop(const struct TOPO_NODEQ* nq, uint64_t failed_peer) { +static uint64_t find_next_hop(const struct TOPO_GROUP_NODE* nq, uint64_t failed_peer) { if (nq->hop_count >= 2 && nq->hop_list && nq->hop_list[nq->hop_count - 1] == failed_peer) return nq->hop_list[nq->hop_count - 2]; - return nq->hash_node_id; + return nq->node_id; } /* Ищет незапущенный (started==0) контекст по next_hop_id */ @@ -117,7 +119,10 @@ static void topo_recovery_timeout_cb(void* arg) { ctx->connect_timer = NULL; uint64_t node_id = ctx->current_node_id; DEBUG_INFO(DEBUG_CATEGORY_BGP, "recovery: next=%016llx timeout connecting to %016llx", (unsigned long long)ctx->next_hop_id, (unsigned long long)node_id); - conn_mgr_cancel_callback(ctx->group->conn_mgr, node_id, topo_recovery_callback, ctx); + if (ctx->current_handle) { + conn_mgr_cancel_callback(ctx->group->conn_mgr, node_id, ctx->current_handle); + ctx->current_handle = NULL; + } int list = ctx->current_list; for (size_t i = 0; i < ctx->count[list]; i++) if (ctx->nodes[list][i].node_id == node_id) { ctx->nodes[list][i] = ctx->nodes[list][ctx->count[list] - 1]; ctx->count[list]--; break; } @@ -156,9 +161,9 @@ static void topo_recovery_try_next(struct TOPO_RECOVERY_CTX* ctx) { (unsigned long long)ctx->next_hop_id, (unsigned long long)node_id, best->min_rtt, ctx->current_list == 0 ? "direct" : "indirect", best->is_next_hop ? "[next_hop]" : ""); ctx->current_node_id = node_id; - int rc = conn_mgr_connect_node(ctx->group->conn_mgr, node_id, 0, topo_recovery_callback, ctx); - if (rc != CONN_MGR_OK) { - DEBUG_WARN(DEBUG_CATEGORY_BGP, "recovery: conn_mgr_connect_node %016llx returned %d", (unsigned long long)node_id, rc); + ctx->current_handle = conn_mgr_connect_node(ctx->group->conn_mgr, node_id, 0, topo_recovery_callback, ctx); + if (!ctx->current_handle) { + DEBUG_WARN(DEBUG_CATEGORY_BGP, "recovery: conn_mgr_connect_node %016llx failed", (unsigned long long)node_id); *best = ctx->nodes[ctx->current_list][ctx->count[ctx->current_list] - 1]; ctx->count[ctx->current_list]--; ctx->current_node_id = 0; continue; @@ -174,9 +179,11 @@ static void topo_recovery_try_next(struct TOPO_RECOVERY_CTX* ctx) { } } -void topo_recovery_add_node(struct TOPO_GROUP* group, struct TOPO_NODEQ* nq, uint64_t failed_peer) { - if (!group || !nq || !nq->node) return; - uint64_t node_id = nq->hash_node_id; +void topo_recovery_add_node(struct TOPO_GROUP* group, struct TOPO_GROUP_NODE* nq, uint64_t failed_peer) { + if (!group || !nq) return; + struct TOPO_NODE* ni = topo_node_registry_find(group->instance->topo_groups, nq->node_id); + if (!ni) return; + uint64_t node_id = nq->node_id; uint64_t next_hop = find_next_hop(nq, failed_peer); struct TOPO_RECOVERY_CTX* ctx = topo_recovery_find_by_next_hop(group, next_hop); @@ -187,7 +194,7 @@ void topo_recovery_add_node(struct TOPO_GROUP* group, struct TOPO_NODEQ* nq, uin group->recovery_list = ctx; } - int list = node_has_direct_ip(nq) ? 0 : 1; + int list = node_has_direct_ip(group, nq) ? 0 : 1; uint16_t rtt = node_get_min_rtt(nq); if (ctx->count[list] >= ctx->capacity[list]) { @@ -244,8 +251,8 @@ void topo_recovery_cancel_for_node(struct TOPO_GROUP* group, uint64_t node_id) { if (found) { DEBUG_INFO(DEBUG_CATEGORY_BGP, "recovery: cancel next=%016llx (node %016llx came up)", (unsigned long long)ctx->next_hop_id, (unsigned long long)node_id); - if (ctx->current_node_id != 0) - conn_mgr_cancel_callback(group->conn_mgr, ctx->current_node_id, topo_recovery_callback, ctx); + if (ctx->current_node_id != 0 && ctx->current_handle) + conn_mgr_cancel_callback(group->conn_mgr, ctx->current_node_id, ctx->current_handle); if (ctx->connect_timer) { uasync_cancel_timeout(group->instance->ua, ctx->connect_timer); ctx->connect_timer = NULL; } *pp = ctx->next; topo_recovery_ctx_free(ctx); @@ -260,8 +267,8 @@ void topo_recovery_cancel_all(struct TOPO_GROUP* group) { struct TOPO_RECOVERY_CTX* ctx = group->recovery_list; while (ctx) { struct TOPO_RECOVERY_CTX* next = ctx->next; - if (ctx->current_node_id != 0) - conn_mgr_cancel_callback(group->conn_mgr, ctx->current_node_id, topo_recovery_callback, ctx); + if (ctx->current_node_id != 0 && ctx->current_handle) + conn_mgr_cancel_callback(group->conn_mgr, ctx->current_node_id, ctx->current_handle); if (ctx->connect_timer) { uasync_cancel_timeout(group->instance->ua, ctx->connect_timer); ctx->connect_timer = NULL; } topo_recovery_ctx_free(ctx); ctx = next; diff --git a/src/routing_layer/topo_recovery.h b/src/routing_layer/topo_recovery.h index d387982d..c3baee5a 100644 --- a/src/routing_layer/topo_recovery.h +++ b/src/routing_layer/topo_recovery.h @@ -29,7 +29,7 @@ extern "C" { #include struct TOPO_GROUP; -struct TOPO_NODEQ; +struct TOPO_GROUP_NODE; #define TOPO_RECOVERY_CONNECT_TIMEOUT_MS 2000 @@ -49,6 +49,7 @@ struct TOPO_RECOVERY_CTX { size_t capacity[2]; /* выделенная ёмкость */ int current_list; /* 0=direct, 1=indirect */ uint64_t current_node_id; /* node_id в текущей попытке, 0=нет активной */ + struct CONN_MGR_HANDLE* current_handle; /* handle текущей попытки conn_mgr_connect_node */ void* connect_timer; /* внешний таймер 2с (uasync) */ uint8_t started; /* 0=сбор узлов (add_node), 1=перебор запущен */ }; @@ -58,7 +59,7 @@ struct TOPO_RECOVERY_CTX { * Вызывается из topo_group_remove_conn ДО topo_node_free_lists (нужен hop_list). * @param failed_peer node_id отвалившегося пира (conn->peer_node_id) */ -void topo_recovery_add_node(struct TOPO_GROUP* group, struct TOPO_NODEQ* nq, uint64_t failed_peer); +void topo_recovery_add_node(struct TOPO_GROUP* group, struct TOPO_GROUP_NODE* nq, uint64_t failed_peer); /** * Запускает перебор узлов для всех pending-контекстов. diff --git a/src/transport_layer/etcp.c b/src/transport_layer/etcp.c index 45825673..2ec19be9 100644 --- a/src/transport_layer/etcp.c +++ b/src/transport_layer/etcp.c @@ -276,8 +276,8 @@ struct ETCP_CONN* etcp_connection_create(struct UTUN_INSTANCE* instance, char* n // Вызываем callback для нового соединения если установлен if (instance) { - struct etcp_cbk_entry* cbe = instance->new_conn_cbks; - while (cbe) { struct etcp_cbk_entry* n = cbe->next; cbe->fn(etcp, cbe->arg); cbe = n; } + struct etcp_inst_cbk_entry* cbe = instance->new_conn_cbks; + while (cbe) { struct etcp_inst_cbk_entry* n = cbe->next; cbe->fn(etcp, cbe->arg); cbe = n; } } etcp_fire_conn_status(etcp, ETCP_CONN_STATUS_NEW); @@ -294,6 +294,17 @@ static void etcp_fire_conn_status(struct ETCP_CONN* etcp, int status) { while (cbe) { struct etcp_status_cbk_entry* n = cbe->next; cbe->fn(etcp, status, cbe->arg); cbe = n; } } +void etcp_cbk_fire(struct ETCP_CONN* conn, int event) { + if (!conn) return; + static const char* names[] = { "INIT", "REINIT", "UP", "DOWN", "NODE_CHANGED" }; + int idx = 0, e = event; while (e >>= 1) idx++; + DEBUG_INFO(DEBUG_CATEGORY_ETCP, "[%s] callback event: %s", conn->log_name, (idx >= 0 && idx < (int)(sizeof(names)/sizeof(names[0]))) ? names[idx] : "?"); + conn->callbacks_running = 1; + struct etcp_cbk_entry* cbe = conn->cbks; + while (cbe) { struct etcp_cbk_entry* n = cbe->next; if (cbe->event_mask & event) cbe->fn(conn, event, cbe->arg); cbe = n; } + conn->callbacks_running = 0; +} + static void etcp_on_up(struct ETCP_CONN* etcp) { DEBUG_INFO(DEBUG_CATEGORY_ETCP, "[%s] Connection UP (links_up=%d initialized=%d)", etcp->log_name, etcp->links_up, etcp->initialized); DEBUG_DEBUG(DEBUG_CATEGORY_DEBUG, "CRYPTO_ON_UP_BEFORE: log=%s seskey=%02x%02x%02x%02x peer_pub=%02x%02x%02x%02x", @@ -302,11 +313,8 @@ static void etcp_on_up(struct ETCP_CONN* etcp) { etcp->crypto_ctx.session_key[2], etcp->crypto_ctx.session_key[3], etcp->crypto_ctx.peer_public_key[0], etcp->crypto_ctx.peer_public_key[1], etcp->crypto_ctx.peer_public_key[2], etcp->crypto_ctx.peer_public_key[3]); - etcp->callbacks_running = 1; - struct etcp_cbk_entry* cbe = etcp->up_cbks; - while (cbe) { struct etcp_cbk_entry* n = cbe->next; cbe->fn(etcp, cbe->arg); cbe = n; } + etcp_cbk_fire(etcp, ETCP_CBK_EVENT_UP); etcp_fire_conn_status(etcp, ETCP_CONN_STATUS_UP); - etcp->callbacks_running = 0; DEBUG_DEBUG(DEBUG_CATEGORY_DEBUG, "CRYPTO_ON_UP_AFTER: log=%s seskey=%02x%02x%02x%02x peer_pub=%02x%02x%02x%02x", etcp->log_name, etcp->crypto_ctx.session_key[0], etcp->crypto_ctx.session_key[1], @@ -317,11 +325,8 @@ static void etcp_on_up(struct ETCP_CONN* etcp) { static void etcp_on_down(struct ETCP_CONN* etcp) { DEBUG_INFO(DEBUG_CATEGORY_ETCP, "[%s] Connection DOWN (links_up=%d)", etcp->log_name, etcp->links_up); - etcp->callbacks_running = 1; - struct etcp_cbk_entry* cbe = etcp->down_cbks; - while (cbe) { struct etcp_cbk_entry* n = cbe->next; cbe->fn(etcp, cbe->arg); cbe = n; } + etcp_cbk_fire(etcp, ETCP_CBK_EVENT_DOWN); etcp_fire_conn_status(etcp, ETCP_CONN_STATUS_DOWN); - etcp->callbacks_running = 0; } @@ -345,11 +350,8 @@ static void etcp_connection_free_resources(struct ETCP_CONN* etcp) { drain_and_free_queue(&etcp->recv_q); drain_and_free_queue(&etcp->ack_q); - // Free callback chains - { struct etcp_cbk_entry* cbe = etcp->init_cbks; while (cbe) { struct etcp_cbk_entry* n = cbe->next; u_free(cbe); cbe = n; } etcp->init_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->node_changed_cbks; while (cbe) { struct etcp_cbk_entry* n = cbe->next; u_free(cbe); cbe = n; } etcp->node_changed_cbks = NULL; } + // Free callback chain + { struct etcp_cbk_entry* cbe = etcp->cbks; while (cbe) { struct etcp_cbk_entry* n = cbe->next; u_free(cbe); cbe = n; } etcp->cbks = NULL; } if (etcp->inflight_pool) { memory_pool_destroy(etcp->inflight_pool); etcp->inflight_pool = NULL; } if (etcp->io_pool) { memory_pool_destroy(etcp->io_pool); etcp->io_pool = NULL; } @@ -574,6 +576,7 @@ void etcp_conn_reinit(struct ETCP_CONN* etcp) {// Если сбой в обме etcp->log_name, etcp->initialized, etcp->links_up, etcp->reinit_count, etcp->tx_state); etcp->reinit_count++; + etcp->reinit_pending = 1; etcp->reset_done = 0; etcp->initialized = 0;// еще раз придёт conn_ready_callback @@ -652,10 +655,9 @@ void etcp_conn_queue_set_ready(struct ETCP_CONN* conn) { etcp_metrics_start_timer(conn); - conn->callbacks_running = 1; - { struct etcp_cbk_entry* cbe = conn->init_cbks; while (cbe) { struct etcp_cbk_entry* n = cbe->next; cbe->fn(conn, cbe->arg); cbe = n; } } + etcp_cbk_fire(conn, ETCP_CBK_EVENT_INIT); + if (conn->reinit_pending) { conn->reinit_pending = 0; etcp_cbk_fire(conn, ETCP_CBK_EVENT_REINIT); } if (conn->links_up) etcp_on_up(conn); - conn->callbacks_running = 0; } diff --git a/src/transport_layer/etcp.h b/src/transport_layer/etcp.h index c46efb21..4e656ff5 100644 --- a/src/transport_layer/etcp.h +++ b/src/transport_layer/etcp.h @@ -20,12 +20,7 @@ struct stcp_link; // forward declaration // Forward declarations struct UTUN_INSTANCE; struct ETCP_CONN; -typedef void (*etcp_on_conn_ready)(struct ETCP_CONN* conn, void* arg); -struct etcp_cbk_entry { - etcp_on_conn_ready fn; - void* arg; - struct etcp_cbk_entry* next; -}; +struct etcp_cbk_entry; // defined in etcp_api.h struct UASYNC; uint16_t get_current_timestamp(void); @@ -236,13 +231,15 @@ struct ETCP_CONN { uint8_t reset_done; // 0 - рукопожатие не завершено (реинит разрешён), 1 - соединение стабильно (реинит заблокирован) uint8_t callbacks_running; // 1 - внутри итерации колбэк-цепочек, etcp_connection_close запрещён - // Callback chains for ready/up/down notifications - struct etcp_cbk_entry* init_cbks; // цепочка callback'ов при инициализации соединения - struct etcp_cbk_entry* up_cbks; // цепочка callback'ов при поднятии канала - struct etcp_cbk_entry* down_cbks; // цепочка callback'ов при падении канала - struct etcp_cbk_entry* node_changed_cbks; // цепочка callback'ов при смене pubkey узла (peer_id совпал, ключ изменился) + // Unified callback chain with event mask (init/reinit/up/down/node_changed) + struct etcp_cbk_entry* cbks; + uint8_t reinit_pending; // 1 = reinit в процессе, ждём завершения void (*bgp_ready_cbk)(struct ETCP_CONN* conn); // вызывается когда BGP готов (завершён или пропущен) + uint8_t fin_wait : 1; // close-pending: ожидаем подтверждение закрытия от remote + void (*fin_wait_clear_cb)(struct ETCP_CONN* conn, void* arg); // вызывается при сбросе fin_wait + void* fin_wait_clear_arg; + uint32_t cnt_ack_hit_inf; // счетчик удлений из inflight uint32_t cnt_ack_hit_sndq; // счетчик удалений inflight пакетов из sndq uint32_t cnt_ack_miss; // счетчик не найденных ack diff --git a/src/transport_layer/etcp_api.c b/src/transport_layer/etcp_api.c index 0fdcf9e3..f58f8bcb 100644 --- a/src/transport_layer/etcp_api.c +++ b/src/transport_layer/etcp_api.c @@ -12,11 +12,12 @@ #define DEBUG_CATEGORY_ETCP_API DEBUG_CATEGORY_ETCP -static void etcp_cbk_add_to_chain(struct etcp_cbk_entry** head, etcp_cbk_fn fn, void* arg) { +// Per-connection unified callback chain helpers +static void etcp_cbk_add_to_chain(struct etcp_cbk_entry** head, etcp_cbk_fn fn, void* arg, int event_mask) { if (!head || !fn) return; struct etcp_cbk_entry* e = u_malloc(sizeof(struct etcp_cbk_entry)); if (!e) return; - e->fn = fn; e->arg = arg; e->next = *head; + e->fn = fn; e->arg = arg; e->event_mask = event_mask; e->next = *head; *head = e; } static void etcp_cbk_remove_from_chain(struct etcp_cbk_entry** head, etcp_cbk_fn fn, void* arg) { @@ -31,17 +32,35 @@ static void etcp_cbk_remove_from_chain(struct etcp_cbk_entry** head, etcp_cbk_fn } } -void etcp_conn_add_init_cbk(struct ETCP_CONN* conn, etcp_cbk_fn fn, void* arg) { if (conn) etcp_cbk_add_to_chain(&conn->init_cbks, fn, arg); } -void etcp_conn_remove_init_cbk(struct ETCP_CONN* conn, etcp_cbk_fn fn, void* arg) { if (conn) etcp_cbk_remove_from_chain(&conn->init_cbks, fn, arg); } -void etcp_conn_add_up_cbk(struct ETCP_CONN* conn, etcp_cbk_fn fn, void* arg) { if (conn) etcp_cbk_add_to_chain(&conn->up_cbks, fn, arg); } -void etcp_conn_remove_up_cbk(struct ETCP_CONN* conn, etcp_cbk_fn fn, void* arg) { if (conn) etcp_cbk_remove_from_chain(&conn->up_cbks, fn, arg); } -void etcp_conn_add_down_cbk(struct ETCP_CONN* conn, etcp_cbk_fn fn, void* arg) { if (conn) etcp_cbk_add_to_chain(&conn->down_cbks, fn, arg); } -void etcp_conn_remove_down_cbk(struct ETCP_CONN* conn, etcp_cbk_fn fn, void* arg) { if (conn) etcp_cbk_remove_from_chain(&conn->down_cbks, fn, arg); } -void etcp_conn_node_changed(struct ETCP_CONN* conn, etcp_cbk_fn fn, void* arg) { if (conn && fn) fn(conn, arg); } -void etcp_conn_add_node_changed_cbk(struct ETCP_CONN* conn, etcp_cbk_fn fn, void* arg) { if (conn) etcp_cbk_add_to_chain(&conn->node_changed_cbks, fn, arg); } -void etcp_conn_remove_node_changed_cbk(struct ETCP_CONN* conn, etcp_cbk_fn fn, void* arg) { if (conn) etcp_cbk_remove_from_chain(&conn->node_changed_cbks, fn, arg); } -void etcp_add_new_conn_cbk(struct UTUN_INSTANCE* inst, etcp_cbk_fn fn, void* arg) { if (inst) etcp_cbk_add_to_chain(&inst->new_conn_cbks, fn, arg); } -void etcp_remove_new_conn_cbk(struct UTUN_INSTANCE* inst, etcp_cbk_fn fn, void* arg) { if (inst) etcp_cbk_remove_from_chain(&inst->new_conn_cbks, fn, arg); } +void etcp_conn_add_cbk(struct ETCP_CONN* conn, etcp_cbk_fn fn, void* arg, int event_mask) { if (conn) etcp_cbk_add_to_chain(&conn->cbks, fn, arg, event_mask); } +void etcp_conn_remove_cbk(struct ETCP_CONN* conn, etcp_cbk_fn fn, void* arg) { if (conn) etcp_cbk_remove_from_chain(&conn->cbks, fn, arg); } +void etcp_conn_update_cbk_mask(struct ETCP_CONN* conn, etcp_cbk_fn fn, void* arg, int event_mask) { + if (!conn || !fn) return; + struct etcp_cbk_entry* p = conn->cbks; + while (p) { if (p->fn == fn && p->arg == arg) { p->event_mask = event_mask; return; } p = p->next; } +} + +// Instance-level callback chain helpers +static void etcp_inst_cbk_add_chain(struct etcp_inst_cbk_entry** head, etcp_inst_cbk_fn fn, void* arg) { + if (!head || !fn) return; + struct etcp_inst_cbk_entry* e = u_malloc(sizeof(struct etcp_inst_cbk_entry)); + if (!e) return; + e->fn = fn; e->arg = arg; e->next = *head; + *head = e; +} +static void etcp_inst_cbk_remove_chain(struct etcp_inst_cbk_entry** head, etcp_inst_cbk_fn fn, void* arg) { + if (!head || !fn) return; + struct etcp_inst_cbk_entry** p = head; + while (*p) { + if ((*p)->fn == fn && (*p)->arg == arg) { + struct etcp_inst_cbk_entry* rm = *p; + *p = rm->next; u_free(rm); return; + } + p = &(*p)->next; + } +} +void etcp_add_new_conn_cbk(struct UTUN_INSTANCE* inst, etcp_inst_cbk_fn fn, void* arg) { if (inst) etcp_inst_cbk_add_chain(&inst->new_conn_cbks, fn, arg); } +void etcp_remove_new_conn_cbk(struct UTUN_INSTANCE* inst, etcp_inst_cbk_fn fn, void* arg) { if (inst) etcp_inst_cbk_remove_chain(&inst->new_conn_cbks, fn, arg); } static void etcp_status_cbk_add_chain(struct etcp_status_cbk_entry** head, etcp_conn_status_fn fn, void* arg) { if (!head || !fn) return; diff --git a/src/transport_layer/etcp_api.h b/src/transport_layer/etcp_api.h index f2e127ae..117103a6 100644 --- a/src/transport_layer/etcp_api.h +++ b/src/transport_layer/etcp_api.h @@ -44,6 +44,7 @@ extern "C" { #define ETCP_RT_ID_MEDIA_DELIVERY 0x07 // распространение медиа (аудио/видео стриминг) #define ETCP_RT_ID_CONN_MGR 0x11 // Connection Manager — management connections +#define ETCP_RT_ID_NCD_CONTROL 0x12 // node_conn_direct — CLOSE / KEEP_ALIVE control // Connection status events (instance-level callback) #define ETCP_CONN_STATUS_NEW 0 // соединение создано @@ -95,8 +96,41 @@ typedef void (*etcp_recv_fn)(struct ETCP_CONN* conn, struct ll_entry* entry); // Forward declaration struct ETCP_CONN; struct UTUN_INSTANCE; -struct TOPO_NODEQ; -typedef void (*etcp_cbk_fn)(struct ETCP_CONN* conn, void* arg); +struct TOPO_GROUP_NODE; + +// Per-connection callback event types (bitmask) +#define ETCP_CBK_EVENT_INIT (1 << 0) +#define ETCP_CBK_EVENT_REINIT (1 << 1) +#define ETCP_CBK_EVENT_UP (1 << 2) +#define ETCP_CBK_EVENT_DOWN (1 << 3) +#define ETCP_CBK_EVENT_NODE_CHANGED (1 << 4) + +typedef void (*etcp_cbk_fn)(struct ETCP_CONN* conn, int event, void* arg); +struct etcp_cbk_entry { + etcp_cbk_fn fn; + void* arg; + int event_mask; + struct etcp_cbk_entry* next; +}; + +void etcp_cbk_fire(struct ETCP_CONN* conn, int event); + +// Instance-level callback types (separate from per-connection etcp_cbk_fn in etcp.h) +typedef void (*etcp_inst_cbk_fn)(struct ETCP_CONN* conn, void* arg); +struct etcp_inst_cbk_entry { + etcp_inst_cbk_fn fn; + void* arg; + struct etcp_inst_cbk_entry* next; +}; + +// ---- Per-connection unified callback API ---- +void etcp_conn_add_cbk(struct ETCP_CONN* conn, etcp_cbk_fn fn, void* arg, int event_mask); +void etcp_conn_remove_cbk(struct ETCP_CONN* conn, etcp_cbk_fn fn, void* arg); +void etcp_conn_update_cbk_mask(struct ETCP_CONN* conn, etcp_cbk_fn fn, void* arg, int event_mask); + +// ---- Instance-level callbacks ---- +void etcp_add_new_conn_cbk(struct UTUN_INSTANCE* inst, etcp_inst_cbk_fn fn, void* arg); +void etcp_remove_new_conn_cbk(struct UTUN_INSTANCE* inst, etcp_inst_cbk_fn fn, void* arg); // ---- Background connection initialization ---- #define ETCP_CONNECT_EARLY 1 @@ -105,7 +139,7 @@ typedef void (*etcp_cbk_fn)(struct ETCP_CONN* conn, void* arg); typedef void (*etcp_connect_callback_t)(void* arg, struct ETCP_CONN* conn, int type); -int etcp_connect(struct UTUN_INSTANCE* instance, struct TOPO_NODEQ* node, +int etcp_connect(struct UTUN_INSTANCE* instance, struct TOPO_GROUP_NODE* node, etcp_connect_callback_t cb, void* arg, uint8_t flags); /** @@ -151,18 +185,6 @@ int etcp_bind(struct UTUN_INSTANCE* inst, uint8_t id, etcp_recv_fn callback); */ int etcp_unbind(struct UTUN_INSTANCE* inst, uint8_t id); -void etcp_conn_add_init_cbk(struct ETCP_CONN* conn, etcp_cbk_fn fn, void* arg); -void etcp_conn_remove_init_cbk(struct ETCP_CONN* conn, etcp_cbk_fn fn, void* arg); -void etcp_conn_add_up_cbk(struct ETCP_CONN* conn, etcp_cbk_fn fn, void* arg); -void etcp_conn_remove_up_cbk(struct ETCP_CONN* conn, etcp_cbk_fn fn, void* arg); -void etcp_conn_add_down_cbk(struct ETCP_CONN* conn, etcp_cbk_fn fn, void* arg); -void etcp_conn_remove_down_cbk(struct ETCP_CONN* conn, etcp_cbk_fn fn, void* arg); -void etcp_conn_node_changed(struct ETCP_CONN* conn, etcp_cbk_fn fn, void* arg); -void etcp_conn_add_node_changed_cbk(struct ETCP_CONN* conn, etcp_cbk_fn fn, void* arg); -void etcp_conn_remove_node_changed_cbk(struct ETCP_CONN* conn, etcp_cbk_fn fn, void* arg); -void etcp_add_new_conn_cbk(struct UTUN_INSTANCE* inst, etcp_cbk_fn fn, void* arg); -void etcp_remove_new_conn_cbk(struct UTUN_INSTANCE* inst, etcp_cbk_fn fn, void* arg); - void etcp_add_conn_status_cbk(struct UTUN_INSTANCE* inst, etcp_conn_status_fn fn, void* arg); void etcp_remove_conn_status_cbk(struct UTUN_INSTANCE* inst, etcp_conn_status_fn fn, void* arg); diff --git a/src/transport_layer/etcp_connect.c b/src/transport_layer/etcp_connect.c index df5742a0..fb9f6b73 100644 --- a/src/transport_layer/etcp_connect.c +++ b/src/transport_layer/etcp_connect.c @@ -35,7 +35,7 @@ struct ETCP_CONNECT { }; static void tcp_link_ready_cb(struct stcp_link* link, void* arg); static void tcp_link_close_cb(struct stcp_link* link, int err, void* arg); -static void connect_init_cb(struct ETCP_CONN* conn, void* arg); +static void connect_init_cb(struct ETCP_CONN* conn, int event, void* arg); static struct ETCP_CONNECT* connect_find(struct UTUN_INSTANCE* inst, uint64_t node_id) { struct ETCP_CONNECT* ctx = inst->pending_connects; @@ -69,9 +69,14 @@ static void connect_cancel(struct ETCP_CONNECT* ctx) { u_free(ctx); } -static void connect_create_links_v4(struct ETCP_CONNECT* ctx, struct TOPO_NODEQ* node) { - if (!node || !node->node) return; - for (const struct TOPO_ADDR4* a = node->node->v4_addrs; a; a = a->next) { +static void connect_create_links_v4(struct ETCP_CONNECT* ctx, struct TOPO_GROUP_NODE* node) { + if (!node) return; + struct TOPO_NODE* ni = topo_node_registry_find(ctx->instance->topo_groups, node->node_id); + if (!ni) { DEBUG_DEBUG(DEBUG_CATEGORY_DEBUG, "[etcp_connect] links_v4: ni NOT in registry for node=%016llx", (unsigned long long)node->node_id); return; } + int addr_count = 0, sock_count = 0, link_count = 0; + { struct ETCP_SOCKET* s = ctx->instance->etcp_sockets; while (s) { sock_count++; s = s->next; } } + for (const struct TOPO_ADDR4* a = ni->v4_addrs; a; a = a->next) { + addr_count++; if (a->port == 0) continue; { int zero = 1; for (int j = 0; j < 4; j++) if (a->addr[j] != 0) { zero = 0; break; } if (zero) continue; } struct sockaddr_in sin; memset(&sin, 0, sizeof(sin)); sin.sin_family = AF_INET; @@ -79,21 +84,25 @@ static void connect_create_links_v4(struct ETCP_CONNECT* ctx, struct TOPO_NODEQ* struct sockaddr_storage sa; memcpy(&sa, &sin, sizeof(sin)); if (!(a->protocol & TOPO_PROTO_TCP)) { struct ETCP_SOCKET* s = ctx->instance->etcp_sockets; - while (s) { if (s->local_addr.ss_family == AF_INET) etcp_link_new(ctx->conn, s, &sa, 0); s = s->next; } + while (s) { if (s->local_addr.ss_family == AF_INET && etcp_link_new(ctx->conn, s, &sa, 0)) link_count++; s = s->next; } } if ((a->protocol & TOPO_PROTO_TCP) && !ctx->tcp_link) { struct stcp_link_config tcp_cfg = {.ua = ctx->instance->ua, .my_keys = &ctx->instance->my_keys, .inst = ctx->instance, .peer_pubkey = ctx->conn->crypto_ctx.peer_public_key, .peer_pubkey_mode = 0, .remote_addr = &sa, .remote_port = a->port}; ctx->tcp_link = stcp_link_connect(&tcp_cfg); - if (ctx->tcp_link) { ctx->conn->transport_link = (void*)ctx->tcp_link; stcp_link_set_on_ready(ctx->tcp_link, tcp_link_ready_cb, ctx); stcp_link_set_on_close(ctx->tcp_link, tcp_link_close_cb, ctx); } + if (ctx->tcp_link) { ctx->conn->transport_link = (void*)ctx->tcp_link; stcp_link_set_on_ready(ctx->tcp_link, tcp_link_ready_cb, ctx); stcp_link_set_on_close(ctx->tcp_link, tcp_link_close_cb, ctx); link_count++; } } } + DEBUG_DEBUG(DEBUG_CATEGORY_DEBUG, "[etcp_connect] links_v4: addrs=%d socks=%d links=%d for node=%016llx", + addr_count, sock_count, link_count, (unsigned long long)node->node_id); } -static void connect_create_links_v6(struct ETCP_CONNECT* ctx, struct TOPO_NODEQ* node) { - if (!node || !node->node) return; - for (const struct TOPO_ADDR6* a = node->node->v6_addrs; a; a = a->next) { +static void connect_create_links_v6(struct ETCP_CONNECT* ctx, struct TOPO_GROUP_NODE* node) { + if (!node) return; + struct TOPO_NODE* ni = topo_node_registry_find(ctx->instance->topo_groups, node->node_id); + if (!ni) return; + for (const struct TOPO_ADDR6* a = ni->v6_addrs; a; a = a->next) { if (a->port == 0) continue; int zero = 1; for (int j = 0; j < 16; j++) if (a->addr[j] != 0) { zero = 0; break; } if (zero) continue; struct sockaddr_in6 sin6; memset(&sin6, 0, sizeof(sin6)); sin6.sin6_family = AF_INET6; @@ -122,7 +131,7 @@ static void tcp_link_ready_cb(struct stcp_link* link, void* arg) { if (!ctx || ctx->done || ctx->tcp_ready) return; ctx->tcp_ready = 1; DEBUG_INFO(DEBUG_CATEGORY_ETCP_CONNECT, "[etcp_connect] TCP link ready for node 0x%016llx", (unsigned long long)ctx->node_id); - connect_init_cb(ctx->conn, ctx); + connect_init_cb(ctx->conn, ETCP_CBK_EVENT_INIT, ctx); } static void tcp_link_close_cb(struct stcp_link* link, int err, void* arg) { @@ -135,6 +144,8 @@ static void tcp_link_close_cb(struct stcp_link* link, int err, void* arg) { static void connect_bgp_ready_cb(struct ETCP_CONN* conn) { struct ETCP_CONNECT* ctx = connect_find(conn->instance, conn->peer_node_id); + DEBUG_DEBUG(DEBUG_CATEGORY_DEBUG, "[etcp_connect] bgp_ready_cb: node=%016llx ctx=%p ctx_done=%d ra=%d", + (unsigned long long)conn->peer_node_id, (void*)ctx, ctx ? ctx->done : -1, conn->routing_exchange_active); if (!ctx || ctx->done) return; connect_deliver(ctx, ETCP_CONNECT_BGP_READY, 1); } @@ -174,12 +185,12 @@ static void connect_settle_timeout_cb(void* arg) { (unsigned long long)ctx->node_id); stcp_link_close(ctx->tcp_link); ctx->tcp_link = NULL; } - etcp_conn_remove_init_cbk(ctx->conn, connect_init_cb, ctx); + etcp_conn_remove_cbk(ctx->conn, connect_init_cb, ctx); connect_deliver(ctx, ETCP_CONNECT_LATE, 1); connect_cancel(ctx); } -static void connect_init_cb(struct ETCP_CONN* conn, void* arg) { +static void connect_init_cb(struct ETCP_CONN* conn, int event, void* arg) { (void)event; struct ETCP_CONNECT* ctx = (struct ETCP_CONNECT*)arg; if (!ctx || ctx->done) return; @@ -222,25 +233,30 @@ static void connect_init_cb(struct ETCP_CONN* conn, void* arg) { connect_settle_timeout_cb, "etcp_connect_settle"); } -int etcp_connect(struct UTUN_INSTANCE* inst, struct TOPO_NODEQ* node, +int etcp_connect(struct UTUN_INSTANCE* inst, struct TOPO_GROUP_NODE* node, etcp_connect_callback_t cb, void* arg, uint8_t flags) { if (!inst || !node || !cb) return -1; - uint64_t node_id = node->node->node_id; + struct TOPO_NODE* ni = topo_node_registry_find(inst->topo_groups, node->node_id); + if (!ni) { DEBUG_ERROR(DEBUG_CATEGORY_ETCP_CONNECT, "[etcp_connect] node not in registry"); return -1; } + DEBUG_DEBUG(DEBUG_CATEGORY_DEBUG, "[etcp_connect] registry OK node=%016llx pub=%016llx v4addrs=%p v4socks=%p", + (unsigned long long)node->node_id, *(uint64_t*)ni->public_key, (void*)ni->v4_addrs, (void*)ni->v4_sock_meta); + + uint64_t node_id = ni->node_id; if (node_id == 0) { - if (!node->node->public_key) { + if (!ni->public_key) { DEBUG_ERROR(DEBUG_CATEGORY_ETCP_CONNECT, "[etcp_connect] node_id=0 and public_key is NULL"); return -1; } int zero = 1; for (int i = 0; i < SC_PUBKEY_SIZE; i++) - if (node->node->public_key[i]) { zero = 0; break; } + if (ni->public_key[i]) { zero = 0; break; } if (zero) { DEBUG_ERROR(DEBUG_CATEGORY_ETCP_CONNECT, "[etcp_connect] node_id=0 and public_key is all zeros"); return -1; } - node_id = sc_derive_node_id_from_pubkey(node->node->public_key); - node->node->node_id = node_id; + node_id = sc_derive_node_id_from_pubkey(ni->public_key); + ni->node_id = node_id; } struct ETCP_CONN* conn = instance_find_conn(inst, node_id); @@ -281,7 +297,7 @@ int etcp_connect(struct UTUN_INSTANCE* inst, struct TOPO_NODEQ* node, struct etcp_connect_cb_node* cn = u_calloc(1, sizeof(struct etcp_connect_cb_node)); if (!cn) { u_free(ctx); return -1; } cn->cb = cb; cn->arg = arg; cn->flags = flags; ctx->cb_list = cn; - etcp_conn_add_init_cbk(conn, connect_init_cb, ctx); + etcp_conn_add_cbk(conn, connect_init_cb, ctx, ETCP_CBK_EVENT_INIT); conn->bgp_ready_cbk = connect_bgp_ready_cb; ctx->initial_timer = uasync_set_timeout(inst->ua, (int)inst->etcp_connect_timeout_tb, ctx, connect_initial_timeout_cb, "etcp_connect_init"); @@ -348,7 +364,7 @@ int etcp_connect(struct UTUN_INSTANCE* inst, struct TOPO_NODEQ* node, 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; } - if (sc_set_peer_public_key(&conn->crypto_ctx, node->node->public_key, 0) != SC_OK) { + if (sc_set_peer_public_key(&conn->crypto_ctx, ni->public_key, 0) != SC_OK) { DEBUG_ERROR(DEBUG_CATEGORY_ETCP_CONNECT, "[etcp_connect] sc_set_peer_public_key failed for node 0x%016llx", (unsigned long long)node_id); etcp_connection_close(conn); cb(arg, NULL, 0); return -1; } @@ -366,15 +382,16 @@ int etcp_connect(struct UTUN_INSTANCE* inst, struct TOPO_NODEQ* node, cn->cb = cb; cn->arg = arg; cn->flags = flags; ctx->cb_list = cn; } - etcp_conn_add_init_cbk(conn, connect_init_cb, ctx); + etcp_conn_add_cbk(conn, connect_init_cb, ctx, ETCP_CBK_EVENT_INIT); conn->bgp_ready_cbk = connect_bgp_ready_cb; connect_create_links_v4(ctx, node); connect_create_links_v6(ctx, node); if (!conn->links && !conn->transport_link) { - DEBUG_ERROR(DEBUG_CATEGORY_ETCP_CONNECT, "[etcp_connect] no links created for node 0x%016llx", - (unsigned long long)node_id); + DEBUG_ERROR(DEBUG_CATEGORY_ETCP_CONNECT, "[etcp_connect] no links created for node 0x%016llx (links=%p tcp=%p sock_count=%d)", + (unsigned long long)node_id, (void*)conn->links, (void*)ctx->tcp_link, + ({ int sc = 0; struct ETCP_SOCKET* ss = inst->etcp_sockets; while (ss) { sc++; ss = ss->next; } sc; })); if (ctx->tcp_link) { stcp_link_close(ctx->tcp_link); ctx->tcp_link = NULL; } connect_deliver(ctx, 0, 1); etcp_connection_close(conn); u_free(ctx); return -1; diff --git a/src/transport_layer/etcp_connect.h b/src/transport_layer/etcp_connect.h index cfb9c0dd..d779052b 100644 --- a/src/transport_layer/etcp_connect.h +++ b/src/transport_layer/etcp_connect.h @@ -5,7 +5,7 @@ struct UTUN_INSTANCE; struct ETCP_CONN; -struct TOPO_NODEQ; +struct TOPO_GROUP_NODE; typedef void (*etcp_connect_callback_t)(void* arg, struct ETCP_CONN* conn, int type); /** @@ -18,7 +18,7 @@ typedef void (*etcp_connect_callback_t)(void* arg, struct ETCP_CONN* conn, int t * фаз установки соединения. * * @param inst UTUN_INSTANCE - * @param node узел-адресат (TOPO_NODEQ). Из node->node берутся: + * @param node узел-адресат (TOPO_GROUP_NODE). Из node->node берутся: * - node_id — идентификатор узла (0 = вычислить из pubkey) * - public_key — X25519 pubkey (32 байта) * - v4_addrs / v6_addrs — адреса для создания линков @@ -115,7 +115,7 @@ typedef void (*etcp_connect_callback_t)(void* arg, struct ETCP_CONN* conn, int t * connect_settle_timeout_cb (settle-таймер): * ├─ Закрывает неинициализированные линки (!link->initialized) * ├─ Закрывает TCP-линк если !tcp_ready - * ├─ Убирает connect_init_cb из conn->init_cbks + * ├─ Убирает connect_init_cb из conn->cbks * └─ connect_deliver(ctx, ETCP_CONNECT_LATE) → ctx освобождается * * connect_bgp_ready_cb (вызывается при routing_exchange_active >= 3): @@ -164,7 +164,7 @@ typedef void (*etcp_connect_callback_t)(void* arg, struct ETCP_CONN* conn, int t * * ============================================================================ */ -int etcp_connect(struct UTUN_INSTANCE* instance, struct TOPO_NODEQ* node, +int etcp_connect(struct UTUN_INSTANCE* instance, struct TOPO_GROUP_NODE* node, etcp_connect_callback_t cb, void* arg, uint8_t flags); /* diff --git a/src/transport_layer/etcp_connections.c b/src/transport_layer/etcp_connections.c index cbac21a5..fcf7e4b4 100644 --- a/src/transport_layer/etcp_connections.c +++ b/src/transport_layer/etcp_connections.c @@ -35,9 +35,9 @@ static void tcp_server_on_link(struct stcp_link *link, void *arg) { if (!conn) return; conn->transport_link = link; snprintf(conn->log_name, sizeof(conn->log_name), "tcp-[%p]", (void*)link); - struct etcp_cbk_entry* cbe = inst->new_conn_cbks; - while (cbe) { struct etcp_cbk_entry* n = cbe->next; cbe->fn(conn, cbe->arg); cbe = n; } - { struct etcp_cbk_entry* rcb = conn->init_cbks; while (rcb) { struct etcp_cbk_entry* n = rcb->next; rcb->fn(conn, rcb->arg); rcb = n; } } + struct etcp_inst_cbk_entry* cbe = inst->new_conn_cbks; + while (cbe) { struct etcp_inst_cbk_entry* n = cbe->next; cbe->fn(conn, cbe->arg); cbe = n; } + etcp_cbk_fire(conn, ETCP_CBK_EVENT_INIT); DEBUG_INFO(DEBUG_CATEGORY_ETCP, "TCP server new conn=%p total=%d pending=%d", (void*)conn, queue_entry_count(inst->connections), @@ -197,6 +197,7 @@ static void etcp_link_init_timer_cbk(void* arg) { if (link->link_state == 3 && link->initialized) { DEBUG_DEBUG(DEBUG_CATEGORY_DEBUG, "[%s] init_timer: SUPPRESSED state=%d init=%d link_status=%d recv_ka=%d remote_ka=%d links_up=%d", link->etcp->log_name, link->link_state, link->initialized, link->link_status, link->recv_keepalive, link->remote_keepalive, link->etcp->links_up); return; } if (link->etcp->links_up > 0) { DEBUG_DEBUG(DEBUG_CATEGORY_DEBUG, "[%s] init_timer: SUPPRESSED (links_up=%d > 0)", link->etcp->log_name, link->etcp->links_up); return; } + if (link->etcp->fin_wait) { DEBUG_DEBUG(DEBUG_CATEGORY_CONNECTION, "[%s] init_timer: SUPPRESSED (fin_wait)", link->etcp->log_name); return; } if (link->etcp->got_initial_pkt == 0) etcp_link_send_init(link,1,0); else etcp_link_send_init(link,0,0); } @@ -1383,7 +1384,9 @@ static void send_init_response(struct ETCP_SOCKET* e_sock, struct ETCP_DGRAM* pk if (g) { topo_group_update_my_nodeinfo(g->instance, g); if (g->local_node && g->senders_list) { - g->local_node->node->ver = (g->local_node->node->ver % 255) + 1; + struct TOPO_NODE* vni = topo_node_registry_find(g->instance->topo_groups, g->local_node->node_id); + if (vni) vni->ver = (vni->ver % 255) + 1; + g->local_node->dirty = 1; g->local_node->dirty = 1; struct ll_entry* se = g->senders_list->head; while (se) { @@ -1817,13 +1820,18 @@ void etcp_connections_read_callback_socket(socket_t sock, void* arg) { *(uint64_t*)conn->crypto_ctx.peer_public_key, *(uint64_t*)sc.peer_public_key); DEBUG_ERROR(DEBUG_CATEGORY_CRYPTO, "peer key mismatch for node %016llx, firing node_changed callbacks", (unsigned long long)peer_id); conn->callbacks_running = 1; - struct etcp_cbk_entry* cbe = conn->node_changed_cbks; - while (cbe) { struct etcp_cbk_entry* n = cbe->next; cbe->fn(conn, cbe->arg); cbe = n; } + etcp_cbk_fire(conn, ETCP_CBK_EVENT_NODE_CHANGED); conn->callbacks_running = 0; goto ec_fr; }// коллизия - peer id совпал а ключи разные. } + if (conn->fin_wait) { + DEBUG_INFO(DEBUG_CATEGORY_CONNECTION, "[%s] received INIT during fin_wait, clearing", conn->log_name); + conn->fin_wait = 0; + if (conn->fin_wait_clear_cb) { conn->fin_wait_clear_cb(conn, conn->fin_wait_clear_arg); conn->fin_wait_clear_cb = NULL; conn->fin_wait_clear_arg = NULL; } + } + DEBUG_DEBUG(DEBUG_CATEGORY_DEBUG, "INIT conn=%s new_conn=%d peer=0x%016llx state=%d links_up=%d links=%p", conn->log_name, new_conn, (unsigned long long)peer_id, conn->state, conn->links_up, (void*)conn->links); // Check if link already exists (for CHANNEL_INIT recovery) @@ -1836,8 +1844,7 @@ void etcp_connections_read_callback_socket(socket_t sock, void* arg) { DEBUG_WARN(DEBUG_CATEGORY_CONNECTION, "[%s] link address match but pubkey mismatch, firing node_changed", conn->log_name); DEBUG_ERROR(DEBUG_CATEGORY_CRYPTO, "pubkey mismatch on reused link for node %016llx", (unsigned long long)peer_id); conn->callbacks_running = 1; - struct etcp_cbk_entry* cbe = conn->node_changed_cbks; - while (cbe) { struct etcp_cbk_entry* n = cbe->next; cbe->fn(conn, cbe->arg); cbe = n; } + etcp_cbk_fire(conn, ETCP_CBK_EVENT_NODE_CHANGED); conn->callbacks_running = 0; errorcode = 67; goto ec_fr; @@ -1851,8 +1858,7 @@ void etcp_connections_read_callback_socket(socket_t sock, void* arg) { DEBUG_ERROR(DEBUG_CATEGORY_CRYPTO, "link address conflict for node %016llx, firing node_changed callbacks on %s", (unsigned long long)peer_id, old_conn->log_name); old_conn->callbacks_running = 1; - struct etcp_cbk_entry* cbe = old_conn->node_changed_cbks; - while (cbe) { struct etcp_cbk_entry* n = cbe->next; cbe->fn(old_conn, cbe->arg); cbe = n; } + etcp_cbk_fire(old_conn, ETCP_CBK_EVENT_NODE_CHANGED); old_conn->callbacks_running = 0; errorcode = 67; goto ec_fr; diff --git a/src/transport_layer/node_conn_direct.c b/src/transport_layer/node_conn_direct.c new file mode 100644 index 00000000..bca1183e --- /dev/null +++ b/src/transport_layer/node_conn_direct.c @@ -0,0 +1,670 @@ +/* + * node_conn_direct.c — handle-based ETCP connection layer + * + * Тонкая прослойка над ETCP: управляет подключениями по node_id. + * Несколько handle'ов могут разделять одно ETCP_CONN. + * Conn закрывается при закрытии последнего handle (с протоколом CLOSE/KEEP_ALIVE). + * Поддерживает REVERSE-подключение через etcp_router если задан group_id. + */ + +#include "node_conn_direct.h" +#include "etcp_api.h" +#include "etcp.h" +#include "etcp_connections.h" +#include "etcp_router.h" +#include "conn_mgr.h" +#include "secure_channel.h" +#include "utun_instance.h" +#include "topo_node.h" +#include "topo_node_sqlite.h" +#include "topo_group.h" +#include "config_parser.h" +#include "../lib/debug_config.h" +#include "../lib/mem.h" +#include "../lib/u_async.h" +#include "../lib/ll_queue.h" +#include "../lib/socket_compat.h" +#include "../lib/platform_compat.h" +#include + +#define DEBUG_CATEGORY_NCD DEBUG_CATEGORY_ETCP + +/* ═══════════ локальные константы ═══════════ */ + +#define CM_V6_LINK_LOCAL 0 +#define CM_V6_LOCAL 1 +#define CM_V6_DIRECT 2 +#define CM_V6_OTHER 3 + +#define NCD_FIN_WAIT_TIMEOUT_TB 50000 /* 5 сек в 0.1ms */ + +/* ═══════════ внутренние структуры ═══════════ */ + +struct ncd_entry { + uint64_t node_id; + uint64_t group_id; /* для REVERSE (0 = без REVERSE) */ + struct ETCP_CONN* conn; + struct UASYNC* ua; + int handle_count; + + struct NODE_CONN_DIRECT* handles; /* связный список всех handle'ов */ + + void* connect_timer; /* однократный таймер первого подъёма */ + void* fin_wait_timer; /* таймер ожидания ответа на CLOSE */ + void* reverse_poll_timer; /* таймер polling входящего conn при REVERSE */ + uint8_t up : 1; /* текущий статус: 1=есть живой линк */ + uint8_t timed_out : 1; /* connect_timer уже сработал */ + + struct ncd_entry* next; /* цепочка статического реестра */ +}; + +struct NODE_CONN_DIRECT { + struct ncd_entry* entry; + ncd_callback cb; + void* cb_arg; + struct NODE_CONN_DIRECT* next; +}; + +static struct ncd_entry* g_ncd_registry; +static uint8_t g_ncd_control_bound; /* 1 = etcp_bind(ETCP_RT_ID_NCD_CONTROL) уже сделан */ + +/* ─── forward declarations ─── */ +static void ncd_event_dispatch(struct ncd_entry* entry, enum ncd_event event); +static void ncd_connect_timeout_cb(void* arg); +static void ncd_deliver_up_cb(void* arg); + +/* ═══════════ реестр ═══════════ */ + +static struct ncd_entry* ncd_registry_find(uint64_t node_id) { + struct ncd_entry* e = g_ncd_registry; + while (e) { if (e->node_id == node_id) return e; e = e->next; } + return NULL; +} +static void ncd_registry_add(struct ncd_entry* entry) { + entry->next = g_ncd_registry; g_ncd_registry = entry; +} +static void ncd_registry_remove(struct ncd_entry* entry) { + struct ncd_entry** pp = &g_ncd_registry; + while (*pp) { if (*pp == entry) { *pp = entry->next; return; } pp = &(*pp)->next; } +} + +/* ═══════════ поиск узла ═══════════ */ + +static struct TOPO_NODE* ncd_lookup_node(struct UTUN_INSTANCE* inst, uint64_t node_id) { + if (inst->topo_groups && inst->topo_groups->node_registry) { + struct ll_entry* e = queue_find_data_by_index(inst->topo_groups->node_registry, (const uint8_t*)&node_id); + if (e) { + struct TOPO_NODE* node; + memcpy(&node, e->data + 8, sizeof(node)); + if (node) { topo_node_registry_ref(inst->topo_groups, node_id); return node; } + } + } + if (inst->topo_sqlite_db && inst->topo_groups) { + struct TOPO_NODE* ni = topo_node_sqlite_node_load(inst->topo_sqlite_db, inst->topo_groups, node_id); + if (ni) return topo_node_registry_store(inst->topo_groups, ni); + } + return NULL; +} + +/* ═══════════ создание линков (round‑robin, все сокеты кроме PRIVATE) ═══════════ */ + +static int ncd_create_links(struct ncd_entry* entry, struct TOPO_NODE* ni) { + struct ETCP_CONN* conn = entry->conn; + struct UTUN_INSTANCE* inst = conn->instance; + int link_count = 0; + + /* ---- IPv4 ---- */ + { + struct ETCP_SOCKET* socks[64]; int sock_count = 0; + for (struct ETCP_SOCKET* s = inst->etcp_sockets; s; s = s->next) + if (s->local_addr.ss_family == AF_INET && s->type != CFG_SERVER_TYPE_PRIVATE) + socks[sock_count++] = s; + + if (sock_count > 0) { + int rr = 0; + for (const struct TOPO_ADDR4* a = ni->v4_addrs; a; a = a->next) { + if (a->port == 0 || !(a->protocol & TOPO_PROTO_UDP)) continue; + int zero = 1; for (int j = 0; j < 4; j++) if (a->addr[j] != 0) { zero = 0; break; } + if (zero) continue; + struct sockaddr_in sin; memset(&sin, 0, sizeof(sin)); sin.sin_family = AF_INET; + memcpy(&sin.sin_addr.s_addr, a->addr, 4); sin.sin_port = htons(a->port); + struct sockaddr_storage sa; memcpy(&sa, &sin, sizeof(sin)); + if (etcp_link_new(conn, socks[rr++ % sock_count], &sa, 0)) link_count++; + } + } + } + + /* ---- IPv6 ---- */ + { + struct ETCP_SOCKET* socks[64]; int sock_count = 0; + for (struct ETCP_SOCKET* s = inst->etcp_sockets; s; s = s->next) + if (s->local_addr.ss_family == AF_INET6 && s->type != CFG_SERVER_TYPE_PRIVATE) + socks[sock_count++] = s; + + if (sock_count > 0) { + int rr = 0; + for (const struct TOPO_ADDR6* a = ni->v6_addrs; a; a = a->next) { + if (a->port == 0 || !(a->protocol & TOPO_PROTO_UDP)) continue; + int zero = 1; for (int j = 0; j < 16; j++) if (a->addr[j] != 0) { zero = 0; break; } + if (zero) continue; + struct sockaddr_in6 sin6; memset(&sin6, 0, sizeof(sin6)); sin6.sin6_family = AF_INET6; + memcpy(&sin6.sin6_addr, a->addr, 16); sin6.sin6_port = htons(a->port); + struct sockaddr_storage sa; memcpy(&sa, &sin6, sizeof(sin6)); + if (etcp_link_new(conn, socks[rr++ % sock_count], &sa, 0)) link_count++; + } + } + } + + return link_count; +} + +/* ═══════════ REVERSE ═══════════ */ + +static uint8_t ncd_classify_v6_addr(const uint8_t addr[16]) { + if (addr[0] == 0xfe && (addr[1] & 0xc0) == 0x80) return CM_V6_LINK_LOCAL; + if (addr[0] == 0xfc || addr[0] == 0xfd) return CM_V6_LOCAL; + if (addr[0] == 0xff) return CM_V6_OTHER; + { uint8_t zero[16] = {0}; if (memcmp(addr, zero, 16) == 0) return CM_V6_OTHER; } + { uint8_t lb[16] = {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1}; if (memcmp(addr, lb, 16) == 0) return CM_V6_OTHER; } + return CM_V6_DIRECT; +} + +static int ncd_has_direct_ip(const struct TOPO_NODE* node) { + if (!node) return 0; + for (const struct TOPO_ADDR4* a = node->v4_addrs; a; a = a->next) { + if (a->type == TOPO_ADDR_NAT || a->type == TOPO_ADDR_INTERFACE) { + for (const struct TOPO_SOCKMETA4* m = node->v4_sock_meta; m; m = m->next) { + if (m->id == a->socket_id && (m->config_type == CFG_SERVER_TYPE_PUBLIC || m->config_type == CFG_SERVER_TYPE_UNKNOWN + || m->nat_type == NAT_VERIFIED_EIM || m->nat_type == NAT_VERIFIED_DIRECT)) + return 1; + } + } + } + for (const struct TOPO_ADDR6* a6 = node->v6_addrs; a6; a6 = a6->next) { + if ((a6->type != TOPO_ADDR_NAT && a6->type != TOPO_ADDR_INTERFACE) || ncd_classify_v6_addr(a6->addr) != CM_V6_DIRECT) continue; + for (const struct TOPO_SOCKMETA6* m = node->v6_sock_meta; m; m = m->next) + if (m->id == a6->socket_id && (m->config_type == CFG_SERVER_TYPE_PUBLIC || m->config_type == CFG_SERVER_TYPE_UNKNOWN + || m->nat_type == NAT_VERIFIED_EIM || m->nat_type == NAT_VERIFIED_DIRECT)) + return 1; + } + return 0; +} + +static void ncd_try_reverse(struct ncd_entry* entry, struct TOPO_NODE* local_node, struct TOPO_NODE* target_node) { + if (!local_node || !target_node) return; + int has_our = ncd_has_direct_ip(local_node); + int has_target = ncd_has_direct_ip(target_node); + if (!has_our || has_target) return; + + uint8_t direct_count = 0; + struct { uint8_t type; uint8_t ip[4]; uint16_t port; uint8_t socket_id; } out_addrs[8]; + for (const struct TOPO_ADDR4* a = local_node->v4_addrs; a && direct_count < 8; a = a->next) { + if (a->type != TOPO_ADDR_NAT && a->type != TOPO_ADDR_INTERFACE) continue; + for (const struct TOPO_SOCKMETA4* m = local_node->v4_sock_meta; m; m = m->next) { + if (m->id == a->socket_id && (m->config_type == CFG_SERVER_TYPE_PUBLIC || m->config_type == CFG_SERVER_TYPE_UNKNOWN + || m->nat_type == NAT_VERIFIED_EIM || m->nat_type == NAT_VERIFIED_DIRECT)) { + out_addrs[direct_count].type = a->type; memcpy(out_addrs[direct_count].ip, a->addr, 4); + out_addrs[direct_count].port = htons(a->port); out_addrs[direct_count].socket_id = a->socket_id; + direct_count++; break; + } + } + } + if (direct_count == 0) return; + + size_t pkt_size = CONN_MGR_DIRECT_REQ_HDR_SIZE + (size_t)direct_count * 8; + uint8_t* pkt = u_malloc(pkt_size); + if (!pkt) return; + struct CONN_MGR_DIRECT_REQ* req = (struct CONN_MGR_DIRECT_REQ*)pkt; + req->cmd = ETCP_RT_ID_CONN_MGR; + req->subcmd = CONN_MGR_SUBCMD_DIRECT_REQ; + req->request_id = 0; + req->addr_count = direct_count; + uint8_t* p = pkt + CONN_MGR_DIRECT_REQ_HDR_SIZE; + for (uint8_t i = 0; i < direct_count; i++) { + *p++ = out_addrs[i].type; + memcpy(p, out_addrs[i].ip, 4); p += 4; + memcpy(p, &out_addrs[i].port, 2); p += 2; + *p++ = out_addrs[i].socket_id; + } + struct ll_entry* qe = queue_entry_new(0); + if (!qe) { u_free(pkt); return; } + qe->dgram = pkt; qe->len = (uint16_t)pkt_size; + etcp_route_send(entry->conn->instance, entry->group_id, entry->node_id, qe, 1); + DEBUG_INFO(DEBUG_CATEGORY_NCD, "[ncd] REVERSE: sent DIRECT_REQ to 0x%016llx with %u addrs", + (unsigned long long)entry->node_id, direct_count); +} + +/* ═══════════ REVERSE polling: проверка incoming conn ═══════════ */ + +static void ncd_reverse_poll_cb(void* arg) { + struct ncd_entry* entry = (struct ncd_entry*)arg; + if (!entry || entry->up || entry->timed_out || entry->conn->fin_wait) return; + struct ETCP_CONN* incoming = instance_find_conn(entry->conn->instance, entry->node_id); + if (incoming && incoming->state == 1 && incoming->peer_node_id == entry->node_id) { + DEBUG_INFO(DEBUG_CATEGORY_NCD, "[ncd] REVERSE: incoming conn found node=0x%016llx", (unsigned long long)entry->node_id); + struct ETCP_CONN* old_conn = entry->conn; + entry->conn = incoming; + ncd_event_dispatch(entry, NCD_EVENT_UP); + if (old_conn && old_conn != incoming) etcp_connection_close(old_conn); + } else { + entry->reverse_poll_timer = uasync_set_timeout(entry->ua, 1000, entry, ncd_reverse_poll_cb, "ncd_rev_poll"); + } +} + +/* ═══════════ единая диспетчеризация событий ═══════════ */ + +static void ncd_event_dispatch(struct ncd_entry* entry, enum ncd_event event) { + if (!entry) return; + switch (event) { + case NCD_EVENT_UP: + if (entry->up) return; + if (entry->connect_timer) { uasync_cancel_timeout(entry->ua, entry->connect_timer); entry->connect_timer = NULL; } + if (entry->reverse_poll_timer) { uasync_cancel_timeout(entry->ua, entry->reverse_poll_timer); entry->reverse_poll_timer = NULL; } + entry->up = 1; + break; + case NCD_EVENT_DOWN: + if (!entry->up) return; + entry->up = 0; + break; + case NCD_EVENT_TIMEOUT: + if (entry->timed_out) return; + entry->timed_out = 1; + entry->connect_timer = NULL; + break; + } + DEBUG_INFO(DEBUG_CATEGORY_NCD, "[ncd] event=%d node=0x%016llx handles=%d", + (int)event, (unsigned long long)entry->node_id, entry->handle_count); + struct NODE_CONN_DIRECT* h = entry->handles; + while (h) { struct NODE_CONN_DIRECT* next = h->next; if (h->cb) h->cb(h, event, h->cb_arg); h = next; } +} + +/* ═══════════ ETCP коллбэки (прокидывают в ncd_event_dispatch) ═══════════ */ + +static void ncd_init_cb(struct ETCP_CONN* conn, int event, void* arg) { (void)event; + struct ncd_entry* entry = (struct ncd_entry*)arg; + if (!entry || entry->timed_out || conn->fin_wait) return; + ncd_event_dispatch(entry, NCD_EVENT_UP); +} +static void ncd_up_cb(struct ETCP_CONN* conn, int event, void* arg) { (void)event; + struct ncd_entry* entry = (struct ncd_entry*)arg; + if (!entry || conn->fin_wait) return; + ncd_event_dispatch(entry, NCD_EVENT_UP); +} +static void ncd_down_cb(struct ETCP_CONN* conn, int event, void* arg) { (void)event; + struct ncd_entry* entry = (struct ncd_entry*)arg; + if (!entry) return; + if (conn->fin_wait && entry->handle_count <= 0) { + DEBUG_INFO(DEBUG_CATEGORY_NCD, "[ncd] DOWN during fin_wait, cleaning up node=0x%016llx", (unsigned long long)entry->node_id); + conn->fin_wait = 0; conn->fin_wait_clear_cb = NULL; conn->fin_wait_clear_arg = NULL; + if (entry->fin_wait_timer) { uasync_cancel_timeout(entry->ua, entry->fin_wait_timer); entry->fin_wait_timer = NULL; } + etcp_conn_remove_cbk(conn, ncd_init_cb, entry);; + etcp_conn_remove_cbk(conn, ncd_up_cb, entry);; + etcp_conn_remove_cbk(conn, ncd_down_cb, entry);; + etcp_connection_close(conn); + ncd_registry_remove(entry); + u_free(entry); + return; + } + ncd_event_dispatch(entry, NCD_EVENT_DOWN); +} + +/* ═══════════ таймер подключения ═══════════ */ + +static void ncd_connect_timeout_cb(void* arg) { + struct ncd_entry* entry = (struct ncd_entry*)arg; + if (!entry || entry->timed_out) return; + entry->connect_timer = NULL; + DEBUG_WARN(DEBUG_CATEGORY_NCD, "[ncd] connect timeout node=0x%016llx handles=%d", + (unsigned long long)entry->node_id, entry->handle_count); + /* проверяем incoming conn (REVERSE) */ + struct ETCP_CONN* incoming = instance_find_conn(entry->conn->instance, entry->node_id); + if (incoming && incoming->state == 1 && incoming->peer_node_id == entry->node_id) { + DEBUG_INFO(DEBUG_CATEGORY_NCD, "[ncd] connect timeout but incoming found, UP"); + struct ETCP_CONN* old_conn = entry->conn; + entry->conn = incoming; + ncd_event_dispatch(entry, NCD_EVENT_UP); + if (old_conn && old_conn != incoming) etcp_connection_close(old_conn); + return; + } + ncd_event_dispatch(entry, NCD_EVENT_TIMEOUT); +} + +/* ═══════════ асинхронная доставка UP ═══════════ */ + +static void ncd_deliver_up_cb(void* arg) { + struct NODE_CONN_DIRECT* h = (struct NODE_CONN_DIRECT*)arg; + if (h->cb) h->cb(h, NCD_EVENT_UP, h->cb_arg); +} + +/* ═══════════ FIN_WAIT ═══════════ */ + +static void ncd_fin_wait_cancelled(struct ETCP_CONN* conn, void* arg) { + struct ncd_entry* entry = (struct ncd_entry*)arg; + DEBUG_INFO(DEBUG_CATEGORY_NCD, "[ncd] fin_wait cancelled node=0x%016llx", (unsigned long long)entry->node_id); + if (entry->fin_wait_timer) { uasync_cancel_timeout(entry->ua, entry->fin_wait_timer); entry->fin_wait_timer = NULL; } +} + +static void ncd_fin_wait_timeout_cb(void* arg) { + struct ncd_entry* entry = (struct ncd_entry*)arg; + if (!entry || !entry->conn) return; + entry->fin_wait_timer = NULL; + if (!entry->conn->fin_wait) return; + DEBUG_WARN(DEBUG_CATEGORY_NCD, "[ncd] fin_wait timeout, force close node=0x%016llx", (unsigned long long)entry->node_id); + entry->conn->fin_wait = 0; entry->conn->fin_wait_clear_cb = NULL; entry->conn->fin_wait_clear_arg = NULL; + etcp_conn_remove_cbk(entry->conn, ncd_init_cb, entry); + etcp_conn_remove_cbk(entry->conn, ncd_up_cb, entry); + etcp_conn_remove_cbk(entry->conn, ncd_down_cb, entry); + etcp_connection_close(entry->conn); + ncd_registry_remove(entry); + u_free(entry); +} + +/* ═══════════ приём CLOSE / KEEP_ALIVE ═══════════ */ + +static void ncd_recv_control_handler(struct ETCP_CONN* conn, struct ll_entry* e) { + if (!conn || !e || e->len < NCD_CONTROL_MSG_SIZE) { + if (e) { queue_dgram_free(e); queue_entry_free(e); } + return; + } + struct ncd_control_msg* msg = (struct ncd_control_msg*)e->dgram; + uint64_t sender_id = msg->node_id; + struct ncd_entry* entry = ncd_registry_find(sender_id); + + switch (msg->subcmd) { + case NCD_SUBCMD_CLOSE: + DEBUG_INFO(DEBUG_CATEGORY_NCD, "[ncd] recv CLOSE from 0x%016llx", (unsigned long long)sender_id); + if (!entry) { + DEBUG_INFO(DEBUG_CATEGORY_NCD, "[ncd] no entry for CLOSE sender 0x%016llx, closing conn", (unsigned long long)sender_id); + if (conn) etcp_connection_close(conn); + break; + } + if (entry->handle_count > 0) { + DEBUG_INFO(DEBUG_CATEGORY_NCD, "[ncd] refusing CLOSE, still have %d handles for 0x%016llx", + entry->handle_count, (unsigned long long)sender_id); + { struct ncd_control_msg resp; resp.cmd = ETCP_RT_ID_NCD_CONTROL; resp.subcmd = NCD_SUBCMD_KEEP_ALIVE; + resp.node_id = conn->instance->node_id; + struct ll_entry* qe = queue_entry_new(0); + if (qe) { qe->dgram = u_malloc(sizeof(resp)); memcpy(qe->dgram, &resp, sizeof(resp)); qe->len = sizeof(resp); + etcp_send(conn, qe); } + } + } else { + DEBUG_INFO(DEBUG_CATEGORY_NCD, "[ncd] closing conn for CLOSE from 0x%016llx", (unsigned long long)sender_id); + if (conn) etcp_connection_close(conn); + } + break; + case NCD_SUBCMD_KEEP_ALIVE: + DEBUG_INFO(DEBUG_CATEGORY_NCD, "[ncd] recv KEEP_ALIVE from 0x%016llx", (unsigned long long)sender_id); + if (entry && entry->conn->fin_wait) { + entry->conn->fin_wait = 0; entry->conn->fin_wait_clear_cb = NULL; entry->conn->fin_wait_clear_arg = NULL; + if (entry->fin_wait_timer) { uasync_cancel_timeout(entry->ua, entry->fin_wait_timer); entry->fin_wait_timer = NULL; } + DEBUG_INFO(DEBUG_CATEGORY_NCD, "[ncd] KEEP_ALIVE: fin_wait cleared, conn stays alive"); + } + break; + } + queue_dgram_free(e); queue_entry_free(e); +} + +/* ═══════════ инициализация глобального обработчика ═══════════ */ + +static void ncd_init_control_binding(struct UTUN_INSTANCE* inst) { + if (g_ncd_control_bound) return; + etcp_bind(inst, ETCP_RT_ID_NCD_CONTROL, ncd_recv_control_handler); + g_ncd_control_bound = 1; + DEBUG_INFO(DEBUG_CATEGORY_NCD, "[ncd] bound ETCP_RT_ID_NCD_CONTROL handler"); +} + +/* ═══════════ API ═══════════ */ + +int node_conn_direct_open(struct UTUN_INSTANCE* inst, uint64_t node_id, uint64_t group_id, + ncd_callback cb, void* cb_arg, + struct NODE_CONN_DIRECT** out_handle) { + if (!inst || !out_handle) return NCD_ERR; + *out_handle = NULL; + ncd_init_control_binding(inst); + + /* 1. Ищем в реестре — conn уже есть */ + struct ncd_entry* entry = ncd_registry_find(node_id); + if (entry) { + /* снять fin_wait если был */ + if (entry->conn && entry->conn->fin_wait) { + DEBUG_INFO(DEBUG_CATEGORY_NCD, "[ncd] open REUSED clearing fin_wait node=0x%016llx", (unsigned long long)node_id); + entry->conn->fin_wait = 0; entry->conn->fin_wait_clear_cb = NULL; entry->conn->fin_wait_clear_arg = NULL; + if (entry->fin_wait_timer) { uasync_cancel_timeout(entry->ua, entry->fin_wait_timer); entry->fin_wait_timer = NULL; } + } + struct NODE_CONN_DIRECT* h = u_calloc(1, sizeof(*h)); + if (!h) { DEBUG_ERROR(DEBUG_CATEGORY_NCD, "[ncd] alloc handle failed node=0x%016llx", (unsigned long long)node_id); return NCD_ERR; } + h->entry = entry; h->cb = cb; h->cb_arg = cb_arg; + h->next = entry->handles; entry->handles = h; + entry->handle_count++; + *out_handle = h; + if (entry->conn && entry->conn->state == 1 && entry->up) { + uasync_call_soon(inst->ua, h, ncd_deliver_up_cb); + DEBUG_INFO(DEBUG_CATEGORY_NCD, "[ncd] open REUSED (ready) node=0x%016llx handles=%d", (unsigned long long)node_id, entry->handle_count); + } else { + DEBUG_INFO(DEBUG_CATEGORY_NCD, "[ncd] open REUSED (pending) node=0x%016llx handles=%d", (unsigned long long)node_id, entry->handle_count); + } + return NCD_REUSED; + } + + /* 2. Ищем conn через instance_find_conn (входящее / созданное etcp_connect) */ + struct ETCP_CONN* conn = instance_find_conn(inst, node_id); + if (conn) { + /* снять fin_wait если был */ + if (conn->fin_wait) { + DEBUG_INFO(DEBUG_CATEGORY_NCD, "[ncd] open REUSED clearing fin_wait (existing conn) node=0x%016llx", (unsigned long long)node_id); + conn->fin_wait = 0; conn->fin_wait_clear_cb = NULL; conn->fin_wait_clear_arg = NULL; + } + entry = u_calloc(1, sizeof(*entry)); + if (!entry) { DEBUG_ERROR(DEBUG_CATEGORY_NCD, "[ncd] alloc entry failed node=0x%016llx", (unsigned long long)node_id); return NCD_ERR; } + entry->node_id = node_id; entry->group_id = group_id; entry->conn = conn; entry->ua = inst->ua; + entry->up = (uint8_t)(conn->links_up ? 1 : 0); + ncd_registry_add(entry); + + struct NODE_CONN_DIRECT* h = u_calloc(1, sizeof(*h)); + if (!h) { DEBUG_ERROR(DEBUG_CATEGORY_NCD, "[ncd] alloc handle failed node=0x%016llx", (unsigned long long)node_id); + ncd_registry_remove(entry); u_free(entry); return NCD_ERR; } + h->entry = entry; h->cb = cb; h->cb_arg = cb_arg; + h->next = entry->handles; entry->handles = h; + entry->handle_count = 1; + *out_handle = h; + + etcp_conn_add_cbk(conn, ncd_init_cb, entry, ETCP_CBK_EVENT_INIT);; + etcp_conn_add_cbk(conn, ncd_up_cb, entry, ETCP_CBK_EVENT_UP);; + etcp_conn_add_cbk(conn, ncd_down_cb, entry, ETCP_CBK_EVENT_DOWN);; + + if (conn->state == 1 && entry->up) { + uasync_call_soon(inst->ua, h, ncd_deliver_up_cb); + DEBUG_INFO(DEBUG_CATEGORY_NCD, "[ncd] open REUSED new-entry (ready) node=0x%016llx conn=%p", (unsigned long long)node_id, (void*)conn); + } else { + entry->connect_timer = uasync_set_timeout(inst->ua, (int)inst->etcp_connect_timeout_tb, entry, ncd_connect_timeout_cb, "ncd_connect"); + DEBUG_INFO(DEBUG_CATEGORY_NCD, "[ncd] open REUSED new-entry (pending) node=0x%016llx conn=%p", (unsigned long long)node_id, (void*)conn); + } + return NCD_REUSED; + } + + /* 3. Новое подключение */ + struct TOPO_NODE* ni = ncd_lookup_node(inst, node_id); + if (!ni) { + DEBUG_ERROR(DEBUG_CATEGORY_NCD, "[ncd] node not found id=0x%016llx", (unsigned long long)node_id); + return NCD_ERR; + } + + conn = etcp_connection_create(inst, NULL); + if (!conn) { + DEBUG_ERROR(DEBUG_CATEGORY_NCD, "[ncd] etcp_connection_create failed node=0x%016llx", (unsigned long long)node_id); + topo_node_registry_unref(inst->topo_groups, ni->node_id); + return NCD_ERR; + } + conn->peer_node_id = node_id; + etcp_update_log_name(conn); + + if (sc_init_ctx(&conn->crypto_ctx, &inst->my_keys) != SC_OK) { + DEBUG_ERROR(DEBUG_CATEGORY_NCD, "[ncd] sc_init_ctx failed node=0x%016llx", (unsigned long long)node_id); + etcp_connection_close(conn); topo_node_registry_unref(inst->topo_groups, ni->node_id); return NCD_ERR; + } + if (sc_set_peer_public_key(&conn->crypto_ctx, ni->public_key, 0) != SC_OK) { + DEBUG_ERROR(DEBUG_CATEGORY_NCD, "[ncd] sc_set_peer_public_key failed node=0x%016llx", (unsigned long long)node_id); + etcp_connection_close(conn); topo_node_registry_unref(inst->topo_groups, ni->node_id); return NCD_ERR; + } + + /* Подключаем conn в inst->connections с индексом по 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) { struct conn_queue_entry* ce = (struct conn_queue_entry*)qe->data; + ce->peer_node_id = node_id; ce->conn = conn; + conn->conn_queue_entry = qe; conn->conn_queue = inst->connections; + queue_data_put_with_index(inst->connections, qe); } + } + + entry = u_calloc(1, sizeof(*entry)); + if (!entry) { + DEBUG_ERROR(DEBUG_CATEGORY_NCD, "[ncd] alloc entry failed node=0x%016llx", (unsigned long long)node_id); + etcp_connection_close(conn); topo_node_registry_unref(inst->topo_groups, ni->node_id); return NCD_ERR; + } + entry->node_id = node_id; entry->group_id = group_id; entry->conn = conn; entry->ua = inst->ua; entry->up = 0; + ncd_registry_add(entry); + + struct NODE_CONN_DIRECT* h = u_calloc(1, sizeof(*h)); + if (!h) { DEBUG_ERROR(DEBUG_CATEGORY_NCD, "[ncd] alloc handle failed node=0x%016llx", (unsigned long long)node_id); + ncd_registry_remove(entry); u_free(entry); etcp_connection_close(conn); topo_node_registry_unref(inst->topo_groups, ni->node_id); return NCD_ERR; } + h->entry = entry; h->cb = cb; h->cb_arg = cb_arg; + h->next = entry->handles; entry->handles = h; + entry->handle_count = 1; + *out_handle = h; + + etcp_conn_add_cbk(conn, ncd_init_cb, entry, ETCP_CBK_EVENT_INIT);; + etcp_conn_add_cbk(conn, ncd_up_cb, entry, ETCP_CBK_EVENT_UP);; + etcp_conn_add_cbk(conn, ncd_down_cb, entry, ETCP_CBK_EVENT_DOWN);; + + int link_count = ncd_create_links(entry, ni); + + if (link_count == 0 && group_id != 0) { + struct TOPO_GROUP* grp = topo_groups_find(inst->topo_groups, group_id); + struct TOPO_GROUP_NODE* local_nq = grp ? grp->local_node : NULL; + struct TOPO_NODE* local_node = local_nq ? topo_node_registry_find(inst->topo_groups, local_nq->node_id) : NULL; + DEBUG_INFO(DEBUG_CATEGORY_NCD, "[ncd] no links, trying REVERSE node=0x%016llx group=0x%016llx", + (unsigned long long)node_id, (unsigned long long)group_id); + ncd_try_reverse(entry, local_node, ni); + entry->reverse_poll_timer = uasync_set_timeout(inst->ua, 1000, entry, ncd_reverse_poll_cb, "ncd_rev_poll"); + } + if (link_count == 0) { + DEBUG_WARN(DEBUG_CATEGORY_NCD, "[ncd] no links created for node=0x%016llx (will wait for incoming)", (unsigned long long)node_id); + } + + entry->connect_timer = uasync_set_timeout(inst->ua, (int)inst->etcp_connect_timeout_tb, entry, ncd_connect_timeout_cb, "ncd_connect"); + DEBUG_INFO(DEBUG_CATEGORY_NCD, "[ncd] open NEW node=0x%016llx conn=%p links=%d handles=%d", + (unsigned long long)node_id, (void*)conn, link_count, entry->handle_count); + topo_node_registry_unref(inst->topo_groups, ni->node_id); + return NCD_NEW; +} + +void node_conn_direct_close(struct NODE_CONN_DIRECT* h) { + if (!h) return; + struct ncd_entry* entry = h->entry; + if (!entry) { u_free(h); return; } + + uint64_t node_id = entry->node_id; + struct ETCP_CONN* conn = entry->conn; + + /* Удаляем handle из списка */ + struct NODE_CONN_DIRECT** pp = &entry->handles; + while (*pp) { if (*pp == h) { *pp = h->next; break; } pp = &(*pp)->next; } + entry->handle_count--; + + DEBUG_INFO(DEBUG_CATEGORY_NCD, "[ncd] close node=0x%016llx remaining=%d", (unsigned long long)node_id, entry->handle_count); + + if (entry->handle_count <= 0) { + if (entry->connect_timer) { uasync_cancel_timeout(entry->ua, entry->connect_timer); entry->connect_timer = NULL; } + if (entry->reverse_poll_timer) { uasync_cancel_timeout(entry->ua, entry->reverse_poll_timer); entry->reverse_poll_timer = NULL; } + if (conn && conn->state != 2) { + /* устанавливаем fin_wait, отправляем CLOSE, ставим таймер */ + conn->fin_wait = 1; + conn->fin_wait_clear_cb = ncd_fin_wait_cancelled; + conn->fin_wait_clear_arg = entry; + { struct ncd_control_msg msg; msg.cmd = ETCP_RT_ID_NCD_CONTROL; msg.subcmd = NCD_SUBCMD_CLOSE; + msg.node_id = conn->instance->node_id; + struct ll_entry* qe = queue_entry_new(0); + if (qe) { qe->dgram = u_malloc(sizeof(msg)); memcpy(qe->dgram, &msg, sizeof(msg)); qe->len = sizeof(msg); + etcp_send(conn, qe); + DEBUG_INFO(DEBUG_CATEGORY_NCD, "[ncd] sent CLOSE to 0x%016llx", (unsigned long long)node_id); } + } + entry->fin_wait_timer = uasync_set_timeout(entry->ua, NCD_FIN_WAIT_TIMEOUT_TB, entry, ncd_fin_wait_timeout_cb, "ncd_fin_wait"); + DEBUG_INFO(DEBUG_CATEGORY_NCD, "[ncd] fin_wait started node=0x%016llx", (unsigned long long)node_id); + } else { + etcp_conn_remove_cbk(conn, ncd_init_cb, entry);; + etcp_conn_remove_cbk(conn, ncd_up_cb, entry);; + etcp_conn_remove_cbk(conn, ncd_down_cb, entry);; + if (conn) etcp_connection_close(conn); + ncd_registry_remove(entry); + u_free(entry); + } + } + + h->entry = NULL; + u_free(h); +} + +int node_conn_direct_adopt(struct UTUN_INSTANCE* inst, struct ETCP_CONN* conn, + ncd_callback cb, void* cb_arg, + struct NODE_CONN_DIRECT** out_handle) { + if (!inst || !conn || !out_handle) return NCD_ERR; + *out_handle = NULL; + ncd_init_control_binding(inst); + + uint64_t node_id = conn->peer_node_id; + + /* снять fin_wait если был */ + if (conn->fin_wait) { + DEBUG_INFO(DEBUG_CATEGORY_NCD, "[ncd] adopt clearing fin_wait node=0x%016llx", (unsigned long long)node_id); + conn->fin_wait = 0; conn->fin_wait_clear_cb = NULL; conn->fin_wait_clear_arg = NULL; + } + + /* проверяем реестр */ + struct ncd_entry* entry = ncd_registry_find(node_id); + if (entry) { + /* entry уже есть — просто добавляем handle */ + if (entry->fin_wait_timer) { uasync_cancel_timeout(entry->ua, entry->fin_wait_timer); entry->fin_wait_timer = NULL; } + if (entry->conn != conn) { + if (entry->conn && entry->conn->state != 2) etcp_connection_close(entry->conn); + entry->conn = conn; + } + struct NODE_CONN_DIRECT* h = u_calloc(1, sizeof(*h)); + if (!h) return NCD_ERR; + h->entry = entry; h->cb = cb; h->cb_arg = cb_arg; + h->next = entry->handles; entry->handles = h; + entry->handle_count++; + *out_handle = h; + if (conn->state == 1 && entry->up) uasync_call_soon(inst->ua, h, ncd_deliver_up_cb); + DEBUG_INFO(DEBUG_CATEGORY_NCD, "[ncd] adopt REUSED node=0x%016llx handles=%d", (unsigned long long)node_id, entry->handle_count); + return NCD_REUSED; + } + + entry = u_calloc(1, sizeof(*entry)); + if (!entry) { DEBUG_ERROR(DEBUG_CATEGORY_NCD, "[ncd] adopt alloc entry failed node=0x%016llx", (unsigned long long)node_id); return NCD_ERR; } + entry->node_id = node_id; entry->conn = conn; entry->ua = inst->ua; + entry->up = (uint8_t)(conn->links_up ? 1 : 0); + ncd_registry_add(entry); + + struct NODE_CONN_DIRECT* h = u_calloc(1, sizeof(*h)); + if (!h) { ncd_registry_remove(entry); u_free(entry); return NCD_ERR; } + h->entry = entry; h->cb = cb; h->cb_arg = cb_arg; + h->next = entry->handles; entry->handles = h; + entry->handle_count = 1; + *out_handle = h; + + etcp_conn_add_cbk(conn, ncd_init_cb, entry, ETCP_CBK_EVENT_INIT);; + etcp_conn_add_cbk(conn, ncd_up_cb, entry, ETCP_CBK_EVENT_UP);; + etcp_conn_add_cbk(conn, ncd_down_cb, entry, ETCP_CBK_EVENT_DOWN);; + + if (conn->state == 1 && entry->up) uasync_call_soon(inst->ua, h, ncd_deliver_up_cb); + DEBUG_INFO(DEBUG_CATEGORY_NCD, "[ncd] adopt NEW node=0x%016llx conn=%p handles=%d", (unsigned long long)node_id, (void*)conn, entry->handle_count); + return NCD_REUSED; +} + +struct ETCP_CONN* node_conn_direct_get_conn(struct NODE_CONN_DIRECT* h) { + if (!h || !h->entry) return NULL; + return h->entry->conn; +} diff --git a/src/transport_layer/node_conn_direct.h b/src/transport_layer/node_conn_direct.h new file mode 100644 index 00000000..90129503 --- /dev/null +++ b/src/transport_layer/node_conn_direct.h @@ -0,0 +1,67 @@ +#ifndef NODE_CONN_DIRECT_H +#define NODE_CONN_DIRECT_H + +#include + +struct UTUN_INSTANCE; +struct ETCP_CONN; + +/* ─── Return codes ─── */ +#define NCD_NEW 0 +#define NCD_REUSED 1 +#define NCD_ERR -1 + +/* ─── Opaque handle ─── */ +struct NODE_CONN_DIRECT; + +/* ─── Callback events (единый callback на handle) ─── */ +enum ncd_event { + NCD_EVENT_UP = 0, + NCD_EVENT_DOWN = 1, + NCD_EVENT_TIMEOUT = 2, +}; + +typedef void (*ncd_callback)(struct NODE_CONN_DIRECT* h, enum ncd_event event, void* arg); + +/* + * Открыть handle к узлу по node_id. + * Node info — из node_registry, fallback SQLite. + * group_id — для REVERSE-подключения через etcp_route_send (0 = без REVERSE). + * cb — вызывается при каждом изменении статуса / таймауте. + * Если conn уже готов — cb(h, NCD_EVENT_UP) через uasync_call_soon (не синхронно). + * + * Возвращает NCD_NEW / NCD_REUSED / NCD_ERR. + */ +int node_conn_direct_open(struct UTUN_INSTANCE* inst, uint64_t node_id, uint64_t group_id, + ncd_callback cb, void* cb_arg, + struct NODE_CONN_DIRECT** out_handle); + +/* + * Обернуть уже существующий ETCP_CONN в handle (crypto+links уже готовы). + * Не создаёт линки, не делает crypto, не ставит connect_timer. + * Возвращает NCD_REUSED / NCD_ERR. + */ +int node_conn_direct_adopt(struct UTUN_INSTANCE* inst, struct ETCP_CONN* conn, + ncd_callback cb, void* cb_arg, + struct NODE_CONN_DIRECT** out_handle); + +void node_conn_direct_close(struct NODE_CONN_DIRECT* h); + +struct ETCP_CONN* node_conn_direct_get_conn(struct NODE_CONN_DIRECT* h); + +/* ─── Протокол CLOSE / KEEP_ALIVE (ETCP_RT_ID_NCD_CONTROL = 0x12) ─── */ + +#define NCD_SUBCMD_CLOSE 0x01 +#define NCD_SUBCMD_KEEP_ALIVE 0x02 + +#pragma pack(push, 1) +struct ncd_control_msg { + uint8_t cmd; /* ETCP_RT_ID_NCD_CONTROL */ + uint8_t subcmd; /* NCD_SUBCMD_CLOSE / KEEP_ALIVE */ + uint64_t node_id; /* node_id отправителя */ +}; +#pragma pack(pop) + +#define NCD_CONTROL_MSG_SIZE sizeof(struct ncd_control_msg) + +#endif diff --git a/src/utun_instance.h b/src/utun_instance.h index 7e070ccd..a3329f9b 100644 --- a/src/utun_instance.h +++ b/src/utun_instance.h @@ -114,7 +114,7 @@ struct UTUN_INSTANCE { struct ll_queue* tcp_connections; // indexed by node_id, data=tcp_conn_entry [TCP-only] // Callback chain for new ETCP connections - struct etcp_cbk_entry* new_conn_cbks; + struct etcp_inst_cbk_entry* new_conn_cbks; struct etcp_status_cbk_entry* conn_status_cbks; // instance-level: NEW/UP/DOWN/DELETE void* test_user_ptr; // Generic user pointer (used by tests) diff --git a/tests/Makefile.am b/tests/Makefile.am index e767dc0f..16c0d625 100644 --- a/tests/Makefile.am +++ b/tests/Makefile.am @@ -50,6 +50,7 @@ check_PROGRAMS = \ test_conn_mgr \ test_conn_mgr_already_connected \ test_etcp_connect \ + test_node_conn_direct \ test_db_sync \ test_merkle_sync \ test_chat_sync_stress \ @@ -291,6 +292,10 @@ test_etcp_connect_SOURCES = test_etcp_connect.c test_etcp_connect_CFLAGS = -I$(top_srcdir)/src -I$(top_srcdir)/lib test_etcp_connect_LDADD = $(LIBUTUN) $(CRYPTO_LIBS) $(COMMON_LIBS) +test_node_conn_direct_SOURCES = test_node_conn_direct.c +test_node_conn_direct_CFLAGS = -I$(top_srcdir)/src -I$(top_srcdir)/lib +test_node_conn_direct_LDADD = $(LIBUTUN) $(CRYPTO_LIBS) $(COMMON_LIBS) + test_db_sync_SOURCES = test_db_sync.c test_db_sync_CFLAGS = -I$(top_srcdir)/src -I$(top_srcdir)/lib test_db_sync_LDADD = $(LIBUTUN) $(CRYPTO_LIBS) $(COMMON_LIBS) diff --git a/tests/test_bgp_route_exchange.c b/tests/test_bgp_route_exchange.c index 66207f20..2e55cbe7 100644 --- a/tests/test_bgp_route_exchange.c +++ b/tests/test_bgp_route_exchange.c @@ -214,7 +214,7 @@ static int check_learned_route(struct UTUN_INSTANCE* inst, uint32_t network, uin for (size_t i = 0; i < inst->rt->count; i++) { struct ROUTE_ENTRY* e = &inst->rt->entries[i]; if (e->network == network && e->prefix_length == prefix_len && - e->v_node_info && e->v_node_info->node->node_id == node_id) return 1; + e->v_node_info && e->v_node_info->node_id == node_id) return 1; } return 0; } diff --git a/tests/test_bgp_triangle.c b/tests/test_bgp_triangle.c index 9d6750d4..575b85ac 100644 --- a/tests/test_bgp_triangle.c +++ b/tests/test_bgp_triangle.c @@ -127,13 +127,13 @@ static int peer_in_nodes(struct UTUN_INSTANCE* inst, uint64_t node_id) { } static int node_path_count(struct UTUN_INSTANCE* inst, uint64_t node_id) { - struct TOPO_NODEQ* nq = topo_node_find_by_id(topo_groups_get_default(inst->topo_groups), node_id); + struct TOPO_GROUP_NODE* nq = topo_node_find_by_id(topo_groups_get_default(inst->topo_groups), node_id); if (!nq || !nq->paths) return 0; return queue_entry_count(nq->paths); } static int node_path_has_peer(struct UTUN_INSTANCE* inst, uint64_t node_id, uint64_t peer_id) { - struct TOPO_NODEQ* nq = topo_node_find_by_id(topo_groups_get_default(inst->topo_groups), node_id); + struct TOPO_GROUP_NODE* nq = topo_node_find_by_id(topo_groups_get_default(inst->topo_groups), node_id); if (!nq || !nq->paths) return 0; struct ll_entry* e = nq->paths->head; while (e) { @@ -147,7 +147,7 @@ static int node_path_has_peer(struct UTUN_INSTANCE* inst, uint64_t node_id, uint } static int node_version(struct UTUN_INSTANCE* inst, uint64_t node_id) { - struct TOPO_NODEQ* nq = topo_node_find_by_id(topo_groups_get_default(inst->topo_groups), node_id); + struct TOPO_GROUP_NODE* nq = topo_node_find_by_id(topo_groups_get_default(inst->topo_groups), node_id); return nq ? nq->last_ver : -1; } @@ -156,7 +156,7 @@ static int route_count_for_node(struct UTUN_INSTANCE* inst, uint64_t node_id) { int cnt = 0; for (size_t i = 0; i < inst->rt->count; i++) if (inst->rt->entries[i].v_node_info && - inst->rt->entries[i].v_node_info->node->node_id == node_id) cnt++; + inst->rt->entries[i].v_node_info->node_id == node_id) cnt++; return cnt; } diff --git a/tests/test_conn_mgr.c b/tests/test_conn_mgr.c index abfc38fa..3e4faecd 100644 --- a/tests/test_conn_mgr.c +++ b/tests/test_conn_mgr.c @@ -171,7 +171,7 @@ static void test3_check(void* arg) { fprintf(stderr, " state=%d type=%d\n", st, ty); fflush(stderr); if (st != CONN_MGR_STATE_CONNECTED || ty != CONN_TYPE_DIRECT) { fail("test3: status mismatch"); return; } - struct TOPO_NODEQ* nq = topo_node_find_by_id(g_a2->conn_mgr->group, g_nid_b2); + struct TOPO_GROUP_NODE* nq = topo_node_find_by_id(g_a2->conn_mgr->group, g_nid_b2); if (!nq) { fail("test3: B2 not in group after connect"); return; } if (g_a2->conn_mgr->group->group_id != TOPO_GROUP_UTUN) { fail("test3: wrong group_id"); return; } @@ -192,7 +192,7 @@ static void test4_check(void* arg) { if (!cdone2) { uasync_set_timeout(ua2, 20, NULL, (timeout_callback_t)test4_check, "t4c"); return; } if (cresult2 != CONN_MGR_ERR_UNREACHABLE) { fail("test4: expected UNREACHABLE"); return; } - struct TOPO_NODEQ* nq = topo_node_find_by_id(g_a2->conn_mgr->group, g_nid_c); + struct TOPO_GROUP_NODE* nq = topo_node_find_by_id(g_a2->conn_mgr->group, g_nid_c); if (nq && !(nq->paths && queue_entry_count(nq->paths) > 0)) { fail("test4: C still in group after unreachable"); return; } fprintf(stderr, " OK: UNREACHABLE, node cleaned up\n"); fflush(stderr); diff --git a/tests/test_conn_mgr_already_connected.c b/tests/test_conn_mgr_already_connected.c index 48f25beb..067ab2a7 100644 --- a/tests/test_conn_mgr_already_connected.c +++ b/tests/test_conn_mgr_already_connected.c @@ -72,7 +72,7 @@ static void test_mgr_connect(void* arg) { (void)arg; cb_fired = 0; cb_ok = 0; struct CONN_MGR* mgr = g_a->conn_mgr; - struct TOPO_NODEQ* nq = topo_node_find_by_id(mgr->group, g_b->node_id); + struct TOPO_GROUP_NODE* nq = topo_node_find_by_id(mgr->group, g_b->node_id); if (nq) { fprintf(stderr, "node already in UTUN group\n"); fflush(stderr); } fprintf(stderr, "Test 2: conn_mgr_connect_node(B)\n"); fflush(stderr); conn_mgr_connect_node(mgr, g_b->node_id, 0, conn_cb, NULL); @@ -99,7 +99,10 @@ static void test3_preconnect(void* arg) { struct ETCP_CONN* c = instance_find_conn(g_a, g_b->node_id); if (c) { fprintf(stderr, "Test3: SKIP — conn already established\n"); return; } fprintf(stderr, "Test 3: conn_mgr_connect_node BEFORE link up\n"); fflush(stderr); - conn_mgr_connect_node(mgr, g_b->node_id, 0, conn_cb, NULL); + if (!conn_mgr_connect_node(mgr, g_b->node_id, 0, conn_cb, NULL)) { + fprintf(stderr, "Test 3: SKIP — node not found in group (BGP not done yet)\n"); fflush(stderr); + return; + } uasync_call_soon(ua, NULL, test3_check); } diff --git a/tests/test_etcp_connect.c b/tests/test_etcp_connect.c index c9f6bcaf..be830dc3 100644 --- a/tests/test_etcp_connect.c +++ b/tests/test_etcp_connect.c @@ -66,20 +66,21 @@ static volatile int cb_type = 0, cb_conn_ok = 0, cb_count = 0; static void connect_ccb(void* a, struct ETCP_CONN* conn, int type) { cb_type = type; cb_conn_ok = (conn != NULL); cb_count++; } -/* ----- helper: create minimal TOPO_NODEQ with 1 v4 addr ----- */ -static struct TOPO_NODEQ* mknode(uint64_t nid, const uint8_t pubkey[32], +/* ----- helper: create minimal TOPO_GROUP_NODE with 1 v4 addr ----- */ +static struct TOPO_GROUP_NODE* mknode(struct UTUN_INSTANCE* inst, uint64_t nid, const uint8_t pubkey[32], uint8_t a, uint8_t b, uint8_t c, uint8_t d, uint16_t port) { - struct TOPO_NODEQ* nq = u_calloc(1, sizeof(struct TOPO_NODEQ)); + struct TOPO_GROUP_NODE* nq = u_calloc(1, sizeof(struct TOPO_GROUP_NODE)); struct TOPO_NODE* ni = u_calloc(1, sizeof(struct TOPO_NODE)); if (!nq || !ni) { u_free(nq); u_free(ni); return NULL; } - ni->ref_count = 1; ni->node_id = nid; ni->ver = 0; + ni->group_ref_count = 1; ni->node_id = nid; ni->ver = 0; ni->group_id = TOPO_GROUP_UTUN; memcpy(ni->public_key, pubkey, SC_PUBKEY_SIZE); - nq->ll.size = sizeof(struct TOPO_NODEQ) - sizeof(struct ll_entry); - nq->node = ni; nq->hash_node_id = nid; - struct TOPO_SOCKMETA4* sm = u_calloc(1, sizeof(struct TOPO_SOCKMETA4)); + nq->ll.size = sizeof(struct TOPO_GROUP_NODE) - sizeof(struct ll_entry); + nq->node_id = nid; + struct TOPO_SOCKMETA4* sm = memory_pool_alloc(inst->topo_groups->v4_sock_meta_pool); if (sm) { sm->id = 0; sm->config_type = CFG_SERVER_TYPE_PUBLIC; sm->nat_type = NAT_TYPE_UNKNOWN; sm->next = ni->v4_sock_meta; ni->v4_sock_meta = sm; } - struct TOPO_ADDR4* addr = u_calloc(1, sizeof(struct TOPO_ADDR4)); + struct TOPO_ADDR4* addr = memory_pool_alloc(inst->topo_groups->v4_addr_pool); if (addr) { addr->addr[0] = a; addr->addr[1] = b; addr->addr[2] = c; addr->addr[3] = d; addr->port = port; addr->type = TOPO_ADDR_INTERFACE; addr->socket_id = 0; addr->next = ni->v4_addrs; ni->v4_addrs = addr; } + topo_node_registry_store(inst->topo_groups, ni); return nq; } @@ -87,7 +88,7 @@ static struct TOPO_NODEQ* mknode(uint64_t nid, const uint8_t pubkey[32], static void test1(void* arg) { (void)arg; if (result) return; if (lks(g_a) < 1 || lks(g_b) < 1) { uasync_set_timeout(ua, 50, NULL, (timeout_callback_t)test1, "t1"); return; } - struct TOPO_NODEQ* nb = topo_node_find_by_id(topo_groups_get_default(g_a->topo_groups), nid_b); + struct TOPO_GROUP_NODE* nb = topo_node_find_by_id(topo_groups_get_default(g_a->topo_groups), nid_b); if (!nb) { uasync_set_timeout(ua, 50, NULL, (timeout_callback_t)test1, "t1b"); return; } fprintf(stderr, "Test 1: already connected → EARLY|LATE\n"); fflush(stderr); cb_type = 0; cb_conn_ok = 0; cb_count = 0; @@ -111,7 +112,7 @@ static void test3(void* arg) { fprintf(stderr, "Test 3: unreachable node → error\n"); fflush(stderr); struct SC_MYKEYS fk; sc_generate_keypair(&fk); uint64_t fnid = 0xF000000000000001ULL; - struct TOPO_NODEQ* fn = mknode(fnid, fk.public_key, 127,0,0,1, 49999); + struct TOPO_GROUP_NODE* fn = mknode(g_a, fnid, fk.public_key, 127,0,0,1, 49999); if (!fn) { fail("mknode failed"); return; } queue_data_put_with_index(topo_groups_get_default(g_a->topo_groups)->nodes, &fn->ll); g_a->etcp_connect_timeout_tb = 2000; @@ -141,7 +142,7 @@ static void test5(void* arg) { fprintf(stderr, "Test 5: double connect to unreachable → both callbacks\n"); fflush(stderr); struct SC_MYKEYS fk; sc_generate_keypair(&fk); uint64_t fnid = 0xF000000000000002ULL; - struct TOPO_NODEQ* fn = mknode(fnid, fk.public_key, 127,0,0,1, 49998); + struct TOPO_GROUP_NODE* fn = mknode(g_a, fnid, fk.public_key, 127,0,0,1, 49998); if (!fn) { fail("mknode failed"); return; } queue_data_put_with_index(topo_groups_get_default(g_a->topo_groups)->nodes, &fn->ll); g_a->etcp_connect_timeout_tb = 2000; @@ -162,7 +163,7 @@ static void test6(void* arg) { /* ======================== Test 7: EARLY-only flag ======================== */ static void test7(void* arg) { (void)arg; if (result) return; - struct TOPO_NODEQ* nb = topo_node_find_by_id(topo_groups_get_default(g_a->topo_groups), nid_b); + struct TOPO_GROUP_NODE* nb = topo_node_find_by_id(topo_groups_get_default(g_a->topo_groups), nid_b); if (!nb || lks(g_a) < 1) { uasync_set_timeout(ua, 50, NULL, (timeout_callback_t)test7, "t7"); return; } fprintf(stderr, "Test 7: EARLY-only flag\n"); fflush(stderr); cb_type = 0; cb_conn_ok = 0; cb_count = 0; @@ -182,7 +183,7 @@ static void test8(void* arg) { /* ======================== Test 9: LATE-only flag ======================== */ static void test9(void* arg) { (void)arg; if (result) return; - struct TOPO_NODEQ* nb = topo_node_find_by_id(topo_groups_get_default(g_a->topo_groups), nid_b); + struct TOPO_GROUP_NODE* nb = topo_node_find_by_id(topo_groups_get_default(g_a->topo_groups), nid_b); if (!nb || lks(g_a) < 1) { uasync_set_timeout(ua, 50, NULL, (timeout_callback_t)test9, "t9"); return; } fprintf(stderr, "Test 9: LATE-only flag\n"); fflush(stderr); cb_type = 0; cb_conn_ok = 0; cb_count = 0; diff --git a/tests/test_etcp_router_reconnect.c b/tests/test_etcp_router_reconnect.c index 5ce4636c..4a22fe43 100644 --- a/tests/test_etcp_router_reconnect.c +++ b/tests/test_etcp_router_reconnect.c @@ -78,18 +78,20 @@ static void gen_payload(uint32_t seq, uint8_t* buf, int len) { for (int i=0;iref_count = 1; ni->node_id = nid; ni->ver = 0; + ni->group_ref_count = 1; ni->node_id = nid; ni->ver = 0; ni->group_id = TOPO_GROUP_UTUN; memcpy(ni->public_key, pubkey, SC_PUBKEY_SIZE); - nq->node = ni; nq->hash_node_id = nid; - struct TOPO_SOCKMETA4* sm = u_calloc(1, sizeof(struct TOPO_SOCKMETA4)); + nq->ll.size = sizeof(struct TOPO_GROUP_NODE) - sizeof(struct ll_entry); + nq->node_id = nid; + struct TOPO_SOCKMETA4* sm = memory_pool_alloc(inst->topo_groups->v4_sock_meta_pool); if (sm) { sm->id = 0; sm->config_type = CFG_SERVER_TYPE_PUBLIC; sm->nat_type = NAT_TYPE_UNKNOWN; sm->next = ni->v4_sock_meta; ni->v4_sock_meta = sm; } - struct TOPO_ADDR4* addr = u_calloc(1, sizeof(struct TOPO_ADDR4)); - if (addr) { addr->addr[0] = a; addr->addr[1] = b; addr->addr[2] = c; addr->addr[3] = d; addr->port = port; addr->type = TOPO_ADDR_INTERFACE; addr->socket_id = 0; addr->next = ni->v4_addrs; ni->v4_addrs = addr; } + struct TOPO_ADDR4* addr = memory_pool_alloc(inst->topo_groups->v4_addr_pool); + if (addr) { addr->addr[0] = a; addr->addr[1] = b; addr->addr[2] = c; addr->addr[3] = d; addr->port = port; addr->type = TOPO_ADDR_INTERFACE; addr->socket_id = 0; addr->protocol = TOPO_PROTO_UDP; addr->next = ni->v4_addrs; ni->v4_addrs = addr; } + if (inst && inst->topo_groups) topo_node_registry_store(inst->topo_groups, ni); return nq; } @@ -197,8 +199,8 @@ static void state_step(void) { g_b=utun_instance_create(ua,b_conf); if (!g_b||utun_instance_init(g_b)<0) { fprintf(stderr,"FAIL: b restart\n"); g_test_ok=-1; g_fail_code=20; return; } g_b->etcp_connect_timeout_tb = 300000; - struct TOPO_NODEQ* nq_a = mknode(node_a, keys_a.public_key, 127,0,0,1, (uint16_t)a_port); - struct TOPO_NODEQ* nq_c = mknode(node_c, keys_c.public_key, 127,0,0,1, (uint16_t)c_port); + struct TOPO_GROUP_NODE* nq_a = mknode(g_b, node_a, keys_a.public_key, 127,0,0,1, (uint16_t)a_port); + struct TOPO_GROUP_NODE* nq_c = mknode(g_b, node_c, keys_c.public_key, 127,0,0,1, (uint16_t)c_port); if (!nq_a || !nq_c) { fprintf(stderr,"FAIL: mknode\n"); g_test_ok=-1; return; } static int cc_a=0,cc_c=1; etcp_connect(g_b,nq_a,connect_bgp_ready_cb,&cc_a,ETCP_CONNECT_BGP_READY); @@ -248,8 +250,8 @@ int main(void) { etcp_router_bind(g_c, TEST_SVC_ID, recv_handler); - struct TOPO_NODEQ* nq_a = mknode(node_a, keys_a.public_key, 127,0,0,1, (uint16_t)a_port); - struct TOPO_NODEQ* nq_c = mknode(node_c, keys_c.public_key, 127,0,0,1, (uint16_t)c_port); + struct TOPO_GROUP_NODE* nq_a = mknode(g_b, node_a, keys_a.public_key, 127,0,0,1, (uint16_t)a_port); + struct TOPO_GROUP_NODE* nq_c = mknode(g_b, node_c, keys_c.public_key, 127,0,0,1, (uint16_t)c_port); if (!nq_a || !nq_c) { fprintf(stderr,"FAIL: mknode\n"); goto fail; } g_b->etcp_connect_timeout_tb = 300000; { static int cc_a=0,cc_c=1; diff --git a/tests/test_etcp_router_unit.c b/tests/test_etcp_router_unit.c index f5158f71..d7b7d58d 100644 --- a/tests/test_etcp_router_unit.c +++ b/tests/test_etcp_router_unit.c @@ -144,6 +144,7 @@ static void setup_bgp_for_sign_test(struct UTUN_INSTANCE* inst, int with_ed25519 inst->topo_groups = u_calloc(1, sizeof(struct TOPO_GROUPS)); if (!inst->topo_groups) { printf(" setup_bgp: topo_groups alloc failed\n"); return; } inst->topo_groups->group_list = queue_new(inst->ua, 16, 0, 8, "group_list_test"); + inst->topo_groups->node_registry = queue_new(inst->ua, BGP_NODES_HASH_SIZE, 0, 8, "node_registry_test"); struct ll_entry* qe = queue_entry_new(sizeof(struct TOPO_GROUP)); if (!qe) { printf(" setup_bgp: queue_entry_new failed\n"); return; } @@ -152,18 +153,19 @@ static void setup_bgp_for_sign_test(struct UTUN_INSTANCE* inst, int with_ed25519 group->instance = inst; group->group_id = TOPO_GROUP_UTUN; group->nodes = queue_new(inst->ua, BGP_NODES_HASH_SIZE, - offsetof(struct TOPO_NODEQ, hash_node_id) - sizeof(struct ll_entry), 8, "group_nodes_test"); + offsetof(struct TOPO_GROUP_NODE, node_id) - sizeof(struct ll_entry), 8, "group_nodes_test"); if (!group->nodes) { printf(" setup_bgp: queue_new failed\n"); queue_entry_free(qe); return; } queue_data_put_with_index(inst->topo_groups->group_list, &group->ll); - struct TOPO_NODEQ* nq = u_calloc(1, sizeof(struct TOPO_NODEQ)); + struct TOPO_GROUP_NODE* nq = u_calloc(1, sizeof(struct TOPO_GROUP_NODE)); if (!nq) { printf(" setup_bgp: nq alloc failed\n"); return; } - nq->ll.size = sizeof(struct TOPO_NODEQ) - sizeof(struct ll_entry); + nq->ll.size = sizeof(struct TOPO_GROUP_NODE) - sizeof(struct ll_entry); struct TOPO_NODE* ni = u_calloc(1, sizeof(struct TOPO_NODE)); if (!ni) { printf(" setup_bgp: ni alloc failed\n"); u_free(nq); return; } - ni->ref_count = 1; ni->node_id = SIGNER_NODE; + ni->group_ref_count = 1; ni->node_id = SIGNER_NODE; if (with_ed25519_key) memcpy(ni->ed25519_public_key, g_sign_ed25519_pubkey, SC_PUBKEY_SIZE); - nq->node = ni; nq->hash_node_id = ni->node_id; + topo_node_registry_store(inst->topo_groups, ni); + nq->node_id = ni->node_id; queue_data_put_with_index(group->nodes, &nq->ll); } diff --git a/tests/test_ipv6_sockets.c b/tests/test_ipv6_sockets.c index a10e2aa0..bda3bb23 100644 --- a/tests/test_ipv6_sockets.c +++ b/tests/test_ipv6_sockets.c @@ -136,11 +136,11 @@ static int is_connection_established(struct UTUN_INSTANCE* inst) { static int verify_ipv6_local_nodeinfo(const char* name, struct UTUN_INSTANCE* inst, int expect_socks, int expect_addrs, int expect_subnets) { - if (!inst || !topo_groups_get_default(inst->topo_groups) || !topo_groups_get_default(inst->topo_groups)->local_node || !topo_groups_get_default(inst->topo_groups)->local_node->node) { - printf("FAIL [%s]: no bgp/local_node\n", name); - return 0; - } - struct TOPO_NODE* ni = topo_groups_get_default(inst->topo_groups)->local_node->node; + struct TOPO_GROUPS* groups = inst ? inst->topo_groups : NULL; + struct TOPO_GROUP* grp = groups ? topo_groups_get_default(groups) : NULL; + if (!grp || !grp->local_node) { printf("FAIL [%s]: no bgp/local_node\n", name); return 0; } + struct TOPO_NODE* ni = topo_node_registry_find(groups, grp->local_node->node_id); + if (!ni) { printf("FAIL [%s]: local node not in registry\n", name); return 0; } struct ETCP_SOCKET* es = inst->etcp_sockets; int v6_sock_found = 0; @@ -188,18 +188,21 @@ static int verify_ipv6_local_nodeinfo(const char* name, struct UTUN_INSTANCE* in } static int verify_remote_v6_nodeinfo(const char* name, struct UTUN_INSTANCE* inst, uint64_t peer_node_id) { - if (!inst || !topo_groups_get_default(inst->topo_groups)) { printf("FAIL [%s]: no bgp\n", name); return 0; } - struct TOPO_NODEQ* nq = topo_node_find_by_id(topo_groups_get_default(inst->topo_groups), peer_node_id); - if (!nq || !nq->node) { printf("FAIL [%s]: remote node %016llx not found\n", name, (unsigned long long)peer_node_id); return 0; } + struct TOPO_GROUPS* groups = inst ? inst->topo_groups : NULL; + if (!groups) { printf("FAIL [%s]: no bgp\n", name); return 0; } + struct TOPO_GROUP_NODE* nq = topo_node_find_by_id(topo_groups_get_default(groups), peer_node_id); + if (!nq) { printf("FAIL [%s]: remote node %016llx not found\n", name, (unsigned long long)peer_node_id); return 0; } + struct TOPO_NODE* rni = topo_node_registry_find(groups, nq->node_id); + if (!rni) { printf("FAIL [%s]: remote node %016llx not in registry\n", name, (unsigned long long)peer_node_id); return 0; } printf("PASS [%s]: remote node %016llx found\n", name, (unsigned long long)peer_node_id); - if (!nq->node->v6_sock_meta) { printf("FAIL [%s]: remote node has no v6 sockets\n", name); return 0; } - printf("PASS [%s]: remote node v6_sockets=%d\n", name, topo_list_count((struct _topo_head*)nq->node->v6_sock_meta)); + if (!rni->v6_sock_meta) { printf("FAIL [%s]: remote node has no v6 sockets\n", name); return 0; } + printf("PASS [%s]: remote node v6_sockets=%d\n", name, topo_list_count((struct _topo_head*)rni->v6_sock_meta)); - if (!nq->node->v6_addrs) { printf("FAIL [%s]: remote node has no v6 addrs\n", name); return 0; } - printf("PASS [%s]: remote node v6_addrs=%d\n", name, topo_list_count((struct _topo_head*)nq->node->v6_addrs)); + if (!rni->v6_addrs) { printf("FAIL [%s]: remote node has no v6 addrs\n", name); return 0; } + printf("PASS [%s]: remote node v6_addrs=%d\n", name, topo_list_count((struct _topo_head*)rni->v6_addrs)); - const struct TOPO_ADDR6* a = nq->node->v6_addrs; + const struct TOPO_ADDR6* a = rni->v6_addrs; if (a) { uint8_t loopback[16] = {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1}; if (memcmp(a->addr, loopback, 16) != 0) { printf("FAIL [%s]: remote v6 addr not ::1\n", name); return 0; } diff --git a/tests/test_nat_detection.c b/tests/test_nat_detection.c index d2142c20..48677cee 100644 --- a/tests/test_nat_detection.c +++ b/tests/test_nat_detection.c @@ -311,9 +311,9 @@ int main(void) { } // 5. Verify all NAT fields on server - struct TOPO_NODEQ* node_c1 = topo_node_find_by_id(topo_groups_get_default(inst_s->topo_groups), g_node_id_c1); + struct TOPO_GROUP_NODE* node_c1 = topo_node_find_by_id(topo_groups_get_default(inst_s->topo_groups), g_node_id_c1); if (!node_c1) { - DEBUG_ERROR(DEBUG_CATEGORY_BGP, "TOPO_NODEQ for C1 disappeared"); + DEBUG_ERROR(DEBUG_CATEGORY_BGP, "TOPO_GROUP_NODE for C1 disappeared"); goto cleanup; } if (link_sc1->nat_check_status != NAT_CHECK_EIM) { @@ -361,9 +361,11 @@ int main(void) { } // Verify local_node socket update on C1 (received NAT_INFO updated nodeinfo) - if (topo_groups_get_default(inst_c1->topo_groups) && topo_groups_get_default(inst_c1->topo_groups)->local_node && topo_groups_get_default(inst_c1->topo_groups)->local_node->node) { - int found = 0; - for (const struct TOPO_SOCKMETA4* m = topo_groups_get_default(inst_c1->topo_groups)->local_node->node->v4_sock_meta; m; m = m->next) { + if (topo_groups_get_default(inst_c1->topo_groups) && topo_groups_get_default(inst_c1->topo_groups)->local_node) { + struct TOPO_NODE* lni = topo_node_registry_find(inst_c1->topo_groups, topo_groups_get_default(inst_c1->topo_groups)->local_node->node_id); + if (lni) { + int found = 0; + for (const struct TOPO_SOCKMETA4* m = lni->v4_sock_meta; m; m = m->next) { if (m->id == sock_c1->sock_id) { if (m->nat_type != NAT_VERIFIED_EIM) { DEBUG_ERROR(DEBUG_CATEGORY_BGP, "FAIL: local_node socket meta not updated on C1: nat_type=%u expected=%u", m->nat_type, NAT_VERIFIED_EIM); @@ -375,7 +377,7 @@ int main(void) { if (!found) { DEBUG_ERROR(DEBUG_CATEGORY_BGP, "FAIL: socket not found in local_node meta on C1"); goto cleanup; } // Verify NAT addr in flat address list int nat_found = 0; - for (const struct TOPO_ADDR4* a = topo_groups_get_default(inst_c1->topo_groups)->local_node->node->v4_addrs; a; a = a->next) { + for (const struct TOPO_ADDR4* a = lni->v4_addrs; a; a = a->next) { if (a->type == TOPO_ADDR_NAT && a->socket_id == (sock_c1->sock_id | 1)) { uint32_t addr_ip; memcpy(&addr_ip, a->addr, 4); if (addr_ip != link_sc1->nat_ip || a->port != link_sc1->nat_port) { @@ -388,16 +390,20 @@ int main(void) { } if (!nat_found) { DEBUG_ERROR(DEBUG_CATEGORY_BGP, "FAIL: NAT addr not found in local_node flat addr list on C1"); goto cleanup; } DEBUG_INFO(DEBUG_CATEGORY_BGP, "local_node meta+addr check PASSED on C1"); + } } // Wait for C2 to learn updated C1 nodeinfo (with verified NAT type) DEBUG_INFO(DEBUG_CATEGORY_BGP, "Waiting for C2 to learn updated C1 nodeinfo..."); bgp_wait_cycles = 0; int c2_verified_nat = 0; + struct TOPO_GROUP_NODE* node_c1_on_c2 = NULL; + struct TOPO_NODE* cni = NULL; while (!test_timed_out && bgp_wait_cycles < 500) { - struct TOPO_NODEQ* node_c1_on_c2 = topo_groups_get_default(inst_c2->topo_groups) ? topo_node_find_by_id(topo_groups_get_default(inst_c2->topo_groups), g_node_id_c1) : NULL; - if (node_c1_on_c2 && node_c1_on_c2->node) { - for (const struct TOPO_SOCKMETA4* m = node_c1_on_c2->node->v4_sock_meta; m; m = m->next) { + node_c1_on_c2 = topo_groups_get_default(inst_c2->topo_groups) ? topo_node_find_by_id(topo_groups_get_default(inst_c2->topo_groups), g_node_id_c1) : NULL; + cni = node_c1_on_c2 ? topo_node_registry_find(inst_c2->topo_groups, node_c1_on_c2->node_id) : NULL; + if (node_c1_on_c2 && cni) { + for (const struct TOPO_SOCKMETA4* m = cni->v4_sock_meta; m; m = m->next) { if (m->nat_type == NAT_VERIFIED_EIM) { c2_verified_nat = 1; break; } } if (c2_verified_nat) break; @@ -433,7 +439,7 @@ int main(void) { int ret = route_ping_send_req_addr(inst_s->nat_det, topo_groups_get_default(inst_s->topo_groups), conn_sc2, target_ip_net, link_sc1->nat_port, 3, 10, 200, 3000, nat_ping_resp_cb, NULL, - node_c1->node->public_key); + node_c1_on_c2 ? topo_node_registry_find(inst_c2->topo_groups, node_c1_on_c2->node_id)->public_key : NULL); if (ret != 0) { DEBUG_ERROR(DEBUG_CATEGORY_BGP, "route_ping_send_req_addr failed: %d", ret); goto cleanup; @@ -481,7 +487,7 @@ int main(void) { ret = route_ping_send_req_addr(inst_s->nat_det, topo_groups_get_default(inst_s->topo_groups), conn_sc2, target_ip_net, link_sc1->nat_port, 3, 10, 200, 3000, nat_ping_resp_cb, NULL, - node_c1->node->public_key); + cni->public_key); if (ret != 0) { DEBUG_ERROR(DEBUG_CATEGORY_BGP, "route_ping_send_req_addr (no pubkey) failed: %d", ret); goto cleanup; diff --git a/tests/test_node_conn_direct.c b/tests/test_node_conn_direct.c new file mode 100644 index 00000000..232629ef --- /dev/null +++ b/tests/test_node_conn_direct.c @@ -0,0 +1,251 @@ +/** + * @file test_node_conn_direct.c + * @brief Тест node_conn_direct с 2 инстансами. + * + * Фазы: + * 1 — два open ДО eventloop → NCD_NEW + NCD_REUSED(pending) + * 2 — poll → init_cb → NCD_EVENT_UP на обоих + * 3 — open на ready conn → NCD_REUSED + async cb + * 4 — ещё open → NCD_REUSED + * 5 — close h3,h2 → conn жив + * 6 — close h1,h0 (last) → conn закрыт (fin_wait → CLOSE → DOWN) + * 7 — NCD_ERR unknown node + * 8 — inject unreachable → NCD_NEW → TIMEOUT + */ +#include +#include +#include +#include +#include "../lib/platform_compat.h" +#include "test_utils.h" +#ifndef _WIN32 +#include +#endif + +#include "etcp.h" +#include "etcp_connections.h" +#include "node_conn_direct.h" +#include "../src/config_parser.h" +#include "../src/config_updater.h" +#include "../src/utun_instance.h" +#include "topo_group.h" +#include "topo_node.h" +#include "secure_channel.h" +#include "../lib/u_async.h" +#include "../lib/ll_queue.h" +#include "../lib/memory_pool.h" +#include "../lib/debug_config.h" +#include "../lib/mem.h" + +#define TIMEOUT_TB 600000 +#define SHORT_TO_TB 50000 +#define POLL_MS 5 + +static struct UTUN_INSTANCE *g_a = NULL, *g_b = NULL; +static struct UASYNC *ua = NULL; +static volatile int g_result = 0; +static int g_phase = 0; +static void *g_ttimer = NULL; + +static char tdir[] = "/tmp/utun_ncd_XXXXXX"; +static char ca[256], cb[256]; +static int pa = 0, pb = 0; +static uint64_t nid_b = 0; +static uint8_t pubkey_b[SC_PUBKEY_SIZE]; + +static struct NODE_CONN_DIRECT *gh[4]; +static volatile int g_up[4], g_down[4], g_tout[4]; + +static void ncd_cb(struct NODE_CONN_DIRECT* h, enum ncd_event event, void* arg) { + int idx = (int)(intptr_t)arg; + if (event == NCD_EVENT_UP) g_up[idx]++; + else if (event == NCD_EVENT_DOWN) g_down[idx]++; + else if (event == NCD_EVENT_TIMEOUT) g_tout[idx]++; + fprintf(stderr, " cb[%d]: event=%d (up=%d down=%d tout=%d)\n", idx, (int)event, g_up[idx], g_down[idx], g_tout[idx]); fflush(stderr); +} + +static void to_cb(void* arg) { (void)arg; fprintf(stderr, "GLOBAL TIMEOUT phase=%d\n", g_phase); g_result = 2; } + +static int wf(const char* p, const char* f, ...) { va_list ap; FILE* fp = fopen(p, "w"); if (!fp) return -1; va_start(ap, f); vfprintf(fp, f, ap); va_end(ap); fclose(fp); return 0; } +static void fail(const char* msg) { fprintf(stderr, "FAIL[%d]: %s\n", g_phase, msg); fflush(stderr); g_result = 2; } + +static void t1(void* arg); static void t2(void* arg); static void t3(void* arg); +static void t4(void* arg); static void t5(void* arg); static void t6(void* arg); +static void t7(void* arg); static void t8(void* arg); static void t9(void* arg); +static void t_done(void* arg); + +static void t1(void* arg) { + (void)arg; g_phase = 1; + fprintf(stderr, "\n=== P1: two opens before poll ===\n"); fflush(stderr); + g_a->etcp_connect_timeout_tb = TIMEOUT_TB; + + int r = node_conn_direct_open(g_a, nid_b, 0, ncd_cb, (void*)0, &gh[0]); + fprintf(stderr, " open#0 → %s\n", r == NCD_NEW ? "NCD_NEW" : r == NCD_REUSED ? "NCD_REUSED" : "ERR"); + if (r != NCD_NEW) { fail("expected NCD_NEW"); return; } + + r = node_conn_direct_open(g_a, nid_b, 0, ncd_cb, (void*)1, &gh[1]); + fprintf(stderr, " open#1 → %s\n", r == NCD_NEW ? "NCD_NEW" : r == NCD_REUSED ? "NCD_REUSED" : "ERR"); + if (r != NCD_REUSED) { fail("expected NCD_REUSED"); return; } + + uasync_call_soon(ua, NULL, t2); +} + +static void t2(void* arg) { + (void)arg; g_phase = 2; + if (g_up[0] > 0 && g_up[1] > 0) { + if (node_conn_direct_get_conn(gh[0]) == NULL) { fail("get_conn h0 NULL"); return; } + if (node_conn_direct_get_conn(gh[1]) == NULL) { fail("get_conn h1 NULL"); return; } + fprintf(stderr, "\n=== P2: both up, get_conn OK ===\n"); fflush(stderr); + uasync_call_soon(ua, NULL, t3); return; + } + uasync_set_timeout(ua, POLL_MS, NULL, (timeout_callback_t)t2, "t2"); +} + +static void t3(void* arg) { + (void)arg; g_phase = 3; + fprintf(stderr, "\n=== P3: open on ready conn ===\n"); fflush(stderr); + int r = node_conn_direct_open(g_a, nid_b, 0, ncd_cb, (void*)2, &gh[2]); + fprintf(stderr, " open#2 → %s\n", r == NCD_REUSED ? "NCD_REUSED" : "?"); + if (r != NCD_REUSED) { fail("expected NCD_REUSED"); return; } + uasync_call_soon(ua, NULL, t4); +} + +static void t4(void* arg) { + (void)arg; g_phase = 4; + if (g_up[2] == 0) { uasync_set_timeout(ua, POLL_MS, NULL, (timeout_callback_t)t4, "t4"); return; } + fprintf(stderr, "\n=== P4: async cb OK, open h3 ===\n"); fflush(stderr); + int r = node_conn_direct_open(g_a, nid_b, 0, ncd_cb, (void*)3, &gh[3]); + fprintf(stderr, " open#3 → %s\n", r == NCD_REUSED ? "NCD_REUSED" : "?"); + if (r != NCD_REUSED) { fail("expected NCD_REUSED"); return; } + uasync_call_soon(ua, NULL, t5); +} + +static void t5(void* arg) { + (void)arg; g_phase = 5; + if (g_up[3] == 0) { uasync_set_timeout(ua, POLL_MS, NULL, (timeout_callback_t)t5, "t5"); return; } + fprintf(stderr, "\n=== P5: close h3, h2 → conn stays ===\n"); fflush(stderr); + node_conn_direct_close(gh[3]); gh[3] = NULL; + if (node_conn_direct_get_conn(gh[2]) == NULL) { fail("conn died after h3 close"); return; } + node_conn_direct_close(gh[2]); gh[2] = NULL; + if (node_conn_direct_get_conn(gh[1]) == NULL) { fail("conn died after h2 close"); return; } + fprintf(stderr, " OK\n"); fflush(stderr); + uasync_call_soon(ua, NULL, t6); +} + +static void t6(void* arg) { + (void)arg; g_phase = 6; + fprintf(stderr, "\n=== P6: close last → conn closed ===\n"); fflush(stderr); + node_conn_direct_close(gh[1]); gh[1] = NULL; + if (node_conn_direct_get_conn(gh[0]) == NULL) { fail("conn died before last handle"); return; } + node_conn_direct_close(gh[0]); gh[0] = NULL; + fprintf(stderr, " OK\n"); fflush(stderr); + uasync_set_timeout(ua, 200, NULL, (timeout_callback_t)t7, "t6d"); +} + +static void t7(void* arg) { + (void)arg; g_phase = 7; + fprintf(stderr, "\n=== P7: NCD_ERR unknown node ===\n"); fflush(stderr); + struct NODE_CONN_DIRECT* hx = NULL; + int r = node_conn_direct_open(g_a, 0xDEADBEEF00000001ULL, 0, ncd_cb, (void*)99, &hx); + fprintf(stderr, " open(unknown) → %s\n", r == NCD_ERR ? "NCD_ERR" : "?"); + if (r != NCD_ERR) { fail("expected NCD_ERR"); return; } + if (hx != NULL) { fail("handle not NULL"); return; } + fprintf(stderr, " OK\n"); fflush(stderr); + uasync_call_soon(ua, NULL, t8); +} + +static void t8(void* arg) { + (void)arg; g_phase = 8; + fprintf(stderr, "\n=== P8: unreachable node → timeout ===\n"); fflush(stderr); + + struct TOPO_NODE* ni = u_calloc(1, sizeof(struct TOPO_NODE)); + if (!ni) { fail("alloc ni"); return; } + uint64_t fake_id = 0xF000000000000001ULL; + ni->group_ref_count = 0; ni->node_id = fake_id; ni->ver = 0; + memcpy(ni->public_key, pubkey_b, SC_PUBKEY_SIZE); + + struct TOPO_SOCKMETA4* sm = memory_pool_alloc(g_a->topo_groups->v4_sock_meta_pool); + if (sm) { sm->id = 0; sm->config_type = CFG_SERVER_TYPE_PUBLIC; sm->nat_type = NAT_TYPE_UNKNOWN; sm->next = ni->v4_sock_meta; ni->v4_sock_meta = sm; } + + struct TOPO_ADDR4* addr = memory_pool_alloc(g_a->topo_groups->v4_addr_pool); + if (addr) { addr->addr[0]=127; addr->addr[1]=0; addr->addr[2]=0; addr->addr[3]=1; addr->port=1; addr->type=TOPO_ADDR_INTERFACE; addr->socket_id=0; addr->protocol=TOPO_PROTO_UDP; addr->next=ni->v4_addrs; ni->v4_addrs=addr; } + + if (!topo_node_registry_store(g_a->topo_groups, ni)) { fail("registry acquire"); return; } + + memset((void*)g_up, 0, sizeof(g_up)); + memset((void*)g_tout, 0, sizeof(g_tout)); + g_a->etcp_connect_timeout_tb = SHORT_TO_TB; + + int r = node_conn_direct_open(g_a, fake_id, 0, ncd_cb, (void*)0, &gh[0]); + fprintf(stderr, " open(unreachable) → %s\n", r == NCD_NEW ? "NCD_NEW" : "?"); + if (r != NCD_NEW) { fail("expected NCD_NEW"); return; } + + uasync_call_soon(ua, NULL, t9); +} + +static void t9(void* arg) { + (void)arg; g_phase = 9; + if (g_tout[0] == 0) { uasync_set_timeout(ua, POLL_MS, NULL, (timeout_callback_t)t9, "t9"); return; } + if (g_up[0] > 0) { fail("up fired, expected only timeout"); return; } + fprintf(stderr, "\n=== P9: timeout_cb fired, no up ===\n"); fflush(stderr); + node_conn_direct_close(gh[0]); gh[0] = NULL; + uasync_call_soon(ua, NULL, t_done); +} + +static void t_done(void* arg) { + (void)arg; g_phase = 99; + fprintf(stderr, "\n=== ALL PASSED ===\n"); fflush(stderr); + g_result = 1; +} + +static int inject_node(struct UTUN_INSTANCE* inst, uint64_t nid, const uint8_t pk[SC_PUBKEY_SIZE], uint16_t port) { + if (!inst->topo_groups) return -1; + struct TOPO_NODE* ni = u_calloc(1, sizeof(*ni)); + if (!ni) return -1; + ni->group_ref_count = 0; ni->node_id = nid; ni->ver = 0; + memcpy(ni->public_key, pk, SC_PUBKEY_SIZE); + struct TOPO_SOCKMETA4* sm = memory_pool_alloc(inst->topo_groups->v4_sock_meta_pool); + if (sm) { sm->id = 0; sm->config_type = CFG_SERVER_TYPE_PUBLIC; sm->nat_type = NAT_TYPE_UNKNOWN; sm->next = ni->v4_sock_meta; ni->v4_sock_meta = sm; } + struct TOPO_ADDR4* addr = memory_pool_alloc(inst->topo_groups->v4_addr_pool); + if (addr) { addr->addr[0]=127; addr->addr[1]=0; addr->addr[2]=0; addr->addr[3]=1; addr->port=port; addr->type=TOPO_ADDR_INTERFACE; addr->socket_id=0; addr->protocol=TOPO_PROTO_UDP; addr->next=ni->v4_addrs; ni->v4_addrs=addr; } + if (!topo_node_registry_store(inst->topo_groups, ni)) { u_free(ni); return -1; } + return 0; +} + +static int setup(void) { + test_mkdtemp(tdir); + int base = 49000 + (getpid() % 10000); pa = base; pb = base + 1; + snprintf(ca, sizeof(ca), "%s/a.conf", tdir); snprintf(cb, sizeof(cb), "%s/b.conf", tdir); + wf(ca, "[global]\ntun_ip=10.97.0.1/24\ntun_ifname=tun95\n[server: s1]\naddr=127.0.0.1:%d\ntype=public\n[allowed_keys]\nallow_all=1\n", pa); + wf(cb, "[global]\ntun_ip=10.97.0.2/24\ntun_ifname=tun94\n[server: s1]\naddr=127.0.0.1:%d\ntype=public\n[allowed_keys]\nallow_all=1\n", pb); + config_ensure_keys_and_node_id(ca); config_ensure_keys_and_node_id(cb); + return 0; +} +static void cleanup(void) { test_unlink(ca); test_unlink(cb); test_rmdir(tdir); } + +int main(void) { + debug_config_init(); debug_set_level(DEBUG_LEVEL_WARN); + utun_instance_set_tun_init_enabled(0); + if (setup() != 0) return 1; + ua = uasync_create(); + g_a = utun_instance_create(ua, ca); g_b = utun_instance_create(ua, cb); + if (!g_a || !g_b) { g_result = 2; goto done; } + nid_b = g_b->node_id; memcpy(pubkey_b, g_b->my_keys.public_key, SC_PUBKEY_SIZE); + fprintf(stderr, "B: 0x%016llx port=%d\n", (unsigned long long)nid_b, pb); fflush(stderr); + utun_instance_init(g_a); utun_instance_init(g_b); + if (inject_node(g_a, nid_b, pubkey_b, pb) != 0) { fail("inject_node"); goto done; } + fprintf(stderr, "injected B into A registry\n"); fflush(stderr); + uasync_call_soon(ua, NULL, t1); + g_ttimer = uasync_set_timeout(ua, TIMEOUT_TB, NULL, to_cb, "to"); + { uint64_t st = get_time_tb(); while (!g_result && (int)(get_time_tb() - st) < TIMEOUT_TB + 50000) uasync_poll(ua, POLL_MS); } + if (g_result == 0) g_result = 2; +done: + if (g_ttimer && ua) uasync_cancel_timeout(ua, g_ttimer); + for (int i = 0; i < 4; i++) if (gh[i]) node_conn_direct_close(gh[i]); + if (g_a) { g_a->running = 0; utun_instance_destroy(g_a); } + if (g_b) { g_b->running = 0; utun_instance_destroy(g_b); } + if (ua) uasync_destroy(ua, 0); + cleanup(); + fprintf(stderr, "\n%s\n", g_result == 1 ? "=== TEST PASSED ===" : "=== TEST FAILED ==="); fflush(stderr); + return (g_result == 1) ? 0 : 1; +} diff --git a/tests/test_route6_lib.c b/tests/test_route6_lib.c index cc49463e..684c4764 100644 --- a/tests/test_route6_lib.c +++ b/tests/test_route6_lib.c @@ -19,14 +19,14 @@ static int tests_run = 0; /* ──── helpers ──── */ -static struct TOPO_NODEQ *make_node(uint64_t node_id, +static struct TOPO_GROUP_NODE *make_node(uint64_t node_id, const uint8_t addrs[][16], const uint8_t *plens, int N) { - struct TOPO_NODEQ *nq = u_calloc(1, sizeof(*nq)); + struct TOPO_GROUP_NODE *nq = u_calloc(1, sizeof(*nq)); struct TOPO_NODE *ni = u_calloc(1, sizeof(struct TOPO_NODE)); if (!nq || !ni) { u_free(nq); u_free(ni); return NULL; } - ni->ref_count = 1; ni->node_id = node_id; - nq->node = ni; nq->hash_node_id = node_id; + ni->group_ref_count = 1; ni->node_id = node_id; + nq->node_id = node_id; if (N > 0) { struct TOPO_NODESUBNETS *r = u_calloc(1, sizeof(struct TOPO_NODESUBNETS)); nq->subnets = r; @@ -41,14 +41,13 @@ static struct TOPO_NODEQ *make_node(uint64_t node_id, return nq; } -static void free_node(struct TOPO_NODEQ *nq) { +static void free_node(struct TOPO_GROUP_NODE *nq) { if (nq) { if (nq->subnets) { struct TOPO_SUBNET6 *s = nq->subnets->v6_subnets; while (s) { struct TOPO_SUBNET6 *next = s->next; u_free(s); s = next; } u_free(nq->subnets); } - if (nq->node) u_free(nq->node); u_free(nq); } } @@ -69,7 +68,7 @@ static void test_insert_null_args(void) { assert(t != NULL); assert(route6_insert(NULL, NULL) == false); assert(route6_insert(t, NULL) == false); - assert(route6_insert(NULL, t ? (struct TOPO_NODEQ*)(uintptr_t)t : NULL) == false); + assert(route6_insert(NULL, t ? (struct TOPO_GROUP_NODE*)(uintptr_t)t : NULL) == false); route6_table_destroy(t); uasync_destroy(ua, 0); } @@ -81,7 +80,7 @@ static void test_insert_zero_subnets(void) { int addr[16] = {0}; uint8_t zero_plen = 0; - struct TOPO_NODEQ *nq = make_node(1, (const uint8_t(*)[16])addr, &zero_plen, 0); + struct TOPO_GROUP_NODE *nq = make_node(1, (const uint8_t(*)[16])addr, &zero_plen, 0); assert(route6_insert(t, nq) == false); const uint8_t any[16] = {0}; @@ -99,7 +98,7 @@ static void test_insert_duplicate(void) { uint8_t addr[16] = {0xfd, 0}; uint8_t plen = 64; - struct TOPO_NODEQ *nq = make_node(10, &addr, &plen, 1); + struct TOPO_GROUP_NODE *nq = make_node(10, &addr, &plen, 1); assert(route6_insert(t, nq) == true); assert(route6_insert(t, nq) == true); // re-insert should not crash @@ -117,7 +116,7 @@ static void test_insert_delete_reinsert(void) { uint8_t addrs[2][16] = {{0xfd, 1}, {0xfd, 2}}; uint8_t plens[2] = {64, 64}; - struct TOPO_NODEQ *nq = make_node(20, addrs, plens, 2); + struct TOPO_GROUP_NODE *nq = make_node(20, addrs, plens, 2); assert(route6_insert(t, nq) == true); assert(route6_lookup(t, addrs[0]) != NULL); @@ -146,8 +145,8 @@ static void test_delete_nonexistent(void) { uint8_t addr[16] = {0xfd, 10}; uint8_t plen = 64; - struct TOPO_NODEQ *nq1 = make_node(1, &addr, &plen, 1); - struct TOPO_NODEQ *nq2 = make_node(2, &addr, &plen, 1); + struct TOPO_GROUP_NODE *nq1 = make_node(1, &addr, &plen, 1); + struct TOPO_GROUP_NODE *nq2 = make_node(2, &addr, &plen, 1); assert(route6_insert(t, nq1) == true); route6_delete(t, nq2); // nq2 not in table — should not crash @@ -165,7 +164,7 @@ static void test_delete_null_args(void) { route6_delete(NULL, NULL); route6_delete(t, NULL); - route6_delete(NULL, (struct TOPO_NODEQ*)(uintptr_t)t); + route6_delete(NULL, (struct TOPO_GROUP_NODE*)(uintptr_t)t); route6_table_destroy(t); uasync_destroy(ua, 0); @@ -178,7 +177,7 @@ static void test_delete_multi_subnet(void) { uint8_t addrs[3][16] = {{0xfc, 1}, {0xfc, 2}, {0xfc, 3}}; uint8_t plens[3] = {48, 56, 64}; - struct TOPO_NODEQ *nq = make_node(30, addrs, plens, 3); + struct TOPO_GROUP_NODE *nq = make_node(30, addrs, plens, 3); assert(route6_insert(t, nq) == true); assert(route6_lookup(t, addrs[0]) != NULL); @@ -204,8 +203,8 @@ static void test_delete_one_of_two(void) { uint8_t addr_a[16] = {0xfc, 0}; uint8_t addr_b[16] = {0xfd, 0}; uint8_t plen = 64; - struct TOPO_NODEQ *nqa = make_node(100, &addr_a, &plen, 1); - struct TOPO_NODEQ *nqb = make_node(200, &addr_b, &plen, 1); + struct TOPO_GROUP_NODE *nqa = make_node(100, &addr_a, &plen, 1); + struct TOPO_GROUP_NODE *nqb = make_node(200, &addr_b, &plen, 1); assert(route6_insert(t, nqa) == true); assert(route6_insert(t, nqb) == true); @@ -240,7 +239,7 @@ static void test_lookup_returns_correct_data(void) { uint8_t addr[16] = {0xfe, 0x80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1}; uint8_t plen = 64; - struct TOPO_NODEQ *nq = make_node(42, &addr, &plen, 1); + struct TOPO_GROUP_NODE *nq = make_node(42, &addr, &plen, 1); assert(route6_insert(t, nq) == true); const struct ROUTE6_DATA *rd = route6_lookup(t, addr); @@ -301,7 +300,7 @@ static void test_stress(void) { struct saved_route sr[MAX_SAVED]; int nsr = 0; - struct TOPO_NODEQ *nodes[STRESS_NODES + STRESS_OPS]; + struct TOPO_GROUP_NODE *nodes[STRESS_NODES + STRESS_OPS]; // create nodes with random subnets int n_created = 0; @@ -333,7 +332,7 @@ static void test_stress(void) { if (rand() % 10 < 6 && n_created > 0) { // delete random active node int idx = rand() % n_created; - uint64_t nid = nodes[idx]->node->node_id; + uint64_t nid = nodes[idx]->node_id; route6_delete(t, nodes[idx]); // mark all routes of this node inactive for (int j = 0; j < nsr; j++) { @@ -392,7 +391,7 @@ static void test_stress(void) { { uint8_t addr[16] = {0xfc, 0}; uint8_t plen = 64; - struct TOPO_NODEQ *ghost = make_node(999999, &addr, &plen, 1); + struct TOPO_GROUP_NODE *ghost = make_node(999999, &addr, &plen, 1); route6_delete(t, ghost); // should no-op free_node(ghost); } @@ -401,7 +400,7 @@ static void test_stress(void) { { uint8_t addr[16] = {0}; uint8_t zero_plen = 0; - struct TOPO_NODEQ *z = make_node(777, (const uint8_t(*)[16])addr, &zero_plen, 0); + struct TOPO_GROUP_NODE *z = make_node(777, (const uint8_t(*)[16])addr, &zero_plen, 0); assert(route6_insert(t, z) == false); free_node(z); } diff --git a/tests/test_route_lib.c b/tests/test_route_lib.c index 77c4295e..78811ee4 100644 --- a/tests/test_route_lib.c +++ b/tests/test_route_lib.c @@ -2,7 +2,7 @@ * FULL REFACTORED TEST for new NODEINFO-based routing system * * Tests: - * - TOPO_NODEQ creation and insertion + * - TOPO_GROUP_NODE creation and insertion * - paths management (add/remove) * - versioning (ver field) * - WITHDRAW when no paths left @@ -40,17 +40,16 @@ struct UTUN_INSTANCE { struct TOPO_GROUP* group; }; -/* Helper to create test TOPO_NODEQ with subnet in linked list */ -static struct TOPO_NODEQ* create_test_node(uint64_t node_id, uint8_t ver, uint32_t subnet_net, uint8_t prefix) { - struct TOPO_NODEQ* nq = u_calloc(1, sizeof(struct TOPO_NODEQ)); +/* Helper to create test TOPO_GROUP_NODE with subnet in linked list */ +static struct TOPO_GROUP_NODE* create_test_node(uint64_t node_id, uint8_t ver, uint32_t subnet_net, uint8_t prefix) { + struct TOPO_GROUP_NODE* nq = u_calloc(1, sizeof(struct TOPO_GROUP_NODE)); if (!nq) return NULL; struct TOPO_NODE* ni = u_calloc(1, sizeof(struct TOPO_NODE)); if (!ni) { u_free(nq); return NULL; } - ni->ref_count = 1; + ni->group_ref_count = 1; ni->node_id = htobe64(node_id); ni->ver = ver; - nq->node = ni; - nq->hash_node_id = ni->node_id; + nq->node_id = ni->node_id; nq->hop_count = 0; nq->paths = queue_new(NULL, 16, 0, 0, "test_paths"); struct TOPO_NODESUBNETS* r = u_calloc(1, sizeof(struct TOPO_NODESUBNETS)); @@ -64,11 +63,10 @@ static struct TOPO_NODEQ* create_test_node(uint64_t node_id, uint8_t ver, uint32 return nq; } -static void free_test_node(struct TOPO_NODEQ* nq) { +static void free_test_node(struct TOPO_GROUP_NODE* nq) { if (nq) { if (nq->paths) queue_free(nq->paths); if (nq->subnets) { u_free(nq->subnets->v4_subnets); u_free(nq->subnets); } - if (nq->node) { u_free(nq->node->node_name); u_free(nq->node); } u_free(nq); } } @@ -118,9 +116,9 @@ static void test_table_create_destroy(void) { } static void test_nodeinfo_insert(void) { - TEST("TOPO_NODEQ insert into routing table"); + TEST("TOPO_GROUP_NODE insert into routing table"); struct ROUTE_TABLE *t = route_table_create(); - struct TOPO_NODEQ* nq = create_test_node(0x12345678ULL, 1, 0x0a000000, 8); + struct TOPO_GROUP_NODE* nq = create_test_node(0x12345678ULL, 1, 0x0a000000, 8); bool inserted = route_insert(t, nq); ASSERT(inserted, "insert nodeinfo"); @@ -134,8 +132,8 @@ static void test_nodeinfo_insert(void) { static void test_versioning(void) { TEST("NODEINFO multiple nodes"); struct ROUTE_TABLE *t = route_table_create(); - struct TOPO_NODEQ* nq1 = create_test_node(0x1111ULL, 5, 0x0a000000, 8); - struct TOPO_NODEQ* nq2 = create_test_node(0x2222ULL, 1, 0xac100000, 12); + struct TOPO_GROUP_NODE* nq1 = create_test_node(0x1111ULL, 5, 0x0a000000, 8); + struct TOPO_GROUP_NODE* nq2 = create_test_node(0x2222ULL, 1, 0xac100000, 12); route_insert(t, nq1); bool ins2 = route_insert(t, nq2); @@ -157,7 +155,7 @@ static void test_performance_1000_nodes(void) { for (int i = 0; i < 1000; i++) { uint32_t sub = 0x0a000000 + (i * 0x100); /* distinct 10.0.x.0/24 to avoid overlap */ - struct TOPO_NODEQ* nq = create_test_node(0x1000000ULL + i, 1, sub, 24); + struct TOPO_GROUP_NODE* nq = create_test_node(0x1000000ULL + i, 1, sub, 24); route_insert(t, nq); /* note: nq not freed here (table holds ref to it), freed in test cleanup if needed */ } @@ -175,7 +173,7 @@ static void test_performance_1000_nodes(void) { static void test_destroy_refcount(void) { TEST("destroy with multiple paths (ref_count safety)"); struct ROUTE_TABLE *t = route_table_create(); - /* TOPO_NODEQ memory managed externally, table only holds pointers */ + /* TOPO_GROUP_NODE memory managed externally, table only holds pointers */ route_table_destroy(t); PASS(); } diff --git a/tests/test_route_ping.c b/tests/test_route_ping.c index 8d80b6a3..0b1e3706 100644 --- a/tests/test_route_ping.c +++ b/tests/test_route_ping.c @@ -268,9 +268,10 @@ int main(void) { /* addr[] в network byte order, port в host byte order */ uint32_t target_ip = 0; uint16_t target_port = 0; - struct TOPO_NODEQ* nq = topo_groups_get_default(inst_b->topo_groups) ? topo_node_find_by_id(topo_groups_get_default(inst_b->topo_groups), g_node_id_c) : NULL; - if (nq && nq->node) { - const struct TOPO_ADDR4* a = nq->node->v4_addrs; + struct TOPO_GROUP_NODE* nq = topo_groups_get_default(inst_b->topo_groups) ? topo_node_find_by_id(topo_groups_get_default(inst_b->topo_groups), g_node_id_c) : NULL; + struct TOPO_NODE* ni = nq ? topo_node_registry_find(inst_b->topo_groups, nq->node_id) : NULL; + if (nq && ni) { + const struct TOPO_ADDR4* a = ni->v4_addrs; if (a) { memcpy(&target_ip, a->addr, 4); target_port = a->port; @@ -290,7 +291,7 @@ int main(void) { 200, // timeout_ms 5000,// wait_timeout_ms ping_resp_cb, NULL, - nq ? nq->node->public_key : NULL); + ni ? ni->public_key : NULL); if (ret != 0) { DEBUG_ERROR(DEBUG_CATEGORY_BGP, "route_ping_send_req_addr failed: %d", ret); goto cleanup; diff --git a/tests/test_stcp_traffic.c b/tests/test_stcp_traffic.c index 9b1af0ad..5dda7e43 100644 --- a/tests/test_stcp_traffic.c +++ b/tests/test_stcp_traffic.c @@ -167,8 +167,17 @@ int main(void) { TASSERT(etcp_bind(srv_inst, 1, srv_recv_cb) >= 0); TASSERT(etcp_bind(cli_inst, 2, cli_recv_cb) >= 0); - struct TOPO_NODEQ *srv_node = topo_groups_get_default(srv_inst->topo_groups) ? topo_groups_get_default(srv_inst->topo_groups)->local_node : NULL; + struct TOPO_GROUP_NODE *srv_node = topo_groups_get_default(srv_inst->topo_groups) ? topo_groups_get_default(srv_inst->topo_groups)->local_node : NULL; TASSERT(srv_node); + { struct TOPO_NODE* srv_ni = topo_node_registry_find(srv_inst->topo_groups, srv_node->node_id); TASSERT(srv_ni); + struct TOPO_NODE* cli_ni = u_calloc(1, sizeof(struct TOPO_NODE)); + TASSERT(cli_ni); memcpy(cli_ni, srv_ni, sizeof(struct TOPO_NODE)); cli_ni->group_ref_count = 0; + cli_ni->v4_sock_meta = NULL; cli_ni->v4_addrs = NULL; cli_ni->v6_sock_meta = NULL; cli_ni->v6_addrs = NULL; + struct TOPO_ADDR4* a = memory_pool_alloc(cli_inst->topo_groups->v4_addr_pool); + TASSERT(a); a->addr[0]=127; a->addr[1]=0; a->addr[2]=0; a->addr[3]=1; a->port = port; + a->type = TOPO_ADDR_INTERFACE; a->socket_id = 0; a->protocol = TOPO_PROTO_TCP; + a->next = cli_ni->v4_addrs; cli_ni->v4_addrs = a; + topo_node_registry_store(cli_inst->topo_groups, cli_ni); } // Increase timeout for TCP STCP handshake cli_inst->etcp_connect_timeout_tb = 100000; // 10 seconds TASSERT(etcp_connect(cli_inst, srv_node, connect_cb, NULL, ETCP_CONNECT_EARLY | ETCP_CONNECT_LATE) == 0);