Browse Source

fix: memory pool alloc mismatch in tests, db_sync SEND_DATA buffer overflow

- 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)
topo_upd
evgeny 2 months ago
parent
commit
d310bccf08
  1. 2
      src/Makefile.am
  2. 12
      src/chat/chat_sync.c
  3. 4
      src/chat/db_sync.c
  4. 14
      src/control_server.c
  5. 4
      src/control_server.h
  6. 5
      src/media_delivery/media_delivery.c
  7. 6
      src/ntp_node_time.c
  8. 334
      src/routing_layer/conn_mgr.c
  9. 121
      src/routing_layer/conn_mgr.h
  10. 12
      src/routing_layer/etcp_router.c
  11. 30
      src/routing_layer/nat_detection.c
  12. 8
      src/routing_layer/route6_lib.c
  13. 6
      src/routing_layer/route6_lib.h
  14. 38
      src/routing_layer/route_connectivity.c
  15. 4
      src/routing_layer/route_connectivity.h
  16. 10
      src/routing_layer/route_lib.c
  17. 8
      src/routing_layer/route_lib.h
  18. 2
      src/routing_layer/route_ping.c
  19. 9
      src/routing_layer/routing.c
  20. 97
      src/routing_layer/topo_group.c
  21. 8
      src/routing_layer/topo_group.h
  22. 4
      src/routing_layer/topo_group_connect.c
  23. 98
      src/routing_layer/topo_node.c
  24. 9
      src/routing_layer/topo_node_sqlite.c
  25. 6
      src/routing_layer/topo_node_sqlite.h
  26. 45
      src/routing_layer/topo_recovery.c
  27. 5
      src/routing_layer/topo_recovery.h
  28. 38
      src/transport_layer/etcp.c
  29. 19
      src/transport_layer/etcp.h
  30. 45
      src/transport_layer/etcp_api.c
  31. 52
      src/transport_layer/etcp_api.h
  32. 63
      src/transport_layer/etcp_connect.c
  33. 8
      src/transport_layer/etcp_connect.h
  34. 26
      src/transport_layer/etcp_connections.c
  35. 670
      src/transport_layer/node_conn_direct.c
  36. 67
      src/transport_layer/node_conn_direct.h
  37. 2
      src/utun_instance.h
  38. 5
      tests/Makefile.am
  39. 2
      tests/test_bgp_route_exchange.c
  40. 8
      tests/test_bgp_triangle.c
  41. 4
      tests/test_conn_mgr.c
  42. 7
      tests/test_conn_mgr_already_connected.c
  43. 27
      tests/test_etcp_connect.c
  44. 24
      tests/test_etcp_router_reconnect.c
  45. 12
      tests/test_etcp_router_unit.c
  46. 29
      tests/test_ipv6_sockets.c
  47. 26
      tests/test_nat_detection.c
  48. 251
      tests/test_node_conn_direct.c
  49. 41
      tests/test_route6_lib.c
  50. 28
      tests/test_route_lib.c
  51. 9
      tests/test_route_ping.c
  52. 11
      tests/test_stcp_traffic.c

2
src/Makefile.am

@ -41,6 +41,7 @@ utun_CORE_SOURCES = \
transport_layer/packet_dump.c \ transport_layer/packet_dump.c \
transport_layer/etcp_api.c \ transport_layer/etcp_api.c \
transport_layer/etcp_connect.c \ transport_layer/etcp_connect.c \
transport_layer/node_conn_direct.c \
control_server.c \ control_server.c \
firewall.c \ firewall.c \
eim_nat.c \ eim_nat.c \
@ -112,6 +113,7 @@ libutun_a_SOURCES = \
transport_layer/packet_dump.c \ transport_layer/packet_dump.c \
transport_layer/etcp_api.c \ transport_layer/etcp_api.c \
transport_layer/etcp_connect.c \ transport_layer/etcp_connect.c \
transport_layer/node_conn_direct.c \
control_server.c \ control_server.c \
firewall.c \ firewall.c \
eim_nat.c \ eim_nat.c \

12
src/chat/chat_sync.c

