Browse Source

topo_group: асимметричный старт BGP (JOIN_GROUP) + вынос group_id/flags из TOPO_NODE

- UTUN: BGP инициирует только сторона-клиент ([client]) и явные etcp_connect;
  сервер добавляет пира по JOIN_GROUP (фикс обратного маршрута: сервер не
  создавал узел клиента из-за дедупа senders_list)
- topo_group_new_conn: conn_add_path до дедупа; handle_request_table без add_to_senders
- TOPO_NODE: убраны group_id/flags (shared-реестр), передаются в topo_node_serialize
- routing/control_server: явный TOPO_GROUP_UTUN
- CHAT: гейт по peers_* (get_member_channels) + пассивное add/remove в member_sync
- tests: тесты адаптированы (route_ping ждёт registry+v4_addrs)
- chatgui-android: voice_recorder_init в utun_bridge_voice_start (idempotent)
v2
evgeny 4 weeks ago
parent
commit
656da48e35
  1. 19
      src/chat/member_sync.c
  2. 11
      src/config_parser.c
  3. 3
      src/config_parser.h
  4. 2
      src/control_server.c
  5. 5
      src/media_delivery/media_download.c
  6. 2
      src/routing_layer/routing.c
  7. 133
      src/routing_layer/topo_group.c
  8. 10
      src/routing_layer/topo_group.h
  9. 2
      src/routing_layer/topo_group_invite.c
  10. 16
      src/routing_layer/topo_node.c
  11. 6
      src/routing_layer/topo_node.h
  12. 4
      src/routing_layer/topo_node_doc.md
  13. 41
      src/routing_layer/topo_node_sqlite.c
  14. 4
      src/routing_layer/topo_node_sqlite.h
  15. 2
      tests/test_auto_socket_dynamic.c
  16. 2
      tests/test_etcp_connect.c
  17. 2
      tests/test_etcp_router_reconnect.c
  18. 7
      tests/test_route_ping.c
  19. 1
      tools/chatgui-android/jni_bridge/android_jni_bridge.c
  20. 6
      tools/chatgui-android/libutun_lite/voice_recorder.c

19
src/chat/member_sync.c

@ -497,6 +497,16 @@ int member_sync_apply_record(struct UTUN_INSTANCE* inst, const char* ch_id,
if (rc == 0) changed = 0; /* запись была no-op (данные идентичны) */ if (rc == 0) changed = 0; /* запись была no-op (данные идентичны) */
} }
/* ── пассивное добавление: если узел уже подключён напрямую — добавить в CHAT-группу ── */
if (changed && inst->topo_groups) {
uint64_t gid = strtoull(ch_id, NULL, 10);
struct TOPO_GROUP* g = topo_groups_find(inst->topo_groups, gid);
if (g && g->group_type == TOPO_GROUP_TYPE_CHAT) {
struct ETCP_CONN* c = instance_find_conn(inst, m->node_id);
if (c) topo_group_new_conn(g, c);
}
}
DEBUG_DEBUG(DEBUG_CATEGORY_MEMBER_SYNC, "%s: apply_record ch=%s nid=0x%016llx → changed=%d stale=%d", DEBUG_DEBUG(DEBUG_CATEGORY_MEMBER_SYNC, "%s: apply_record ch=%s nid=0x%016llx → changed=%d stale=%d",
MS_ID, ch_id, (unsigned long long)m->node_id, changed, stale); MS_ID, ch_id, (unsigned long long)m->node_id, changed, stale);
return (changed ? MS_APPLY_CHANGED : 0) | (stale ? MS_APPLY_STALE : 0); return (changed ? MS_APPLY_CHANGED : 0) | (stale ? MS_APPLY_STALE : 0);
@ -608,6 +618,15 @@ int member_sync_verify_and_purge(struct UTUN_INSTANCE* inst, const char* ch_id)
MS_ID, (unsigned long long)nid, ch_id); MS_ID, (unsigned long long)nid, ch_id);
topo_node_sqlite_member_del(db, ch_id, nid); topo_node_sqlite_member_del(db, ch_id, nid);
merkle_sync_recompute_path(inst, ch_id, nid); merkle_sync_recompute_path(inst, ch_id, nid);
/* пассивное удаление: если узел подключён напрямую — убрать из CHAT-группы */
if (inst->topo_groups) {
uint64_t gid = strtoull(ch_id, NULL, 10);
struct TOPO_GROUP* g = topo_groups_find(inst->topo_groups, gid);
if (g && g->group_type == TOPO_GROUP_TYPE_CHAT) {
struct ETCP_CONN* c = instance_find_conn(inst, nid);
if (c) topo_group_remove_conn(g, c);
}
}
purged++; purged++;
} }
} }

11
src/config_parser.c

@ -1570,3 +1570,14 @@ int update_config_keys(const char *filename, const char *priv_key, const char *p
fclose(fp); fclose(fp);
return 0; return 0;
} }
int config_peer_in_clients(const struct utun_config* config, const uint8_t* pubkey) {
if (!config || !pubkey) return 0;
uint8_t pk_bin[SC_PUBKEY_SIZE];
for (const struct CFG_CLIENT* cli = config->clients; cli; cli = cli->next) {
if (!cli->peer_public_key_hex[0]) continue;
if (hex_to_binary(cli->peer_public_key_hex, pk_bin, SC_PUBKEY_SIZE) != 0) continue;
if (memcmp(pk_bin, pubkey, SC_PUBKEY_SIZE) == 0) return 1;
}
return 0;
}

3
src/config_parser.h

@ -228,6 +228,9 @@ void print_config(const struct utun_config *config);
void print_chatserver_invite_link(const struct utun_config *config, const struct CFG_SERVER *servers); void print_chatserver_invite_link(const struct utun_config *config, const struct CFG_SERVER *servers);
int update_config_keys(const char *filename, const char *priv_key, const char *pub_key); int update_config_keys(const char *filename, const char *priv_key, const char *pub_key);
/* Узел (по pubkey, 32 байта) присутствует в списке [client] конфига? 1=да, 0=нет. */
int config_peer_in_clients(const struct utun_config* config, const uint8_t* pubkey);
#ifdef __cplusplus #ifdef __cplusplus
} }
#endif #endif

