chat: user-friendly INFO logs for invite/connect flow

- chat_sync.c: invite entry, JOINED/TIMEOUT messages
- conn_mgr_core.c: invite start (addrs+udp+tcp), NCD UP/DOWN/TIMEOUT,
  TCP connect/ready/close, membership check send/receive/confirm/deny,
  overall timeout
- node_conn_direct.c: NCD event names instead of numbers
This commit is contained in:
evgeny committed 2026-08-02 20:32:01 +03:00
1 parent c35135cb52
commit c2191aad3b
34 files changed
+597 -230

No files matched your search

+1
View File
@@ -34,6 +34,7 @@ extern "C" {
#define CHAT_EVT_SERVICE_STOPPED 13 /* data: none */
#define CHAT_EVT_ATTACHMENT_DOWNLOADED 14 /* [ch_id_len:1][ch_id:var][msg_id:8] */
#define CHAT_EVT_DOWNLOAD_PROGRESS 15 /* [ch_id_len:1][ch_id:var][msg_id:8][blocks_done:4][num_blocks:4] */
#define CHAT_EVT_NODEINFO_UPDATED 16 /* [group_id:8][node_id:8][conn_presence:1][conn_up:1][best_rtt:2] */
typedef void (*chat_event_handler_fn)(int type, const uint8_t* data, int len);
+50 -75
View File
@@ -600,61 +600,47 @@ struct chat_invite {
int addr_count;
};
/* коллбэк invite-подключения: conn_mgr сам всё делает, здесь только лог результата.
реальный join-протокол запускается через cs_on_conn_up (глобальный callback),
сохранение в БД — через cs_handle_channel_info_resp после верификации */
static void cs_invite_conn_cb(struct CONN_MGR_HANDLE* h,
uint64_t node_id, uint64_t group_id,
enum conn_mgr_event event, void* arg) {
(void)arg;
if (event == CONN_EVENT_JOIN) {
DEBUG_INFO(DEBUG_CATEGORY_GENERAL, "channel invite: JOINED — connected to 0x%016llx, online now",
(unsigned long long)node_id);
struct UTUN_INSTANCE* inst = chat_core_get_inst();
if (inst && inst->topo_groups) {
struct TOPO_GROUP* g = topo_groups_find(inst->topo_groups, group_id);
if (g) {
struct TOPO_GROUP_NODE* nq = topo_node_find_by_id(g, node_id);
if (nq) nq->handle = h;
}
}
} else if (event == CONN_EVENT_TIMEOUT) {
DEBUG_WARN(DEBUG_CATEGORY_GENERAL, "channel invite: TIMEOUT — could not connect to 0x%016llx",
(unsigned long long)node_id);
struct UTUN_INSTANCE* inst = chat_core_get_inst();
if (inst && inst->topo_groups) {
struct TOPO_GROUP* g = topo_groups_find(inst->topo_groups, group_id);
if (g) {
struct TOPO_GROUP_NODE* nq = topo_node_find_by_id(g, node_id);
if (nq) nq->handle = NULL;
}
}
}
}
struct cm_invite_wrap { struct chat_invite inv; };
static void cm_invite_trampoline(void* arg) {
struct cm_invite_wrap* w = (struct cm_invite_wrap*)arg;
struct chat_invite* inv = &w->inv;
struct UTUN_INSTANCE* inst = chat_core_get_inst();
sqlite3* db = chat_core_get_db();
uint64_t node_id = inv->node_id;
uint64_t channel_id = inv->channel_id;
DEBUG_INFO(DEBUG_CATEGORY_DB_SYNC, "%s: invite start ch=%llu node=0x%016llx addrs=%d",
CS_ID, channel_id, node_id, inv->addr_count);
/* save pubkey to nodes */
sqlite3_stmt* st = NULL;
sqlite3_prepare_v2(db, "INSERT INTO nodes(node_id,x25519_pubkey,created_at) VALUES(?,?,?)"
" ON CONFLICT(node_id) DO UPDATE SET x25519_pubkey=excluded.x25519_pubkey,"
" created_at=COALESCE(nodes.created_at, excluded.created_at)", -1, &st, NULL);
if (st) { sqlite3_bind_int64(st, 1, (sqlite3_int64)node_id); sqlite3_bind_blob(st, 2, inv->pubkey, 32, SQLITE_STATIC); sqlite3_bind_int64(st, 3, 0); sqlite3_step(st); sqlite3_finalize(st); }
/* save invite addresses (socket_id=0) */
sqlite3_prepare_v2(db, "DELETE FROM node_addresses WHERE node_id=? AND socket_id=0", -1, &st, NULL);
if (st) { sqlite3_bind_int64(st, 1, (sqlite3_int64)node_id); sqlite3_step(st); sqlite3_finalize(st); }
sqlite3_prepare_v2(db, "INSERT INTO node_addresses(node_id,family,protocol,address,port,addr_type,socket_id) VALUES(?,?,?,?,?,0,?)", -1, &st, NULL);
if (st) {
const uint8_t* ap = inv->addrs_data;
for (int i = 0; i < inv->addr_count; i++) {
uint8_t family = *ap++; ap++; /* skip sock_id */
uint8_t proto = *ap++; /* read proto */
if (family == 4) {
sqlite3_bind_int64(st, 1, (sqlite3_int64)node_id); sqlite3_bind_int(st, 2, 4); sqlite3_bind_int(st, 3, proto);
sqlite3_bind_blob(st, 4, ap, 4, SQLITE_STATIC); ap += 4;
uint16_t port = ((uint16_t)ap[0] << 8) | ap[1]; ap += 2;
sqlite3_bind_int(st, 5, (int)port); sqlite3_bind_int(st, 6, 1);
sqlite3_step(st); sqlite3_reset(st);
} else if (family == 6) {
sqlite3_bind_int64(st, 1, (sqlite3_int64)node_id); sqlite3_bind_int(st, 2, 6); sqlite3_bind_int(st, 3, proto);
sqlite3_bind_blob(st, 4, ap, 16, SQLITE_STATIC); ap += 16;
uint16_t port = ((uint16_t)ap[0] << 8) | ap[1]; ap += 2;
sqlite3_bind_int(st, 5, (int)port); sqlite3_bind_int(st, 6, 1);
sqlite3_step(st); sqlite3_reset(st);
} else { ap += 18; }
}
sqlite3_finalize(st);
}
char ch_str[32]; snprintf(ch_str, sizeof(ch_str), "%llu", (unsigned long long)channel_id);
/* TOPO_GROUP (lightweight) for conn_mgr_connect_from_invite below.
DB channel + sync created later in cs_handle_channel_info_resp. */
if (inst->topo_groups) {
if (!topo_groups_find(inst->topo_groups, channel_id))
topo_groups_create_group(inst->topo_groups, channel_id, TOPO_GROUP_TYPE_CHAT, ch_str);
}
/* build TOPO_NODE from invite data */
struct TOPO_NODE* ni = u_calloc(1, sizeof(*ni));
if (!ni) { u_free(w); return; }
@@ -664,8 +650,8 @@ static void cm_invite_trampoline(void* arg) {
uint8_t family = *ap++; uint8_t sock_id = *ap++; uint8_t proto = *ap++;
uint16_t port = 0;
if (family == 4) { port = ((uint16_t)ap[4] << 8) | ap[5]; }
DEBUG_DEBUG(DEBUG_CATEGORY_DB_SYNC, "%s: invite addr[%d] family=%d proto=%d sock=%d port=%d",
CS_ID, i, family, proto, sock_id, port);
else if (family == 6) { port = ((uint16_t)ap[16] << 8) | ap[17]; }
const char* proto_str = proto == 1 ? "UDP" : proto == 2 ? "TCP" : proto == 3 ? "UDP+TCP" : "?";
if (family == 4) {
struct TOPO_ADDR4* a4 = u_calloc(1, sizeof(*a4));
if (!a4) break;
@@ -683,32 +669,20 @@ static void cm_invite_trampoline(void* arg) {
} else { ap += 18; }
}
struct ETCP_CONN* existing = instance_find_conn(inst, node_id);
if (!existing) existing = cs_find_conn_by_pubkey(inst, inv->pubkey, &node_id);
if (!ni->v4_addrs && !ni->v6_addrs) {
DEBUG_ERROR(DEBUG_CATEGORY_GENERAL, "invite: no valid addrs for node=0x%016llx", (unsigned long long)node_id);
while (ni->v4_addrs) { struct TOPO_ADDR4* n = ni->v4_addrs->next; u_free(ni->v4_addrs); ni->v4_addrs = n; }
while (ni->v6_addrs) { struct TOPO_ADDR6* n = ni->v6_addrs->next; u_free(ni->v6_addrs); ni->v6_addrs = n; }
u_free(ni); u_free(w); return;
}
if (existing) {
if (existing->links_up) {
DEBUG_INFO(DEBUG_CATEGORY_DB_SYNC, "%s: invite start — already connected peer=0x%016llx, joining ch=%llu",
CS_ID, (unsigned long long)node_id, (unsigned long long)channel_id);
if (node_id != existing->peer_node_id)
DEBUG_WARN(DEBUG_CATEGORY_DB_SYNC, "%s: invite node_id mismatch: derived=%016llx ETCP=%016llx — using ETCP",
CS_ID, (unsigned long long)node_id, (unsigned long long)existing->peer_node_id);
g_cs->pending_invite_ch_id = channel_id;
g_cs->pending_invite_node_id = existing->peer_node_id;
cs_start_channel_join(g_cs, existing->peer_node_id);
cs_on_peer_status_changed(existing->peer_node_id, 1);
} else {
DEBUG_INFO(DEBUG_CATEGORY_DB_SYNC, "%s: invite conn in progress node=0x%016llx, waiting UP", CS_ID, (unsigned long long)node_id);
}
} else if (inst->topo_groups && (ni->v4_addrs || ni->v6_addrs)) {
struct TOPO_GROUP* g = topo_groups_find(inst->topo_groups, channel_id);
if (g && g->conn_mgr) {
conn_mgr_open_invite(inst, channel_id, ni, node_id, NULL, NULL, NULL);
DEBUG_INFO(DEBUG_CATEGORY_DB_SYNC, "%s: invite connect posted node=0x%016llx", CS_ID, (unsigned long long)node_id);
}
} else {
DEBUG_ERROR(DEBUG_CATEGORY_DB_SYNC, "%s: invite no addrs topo=%p v4=%p v6=%p", CS_ID,
(void*)inst->topo_groups, (void*)ni->v4_addrs, (void*)ni->v6_addrs);
/* conn_mgr_open_invite сам создаст TOPO_GROUP, найдёт/создаст соединение, запустит INVITE_INFO.
DB channel + sync создаются позже в cs_handle_channel_info_resp после верификации */
DEBUG_INFO(DEBUG_CATEGORY_GENERAL, "channel invite: connecting to 0x%016llx via %d addresses",
(unsigned long long)node_id, inv->addr_count);
int r = conn_mgr_open_invite(inst, channel_id, ni, node_id, cs_invite_conn_cb, NULL, NULL);
if (r < 0) {
DEBUG_ERROR(DEBUG_CATEGORY_GENERAL, "channel invite: FAILED — conn_mgr_open_invite error=%d", r);
}
/* free caller-owned ni */
@@ -720,7 +694,8 @@ static void cm_invite_trampoline(void* arg) {
void chat_sync_connect_from_invite(uint64_t channel_id, uint64_t node_id,
const uint8_t* pubkey_bin,
const uint8_t* addrs_data, int addr_count) {
const uint8_t* addrs_data, int addr_count,
int addrs_data_len) {
if (!g_cs || !g_cs->inst || !g_cs->inst->ua) {
int r = -7;
#ifdef __ANDROID__
@@ -741,7 +716,7 @@ void chat_sync_connect_from_invite(uint64_t channel_id, uint64_t node_id,
inv->node_id = node_id;
memcpy(inv->pubkey, pubkey_bin, 32);
size_t addrs_sz = (size_t)addr_count * 9;
size_t addrs_sz = addrs_data_len > 0 ? (size_t)addrs_data_len : (size_t)addr_count * 9;
inv->addrs_data = u_malloc(addrs_sz);
if (!inv->addrs_data) { u_free(inv); return; }
memcpy(inv->addrs_data, addrs_data, addrs_sz);
+2 -1
View File
@@ -75,7 +75,8 @@ void chat_sync_connect_node(struct UTUN_INSTANCE* inst, uint64_t node_id);
/* Подключиться к пиру по данным invite-ссылки (вызывается из uasync-потока) */
void chat_sync_connect_from_invite(uint64_t channel_id, uint64_t node_id,
const uint8_t* pubkey_bin,
const uint8_t* addrs_data, int addr_count);
const uint8_t* addrs_data, int addr_count,
int addrs_data_len);
/* Присоединиться к каналу через уже подключённый узел (uasync-поток).
Соединение с target_node_id должно быть установлено (links_up && initialized).
+39 -33
View File
@@ -206,6 +206,9 @@ void cm_update_nodeinfo(struct CONN_MGR_ENTRY* entry) {
nq->conn_mgr_type = entry->conn_type;
memcpy(nq->conn_mgr_intermediaries, entry->intermediaries, sizeof(entry->intermediaries));
nq->conn_mgr_intermediariy_count = entry->intermediariy_count;
if (entry->conn_type == CONN_TYPE_INDIRECT) { nq->conn_presence |= NCONN_INDIRECT; nq->conn_up |= NCONN_INDIRECT; }
else { nq->conn_presence &= ~NCONN_INDIRECT; nq->conn_up &= ~NCONN_INDIRECT; }
topo_fire_nodeinfo_cbk(entry->mgr->instance, entry->mgr->group, nq);
}
void cm_cleanup_db_node(struct CONN_MGR_ENTRY* entry) {
@@ -235,14 +238,16 @@ void cm_ncd_callback(struct NODE_CONN_DIRECT* ncd_h, enum ncd_event ncd_ev, void
switch (ncd_ev) {
case NCD_EVENT_UP:
if (entry->state == CONN_MGR_STATE_CONNECTED) return;
DEBUG_INFO(DEBUG_CATEGORY_GENERAL, "conn_mgr: NCD UP for 0x%016llx", (unsigned long long)entry->node_id);
DEBUG_INFO(DEBUG_CATEGORY_GENERAL, "link: ETCP handshake OK with 0x%016llx — connection ESTABLISHED",
(unsigned long long)entry->node_id);
if (entry->main.timer) { uasync_cancel_timeout(entry->mgr->instance->ua, entry->main.timer); entry->main.timer = NULL; }
entry->main_connect_state = CM_TRY_OK; entry->conn_type = CONN_TYPE_DIRECT;
entry->state = CONN_MGR_STATE_CONNECTED;
cm_update_nodeinfo(entry); cm_deliver_event(entry, CONN_EVENT_UP);
break;
case NCD_EVENT_TIMEOUT:
DEBUG_INFO(DEBUG_CATEGORY_GENERAL, "conn_mgr: NCD TIMEOUT for 0x%016llx", (unsigned long long)entry->node_id);
DEBUG_INFO(DEBUG_CATEGORY_GENERAL, "link: ETCP connect TIMEOUT to 0x%016llx — no response, trying fallback",
(unsigned long long)entry->node_id);
entry->ncd_handle = NULL;
if (entry->db_loaded) {
entry->main_connect_state = CM_TRY_FAILED;
@@ -261,7 +266,8 @@ void cm_ncd_callback(struct NODE_CONN_DIRECT* ncd_h, enum ncd_event ncd_ev, void
if (entry->local_scan_state == CM_TRY_FAILED) cm_deliver_event(entry, CONN_EVENT_TIMEOUT);
break;
case NCD_EVENT_DOWN:
DEBUG_WARN(DEBUG_CATEGORY_GENERAL, "conn_mgr: NCD DOWN for 0x%016llx", (unsigned long long)entry->node_id);
DEBUG_WARN(DEBUG_CATEGORY_GENERAL, "link: ETCP link DROPPED to 0x%016llx",
(unsigned long long)entry->node_id);
entry->ncd_handle = NULL;
cm_deliver_event(entry, CONN_EVENT_DOWN);
break;
@@ -275,7 +281,8 @@ void cm_invite_ncd_callback(struct NODE_CONN_DIRECT* ncd_h, enum ncd_event ncd_e
switch (ncd_ev) {
case NCD_EVENT_UP:
if (inv->state != CM_INVITE_CONNECTING) return;
DEBUG_INFO(DEBUG_CATEGORY_GENERAL, "conn_mgr: invite INIT OK node=0x%016llx group=0x%016llx", (unsigned long long)inv->node_id, (unsigned long long)inv->mgr->group->group_id);
DEBUG_INFO(DEBUG_CATEGORY_GENERAL, "invite: ETCP handshake OK with 0x%016llx — sending membership check",
(unsigned long long)inv->node_id);
inv->state = CM_INVITE_WAIT_INFO;
{ struct CM_INVITE_REQ req; memset(&req, 0, sizeof(req));
req.cmd = ETCP_RT_ID_CONN_MGR; req.subcmd = CM_SUBCMD_INVITE_INFO_REQ; req.group_id = inv->mgr->group->group_id;
@@ -284,15 +291,15 @@ void cm_invite_ncd_callback(struct NODE_CONN_DIRECT* ncd_h, enum ncd_event ncd_e
qe->dgram = u_malloc(sizeof(req)); if (!qe->dgram) { queue_entry_free(qe); cm_invite_fail(inv); return; }
memcpy(qe->dgram, &req, sizeof(req)); qe->len = sizeof(req);
etcp_send(node_conn_direct_get_conn(ncd_h), qe);
DEBUG_INFO(DEBUG_CATEGORY_GENERAL, "conn_mgr: sent INVITE_INFO_REQ to 0x%016llx group=0x%016llx",
(unsigned long long)inv->node_id, (unsigned long long)inv->mgr->group->group_id);
}
break;
case NCD_EVENT_TIMEOUT:
DEBUG_INFO(DEBUG_CATEGORY_GENERAL, "conn_mgr: invite connect timeout node=0x%016llx", (unsigned long long)inv->node_id);
DEBUG_INFO(DEBUG_CATEGORY_GENERAL, "invite: ETCP connect TIMEOUT to 0x%016llx — no response from peer",
(unsigned long long)inv->node_id);
cm_invite_fail(inv); break;
case NCD_EVENT_DOWN:
DEBUG_WARN(DEBUG_CATEGORY_GENERAL, "conn_mgr: invite conn down node=0x%016llx", (unsigned long long)inv->node_id);
DEBUG_WARN(DEBUG_CATEGORY_GENERAL, "invite: ETCP link DROPPED to 0x%016llx while connecting",
(unsigned long long)inv->node_id);
if (inv->state == CM_INVITE_CONNECTING) cm_invite_fail(inv);
break;
}
@@ -711,9 +718,8 @@ void conn_mgr_router_recv_handler(struct ETCP_CONN* conn, struct ll_entry* entry
/* ═══════ invite обработчики ═══════ */
void cm_handle_invite_info_req(struct CONN_MGR* mgr, struct ETCP_CONN* conn, const uint8_t* data, size_t len) {
if (len < sizeof(struct CM_INVITE_REQ)) { DEBUG_WARN(DEBUG_CATEGORY_GENERAL, "conn_mgr: INVITE_INFO_REQ too short (%zu)", len); return; }
if (len < sizeof(struct CM_INVITE_REQ)) { DEBUG_WARN(DEBUG_CATEGORY_GENERAL, "invite: INVITE_INFO_REQ too short (%zu)", len); return; }
struct CM_INVITE_REQ* req = (struct CM_INVITE_REQ*)data;
DEBUG_INFO(DEBUG_CATEGORY_GENERAL, "conn_mgr: INVITE_INFO_REQ from 0x%016llx group=0x%016llx", (unsigned long long)conn->peer_node_id, (unsigned long long)req->group_id);
struct TOPO_GROUP* grp = topo_groups_find(mgr->instance->topo_groups, req->group_id);
struct TOPO_GROUP* sg = grp ? grp : mgr->group;
struct TOPO_NODE* ln = sg->local_node ? topo_node_registry_find(sg->instance->topo_groups, sg->local_node->node_id) : NULL;
@@ -725,19 +731,21 @@ void cm_handle_invite_info_req(struct CONN_MGR* mgr, struct ETCP_CONN* conn, con
if (nl) memcpy(resp->node_name, nm, nl);
struct ll_entry* qe = queue_entry_new(0);
if (qe) { qe->dgram=(uint8_t*)resp; qe->len=(uint16_t)rs; etcp_send(conn, qe); } else u_free(resp);
DEBUG_INFO(DEBUG_CATEGORY_GENERAL, "conn_mgr: sent INVITE_INFO_RESP to 0x%016llx name_len=%zu", (unsigned long long)conn->peer_node_id, nl);
DEBUG_INFO(DEBUG_CATEGORY_GENERAL, "invite: membership check RECEIVED from 0x%016llx — responding is_member=yes name=\"%.*s\"",
(unsigned long long)conn->peer_node_id, (int)nl, nm ? nm : "");
}
void cm_handle_invite_info_resp(struct CONN_MGR* mgr, const uint8_t* data, size_t len) {
if (len < sizeof(struct CM_INVITE_RESP)) { DEBUG_WARN(DEBUG_CATEGORY_GENERAL, "conn_mgr: INVITE_INFO_RESP too short (%zu)", len); return; }
if (len < sizeof(struct CM_INVITE_RESP)) { DEBUG_WARN(DEBUG_CATEGORY_GENERAL, "invite: INVITE_INFO_RESP too short (%zu)", len); return; }
struct CM_INVITE_RESP* resp = (struct CM_INVITE_RESP*)data;
if (len < sizeof(struct CM_INVITE_RESP) + (size_t)resp->node_name_len) { DEBUG_WARN(DEBUG_CATEGORY_GENERAL, "conn_mgr: INVITE_INFO_RESP truncated"); return; }
if (len < sizeof(struct CM_INVITE_RESP) + (size_t)resp->node_name_len) { DEBUG_WARN(DEBUG_CATEGORY_GENERAL, "invite: INVITE_INFO_RESP truncated"); return; }
struct cm_invite_pending* inv = NULL;
for (struct cm_invite_pending* p = mgr->invite_list; p; p = p->next) if (p->state == CM_INVITE_WAIT_INFO) { inv = p; break; }
if (!inv) { DEBUG_WARN(DEBUG_CATEGORY_GENERAL, "conn_mgr: INVITE_INFO_RESP — no pending invite in WAIT_INFO"); return; }
DEBUG_INFO(DEBUG_CATEGORY_GENERAL, "conn_mgr: INVITE_INFO_RESP node=0x%016llx group=0x%016llx member=%d name_len=%u",
(unsigned long long)inv->node_id, (unsigned long long)resp->group_id, resp->is_member, resp->node_name_len);
if (!resp->is_member) { cm_invite_fail(inv); return; }
if (!inv) { DEBUG_WARN(DEBUG_CATEGORY_GENERAL, "invite: INVITE_INFO_RESP — no pending invite in WAIT_INFO state"); return; }
if (!resp->is_member) {
DEBUG_WARN(DEBUG_CATEGORY_GENERAL, "invite: membership DENIED by 0x%016llx — not in channel", (unsigned long long)inv->node_id);
cm_invite_fail(inv); return;
}
if (inv->temp_nq) {
struct TOPO_NODE* ni2 = topo_node_registry_find(inv->mgr->instance->topo_groups, inv->temp_nq->node_id);
if (ni2) { memcpy(ni2->ed25519_public_key, resp->ed25519_pubkey, SC_PUBKEY_SIZE);
@@ -746,16 +754,16 @@ void cm_handle_invite_info_resp(struct CONN_MGR* mgr, const uint8_t* data, size_
if (inv->mgr->instance->topo_sqlite_db) topo_node_sqlite_node_put(inv->mgr->instance->topo_sqlite_db, inv->mgr->instance->topo_groups, inv->temp_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; }
DEBUG_INFO(DEBUG_CATEGORY_GENERAL, "conn_mgr: invite JOIN OK node=0x%016llx group=0x%016llx name=%.*s",
(unsigned long long)inv->node_id, (unsigned long long)inv->mgr->group->group_id,
resp->node_name_len, resp->node_name_len ? (const char*)resp->node_name : "");
DEBUG_INFO(DEBUG_CATEGORY_GENERAL, "invite: membership CONFIRMED by 0x%016llx name=\"%.*s\" — joined to channel, handle registered",
(unsigned long long)inv->node_id, resp->node_name_len, resp->node_name_len ? (const char*)resp->node_name : "");
if (inv->cb) inv->cb(inv->handle, inv->node_id, inv->mgr->group->group_id, CONN_EVENT_JOIN, inv->cb_arg);
cm_invite_cleanup(inv);
}
void cm_invite_overall_timeout(void* arg) {
struct cm_invite_pending* inv = (struct cm_invite_pending*)arg; inv->overall_timer = NULL;
DEBUG_INFO(DEBUG_CATEGORY_GENERAL, "conn_mgr: invite overall timeout node=0x%016llx", (unsigned long long)inv->node_id);
DEBUG_INFO(DEBUG_CATEGORY_GENERAL, "invite: overall TIMEOUT to 0x%016llx — no response within %ds",
(unsigned long long)inv->node_id, CM_INVITE_DEFAULT_TIMEOUT_MS/1000);
cm_invite_fail(inv);
}
@@ -804,22 +812,21 @@ static void cm_tcp_ready_cb(struct stcp_link* link, void* arg) {
if (!qe) { cm_invite_fail(inv); return; }
{ struct tcp_conn_entry* te = (struct tcp_conn_entry*)qe->data; te->node_id=inv->node_id; te->link=link; te->etcp_conn=etcp; }
queue_data_put_with_index(inv->mgr->instance->tcp_connections, qe);
DEBUG_INFO(DEBUG_CATEGORY_GENERAL, "conn_mgr: TCP invite ready node=0x%016llx group=0x%016llx", (unsigned long long)inv->node_id, (unsigned long long)inv->mgr->group->group_id);
DEBUG_INFO(DEBUG_CATEGORY_GENERAL, "invite: TCP handshake OK with 0x%016llx — sending membership check",
(unsigned long long)inv->node_id);
inv->state = CM_INVITE_WAIT_INFO;
{ struct CM_INVITE_REQ req; memset(&req,0,sizeof(req));
req.cmd=ETCP_RT_ID_CONN_MGR; req.subcmd=CM_SUBCMD_INVITE_INFO_REQ; req.group_id=inv->mgr->group->group_id;
struct ll_entry* qe2 = queue_entry_new(0); if (!qe2) { cm_invite_fail(inv); return; }
qe2->dgram=u_malloc(sizeof(req)); if (!qe2->dgram) { queue_entry_free(qe2); cm_invite_fail(inv); return; }
memcpy(qe2->dgram,&req,sizeof(req)); qe2->len=sizeof(req); etcp_send(etcp, qe2);
DEBUG_INFO(DEBUG_CATEGORY_GENERAL, "conn_mgr: TCP invite sent INVITE_INFO_REQ to 0x%016llx group=0x%016llx",
(unsigned long long)inv->node_id, (unsigned long long)inv->mgr->group->group_id);
}
}
static void cm_tcp_close_cb(struct stcp_link* link, int err, void* arg) {
struct cm_invite_pending* inv = (struct cm_invite_pending*)arg;
if (!inv) return;
DEBUG_INFO(DEBUG_CATEGORY_GENERAL, "conn_mgr: TCP invite closed node=0x%016llx err=%d", (unsigned long long)inv->node_id, err);
DEBUG_INFO(DEBUG_CATEGORY_GENERAL, "invite: TCP link CLOSED to 0x%016llx err=%d", (unsigned long long)inv->node_id, err);
inv->tcp_link = NULL;
}
@@ -833,9 +840,8 @@ static int cm_invite_tcp_connect(struct cm_invite_pending* inv, const struct TOP
.peer_pubkey=ni->public_key, .peer_pubkey_mode=0, .remote_addr=&sa, .remote_port=a->port};
inv->tcp_link = stcp_link_connect(&cfg);
if (inv->tcp_link) { stcp_link_set_on_ready(inv->tcp_link, cm_tcp_ready_cb, inv); stcp_link_set_on_close(inv->tcp_link, cm_tcp_close_cb, inv);
DEBUG_INFO(DEBUG_CATEGORY_GENERAL, "conn_mgr: TCP connect %d.%d.%d.%d:%d node=0x%016llx group=0x%016llx",
a->addr[0], a->addr[1], a->addr[2], a->addr[3], (int)a->port,
(unsigned long long)inv->node_id, (unsigned long long)inv->mgr->group->group_id);
DEBUG_INFO(DEBUG_CATEGORY_GENERAL, "invite: TCP connecting to %d.%d.%d.%d:%d",
a->addr[0], a->addr[1], a->addr[2], a->addr[3], (int)a->port);
return 1; }
return 0;
}
@@ -852,8 +858,8 @@ static int cm_invite_tcp_connect(struct cm_invite_pending* inv, const struct TOP
.peer_pubkey=ni->public_key, .peer_pubkey_mode=0, .remote_addr=&sa, .remote_port=a6->port};
inv->tcp_link = stcp_link_connect(&cfg);
if (inv->tcp_link) { stcp_link_set_on_ready(inv->tcp_link, cm_tcp_ready_cb, inv); stcp_link_set_on_close(inv->tcp_link, cm_tcp_close_cb, inv);
DEBUG_INFO(DEBUG_CATEGORY_GENERAL, "conn_mgr: TCP6 connect port=%d node=0x%016llx group=0x%016llx",
(int)a6->port, (unsigned long long)inv->node_id, (unsigned long long)inv->mgr->group->group_id);
DEBUG_INFO(DEBUG_CATEGORY_GENERAL, "invite: TCP6 connecting to port %d",
(int)a6->port);
return 1; }
return 0;
}
@@ -929,7 +935,7 @@ int conn_mgr_open_invite(struct UTUN_INSTANCE* inst,
inv->overall_timer = uasync_set_timeout(inst->ua, CM_INVITE_DEFAULT_TIMEOUT_MS*10, inv, cm_invite_overall_timeout, "conn_mgr_invite");
{
char addrs[256] = ""; int aoff = 0;
char addrs[512] = ""; int aoff = 0;
for (const struct TOPO_ADDR4* a = gni->v4_addrs; a; a = a->next)
aoff += snprintf(addrs + aoff, sizeof(addrs) - (size_t)aoff, " %d.%d.%d.%d:%d(%s)",
a->addr[0], a->addr[1], a->addr[2], a->addr[3], (int)a->port,
@@ -937,8 +943,8 @@ int conn_mgr_open_invite(struct UTUN_INSTANCE* inst,
for (const struct TOPO_ADDR6* a6 = gni->v6_addrs; a6; a6 = a6->next)
aoff += snprintf(addrs + aoff, sizeof(addrs) - (size_t)aoff, " [v6]:%d(%s)", (int)a6->port,
a6->protocol == TOPO_PROTO_UDP ? "UDP" : a6->protocol == TOPO_PROTO_TCP ? "TCP" : "?");
DEBUG_INFO(DEBUG_CATEGORY_GENERAL, "conn_mgr: invite started node=0x%016llx group=0x%016llx udp=%d tcp=%d%s",
(unsigned long long)nid, (unsigned long long)group_id, udp, tcp, addrs);
DEBUG_INFO(DEBUG_CATEGORY_GENERAL, "invite: starting to 0x%016llx via%s udp=%d tcp=%d timeout=%ds",
(unsigned long long)nid, addrs, udp, tcp, CM_INVITE_DEFAULT_TIMEOUT_MS/1000);
}
return 0;
}
+2 -2
View File
@@ -98,7 +98,7 @@ void cm_handle_interm_exchange_resp(struct CONN_MGR* mgr, const uint8_t* data, s
uint8_t need=0; uint64_t now=get_time_tb();
for(uint8_t i=0;i<ep->cached.my_count&&i<4;i++){
struct TOPO_GROUP_NODE* nq=topo_node_find_by_id(mgr->group,ep->cached.my_candidates[i].node_id);
if(!nq||!cm_is_rtt_fresh(nq,now)){need++;route_connectivity_probe_node(mgr->instance,nq);}
if(!nq||!cm_is_rtt_fresh(nq,now)){need++;route_connectivity_probe_node(mgr->instance,mgr->group,nq);}
}
if(!need){ep->probed=1;cm_compute_intermediaries(ep->entry,&ep->cached);struct cm_exchange_pending**pp=&mgr->exchange_pending;while(*pp){if(*pp==ep){*pp=ep->next;break;}pp=&(*pp)->next;}u_free(ep);return;}
ep->timer=uasync_set_timeout(mgr->instance->ua,2000,ep,cm_exchange_probe_retry_cb,"cm_exch_probe");
@@ -126,7 +126,7 @@ void cm_handle_interm_exchange_req(struct ETCP_CONN* conn, struct CM_EXCHANGE_RE
uint64_t now=get_time_tb();
for(uint8_t i=0;i<req->candidate_count&&i<4;i++){
struct TOPO_GROUP_NODE* nq=topo_node_find_by_id(mgr->group,req->candidates[i].node_id);
if(nq&&cm_is_rtt_fresh(nq,now))continue; route_connectivity_probe_node(mgr->instance,nq);
if(nq&&cm_is_rtt_fresh(nq,now))continue; route_connectivity_probe_node(mgr->instance,mgr->group,nq);
}
struct CM_EXCHANGE_RESP resp; memset(&resp,0,sizeof(resp));
resp.cmd=ETCP_RT_ID_CONN_MGR; resp.subcmd=CM_SUBCMD_INTERM_EXCHANGE_RESP; resp.request_id=req->request_id;
+1 -1
View File
@@ -169,7 +169,7 @@ void cm_candidate_ping_timer_cb(void* arg) {
struct ETCP_CONN* dc = topo_group_find_conn_for_node(mgr->group, nid);
struct TOPO_NODE* ni = topo_node_registry_find(mgr->instance->topo_groups, nq->node_id);
if (ni && dc) etcp_send_ping(mgr->instance, ni->public_key, &dc->links->remote_addr, CONN_PROBE_TIMEOUT_MS, NULL, NULL, NULL, 0);
} else route_connectivity_probe_node(mgr->instance, nq);
} else route_connectivity_probe_node(mgr->instance, mgr->group, nq);
}
mgr->candidate_ping_timer = uasync_set_timeout(mgr->instance->ua, CONN_MGR_CANDIDATE_PING_TB, mgr, cm_candidate_ping_timer_cb, "conn_mgr_cand_ping");
}
+4 -2
View File
@@ -23,6 +23,7 @@
struct conn_probe_ctx {
struct conn_probe_ctx* next; /* linked list in nq->connectivity.probe_list */
struct UTUN_INSTANCE* instance;
struct TOPO_GROUP* group;
struct TOPO_GROUP_NODE* nq;
uint8_t addr_type; // ADDR_TYPE_*
struct sockaddr_storage target_addr;
@@ -283,6 +284,7 @@ static void conn_probe_finish(struct conn_probe_ctx* ctx, int ok) {
c->interface_status, c->nat_status, c->real_status);
if (ctx->instance && ctx->instance->topo_sqlite_db)
topo_node_sqlite_nodeinfo_updated(ctx->instance->topo_sqlite_db, ctx->nq->node_id);
topo_fire_nodeinfo_cbk(ctx->instance, ctx->group, ctx->nq);
}
}
@@ -291,7 +293,7 @@ static void conn_probe_finish(struct conn_probe_ctx* ctx, int ok) {
// ---- public API ----
void route_connectivity_probe_node(struct UTUN_INSTANCE* instance, struct TOPO_GROUP_NODE* nq) {
void route_connectivity_probe_node(struct UTUN_INSTANCE* instance, struct TOPO_GROUP* group, struct TOPO_GROUP_NODE* nq) {
if (!instance || !nq) return;
struct TOPO_NODE* ni = topo_node_registry_find(instance->topo_groups, nq->node_id);
if (!ni) return;
@@ -332,7 +334,7 @@ void route_connectivity_probe_node(struct UTUN_INSTANCE* instance, struct TOPO_G
if (cand_count == 0) { DEBUG_INFO(DEBUG_CATEGORY_BGP, "probe: no local sockets for target %s:%u type=%d", ip_to_str(&ip, AF_INET).str, port, a->type); 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->group = group; ctx->nq = nq; ctx->addr_type = a->type; ctx->target_addr = target;
memcpy(ctx->peer_pubkey, ni->public_key, SC_PUBKEY_SIZE);
memcpy(ctx->candidate_sockets, candidates, cand_count * sizeof(struct ETCP_SOCKET*));
ctx->candidate_count = cand_count; ctx->candidate_index = 0; ctx->count_total = CONN_PROBE_COUNT; ctx->timeout_ms = CONN_PROBE_TIMEOUT_MS; ctx->best_across_sockets = 65535;
+2 -1
View File
@@ -10,9 +10,10 @@ extern "C" {
#include "topo_node.h"
struct UTUN_INSTANCE;
struct TOPO_GROUP;
// Запускает зондирование связности ко всем адресам удалённого узла
void route_connectivity_probe_node(struct UTUN_INSTANCE* instance,
void route_connectivity_probe_node(struct UTUN_INSTANCE* instance, struct TOPO_GROUP* group,
struct TOPO_GROUP_NODE* nq);
// Отменяет все pending пробы для узла (при удалении / withdraw)
+5 -4
View File
@@ -107,13 +107,14 @@ static struct ROUTE_ENTRY* binary_search_lpm(struct ROUTE_TABLE *table, uint32_t
// Основные функции
// ============================================================================
struct ROUTE_TABLE *route_table_create(void) {
struct ROUTE_TABLE *route_table_create(uint64_t my_node_id) {
struct ROUTE_TABLE *table = u_calloc(1, sizeof(struct ROUTE_TABLE));
if (!table) {
DEBUG_ERROR(DEBUG_CATEGORY_ROUTING, "alloc failed");
return NULL;
}
table->my_node_id = my_node_id;
table->entries = u_calloc(INITIAL_CAPACITY, sizeof(struct ROUTE_ENTRY));
if (!table->entries) {
DEBUG_ERROR(DEBUG_CATEGORY_ROUTING, "alloc entries failed");
@@ -180,7 +181,7 @@ bool route_insert(struct ROUTE_TABLE *table, struct TOPO_GROUP_NODE *node) {
struct ROUTE_ENTRY* e = &table->entries[pos];
e->network = network; e->prefix_length = s->prefix_length; e->v_node_info = node;
table->count++;
if (node->hop_count == 0) table->stats.local_routes++; else table->stats.learned_routes++;
if (node->node_id == table->my_node_id) table->stats.local_routes++; else table->stats.learned_routes++;
}
DEBUG_INFO(DEBUG_CATEGORY_ROUTING, "added %zu route(s) for node=%016llx",
@@ -196,7 +197,7 @@ void route_delete(struct ROUTE_TABLE *table, struct TOPO_GROUP_NODE *node) {
if (table->entries[i].v_node_info == node) {
if (i < table->count - 1) memmove(&table->entries[i], &table->entries[i + 1], (table->count - i - 1) * sizeof(struct ROUTE_ENTRY));
table->count--;
if (node->hop_count == 0) table->stats.local_routes--; else table->stats.learned_routes--;
if (node->node_id == table->my_node_id) table->stats.local_routes--; else table->stats.learned_routes--;
removed++; continue;
}
i++;
@@ -236,7 +237,7 @@ void route_table_print(const struct ROUTE_TABLE *table) {
const struct ROUTE_ENTRY *entry = &table->entries[i];
DEBUG_INFO(DEBUG_CATEGORY_ROUTING, " %zu: %s/%d",
i + 1, ip_to_string(entry->network).a, entry->prefix_length);
if (entry->v_node_info && entry->v_node_info->hop_count != 0) {
if (entry->v_node_info && entry->v_node_info->node_id != table->my_node_id) {
DEBUG_INFO(DEBUG_CATEGORY_ROUTING, " v_node_info=%p", (void*)entry->v_node_info);
} else {
DEBUG_INFO(DEBUG_CATEGORY_ROUTING, " LOCAL");
+2 -1
View File
@@ -47,6 +47,7 @@ struct ROUTE_TABLE {
struct ROUTE_ENTRY *entries; /**< Массив записей маршрутов */
size_t count; /**< Текущее количество записей */
size_t capacity; /**< Максимальная емкость таблицы */
uint64_t my_node_id; /**< ID локального узла */
uint32_t *dynamic_subnets; /**< Динамические подсети (массив пар: сеть, префикс) */
size_t dynamic_subnet_count; /**< Количество динамических подсетей */
uint32_t *local_subnets; /**< Локальные подсети (массив пар: сеть, префикс) */
@@ -64,7 +65,7 @@ struct ROUTE_TABLE {
*
* @return Указатель на созданную таблицу или NULL в случае ошибки
*/
struct ROUTE_TABLE *route_table_create(void);
struct ROUTE_TABLE *route_table_create(uint64_t my_node_id);
/**
* @brief Уничтожает таблицу маршрутизации и освобождает ресурсы
+2 -2
View File
@@ -140,7 +140,7 @@ void route_pkt(struct UTUN_INSTANCE* instance, struct ll_entry* entry, uint64_t
struct TOPO_GROUP_NODE* nq = route->v_node_info;
struct TOPO_NODE* rni = nq ? topo_node_registry_find(instance->topo_groups, nq->node_id) : NULL;
if (!nq || !rni || nq->hop_count == 0) {
if (!nq || !rni || nq->node_id == instance->node_id) {
DEBUG_TRACE(DEBUG_CATEGORY_ROUTING, "Local route to %s", ip_to_str(&addr, AF_INET).str);
} else {
DEBUG_TRACE(DEBUG_CATEGORY_ROUTING, "route_pkt: sending %zu bytes to node %016llx dst=%s",
@@ -233,7 +233,7 @@ int routing_create(struct UTUN_INSTANCE* instance) {
}
// Create route table
instance->rt = route_table_create();
instance->rt = route_table_create(instance->node_id);
if (!instance->rt) {
DEBUG_ERROR(DEBUG_CATEGORY_ROUTING, "failed to create route table for node %016llx",
(unsigned long long)instance->node_id);
+91 -34
View File
@@ -201,6 +201,10 @@ static void topo_group_conn_status(struct ETCP_CONN* conn, int status, void* arg
while (fe) { struct TOPO_GROUP* g = (struct TOPO_GROUP*)fe; fe = fe->next;
if (status == ETCP_CONN_STATUS_UP) topo_group_new_conn(g, conn);
if (status == ETCP_CONN_STATUS_DOWN) topo_group_remove_conn(g, conn);
if (status == ETCP_CONN_STATUS_DELETE) {
struct TOPO_GROUP_NODE* nq = topo_node_find_by_id(g, conn->peer_node_id);
if (nq) { nq->conn_presence &= ~NCONN_DIRECT; nq->conn_up &= ~NCONN_DIRECT; }
}
}
}
@@ -408,6 +412,12 @@ void topo_group_new_conn(struct TOPO_GROUP* group, struct ETCP_CONN* conn) {
topo_recovery_cancel_for_node(group, conn->peer_node_id);
struct TOPO_GROUP_NODE* peer_nq = topo_node_find_by_id(group, conn->peer_node_id);
if (peer_nq) {
peer_nq->connectivity.last_ping_time = get_time_tb();
peer_nq->conn_presence |= NCONN_DIRECT;
peer_nq->conn_up |= NCONN_DIRECT;
if (conn->instance->topo_sqlite_db) topo_node_sqlite_nodeinfo_updated(conn->instance->topo_sqlite_db, conn->peer_node_id);
}
topo_group_add_to_senders(group, conn);
DEBUG_DEBUG(DEBUG_CATEGORY_DEBUG, "topo_group_new_conn: peer=%016llx group=%016llx type=%d ch=%s", (unsigned long long)conn->peer_node_id, (unsigned long long)group->group_id, group->group_type, group->channel_id);
@@ -434,21 +444,34 @@ void topo_group_remove_conn(struct TOPO_GROUP* group, struct ETCP_CONN* conn) {
while (node_entry) {
struct ll_entry* next = node_entry->next;
struct TOPO_GROUP_NODE* nq = (struct TOPO_GROUP_NODE*)node_entry;
if (topo_group_remove_path(nq, conn) == 1) {
uint64_t key = nq->node_id;
if (key != conn->peer_node_id) { topo_recovery_add_node(group, nq, conn->peer_node_id); cascaded++; }
if (group->group_type != TOPO_GROUP_TYPE_CHAT && rt) route_delete(rt, nq);
if (conn->instance && conn->instance->control_srv) control_server_notify_node_removed(conn->instance->control_srv, key);
route_connectivity_cancel_node(conn->instance, nq);
if (nq->paths) { queue_free(nq->paths); nq->paths = NULL; }
topo_nodeq_free_group_fields(group->instance->topo_groups, nq);
struct ll_entry* entry = node_entry;
if (entry) { queue_remove_data(group->nodes, entry); queue_entry_free(entry); }
nodes_removed++;
etcp_router_conn_close_all_for_node(group->instance, group->group_id, key);
uint64_t wd_src = (key == conn->peer_node_id) ? conn->instance->node_id : conn->peer_node_id;
topo_group_broadcast_withdraw(group, key, wd_src, NULL);
DEBUG_DEBUG(DEBUG_CATEGORY_BGP, "Removed node %016llx after link down", (unsigned long long)key);
{
uint64_t next_hop = nq->node_id;
struct ll_entry* pe = nq->paths ? nq->paths->head : NULL;
while (pe) { struct TOPO_NODEPATH* p = (struct TOPO_NODEPATH*)pe; if (p->conn == conn && p->hop_count >= 2) { uint64_t* hop = (uint64_t*)((uint8_t*)p + sizeof(struct TOPO_NODEPATH)); if (hop[p->hop_count - 1] == conn->peer_node_id) next_hop = hop[p->hop_count - 2]; break; } pe = pe->next; }
if (topo_group_remove_path(nq, conn) == 1) {
uint64_t key = nq->node_id;
if (key != conn->peer_node_id) { topo_recovery_add_node(group, nq, next_hop); cascaded++; }
nq->conn_presence = 0; nq->conn_up = 0;
topo_fire_nodeinfo_cbk(conn->instance, group, 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);
route_connectivity_cancel_node(conn->instance, nq);
if (nq->paths) { queue_free(nq->paths); nq->paths = NULL; }
topo_nodeq_free_group_fields(group->instance->topo_groups, nq);
struct ll_entry* entry = node_entry;
if (entry) { queue_remove_data(group->nodes, entry); queue_entry_free(entry); }
nodes_removed++;
etcp_router_conn_close_all_for_node(group->instance, group->group_id, key);
uint64_t wd_src = (key == conn->peer_node_id) ? conn->instance->node_id : conn->peer_node_id;
topo_group_broadcast_withdraw(group, key, wd_src, NULL);
DEBUG_DEBUG(DEBUG_CATEGORY_BGP, "Removed node %016llx after link down", (unsigned long long)key);
} else {
nq->conn_up &= ~NCONN_DIRECT;
{ int has_direct = 0; struct ll_entry* pe2 = nq->paths ? nq->paths->head : NULL;
while (pe2) { struct TOPO_NODEPATH* p = (struct TOPO_NODEPATH*)pe2; if (p->conn->peer_node_id == nq->node_id) { has_direct = 1; break; } pe2 = pe2->next; }
if (!has_direct) nq->conn_presence &= ~NCONN_DIRECT; }
topo_fire_nodeinfo_cbk(conn->instance, group, nq);
}
}
node_entry = next;
}
@@ -579,14 +602,15 @@ int topo_group_process_nodeinfo(struct TOPO_GROUP* group, struct ETCP_CONN* from
memcpy(hop_list, hop_src, ni->hop_count * 8);
hop_list[ni->hop_count] = from->peer_node_id;
topo_group_remove_path(nodeinfo1, from);
topo_group_add_path(nodeinfo1, from, hop_list, new_hops, nodeinfo1->cumulative_rtt);
{ uint8_t _hc; uint16_t _rtt; topo_node_best_hop_list(nodeinfo1, &_hc, &_rtt);
topo_group_add_path(nodeinfo1, from, hop_list, new_hops, _rtt); }
}
return 0;
}
DEBUG_INFO(DEBUG_CATEGORY_BGP, "NODEINFO: node=%016llx ver=%d v4s=%d v4a=%d v6s=%d v6a=%d hops=%d from=%s",
(unsigned long long)node_id, new_ver, ni->local_v4_sockets, ni->local_v4_addrs,
ni->local_v6_sockets, ni->local_v6_addrs, ni->hop_count, from->log_name);
(unsigned long long)node_id, new_ver, ni->local_v4_sockets, ni->local_v4_addrs,
ni->local_v6_sockets, ni->local_v6_addrs, ni->hop_count, from->log_name);
struct ll_queue* paths = NULL;
int is_new_node = 0;
@@ -606,6 +630,10 @@ int topo_group_process_nodeinfo(struct TOPO_GROUP* group, struct ETCP_CONN* from
if (nodeinfo1) { queue_remove_data(group->nodes, &nodeinfo1->ll); queue_free(paths); queue_entry_free(&nodeinfo1->ll); }
return -1;
}
{ int v4c = topo_list_count((struct _topo_head*)new_ni->v4_addrs);
int v6c = topo_list_count((struct _topo_head*)new_ni->v6_addrs);
DEBUG_INFO(DEBUG_CATEGORY_BGP, "NODEINFO deser: nid=%016llx v4a=%d v6a=%d from=%s",
(unsigned long long)node_id, v4c, v6c, from->log_name); }
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);
@@ -637,9 +665,6 @@ int topo_group_process_nodeinfo(struct TOPO_GROUP* group, struct ETCP_CONN* from
new_ni->flags = pkt->node.flags;
nodeinfo1->node_id = new_ni ? new_ni->node_id : 0;
nodeinfo1->subnets = new_subnets;
nodeinfo1->cumulative_rtt = incoming_cumulative_rtt;
u_free(nodeinfo1->hop_list); nodeinfo1->hop_list = new_hop_list;
nodeinfo1->hop_count = new_hop_count;
} else {
struct ll_entry* qe = queue_entry_new(sizeof(struct TOPO_GROUP_NODE));
if (!qe) { topo_node_destroy(group->instance->topo_groups, new_ni); u_free(new_hop_list); u_free(new_subnets); return -1; }
@@ -653,8 +678,6 @@ int topo_group_process_nodeinfo(struct TOPO_GROUP* group, struct ETCP_CONN* from
new_ni->flags = pkt->node.flags;
nodeinfo1->node_id = new_ni ? new_ni->node_id : 0;
nodeinfo1->subnets = new_subnets;
nodeinfo1->cumulative_rtt = incoming_cumulative_rtt;
nodeinfo1->hop_list = new_hop_list; nodeinfo1->hop_count = new_hop_count;
nodeinfo1->connectivity.probe_status = PROBE_STATUS_NONE;
nodeinfo1->connectivity.interface_status = PROBE_RESULT_UNKNOWN;
nodeinfo1->connectivity.nat_status = PROBE_RESULT_UNKNOWN;
@@ -666,16 +689,17 @@ int topo_group_process_nodeinfo(struct TOPO_GROUP* group, struct ETCP_CONN* from
nodeinfo1->paths = paths; nodeinfo1->last_ver = new_ver;
uint64_t hop_list[MAX_HOPS];
if (nodeinfo1->hop_count > 0) memcpy(hop_list, nodeinfo1->hop_list, nodeinfo1->hop_count * 8);
hop_list[nodeinfo1->hop_count] = from->peer_node_id;
nodeinfo1->hop_count++;
if (new_hop_count > 0) memcpy(hop_list, new_hop_list, new_hop_count * 8);
hop_list[new_hop_count] = from->peer_node_id;
uint8_t extended_count = new_hop_count + 1;
u_free(nodeinfo1->hop_list);
nodeinfo1->hop_list = u_malloc(nodeinfo1->hop_count * 8);
memcpy(nodeinfo1->hop_list, hop_list, nodeinfo1->hop_count * 8);
u_free(new_hop_list);
topo_group_remove_path_by_hop(nodeinfo1, from->peer_node_id);
topo_group_add_path(nodeinfo1, from, hop_list, nodeinfo1->hop_count, incoming_cumulative_rtt);
topo_group_add_path(nodeinfo1, from, hop_list, extended_count, incoming_cumulative_rtt);
nodeinfo1->conn_presence |= NCONN_BGP; nodeinfo1->conn_up |= NCONN_BGP;
if (from->peer_node_id == node_id) { nodeinfo1->conn_presence |= NCONN_DIRECT; nodeinfo1->conn_up |= NCONN_DIRECT; }
if (group->group_type != TOPO_GROUP_TYPE_CHAT && group->instance->rt) route_insert(group->instance->rt, nodeinfo1);
if (node_id != group->instance->node_id) {
@@ -700,8 +724,9 @@ int topo_group_process_nodeinfo(struct TOPO_GROUP* group, struct ETCP_CONN* from
struct topo_node_cbk_entry* c = group->node_cbks;
while (c) { c->fn(group, node_id, ev, c->arg); c = c->next; }
}
topo_fire_nodeinfo_cbk(group->instance, group, nodeinfo1);
int hop_count = nodeinfo1->hop_count;
int hop_count = extended_count;
struct ll_entry* se = group->senders_list ? group->senders_list->head : NULL;
while (se) {
struct TOPO_GROUP_CONN_ITEM* item = (struct TOPO_GROUP_CONN_ITEM*)se->data;
@@ -725,7 +750,7 @@ int topo_group_process_nodeinfo(struct TOPO_GROUP* group, struct ETCP_CONN* from
struct TOPO_NODE* tni = topo_node_registry_find(group->instance->topo_groups, nodeinfo1->node_id);
int v4a = tni ? topo_list_count((struct _topo_head*)tni->v4_addrs) : 0;
int v6a = tni ? topo_list_count((struct _topo_head*)tni->v6_addrs) : 0;
if (v4a > 0 || v6a > 0) route_connectivity_probe_node(group->instance, nodeinfo1);
if (v4a > 0 || v6a > 0) route_connectivity_probe_node(group->instance, group, nodeinfo1);
}
return 0;
@@ -740,6 +765,8 @@ int topo_group_process_withdraw(struct TOPO_GROUP* group, struct ETCP_CONN* send
if (!nq) { DEBUG_INFO(DEBUG_CATEGORY_BGP, "node not found"); return 0; }
int ret = topo_group_remove_path_by_hop(nq, wd_source);
if (ret == 1) {
nq->conn_presence = 0; nq->conn_up = 0;
topo_fire_nodeinfo_cbk(group->instance, group, nq);
if (group->group_type != TOPO_GROUP_TYPE_CHAT && group->instance && group->instance->rt) route_delete(group->instance->rt, nq);
if (group->instance->control_srv) control_server_notify_node_removed(group->instance->control_srv, node_id);
route_connectivity_cancel_node(group->instance, nq);
@@ -811,9 +838,9 @@ static void topo_group_send_full_table(struct TOPO_GROUP* group, struct ETCP_CON
struct ll_entry* e = group->nodes ? group->nodes->head : NULL;
while (e) {
struct TOPO_GROUP_NODE* nq = (struct TOPO_GROUP_NODE*)e;
if (nq->hop_count == 0) { e = e->next; continue; }
if (nq->node_id == group->instance->node_id) { 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)) { uint8_t _hc; uint16_t _rtt; topo_node_best_hop_list(nq, &_hc, &_rtt); topo_group_send_nodeinfo(group, nq, conn, (uint16_t)((uint32_t)conn->rtt_last + _rtt)); }
e = e->next;
}
}
@@ -850,6 +877,36 @@ void topo_group_remove_node_cbk(struct TOPO_GROUP* group, topo_node_event_fn fn,
}
}
/* ── Global nodeinfo callbacks (instance-level, any node change) ── */
void utun_add_nodeinfo_cbk(struct UTUN_INSTANCE* instance, nodeinfo_cbk_fn fn, void* arg) {
if (!instance || !fn) return;
struct nodeinfo_cbk_entry* e = u_malloc(sizeof(*e));
if (!e) return;
e->fn = fn; e->arg = arg;
e->next = instance->nodeinfo_cbks;
instance->nodeinfo_cbks = e;
}
void utun_remove_nodeinfo_cbk(struct UTUN_INSTANCE* instance, nodeinfo_cbk_fn fn, void* arg) {
if (!instance || !fn) return;
struct nodeinfo_cbk_entry** p = &instance->nodeinfo_cbks;
while (*p) {
if ((*p)->fn == fn && (*p)->arg == arg) {
struct nodeinfo_cbk_entry* r = *p;
*p = r->next; u_free(r);
return;
}
p = &(*p)->next;
}
}
void topo_fire_nodeinfo_cbk(struct UTUN_INSTANCE* instance, struct TOPO_GROUP* group, struct TOPO_GROUP_NODE* node) {
if (!instance || !group || !node) return;
struct nodeinfo_cbk_entry* c = instance->nodeinfo_cbks;
while (c) { c->fn(group, node, c->arg); c = c->next; }
}
void topo_group_send_withdraw(struct TOPO_GROUP* group, uint64_t node_id) {
if (!group) return;
DEBUG_INFO(DEBUG_CATEGORY_BGP, "node=%016llx", (unsigned long long)node_id);
+14
View File
@@ -71,6 +71,20 @@ struct topo_node_cbk_entry {
struct topo_node_cbk_entry* next;
};
/* ── Global nodeinfo callbacks (one per instance, any node change) ── */
typedef void (*nodeinfo_cbk_fn)(struct TOPO_GROUP* group, struct TOPO_GROUP_NODE* node, void* arg);
struct nodeinfo_cbk_entry {
nodeinfo_cbk_fn fn;
void* arg;
struct nodeinfo_cbk_entry* next;
};
void utun_add_nodeinfo_cbk(struct UTUN_INSTANCE* instance, nodeinfo_cbk_fn fn, void* arg);
void utun_remove_nodeinfo_cbk(struct UTUN_INSTANCE* instance, nodeinfo_cbk_fn fn, void* arg);
void topo_fire_nodeinfo_cbk(struct UTUN_INSTANCE* instance, struct TOPO_GROUP* group, struct TOPO_GROUP_NODE* node);
// ETCP ID для пакетов топологии
#define ETCP_ID_TOPO_ENTRY 0x01
+28 -7
View File
@@ -53,6 +53,19 @@ static void free_v6_sub_list(struct memory_pool* pool, struct TOPO_SUBNET6* head
while (head) { struct TOPO_SUBNET6* next = head->next; memory_pool_free(pool, head); head = next; }
}
/* извлекает hop_list из лучшего живого path в nq->paths (или любого, если живых нет).
память принадлежит TOPO_NODEPATH в paths, не освобождать */
uint64_t* topo_node_best_hop_list(struct TOPO_GROUP_NODE* nq, uint8_t* out_count, uint16_t* out_rtt) {
if (!nq || !nq->paths || !nq->paths->head) { *out_count = 0; if (out_rtt) *out_rtt = 0; return NULL; }
struct TOPO_NODEPATH* best = NULL; uint8_t min_hops = 255;
struct ll_entry* e = nq->paths->head;
while (e) { struct TOPO_NODEPATH* p = (struct TOPO_NODEPATH*)e; if (p->conn && p->conn->links_up && p->hop_count < min_hops) { best = p; min_hops = p->hop_count; } e = e->next; }
if (!best) best = (struct TOPO_NODEPATH*)nq->paths->head;
*out_count = best->hop_count;
if (out_rtt) *out_rtt = best->cumulative_rtt;
return (uint64_t*)((uint8_t*)best + sizeof(struct TOPO_NODEPATH));
}
void topo_node_free_raw(struct TOPO_GROUPS* groups, struct TOPO_NODE* ni) {
if (!ni) return;
if (groups) {
@@ -87,7 +100,6 @@ void topo_nodeq_free_group_fields(struct TOPO_GROUPS* groups, struct TOPO_GROUP_
u_free(nq->subnets);
nq->subnets = NULL;
}
u_free(nq->hop_list); nq->hop_list = NULL; nq->hop_count = 0;
nq->handle = NULL;
}
@@ -169,7 +181,8 @@ int topo_node_serialize(struct TOPO_NODE* ni, struct TOPO_GROUP_NODE* nq, uint8_
msg.local_v6_addrs = topo_list_count((struct _topo_head*)ni->v6_addrs);
msg.local_v4_subnets = nq->subnets ? topo_list_count((struct _topo_head*)nq->subnets->v4_subnets) : 0;
msg.local_v6_subnets = nq->subnets ? topo_list_count((struct _topo_head*)nq->subnets->v6_subnets) : 0;
msg.hop_count = nq->hop_count;
{ uint8_t hc; uint64_t* hl = topo_node_best_hop_list(nq, &hc, NULL);
msg.hop_count = hc; }
msg.cumulative_rtt = cumulative_rtt;
size_t total = TOPOMSG_NODE_HDR_SIZE + (size_t)topo_node_dyn_size(&msg);
@@ -208,10 +221,8 @@ int topo_node_serialize(struct TOPO_NODE* ni, struct TOPO_GROUP_NODE* nq, uint8_
}}
}
}
if (msg.hop_count && nq->hop_list) {
size_t sz = msg.hop_count * 8;
memcpy(dp, nq->hop_list, sz); dp += sz;
}
{ uint8_t hc; uint64_t* hl = topo_node_best_hop_list(nq, &hc, NULL);
if (hc && hl) { size_t sz = hc * 8; memcpy(dp, hl, sz); dp += sz; } }
return (int)(dp - out);
}
@@ -364,11 +375,22 @@ void topo_node_ping_update_rtt(struct TOPO_GROUPS* groups, uint64_t node_id, uin
(unsigned long long)node_id, (unsigned)rtt, (unsigned long long)now, (unsigned long long)g->group_id);
if (groups->instance && groups->instance->topo_sqlite_db)
topo_node_sqlite_update_rtt(groups->instance->topo_sqlite_db, node_id, rtt);
topo_fire_nodeinfo_cbk(groups->instance, g, nq);
}
ge = ge->next;
}
}
uint16_t node_best_rtt(struct TOPO_GROUP_NODE* nq) {
if (!nq) return 0xFFFF;
struct TOPO_CONNECTIVITY* c = &nq->connectivity;
uint16_t best = 0x3FFF; uint8_t type = 0;
if (c->interface_status == PROBE_RESULT_REACHABLE && c->interface_min_rtt < best) { best = c->interface_min_rtt; type = 0; }
if (c->nat_status == PROBE_RESULT_REACHABLE && c->nat_min_rtt < best) { best = c->nat_min_rtt; type = 1; }
if (c->real_status == PROBE_RESULT_REACHABLE && c->real_min_rtt < best) { best = c->real_min_rtt; type = 2; }
return ((uint16_t)type << 14) | best;
}
// ===== dump / format =====
static int is_node_connectivity_active(struct TOPO_CONNECTIVITY* c) {
@@ -658,7 +680,6 @@ int topo_group_update_my_nodeinfo(struct UTUN_INSTANCE* instance, struct TOPO_GR
topo_node_registry_store(instance->topo_groups, ni);
lq->node_id = ni->node_id;
lq->hop_count = 0;
lq->last_ver = ni->ver;
e_sock = instance->etcp_sockets;
+10 -3
View File
@@ -66,6 +66,10 @@ struct UTUN_INSTANCE;
#define CONN_TYPE_INDIRECT 3
#define CONN_MGR_MAX_INTERMEDIARIES 3
#define NCONN_DIRECT (1 << 0) // прямое ETCP подключение
#define NCONN_INDIRECT (1 << 1) // через посредника
#define NCONN_BGP (1 << 2) // узел виден в топологии (BGP)
#define TOPO_FLAG_SEND_SUBNETS 0x01
#define TOPO_GROUP_UTUN 0x8000000000000000ULL
#define TOPO_GROUP_TYPE_UTUN 1
@@ -151,15 +155,14 @@ struct TOPO_GROUP_NODE {
struct ll_entry ll; // queue entry, хеш-ключ 8 байт → node_id
uint64_t node_id; // идентификатор узла в этой группе
struct TOPO_NODESUBNETS* subnets; // подсети узла (v4/v6), для route_insert()
uint64_t* hop_list; // путь от нас к узлу (массив node_id)
uint8_t hop_count; // длина hop_list
uint16_t cumulative_rtt; // накопленный RTT от нас до узла
struct ll_queue* paths; // TOPO_NODEPATH{conn,hop_count,rtt} — маршруты
uint8_t last_ver; // последняя версия NODEINFO (защита от stale, BGP)
uint8_t conn_mgr_type; // CONN_TYPE_DIRECT/REVERSE/INDIRECT/NONE
uint64_t conn_mgr_intermediaries[CONN_MGR_MAX_INTERMEDIARIES]; // посредники для INDIRECT
uint8_t conn_mgr_intermediariy_count; // количество посредников
struct TOPO_CONNECTIVITY connectivity; // результаты ping-проб (interface/nat/real)
uint8_t conn_presence; // NODE_CONN_* — какие подключения есть в принципе
uint8_t conn_up; // NODE_CONN_* — какие из них подняты
struct CONN_MGR_HANDLE* handle; // активный хендл соединения к узлу
};
@@ -179,6 +182,7 @@ int topo_node_serialize(struct TOPO_NODE* ni, struct TOPO_GROUP_NODE* nq, uint8_
int topo_node_deserialize(struct TOPO_GROUP* group, const uint8_t* data, size_t len, struct TOPO_NODE** out_ni, struct TOPO_NODESUBNETS** out_subnets,
uint64_t** out_hop_list, uint8_t* out_hop_count,
uint16_t* out_cumulative_rtt);
uint64_t* topo_node_best_hop_list(struct TOPO_GROUP_NODE* nq, uint8_t* out_count, uint16_t* out_rtt);
int topo_group_update_my_nodeinfo(struct UTUN_INSTANCE* instance, struct TOPO_GROUP* group);
void topo_node_update_my_addresses(struct UTUN_INSTANCE* instance);
@@ -187,6 +191,9 @@ void topo_node_dump_all(struct TOPO_GROUP* group);
int topo_node_format_all(struct TOPO_GROUP* group, char* buf, size_t buf_size);
int topo_node_ping_request_cbk(struct TOPO_GROUPS* groups, uint64_t node_id);
void topo_node_ping_update_rtt(struct TOPO_GROUPS* groups, uint64_t node_id, uint16_t rtt);
uint16_t node_best_rtt(struct TOPO_GROUP_NODE* nq);
#define NODE_RTT_TYPE(v) ((v) >> 14)
#define NODE_RTT_VALUE(v) ((v) & 0x3FFF)
static inline const struct TOPO_SOCKMETA4* topo_v4_sock_meta(const struct TOPO_NODE* ni) { return ni->v4_sock_meta; }
static inline const struct TOPO_ADDR4* topo_v4_addrs(const struct TOPO_NODE* ni) { return ni->v4_addrs; }
+59 -1
View File
@@ -58,6 +58,7 @@ int topo_node_sqlite_init(sqlite3* db) {
");"
"CREATE INDEX IF NOT EXISTS idx_na_node ON node_addresses(node_id);"
"CREATE UNIQUE INDEX IF NOT EXISTS idx_na_unique ON node_addresses(node_id, family, socket_id, addr_type);"
"CREATE INDEX IF NOT EXISTS idx_na_addr_lookup ON node_addresses(family, address, port, socket_id);"
"CREATE TABLE IF NOT EXISTS channels ("
" channel_id TEXT PRIMARY KEY," // ID канала
@@ -178,6 +179,51 @@ int topo_node_sqlite_node_put(sqlite3* db, struct TOPO_GROUPS* groups, struct TO
}
}
/* Удалить адреса других узлов, конфликтующие с адресами текущего */
{
sqlite3_stmt* del_cfl = NULL;
if (sqlite3_prepare_v2(db,
"DELETE FROM node_addresses WHERE node_id != ?1 AND family = ?2 AND address = ?3 AND port = ?4 AND socket_id = ?5",
-1, &del_cfl, NULL) == SQLITE_OK) {
struct TOPO_ADDR4* a4 = ni->v4_addrs;
while (a4) {
sqlite3_bind_int64(del_cfl, 1, (sqlite3_int64)ni->node_id);
sqlite3_bind_int(del_cfl, 2, 4);
sqlite3_bind_blob(del_cfl, 3, a4->addr, 4, SQLITE_STATIC);
sqlite3_bind_int(del_cfl, 4, a4->port);
sqlite3_bind_int(del_cfl, 5, (int)a4->socket_id);
sqlite3_step(del_cfl);
if (sqlite3_changes(db) > 0)
DEBUG_INFO(DEBUG_CATEGORY_BGP, "node_put: removed stale v4 addr %d.%d.%d.%d:%u sock=%d other_node nid=%016llx",
a4->addr[0], a4->addr[1], a4->addr[2], a4->addr[3], a4->port, (int)a4->socket_id,
(unsigned long long)ni->node_id);
sqlite3_reset(del_cfl);
a4 = a4->next;
}
struct TOPO_ADDR6* a6 = ni->v6_addrs;
while (a6) {
if (a6->addr[0] == 0xFE && (a6->addr[1] & 0xC0) == 0x80) { a6 = a6->next; continue; }
sqlite3_bind_int64(del_cfl, 1, (sqlite3_int64)ni->node_id);
sqlite3_bind_int(del_cfl, 2, 6);
sqlite3_bind_blob(del_cfl, 3, a6->addr, 16, SQLITE_STATIC);
sqlite3_bind_int(del_cfl, 4, a6->port);
sqlite3_bind_int(del_cfl, 5, (int)a6->socket_id);
sqlite3_step(del_cfl);
sqlite3_reset(del_cfl);
a6 = a6->next;
}
sqlite3_finalize(del_cfl);
}
}
addr_stmt_beg:
{
int v4cnt = topo_list_count((struct _topo_head*)ni->v4_addrs);
int v6cnt = topo_list_count((struct _topo_head*)ni->v6_addrs);
DEBUG_INFO(DEBUG_CATEGORY_BGP, "node_put: nid=%016llx INSERT v4=%d v6=%d",
(unsigned long long)ni->node_id, v4cnt, v6cnt);
}
sqlite3_stmt* addr_stmt = NULL;
if (sqlite3_prepare_v2(db,
"INSERT INTO node_addresses(node_id, family, protocol, address, port, rtt, addr_type, socket_id)"
@@ -240,13 +286,25 @@ int topo_node_sqlite_node_put(sqlite3* db, struct TOPO_GROUPS* groups, struct TO
sqlite3_bind_null(addr_stmt, 6);
sqlite3_bind_int(addr_stmt, 7, at);
sqlite3_bind_int(addr_stmt, 8, (int)a6->socket_id);
sqlite3_step(addr_stmt); sqlite3_reset(addr_stmt);
{ int rc = sqlite3_step(addr_stmt);
if (rc != SQLITE_DONE && rc != SQLITE_OK)
DEBUG_ERROR(DEBUG_CATEGORY_BGP, "node_put: INSERT v6 FAILED nid=%016llx rc=%d err=%s",
(unsigned long long)ni->node_id, rc, sqlite3_errmsg(db)); }
sqlite3_reset(addr_stmt);
a6 = a6->next;
}
sqlite3_finalize(addr_stmt);
}
sqlite3_exec(db, "COMMIT", NULL, NULL, NULL);
{ int total = 0;
sqlite3_stmt* cnt_st;
if (sqlite3_prepare_v2(db, "SELECT count(*) FROM node_addresses WHERE node_id=?", -1, &cnt_st, NULL) == SQLITE_OK) {
sqlite3_bind_int64(cnt_st, 1, (sqlite3_int64)ni->node_id);
if (sqlite3_step(cnt_st) == SQLITE_ROW) total = sqlite3_column_int(cnt_st, 0);
sqlite3_finalize(cnt_st); }
DEBUG_INFO(DEBUG_CATEGORY_BGP, "node_put: nid=%016llx COMMITED total=%d",
(unsigned long long)ni->node_id, total); }
return 0;
}
+1 -12
View File
@@ -72,16 +72,6 @@ static uint16_t node_get_min_rtt(const struct TOPO_GROUP_NODE* nq) {
return rtt;
}
/* Вычисляет next_hop относительно failed_peer из hop_list.
* hop_list = [...узлы пути...], последний элемент = failed_peer (форвардил NODEINFO нам).
* next_hop = элемент перед failed_peer (hop_list[hop_count-2]).
* Если failed_peer не найден в конце — fallback: node_id сам себе next_hop. */
static uint64_t find_next_hop(const struct TOPO_GROUP_NODE* nq, uint64_t failed_peer) {
if (nq->hop_count >= 2 && nq->hop_list && nq->hop_list[nq->hop_count - 1] == failed_peer)
return nq->hop_list[nq->hop_count - 2];
return nq->node_id;
}
/* Ищет незапущенный (started==0) контекст по next_hop_id */
static struct TOPO_RECOVERY_CTX* topo_recovery_find_by_next_hop(struct TOPO_GROUP* group, uint64_t next_hop_id) {
struct TOPO_RECOVERY_CTX* ctx = group->recovery_list;
@@ -183,12 +173,11 @@ static void topo_recovery_try_next(struct TOPO_RECOVERY_CTX* ctx) {
}
}
void topo_recovery_add_node(struct TOPO_GROUP* group, struct TOPO_GROUP_NODE* nq, uint64_t failed_peer) {
void topo_recovery_add_node(struct TOPO_GROUP* group, struct TOPO_GROUP_NODE* nq, uint64_t next_hop) {
if (!group || !nq) return;
struct TOPO_NODE* ni = topo_node_registry_find(group->instance->topo_groups, nq->node_id);
if (!ni) return;
uint64_t node_id = nq->node_id;
uint64_t next_hop = find_next_hop(nq, failed_peer);
struct TOPO_RECOVERY_CTX* ctx = topo_recovery_find_by_next_hop(group, next_hop);
if (!ctx) {
+1 -1
View File
@@ -59,7 +59,7 @@ struct TOPO_RECOVERY_CTX {
* Вызывается из topo_group_remove_conn ДО topo_node_free_lists (нужен hop_list).
* @param failed_peer node_id отвалившегося пира (conn->peer_node_id)
*/
void topo_recovery_add_node(struct TOPO_GROUP* group, struct TOPO_GROUP_NODE* nq, uint64_t failed_peer);
void topo_recovery_add_node(struct TOPO_GROUP* group, struct TOPO_GROUP_NODE* nq, uint64_t next_hop);
/**
* Запускает перебор узлов для всех pending-контекстов.
+43 -5
View File
@@ -127,7 +127,28 @@ static int ncd_create_links(struct ncd_entry* entry, struct TOPO_NODE* ni,
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++;
struct ETCP_SOCKET* use_sock = socks[rr++ % sock_count];
{
struct ETCP_LINK* stale = etcp_link_find_by_addr(use_sock, &sa);
if (stale && stale->etcp != conn
&& memcmp(conn->crypto_ctx.peer_public_key, stale->etcp->crypto_ctx.peer_public_key, SC_PUBKEY_SIZE))
{
if (!stale->etcp->links_up || !stale->etcp->initialized) {
struct ETCP_CONN* old = stale->etcp;
DEBUG_INFO(DEBUG_CATEGORY_GENERAL, "[ncd] evict stale link node=0x%016llx(old pk=%016llx) for new node=0x%016llx(pk=%016llx)",
(unsigned long long)old->peer_node_id, *(const uint64_t*)old->crypto_ctx.peer_public_key,
(unsigned long long)conn->peer_node_id, *(const uint64_t*)conn->crypto_ctx.peer_public_key);
etcp_link_close(stale);
etcp_cbk_fire(old, ETCP_CBK_EVENT_NODE_CHANGED);
} else {
DEBUG_ERROR(DEBUG_CATEGORY_GENERAL, "[ncd] addr conflict: %d.%d.%d.%d:%d already used by live conn 0x%016llx for node 0x%016llx",
a->addr[0], a->addr[1], a->addr[2], a->addr[3], (int)a->port,
(unsigned long long)stale->etcp->peer_node_id, (unsigned long long)conn->peer_node_id);
continue;
}
}
}
if (etcp_link_new(conn, use_sock, &sa, 0)) link_count++;
}
}
}
@@ -155,6 +176,24 @@ static int ncd_create_links(struct ncd_entry* entry, struct TOPO_NODE* ni,
struct ETCP_SOCKET* use_sock = socks[rr++ % sock_count];
if ((a->addr[0] == 0xfe && (a->addr[1] & 0xc0) == 0x80)) sin6.sin6_scope_id = use_sock->netif_index;
struct sockaddr_storage sa; memcpy(&sa, &sin6, sizeof(sin6));
{
struct ETCP_LINK* stale = etcp_link_find_by_addr(use_sock, &sa);
if (stale && stale->etcp != conn
&& memcmp(conn->crypto_ctx.peer_public_key, stale->etcp->crypto_ctx.peer_public_key, SC_PUBKEY_SIZE))
{
if (!stale->etcp->links_up || !stale->etcp->initialized) {
struct ETCP_CONN* old = stale->etcp;
DEBUG_INFO(DEBUG_CATEGORY_GENERAL, "[ncd] evict stale v6 link node=0x%016llx(old) -> 0x%016llx(new)",
(unsigned long long)old->peer_node_id, (unsigned long long)conn->peer_node_id);
etcp_link_close(stale);
etcp_cbk_fire(old, ETCP_CBK_EVENT_NODE_CHANGED);
} else {
DEBUG_ERROR(DEBUG_CATEGORY_GENERAL, "[ncd] addr conflict: v6 port=%d already used by live conn 0x%016llx for node 0x%016llx",
(int)a->port, (unsigned long long)stale->etcp->peer_node_id, (unsigned long long)conn->peer_node_id);
continue;
}
}
}
if (etcp_link_new(conn, use_sock, &sa, 0)) link_count++;
}
}
@@ -183,8 +222,9 @@ static void ncd_event_dispatch(struct ncd_entry* entry, enum ncd_event event) {
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);
DEBUG_INFO(DEBUG_CATEGORY_NCD, "[ncd] event=%s node=0x%016llx handles=%d",
event == NCD_EVENT_UP ? "UP" : event == NCD_EVENT_DOWN ? "DOWN" : "TIMEOUT",
(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; }
}
@@ -220,8 +260,6 @@ static void ncd_deferred_close_conn(void* arg) {
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;
DEBUG_DEBUG(DEBUG_CATEGORY_DEBUG, "[ncd-debug] ncd_down_cb conn=%p entry=%p fin_wait=%d handle_count=%d node=0x%016llx",
(void*)conn, entry, conn->fin_wait, entry->handle_count, (unsigned long long)entry->node_id);
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;
+2 -1
View File
@@ -173,7 +173,8 @@ struct stcp_link *stcp_link_connect(struct stcp_link_config *cfg) {
link->etcp_conn.instance = cfg->inst;
link->etcp_conn.transport_link = (void *)link;
DEBUG_INFO(DEBUG_CATEGORY_SOCKET, "connecting to %s:%u", addr_str, port);
DEBUG_INFO(DEBUG_CATEGORY_GENERAL, "stcp_link: connecting to %s:%u pubkey=%016llx",
addr_str, port, (unsigned long long)*(const uint64_t*)link->peer_pubkey);
return link;
}
+7
View File
@@ -618,6 +618,13 @@ void utun_instance_destroy(struct UTUN_INSTANCE *instance) {
// Free the instance memory
DEBUG_INFO(DEBUG_CATEGORY_MEMORY, "[INSTANCE_DESTROY] Freeing instance memory");
// Cleanup nodeinfo callback chain
while (instance->nodeinfo_cbks) {
struct nodeinfo_cbk_entry* r = instance->nodeinfo_cbks;
instance->nodeinfo_cbks = r->next; u_free(r);
}
u_free(instance);
DEBUG_INFO(DEBUG_CATEGORY_MEMORY, "[INSTANCE_DESTROY] Instance destroyed completely");
}
+2
View File
@@ -28,6 +28,7 @@ extern "C" {
#include "etcp_api.h"
#include "config_parser.h"
#include "topo_group.h"
// Forward declarations
struct utun_config;
@@ -126,6 +127,7 @@ struct UTUN_INSTANCE {
struct etcp_inst_cbk_entry* new_conn_cbks;
struct etcp_status_cbk_entry* conn_status_cbks; // instance-level: NEW/UP/DOWN/DELETE
struct etcp_socket_cbk_entry* socket_cbks; // instance-level: socket ADDR/STATUS changes
struct nodeinfo_cbk_entry* nodeinfo_cbks; // глобальная подписка на изменения nodeinfo
void* test_user_ptr; // Generic user pointer (used by tests)
struct memory_pool* data_pool;// для входных-выходных данных пакета