@ -415,7 +415,7 @@ static void cs_start_channel_join(struct chat_sync* cs, uint64_t peer) {
/* ── Connection callbacks ── */ /* ── 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; (void)arg;
if (!conn || !g_cs) return; if (!conn || !g_cs) return;
uint64_t peer = conn->peer_node_id; uint64_t peer = conn->peer_node_id;
@ -438,7 +438,7 @@ static void cs_on_conn_up(struct ETCP_CONN* conn, void* arg) {
cs_on_peer_status_changed(peer, 1); 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; (void)arg;
if (!conn || !g_cs) return; if (!conn || !g_cs) return;
uint64_t peer = conn->peer_node_id; uint64_t peer = conn->peer_node_id;
@ -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) { static void cs_on_conn_status(struct ETCP_CONN* conn, int status, void* arg) {
switch (status) { switch (status) {
case ETCP_CONN_STATUS_UP: cs_on_conn_up(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, arg); break; case ETCP_CONN_STATUS_DOWN: cs_on_conn_down(conn, ETCP_CBK_EVENT_DOWN, arg); break;
default: break; default: break;
} }
} }
@ -478,8 +478,8 @@ static void cs_on_new_conn(struct ETCP_CONN* conn, void* arg) {
(void)arg; (void)arg;
if (!conn) return; if (!conn) return;
DEBUG_TRACE(DEBUG_CATEGORY_DB_SYNC, "%s: new_conn conn=%p", CS_ID, (void*)conn); 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_cbk(conn, cs_on_conn_up, NULL, ETCP_CBK_EVENT_UP);
etcp_conn_add_down_cbk(conn, cs_on_conn_down, NULL); etcp_conn_add_cbk(conn, cs_on_conn_down, NULL, ETCP_CBK_EVENT_DOWN);
} }
#endif #endif

4
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) { } else if (mc < pc) {
// Request tail // Request tail
uint32_t vp_send = mc > 0 ? mc - 1 : (uint32_t)-1; 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 + 1, &mc, 4); uint16_t z = 0; memcpy(req + 5, &z, 2);
memcpy(req + 7, &vp_send, 4); memcpy(req + 11, &mc, 4); 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; 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", 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); SI_SHRT(si), (unsigned long long)(src >> 16), mc, pc, mc);

14
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 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 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 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); 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) * 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) { static void send_single_node_info(struct control_server* server, struct control_client* client, struct TOPO_GROUP_NODE* nq) {
if (!server || !nq || !nq->node) return; 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]; 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; } 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; buf[0] = CTRL_BGP_CMD; buf[1] = CTRL_BGP_SUBCMD;
size_t data_size = 2 + (size_t)ser_len; 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 */ /* Send all known remote nodes */
struct ll_entry* e = group->nodes ? group->nodes->head : NULL; struct ll_entry* e = group->nodes ? group->nodes->head : NULL;
while (e) { 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); send_single_node_info(server, client, nq);
e = e->next; 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); 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; if (!server || !node) return;
struct control_client* client = server->clients; struct control_client* client = server->clients;
while (client) { while (client) {

4
src/control_server.h

@ -19,7 +19,7 @@ extern "C" {
/* Forward declarations */ /* Forward declarations */
struct UTUN_INSTANCE; struct UTUN_INSTANCE;
struct UASYNC; struct UASYNC;
struct TOPO_NODEQ; struct TOPO_GROUP_NODE;
struct ll_queue; struct ll_queue;
/* Forward declaration */ /* 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); 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); void control_server_notify_node_removed(struct control_server* server, uint64_t node_id);
#ifdef __cplusplus #ifdef __cplusplus

5
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 */ /* check if this node is a supernode */
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 || !nq->node) return; 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 */ /* for CHAT groups, check node_type via DB */
if (group->group_type == TOPO_GROUP_TYPE_CHAT && group->channel_id[0]) { if (group->group_type == TOPO_GROUP_TYPE_CHAT && group->channel_id[0]) {

6
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 #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_on_new_conn(struct ETCP_CONN* conn, void* arg);
static void ntp_node_recv_cb(struct ETCP_CONN* conn, struct ll_entry* entry); 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; struct UTUN_INSTANCE* inst = (struct UTUN_INSTANCE*)arg;
if (!inst || !conn) return; if (!inst || !conn) return;
@ -117,7 +117,7 @@ static void ntp_node_on_new_conn(struct ETCP_CONN* conn, void* arg) {
if (!conn) return; if (!conn) return;
struct UTUN_INSTANCE* inst = (struct UTUN_INSTANCE*)arg; struct UTUN_INSTANCE* inst = (struct UTUN_INSTANCE*)arg;
if (!inst) return; 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) { static void ntp_node_recv_cb(struct ETCP_CONN* conn, struct ll_entry* entry) {

334
src/routing_layer/conn_mgr.c

@ -24,6 +24,7 @@
#include "route_ping.h" #include "route_ping.h"
#include "route_connectivity.h" #include "route_connectivity.h"
#include "conn_mgr.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 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); 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_deliver_result(struct CONN_MGR_ENTRY* entry, int result);
static void cm_bg_ping_timer_cb(void* arg); 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 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 uint16_t cm_get_node_min_rtt(struct TOPO_GROUP_NODE* nq);
static int cm_has_direct_ip(struct TOPO_NODEQ* nq); static int cm_has_direct_ip(struct CONN_MGR* mgr, struct TOPO_GROUP_NODE* nq);
static int cm_has_local_addr(struct TOPO_NODEQ* 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_entry_destroy(struct CONN_MGR_ENTRY* entry);
static void cm_exchange_probe_retry_cb(void* arg); static void cm_exchange_probe_retry_cb(void* arg);
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);
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);
static void cm_clear_nodeinfo(struct CONN_MGR* mgr, uint64_t node_id); 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 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_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_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_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_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_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_direct_init_cb(struct ETCP_CONN* conn, int event, void* arg);
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);
static void cm_reverse_timeout_cb(void* arg); static void cm_reverse_timeout_cb(void* arg);
static void cm_exchange_timeout_cb(void* arg); static void cm_exchange_timeout_cb(void* arg);
static void cm_candidate_ping_timer_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_overall_timeout(void* arg);
static void cm_invite_fail(struct cm_invite_pending* inv, int result); static void cm_invite_fail(struct cm_invite_pending* inv, int result);
static void cm_invite_cleanup(struct cm_invite_pending* inv); static void cm_invite_cleanup(struct cm_invite_pending* inv);
@ -101,7 +114,7 @@ struct cm_invite_pending {
void* overall_timer; void* overall_timer;
conn_mgr_connect_callback_t cb; conn_mgr_connect_callback_t cb;
void* cb_arg; void* cb_arg;
struct TOPO_NODEQ* temp_nq; struct TOPO_GROUP_NODE* temp_nq;
}; };
struct CONN_MGR* conn_mgr_init(struct TOPO_GROUP* group) { 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) { static void cm_deliver_result(struct CONN_MGR_ENTRY* entry, int result) {
struct cm_cb_node* node = entry->cb_list; struct CONN_MGR_HANDLE* h = entry->handles;
entry->cb_list = NULL; entry->handles = NULL;
int delivered = 0; while (h) {
while (node) { struct CONN_MGR_HANDLE* next = h->next;
struct cm_cb_node* next = node->next; if (h->result_cb) h->result_cb(result, entry->node_id, h->result_arg);
if (!node->cancelled) { node->cb(result, entry->node_id, node->arg); delivered++; } h->nd_handle = NULL; h->entry = NULL;
u_free(node); u_free(h);
node = next; h = next;
} }
if (delivered == 0 && entry->state == CONN_MGR_STATE_CONNECTING) if (entry->state == CONN_MGR_STATE_CONNECTING)
cm_clear_nodeinfo(entry->mgr, entry->node_id); cm_clear_nodeinfo(entry->mgr, entry->node_id);
} }
static void cm_update_nodeinfo(struct CONN_MGR_ENTRY* entry) { static void cm_update_nodeinfo(struct CONN_MGR_ENTRY* entry) {
struct TOPO_GROUP* group = entry->mgr->group; struct TOPO_GROUP* group = entry->mgr->group;
if (!group) return; 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; if (!nq) return;
nq->conn_mgr_type = entry->conn_type; nq->conn_mgr_type = entry->conn_type;
memcpy(nq->conn_mgr_intermediaries, entry->intermediaries, sizeof(entry->intermediaries)); 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) { 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; if (!nq) return;
nq->conn_mgr_type = CONN_TYPE_NONE; nq->conn_mgr_type = CONN_TYPE_NONE;
nq->conn_mgr_intermediariy_count = 0; 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) { static void cm_cleanup_db_node(struct CONN_MGR_ENTRY* entry) {
if (!entry->db_loaded) return; if (!entry->db_loaded) return;
struct TOPO_GROUP* group = entry->mgr->group; 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) return;
if (nq->paths && queue_entry_count(nq->paths) > 0) { 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); 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); 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); 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); 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) { 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; struct TOPO_GROUP* group = mgr->group;
if (!group) { DEBUG_ERROR(DEBUG_CATEGORY_GENERAL, "BGP not initialized"); return CONN_MGR_ERR_INTERNAL; } 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_NODEQ* target = topo_node_find_by_id(group, node_id); struct TOPO_GROUP_NODE* target = topo_node_find_by_id(group, node_id);
int loaded_from_db = 0; int loaded_from_db = 0;
if (!target && mgr->instance->topo_sqlite_db && mgr->instance->topo_groups) { 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); struct TOPO_NODE* ni = topo_node_sqlite_node_load(mgr->instance->topo_sqlite_db, mgr->instance->topo_groups, node_id);
if (ni) { if (ni) {
ni = topo_node_registry_acquire(mgr->instance->topo_groups, ni); ni = topo_node_registry_store(mgr->instance->topo_groups, ni);
struct ll_entry* qe = queue_entry_new(sizeof(struct TOPO_NODEQ)); struct ll_entry* qe = queue_entry_new(sizeof(struct TOPO_GROUP_NODE));
if (qe) { 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)); 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; target->conn_mgr_type = CONN_TYPE_NONE;
queue_data_put_with_index(group->nodes, &target->ll); queue_data_put_with_index(group->nodes, &target->ll);
loaded_from_db = 1; loaded_from_db = 1;
DEBUG_INFO(DEBUG_CATEGORY_GENERAL, "conn_mgr: node 0x%016llx loaded from DB", (unsigned long long)node_id); 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_ENTRY* entry = cm_find_entry(mgr, node_id);
struct CONN_MGR_HANDLE* h = NULL;
if (entry && entry->state == CONN_MGR_STATE_CONNECTED) { if (entry && entry->state == CONN_MGR_STATE_CONNECTED) {
if (cb) { h = cm_handle_new(entry, cb, cb_arg);
struct cm_cb_node* cn = u_calloc(1, sizeof(struct cm_cb_node)); if (cb) cb(CONN_MGR_OK, node_id, cb_arg);
if (cn) { cn->cb = cb; cn->arg = cb_arg; entry->cb_list = cn; } return h;
cb(CONN_MGR_OK, node_id, cb_arg);
}
return CONN_MGR_OK;
} }
if (entry && entry->state == CONN_MGR_STATE_CONNECTING) { 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); DEBUG_WARN(DEBUG_CATEGORY_GENERAL, "node 0x%016llx already connecting, adding handle", (unsigned long long)node_id);
struct cm_cb_node* cn = u_calloc(1, sizeof(struct cm_cb_node)); return cm_handle_new(entry, cb, cb_arg);
if (cn) { cn->cb = cb; cn->arg = cb_arg; cn->next = entry->cb_list; entry->cb_list = cn; }
return CONN_MGR_OK;
} }
struct ETCP_CONN* existing = topo_group_find_conn_for_node(group, node_id); struct ETCP_CONN* existing = topo_group_find_conn_for_node(group, node_id);
if (!existing) existing = instance_find_conn(mgr->instance, 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; } while (l) { if (l->link_state == 3 && l->initialized) break; l = l->next; }
if (l) { if (l) {
entry = cm_ensure_entry(mgr, node_id); 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->state = CONN_MGR_STATE_CONNECTED;
entry->conn_type = CONN_TYPE_DIRECT; entry->conn_type = CONN_TYPE_DIRECT;
entry->idle_timeout_ms = idle_timeout_ms; entry->idle_timeout_ms = idle_timeout_ms;
entry->last_traffic_tb = get_time_tb(); entry->last_traffic_tb = get_time_tb();
{ struct cm_cb_node* cn = u_calloc(1, sizeof(struct cm_cb_node)); h = cm_handle_new(entry, cb, cb_arg);
if (cn) { cn->cb = cb; cn->arg = cb_arg; entry->cb_list = cn; } }
cm_update_nodeinfo(entry); cm_update_nodeinfo(entry);
cm_deliver_result(entry, CONN_MGR_OK); cm_deliver_result(entry, CONN_MGR_OK);
return CONN_MGR_OK; return h;
} }
} }
entry = cm_ensure_entry(mgr, node_id); 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; entry->state = CONN_MGR_STATE_CONNECTING;
{ struct cm_cb_node* cn = u_calloc(1, sizeof(struct cm_cb_node)); h = cm_handle_new(entry, cb, cb_arg);
if (cn) { cn->cb = cb; cn->arg = cb_arg; cn->next = entry->cb_list; entry->cb_list = cn; } }
entry->idle_timeout_ms = idle_timeout_ms; entry->idle_timeout_ms = idle_timeout_ms;
entry->last_traffic_tb = get_time_tb(); entry->last_traffic_tb = get_time_tb();
entry->local_scan_state = CM_TRY_NONE; 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.timer = NULL;
entry->main.request_id = 0; entry->main.request_id = 0;
entry->db_loaded = (uint8_t)loaded_from_db; entry->db_loaded = (uint8_t)loaded_from_db;
int has_our_local = cm_has_local_addr(group->local_node); int has_our_local = cm_has_local_addr(mgr, group->local_node);
int has_target_local = cm_has_local_addr(target); 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); } 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; entry->main_connect_state = CM_TRY_PENDING;
cm_start_phase_direct(entry); cm_start_phase_direct(entry);
return CONN_MGR_OK; return h;
} }
int conn_mgr_disconnect_node(struct CONN_MGR* mgr, uint64_t node_id) { 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, 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; if (!mgr) return CONN_MGR_ERR_INTERNAL;
struct CONN_MGR_ENTRY* entry = cm_find_entry(mgr, node_id); struct CONN_MGR_ENTRY* entry = cm_find_entry(mgr, node_id);
if (!entry) return CONN_MGR_ERR_NOT_FOUND; if (!entry) return CONN_MGR_ERR_NOT_FOUND;
int cb_count = 0; int cb_count = 0;
struct cm_cb_node* target = NULL; for (struct CONN_MGR_HANDLE* n = entry->handles; n; n = n->next) cb_count++;
for (struct cm_cb_node* n = entry->cb_list; n; n = n->next) { if (cb_count == 0) return CONN_MGR_ERR_NOT_FOUND;
cb_count++;
if (n->cb == cb && n->arg == cb_arg) target = n; 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); cm_entry_destroy(entry);
u_free(target);
entry->cb_list = NULL;
cm_clear_nodeinfo(mgr, node_id); 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); struct CONN_MGR_HANDLE* rem = h ? entry->handles : NULL;
} else { while (rem) { struct CONN_MGR_HANDLE* nx = rem->next; rem->nd_handle = NULL; u_free(rem); rem = nx; }
target->cancelled = 1; entry->handles = NULL;
DEBUG_INFO(DEBUG_CATEGORY_GENERAL, "conn_mgr: cancel cb for 0x%016llx (cbs=%d)", (unsigned long long)node_id, cb_count);
} }
return CONN_MGR_OK; 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) { 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; if (!mgr) return CONN_MGR_ERR_INTERNAL;
struct CONN_MGR_ENTRY* entry = cm_find_entry(mgr, node_id); 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; 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; uint16_t rtt = 0xFFFF;
if (nq->connectivity.interface_status == PROBE_RESULT_REACHABLE && nq->connectivity.interface_min_rtt < rtt) if (nq->connectivity.interface_status == PROBE_RESULT_REACHABLE && nq->connectivity.interface_min_rtt < rtt)
rtt = nq->connectivity.interface_min_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; 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; uint64_t t = nq->connectivity.interface_probe_time;
if (nq->connectivity.nat_probe_time > t) t = nq->connectivity.nat_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; if (nq->connectivity.real_probe_time > t) t = nq->connectivity.real_probe_time;
return t; 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); uint64_t last = cm_get_node_max_probe_time(nq);
return (last > 0 && (now_tb - last) < (uint64_t)(CONN_MGR_CANDIDATE_CACHE_MS * 10)); 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); 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) { static int cm_has_direct_ip(struct CONN_MGR* mgr, struct TOPO_GROUP_NODE* nq) {
if (!nq || !nq->node) return 0; if (!nq) return 0;
for (const struct TOPO_ADDR4* a = nq->node->v4_addrs; a; a = a->next) { 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) { 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 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)) || m->nat_type == NAT_VERIFIED_EIM || m->nat_type == NAT_VERIFIED_DIRECT))
return 1; 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; 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 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)) || m->nat_type == NAT_VERIFIED_EIM || m->nat_type == NAT_VERIFIED_DIRECT))
return 1; return 1;
@ -518,21 +541,23 @@ static int cm_has_direct_ip(struct TOPO_NODEQ* nq) {
return 0; return 0;
} }
static int cm_has_local_addr(struct TOPO_NODEQ* nq) { static int cm_has_local_addr(struct CONN_MGR* mgr, struct TOPO_GROUP_NODE* nq) {
if (!nq || !nq->node) return 0; if (!nq) return 0;
for (const struct TOPO_ADDR4* a = nq->node->v4_addrs; a; a = a->next) { 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) { 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)) if (m->id == a->socket_id && (m->config_type == CFG_SERVER_TYPE_PRIVATE || m->config_type == CFG_SERVER_TYPE_LOCAL))
return 1; 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; if (a6->type != TOPO_ADDR_INTERFACE) continue;
uint8_t cls = cm_classify_v6_addr(a6->addr); uint8_t cls = cm_classify_v6_addr(a6->addr);
if (cls != CM_V6_LINK_LOCAL && cls != CM_V6_LOCAL) continue; 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)) if (m->id == a6->socket_id && (m->config_type == CFG_SERVER_TYPE_PRIVATE || m->config_type == CFG_SERVER_TYPE_LOCAL))
return 1; return 1;
} }
@ -553,11 +578,12 @@ struct cm_ping_ctx {
static void cm_start_local_scan(struct CONN_MGR_ENTRY* entry) { static void cm_start_local_scan(struct CONN_MGR_ENTRY* entry) {
struct TOPO_GROUP* group = entry->mgr->group; struct TOPO_GROUP* group = entry->mgr->group;
struct TOPO_NODEQ* target = topo_node_find_by_id(group, entry->node_id); struct TOPO_GROUP_NODE* target = topo_node_find_by_id(group, entry->node_id);
if (!target || !target->node) { entry->local_scan_state = CM_TRY_FAILED; return; } struct TOPO_NODE* ni = target ? topo_node_registry_find(entry->mgr->instance->topo_groups, target->node_id) : NULL;
for (const struct TOPO_ADDR4* a = target->node->v4_addrs; a; a = a->next) { 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; 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; 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; struct ETCP_SOCKET* s = entry->mgr->instance->etcp_sockets;
while (s) { 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 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; 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; 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; return;
} }
s = s->next; 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; 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); 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) conn = instance_find_conn(entry->mgr->instance, entry->node_id);
if (conn) { 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", 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); (unsigned long long)conn->peer_node_id, (unsigned long long)entry->node_id);
} }
conn = etcp_connection_create(entry->mgr->instance, NULL); conn = etcp_connection_create(entry->mgr->instance, NULL);
if (!conn) return NULL; if (!conn) return NULL;
sc_init_ctx(&conn->crypto_ctx, &entry->mgr->instance->my_keys); 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; conn->peer_node_id = entry->node_id;
etcp_update_log_name(conn); 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; 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) { 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; 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_GROUP* group = entry->mgr->group;
struct TOPO_NODEQ* target = topo_node_find_by_id(group, entry->node_id); struct TOPO_GROUP_NODE* 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_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); struct ETCP_CONN* conn = cm_get_or_create_conn(entry, target);
if (!conn) { cm_deliver_result(entry, CONN_MGR_ERR_INTERNAL); return; } 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"; const char* timer_name = entry->db_loaded ? "conn_mgr_db_direct" : "conn_mgr_direct";
if (entry->db_loaded) { 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; if (!(a->protocol & TOPO_PROTO_UDP)) continue;
struct ETCP_SOCKET* s = entry->mgr->instance->etcp_sockets; struct ETCP_SOCKET* s = entry->mgr->instance->etcp_sockets;
while (s) { while (s) {
@ -629,7 +657,7 @@ static void cm_start_phase_direct(struct CONN_MGR_ENTRY* entry) {
s = s->next; 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; if (!(a6->protocol & TOPO_PROTO_UDP)) continue;
uint8_t tclass = cm_classify_v6_addr(a6->addr); if (tclass == CM_V6_OTHER) 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; 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 }; uint8_t priority_order[] = { TOPO_ADDR_NAT, TOPO_ADDR_INTERFACE };
for (int pri = 0; pri < 2; pri++) { 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; 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; if (m->id != a->socket_id) continue;
struct ETCP_SOCKET* s = entry->mgr->instance->etcp_sockets; struct ETCP_SOCKET* s = entry->mgr->instance->etcp_sockets;
while (s) { 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; if (a6->type != priority_order[pri]) continue;
uint8_t tclass = cm_classify_v6_addr(a6->addr); if (tclass == CM_V6_OTHER) 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; 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; return;
} }
etcp_connection_close(conn); etcp_connection_close(conn);
int has_our = cm_has_direct_ip(group->local_node); int has_our = cm_has_direct_ip(entry->mgr, group->local_node);
int has_target = cm_has_direct_ip(target); int has_target = cm_has_direct_ip(entry->mgr, target);
if (has_our && !has_target) { cm_start_phase_reverse(entry); return; } if (has_our && !has_target) { cm_start_phase_reverse(entry); return; }
cm_start_phase_indirect(entry); cm_start_phase_indirect(entry);
} }
@ -692,9 +720,9 @@ static void cm_direct_timeout_cb(void* arg) {
return; return;
} }
struct TOPO_GROUP* group = entry->mgr->group; struct TOPO_GROUP* group = entry->mgr->group;
struct TOPO_NODEQ* target = topo_node_find_by_id(group, entry->node_id); struct TOPO_GROUP_NODE* target = topo_node_find_by_id(group, entry->node_id);
int has_our = cm_has_direct_ip(group->local_node); int has_our = cm_has_direct_ip(entry->mgr, group->local_node);
int has_target = target ? cm_has_direct_ip(target) : 0; int has_target = target ? cm_has_direct_ip(entry->mgr, target) : 0;
if (entry->local_scan_state != CM_TRY_OK) { if (entry->local_scan_state != CM_TRY_OK) {
if (has_our && !has_target) { cm_start_phase_reverse(entry); return; } if (has_our && !has_target) { cm_start_phase_reverse(entry); return; }
cm_start_phase_indirect(entry); 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); 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; struct CONN_MGR_ENTRY* entry = (struct CONN_MGR_ENTRY*)arg;
if (!conn || conn->peer_node_id != entry->node_id) { 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", 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++; ctx->attempt++;
if (ctx->attempt < CONN_MGR_LOCAL_SCAN_ATTEMPTS && ctx->phase == 0) { 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, 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); &ctx->addr, CONN_MGR_LOCAL_SCAN_TIMEOUT_MS, cm_ping_cb, ctx, NULL, 0, 0);
return; 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) { static void cm_start_phase_reverse(struct CONN_MGR_ENTRY* entry) {
struct TOPO_GROUP* group = entry->mgr->group; struct TOPO_GROUP* group = entry->mgr->group;
struct TOPO_NODEQ* local = group->local_node; struct TOPO_GROUP_NODE* local = group->local_node;
if (!local || !local->node) { cm_deliver_result(entry, CONN_MGR_ERR_INTERNAL); return; } 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; uint32_t req_id = ++entry->mgr->next_request_id;
entry->main.request_id = req_id; entry->main.phase = 2; entry->main.request_id = req_id; entry->main.phase = 2;
uint8_t direct_count = 0; uint8_t direct_count = 0;
struct { uint8_t type; uint8_t ip[4]; uint16_t port; uint8_t socket_id; } out_addrs[8]; 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; 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 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)) { || 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].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); 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 cm_reverse_pending* rp = (struct cm_reverse_pending*)arg;
struct CONN_MGR_ENTRY* entry = rp->entry; struct CONN_MGR_ENTRY* entry = rp->entry;
if (!conn || conn->peer_node_id != entry->node_id) { u_free(rp); return; } 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++) { for (uint8_t i = 0; i < resp->my_count && count < 8; i++) {
uint64_t id = resp->my_candidates[i].node_id; 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; uint16_t our_rtt = nq ? cm_get_node_min_rtt(nq) : 0;
if (our_rtt == 0xFFFF) our_rtt = 0; if (our_rtt == 0xFFFF) our_rtt = 0;
all[count].node_id = id; 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++) { for (uint8_t i = 0; i < sel; i++) {
pkt.selected[i].node_id = all[i].node_id; pkt.selected[i].node_id = all[i].node_id;
pkt.selected[i].rtt = 0; 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); if (nq) pkt.selected[i].rtt = cm_get_node_min_rtt(nq);
} }
struct ll_entry* qe = queue_entry_new(0); 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; uint8_t need_probe = 0;
uint64_t now_tb = get_time_tb(); uint64_t now_tb = get_time_tb();
for (uint8_t i = 0; i < ep->cached_resp.my_count && i < 4; i++) { 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 (!nq || !cm_is_rtt_fresh(nq, now_tb)) { need_probe++; break; }
} }
if (need_probe == 0) { 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; uint8_t need_probe = 0;
uint64_t now_tb = get_time_tb(); uint64_t now_tb = get_time_tb();
for (uint8_t i = 0; i < ep->cached_resp.my_count && i < 4; i++) { 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 (!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); 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)); memset(&sa, 0, sizeof(sa)); memcpy(&sa, &sin, sizeof(sin));
struct ETCP_CONN* new_conn = etcp_connection_create(conn->instance, NULL); struct ETCP_CONN* new_conn = etcp_connection_create(conn->instance, NULL);
if (new_conn) { 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); 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)); 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; 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); etcp_link_new(new_conn, s, &sa, 0);
} }
break; break;
@ -1081,7 +1113,7 @@ static void cm_handle_interm_exchange_req(struct ETCP_CONN* conn,
uint64_t now_tb = get_time_tb(); uint64_t now_tb = get_time_tb();
for (uint8_t i = 0; i < req->candidate_count && i < 4; i++) { for (uint8_t i = 0; i < req->candidate_count && i < 4; i++) {
uint64_t cand_id = req->candidates[i].node_id; 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; if (nq && cm_is_rtt_fresh(nq, now_tb)) continue;
route_connectivity_probe_node(mgr->instance, nq); 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; resp.your_count = req->candidate_count > 4 ? 4 : req->candidate_count;
for (uint8_t i = 0; i < resp.your_count; i++) { for (uint8_t i = 0; i < resp.your_count; i++) {
resp.your_candidates[i].node_id = req->candidates[i].node_id; 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; uint16_t rtt = nq2 ? cm_get_node_min_rtt(nq2) : 0;
resp.your_candidates[i].rtt = rtt; resp.your_candidates[i].rtt = rtt;
} }
@ -1154,8 +1186,9 @@ static void cm_bg_ping_timer_cb(void* arg) {
e = group->nodes->head; e = group->nodes->head;
while (e && cursor < mgr->bg_ping_cursor) { cursor++; e = e->next; } while (e && cursor < mgr->bg_ping_cursor) { cursor++; e = e->next; }
if (e) { if (e) {
struct TOPO_NODEQ* nq = (struct TOPO_NODEQ*)e; struct TOPO_GROUP_NODE* nq = (struct TOPO_GROUP_NODE*)e;
uint64_t node_id = nq->node->node_id; 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) { if (node_id != mgr->instance->node_id) {
struct ETCP_CONN* dconn = topo_group_find_conn_for_node(group, node_id); struct ETCP_CONN* dconn = topo_group_find_conn_for_node(group, node_id);
int has_active = 0; int has_active = 0;
@ -1163,9 +1196,9 @@ static void cm_bg_ping_timer_cb(void* arg) {
struct ETCP_LINK* l = dconn->links; struct ETCP_LINK* l = dconn->links;
while (l) { if (l->initialized && l->link_status) { has_active = 1; break; } l = l->next; } 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; 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; 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; 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); 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; struct ETCP_SOCKET* s = mgr->instance->etcp_sockets;
while (s) { if (s->local_addr.ss_family == AF_INET) break; s = s->next; } while (s) { if (s->local_addr.ss_family == AF_INET) break; s = s->next; }
if (!s) break; 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); CONN_PROBE_TIMEOUT_MS, cm_bg_ping_noop_cb, NULL, NULL, 0, 0);
sent_v4 = 1; break; sent_v4 = 1; break;
} }
if (!sent_v4) { 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; 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; 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); 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; struct ETCP_SOCKET* s = mgr->instance->etcp_sockets;
while (s) { if (s->local_addr.ss_family == AF_INET6) break; s = s->next; } while (s) { if (s->local_addr.ss_family == AF_INET6) break; s = s->next; }
if (!s) break; 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); CONN_PROBE_TIMEOUT_MS, cm_bg_ping_noop_cb, NULL, NULL, 0, 0);
break; break;
} }
@ -1203,7 +1236,7 @@ static void cm_candidate_ping_timer_cb(void* arg) {
uint64_t now_tb = get_time_tb(); uint64_t now_tb = get_time_tb();
for (uint8_t i = 0; i < mgr->best_candidate_count; i++) { for (uint8_t i = 0; i < mgr->best_candidate_count; i++) {
uint64_t cand_id = mgr->best_candidates[i].node_id; 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) { if (!nq) {
memmove(&mgr->best_candidates[i], &mgr->best_candidates[i+1], (mgr->best_candidate_count - i - 1) * sizeof(mgr->best_candidates[0])); 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; 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; struct ETCP_LINK* l = dconn->links;
while (l) { if (l->initialized && l->link_status) { has_active = 1; break; } l = l->next; } while (l) { if (l->initialized && l->link_status) { has_active = 1; break; } l = l->next; }
} }
if (has_active) if (has_active) {
etcp_send_ping(mgr->instance, nq->node->public_key, &dconn->links->remote_addr, CONN_PROBE_TIMEOUT_MS, NULL, NULL, NULL, 0); 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 else
route_connectivity_probe_node(mgr->instance, nq); 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* group = topo_groups_find(mgr->instance->topo_groups, req->group_id);
struct TOPO_GROUP* src_group = group ? group : mgr->group; struct TOPO_GROUP* src_group = group ? group : mgr->group;
const uint8_t* ed_pubkey = src_group->ed25519_public_key; const uint8_t* ed_pubkey = src_group->ed25519_public_key;
const char* node_name = (src_group->local_node && src_group->local_node->node) struct TOPO_NODE* ln = (src_group->local_node) ? topo_node_registry_find(src_group->instance->topo_groups, src_group->local_node->node_id) : NULL;
? src_group->local_node->node->node_name : NULL; const char* node_name = ln ? ln->node_name : NULL;
size_t name_len = node_name ? strlen(node_name) : 0; size_t name_len = node_name ? strlen(node_name) : 0;
if (name_len > 255) name_len = 255; 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; } 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; struct TOPO_GROUP_NODE* nq = inv->temp_nq;
if (nq && nq->node) { if (nq) {
memcpy(nq->node->ed25519_public_key, resp->ed25519_pubkey, SC_PUBKEY_SIZE); struct TOPO_NODE* inv_ni = topo_node_registry_find(inv->mgr->instance->topo_groups, nq->node_id);
if (nq->node->node_name) { u_free(nq->node->node_name); nq->node->node_name = NULL; } if (inv_ni) {
if (resp->node_name_len) { nq->node->node_name = u_strdup((const char*)resp->node_name); } 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; 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; } 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); 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; struct cm_invite_pending* inv = (struct cm_invite_pending*)arg;
if (inv->state != CM_INVITE_CONNECTING) return; if (inv->state != CM_INVITE_CONNECTING) return;
if (!conn || conn->peer_node_id != inv->node_id) { 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; inv->conn = etcp;
DEBUG_INFO(DEBUG_CATEGORY_GENERAL, "conn_mgr: TCP invite ready node=0x%016llx", (unsigned long long)inv->node_id); 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) { 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) { if (inv->temp_nq) {
struct TOPO_GROUP* group = mgr->group; struct TOPO_GROUP* group = mgr->group;
queue_remove_data(group->nodes, &inv->temp_nq->ll); 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; queue_entry_free(&inv->temp_nq->ll); inv->temp_nq = NULL;
} }
struct cm_invite_pending** pp = &mgr->invite_list; 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)); 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; } 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); memcpy(group_ni->public_key, ni->public_key, SC_PUBKEY_SIZE);
for (const struct TOPO_ADDR4* src = ni->v4_addrs; src; src = src->next) { for (const struct TOPO_ADDR4* src = ni->v4_addrs; src; src = src->next) {
struct TOPO_ADDR4* a4 = memory_pool_alloc(groups->v4_addr_pool); 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; } 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; } 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)); struct ll_entry* qe = queue_entry_new(sizeof(struct TOPO_GROUP_NODE));
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; } 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_NODEQ* nq = (struct TOPO_NODEQ*)qe; 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)); 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)); 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->mgr = mgr; inv->node_id = node_id; inv->group_id = group_id;
inv->timeout_ms = timeout_ms; inv->state = CM_INVITE_CONNECTING; inv->timeout_ms = timeout_ms; inv->state = CM_INVITE_CONNECTING;
inv->temp_nq = nq; inv->cb = cb; inv->cb_arg = cb_arg; 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) { if (!udp_conn) {
udp_conn = etcp_connection_create(mgr->instance, NULL); udp_conn = etcp_connection_create(mgr->instance, NULL);
if (udp_conn) { 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_init_ctx(&udp_conn->crypto_ctx, &mgr->instance->my_keys);
sc_set_peer_public_key(&udp_conn->crypto_ctx, group_ni->public_key, 0); 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) { if (!udp_conn) {
udp_conn = etcp_connection_create(mgr->instance, NULL); udp_conn = etcp_connection_create(mgr->instance, NULL);
if (udp_conn) { 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_init_ctx(&udp_conn->crypto_ctx, &mgr->instance->my_keys);
sc_set_peer_public_key(&udp_conn->crypto_ctx, group_ni->public_key, 0); sc_set_peer_public_key(&udp_conn->crypto_ctx, group_ni->public_key, 0);
} }

121
src/routing_layer/conn_mgr.h

@ -15,7 +15,7 @@ extern "C" {
struct UTUN_INSTANCE; struct UTUN_INSTANCE;
struct ETCP_CONN; struct ETCP_CONN;
struct TOPO_NODEQ; struct TOPO_GROUP_NODE;
struct TOPO_GROUP; 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_EXCHANGE_RESP 0x04 ///< Ответ с нашими кандидатами + замеры RTT до кандидатов инициатора
#define CONN_MGR_SUBCMD_INTERM_SELECTED 0x05 ///< Выбранные посредники (финальный шаг INDIRECT-фазы) #define CONN_MGR_SUBCMD_INTERM_SELECTED 0x05 ///< Выбранные посредники (финальный шаг INDIRECT-фазы)
#define CONN_MGR_SUBCMD_DISCONNECT 0x06 ///< Уведомление о разрыве соединения #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_REQ 0x0A ///< Запрос проверки членства в группе при invite-подключении
#define CONN_MGR_SUBCMD_INVITE_INFO_RESP 0x0B ///< Ответ с членством, ed25519_pubkey, именем узла #define CONN_MGR_SUBCMD_INVITE_INFO_RESP 0x0B ///< Ответ с членством, ed25519_pubkey, именем узла
/** @} */ /** @} */
@ -221,6 +225,37 @@ struct CONN_MGR_DISCONNECT {
uint64_t node_id; ///< Идентификатор ноды, с которой разрываем соединение 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) #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); 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 * @struct CONN_MGR_HANDLE
* @brief Узел связного списка callback'ов для одной ноды. * @brief Универсальный handle подключения (один на каждый conn_mgr_connect_node).
* */
* Позволяет регистрировать несколько callback'ов на одно подключение. struct CONN_MGR_HANDLE {
* Новые callback'и добавляются в начало списка. 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 { struct cm_bridge_session {
conn_mgr_connect_callback_t cb; ///< Функция-callback uint64_t session_id;
void* arg; ///< Пользовательский аргумент uint64_t initiator_id;
struct cm_cb_node* next; ///< Следующий callback в списке uint64_t target_id;
uint8_t cancelled; ///< 1 = callback помечен как недействительный 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-проверки uint64_t last_traffic_tb; ///< Время последнего трафика (tb) для idle-проверки
void* idle_timer; ///< Хендл таймера 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) uint64_t intermediaries[CONN_MGR_MAX_INTERMEDIARIES]; ///< Выбранные посредники (node_id)
uint8_t intermediariy_count; ///< Количество выбранных посредников uint8_t intermediariy_count; ///< Количество выбранных посредников
@ -356,6 +409,8 @@ struct CONN_MGR {
struct cm_reverse_pending* reverse_pending; ///< Связный список ожидающих REVERSE-подключений struct cm_reverse_pending* reverse_pending; ///< Связный список ожидающих REVERSE-подключений
struct cm_exchange_pending* exchange_pending; ///< Связный список ожидающих INDIRECT-обменов struct cm_exchange_pending* exchange_pending; ///< Связный список ожидающих INDIRECT-обменов
struct cm_invite_pending* invite_list; ///< Связный список ожидающих invite-подключений 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 Запускает асинхронное подключение к ноде. * @brief Запускает асинхронное подключение к ноде.
* @param mgr менеджер * @return CONN_MGR_HANDLE* при успехе, NULL при ошибке (cb вызван с CONN_MGR_ERR_*)
* @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-линк).
*/ */
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, uint32_t idle_timeout_ms,
conn_mgr_connect_callback_t cb, void* cb_arg); conn_mgr_connect_callback_t cb, void* cb_arg);
/**
* @brief Освобождает ОДИН handle (не разрывает соединение если есть другие handles).
*/
void conn_mgr_release_handle(struct CONN_MGR_HANDLE* h);
/** /**
* @brief Разрывает подключение к ноде. * @brief Разрывает подключение к ноде.
* @param mgr менеджер * Отправляет DISCONNECT-пакет целевой ноде (через etcp_router).
* @param node_id идентификатор ноды * Для INDIRECT — также отправляет BRIDGE_CLOSE каждому посреднику.
* @return CONN_MGR_OK или CONN_MGR_ERR_NOT_FOUND * Закрывает все handles, чистит entry.
*
* Отправляет DISCONNECT-пакет целевой ноде, сбрасывает conn_mgr_type в NODEINFO_Q,
* уничтожает запись entry.
*/ */
int conn_mgr_disconnect_node(struct CONN_MGR* mgr, uint64_t node_id); int conn_mgr_disconnect_node(struct CONN_MGR* mgr, uint64_t node_id);
/** /**
* @brief Отменяет ожидающий callback подключения к ноде. * @brief Отменяет ожидающий handle подключения к ноде.
* @param mgr менеджер * Если handle единственный — полностью отменяет подключение.
* @param node_id идентификатор ноды * Если h == NULL — отменяет все handles для 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, он будет пропущен при доставке.
*/ */
int conn_mgr_cancel_callback(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);
/* ==================== Управление idle-таймаутом ==================== */ /* ==================== Управление idle-таймаутом ==================== */

12
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 ETCP_CONN* conn = NULL;
struct TOPO_GROUP* group = topo_groups_find(inst->topo_groups, rconn->group_id); struct TOPO_GROUP* group = topo_groups_find(inst->topo_groups, rconn->group_id);
if (group) { 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) { 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++) { 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]); 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 ETCP_CONN* conn = NULL;
struct TOPO_GROUP* group = topo_groups_find(inst->topo_groups, rconn->group_id); struct TOPO_GROUP* group = topo_groups_find(inst->topo_groups, rconn->group_id);
if (group) { 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) { 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++) { 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]); 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; size_t actual_payload = *pl_len - SC_SIGN_SIZE;
uint8_t* sig = (uint8_t*)entry->dgram + SVC_ROUTE_HDR_SIZE + actual_payload; 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_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) { if (!nq) {
DEBUG_WARN(DEBUG_CATEGORY_ETCPROUTE, "router: SIGNED packet from unknown node %016llx — dropping", DEBUG_WARN(DEBUG_CATEGORY_ETCPROUTE, "router: SIGNED packet from unknown node %016llx — dropping",
(unsigned long long)hdr->src_node_id); (unsigned long long)hdr->src_node_id);
free_entry(entry); return -1; 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}; 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", DEBUG_WARN(DEBUG_CATEGORY_ETCPROUTE, "router: no Ed25519 pubkey for node %016llx — dropping",
(unsigned long long)hdr->src_node_id); (unsigned long long)hdr->src_node_id);
free_entry(entry); return -1; free_entry(entry); return -1;
} }
} }
struct sc_stream_sign_state vfy_state; 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", DEBUG_ERROR(DEBUG_CATEGORY_ETCPROUTE, "router: sign_verify_init failed for node %016llx",
(unsigned long long)hdr->src_node_id); (unsigned long long)hdr->src_node_id);
free_entry(entry); return -1; free_entry(entry); return -1;