2
src/control_server.c

@ -1390,7 +1390,7 @@ static void send_single_node_info(struct control_server* server, struct control_
struct TOPO_NODE* ni = topo_node_registry_find(server->instance->topo_groups, nq->node_id); struct TOPO_NODE* ni = topo_node_registry_find(server->instance->topo_groups, nq->node_id);
if (!ni) return; if (!ni) return;
uint8_t buf[8192]; uint8_t buf[8192];
int ser_len = topo_node_serialize(ni, nq, buf + 2, sizeof(buf) - 2, 0); int ser_len = topo_node_serialize(ni, nq, TOPO_GROUP_UTUN, TOPO_FLAG_SEND_SUBNETS, 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;

5
src/media_delivery/media_download.c

@ -61,6 +61,11 @@ static void md_mkdir_parent(const char* filepath) {
static int md_dl_send(struct UTUN_INSTANCE* inst, uint64_t group_id, uint64_t dst, const uint8_t* data, size_t len) { static int md_dl_send(struct UTUN_INSTANCE* inst, uint64_t group_id, uint64_t dst, const uint8_t* data, size_t len) {
if (!inst || !inst->topo_groups || !inst->connections) { DEBUG_WARN(DEBUG_CATEGORY_GENERAL, "%s: md_dl_send: no routing (inst=%p topo=%p conn=%p)", MDL_ID, (void*)inst, (void*)(inst ? inst->topo_groups : NULL), (void*)(inst ? inst->connections : NULL)); return -1; } if (!inst || !inst->topo_groups || !inst->connections) { DEBUG_WARN(DEBUG_CATEGORY_GENERAL, "%s: md_dl_send: no routing (inst=%p topo=%p conn=%p)", MDL_ID, (void*)inst, (void*)(inst ? inst->topo_groups : NULL), (void*)(inst ? inst->connections : NULL)); return -1; }
{ struct TOPO_GROUP* grp = topo_groups_find(inst->topo_groups, group_id);
struct ETCP_CONN* gc = grp ? topo_group_find_conn_for_node(grp, dst) : NULL;
DEBUG_INFO(DEBUG_CATEGORY_GENERAL, "%s: md_dl_send group=0x%016llx dst=0x%016llx grp=%p conn_in_group=%p",
MDL_ID, (unsigned long long)group_id, (unsigned long long)dst, (void*)grp, (void*)gc);
}
struct ll_entry* e = queue_entry_new(0); struct ll_entry* e = queue_entry_new(0);
if (!e) { DEBUG_ERROR(DEBUG_CATEGORY_GENERAL, "%s: queue_entry_new failed for send to 0x%016llx", MDL_ID, (unsigned long long)dst); return -1; } if (!e) { DEBUG_ERROR(DEBUG_CATEGORY_GENERAL, "%s: queue_entry_new failed for send to 0x%016llx", MDL_ID, (unsigned long long)dst); return -1; }
e->dgram = u_malloc(len + 1); e->dgram = u_malloc(len + 1);

2
src/routing_layer/routing.c

@ -145,7 +145,7 @@ void route_pkt(struct UTUN_INSTANCE* instance, struct ll_entry* entry, uint64_t
} 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_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, rni->group_id, nq->node_id, entry, 1); int send_err = etcp_route_send(instance, TOPO_GROUP_UTUN, 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);

133
src/routing_layer/topo_group.c

@ -48,6 +48,20 @@ static void topo_group_send_table_request(struct TOPO_GROUP* group, struct ETCP_
if (etcp_send(conn, e) != 0) { u_free(req); queue_entry_free(e); } if (etcp_send(conn, e) != 0) { u_free(req); queue_entry_free(e); }
} }
static void topo_group_send_join_group(struct TOPO_GROUP* group, struct ETCP_CONN* conn) {
if (!group || !conn) return;
DEBUG_DEBUG(DEBUG_CATEGORY_BGP, "Sending join group request to %s grp=%016llx", conn->log_name, (unsigned long long)group->group_id);
struct TOPOMSG_JOIN_GROUP* req = u_calloc(1, sizeof(struct TOPOMSG_JOIN_GROUP));
if (!req) return;
req->cmd = ETCP_ID_TOPO_ENTRY;
req->subcmd = TOPO_SUBCMD_JOIN_GROUP;
req->group_id = group->group_id;
struct ll_entry* e = queue_entry_new(0);
if (!e) { u_free(req); return; }
e->dgram = (uint8_t*)req; e->len = sizeof(struct TOPOMSG_JOIN_GROUP);
if (etcp_send(conn, e) != 0) { u_free(req); queue_entry_free(e); }
}
static void topo_group_send_table_complete(struct TOPO_GROUP* group, struct ETCP_CONN* conn) { static void topo_group_send_table_complete(struct TOPO_GROUP* group, struct ETCP_CONN* conn) {
if (!group || !conn) return; if (!group || !conn) return;
struct TOPOMSG_TABLE_REQ* req = u_calloc(1, sizeof(struct TOPOMSG_TABLE_REQ)); struct TOPOMSG_TABLE_REQ* req = u_calloc(1, sizeof(struct TOPOMSG_TABLE_REQ));
@ -65,6 +79,7 @@ static void topo_group_add_to_senders(struct TOPO_GROUP* group, struct ETCP_CONN
static bool topo_group_should_send_to(const struct TOPO_GROUP_NODE* 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);
static void topo_group_handle_join_group(struct TOPO_GROUP* group, struct ETCP_CONN* conn);
static void nodeinfo_dump_log(const uint8_t* data, size_t len) { static void nodeinfo_dump_log(const uint8_t* data, size_t len) {
if (!data || len < sizeof(struct TOPOMSG_NODEINFO_PKT)) return; if (!data || len < sizeof(struct TOPOMSG_NODEINFO_PKT)) return;
@ -114,6 +129,7 @@ static const char* group_subcmd_name(uint8_t subcmd) {
case TOPO_SUBCMD_WITHDRAW: return "WITHDRAW"; case TOPO_SUBCMD_WITHDRAW: return "WITHDRAW";
case TOPO_SUBCMD_TABLE_COMPLETE: return "TABLE_COMPLETE"; case TOPO_SUBCMD_TABLE_COMPLETE: return "TABLE_COMPLETE";
case TOPO_SUBCMD_ERR_GROUP_MISMATCH: return "ERR_GROUP_MISMATCH"; case TOPO_SUBCMD_ERR_GROUP_MISMATCH: return "ERR_GROUP_MISMATCH";
case TOPO_SUBCMD_JOIN_GROUP: return "JOIN_GROUP";
default: return "?"; default: return "?";
} }
} }
@ -164,6 +180,8 @@ static void topo_group_receive_cbk(struct ETCP_CONN* from_conn, struct ll_entry*
pkt_group_id = ((struct TOPOMSG_TABLE_REQ*)data)->group_id; pkt_group_id = ((struct TOPOMSG_TABLE_REQ*)data)->group_id;
} else if (subcmd == TOPO_SUBCMD_ERR_GROUP_MISMATCH && entry->len >= sizeof(struct TOPOMSG_ERR_GROUP_MISMATCH)) { } else if (subcmd == TOPO_SUBCMD_ERR_GROUP_MISMATCH && entry->len >= sizeof(struct TOPOMSG_ERR_GROUP_MISMATCH)) {
pkt_group_id = ((struct TOPOMSG_ERR_GROUP_MISMATCH*)data)->group_id; pkt_group_id = ((struct TOPOMSG_ERR_GROUP_MISMATCH*)data)->group_id;
} else if (subcmd == TOPO_SUBCMD_JOIN_GROUP && entry->len >= sizeof(struct TOPOMSG_JOIN_GROUP)) {
pkt_group_id = ((struct TOPOMSG_JOIN_GROUP*)data)->group_id;
} }
struct TOPO_GROUP* group = topo_groups_find(instance->topo_groups, pkt_group_id); struct TOPO_GROUP* group = topo_groups_find(instance->topo_groups, pkt_group_id);
@ -177,6 +195,7 @@ static void topo_group_receive_cbk(struct ETCP_CONN* from_conn, struct ll_entry*
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);
else if (subcmd == TOPO_SUBCMD_REQUEST_TABLE) topo_group_handle_request_table(group, from_conn); else if (subcmd == TOPO_SUBCMD_REQUEST_TABLE) topo_group_handle_request_table(group, from_conn);
else if (subcmd == TOPO_SUBCMD_JOIN_GROUP) topo_group_handle_join_group(group, from_conn);
else if (subcmd == TOPO_SUBCMD_TABLE_COMPLETE) { etcp_set_routing_exchange_state(from_conn, 3); DEBUG_INFO(DEBUG_CATEGORY_BGP, "BGP sync complete with %s: %d nodes grp=%016llx", from_conn->log_name, queue_entry_count(group->nodes), (unsigned long long)group->group_id); } else if (subcmd == TOPO_SUBCMD_TABLE_COMPLETE) { etcp_set_routing_exchange_state(from_conn, 3); DEBUG_INFO(DEBUG_CATEGORY_BGP, "BGP sync complete with %s: %d nodes grp=%016llx", from_conn->log_name, queue_entry_count(group->nodes), (unsigned long long)group->group_id); }
else if (subcmd == TOPO_SUBCMD_ERR_GROUP_MISMATCH) { else if (subcmd == TOPO_SUBCMD_ERR_GROUP_MISMATCH) {
if (entry->len >= sizeof(struct TOPOMSG_ERR_GROUP_MISMATCH)) { if (entry->len >= sizeof(struct TOPOMSG_ERR_GROUP_MISMATCH)) {
@ -194,14 +213,77 @@ static void topo_group_receive_cbk(struct ETCP_CONN* from_conn, struct ll_entry*
// Init / Destroy / New / Remove conn // Init / Destroy / New / Remove conn
// ============================================================================ // ============================================================================
/* Создать TOPO_GROUP_NODE (если нет) + прямой путь через conn (hop=1). Без BGP.
Используется из topo_group_new_conn (add + init BGP) и из приёма TABLE_REQ (входящий запросил BGP). */
static void topo_group_conn_add_path(struct TOPO_GROUP* group, struct ETCP_CONN* conn, uint64_t node_id) {
if (!group || !conn || !node_id || node_id == group->instance->node_id) return;
/* узел в реестре — иначе загрузить из БД (nodes + node_addresses), чтобы форвардинг мог сериализовать */
{ struct TOPO_GROUPS* groups = group->instance->topo_groups;
struct TOPO_NODE* ni = topo_node_registry_find(groups, node_id);
if (!ni) {
sqlite3* db = group->instance->topo_sqlite_db;
ni = db ? topo_node_sqlite_node_load(db, groups, node_id) : NULL;
if (ni) ni = topo_node_registry_store(groups, ni);
}
}
struct TOPO_GROUP_NODE* nq = topo_node_find_by_id(group, node_id);
if (nq && nq->paths) {
struct ll_entry* pe = nq->paths->head;
while (pe) { if (((struct TOPO_NODEPATH*)pe)->conn == conn) return; pe = pe->next; }
}
if (!nq) {
struct ll_entry* qe = queue_entry_new(sizeof(struct TOPO_GROUP_NODE));
if (!qe) return;
nq = (struct TOPO_GROUP_NODE*)qe;
memset((uint8_t*)nq + sizeof(struct ll_entry), 0, sizeof(*nq) - sizeof(struct ll_entry));
nq->node_id = node_id;
queue_data_put_with_index(group->nodes, &nq->ll);
}
uint64_t hop[1] = { node_id };
topo_group_add_path(nq, conn, hop, 1, 0);
nq->conn_presence |= NCONN_DIRECT;
nq->conn_up |= NCONN_DIRECT;
DEBUG_INFO(DEBUG_CATEGORY_BGP, "conn_add_path: node=0x%016llx grp=%016llx ch=%s conn=%s",
(unsigned long long)node_id, (unsigned long long)group->group_id, group->channel_id, conn->log_name);
}
static void topo_group_conn_status(struct ETCP_CONN* conn, int status, void* arg) { static void topo_group_conn_status(struct ETCP_CONN* conn, int status, void* arg) {
struct TOPO_GROUPS* groups = (struct TOPO_GROUPS*)arg; struct TOPO_GROUPS* groups = (struct TOPO_GROUPS*)arg;
if (!conn || !groups) return; if (!conn || !groups) return;
if (status == ETCP_CONN_STATUS_UP && conn->peer_node_id) {
/* non-CHAT (UTUN): узлы из конфига (clients) + явные подключения (etcp_connect). */
int explicit_conn = conn->bgp_ready_cbk != NULL;
struct ll_entry* fe = groups->group_list->head;
while (fe) {
struct TOPO_GROUP* g = (struct TOPO_GROUP*)fe; fe = fe->next;
if (g->group_type != TOPO_GROUP_TYPE_CHAT) {
int is_client = conn->instance->config && config_peer_in_clients(conn->instance->config, conn->crypto_ctx.peer_public_key);
if (is_client || explicit_conn) {
topo_group_new_conn(g, conn); /* добавить + инициировать BGP */
topo_group_send_join_group(g, conn); /* запросить членство у пира */
}
}
}
/* CHAT: только члены каналов (peers_* в БД). */
sqlite3* db = conn->instance ? conn->instance->topo_sqlite_db : NULL;
uint64_t* chs = NULL; int chn = 0;
if (db && topo_node_sqlite_get_member_channels(db, conn->peer_node_id, &chs, &chn) == 0 && chn > 0) {
for (int i = 0; i < chn; i++) {
struct TOPO_GROUP* cg = topo_groups_find(groups, chs[i]);
if (cg && cg->group_type == TOPO_GROUP_TYPE_CHAT)
topo_group_new_conn(cg, conn);
}
}
u_free(chs);
} else if (status == ETCP_CONN_STATUS_DOWN || status == ETCP_CONN_STATUS_DELETE) {
struct ll_entry* fe = groups->group_list->head; struct ll_entry* fe = groups->group_list->head;
while (fe) { struct TOPO_GROUP* g = (struct TOPO_GROUP*)fe; fe = fe->next; 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); topo_group_remove_conn(g, conn);
if (status == ETCP_CONN_STATUS_DOWN) topo_group_remove_conn(g, conn); }
if (status == ETCP_CONN_STATUS_DELETE) topo_group_remove_conn(g, conn);
} }
} }
@ -467,9 +549,21 @@ void topo_group_new_conn(struct TOPO_GROUP* group, struct ETCP_CONN* conn) {
if (!conn->instance) { DEBUG_ERROR(DEBUG_CATEGORY_BGP, "conn->instance is NULL"); return; } if (!conn->instance) { DEBUG_ERROR(DEBUG_CATEGORY_BGP, "conn->instance is NULL"); return; }
if (!conn->instance->rt) { DEBUG_ERROR(DEBUG_CATEGORY_BGP, "conn->instance->rt is NULL"); return; } if (!conn->instance->rt) { DEBUG_ERROR(DEBUG_CATEGORY_BGP, "conn->instance->rt is NULL"); return; }
topo_recovery_cancel_for_node(group, conn->peer_node_id);
/* создать узел + прямой путь (сразу, не дожидаясь NODEINFO; дедуп внутри по conn).
Делаем ДО проверки senders_list: conn может уже быть в senders_list (через handle_request_table),
но узел ещё не создан (входящий JOIN_GROUP). */
topo_group_conn_add_path(group, conn, 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();
if (conn->instance->topo_sqlite_db) topo_node_sqlite_nodeinfo_updated(conn->instance->topo_sqlite_db, conn->peer_node_id);
}
}
/* дедуп: тот же conn стреляет ETCP_CONN_STATUS_UP дважды (UDP-линк, затем TCP-линк). /* дедуп: тот же conn стреляет ETCP_CONN_STATUS_UP дважды (UDP-линк, затем TCP-линк).
* Если conn уже в senders_list — повторно не обрабатываем, иначе active_conn_count * Если conn уже в senders_list — не слать повторно TABLE_REQ и не задваивать active_conn_count. */
* задваивается и переподключение никогда не стартует. */
{ struct ll_entry* se = group->senders_list ? group->senders_list->head : NULL; { struct ll_entry* se = group->senders_list ? group->senders_list->head : NULL;
while (se) { while (se) {
if (((struct TOPO_GROUP_CONN_ITEM*)se->data)->conn == conn) { if (((struct TOPO_GROUP_CONN_ITEM*)se->data)->conn == conn) {
@ -480,15 +574,6 @@ 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); topo_group_add_to_senders(group, conn);
DEBUG_DEBUG(DEBUG_CATEGORY_BGP, "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_BGP, "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);
@ -740,8 +825,6 @@ int topo_group_process_nodeinfo(struct TOPO_GROUP* group, struct ETCP_CONN* from
if (nodeinfo1) { if (nodeinfo1) {
{ struct TOPO_NODE* stored = topo_node_registry_store(group->instance->topo_groups, new_ni); { struct TOPO_NODE* stored = topo_node_registry_store(group->instance->topo_groups, new_ni);
if (stored != new_ni) new_ni = stored; } 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->node_id = new_ni ? new_ni->node_id : 0;
nodeinfo1->subnets = new_subnets; nodeinfo1->subnets = new_subnets;
} else { } else {
@ -751,8 +834,6 @@ int topo_group_process_nodeinfo(struct TOPO_GROUP* group, struct ETCP_CONN* from
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));
{ struct TOPO_NODE* stored = topo_node_registry_store(group->instance->topo_groups, new_ni); { struct TOPO_NODE* stored = topo_node_registry_store(group->instance->topo_groups, new_ni);
if (stored != new_ni) new_ni = stored; } 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->node_id = new_ni ? new_ni->node_id : 0;
nodeinfo1->subnets = new_subnets; nodeinfo1->subnets = new_subnets;
nodeinfo1->connectivity.probe_status = PROBE_STATUS_NONE; nodeinfo1->connectivity.probe_status = PROBE_STATUS_NONE;
@ -888,8 +969,9 @@ void topo_group_send_nodeinfo(struct TOPO_GROUP* group, struct TOPO_GROUP_NODE*
u_free(p); u_free(p);
return; return;
} }
DEBUG_DEBUG(DEBUG_CATEGORY_BGP, "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); DEBUG_DEBUG(DEBUG_CATEGORY_BGP, "send_nodeinfo: node %016llx ver=%d grp=%016llx to conn=%s", (unsigned long long)node->node_id, sni->ver, (unsigned long long)group->group_id, conn->log_name);
int ser_len = topo_node_serialize(sni, node, p + 2, max_sz - 2, cumulative_rtt); uint8_t sflags = (group->group_type == TOPO_GROUP_TYPE_CHAT) ? 0 : TOPO_FLAG_SEND_SUBNETS;
int ser_len = topo_node_serialize(sni, node, group->group_id, sflags, p + 2, max_sz - 2, cumulative_rtt);
if (ser_len < 0) { DEBUG_ERROR(DEBUG_CATEGORY_BGP, "send_nodeinfo: serialize failed for node %016llx", (unsigned long long)node->node_id); u_free(p); return; } if (ser_len < 0) { DEBUG_ERROR(DEBUG_CATEGORY_BGP, "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);
@ -941,10 +1023,19 @@ static void topo_group_handle_request_table(struct TOPO_GROUP* group, struct ETC
if (!group || !conn) return; if (!group || !conn) return;
topo_group_send_nodeinfo(group, group->local_node, conn, 0); topo_group_send_nodeinfo(group, group->local_node, conn, 0);
topo_group_send_full_table(group, conn); topo_group_send_full_table(group, conn);
topo_group_add_to_senders(group, conn); /* senders_list заполняется только через topo_group_new_conn (add + BGP);
здесь не добавляем, иначе new_conn (на JOIN_GROUP) упирается в дедуп и не шлёт TABLE_REQ обратно. */
topo_group_send_table_complete(group, conn); topo_group_send_table_complete(group, conn);
} }
static void topo_group_handle_join_group(struct TOPO_GROUP* group, struct ETCP_CONN* conn) {
if (!group || !conn) return;
DEBUG_INFO(DEBUG_CATEGORY_BGP, "handle_join_group: from %s grp=%016llx type=%d", conn->log_name, (unsigned long long)group->group_id, group->group_type);
/* входящий запросил членство: для non-CHAT (UTUN) — добавить запросившего узла + инициировать BGP обратно */
if (group->group_type != TOPO_GROUP_TYPE_CHAT)
topo_group_new_conn(group, conn);
}
/* ── BGP node event callbacks ── */ /* ── BGP node event callbacks ── */
void topo_group_add_node_cbk(struct TOPO_GROUP* group, topo_node_event_fn fn, void* arg) { void topo_group_add_node_cbk(struct TOPO_GROUP* group, topo_node_event_fn fn, void* arg) {

10
src/routing_layer/topo_group.h

@ -96,6 +96,7 @@ void topo_fire_nodeinfo_cbk(struct UTUN_INSTANCE* instance, struct TOPO_GROUP* g
#define TOPO_SUBCMD_WITHDRAW 0x06 // узел стал недоступен #define TOPO_SUBCMD_WITHDRAW 0x06 // узел стал недоступен
#define TOPO_SUBCMD_TABLE_COMPLETE 0x0B // завершение начальной синхронизации таблицы #define TOPO_SUBCMD_TABLE_COMPLETE 0x0B // завершение начальной синхронизации таблицы
#define TOPO_SUBCMD_ERR_GROUP_MISMATCH 0x0C // ошибка несоответствия типа группы #define TOPO_SUBCMD_ERR_GROUP_MISMATCH 0x0C // ошибка несоответствия типа группы
#define TOPO_SUBCMD_JOIN_GROUP 0x0D // запрос членства в группе
#define MAX_HOPS 16 #define MAX_HOPS 16
#define BGP_NODES_HASH_SIZE 256 #define BGP_NODES_HASH_SIZE 256
@ -129,6 +130,15 @@ struct TOPOMSG_TABLE_REQ {
uint64_t group_id; // идентификатор группы uint64_t group_id; // идентификатор группы
} __attribute__((packed)); } __attribute__((packed));
/**
* @brief Пакет запроса членства в группе (отправитель просит добавить его в группу)
*/
struct TOPOMSG_JOIN_GROUP {
uint8_t cmd;
uint8_t subcmd;
uint64_t group_id; // идентификатор группы
} __attribute__((packed));
/** /**
* @brief Пакет ошибки несоответствия типа группы * @brief Пакет ошибки несоответствия типа группы
*/ */

2
src/routing_layer/topo_group_invite.c

@ -436,7 +436,7 @@ int topo_group_invite_join(struct UTUN_INSTANCE* inst, uint64_t group_id,
/* 2. Копировать ni в group_ni */ /* 2. Копировать ni в group_ni */
struct TOPO_GROUPS* groups = inst->topo_groups; struct TOPO_GROUPS* groups = inst->topo_groups;
struct TOPO_NODE* gni = u_calloc(1, sizeof(*gni)); if (!gni) return -1; struct TOPO_NODE* gni = u_calloc(1, sizeof(*gni)); if (!gni) return -1;
gni->node_id = nid; gni->ver = 1; gni->group_id = group_id; memcpy(gni->public_key, ni->public_key, SC_PUBKEY_SIZE); gni->node_id = nid; gni->ver = 1; memcpy(gni->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); if (!a4) continue; struct TOPO_ADDR4* a4 = memory_pool_alloc(groups->v4_addr_pool); if (!a4) continue;
memset(a4, 0, sizeof(*a4)); memcpy(a4->addr, src->addr, 4); a4->port = src->port; memset(a4, 0, sizeof(*a4)); memcpy(a4->addr, src->addr, 4); a4->port = src->port;

16
src/routing_layer/topo_node.c

@ -259,13 +259,15 @@ int topo_node_dyn_size(const struct TOPOMSG_NODE* msg) {
+ msg->hop_count * 8; + msg->hop_count * 8;
} }
int topo_node_serialize(struct TOPO_NODE* ni, struct TOPO_GROUP_NODE* 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,
uint64_t group_id, uint8_t flags,
uint8_t* out, size_t out_max, uint16_t cumulative_rtt) {
if (!ni || !nq || !out) return -1; if (!ni || !nq || !out) return -1;
struct TOPOMSG_NODE msg; struct TOPOMSG_NODE msg;
memset(&msg, 0, sizeof(msg)); memset(&msg, 0, sizeof(msg));
msg.flags = ni->flags; msg.flags = flags;
msg.group_id = ni->group_id; msg.group_id = group_id;
msg.node_id = ni->node_id; msg.node_id = ni->node_id;
msg.ver = ni->ver; msg.ver = ni->ver;
memcpy(msg.public_key, ni->public_key, SC_PUBKEY_SIZE); memcpy(msg.public_key, ni->public_key, SC_PUBKEY_SIZE);
@ -339,8 +341,6 @@ 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->group_ref_count = 0; ni->group_ref_count = 0;
ni->flags = msg->flags;
ni->group_id = msg->group_id;
ni->node_id = msg->node_id; ni->node_id = msg->node_id;
ni->ver = msg->ver; ni->ver = msg->ver;
memcpy(ni->public_key, msg->public_key, SC_PUBKEY_SIZE); memcpy(ni->public_key, msg->public_key, SC_PUBKEY_SIZE);
@ -551,7 +551,7 @@ void topo_node_dump_all(struct TOPO_GROUP* group) {
DEBUG_INFO(DEBUG_CATEGORY_BGP, "--- Node #%d: id=%016llx name=\"%s\" ver=%u grp=%llu%s ---", DEBUG_INFO(DEBUG_CATEGORY_BGP, "--- Node #%d: id=%016llx name=\"%s\" ver=%u grp=%llu%s ---",
idx, (unsigned long long)ni->node_id, name_s, ni->ver, idx, (unsigned long long)ni->node_id, name_s, ni->ver,
(unsigned long long)ni->group_id, is_local ? " [LOCAL]" : ""); (unsigned long long)group->group_id, is_local ? " [LOCAL]" : "");
log_dump(DEBUG_LEVEL_INFO, DEBUG_CATEGORY_BGP, " pubkey ", ni->public_key, SC_PUBKEY_SIZE); log_dump(DEBUG_LEVEL_INFO, DEBUG_CATEGORY_BGP, " pubkey ", ni->public_key, SC_PUBKEY_SIZE);
log_dump(DEBUG_LEVEL_INFO, DEBUG_CATEGORY_BGP, " ed25519", ni->ed25519_public_key, SC_PUBKEY_SIZE); log_dump(DEBUG_LEVEL_INFO, DEBUG_CATEGORY_BGP, " ed25519", ni->ed25519_public_key, SC_PUBKEY_SIZE);
@ -642,7 +642,7 @@ int topo_node_format_all(struct TOPO_GROUP* group, char* buf, size_t buf_size) {
FMT_ADD("--- Node #%d: id=%016llx name=\"%s\" ver=%u grp=%llu%s ---\n", FMT_ADD("--- Node #%d: id=%016llx name=\"%s\" ver=%u grp=%llu%s ---\n",
idx, (unsigned long long)ni->node_id, name_s, ni->ver, idx, (unsigned long long)ni->node_id, name_s, ni->ver,
(unsigned long long)ni->group_id, is_local ? " [LOCAL]" : ""); (unsigned long long)group->group_id, is_local ? " [LOCAL]" : "");
FMT_ADD(" pubkey: "); for (int k = 0; k < SC_PUBKEY_SIZE; k++) FMT_ADD("%02x", ni->public_key[k]); FMT_ADD("\n"); FMT_ADD(" pubkey: "); for (int k = 0; k < SC_PUBKEY_SIZE; k++) FMT_ADD("%02x", ni->public_key[k]); FMT_ADD("\n");
FMT_ADD(" ed25519: "); for (int k = 0; k < SC_PUBKEY_SIZE; k++) FMT_ADD("%02x", ni->ed25519_public_key[k]); FMT_ADD("\n"); FMT_ADD(" ed25519: "); for (int k = 0; k < SC_PUBKEY_SIZE; k++) FMT_ADD("%02x", ni->ed25519_public_key[k]); FMT_ADD("\n");
@ -797,8 +797,6 @@ int topo_group_update_my_nodeinfo(struct UTUN_INSTANCE* instance, struct TOPO_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(lq); group->local_node = NULL; return -1; } if (!ni) { u_free(lq); group->local_node = NULL; return -1; }
ni->group_ref_count = 0; ni->group_ref_count = 0;
ni->flags = (group->group_type == TOPO_GROUP_TYPE_CHAT) ? 0 : TOPO_FLAG_SEND_SUBNETS;
ni->group_id = group->group_id;
ni->node_id = instance->node_id; ni->node_id = instance->node_id;
ni->ver = (old_ver % 255) + 1; ni->ver = (old_ver % 255) + 1;
ni->client_type = instance->client_type; ni->client_type = instance->client_type;

6
src/routing_layer/topo_node.h

@ -139,7 +139,7 @@ static inline int topo_list_count(struct _topo_head* head) { int n = 0; while (h
struct TOPO_NODE { struct TOPO_NODE {
struct ll_entry ll; struct ll_entry ll;
uint32_t group_ref_count; uint32_t group_ref_count;
uint8_t flags; uint64_t group_id, node_id; uint8_t ver; uint64_t node_id; uint8_t ver;
uint8_t public_key[SC_PUBKEY_SIZE], ed25519_public_key[SC_PUBKEY_SIZE]; uint8_t public_key[SC_PUBKEY_SIZE], ed25519_public_key[SC_PUBKEY_SIZE];
uint8_t x25519_self_sig[64]; uint8_t x25519_self_sig[64];
uint8_t client_type; // CLIENT_TYPE_SERVER/DESKTOP/MOBILE uint8_t client_type; // CLIENT_TYPE_SERVER/DESKTOP/MOBILE
@ -191,7 +191,9 @@ struct TOPO_GROUP_NODE* topo_node_find_by_id(struct TOPO_GROUP* group, uint64_t
void topo_nodeq_free_group_fields(struct TOPO_GROUPS* groups, struct TOPO_GROUP_NODE* nq); void topo_nodeq_free_group_fields(struct TOPO_GROUPS* groups, struct TOPO_GROUP_NODE* nq);
int topo_node_dyn_size(const struct TOPOMSG_NODE* msg); int topo_node_dyn_size(const struct TOPOMSG_NODE* msg);
int topo_node_serialize(struct TOPO_NODE* ni, struct TOPO_GROUP_NODE* 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,
uint64_t group_id, uint8_t flags,
uint8_t* out, size_t out_max, uint16_t cumulative_rtt);
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, 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, uint64_t** out_hop_list, uint8_t* out_hop_count,
uint16_t* out_cumulative_rtt); uint16_t* out_cumulative_rtt);

4
src/routing_layer/topo_node_doc.md

@ -37,7 +37,7 @@ topo_group_update_my_nodeinfo(instance, group);
```c ```c
struct TOPO_NODE* ni = topo_node_registry_find(groups, nq->node_id); struct TOPO_NODE* ni = topo_node_registry_find(groups, nq->node_id);
uint8_t buf[4096]; uint8_t buf[4096];
int len = topo_node_serialize(ni, nq, buf, sizeof(buf), cumulative_rtt); int len = topo_node_serialize(ni, nq, group_id, flags, buf, sizeof(buf), cumulative_rtt);
``` ```
Запаковывает `TOPO_NODE` + per-group данные (`TOPO_GROUP_NODE`) в буфер: заголовок Запаковывает `TOPO_NODE` + per-group данные (`TOPO_GROUP_NODE`) в буфер: заголовок
`TOPOMSG_NODE`, затем имя узла, v4/v6 sock_meta, v4/v6 адреса, подсети (если `TOPOMSG_NODE`, затем имя узла, v4/v6 sock_meta, v4/v6 адреса, подсети (если
@ -143,7 +143,7 @@ topo_node_format_all(group, buf, size); // в строку
**Сериализация:** **Сериализация:**
- `topo_node_dyn_size(msg)` — размер динамической части по счётчикам заголовка. - `topo_node_dyn_size(msg)` — размер динамической части по счётчикам заголовка.
- `topo_node_serialize(ni, nq, out, out_max, cumulative_rtt)` — сериализовать `TOPO_NODE` + per-group в буфер. - `topo_node_serialize(ni, nq, group_id, flags, out, out_max, cumulative_rtt)` — сериализовать `TOPO_NODE` + per-group в буфер.
- `topo_node_deserialize(group, data, len, &out_ni, &out_subnets, &out_hop_list, &out_hop_count, &out_cumulative_rtt)` — восстановить из wire-формата. - `topo_node_deserialize(group, data, len, &out_ni, &out_subnets, &out_hop_list, &out_hop_count, &out_cumulative_rtt)` — восстановить из wire-формата.
**Пути:** **Пути:**

41
src/routing_layer/topo_node_sqlite.c

@ -9,6 +9,7 @@
#include "../lib/json_flat.h" #include "../lib/json_flat.h"
#include <string.h> #include <string.h>
#include <stdio.h> #include <stdio.h>
#include <stdlib.h>
#define PEERS_JOIN_SIG_SIZE 64 #define PEERS_JOIN_SIG_SIZE 64
#define PEERS_JOIN_TS_SIZE 8 #define PEERS_JOIN_TS_SIZE 8
@ -168,6 +169,46 @@ int topo_node_sqlite_member_exists(sqlite3* db, uint64_t node_id) {
return found; return found;
} }
int topo_node_sqlite_get_member_channels(sqlite3* db, uint64_t node_id,
uint64_t** out_ch_ids, int* out_count) {
if (!db || !out_ch_ids || !out_count) return -1;
*out_ch_ids = NULL; *out_count = 0;
sqlite3_stmt* s = NULL;
if (sqlite3_prepare_v2(db,
"SELECT name FROM sqlite_master WHERE type='table' AND name LIKE 'peers\\_%' ESCAPE '\\'",
-1, &s, NULL) != SQLITE_OK) return -1;
uint64_t* ids = NULL; int count = 0, cap = 0;
char sql[256];
while (sqlite3_step(s) == SQLITE_ROW) {
const char* tbl = (const char*)sqlite3_column_text(s, 0);
if (!tbl || strncmp(tbl, "peers_", 6) != 0) continue;
snprintf(sql, sizeof(sql), "SELECT 1 FROM \"%s\" WHERE node_id=?", tbl);
sqlite3_stmt* m = NULL;
if (sqlite3_prepare_v2(db, sql, -1, &m, NULL) == SQLITE_OK) {
sqlite3_bind_int64(m, 1, (sqlite3_int64)node_id);
int is_member = (sqlite3_step(m) == SQLITE_ROW);
sqlite3_finalize(m);
if (is_member) {
uint64_t ch = strtoull(tbl + 6, NULL, 10);
if (ch != 0) {
if (count == cap) {
cap = cap ? cap * 2 : 8;
uint64_t* tmp = u_realloc(ids, (size_t)cap * sizeof(uint64_t));
if (!tmp) { u_free(ids); sqlite3_finalize(s); return -1; }
ids = tmp;
}
ids[count++] = ch;
}
}
}
}
sqlite3_finalize(s);
*out_ch_ids = ids; *out_count = count;
return 0;
}
int topo_node_sqlite_init(sqlite3* db) { int topo_node_sqlite_init(sqlite3* db) {
if (!db) return -1; if (!db) return -1;

4
src/routing_layer/topo_node_sqlite.h

@ -23,6 +23,10 @@ int topo_node_sqlite_addrs_put(sqlite3* db, uint64_t node_id, struct TOPO_NODE*
/* Есть ли у узла запись мембера хотя бы в одной peers_<ch> (реальный мембер с ключами). */ /* Есть ли у узла запись мембера хотя бы в одной peers_<ch> (реальный мембер с ключами). */
int topo_node_sqlite_member_exists(sqlite3* db, uint64_t node_id); int topo_node_sqlite_member_exists(sqlite3* db, uint64_t node_id);
/* Список channel_id (uint64) всех peers_<ch>, где узел является членом. 0=ок. */
int topo_node_sqlite_get_member_channels(sqlite3* db, uint64_t node_id,
uint64_t** out_ch_ids, int* out_count);
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,
const uint8_t* x25519_pub, const uint8_t* x25519_priv, const uint8_t* x25519_pub, const uint8_t* x25519_priv,

2
tests/test_auto_socket_dynamic.c

@ -160,7 +160,7 @@ static struct TOPO_GROUP_NODE* mk_srv_node(void) {
struct TOPO_GROUP_NODE* nq = u_calloc(1, sizeof(struct TOPO_GROUP_NODE)); 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->group_ref_count = 1; ni->node_id = ctx.srv_node_id; ni->group_id = TOPO_GROUP_UTUN; ni->group_ref_count = 1; ni->node_id = ctx.srv_node_id;
memcpy(ni->public_key, ctx.srv_pubkey, SC_PUBKEY_SIZE); memcpy(ni->public_key, ctx.srv_pubkey, SC_PUBKEY_SIZE);
nq->ll.size = sizeof(struct TOPO_GROUP_NODE) - sizeof(struct ll_entry); nq->ll.size = sizeof(struct TOPO_GROUP_NODE) - sizeof(struct ll_entry);
nq->node_id = ctx.srv_node_id; nq->node_id = ctx.srv_node_id;

2
tests/test_etcp_connect.c

@ -72,7 +72,7 @@ static struct TOPO_GROUP_NODE* mknode(struct UTUN_INSTANCE* inst, uint64_t nid,
struct TOPO_GROUP_NODE* nq = u_calloc(1, sizeof(struct TOPO_GROUP_NODE)); 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->group_ref_count = 1; ni->node_id = nid; ni->ver = 0; ni->group_id = TOPO_GROUP_UTUN; ni->group_ref_count = 1; ni->node_id = nid; ni->ver = 0;
memcpy(ni->public_key, pubkey, SC_PUBKEY_SIZE); memcpy(ni->public_key, pubkey, SC_PUBKEY_SIZE);
nq->ll.size = sizeof(struct TOPO_GROUP_NODE) - sizeof(struct ll_entry); nq->ll.size = sizeof(struct TOPO_GROUP_NODE) - sizeof(struct ll_entry);
nq->node_id = nid; nq->node_id = nid;

2
tests/test_etcp_router_reconnect.c

@ -83,7 +83,7 @@ static struct TOPO_GROUP_NODE* mknode(struct UTUN_INSTANCE* inst, uint64_t nid,
struct TOPO_GROUP_NODE* nq = u_calloc(1, sizeof(struct TOPO_GROUP_NODE)); 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->group_ref_count = 1; ni->node_id = nid; ni->ver = 0; ni->group_id = TOPO_GROUP_UTUN; ni->group_ref_count = 1; ni->node_id = nid; ni->ver = 0;
memcpy(ni->public_key, pubkey, SC_PUBKEY_SIZE); memcpy(ni->public_key, pubkey, SC_PUBKEY_SIZE);
nq->ll.size = sizeof(struct TOPO_GROUP_NODE) - sizeof(struct ll_entry); nq->ll.size = sizeof(struct TOPO_GROUP_NODE) - sizeof(struct ll_entry);
nq->node_id = nid; nq->node_id = nid;

7
tests/test_route_ping.c

@ -241,18 +241,19 @@ int main(void) {
goto cleanup; goto cleanup;
} }
// 2. Wait for BGP exchange so B learns about C // 2. Wait for BGP exchange so B learns C (registry has C's nodeinfo + addresses)
DEBUG_INFO(DEBUG_CATEGORY_BGP, "Waiting for BGP exchange (B learns C)..."); DEBUG_INFO(DEBUG_CATEGORY_BGP, "Waiting for BGP exchange (B learns C)...");
int bgp_wait_cycles = 0; int bgp_wait_cycles = 0;
while (!test_timed_out && bgp_wait_cycles < 500) { while (!test_timed_out && bgp_wait_cycles < 500) {
if (topo_groups_get_default(inst_b->topo_groups) && topo_node_find_by_id(topo_groups_get_default(inst_b->topo_groups), g_node_id_c) != NULL) { struct TOPO_NODE* ni_c = topo_node_registry_find(inst_b->topo_groups, g_node_id_c);
if (ni_c && ni_c->v4_addrs) {
DEBUG_INFO(DEBUG_CATEGORY_BGP, "B learned about C"); DEBUG_INFO(DEBUG_CATEGORY_BGP, "B learned about C");
break; break;
} }
uasync_poll(ua, 10); uasync_poll(ua, 10);
bgp_wait_cycles++; bgp_wait_cycles++;
} }
if (!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 (!topo_node_registry_find(inst_b->topo_groups, g_node_id_c)) {
DEBUG_ERROR(DEBUG_CATEGORY_BGP, "B did not learn about C in time"); DEBUG_ERROR(DEBUG_CATEGORY_BGP, "B did not learn about C in time");
goto cleanup; goto cleanup;
} }

1
tools/chatgui-android/jni_bridge/android_jni_bridge.c

@ -1245,6 +1245,7 @@ int utun_bridge_is_responsive(void) {
* ────────────────────────────────────────────────────────────────── */ * ────────────────────────────────────────────────────────────────── */
int utun_bridge_voice_start(const char* channel_id) { int utun_bridge_voice_start(const char* channel_id) {
voice_recorder_init(g_db_path); /* idempotent: гарантирует g_rec после stop/restart */
return voice_recorder_start(channel_id, 48000, 1); return voice_recorder_start(channel_id, 48000, 1);
} }

6
tools/chatgui-android/libutun_lite/voice_recorder.c

@ -115,7 +115,11 @@ void voice_recorder_deinit(void) {
int voice_recorder_start(const char* channel_id, int sample_rate, int channels) { int voice_recorder_start(const char* channel_id, int sample_rate, int channels) {
pthread_mutex_lock(&g_init_mtx); pthread_mutex_lock(&g_init_mtx);
if (!g_rec) { pthread_mutex_unlock(&g_init_mtx); return -1; } if (!g_rec) {
DEBUG_ERROR(DEBUG_CATEGORY_GENERAL, "voice_recorder_start: not initialized (g_rec=NULL)");
pthread_mutex_unlock(&g_init_mtx);
return -1;
}
pthread_mutex_lock(&g_rec->mtx); pthread_mutex_lock(&g_rec->mtx);
if (g_rec->active) { if (g_rec->active) {
DEBUG_WARN(DEBUG_CATEGORY_GENERAL, "voice_recorder_start: already active"); DEBUG_WARN(DEBUG_CATEGORY_GENERAL, "voice_recorder_start: already active");

Loading…
Cancel
Save