30
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 if (info->nat_type == NAT_TYPE_DIRECT) verified_type = NAT_VERIFIED_DIRECT;
else verified_type = NAT_VERIFIED_UNKNOWN; 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; 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) { while (sm) {
if (sm->id == socket_id) { if (sm->id == socket_id) {
if (sm->nat_type != verified_type) { sm->nat_type = verified_type; data_changed = 1; } 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) { 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); 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->dirty = 1;
group->local_node->node->ver = (group->local_node->node->ver % 255) + 1; ni->ver = (ni->ver % 255) + 1;
group->local_node->last_ver = group->local_node->node->ver; group->local_node->last_ver = ni->ver;
} }
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) {
struct TOPO_ADDR4* a = group->local_node->node->v4_addrs; struct TOPO_ADDR4* a = ni->v4_addrs;
while (a) { while (a) {
if (a->type == TOPO_ADDR_INTERFACE && a->socket_id == socket_id) { if (a->type == TOPO_ADDR_INTERFACE && a->socket_id == socket_id) {
uint32_t a_ip; memcpy(&a_ip, a->addr, 4); uint32_t a_ip; memcpy(&a_ip, a->addr, 4);
if (a_ip == nat_ip && a->port == nat_port) { if (a_ip == nat_ip && a->port == nat_port) {
a->type = TOPO_ADDR_NAT; a->socket_id = socket_id | 1; a->type = TOPO_ADDR_NAT; a->socket_id = socket_id | 1;
group->local_node->dirty = 1; group->local_node->dirty = 1;
group->local_node->node->ver = (group->local_node->node->ver % 255) + 1; ni->ver = (ni->ver % 255) + 1;
group->local_node->last_ver = group->local_node->node->ver; 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); DEBUG_INFO(DEBUG_CATEGORY_BGP, "NAT_INFO matched interface addr, updated entry type to NAT sock=%d", socket_id);
} }
break; 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_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; struct NAT_DETECTION* nd = (struct NAT_DETECTION*)arg;
if (!nd) return; if (!nd) return;
nat_detection_trigger_checks(nd); 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; struct NAT_DETECTION* nd = (struct NAT_DETECTION*)arg;
if (!nd || !conn) return; if (!nd || !conn) return;
route_ping_cancel_for_conn(nd, conn); 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; if (!conn || !conn->instance) return;
struct NAT_DETECTION* nd = (struct NAT_DETECTION*)arg; struct NAT_DETECTION* nd = (struct NAT_DETECTION*)arg;
DEBUG_DEBUG(DEBUG_CATEGORY_NAT, "NAT detection set callbacks: %s", conn->log_name); 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_cbk(conn, nat_detection_on_conn_up, nd, ETCP_CBK_EVENT_UP);
etcp_conn_add_down_cbk(conn, nat_detection_on_conn_down, nd); etcp_conn_add_cbk(conn, nat_detection_on_conn_down, nd, ETCP_CBK_EVENT_DOWN);
} }

8
src/routing_layer/route6_lib.c

@ -63,7 +63,7 @@ void route6_table_destroy(struct ROUTE6_TABLE *table) {
u_free(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; 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) { for (const struct TOPO_SUBNET6* s = node->subnets->v6_subnets; s; s = s->next) {
struct ll_entry *entry = queue_entry_new(ROUTE6_DATA_SIZE); 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_key6(rd->key, s->addr);
make_mask6(rd->mask, s->prefix_length); make_mask6(rd->mask, s->prefix_length);
rd->v_node_info = node; rd->v_node_info = node;
rd->node_id = node->node->node_id; rd->node_id = node->node_id;
rd->prefix_length = s->prefix_length; rd->prefix_length = s->prefix_length;
struct radix_node *rn = rn_addroute(rd->key, rd->mask, &table->rnh->rh, rd->rn_nodes); struct radix_node *rn = rn_addroute(rd->key, rd->mask, &table->rnh->rh, rd->rn_nodes);
if (!rn) { queue_entry_free(entry); continue; } if (!rn) { queue_entry_free(entry); continue; }
@ -81,9 +81,9 @@ bool route6_insert(struct ROUTE6_TABLE *table, struct TOPO_NODEQ *node) {
return true; 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; 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); struct ll_entry *entry = queue_find_data_by_index(table->queue, &node_id);
while (entry) { while (entry) {
struct ROUTE6_DATA *rd = (struct ROUTE6_DATA *)entry->data; struct ROUTE6_DATA *rd = (struct ROUTE6_DATA *)entry->data;

6
src/routing_layer/route6_lib.h

@ -48,7 +48,7 @@ FDxx/8 - опциональные ipv6 подсети узлов /64 (если
struct ROUTE6_DATA { struct ROUTE6_DATA {
struct radix_node rn_nodes[2]; // scratch-буфер для rn_addroute 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 uint64_t node_id; // ключ для hash-поиска в ll_queue
uint8_t prefix_length; // 0..128 uint8_t prefix_length; // 0..128
uint8_t key[17]; // sockaddr-like: {len=17, addr[16]} 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); struct ROUTE6_TABLE *route6_table_create(struct UASYNC *ua);
void route6_table_destroy(struct ROUTE6_TABLE *table); void route6_table_destroy(struct ROUTE6_TABLE *table);
bool route6_insert(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_NODEQ *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]); const struct ROUTE6_DATA *route6_lookup(struct ROUTE6_TABLE *table, const uint8_t addr[16]);

38
src/routing_layer/route_connectivity.c

@ -23,7 +23,7 @@
struct conn_probe_ctx { struct conn_probe_ctx {
struct conn_probe_ctx* next; /* linked list in nq->connectivity.probe_list */ struct conn_probe_ctx* next; /* linked list in nq->connectivity.probe_list */
struct UTUN_INSTANCE* instance; struct UTUN_INSTANCE* instance;
struct TOPO_NODEQ* nq; struct TOPO_GROUP_NODE* nq;
uint8_t addr_type; // ADDR_TYPE_* uint8_t addr_type; // ADDR_TYPE_*
struct sockaddr_storage target_addr; struct sockaddr_storage target_addr;
uint8_t peer_pubkey[SC_PUBKEY_SIZE]; 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) { if (c->pending_count == 0) {
c->probe_status = PROBE_STATUS_DONE; c->probe_status = PROBE_STATUS_DONE;
DEBUG_INFO(DEBUG_CATEGORY_BGP, "connectivity probe DONE for node %016llx: intf=%d nat=%d real=%d", 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); c->interface_status, c->nat_status, c->real_status);
if (ctx->instance && ctx->instance->topo_sqlite_db) 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 ---- // ---- 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 (!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) { 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; return;
} }
@ -312,9 +314,9 @@ void route_connectivity_probe_node(struct UTUN_INSTANCE* instance, struct TOPO_N
int pend = 0; int pend = 0;
// ---- IPv4 addresses ---- // ---- 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; 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; uint32_t ip; memcpy(&ip, a->addr, 4); uint16_t port = a->port;
if (ip == 0 || port == 0) continue; 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; } } 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; 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; 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*)); 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->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++; 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 ---- // ---- 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; 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; 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 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; } } 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; 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; 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*)); 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->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++; 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) { if (pend == 0) {
nq->connectivity.probe_status = PROBE_STATUS_DONE; nq->connectivity.probe_status = PROBE_STATUS_DONE;
if (!nq->node->v4_addrs && !nq->node->v6_addrs) { if (!ni->v4_addrs && !ni->v6_addrs) {
DEBUG_INFO(DEBUG_CATEGORY_BGP, "no addresses to probe for node %016llx", (unsigned long long)nq->node->node_id); DEBUG_INFO(DEBUG_CATEGORY_BGP, "no addresses to probe for node %016llx", (unsigned long long)ni->node_id);
} }
} else { } else {
nq->connectivity.pending_count = pend; nq->connectivity.pending_count = pend;
DEBUG_INFO(DEBUG_CATEGORY_BGP, "connectivity probe started for node %016llx: %d series pending", 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; if (!instance || !nq) return;
nq->connectivity.probe_status = PROBE_STATUS_NONE; nq->connectivity.probe_status = PROBE_STATUS_NONE;
nq->connectivity.pending_count = 0; nq->connectivity.pending_count = 0;
struct conn_probe_ctx* ctx = (struct conn_probe_ctx*)nq->connectivity.probe_list; struct conn_probe_ctx* ctx = (struct conn_probe_ctx*)nq->connectivity.probe_list;
while (ctx) { ctx->nq = NULL; ctx = ctx->next; } while (ctx) { ctx->nq = NULL; ctx = ctx->next; }
nq->connectivity.probe_list = NULL; 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) { 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; int count = 0;
struct ll_entry* e = group->nodes->head; struct ll_entry* e = group->nodes->head;
while (e) { 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.probe_status = PROBE_STATUS_NONE;
nq->connectivity.pending_count = 0; nq->connectivity.pending_count = 0;
e = e->next; e = e->next;

4
src/routing_layer/route_connectivity.h

@ -13,11 +13,11 @@ struct UTUN_INSTANCE;
// Запускает зондирование связности ко всем адресам удалённого узла // Запускает зондирование связности ко всем адресам удалённого узла
void route_connectivity_probe_node(struct UTUN_INSTANCE* instance, void route_connectivity_probe_node(struct UTUN_INSTANCE* instance,
struct TOPO_NODEQ* nq); struct TOPO_GROUP_NODE* nq);
// Отменяет все pending пробы для узла (при удалении / withdraw) // Отменяет все pending пробы для узла (при удалении / withdraw)
void route_connectivity_cancel_node(struct UTUN_INSTANCE* instance, void route_connectivity_cancel_node(struct UTUN_INSTANCE* instance,
struct TOPO_NODEQ* nq); struct TOPO_GROUP_NODE* nq);
// Отменяет все pending пробы для всех узлов (при destroy) // Отменяет все pending пробы для всех узлов (при destroy)
void route_connectivity_cancel_all(struct UTUN_INSTANCE* instance); void route_connectivity_cancel_all(struct UTUN_INSTANCE* instance);

10
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); 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->dynamic_subnets);
u_free(table->local_subnets); u_free(table->local_subnets);
u_free(table->entries); u_free(table->entries);
@ -145,7 +145,7 @@ void route_table_destroy(struct ROUTE_TABLE *table) {
DEBUG_INFO(DEBUG_CATEGORY_ROUTING, "Routing table destroyed"); 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 (!table || !node) { DEBUG_ERROR(DEBUG_CATEGORY_ROUTING, "invalid arguments"); return false; }
if (!node->subnets || !node->subnets->v4_subnets) { 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", 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; 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; 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; size_t i = 0, removed = 0;
while (i < table->count) { while (i < table->count) {
if (table->entries[i].v_node_info == node) { if (table->entries[i].v_node_info == node) {

8
src/routing_layer/route_lib.h

@ -15,7 +15,7 @@ extern "C" {
struct ETCP_CONNECTIONS; struct ETCP_CONNECTIONS;
struct ROUTE_TABLE; struct ROUTE_TABLE;
struct ROUTE_ENTRY; struct ROUTE_ENTRY;
struct TOPO_NODEQ; struct TOPO_GROUP_NODE;
/** /**
* @brief Флаги узла * @brief Флаги узла
@ -34,7 +34,7 @@ typedef enum {
struct ROUTE_ENTRY { struct ROUTE_ENTRY {
uint32_t network; // Сетевой адрес (big-endian) uint32_t network; // Сетевой адрес (big-endian)
uint8_t prefix_length; // Длина префикса подсети 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 если вставка/обновление успешно * @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 Удаляет все записи из таблицы маршрутизации для указанного узла * @brief Удаляет все записи из таблицы маршрутизации для указанного узла
@ -87,7 +87,7 @@ bool route_insert(struct ROUTE_TABLE *table, struct TOPO_NODEQ *node);
* @param table Указатель на таблицу маршрутизации * @param table Указатель на таблицу маршрутизации
* @param node узел, все маршруты которого нужно удалить * @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-адреса * @brief Выполняет поиск маршрута для заданного IP-адреса

2
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) { 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(); if (req_nq) req_nq->connectivity.ping_req_time = get_time_tb();
} }
} }

9
src/routing_layer/routing.c

@ -138,13 +138,14 @@ void route_pkt(struct UTUN_INSTANCE* instance, struct ll_entry* entry, uint64_t
return; return;
} }
struct TOPO_NODEQ* nq = route->v_node_info; struct TOPO_GROUP_NODE* nq = route->v_node_info;
if (!nq || !nq->node || nq->hop_count == 0) { 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); DEBUG_TRACE(DEBUG_CATEGORY_ROUTING, "Local route to %s", ip_to_str(&addr, AF_INET).str);
} else { } else {
DEBUG_TRACE(DEBUG_CATEGORY_ROUTING, "route_pkt: sending %zu bytes to node %016llx dst=%s", 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); ip_len, (unsigned long long)nq->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); int send_err = etcp_route_send(instance, rni->group_id, nq->node_id, entry, 1);
if (send_err != 0) { if (send_err != 0) {
DEBUG_WARN(DEBUG_CATEGORY_ROUTING, "route_pkt: etcp_route_send failed: dst=%s err=%d", DEBUG_WARN(DEBUG_CATEGORY_ROUTING, "route_pkt: etcp_route_send failed: dst=%s err=%d",
ip_to_str(&addr, AF_INET).str, send_err); ip_to_str(&addr, AF_INET).str, send_err);

97
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 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_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); 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); 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); } 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); 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); 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"); 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; } 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"); 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); while ((e = queue_data_get(group->senders_list)) != NULL) queue_entry_free(e);
queue_free(group->senders_list); 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); if (group->nodes) queue_free(group->nodes);
group->conn_mgr = NULL; /* destroyed externally in utun_instance_destroy before etcp_router_destroy */ 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) { while ((re = queue_data_get(g->node_registry)) != NULL) {
struct TOPO_NODE* node; struct TOPO_NODE* node;
memcpy(&node, re->data + 8, sizeof(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_entry_free(re);
} }
queue_free(g->node_registry); 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); 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); 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); 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; struct ll_entry* node_entry = group->nodes ? group->nodes->head : NULL;
while (node_entry) { while (node_entry) {
struct ll_entry* next = node_entry->next; 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) { 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 (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 (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); if (conn->instance && conn->instance->control_srv) control_server_notify_node_removed(conn->instance->control_srv, key);
nq->dirty = 1; nq->dirty = 1;
route_connectivity_cancel_node(conn->instance, nq); route_connectivity_cancel_node(conn->instance, nq);
if (nq->paths) { queue_free(nq->paths); nq->paths = NULL; } 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; struct ll_entry* entry = node_entry;
if (entry) { queue_remove_data(group->nodes, entry); queue_entry_free(entry); } if (entry) { queue_remove_data(group->nodes, entry); queue_entry_free(entry); }
nodes_removed++; 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) { struct ETCP_CONN* topo_group_find_conn_for_node(struct TOPO_GROUP* group, uint64_t node_id) {
if (!group) return NULL; 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; if (!nq || !nq->paths || !nq->paths->head) return NULL;
struct ll_entry* e = nq->paths->head; struct ll_entry* e = nq->paths->head;
struct TOPO_NODEPATH* best_live = NULL; uint8_t best_live_hops = 255; 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); 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 || !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; } 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; 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; 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; if (!nq || !nq->paths) return 0;
int removed = 0; int removed = 0;
struct ll_entry* e = nq->paths->head; 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; 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; if (!nq || !conn || !nq->paths) return -1;
struct ll_entry* e = nq->paths->head; 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; } 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; struct TOPOMSG_NODE* ni = &pkt->node;
if (ni->group_id != group->group_id) { if (ni->group_id != group->group_id) {
DEBUG_ERROR(DEBUG_CATEGORY_BGP, "NODEINFO group_id mismatch from %s: expected=%016llx got=%016llx", 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); from->log_name, (unsigned long long)group->group_id, (unsigned long long)ni->group_id, (unsigned long long)ni->node_id);
return -1; 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 (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; 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; 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) { 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); 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; struct ll_queue* paths = NULL;
int is_new_node = 0; int is_new_node = 0;
if (nodeinfo1) { if (nodeinfo1) {
topo_node_free_lists(group, nodeinfo1); topo_nodeq_free_group_fields(group->instance->topo_groups, nodeinfo1);
paths = nodeinfo1->paths; nodeinfo1->paths = NULL; paths = nodeinfo1->paths; nodeinfo1->paths = NULL;
} else { is_new_node = 1; } } 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; uint64_t* new_hop_list = NULL; uint8_t new_hop_count = 0;
uint16_t incoming_cumulative_rtt = 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) { 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); } if (nodeinfo1) { queue_remove_data(group->nodes, &nodeinfo1->ll); queue_free(paths); queue_entry_free(&nodeinfo1->ll); }
return -1; 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 */ /* 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) { } 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", 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); (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); 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); } if (nodeinfo1) { queue_remove_data(group->nodes, &nodeinfo1->ll); queue_free(paths); queue_entry_free(&nodeinfo1->ll); }
return -1; return -1;
@ -622,8 +629,13 @@ int topo_group_process_nodeinfo(struct TOPO_GROUP* group, struct ETCP_CONN* from
} }
if (nodeinfo1) { if (nodeinfo1) {
nodeinfo1->node = topo_node_registry_acquire(group->instance->topo_groups, new_ni); { struct TOPO_NODE* stored = topo_node_registry_store(group->instance->topo_groups, new_ni);
nodeinfo1->hash_node_id = nodeinfo1->node ? nodeinfo1->node->node_id : 0; 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->subnets = new_subnets;
nodeinfo1->cumulative_rtt = incoming_cumulative_rtt; nodeinfo1->cumulative_rtt = incoming_cumulative_rtt;
u_free(nodeinfo1->tranzit_data); nodeinfo1->tranzit_data = new_tranzit; 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; u_free(nodeinfo1->hop_list); nodeinfo1->hop_list = new_hop_list;
nodeinfo1->hop_count = new_hop_count; nodeinfo1->hop_count = new_hop_count;
} else { } else {
struct ll_entry* qe = queue_entry_new(sizeof(struct TOPO_NODEQ)); struct ll_entry* qe = queue_entry_new(sizeof(struct TOPO_GROUP_NODE));
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; } 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_NODEQ*)qe; nodeinfo1 = (struct TOPO_GROUP_NODE*)qe;
memset((uint8_t*)nodeinfo1 + sizeof(struct ll_entry), 0, sizeof(*nodeinfo1) - sizeof(struct ll_entry)); 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); { struct TOPO_NODE* stored = topo_node_registry_store(group->instance->topo_groups, new_ni);
nodeinfo1->hash_node_id = nodeinfo1->node ? nodeinfo1->node->node_id : 0; 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->subnets = new_subnets;
nodeinfo1->cumulative_rtt = incoming_cumulative_rtt; nodeinfo1->cumulative_rtt = incoming_cumulative_rtt;
nodeinfo1->tranzit_data = new_tranzit; nodeinfo1->tranzit_count = new_tranzit_count; 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) { if (node_id != group->instance->node_id) {
sqlite3* sdb = group->instance->topo_sqlite_db; sqlite3* sdb = group->instance->topo_sqlite_db;
if (sdb) { 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]) { 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); 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); 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 (!is_new_node) socks_changed = 1;
if (node_id != group->instance->node_id && (is_new_node || socks_changed)) { if (node_id != group->instance->node_id && (is_new_node || socks_changed)) {
int v4a = topo_list_count((struct _topo_head*)nodeinfo1->node->v4_addrs); struct TOPO_NODE* tni = topo_node_registry_find(group->instance->topo_groups, nodeinfo1->node_id);
int v6a = topo_list_count((struct _topo_head*)nodeinfo1->node->v6_addrs); 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); 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; struct TOPOMSG_WITHDRAW_PKT* wp = (struct TOPOMSG_WITHDRAW_PKT*)data;
uint64_t node_id = wp->node_id, wd_source = wp->wd_source; 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; } if (!nq) { DEBUG_INFO(DEBUG_CATEGORY_BGP, "node not found"); return 0; }
int ret = topo_group_remove_path_by_hop(nq, wd_source); int ret = topo_group_remove_path_by_hop(nq, wd_source);
if (ret == 1) { if (ret == 1) {
@ -730,7 +748,7 @@ int topo_group_process_withdraw(struct TOPO_GROUP* group, struct ETCP_CONN* send
nq->dirty = 1; nq->dirty = 1;
route_connectivity_cancel_node(group->instance, nq); route_connectivity_cancel_node(group->instance, nq);
if (nq->paths) { queue_free(nq->paths); nq->paths = NULL; } 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); 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); } if (entry) { queue_remove_data(group->nodes, entry); queue_entry_free(entry); }
topo_group_broadcast_withdraw(group, node_id, wd_source, sender); 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; 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; if (!group || !node || !conn) return;
size_t max_sz = sizeof(struct TOPOMSG_NODEINFO_PKT) + 4096; 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; 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); struct TOPO_NODE* sni = topo_node_registry_find(group->instance->topo_groups, node->node_id);
if (ser_len < 0) { u_free(p); return; } 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); struct ll_entry* e = queue_entry_new(0);
if (!e) { u_free(p); return; } if (!e) { u_free(p); return; }
e->dgram = p; e->len = (size_t)ser_len + 2; 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) { 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) { static bool topo_group_should_send_to(const struct TOPO_GROUP_NODE* nq, uint64_t target_id) {
if (!nq || !nq->node || !nq->paths) return false; if (!nq || !nq->paths) return false;
if (target_id == nq->node->node_id) return false; if (target_id == nq->node_id) return false;
struct ll_entry* e = nq->paths->head; struct ll_entry* e = nq->paths->head;
while (e) { while (e) {
struct TOPO_NODEPATH* path = (struct TOPO_NODEPATH*)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; uint64_t target = conn->peer_node_id;
struct ll_entry* e = group->nodes ? group->nodes->head : NULL; struct ll_entry* e = group->nodes ? group->nodes->head : NULL;
while (e) { 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->hop_count == 0) { e = e->next; continue; }
if (!nq->paths) { DEBUG_WARN(DEBUG_CATEGORY_BGP, "node has no paths"); 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)); 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));

8
src/routing_layer/topo_group.h

@ -148,7 +148,7 @@ struct TOPO_GROUP {
struct UTUN_INSTANCE* instance; struct UTUN_INSTANCE* instance;
struct ll_queue* senders_list; struct ll_queue* senders_list;
struct ll_queue* nodes; struct ll_queue* nodes;
struct TOPO_NODEQ* local_node; struct TOPO_GROUP_NODE* local_node;
uint8_t ed25519_public_key[SC_PUBKEY_SIZE]; uint8_t ed25519_public_key[SC_PUBKEY_SIZE];
char channel_id[64]; // channel_id для групп типа CHAT char channel_id[64]; // channel_id для групп типа CHAT
struct CONN_MGR* conn_mgr; // менеджер соединений для этой группы 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). * @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). * @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 при успехе * @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 узла. * @brief Удаляет conn из paths узла.
* *
* @return 1 если путей не осталось (unreachable) * @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); void topo_groups_set_node_updated_cb(struct TOPO_GROUPS* groups, topo_node_updated_fn fn);

4
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? */ /* есть ли 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) { if (nq && nq->paths) {
struct ll_entry* pe = nq->paths->head; struct ll_entry* pe = nq->paths->head;
while (pe) { while (pe) {
@ -216,7 +216,7 @@ static void tgc_phase1_timeout(void* arg) {
uint8_t status; uint8_t conn_type; uint8_t status; uint8_t conn_type;
conn_mgr_get_status(gc->group->conn_mgr, ids[i], &status, &conn_type); conn_mgr_get_status(gc->group->conn_mgr, ids[i], &status, &conn_type);
if (status != CONN_MGR_STATE_CONNECTED) { 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); topo_node_sqlite_set_connected(db, gc->group->channel_id, ids[i], 0);
} }
} }

98
src/routing_layer/topo_node.c

@ -18,12 +18,17 @@
void topo_node_ref(struct TOPO_NODE* ni) { void topo_node_ref(struct TOPO_NODE* ni) {
if (!ni) return; 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) { void topo_node_unref(struct TOPO_NODE* ni) {
if (!ni) return; if (!ni) return;
if (--ni->ref_count == 0) { if (--ni->group_ref_count == 0) {
u_free(ni->node_name); u_free(ni->node_name);
u_free(ni); u_free(ni);
} }
@ -60,21 +65,25 @@ void topo_node_free_raw(struct TOPO_GROUPS* groups, struct TOPO_NODE* ni) {
u_free(ni); u_free(ni);
} }
void topo_node_free_lists(struct TOPO_GROUP* group, struct TOPO_NODEQ* nq) { void topo_node_destroy(struct TOPO_GROUPS* groups, struct TOPO_NODE* ni) {
if (!group || !nq) return; topo_node_free_raw(groups, ni);
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); void topo_nodeq_free_group_fields(struct TOPO_GROUPS* groups, struct TOPO_GROUP_NODE* nq) {
free_v6_sock_list(group->instance->topo_groups->v6_sock_meta_pool, nq->node->v6_sock_meta); if (!groups || !nq) return;
free_v6_addr_list(group->instance->topo_groups->v6_addr_pool, nq->node->v6_addrs); struct TOPO_NODE* ni = topo_node_registry_find(groups, nq->node_id);
nq->node->v4_sock_meta = NULL; nq->node->v4_addrs = NULL; if (ni) {
nq->node->v6_sock_meta = NULL; nq->node->v6_addrs = NULL; free_v4_sock_list(groups->v4_sock_meta_pool, ni->v4_sock_meta);
topo_node_registry_release(group->instance->topo_groups, nq->node); free_v4_addr_list(groups->v4_addr_pool, ni->v4_addrs);
nq->node = NULL; 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) { if (nq->subnets) {
free_v4_sub_list(group->instance->topo_groups->v4_subnet_pool, nq->subnets->v4_subnets); free_v4_sub_list(groups->v4_subnet_pool, nq->subnets->v4_subnets);
free_v6_sub_list(group->instance->topo_groups->v6_subnet_pool, nq->subnets->v6_subnets); free_v6_sub_list(groups->v6_subnet_pool, nq->subnets->v6_subnets);
u_free(nq->subnets); u_free(nq->subnets);
nq->subnets = NULL; nq->subnets = NULL;
} }
@ -84,7 +93,7 @@ void topo_node_free_lists(struct TOPO_GROUP* group, struct TOPO_NODEQ* nq) {
// ===== глобальный реестр TOPO_NODE ===== // ===== глобальный реестр 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; if (!groups || !groups->node_registry) return NULL;
struct ll_entry* e = queue_find_data_by_index(groups->node_registry, &node_id); struct ll_entry* e = queue_find_data_by_index(groups->node_registry, &node_id);
if (!e) return NULL; if (!e) return NULL;
@ -93,9 +102,9 @@ static struct TOPO_NODE* node_registry_find(struct TOPO_GROUPS* groups, uint64_t
return node; 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; 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) { if (existing) {
topo_node_ref(existing); topo_node_ref(existing);
topo_node_free_raw(groups, ni); topo_node_free_raw(groups, ni);
@ -113,10 +122,11 @@ struct TOPO_NODE* topo_node_registry_acquire(struct TOPO_GROUPS* groups, struct
return ni; return ni;
} }
void topo_node_registry_release(struct TOPO_GROUPS* groups, struct TOPO_NODE* ni) { void topo_node_registry_unref(struct TOPO_GROUPS* groups, uint64_t node_id) {
if (!groups || !ni) return; if (!groups) return;
uint64_t node_id = ni->node_id; struct TOPO_NODE* ni = topo_node_registry_find(groups, node_id);
uint32_t prev_ref = ni->ref_count; if (!ni) return;
uint32_t prev_ref = ni->group_ref_count;
topo_node_unref(ni); topo_node_unref(ni);
if (prev_ref == 1) { if (prev_ref == 1) {
struct ll_entry* e = groups->node_registry ? queue_find_data_by_index(groups->node_registry, &node_id) : NULL; 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; + 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) { 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 (!group || !nq || !nq->node || !out) return -1; if (!ni || !nq || !out) return -1;
struct TOPO_NODE* ni = nq->node;
struct TOPOMSG_NODE msg; struct TOPOMSG_NODE msg;
memset(&msg, 0, sizeof(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)); struct TOPO_NODE* ni = u_calloc(1, sizeof(struct TOPO_NODE));
if (!ni) { DEBUG_ERROR(DEBUG_CATEGORY_BGP, "alloc TOPO_NODE failed"); return -1; } 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->flags = msg->flags;
ni->group_id = msg->group_id; ni->group_id = msg->group_id;
ni->node_id = msg->node_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; 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; if (!group || !group->nodes) return NULL;
uint64_t key = node_id; uint64_t key = node_id;
struct ll_entry* e = queue_find_data_by_index(group->nodes, &key); 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) { 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; struct ll_entry* ge = groups->group_list->head;
while (ge) { while (ge) {
struct TOPO_GROUP* g = (struct TOPO_GROUP*)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) { if (nq) {
struct TOPO_CONNECTIVITY* c = &nq->connectivity; struct TOPO_CONNECTIVITY* c = &nq->connectivity;
if (c->last_ping_time == 0 || (now - c->last_ping_time) > PING_TTL_TB) { 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; struct ll_entry* ge = groups->group_list->head;
while (ge) { while (ge) {
struct TOPO_GROUP* g = (struct TOPO_GROUP*)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) { if (nq) {
nq->connectivity.last_ping_time = now; nq->connectivity.last_ping_time = now;
if (nq->connectivity.interface_min_rtt == 0 || rtt < nq->connectivity.interface_min_rtt) 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; int idx = 0;
struct ll_entry* e = group->nodes->head; struct ll_entry* e = group->nodes->head;
while (e) { while (e) {
struct TOPO_NODEQ* nq = (struct TOPO_NODEQ*)e; struct TOPO_GROUP_NODE* nq = (struct TOPO_GROUP_NODE*)e;
struct TOPO_NODE* ni = nq->node; struct TOPO_NODE* ni = topo_node_registry_find(group->instance->topo_groups, nq->node_id);
if (!ni) { e = e->next; continue; } if (!ni) { e = e->next; continue; }
int is_local = (group->local_node == nq); int is_local = (group->local_node == nq);
const char* name_s = ni->node_name ? ni->node_name : ""; 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; int idx = 0;
struct ll_entry* e = group->nodes->head; struct ll_entry* e = group->nodes->head;
while (e && (size_t)pos < buf_size) { while (e && (size_t)pos < buf_size) {
struct TOPO_NODEQ* nq = (struct TOPO_NODEQ*)e; struct TOPO_GROUP_NODE* nq = (struct TOPO_GROUP_NODE*)e;
struct TOPO_NODE* ni = nq->node; struct TOPO_NODE* ni = topo_node_registry_find(group->instance->topo_groups, nq->node_id);
if (!ni) { e = e->next; continue; } if (!ni) { e = e->next; continue; }
int is_local = (group->local_node == nq); int is_local = (group->local_node == nq);
const char* name_s = ni->node_name ? ni->node_name : ""; const char* name_s = ni->node_name ? ni->node_name : "";
@ -620,8 +629,9 @@ int topo_group_update_my_nodeinfo(struct UTUN_INSTANCE* instance, struct TOPO_GR
} }
int changed = 1; uint8_t old_ver = 0; int changed = 1; uint8_t old_ver = 0;
if (group->local_node && group->local_node->node) { if (group->local_node) {
struct TOPO_NODE* oni = group->local_node->node; struct TOPO_NODE* oni = topo_node_registry_find(instance->topo_groups, group->local_node->node_id);
if (oni) {
old_ver = oni->ver; old_ver = oni->ver;
changed = (vc != (group->local_node->subnets ? topo_list_count((struct _topo_head*)group->local_node->subnets->v4_subnets) : 0)) 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)) || (sock_count != topo_list_count((struct _topo_head*)oni->v4_sock_meta))
@ -644,22 +654,23 @@ int topo_group_update_my_nodeinfo(struct UTUN_INSTANCE* instance, struct TOPO_GR
if (group->local_node && group->instance && group->instance->topo_groups) 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 (memcmp(oni->ed25519_public_key, group->ed25519_public_key, SC_PUBKEY_SIZE) != 0) changed = 1;
} }
}
if (changed) { if (changed) {
if (group->local_node) { if (group->local_node) {
if (instance->rt) route_delete(instance->rt, 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); u_free(group->local_node);
group->local_node = NULL; 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; 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)); struct TOPO_NODE* ni = u_calloc(1, sizeof(struct TOPO_NODE));
if (!ni) { u_free(lq); group->local_node = NULL; return -1; } 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->flags = (group->group_type == TOPO_GROUP_TYPE_CHAT) ? 0 : TOPO_FLAG_SEND_SUBNETS;
ni->group_id = group->group_id; ni->group_id = group->group_id;
ni->node_id = instance->node_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); 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; } } 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); topo_node_registry_store(instance->topo_groups, ni);
lq->hash_node_id = ni->node_id; lq->node_id = ni->node_id;
lq->hop_count = 0; lq->hop_count = 0;
lq->dirty = 1; lq->dirty = 1;
lq->last_ver = ni->ver; 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)) 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"); DEBUG_WARN(DEBUG_CATEGORY_ROUTING, "failed to insert local routes");
} else { } 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; return vc;
} }

9
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; return count;
} }
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) {
if (!db || !nq || !nq->node) return -1; if (!db || !nq) return -1;
struct TOPO_NODE* ni = nq->node; struct TOPO_NODE* ni = topo_node_registry_find(groups, nq->node_id);
if (!ni) return -1;
sqlite3_exec(db, "BEGIN", NULL, NULL, NULL); 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)); struct TOPO_NODE* ni = u_calloc(1, sizeof(struct TOPO_NODE));
if (!ni) { u_free(name); return NULL; } 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); memcpy(ni->public_key, pubkey, 32); memcpy(ni->ed25519_public_key, ed_pubkey, 32);
ni->node_name = name; ni->node_name = name;
ni->v4_addrs = v4_head; ni->v4_addrs = v4_head;

6
src/routing_layer/topo_node_sqlite.h

@ -5,9 +5,7 @@
#include <stdint.h> #include <stdint.h>
#include <stddef.h> #include <stddef.h>
#include <time.h> #include <time.h>
#include "topo_node.h"
struct TOPO_NODEQ;
struct TOPO_GROUPS;
#define PEERS_JOIN_SIG_SIZE 64 #define PEERS_JOIN_SIG_SIZE 64
#define PEERS_JOIN_TS_SIZE 8 #define PEERS_JOIN_TS_SIZE 8
@ -18,7 +16,7 @@ struct TOPO_GROUPS;
int topo_node_sqlite_init(sqlite3* db); 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, int topo_node_sqlite_channel_put(sqlite3* db, const char* channel_id,
const char* name, uint64_t owner_node_id, const char* name, uint64_t owner_node_id,

45
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) */ /* Аналог cm_has_direct_ip: есть ли адреса с прямым доступом (PUBLIC/UNKNOWN/EIM/DIRECT) */
static int node_has_direct_ip(const struct TOPO_NODEQ* nq) { static int node_has_direct_ip(struct TOPO_GROUP* group, const struct TOPO_GROUP_NODE* nq) {
if (!nq || !nq->node) return 0; if (!nq) return 0;
for (const struct TOPO_ADDR4* a = nq->node->v4_addrs; a; a = a->next) { 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; 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->id != a->socket_id) continue;
if (m->config_type == CFG_SERVER_TYPE_PUBLIC || m->config_type == CFG_SERVER_TYPE_UNKNOWN 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) || 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 если неизвестен */ /* Минимальный 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; uint16_t rtt = 0xFFFF;
if (nq->connectivity.interface_status == PROBE_RESULT_REACHABLE && nq->connectivity.interface_min_rtt < rtt) if (nq->connectivity.interface_status == PROBE_RESULT_REACHABLE && nq->connectivity.interface_min_rtt < rtt)
rtt = nq->connectivity.interface_min_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 нам). * hop_list = [...узлы пути...], последний элемент = failed_peer (форвардил NODEINFO нам).
* next_hop = элемент перед failed_peer (hop_list[hop_count-2]). * next_hop = элемент перед failed_peer (hop_list[hop_count-2]).
* Если failed_peer не найден в конце — fallback: node_id сам себе next_hop. */ * Если 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) 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->hop_list[nq->hop_count - 2];
return nq->hash_node_id; return nq->node_id;
} }
/* Ищет незапущенный (started==0) контекст по next_hop_id */ /* Ищет незапущенный (started==0) контекст по next_hop_id */
@ -117,7 +119,10 @@ static void topo_recovery_timeout_cb(void* arg) {
ctx->connect_timer = NULL; ctx->connect_timer = NULL;
uint64_t node_id = ctx->current_node_id; 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); 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; int list = ctx->current_list;
for (size_t i = 0; i < ctx->count[list]; i++) 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; } 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, (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_list == 0 ? "direct" : "indirect", best->is_next_hop ? "[next_hop]" : "");
ctx->current_node_id = node_id; ctx->current_node_id = node_id;
int rc = conn_mgr_connect_node(ctx->group->conn_mgr, node_id, 0, topo_recovery_callback, ctx); ctx->current_handle = conn_mgr_connect_node(ctx->group->conn_mgr, node_id, 0, topo_recovery_callback, ctx);
if (rc != CONN_MGR_OK) { if (!ctx->current_handle) {
DEBUG_WARN(DEBUG_CATEGORY_BGP, "recovery: conn_mgr_connect_node %016llx returned %d", (unsigned long long)node_id, rc); 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]--; *best = ctx->nodes[ctx->current_list][ctx->count[ctx->current_list] - 1]; ctx->count[ctx->current_list]--;
ctx->current_node_id = 0; ctx->current_node_id = 0;
continue; 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) { void topo_recovery_add_node(struct TOPO_GROUP* group, struct TOPO_GROUP_NODE* nq, uint64_t failed_peer) {
if (!group || !nq || !nq->node) return; if (!group || !nq) return;
uint64_t node_id = nq->hash_node_id; 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); uint64_t next_hop = find_next_hop(nq, failed_peer);
struct TOPO_RECOVERY_CTX* ctx = topo_recovery_find_by_next_hop(group, next_hop); 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; 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); uint16_t rtt = node_get_min_rtt(nq);
if (ctx->count[list] >= ctx->capacity[list]) { 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) { if (found) {
DEBUG_INFO(DEBUG_CATEGORY_BGP, "recovery: cancel next=%016llx (node %016llx came up)", 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); (unsigned long long)ctx->next_hop_id, (unsigned long long)node_id);
if (ctx->current_node_id != 0) if (ctx->current_node_id != 0 && ctx->current_handle)
conn_mgr_cancel_callback(group->conn_mgr, ctx->current_node_id, topo_recovery_callback, ctx); 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; } if (ctx->connect_timer) { uasync_cancel_timeout(group->instance->ua, ctx->connect_timer); ctx->connect_timer = NULL; }
*pp = ctx->next; *pp = ctx->next;
topo_recovery_ctx_free(ctx); 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; struct TOPO_RECOVERY_CTX* ctx = group->recovery_list;
while (ctx) { while (ctx) {
struct TOPO_RECOVERY_CTX* next = ctx->next; struct TOPO_RECOVERY_CTX* next = ctx->next;
if (ctx->current_node_id != 0) if (ctx->current_node_id != 0 && ctx->current_handle)
conn_mgr_cancel_callback(group->conn_mgr, ctx->current_node_id, topo_recovery_callback, ctx); 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; } if (ctx->connect_timer) { uasync_cancel_timeout(group->instance->ua, ctx->connect_timer); ctx->connect_timer = NULL; }
topo_recovery_ctx_free(ctx); topo_recovery_ctx_free(ctx);
ctx = next; ctx = next;

5
src/routing_layer/topo_recovery.h

@ -29,7 +29,7 @@ extern "C" {
#include <stddef.h> #include <stddef.h>
struct TOPO_GROUP; struct TOPO_GROUP;
struct TOPO_NODEQ; struct TOPO_GROUP_NODE;
#define TOPO_RECOVERY_CONNECT_TIMEOUT_MS 2000 #define TOPO_RECOVERY_CONNECT_TIMEOUT_MS 2000
@ -49,6 +49,7 @@ struct TOPO_RECOVERY_CTX {
size_t capacity[2]; /* выделенная ёмкость */ size_t capacity[2]; /* выделенная ёмкость */
int current_list; /* 0=direct, 1=indirect */ int current_list; /* 0=direct, 1=indirect */
uint64_t current_node_id; /* node_id в текущей попытке, 0=нет активной */ uint64_t current_node_id; /* node_id в текущей попытке, 0=нет активной */
struct CONN_MGR_HANDLE* current_handle; /* handle текущей попытки conn_mgr_connect_node */
void* connect_timer; /* внешний таймер 2с (uasync) */ void* connect_timer; /* внешний таймер 2с (uasync) */
uint8_t started; /* 0=сбор узлов (add_node), 1=перебор запущен */ 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). * Вызывается из topo_group_remove_conn ДО topo_node_free_lists (нужен hop_list).
* @param failed_peer node_id отвалившегося пира (conn->peer_node_id) * @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-контекстов. * Запускает перебор узлов для всех pending-контекстов.

38
src/transport_layer/etcp.c

@ -276,8 +276,8 @@ struct ETCP_CONN* etcp_connection_create(struct UTUN_INSTANCE* instance, char* n
// Вызываем callback для нового соединения если установлен // Вызываем callback для нового соединения если установлен
if (instance) { if (instance) {
struct etcp_cbk_entry* cbe = instance->new_conn_cbks; struct etcp_inst_cbk_entry* cbe = instance->new_conn_cbks;
while (cbe) { struct etcp_cbk_entry* n = cbe->next; cbe->fn(etcp, cbe->arg); cbe = n; } 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); 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; } 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) { 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_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", 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.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[0], etcp->crypto_ctx.peer_public_key[1],
etcp->crypto_ctx.peer_public_key[2], etcp->crypto_ctx.peer_public_key[3]); etcp->crypto_ctx.peer_public_key[2], etcp->crypto_ctx.peer_public_key[3]);
etcp->callbacks_running = 1; etcp_cbk_fire(etcp, ETCP_CBK_EVENT_UP);
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_fire_conn_status(etcp, ETCP_CONN_STATUS_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", 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->log_name,
etcp->crypto_ctx.session_key[0], etcp->crypto_ctx.session_key[1], 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) { 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); DEBUG_INFO(DEBUG_CATEGORY_ETCP, "[%s] Connection DOWN (links_up=%d)", etcp->log_name, etcp->links_up);
etcp->callbacks_running = 1; etcp_cbk_fire(etcp, ETCP_CBK_EVENT_DOWN);
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_fire_conn_status(etcp, ETCP_CONN_STATUS_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->recv_q);
drain_and_free_queue(&etcp->ack_q); drain_and_free_queue(&etcp->ack_q);
// Free callback chains // Free callback chain
{ 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->cbks; while (cbe) { struct etcp_cbk_entry* n = cbe->next; u_free(cbe); cbe = n; } etcp->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; }
if (etcp->inflight_pool) { memory_pool_destroy(etcp->inflight_pool); etcp->inflight_pool = NULL; } if (etcp->inflight_pool) { memory_pool_destroy(etcp->inflight_pool); etcp->inflight_pool = NULL; }
if (etcp->io_pool) { memory_pool_destroy(etcp->io_pool); etcp->io_pool = NULL; } 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->log_name, etcp->initialized, etcp->links_up, etcp->reinit_count, etcp->tx_state);
etcp->reinit_count++; etcp->reinit_count++;
etcp->reinit_pending = 1;
etcp->reset_done = 0; etcp->reset_done = 0;
etcp->initialized = 0;// еще раз придёт conn_ready_callback 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); etcp_metrics_start_timer(conn);
conn->callbacks_running = 1; etcp_cbk_fire(conn, ETCP_CBK_EVENT_INIT);
{ struct etcp_cbk_entry* cbe = conn->init_cbks; while (cbe) { struct etcp_cbk_entry* n = cbe->next; cbe->fn(conn, cbe->arg); cbe = n; } } if (conn->reinit_pending) { conn->reinit_pending = 0; etcp_cbk_fire(conn, ETCP_CBK_EVENT_REINIT); }
if (conn->links_up) etcp_on_up(conn); if (conn->links_up) etcp_on_up(conn);
conn->callbacks_running = 0;
} }

19
src/transport_layer/etcp.h

@ -20,12 +20,7 @@ struct stcp_link; // forward declaration
// Forward declarations // Forward declarations
struct UTUN_INSTANCE; struct UTUN_INSTANCE;
struct ETCP_CONN; struct ETCP_CONN;
typedef void (*etcp_on_conn_ready)(struct ETCP_CONN* conn, void* arg); struct etcp_cbk_entry; // defined in etcp_api.h
struct etcp_cbk_entry {
etcp_on_conn_ready fn;
void* arg;
struct etcp_cbk_entry* next;
};
struct UASYNC; struct UASYNC;
uint16_t get_current_timestamp(void); uint16_t get_current_timestamp(void);
@ -236,13 +231,15 @@ struct ETCP_CONN {
uint8_t reset_done; // 0 - рукопожатие не завершено (реинит разрешён), 1 - соединение стабильно (реинит заблокирован) uint8_t reset_done; // 0 - рукопожатие не завершено (реинит разрешён), 1 - соединение стабильно (реинит заблокирован)
uint8_t callbacks_running; // 1 - внутри итерации колбэк-цепочек, etcp_connection_close запрещён uint8_t callbacks_running; // 1 - внутри итерации колбэк-цепочек, etcp_connection_close запрещён
// Callback chains for ready/up/down notifications // Unified callback chain with event mask (init/reinit/up/down/node_changed)
struct etcp_cbk_entry* init_cbks; // цепочка callback'ов при инициализации соединения struct etcp_cbk_entry* cbks;
struct etcp_cbk_entry* up_cbks; // цепочка callback'ов при поднятии канала uint8_t reinit_pending; // 1 = reinit в процессе, ждём завершения
struct etcp_cbk_entry* down_cbks; // цепочка callback'ов при падении канала
struct etcp_cbk_entry* node_changed_cbks; // цепочка callback'ов при смене pubkey узла (peer_id совпал, ключ изменился)
void (*bgp_ready_cbk)(struct ETCP_CONN* conn); // вызывается когда BGP готов (завершён или пропущен) 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_inf; // счетчик удлений из inflight
uint32_t cnt_ack_hit_sndq; // счетчик удалений inflight пакетов из sndq uint32_t cnt_ack_hit_sndq; // счетчик удалений inflight пакетов из sndq
uint32_t cnt_ack_miss; // счетчик не найденных ack uint32_t cnt_ack_miss; // счетчик не найденных ack

45
src/transport_layer/etcp_api.c

@ -12,11 +12,12 @@
#define DEBUG_CATEGORY_ETCP_API DEBUG_CATEGORY_ETCP #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; if (!head || !fn) return;
struct etcp_cbk_entry* e = u_malloc(sizeof(struct etcp_cbk_entry)); struct etcp_cbk_entry* e = u_malloc(sizeof(struct etcp_cbk_entry));
if (!e) return; 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; *head = e;
} }
static void etcp_cbk_remove_from_chain(struct etcp_cbk_entry** head, etcp_cbk_fn fn, void* arg) { 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_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_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_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_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_update_cbk_mask(struct ETCP_CONN* conn, etcp_cbk_fn fn, void* arg, int event_mask) {
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); } if (!conn || !fn) return;
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); } struct etcp_cbk_entry* p = conn->cbks;
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); } while (p) { if (p->fn == fn && p->arg == arg) { p->event_mask = event_mask; return; } p = p->next; }
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); } // Instance-level callback chain helpers
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); } static void etcp_inst_cbk_add_chain(struct etcp_inst_cbk_entry** head, etcp_inst_cbk_fn fn, void* 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); } 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) { static void etcp_status_cbk_add_chain(struct etcp_status_cbk_entry** head, etcp_conn_status_fn fn, void* arg) {
if (!head || !fn) return; if (!head || !fn) return;

52
src/transport_layer/etcp_api.h

@ -44,6 +44,7 @@ extern "C" {
#define ETCP_RT_ID_MEDIA_DELIVERY 0x07 // распространение медиа (аудио/видео стриминг) #define ETCP_RT_ID_MEDIA_DELIVERY 0x07 // распространение медиа (аудио/видео стриминг)
#define ETCP_RT_ID_CONN_MGR 0x11 // Connection Manager — management connections #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) // Connection status events (instance-level callback)
#define ETCP_CONN_STATUS_NEW 0 // соединение создано #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 // Forward declaration
struct ETCP_CONN; struct ETCP_CONN;
struct UTUN_INSTANCE; struct UTUN_INSTANCE;
struct TOPO_NODEQ; struct TOPO_GROUP_NODE;
typedef void (*etcp_cbk_fn)(struct ETCP_CONN* conn, void* arg);
// 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 ---- // ---- Background connection initialization ----
#define ETCP_CONNECT_EARLY 1 #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); 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); 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); 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_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); void etcp_remove_conn_status_cbk(struct UTUN_INSTANCE* inst, etcp_conn_status_fn fn, void* arg);

63
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_ready_cb(struct stcp_link* link, void* arg);
static void tcp_link_close_cb(struct stcp_link* link, int err, 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) { static struct ETCP_CONNECT* connect_find(struct UTUN_INSTANCE* inst, uint64_t node_id) {
struct ETCP_CONNECT* ctx = inst->pending_connects; struct ETCP_CONNECT* ctx = inst->pending_connects;
@ -69,9 +69,14 @@ static void connect_cancel(struct ETCP_CONNECT* ctx) {
u_free(ctx); u_free(ctx);
} }
static void connect_create_links_v4(struct ETCP_CONNECT* ctx, struct TOPO_NODEQ* node) { static void connect_create_links_v4(struct ETCP_CONNECT* ctx, struct TOPO_GROUP_NODE* node) {
if (!node || !node->node) return; if (!node) return;
for (const struct TOPO_ADDR4* a = node->node->v4_addrs; a; a = a->next) { 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; 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; } { 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; 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)); struct sockaddr_storage sa; memcpy(&sa, &sin, sizeof(sin));
if (!(a->protocol & TOPO_PROTO_TCP)) { if (!(a->protocol & TOPO_PROTO_TCP)) {
struct ETCP_SOCKET* s = ctx->instance->etcp_sockets; 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) { 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, 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, .peer_pubkey = ctx->conn->crypto_ctx.peer_public_key, .peer_pubkey_mode = 0,
.remote_addr = &sa, .remote_port = a->port}; .remote_addr = &sa, .remote_port = a->port};
ctx->tcp_link = stcp_link_connect(&tcp_cfg); 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) { static void connect_create_links_v6(struct ETCP_CONNECT* ctx, struct TOPO_GROUP_NODE* node) {
if (!node || !node->node) return; if (!node) return;
for (const struct TOPO_ADDR6* a = node->node->v6_addrs; a; a = a->next) { 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; 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; 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; 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; if (!ctx || ctx->done || ctx->tcp_ready) return;
ctx->tcp_ready = 1; 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); 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) { 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) { static void connect_bgp_ready_cb(struct ETCP_CONN* conn) {
struct ETCP_CONNECT* ctx = connect_find(conn->instance, conn->peer_node_id); 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; if (!ctx || ctx->done) return;
connect_deliver(ctx, ETCP_CONNECT_BGP_READY, 1); 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); (unsigned long long)ctx->node_id);
stcp_link_close(ctx->tcp_link); ctx->tcp_link = NULL; 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_deliver(ctx, ETCP_CONNECT_LATE, 1);
connect_cancel(ctx); 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; struct ETCP_CONNECT* ctx = (struct ETCP_CONNECT*)arg;
if (!ctx || ctx->done) return; 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"); 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) { etcp_connect_callback_t cb, void* arg, uint8_t flags) {
if (!inst || !node || !cb) return -1; 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_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"); DEBUG_ERROR(DEBUG_CATEGORY_ETCP_CONNECT, "[etcp_connect] node_id=0 and public_key is NULL");
return -1; return -1;
} }
int zero = 1; int zero = 1;
for (int i = 0; i < SC_PUBKEY_SIZE; i++) 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) { if (zero) {
DEBUG_ERROR(DEBUG_CATEGORY_ETCP_CONNECT, "[etcp_connect] node_id=0 and public_key is all zeros"); DEBUG_ERROR(DEBUG_CATEGORY_ETCP_CONNECT, "[etcp_connect] node_id=0 and public_key is all zeros");
return -1; return -1;
} }
node_id = sc_derive_node_id_from_pubkey(node->node->public_key); node_id = sc_derive_node_id_from_pubkey(ni->public_key);
node->node->node_id = node_id; ni->node_id = node_id;
} }
struct ETCP_CONN* conn = instance_find_conn(inst, 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)); struct etcp_connect_cb_node* cn = u_calloc(1, sizeof(struct etcp_connect_cb_node));
if (!cn) { u_free(ctx); return -1; } if (!cn) { u_free(ctx); return -1; }
cn->cb = cb; cn->arg = arg; cn->flags = flags; ctx->cb_list = cn; 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; conn->bgp_ready_cbk = connect_bgp_ready_cb;
ctx->initial_timer = uasync_set_timeout(inst->ua, (int)inst->etcp_connect_timeout_tb, ctx, ctx->initial_timer = uasync_set_timeout(inst->ua, (int)inst->etcp_connect_timeout_tb, ctx,
connect_initial_timeout_cb, "etcp_connect_init"); 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); DEBUG_ERROR(DEBUG_CATEGORY_ETCP_CONNECT, "[etcp_connect] sc_init_ctx failed for node 0x%016llx", (unsigned long long)node_id);
etcp_connection_close(conn); cb(arg, NULL, 0); return -1; etcp_connection_close(conn); cb(arg, NULL, 0); return -1;
} }
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); 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; 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; 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; conn->bgp_ready_cbk = connect_bgp_ready_cb;
connect_create_links_v4(ctx, node); connect_create_links_v4(ctx, node);
connect_create_links_v6(ctx, node); connect_create_links_v6(ctx, node);
if (!conn->links && !conn->transport_link) { if (!conn->links && !conn->transport_link) {
DEBUG_ERROR(DEBUG_CATEGORY_ETCP_CONNECT, "[etcp_connect] no links created for node 0x%016llx", 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); (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; } if (ctx->tcp_link) { stcp_link_close(ctx->tcp_link); ctx->tcp_link = NULL; }
connect_deliver(ctx, 0, 1); connect_deliver(ctx, 0, 1);
etcp_connection_close(conn); u_free(ctx); return -1; etcp_connection_close(conn); u_free(ctx); return -1;

8
src/transport_layer/etcp_connect.h

@ -5,7 +5,7 @@
struct UTUN_INSTANCE; struct UTUN_INSTANCE;
struct ETCP_CONN; struct ETCP_CONN;
struct TOPO_NODEQ; struct TOPO_GROUP_NODE;
typedef void (*etcp_connect_callback_t)(void* arg, struct ETCP_CONN* conn, int type); 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 inst UTUN_INSTANCE
* @param node узел-адресат (TOPO_NODEQ). Из node->node берутся: * @param node узел-адресат (TOPO_GROUP_NODE). Из node->node берутся:
* - node_id — идентификатор узла (0 = вычислить из pubkey) * - node_id — идентификатор узла (0 = вычислить из pubkey)
* - public_key — X25519 pubkey (32 байта) * - public_key — X25519 pubkey (32 байта)
* - v4_addrs / v6_addrs — адреса для создания линков * - 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-таймер): * connect_settle_timeout_cb (settle-таймер):
* ├─ Закрывает неинициализированные линки (!link->initialized) * ├─ Закрывает неинициализированные линки (!link->initialized)
* ├─ Закрывает TCP-линк если !tcp_ready * ├─ Закрывает TCP-линк если !tcp_ready
* ├─ Убирает connect_init_cb из conn->init_cbks * ├─ Убирает connect_init_cb из conn->cbks
* └─ connect_deliver(ctx, ETCP_CONNECT_LATE) → ctx освобождается * └─ connect_deliver(ctx, ETCP_CONNECT_LATE) → ctx освобождается
* *
* connect_bgp_ready_cb (вызывается при routing_exchange_active >= 3): * 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); etcp_connect_callback_t cb, void* arg, uint8_t flags);
/* /*

26
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; if (!conn) return;
conn->transport_link = link; conn->transport_link = link;
snprintf(conn->log_name, sizeof(conn->log_name), "tcp-[%p]", (void*)link); snprintf(conn->log_name, sizeof(conn->log_name), "tcp-[%p]", (void*)link);
struct etcp_cbk_entry* cbe = inst->new_conn_cbks; struct etcp_inst_cbk_entry* cbe = inst->new_conn_cbks;
while (cbe) { struct etcp_cbk_entry* n = cbe->next; cbe->fn(conn, cbe->arg); cbe = n; } while (cbe) { struct etcp_inst_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; } } etcp_cbk_fire(conn, ETCP_CBK_EVENT_INIT);
DEBUG_INFO(DEBUG_CATEGORY_ETCP, "TCP server new conn=%p total=%d pending=%d", DEBUG_INFO(DEBUG_CATEGORY_ETCP, "TCP server new conn=%p total=%d pending=%d",
(void*)conn, (void*)conn,
queue_entry_count(inst->connections), 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->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->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); if (link->etcp->got_initial_pkt == 0) etcp_link_send_init(link,1,0);
else etcp_link_send_init(link,0,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) { if (g) {
topo_group_update_my_nodeinfo(g->instance, g); topo_group_update_my_nodeinfo(g->instance, g);
if (g->local_node && g->senders_list) { 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; g->local_node->dirty = 1;
struct ll_entry* se = g->senders_list->head; struct ll_entry* se = g->senders_list->head;
while (se) { 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); *(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); DEBUG_ERROR(DEBUG_CATEGORY_CRYPTO, "peer key mismatch for node %016llx, firing node_changed callbacks", (unsigned long long)peer_id);
conn->callbacks_running = 1; conn->callbacks_running = 1;
struct etcp_cbk_entry* cbe = conn->node_changed_cbks; etcp_cbk_fire(conn, ETCP_CBK_EVENT_NODE_CHANGED);
while (cbe) { struct etcp_cbk_entry* n = cbe->next; cbe->fn(conn, cbe->arg); cbe = n; }
conn->callbacks_running = 0; conn->callbacks_running = 0;
goto ec_fr; goto ec_fr;
}// коллизия - peer id совпал а ключи разные. }// коллизия - 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); 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) // 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_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); DEBUG_ERROR(DEBUG_CATEGORY_CRYPTO, "pubkey mismatch on reused link for node %016llx", (unsigned long long)peer_id);
conn->callbacks_running = 1; conn->callbacks_running = 1;
struct etcp_cbk_entry* cbe = conn->node_changed_cbks; etcp_cbk_fire(conn, ETCP_CBK_EVENT_NODE_CHANGED);
while (cbe) { struct etcp_cbk_entry* n = cbe->next; cbe->fn(conn, cbe->arg); cbe = n; }
conn->callbacks_running = 0; conn->callbacks_running = 0;
errorcode = 67; errorcode = 67;
goto ec_fr; 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", 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); (unsigned long long)peer_id, old_conn->log_name);
old_conn->callbacks_running = 1; old_conn->callbacks_running = 1;
struct etcp_cbk_entry* cbe = old_conn->node_changed_cbks; etcp_cbk_fire(old_conn, ETCP_CBK_EVENT_NODE_CHANGED);
while (cbe) { struct etcp_cbk_entry* n = cbe->next; cbe->fn(old_conn, cbe->arg); cbe = n; }
old_conn->callbacks_running = 0; old_conn->callbacks_running = 0;
errorcode = 67; errorcode = 67;
goto ec_fr; goto ec_fr;

670
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 <string.h>
#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;
}

67
src/transport_layer/node_conn_direct.h

@ -0,0 +1,67 @@
#ifndef NODE_CONN_DIRECT_H
#define NODE_CONN_DIRECT_H
#include <stdint.h>
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

2
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] struct ll_queue* tcp_connections; // indexed by node_id, data=tcp_conn_entry [TCP-only]
// Callback chain for new ETCP connections // 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 struct etcp_status_cbk_entry* conn_status_cbks; // instance-level: NEW/UP/DOWN/DELETE
void* test_user_ptr; // Generic user pointer (used by tests) void* test_user_ptr; // Generic user pointer (used by tests)

5
tests/Makefile.am

@ -50,6 +50,7 @@ check_PROGRAMS = \
test_conn_mgr \ test_conn_mgr \
test_conn_mgr_already_connected \ test_conn_mgr_already_connected \
test_etcp_connect \ test_etcp_connect \
test_node_conn_direct \
test_db_sync \ test_db_sync \
test_merkle_sync \ test_merkle_sync \
test_chat_sync_stress \ 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_CFLAGS = -I$(top_srcdir)/src -I$(top_srcdir)/lib
test_etcp_connect_LDADD = $(LIBUTUN) $(CRYPTO_LIBS) $(COMMON_LIBS) 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_SOURCES = test_db_sync.c
test_db_sync_CFLAGS = -I$(top_srcdir)/src -I$(top_srcdir)/lib test_db_sync_CFLAGS = -I$(top_srcdir)/src -I$(top_srcdir)/lib
test_db_sync_LDADD = $(LIBUTUN) $(CRYPTO_LIBS) $(COMMON_LIBS) test_db_sync_LDADD = $(LIBUTUN) $(CRYPTO_LIBS) $(COMMON_LIBS)

2
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++) { for (size_t i = 0; i < inst->rt->count; i++) {
struct ROUTE_ENTRY* e = &inst->rt->entries[i]; struct ROUTE_ENTRY* e = &inst->rt->entries[i];
if (e->network == network && e->prefix_length == prefix_len && 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; return 0;
} }

8
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) { 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; if (!nq || !nq->paths) return 0;
return queue_entry_count(nq->paths); return queue_entry_count(nq->paths);
} }
static int node_path_has_peer(struct UTUN_INSTANCE* inst, uint64_t node_id, uint64_t peer_id) { 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; if (!nq || !nq->paths) return 0;
struct ll_entry* e = nq->paths->head; struct ll_entry* e = nq->paths->head;
while (e) { 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) { 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; 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; int cnt = 0;
for (size_t i = 0; i < inst->rt->count; i++) for (size_t i = 0; i < inst->rt->count; i++)
if (inst->rt->entries[i].v_node_info && 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; return cnt;
} }

4
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); 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; } 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 (!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; } 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 (!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; } 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; } 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); fprintf(stderr, " OK: UNREACHABLE, node cleaned up\n"); fflush(stderr);

7
tests/test_conn_mgr_already_connected.c

@ -72,7 +72,7 @@ static void test_mgr_connect(void* arg) {
(void)arg; (void)arg;
cb_fired = 0; cb_ok = 0; cb_fired = 0; cb_ok = 0;
struct CONN_MGR* mgr = g_a->conn_mgr; 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); } if (nq) { fprintf(stderr, "node already in UTUN group\n"); fflush(stderr); }
fprintf(stderr, "Test 2: conn_mgr_connect_node(B)\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); 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); struct ETCP_CONN* c = instance_find_conn(g_a, g_b->node_id);
if (c) { fprintf(stderr, "Test3: SKIP — conn already established\n"); return; } 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); 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); uasync_call_soon(ua, NULL, test3_check);
} }

27
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++; } 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 ----- */ /* ----- helper: create minimal TOPO_GROUP_NODE with 1 v4 addr ----- */
static struct TOPO_NODEQ* mknode(uint64_t nid, const uint8_t pubkey[32], 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) { 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)); struct TOPO_NODE* ni = u_calloc(1, sizeof(struct TOPO_NODE));
if (!nq || !ni) { u_free(nq); u_free(ni); return NULL; } 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); memcpy(ni->public_key, pubkey, SC_PUBKEY_SIZE);
nq->ll.size = sizeof(struct TOPO_NODEQ) - sizeof(struct ll_entry); nq->ll.size = sizeof(struct TOPO_GROUP_NODE) - sizeof(struct ll_entry);
nq->node = ni; nq->hash_node_id = nid; nq->node_id = nid;
struct TOPO_SOCKMETA4* sm = u_calloc(1, sizeof(struct TOPO_SOCKMETA4)); 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; } 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; } 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; return nq;
} }
@ -87,7 +88,7 @@ static struct TOPO_NODEQ* mknode(uint64_t nid, const uint8_t pubkey[32],
static void test1(void* arg) { static void test1(void* arg) {
(void)arg; if (result) return; (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; } 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; } 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); fprintf(stderr, "Test 1: already connected → EARLY|LATE\n"); fflush(stderr);
cb_type = 0; cb_conn_ok = 0; cb_count = 0; 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); fprintf(stderr, "Test 3: unreachable node → error\n"); fflush(stderr);
struct SC_MYKEYS fk; sc_generate_keypair(&fk); struct SC_MYKEYS fk; sc_generate_keypair(&fk);
uint64_t fnid = 0xF000000000000001ULL; 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; } if (!fn) { fail("mknode failed"); return; }
queue_data_put_with_index(topo_groups_get_default(g_a->topo_groups)->nodes, &fn->ll); queue_data_put_with_index(topo_groups_get_default(g_a->topo_groups)->nodes, &fn->ll);
g_a->etcp_connect_timeout_tb = 2000; 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); fprintf(stderr, "Test 5: double connect to unreachable → both callbacks\n"); fflush(stderr);
struct SC_MYKEYS fk; sc_generate_keypair(&fk); struct SC_MYKEYS fk; sc_generate_keypair(&fk);
uint64_t fnid = 0xF000000000000002ULL; 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; } if (!fn) { fail("mknode failed"); return; }
queue_data_put_with_index(topo_groups_get_default(g_a->topo_groups)->nodes, &fn->ll); queue_data_put_with_index(topo_groups_get_default(g_a->topo_groups)->nodes, &fn->ll);
g_a->etcp_connect_timeout_tb = 2000; g_a->etcp_connect_timeout_tb = 2000;
@ -162,7 +163,7 @@ static void test6(void* arg) {
/* ======================== Test 7: EARLY-only flag ======================== */ /* ======================== Test 7: EARLY-only flag ======================== */
static void test7(void* arg) { static void test7(void* arg) {
(void)arg; if (result) return; (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; } 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); fprintf(stderr, "Test 7: EARLY-only flag\n"); fflush(stderr);
cb_type = 0; cb_conn_ok = 0; cb_count = 0; cb_type = 0; cb_conn_ok = 0; cb_count = 0;
@ -182,7 +183,7 @@ static void test8(void* arg) {
/* ======================== Test 9: LATE-only flag ======================== */ /* ======================== Test 9: LATE-only flag ======================== */
static void test9(void* arg) { static void test9(void* arg) {
(void)arg; if (result) return; (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; } 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); fprintf(stderr, "Test 9: LATE-only flag\n"); fflush(stderr);
cb_type = 0; cb_conn_ok = 0; cb_count = 0; cb_type = 0; cb_conn_ok = 0; cb_count = 0;

24
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;i<len;i++) buf[i]=(uint8_t)((i^seq^0xA5)&0xFF); for (int i=0;i<len;i++) buf[i]=(uint8_t)((i^seq^0xA5)&0xFF);
} }
static struct TOPO_NODEQ* mknode(uint64_t nid, const uint8_t pubkey[32], 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) { 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)); struct TOPO_NODE* ni = u_calloc(1, sizeof(struct TOPO_NODE));
if (!nq || !ni) { u_free(nq); u_free(ni); return NULL; } 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); memcpy(ni->public_key, pubkey, SC_PUBKEY_SIZE);
nq->node = ni; nq->hash_node_id = nid; nq->ll.size = sizeof(struct TOPO_GROUP_NODE) - sizeof(struct ll_entry);
struct TOPO_SOCKMETA4* sm = u_calloc(1, sizeof(struct TOPO_SOCKMETA4)); 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; } 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; } 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; return nq;
} }
@ -197,8 +199,8 @@ static void state_step(void) {
g_b=utun_instance_create(ua,b_conf); 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; } 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; 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_GROUP_NODE* nq_a = mknode(g_b, 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_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; } if (!nq_a || !nq_c) { fprintf(stderr,"FAIL: mknode\n"); g_test_ok=-1; return; }
static int cc_a=0,cc_c=1; static int cc_a=0,cc_c=1;
etcp_connect(g_b,nq_a,connect_bgp_ready_cb,&cc_a,ETCP_CONNECT_BGP_READY); 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); 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_GROUP_NODE* nq_a = mknode(g_b, 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_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; } if (!nq_a || !nq_c) { fprintf(stderr,"FAIL: mknode\n"); goto fail; }
g_b->etcp_connect_timeout_tb = 300000; g_b->etcp_connect_timeout_tb = 300000;
{ static int cc_a=0,cc_c=1; { static int cc_a=0,cc_c=1;

12
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)); inst->topo_groups = u_calloc(1, sizeof(struct TOPO_GROUPS));
if (!inst->topo_groups) { printf(" setup_bgp: topo_groups alloc failed\n"); return; } 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->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)); struct ll_entry* qe = queue_entry_new(sizeof(struct TOPO_GROUP));
if (!qe) { printf(" setup_bgp: queue_entry_new failed\n"); return; } 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->instance = inst;
group->group_id = TOPO_GROUP_UTUN; group->group_id = TOPO_GROUP_UTUN;
group->nodes = queue_new(inst->ua, BGP_NODES_HASH_SIZE, 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; } 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); 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; } 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)); struct TOPO_NODE* ni = u_calloc(1, sizeof(struct TOPO_NODE));
if (!ni) { printf(" setup_bgp: ni alloc failed\n"); u_free(nq); return; } 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); 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); queue_data_put_with_index(group->nodes, &nq->ll);
} }

29
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, static int verify_ipv6_local_nodeinfo(const char* name, struct UTUN_INSTANCE* inst,
int expect_socks, int expect_addrs, int expect_subnets) { 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) { struct TOPO_GROUPS* groups = inst ? inst->topo_groups : NULL;
printf("FAIL [%s]: no bgp/local_node\n", name); struct TOPO_GROUP* grp = groups ? topo_groups_get_default(groups) : NULL;
return 0; 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);
struct TOPO_NODE* ni = topo_groups_get_default(inst->topo_groups)->local_node->node; if (!ni) { printf("FAIL [%s]: local node not in registry\n", name); return 0; }
struct ETCP_SOCKET* es = inst->etcp_sockets; struct ETCP_SOCKET* es = inst->etcp_sockets;
int v6_sock_found = 0; 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) { 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_GROUPS* groups = inst ? inst->topo_groups : NULL;
struct TOPO_NODEQ* nq = topo_node_find_by_id(topo_groups_get_default(inst->topo_groups), peer_node_id); if (!groups) { printf("FAIL [%s]: no bgp\n", name); return 0; }
if (!nq || !nq->node) { printf("FAIL [%s]: remote node %016llx not found\n", name, (unsigned long long)peer_node_id); 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); 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; } 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*)nq->node->v6_sock_meta)); 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; } 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*)nq->node->v6_addrs)); 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) { if (a) {
uint8_t loopback[16] = {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1}; 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; } if (memcmp(a->addr, loopback, 16) != 0) { printf("FAIL [%s]: remote v6 addr not ::1\n", name); return 0; }

26
tests/test_nat_detection.c

@ -311,9 +311,9 @@ int main(void) {
} }
// 5. Verify all NAT fields on server // 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) { 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; goto cleanup;
} }
if (link_sc1->nat_check_status != NAT_CHECK_EIM) { 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) // 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) { 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; 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) { for (const struct TOPO_SOCKMETA4* m = lni->v4_sock_meta; m; m = m->next) {
if (m->id == sock_c1->sock_id) { if (m->id == sock_c1->sock_id) {
if (m->nat_type != NAT_VERIFIED_EIM) { 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); 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; } 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 // Verify NAT addr in flat address list
int nat_found = 0; 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)) { if (a->type == TOPO_ADDR_NAT && a->socket_id == (sock_c1->sock_id | 1)) {
uint32_t addr_ip; memcpy(&addr_ip, a->addr, 4); uint32_t addr_ip; memcpy(&addr_ip, a->addr, 4);
if (addr_ip != link_sc1->nat_ip || a->port != link_sc1->nat_port) { if (addr_ip != link_sc1->nat_ip || a->port != link_sc1->nat_port) {
@ -389,15 +391,19 @@ 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; } 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"); 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) // 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..."); DEBUG_INFO(DEBUG_CATEGORY_BGP, "Waiting for C2 to learn updated C1 nodeinfo...");
bgp_wait_cycles = 0; bgp_wait_cycles = 0;
int c2_verified_nat = 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) { 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; 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) { cni = node_c1_on_c2 ? topo_node_registry_find(inst_c2->topo_groups, node_c1_on_c2->node_id) : NULL;
for (const struct TOPO_SOCKMETA4* m = node_c1_on_c2->node->v4_sock_meta; m; m = m->next) { 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 (m->nat_type == NAT_VERIFIED_EIM) { c2_verified_nat = 1; break; }
} }
if (c2_verified_nat) 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, 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, target_ip_net, link_sc1->nat_port,
3, 10, 200, 3000, nat_ping_resp_cb, NULL, 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) { if (ret != 0) {
DEBUG_ERROR(DEBUG_CATEGORY_BGP, "route_ping_send_req_addr failed: %d", ret); DEBUG_ERROR(DEBUG_CATEGORY_BGP, "route_ping_send_req_addr failed: %d", ret);
goto cleanup; 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, 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, target_ip_net, link_sc1->nat_port,
3, 10, 200, 3000, nat_ping_resp_cb, NULL, 3, 10, 200, 3000, nat_ping_resp_cb, NULL,
node_c1->node->public_key); cni->public_key);
if (ret != 0) { if (ret != 0) {
DEBUG_ERROR(DEBUG_CATEGORY_BGP, "route_ping_send_req_addr (no pubkey) failed: %d", ret); DEBUG_ERROR(DEBUG_CATEGORY_BGP, "route_ping_send_req_addr (no pubkey) failed: %d", ret);
goto cleanup; goto cleanup;

251
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 <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdarg.h>
#include "../lib/platform_compat.h"
#include "test_utils.h"
#ifndef _WIN32
#include <unistd.h>
#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;
}

41
tests/test_route6_lib.c

@ -19,14 +19,14 @@ static int tests_run = 0;
/* ──── helpers ──── */ /* ──── 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 addrs[][16],
const uint8_t *plens, int N) { 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)); struct TOPO_NODE *ni = u_calloc(1, sizeof(struct TOPO_NODE));
if (!nq || !ni) { u_free(nq); u_free(ni); return NULL; } if (!nq || !ni) { u_free(nq); u_free(ni); return NULL; }
ni->ref_count = 1; ni->node_id = node_id; ni->group_ref_count = 1; ni->node_id = node_id;
nq->node = ni; nq->hash_node_id = node_id; nq->node_id = node_id;
if (N > 0) { if (N > 0) {
struct TOPO_NODESUBNETS *r = u_calloc(1, sizeof(struct TOPO_NODESUBNETS)); struct TOPO_NODESUBNETS *r = u_calloc(1, sizeof(struct TOPO_NODESUBNETS));
nq->subnets = r; nq->subnets = r;
@ -41,14 +41,13 @@ static struct TOPO_NODEQ *make_node(uint64_t node_id,
return nq; return nq;
} }
static void free_node(struct TOPO_NODEQ *nq) { static void free_node(struct TOPO_GROUP_NODE *nq) {
if (nq) { if (nq) {
if (nq->subnets) { if (nq->subnets) {
struct TOPO_SUBNET6 *s = nq->subnets->v6_subnets; struct TOPO_SUBNET6 *s = nq->subnets->v6_subnets;
while (s) { struct TOPO_SUBNET6 *next = s->next; u_free(s); s = next; } while (s) { struct TOPO_SUBNET6 *next = s->next; u_free(s); s = next; }
u_free(nq->subnets); u_free(nq->subnets);
} }
if (nq->node) u_free(nq->node);
u_free(nq); u_free(nq);
} }
} }
@ -69,7 +68,7 @@ static void test_insert_null_args(void) {
assert(t != NULL); assert(t != NULL);
assert(route6_insert(NULL, NULL) == false); assert(route6_insert(NULL, NULL) == false);
assert(route6_insert(t, 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); route6_table_destroy(t);
uasync_destroy(ua, 0); uasync_destroy(ua, 0);
} }
@ -81,7 +80,7 @@ static void test_insert_zero_subnets(void) {
int addr[16] = {0}; int addr[16] = {0};
uint8_t zero_plen = 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); assert(route6_insert(t, nq) == false);
const uint8_t any[16] = {0}; const uint8_t any[16] = {0};
@ -99,7 +98,7 @@ static void test_insert_duplicate(void) {
uint8_t addr[16] = {0xfd, 0}; uint8_t addr[16] = {0xfd, 0};
uint8_t plen = 64; 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);
assert(route6_insert(t, nq) == true); // re-insert should not crash 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 addrs[2][16] = {{0xfd, 1}, {0xfd, 2}};
uint8_t plens[2] = {64, 64}; 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_insert(t, nq) == true);
assert(route6_lookup(t, addrs[0]) != NULL); 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 addr[16] = {0xfd, 10};
uint8_t plen = 64; uint8_t plen = 64;
struct TOPO_NODEQ *nq1 = make_node(1, &addr, &plen, 1); struct TOPO_GROUP_NODE *nq1 = make_node(1, &addr, &plen, 1);
struct TOPO_NODEQ *nq2 = make_node(2, &addr, &plen, 1); struct TOPO_GROUP_NODE *nq2 = make_node(2, &addr, &plen, 1);
assert(route6_insert(t, nq1) == true); assert(route6_insert(t, nq1) == true);
route6_delete(t, nq2); // nq2 not in table — should not crash 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(NULL, NULL);
route6_delete(t, 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); route6_table_destroy(t);
uasync_destroy(ua, 0); 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 addrs[3][16] = {{0xfc, 1}, {0xfc, 2}, {0xfc, 3}};
uint8_t plens[3] = {48, 56, 64}; 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_insert(t, nq) == true);
assert(route6_lookup(t, addrs[0]) != NULL); 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_a[16] = {0xfc, 0};
uint8_t addr_b[16] = {0xfd, 0}; uint8_t addr_b[16] = {0xfd, 0};
uint8_t plen = 64; uint8_t plen = 64;
struct TOPO_NODEQ *nqa = make_node(100, &addr_a, &plen, 1); struct TOPO_GROUP_NODE *nqa = make_node(100, &addr_a, &plen, 1);
struct TOPO_NODEQ *nqb = make_node(200, &addr_b, &plen, 1); struct TOPO_GROUP_NODE *nqb = make_node(200, &addr_b, &plen, 1);
assert(route6_insert(t, nqa) == true); assert(route6_insert(t, nqa) == true);
assert(route6_insert(t, nqb) == 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 addr[16] = {0xfe, 0x80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1};
uint8_t plen = 64; 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); assert(route6_insert(t, nq) == true);
const struct ROUTE6_DATA *rd = route6_lookup(t, addr); const struct ROUTE6_DATA *rd = route6_lookup(t, addr);
@ -301,7 +300,7 @@ static void test_stress(void) {
struct saved_route sr[MAX_SAVED]; struct saved_route sr[MAX_SAVED];
int nsr = 0; 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 // create nodes with random subnets
int n_created = 0; int n_created = 0;
@ -333,7 +332,7 @@ static void test_stress(void) {
if (rand() % 10 < 6 && n_created > 0) { if (rand() % 10 < 6 && n_created > 0) {
// delete random active node // delete random active node
int idx = rand() % n_created; 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]); route6_delete(t, nodes[idx]);
// mark all routes of this node inactive // mark all routes of this node inactive
for (int j = 0; j < nsr; j++) { for (int j = 0; j < nsr; j++) {
@ -392,7 +391,7 @@ static void test_stress(void) {
{ {
uint8_t addr[16] = {0xfc, 0}; uint8_t addr[16] = {0xfc, 0};
uint8_t plen = 64; 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 route6_delete(t, ghost); // should no-op
free_node(ghost); free_node(ghost);
} }
@ -401,7 +400,7 @@ static void test_stress(void) {
{ {
uint8_t addr[16] = {0}; uint8_t addr[16] = {0};
uint8_t zero_plen = 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); assert(route6_insert(t, z) == false);
free_node(z); free_node(z);
} }

28
tests/test_route_lib.c

@ -2,7 +2,7 @@
* FULL REFACTORED TEST for new NODEINFO-based routing system * FULL REFACTORED TEST for new NODEINFO-based routing system
* *
* Tests: * Tests:
* - TOPO_NODEQ creation and insertion * - TOPO_GROUP_NODE creation and insertion
* - paths management (add/remove) * - paths management (add/remove)
* - versioning (ver field) * - versioning (ver field)
* - WITHDRAW when no paths left * - WITHDRAW when no paths left
@ -40,17 +40,16 @@ struct UTUN_INSTANCE {
struct TOPO_GROUP* group; struct TOPO_GROUP* group;
}; };
/* Helper to create test TOPO_NODEQ with subnet in linked list */ /* Helper to create test TOPO_GROUP_NODE 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) { static struct TOPO_GROUP_NODE* 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)); struct TOPO_GROUP_NODE* nq = u_calloc(1, sizeof(struct TOPO_GROUP_NODE));
if (!nq) return NULL; if (!nq) return NULL;
struct TOPO_NODE* ni = u_calloc(1, sizeof(struct TOPO_NODE)); struct TOPO_NODE* ni = u_calloc(1, sizeof(struct TOPO_NODE));
if (!ni) { u_free(nq); return NULL; } if (!ni) { u_free(nq); return NULL; }
ni->ref_count = 1; ni->group_ref_count = 1;
ni->node_id = htobe64(node_id); ni->node_id = htobe64(node_id);
ni->ver = ver; ni->ver = ver;
nq->node = ni; nq->node_id = ni->node_id;
nq->hash_node_id = ni->node_id;
nq->hop_count = 0; nq->hop_count = 0;
nq->paths = queue_new(NULL, 16, 0, 0, "test_paths"); nq->paths = queue_new(NULL, 16, 0, 0, "test_paths");
struct TOPO_NODESUBNETS* r = u_calloc(1, sizeof(struct TOPO_NODESUBNETS)); 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; 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) {
if (nq->paths) queue_free(nq->paths); if (nq->paths) queue_free(nq->paths);
if (nq->subnets) { u_free(nq->subnets->v4_subnets); u_free(nq->subnets); } 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); u_free(nq);
} }
} }
@ -118,9 +116,9 @@ static void test_table_create_destroy(void) {
} }
static void test_nodeinfo_insert(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 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); bool inserted = route_insert(t, nq);
ASSERT(inserted, "insert nodeinfo"); ASSERT(inserted, "insert nodeinfo");
@ -134,8 +132,8 @@ static void test_nodeinfo_insert(void) {
static void test_versioning(void) { static void test_versioning(void) {
TEST("NODEINFO multiple nodes"); TEST("NODEINFO multiple nodes");
struct ROUTE_TABLE *t = route_table_create(); struct ROUTE_TABLE *t = route_table_create();
struct TOPO_NODEQ* nq1 = create_test_node(0x1111ULL, 5, 0x0a000000, 8); struct TOPO_GROUP_NODE* nq1 = create_test_node(0x1111ULL, 5, 0x0a000000, 8);
struct TOPO_NODEQ* nq2 = create_test_node(0x2222ULL, 1, 0xac100000, 12); struct TOPO_GROUP_NODE* nq2 = create_test_node(0x2222ULL, 1, 0xac100000, 12);
route_insert(t, nq1); route_insert(t, nq1);
bool ins2 = route_insert(t, nq2); bool ins2 = route_insert(t, nq2);
@ -157,7 +155,7 @@ static void test_performance_1000_nodes(void) {
for (int i = 0; i < 1000; i++) { for (int i = 0; i < 1000; i++) {
uint32_t sub = 0x0a000000 + (i * 0x100); /* distinct 10.0.x.0/24 to avoid overlap */ 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); route_insert(t, nq);
/* note: nq not freed here (table holds ref to it), freed in test cleanup if needed */ /* 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) { static void test_destroy_refcount(void) {
TEST("destroy with multiple paths (ref_count safety)"); TEST("destroy with multiple paths (ref_count safety)");
struct ROUTE_TABLE *t = route_table_create(); 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); route_table_destroy(t);
PASS(); PASS();
} }

9
tests/test_route_ping.c

@ -268,9 +268,10 @@ int main(void) {
/* addr[] в network byte order, port в host byte order */ /* addr[] в network byte order, port в host byte order */
uint32_t target_ip = 0; uint32_t target_ip = 0;
uint16_t target_port = 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; 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;
if (nq && nq->node) { struct TOPO_NODE* ni = nq ? topo_node_registry_find(inst_b->topo_groups, nq->node_id) : NULL;
const struct TOPO_ADDR4* a = nq->node->v4_addrs; if (nq && ni) {
const struct TOPO_ADDR4* a = ni->v4_addrs;
if (a) { if (a) {
memcpy(&target_ip, a->addr, 4); memcpy(&target_ip, a->addr, 4);
target_port = a->port; target_port = a->port;
@ -290,7 +291,7 @@ int main(void) {
200, // timeout_ms 200, // timeout_ms
5000,// wait_timeout_ms 5000,// wait_timeout_ms
ping_resp_cb, NULL, ping_resp_cb, NULL,
nq ? nq->node->public_key : NULL); ni ? ni->public_key : NULL);
if (ret != 0) { if (ret != 0) {
DEBUG_ERROR(DEBUG_CATEGORY_BGP, "route_ping_send_req_addr failed: %d", ret); DEBUG_ERROR(DEBUG_CATEGORY_BGP, "route_ping_send_req_addr failed: %d", ret);
goto cleanup; goto cleanup;

11
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(srv_inst, 1, srv_recv_cb) >= 0);
TASSERT(etcp_bind(cli_inst, 2, cli_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); 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 // Increase timeout for TCP STCP handshake
cli_inst->etcp_connect_timeout_tb = 100000; // 10 seconds 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); TASSERT(etcp_connect(cli_inst, srv_node, connect_cb, NULL, ETCP_CONNECT_EARLY | ETCP_CONNECT_LATE) == 0);

Loading…
Cancel
Save