Route UTUN configuration and group ownership through NCD

This commit is contained in:
evgeny committed 2026-09-27 21:59:32 +03:00
1 parent 9745a987ac
commit bce17d9cfc
21 files changed
+592 -332

No files matched your search

+1 -1
View File
@@ -855,7 +855,7 @@ int member_sync_verify_and_purge(struct UTUN_INSTANCE* inst, const char* ch_id)
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);
if (c) topo_group_remove_conn(g, c, TOPO_REMOVE_MEMBER_INVALID);
}
}
purged++;
+125 -31
View File
@@ -192,6 +192,7 @@ static const char* group_subcmd_name(uint8_t subcmd) {
case TOPO_SUBCMD_ERR_GROUP_MISMATCH: return "ERR_GROUP_MISMATCH";
case TOPO_SUBCMD_JOIN_GROUP: return "JOIN_GROUP";
case TOPO_SUBCMD_RESYNC: return "RESYNC";
case TOPO_SUBCMD_LEAVE_GROUP: return "LEAVE_GROUP";
default: return "?";
}
}
@@ -225,6 +226,12 @@ static void topo_group_broadcast_withdraw(struct TOPO_GROUP* group, uint64_t nod
// Приём пакетов
// ============================================================================
static struct NODE_CONN_DIRECT* topo_config_handle(struct UTUN_INSTANCE* instance, uint64_t node_id) {
for (struct CONFIG_CONN_HANDLE* owner = instance->config_conn_handles; owner; owner = owner->next)
if (owner->node_id == node_id) return owner->handle;
return NULL;
}
static int topo_group_has_sender(struct TOPO_GROUP* group, struct ETCP_CONN* conn) {
for (struct ll_entry* e = group->senders_list ? group->senders_list->head : NULL; e; e = e->next)
if (((struct TOPO_GROUP_CONN_ITEM*)e->data)->conn == conn) return 1;
@@ -253,7 +260,7 @@ static void topo_group_receive_cbk(struct ETCP_CONN* from_conn, struct ll_entry*
}
uint64_t pkt_group_id = 0;
if (subcmd == TOPO_SUBCMD_NODEINFO && entry->len >= 3) {
if (subcmd == TOPO_SUBCMD_NODEINFO && entry->len >= sizeof(struct TOPOMSG_NODEINFO_PKT)) {
pkt_group_id = ((struct TOPOMSG_NODEINFO_PKT*)data)->node.group_id;
} else if (subcmd == TOPO_SUBCMD_WITHDRAW && entry->len >= sizeof(struct TOPOMSG_WITHDRAW_PKT)) {
pkt_group_id = ((struct TOPOMSG_WITHDRAW_PKT*)data)->group_id;
@@ -263,7 +270,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;
} 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;
} else if (subcmd == TOPO_SUBCMD_JOIN_GROUP && entry->len >= sizeof(struct TOPOMSG_JOIN_GROUP)) {
} else if ((subcmd == TOPO_SUBCMD_JOIN_GROUP || subcmd == TOPO_SUBCMD_LEAVE_GROUP) &&
entry->len == sizeof(struct TOPOMSG_JOIN_GROUP)) {
pkt_group_id = ((struct TOPOMSG_JOIN_GROUP*)data)->group_id;
}
@@ -284,10 +292,23 @@ static void topo_group_receive_cbk(struct ETCP_CONN* from_conn, struct ll_entry*
queue_dgram_free(entry); queue_entry_free(entry); return;
}
if ((subcmd == TOPO_SUBCMD_LEAVE_GROUP || (group->group_type == TOPO_GROUP_TYPE_UTUN &&
(subcmd == TOPO_SUBCMD_NODEINFO || subcmd == TOPO_SUBCMD_WITHDRAW))) &&
!topo_group_has_sender(group, from_conn)) {
DEBUG_DEBUG(DEBUG_CATEGORY_BGP, "ignore %s from detached peer=%016llx group=%016llx",
group_subcmd_name(subcmd), (unsigned long long)from_conn->peer_node_id, (unsigned long long)pkt_group_id);
queue_dgram_free(entry); queue_entry_free(entry); return;
}
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_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_LEAVE_GROUP) {
DEBUG_INFO(DEBUG_CATEGORY_BGP, "peer left group: peer=%016llx group=%016llx",
(unsigned long long)from_conn->peer_node_id, (unsigned long long)group->group_id);
topo_group_remove_conn(group, from_conn, TOPO_REMOVE_REMOTE_LEAVE);
}
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) {
if (entry->len >= sizeof(struct TOPOMSG_ERR_GROUP_MISMATCH)) {
@@ -325,24 +346,9 @@ static void topo_group_conn_status(struct ETCP_CONN* conn, int status, void* arg
return;
}
if (status == ETCP_CONN_STATUS_UP && conn->peer_node_id) {
int is_client = conn->instance->config && config_peer_in_clients(conn->instance->config, conn->crypto_ctx.peer_public_key);
int explicit_conn = conn->bgp_ready_cbk != NULL;
/* пассивная сторона: просим пира заново анонсировать свои группы (после флэпа/переподключения) */
if (!is_client && !explicit_conn)
topo_group_send_resync(conn);
/* non-CHAT (UTUN): узлы из конфига (clients) + явные подключения (etcp_connect). */
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) {
if (is_client || explicit_conn) {
topo_group_new_conn(g, conn); /* добавить + инициировать BGP */
topo_group_send_join_group(g, conn); /* запросить членство у пира */
}
}
}
/* UTUN присоединяет пир через UP принадлежащего ему NCD handle.
* Сам по себе транспортный UP не означает участия в UTUN-группе. */
if (!topo_config_handle(conn->instance, conn->peer_node_id)) topo_group_send_resync(conn);
/* CHAT: только члены каналов (peers_* в БД). */
sqlite3* db = conn->instance ? conn->instance->topo_sqlite_db : NULL;
uint64_t* chs = NULL; int chn = 0;
@@ -357,7 +363,7 @@ static void topo_group_conn_status(struct ETCP_CONN* conn, int status, void* arg
} else if (status == ETCP_CONN_STATUS_DOWN || status == ETCP_CONN_STATUS_DELETE) {
struct ll_entry* fe = groups->group_list->head;
while (fe) { struct TOPO_GROUP* g = (struct TOPO_GROUP*)fe; fe = fe->next;
topo_group_remove_conn(g, conn);
topo_group_remove_conn(g, conn, TOPO_REMOVE_TRANSPORT_DOWN);
}
}
}
@@ -636,7 +642,7 @@ struct TOPO_GROUP* topo_groups_create_group(struct TOPO_GROUPS* g, uint64_t grou
for (uint32_t slot = 0; slot < connections->hash_size; slot++) {
struct ll_entry* entry = connections->hash_table[slot];
while (entry) {
struct conn_queue_entry* cqe = (struct conn_queue_entry*)entry;
struct conn_queue_entry* cqe = (struct conn_queue_entry*)entry->data;
if (cqe->conn && cqe->peer_node_id != 0) {
if (group_type == TOPO_GROUP_TYPE_CHAT && db) {
/* CHAT: добавляем conn только если пир — член именно этого канала */
@@ -647,8 +653,9 @@ struct TOPO_GROUP* topo_groups_create_group(struct TOPO_GROUPS* g, uint64_t grou
}
}
u_free(chs);
} else {
topo_group_new_conn(group, cqe->conn);
} else if (group_type == TOPO_GROUP_TYPE_UTUN && cqe->conn->links_up) {
struct NODE_CONN_DIRECT* handle = topo_config_handle(g->instance, cqe->peer_node_id);
if (handle) topo_group_join_peer(group, handle);
}
}
entry = entry->hash_next;
@@ -762,7 +769,7 @@ int topo_group_new_conn(struct TOPO_GROUP* group, struct ETCP_CONN* conn) {
}
/* Обрабатывает DOWN пира: чистит пути, каскадно удаляет узлы, шлёт withdraw. */
void topo_group_remove_conn(struct TOPO_GROUP* group, struct ETCP_CONN* conn) {
void topo_group_remove_conn(struct TOPO_GROUP* group, struct ETCP_CONN* conn, enum topo_group_remove_reason reason) {
if (!group || !conn) { DEBUG_ERROR(DEBUG_CATEGORY_BGP, "invalid args"); return; }
if (!conn->instance) { DEBUG_ERROR(DEBUG_CATEGORY_BGP, "invalid instance"); return; }
@@ -788,7 +795,9 @@ void topo_group_remove_conn(struct TOPO_GROUP* group, struct ETCP_CONN* conn) {
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++; }
if (reason == TOPO_REMOVE_TRANSPORT_DOWN && 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);
@@ -842,7 +851,8 @@ void topo_group_remove_conn(struct TOPO_GROUP* group, struct ETCP_CONN* conn) {
topo_group_connect_on_down(group, conn);
DEBUG_INFO(DEBUG_CATEGORY_BGP, "BGP peer removed: %s nodes=%d grp=%016llx", conn->log_name, nodes_removed, (unsigned long long)group->group_id);
DEBUG_INFO(DEBUG_CATEGORY_BGP, "BGP peer removed: %s nodes=%d grp=%016llx reason=%d recovery=%d",
conn->log_name, nodes_removed, (unsigned long long)group->group_id, reason, cascaded);
}
@@ -1035,7 +1045,6 @@ int topo_group_process_nodeinfo(struct TOPO_GROUP* group, struct ETCP_CONN* from
u_free(new_hop_list); return -1;
}
new_ni = stored;
node_conn_direct_update_node(group->instance, node_id);
if (!is_new_node) topo_nodeq_free_group_fields(group->instance->topo_groups, nodeinfo1);
nodeinfo1->node_id = node_id;
nodeinfo1->subnets = new_subnets;
@@ -1119,6 +1128,9 @@ int topo_group_process_nodeinfo(struct TOPO_GROUP* group, struct ETCP_CONN* from
if (v4a > 0 || v6a > 0) route_connectivity_probe_node(group->instance, group, nodeinfo1);
}
/* Создание линков может вызвать транспортные callbacks: структура группы уже обновлена. */
node_conn_direct_update_node(group->instance, node_id);
return 0;
}
@@ -1154,7 +1166,7 @@ int topo_group_process_withdraw(struct TOPO_GROUP* group, struct ETCP_CONN* send
void topo_group_send_nodeinfo(struct TOPO_GROUP* group, struct TOPO_GROUP_NODE* node, struct ETCP_CONN* conn, uint16_t cumulative_rtt) {
if (!group || !node || !conn) return;
size_t max_sz = sizeof(struct TOPOMSG_NODEINFO_PKT) + 4096;
size_t max_sz = sizeof(struct TOPOMSG_NODEINFO_PKT) + TOPO_NODE_WIRE_MAX_SIZE;
uint8_t* p = u_malloc(max_sz);
if (!p) return;
@@ -1219,6 +1231,89 @@ static bool topo_group_should_send_to(const struct TOPO_GROUP_NODE* nq, uint64_t
return false;
}
int topo_group_join_peer(struct TOPO_GROUP* group, struct NODE_CONN_DIRECT* handle) {
struct ETCP_CONN* conn = node_conn_direct_get_conn(handle);
if (!group || !conn || !conn->links_up) {
DEBUG_WARN(DEBUG_CATEGORY_BGP, "join peer requires a connected NCD handle"); return -1;
}
return topo_group_new_conn(group, conn);
}
struct topo_leave {
struct UTUN_INSTANCE* instance;
uint64_t group_id;
struct NODE_CONN_DIRECT* handle;
struct ll_queue* queue;
struct queue_waiter_handle waiter;
};
static void topo_leave_finish(struct topo_leave* leave) {
queue_waiter_cancel(leave->queue, &leave->waiter);
node_conn_direct_close(leave->handle);
u_free(leave);
}
static void topo_leave_event(struct NODE_CONN_DIRECT* handle, enum ncd_event event, void* arg) {
(void)handle;
if (event == NCD_EVENT_UP) return;
struct topo_leave* leave = arg;
DEBUG_WARN(DEBUG_CATEGORY_BGP, "LEAVE_GROUP cancelled: transport event=%d group=%016llx",
event, (unsigned long long)leave->group_id);
/* NCD CLOSED приходит до уничтожения send_input_q; waiter ещё можно отменить. */
topo_leave_finish(leave);
}
static void topo_leave_ready(struct ll_queue* queue, void* arg) {
(void)queue;
struct topo_leave* leave = arg;
struct ETCP_CONN* conn = node_conn_direct_get_conn(leave->handle);
struct TOPO_GROUP* group = topo_groups_find(leave->instance->topo_groups, leave->group_id);
if (group && topo_group_has_sender(group, conn)) {
DEBUG_DEBUG(DEBUG_CATEGORY_BGP, "LEAVE_GROUP superseded by a new membership group=%016llx",
(unsigned long long)leave->group_id);
topo_leave_finish(leave); return;
}
struct ll_entry* packet = ll_alloc_lldgram(sizeof(struct TOPOMSG_JOIN_GROUP));
int result = -1;
if (packet) {
struct TOPOMSG_JOIN_GROUP msg = { .cmd = ETCP_ID_TOPO_ENTRY, .subcmd = TOPO_SUBCMD_LEAVE_GROUP,
.group_id = leave->group_id };
memcpy(packet->dgram, &msg, sizeof(msg)); packet->len = sizeof(msg);
result = etcp_send(conn, packet);
if (result != 0) { queue_dgram_free(packet); queue_entry_free(packet); }
}
if (result != 0) DEBUG_ERROR(DEBUG_CATEGORY_BGP, "cannot send LEAVE_GROUP group=%016llx", (unsigned long long)leave->group_id);
else DEBUG_INFO(DEBUG_CATEGORY_BGP, "LEAVE_GROUP sent: peer=%016llx group=%016llx",
(unsigned long long)node_conn_direct_node_id(leave->handle), (unsigned long long)leave->group_id);
topo_leave_finish(leave);
}
int topo_group_leave_peer(struct TOPO_GROUP* group, struct NODE_CONN_DIRECT* handle) {
struct ETCP_CONN* conn = node_conn_direct_get_conn(handle);
if (!group || !conn) { DEBUG_WARN(DEBUG_CATEGORY_BGP, "leave peer without group/connection"); return -1; }
if (!topo_group_has_sender(group, conn)) return 0;
if (!conn->send_input_q || conn->close_requested) {
topo_group_remove_conn(group, conn, TOPO_REMOVE_LOCAL_LEAVE); return 0;
}
struct topo_leave* leave = u_calloc(1, sizeof(*leave));
if (!leave) {
DEBUG_ERROR(DEBUG_CATEGORY_BGP, "LEAVE_GROUP allocation failed");
topo_group_remove_conn(group, conn, TOPO_REMOVE_LOCAL_LEAVE); return -1;
}
leave->instance = group->instance; leave->group_id = group->group_id; leave->queue = conn->send_input_q;
if (node_conn_direct_open(group->instance, conn->peer_node_id, topo_leave_event, leave, &leave->handle, NULL) == NCD_ERR) {
DEBUG_ERROR(DEBUG_CATEGORY_BGP, "cannot retain NCD handle for LEAVE_GROUP"); u_free(leave);
topo_group_remove_conn(group, conn, TOPO_REMOVE_LOCAL_LEAVE); return -1;
}
topo_group_remove_conn(group, conn, TOPO_REMOVE_LOCAL_LEAVE);
/* Отправка единственного управляющего пакета после освобождения общей очереди.
Контекст не зависит от срока жизни группы; новый JOIN отменяет устаревший LEAVE. */
if (queue_waiter_wait(leave->queue, &leave->waiter, topo_leave_ready, leave) < 0) {
DEBUG_ERROR(DEBUG_CATEGORY_BGP, "cannot wait to send LEAVE_GROUP"); topo_leave_finish(leave); return -1;
}
return 0;
}
/* Шлёт пиру полную таблицу узлов группы (все узлы, кроме достижимых через него). */
static void topo_group_send_full_table(struct TOPO_GROUP* group, struct ETCP_CONN* conn) {
if (!group || !conn) return;
@@ -1262,8 +1357,7 @@ static void topo_group_handle_join_group(struct TOPO_GROUP* group, struct ETCP_C
static void topo_group_handle_resync(struct UTUN_INSTANCE* instance, struct ETCP_CONN* conn) {
if (!instance || !conn || !conn->instance || !instance->topo_groups) return;
/* общий сигнал от пассивной стороны: рестартуем обмен, если мы для этого пира VPN-клиент */
int is_client = conn->instance->config && config_peer_in_clients(conn->instance->config, conn->crypto_ctx.peer_public_key);
if (!is_client && conn->bgp_ready_cbk == NULL) return;
if (!topo_config_handle(instance, conn->peer_node_id)) return;
DEBUG_INFO(DEBUG_CATEGORY_BGP, "handle_resync: from %s — re-announce groups", conn->log_name);
struct ll_entry* fe = instance->topo_groups->group_list->head;
while (fe) {
+11 -1
View File
@@ -108,6 +108,7 @@ void topo_fire_nodeinfo_cbk(struct UTUN_INSTANCE* instance, struct TOPO_GROUP* g
#define TOPO_SUBCMD_ERR_GROUP_MISMATCH 0x0C // ошибка несоответствия типа группы
#define TOPO_SUBCMD_JOIN_GROUP 0x0D // запрос членства в группе
#define TOPO_SUBCMD_RESYNC 0x0E // общий запрос: «я переподключился, заново анонсируй свои группы»
#define TOPO_SUBCMD_LEAVE_GROUP 0x0F // завершить участие, сохранив общий транспорт
#define MAX_HOPS 16
#define BGP_NODES_HASH_SIZE 256
@@ -298,7 +299,14 @@ int topo_group_new_conn(struct TOPO_GROUP* group, struct ETCP_CONN* conn);
*
* Вызывается при ETCP on_down.
*/
void topo_group_remove_conn(struct TOPO_GROUP* group, struct ETCP_CONN* conn);
enum topo_group_remove_reason {
TOPO_REMOVE_TRANSPORT_DOWN,
TOPO_REMOVE_LOCAL_LEAVE,
TOPO_REMOVE_REMOTE_LEAVE,
TOPO_REMOVE_MEMBER_INVALID
};
/* Только потеря транспорта запускает recovery каскадно потерянных маршрутов. */
void topo_group_remove_conn(struct TOPO_GROUP* group, struct ETCP_CONN* conn, enum topo_group_remove_reason reason);
/**
* @brief Обрабатывает пакет NODEINFO.
@@ -309,6 +317,8 @@ void topo_group_remove_conn(struct TOPO_GROUP* group, struct ETCP_CONN* conn);
* @return 0 при успехе
*/
int topo_group_process_nodeinfo(struct TOPO_GROUP* group, struct ETCP_CONN* from, const uint8_t* data, size_t len);
int topo_group_join_peer(struct TOPO_GROUP* group, struct NODE_CONN_DIRECT* handle);
int topo_group_leave_peer(struct TOPO_GROUP* group, struct NODE_CONN_DIRECT* handle);
/**
* @brief Обрабатывает WITHDRAW.
+3 -1
View File
@@ -455,7 +455,9 @@ int topo_node_serialize(struct TOPO_NODE* ni, struct TOPO_GROUP_NODE* nq,
topo_list_count((struct _topo_head*)ni->v4_addrs) > UINT8_MAX ||
topo_list_count((struct _topo_head*)ni->v6_sock_meta) > UINT8_MAX ||
topo_list_count((struct _topo_head*)ni->v6_addrs) > UINT8_MAX ||
topo_list_count((struct _topo_head*)ni->reality_socks) > UINT8_MAX) {
topo_list_count((struct _topo_head*)ni->reality_socks) > UINT8_MAX ||
(nq->subnets && (topo_list_count((struct _topo_head*)nq->subnets->v4_subnets) > UINT8_MAX ||
topo_list_count((struct _topo_head*)nq->subnets->v6_subnets) > UINT8_MAX))) {
DEBUG_ERROR(DEBUG_CATEGORY_BGP, "node exceeds wire format limits"); return -1;
}
msg.local_v4_sockets = topo_list_count((struct _topo_head*)ni->v4_sock_meta);
+4 -3
View File
@@ -117,8 +117,8 @@ struct TOPOMSG_SUBNET4 { uint8_t addr[4]; uint8_t prefix_length; } __attribute
struct TOPOMSG_SUBNET6 { uint8_t addr[16]; uint8_t prefix_length; } __attribute__((packed));
/* Reality-камуфляж сокета: что нужно клиенту для подключения к reality TCP-сокету.
* short_id(8) + server_pubkey(32) + version(3) + server_name(фиксированный 64). */
#define TOPO_REALITY_SNAME_MAX 64
* short_id(8) + server_pubkey(32) + version(3) + server_name(фиксированный 256). */
#define TOPO_REALITY_SNAME_MAX 256
struct TOPOMSG_REALITY_SOCK {
uint8_t socket_id;
uint8_t short_id[8];
@@ -160,7 +160,7 @@ struct TOPO_REALITY_SOCK {
};
/* Reality-параметры адреса для персистенции в node_addresses.options (BLOB, фиксированный размер). */
#define TOPO_ADDR_REALITY_OPTS_SIZE 107
#define TOPO_ADDR_REALITY_OPTS_SIZE (8 + 32 + 3 + TOPO_REALITY_SNAME_MAX)
struct TOPO_ADDR_REALITY_OPTS {
uint8_t short_id[8];
uint8_t server_pubkey[32];
@@ -253,6 +253,7 @@ uint16_t topo_get_chain_rtt(struct TOPO_GROUP_NODE* nq);
/** Build canonical message for Ed25519 signature: x25519_pubkey || name || client_type || client_activity || addresses */
#define TOPO_SIG_MSG_MAX_SIZE 2048
#define TOPO_NODE_WIRE_MAX_SIZE 16384
int topo_node_build_sig_msg(struct TOPO_NODE* ni, uint8_t* buf, size_t buf_size);
/** Sign self NODEINFO with Ed25519: build_sig_msg + sc_ed25519_sign → ni->x25519_self_sig */
+1 -1
View File
@@ -921,7 +921,7 @@ uint64_t topo_node_sqlite_snapshot_timestamp(sqlite3* db, uint64_t node_id) {
int topo_node_sqlite_snapshot_put(sqlite3* db, struct TOPO_NODE* ni) {
if (!db || topo_node_verify(ni) < 0) { DEBUG_ERROR(DEBUG_CATEGORY_BGP, "invalid node snapshot"); return -1; }
uint8_t record[4096];
uint8_t record[TOPO_NODE_WIRE_MAX_SIZE];
struct TOPO_GROUP_NODE nq = {0};
int len = topo_node_serialize(ni, &nq, 0, 0, record, sizeof(record), 0);
if (len < 0) { DEBUG_ERROR(DEBUG_CATEGORY_BGP, "snapshot serialization failed"); return -1; }
+100 -134
View File
@@ -2770,53 +2770,111 @@ int init_sockets(struct UTUN_INSTANCE* instance) {
return 0; // All OK
}
/* Создать TCP-линк(и) с REALITY-камуфляжем для [client] с reality=1.
* conn создаётся через NCD (только public_key, без адресов — линки добавляем сами).
* local_srv у link опционален: если задан — это [server] transport=tcp для bind
* на нужный интерфейс (ip); если нет — bind не делается (ОС выбирает source).
* Возвращает NCD-handle (сохранить в config_conn_handles) или NULL. */
struct NODE_CONN_DIRECT* etcp_config_client_reality(struct UTUN_INSTANCE* instance, struct CFG_CLIENT* client,
const uint8_t pubkey_bin[SC_PUBKEY_SIZE], uint64_t node_id) {
if (!instance || !client || !pubkey_bin) return NULL;
struct TOPO_NODE ni_tmp; memset(&ni_tmp, 0, sizeof(ni_tmp));
ni_tmp.node_id = node_id; ni_tmp.node_name = client->name;
memcpy(ni_tmp.public_key, pubkey_bin, SC_PUBKEY_SIZE);
/* Конфигурация владеет только NCD handles; транспортные линки создаёт NCD. */
static void config_client_event(struct NODE_CONN_DIRECT* handle, enum ncd_event event, void* arg) {
struct UTUN_INSTANCE* instance = arg;
if (event == NCD_EVENT_UP) {
struct TOPO_GROUP* group = topo_groups_get_default(instance->topo_groups);
if (group && topo_group_join_peer(group, handle) < 0)
DEBUG_ERROR(DEBUG_CATEGORY_BGP, "configured peer join failed node=%016llx",
(unsigned long long)node_conn_direct_node_id(handle));
} else if (event == NCD_EVENT_TIMEOUT || event == NCD_EVENT_CLOSED) {
DEBUG_WARN(DEBUG_CATEGORY_CONNECTION, "configured peer node=%016llx event=%d",
(unsigned long long)node_conn_direct_node_id(handle), event);
}
}
static struct NODE_CONN_DIRECT* config_client_open(struct UTUN_INSTANCE* instance, struct CFG_CLIENT* client) {
struct TOPO_NODE ni = { .node_name = client->name };
if (sc_hex_to_binary(client->peer_public_key_hex, ni.public_key, SC_PUBKEY_SIZE) != SC_OK) {
DEBUG_ERROR(DEBUG_CATEGORY_CONFIG, "invalid public key for client %s", client->name); return NULL;
}
ni.node_id = sc_derive_node_id_from_pubkey(ni.public_key);
struct NODE_CONN_DIRECT* handle = NULL;
int r = node_conn_direct_open_node(instance, node_id, NULL, NULL, &handle, &ni_tmp, NULL);
if (r == NCD_ERR || !handle) {
DEBUG_ERROR(DEBUG_CATEGORY_REALITY, "client %s reality: ncd open failed node=0x%016llx", client->name, (unsigned long long)node_id);
return NULL;
}
struct ETCP_CONN* conn = node_conn_direct_get_conn(handle);
if (!conn) { node_conn_direct_close(handle); return NULL; }
int link_count = 0;
for (struct CFG_CLIENT_LINK* cl = client->links; cl; cl = cl->next) {
if (cl->remote_addr.ss_family != AF_INET && cl->remote_addr.ss_family != AF_INET6) {
DEBUG_WARN(DEBUG_CATEGORY_REALITY, "client %s reality: link without remote addr, skipping", client->name);
continue;
}
struct ETCP_SOCKET* bind_sock = NULL;
if (cl->local_srv) {
for (struct CFG_CLIENT_LINK* endpoint = client->links; endpoint; endpoint = endpoint->next) {
struct ETCP_SOCKET* sock = NULL;
if (endpoint->local_srv) {
for (struct ETCP_SOCKET* s = instance->etcp_sockets; s; s = s->next)
if (strcmp(cl->local_srv->name, s->name) == 0) { bind_sock = s; break; }
if (!bind_sock) DEBUG_WARN(DEBUG_CATEGORY_REALITY, "client %s reality: bind socket '%s' not found, using auto", client->name, cl->local_srv->name);
else if (!bind_sock->is_tcp) { DEBUG_ERROR(DEBUG_CATEGORY_REALITY, "client %s reality: bind socket '%s' is not TCP, ignoring bind", client->name, cl->local_srv->name); bind_sock = NULL; }
if (!strcmp(s->name, endpoint->local_srv->name)) { sock = s; break; }
if (!sock || (client->reality_enabled && !sock->is_tcp)) {
DEBUG_ERROR(DEBUG_CATEGORY_CONFIG, "client %s: missing/incompatible bind socket %s",
client->name, endpoint->local_srv->name); goto fail;
}
}
struct ETCP_LINK* tlink = etcp_link_new(conn, bind_sock, &cl->remote_addr, 0);
if (!tlink) { DEBUG_ERROR(DEBUG_CATEGORY_REALITY, "client %s reality: etcp_link_new failed", client->name); continue; }
tlink->is_tcp = 1;
tlink->reality = client->reality; tlink->reality_set = 1;
etcp_tcp_link_start_connect(tlink, &cl->remote_addr, 0);
link_count++;
DEBUG_INFO(DEBUG_CATEGORY_REALITY, "client %s reality: TCP link %d → %s sn=%s bind=%s",
client->name, tlink->local_link_id, sockaddr_storage_to_str(&cl->remote_addr).str,
client->reality.server_name, bind_sock ? bind_sock->name : "auto");
uint8_t protocol = client->reality_enabled ? TOPO_PROTO_TCP | TOPO_PROTO_REALITY :
(sock && sock->is_tcp ? TOPO_PROTO_TCP : TOPO_PROTO_UDP);
struct TOPO_ADDR4 v4 = { .protocol = protocol };
struct TOPO_ADDR6 v6 = { .protocol = protocol };
ni.v4_addrs = NULL; ni.v6_addrs = NULL;
if (endpoint->remote_addr.ss_family == AF_INET) {
const struct sockaddr_in* addr = (const struct sockaddr_in*)&endpoint->remote_addr;
memcpy(v4.addr, &addr->sin_addr, 4); v4.port = ntohs(addr->sin_port); ni.v4_addrs = &v4;
} else if (endpoint->remote_addr.ss_family == AF_INET6) {
const struct sockaddr_in6* addr = (const struct sockaddr_in6*)&endpoint->remote_addr;
memcpy(v6.addr, &addr->sin6_addr, 16); v6.port = ntohs(addr->sin6_port); ni.v6_addrs = &v6;
} else {
DEBUG_ERROR(DEBUG_CATEGORY_CONFIG, "client %s: invalid address family", client->name); goto fail;
}
struct TOPO_REALITY_SOCK reality = {0};
if (client->reality_enabled) {
memcpy(reality.short_id, client->reality.short_id, sizeof(reality.short_id));
memcpy(reality.server_pubkey, client->reality.server_static_pubkey, sizeof(reality.server_pubkey));
memcpy(reality.version, client->reality.version, sizeof(reality.version));
memcpy(reality.server_name, client->reality.server_name, sizeof(reality.server_name));
ni.reality_socks = &reality;
}
struct NODE_CONN_DIRECT* added = NULL;
if (node_conn_direct_open_node(instance, ni.node_id, NULL, NULL, &added, &ni, sock) == NCD_ERR) goto fail;
if (!handle) handle = added;
else node_conn_direct_close(added); /* один владелец конфигурации, сколько бы адресов ни было */
}
if (link_count == 0) DEBUG_WARN(DEBUG_CATEGORY_REALITY, "client %s reality: no links created", client->name);
if (!handle && node_conn_direct_open(instance, ni.node_id, NULL, NULL, &handle, NULL) == NCD_ERR) goto fail;
return handle;
fail:
DEBUG_ERROR(DEBUG_CATEGORY_CONNECTION, "cannot configure NCD client %s", client->name);
if (handle) node_conn_direct_close(handle);
return NULL;
}
static void config_handles_close(struct CONFIG_CONN_HANDLE* head) {
while (head) {
struct CONFIG_CONN_HANDLE* next = head->next;
node_conn_direct_close(head->handle); u_free(head); head = next;
}
}
int init_client_connections(struct UTUN_INSTANCE* instance, struct CFG_CLIENT* clients) {
if (!instance) { DEBUG_ERROR(DEBUG_CATEGORY_CONFIG, "configure clients without instance"); return -1; }
struct CONFIG_CONN_HANDLE* updated = NULL;
int count = 0;
for (struct CFG_CLIENT* client = clients; client; client = client->next) {
struct CONFIG_CONN_HANDLE* owner = u_calloc(1, sizeof(*owner));
if (!owner) { DEBUG_ERROR(DEBUG_CATEGORY_CONFIG, "client handle allocation failed"); goto fail; }
owner->handle = config_client_open(instance, client);
if (!owner->handle) { u_free(owner); goto fail; }
owner->node_id = node_conn_direct_node_id(owner->handle);
snprintf(owner->name, sizeof(owner->name), "%s", client->name);
owner->next = updated; updated = owner; count++;
}
struct CONFIG_CONN_HANDLE* old = instance->config_conn_handles;
instance->config_conn_handles = updated;
struct TOPO_GROUP* group = topo_groups_get_default(instance->topo_groups);
for (struct CONFIG_CONN_HANDLE* owner = old; owner; owner = owner->next) {
int retained = 0;
for (struct CONFIG_CONN_HANDLE* next = updated; next; next = next->next)
if (next->node_id == owner->node_id) { retained = 1; break; }
if (!retained && group && node_conn_direct_get_conn(owner->handle)) topo_group_leave_peer(group, owner->handle);
}
for (struct CONFIG_CONN_HANDLE* owner = updated; owner; owner = owner->next) {
node_conn_direct_set_callback(owner->handle, config_client_event, instance);
}
config_handles_close(old);
DEBUG_INFO(DEBUG_CATEGORY_CONNECTION, "configured %d NCD client handles", count);
return 0;
fail:
config_handles_close(updated);
DEBUG_ERROR(DEBUG_CATEGORY_CONFIG, "NCD client configuration rejected; previous handles retained");
return -1;
}
// Инициализация сети: listen-сокеты (если ещё нет) + client-соединения через NCD из конфига.
@@ -2843,99 +2901,7 @@ int init_connections(struct UTUN_INSTANCE* instance) {
etcp_keepalive_register(instance);
// Initialize clients via node_conn_direct
struct CFG_CLIENT* client = config->clients;
DEBUG_DEBUG(DEBUG_CATEGORY_ETCP, "init_connections: clients=%p total_conns=%d", config->clients, queue_entry_count(instance->connections));
while (client) {
if (strlen(client->peer_public_key_hex) == 0) {
DEBUG_WARN(DEBUG_CATEGORY_CONFIG, "no peer public key configured for client %s", client->name);
client = client->next; continue;
}
uint8_t pubkey_bin[SC_PUBKEY_SIZE];
if (sc_hex_to_binary(client->peer_public_key_hex, pubkey_bin, SC_PUBKEY_SIZE) != 0) {
DEBUG_ERROR(DEBUG_CATEGORY_ETCP, "invalid peer pubkey hex for client %s", client->name);
client = client->next; continue;
}
uint64_t node_id = sc_derive_node_id_from_pubkey(pubkey_bin);
DEBUG_INFO(DEBUG_CATEGORY_ETCP, "client %s node_id=0x%016llx", client->name, (unsigned long long)node_id);
if (client->reality_enabled) {
struct NODE_CONN_DIRECT* rhandle = etcp_config_client_reality(instance, client, pubkey_bin, node_id);
if (rhandle) {
struct CONFIG_CONN_HANDLE* ch = u_calloc(1, sizeof(*ch));
if (ch) { ch->node_id = node_id; strncpy(ch->name, client->name, MAX_CONN_NAME_LEN - 1);
ch->handle = rhandle; ch->next = instance->config_conn_handles;
instance->config_conn_handles = ch;
DEBUG_DEBUG(DEBUG_CATEGORY_ETCP, "client %s saved reality handle to config_conn_handles", client->name); }
}
client = client->next; continue;
}
struct NODE_CONN_DIRECT* handle = NULL;
struct ETCP_CONN* conn = NULL;
for (struct CFG_CLIENT_LINK* cl = client->links; cl; cl = cl->next) {
if (cl->local_srv && cl->local_srv->transport) {
DEBUG_WARN(DEBUG_CATEGORY_ETCP, "client %s TCP transport not yet supported via NCD, skipping", client->name);
continue;
}
struct ETCP_SOCKET* sock = NULL;
for (struct ETCP_SOCKET* s = instance->etcp_sockets; s; s = s->next)
if (cl->local_srv && strcmp(cl->local_srv->name, s->name) == 0) { sock = s; break; }
if (!sock) {
DEBUG_ERROR(DEBUG_CATEGORY_ETCP, "no socket for server '%s' client %s link",
cl->local_srv ? cl->local_srv->name : "?", client->name);
continue;
}
if (!handle) {
struct TOPO_ADDR4 v4_addr; struct TOPO_ADDR6 v6_addr;
struct TOPO_NODE ni_tmp; memset(&ni_tmp, 0, sizeof(ni_tmp));
ni_tmp.node_id = node_id; ni_tmp.node_name = client->name;
memcpy(ni_tmp.public_key, pubkey_bin, SC_PUBKEY_SIZE);
if (cl->remote_addr.ss_family == AF_INET) {
struct sockaddr_in* sin = (struct sockaddr_in*)&cl->remote_addr;
v4_addr = (struct TOPO_ADDR4){0}; memcpy(v4_addr.addr, &sin->sin_addr.s_addr, 4);
v4_addr.port = ntohs(sin->sin_port); v4_addr.type = TOPO_ADDR_INTERFACE; v4_addr.protocol = TOPO_PROTO_UDP;
ni_tmp.v4_addrs = &v4_addr;
} else if (cl->remote_addr.ss_family == AF_INET6) {
struct sockaddr_in6* sin6 = (struct sockaddr_in6*)&cl->remote_addr;
v6_addr = (struct TOPO_ADDR6){0}; memcpy(v6_addr.addr, &sin6->sin6_addr, 16);
v6_addr.port = ntohs(sin6->sin6_port); v6_addr.type = TOPO_ADDR_INTERFACE; v6_addr.protocol = TOPO_PROTO_UDP;
ni_tmp.v6_addrs = &v6_addr;
}
int r = node_conn_direct_open_node(instance, node_id, NULL, NULL, &handle, &ni_tmp, sock);
if (r == NCD_ERR || !handle) {
DEBUG_ERROR(DEBUG_CATEGORY_ETCP, "ncd open failed for client %s", client->name);
break;
}
conn = node_conn_direct_get_conn(handle);
DEBUG_DEBUG(DEBUG_CATEGORY_ETCP, "client %s ncd handle=%p conn=%p %s sock=%s",
client->name, handle, conn, r == NCD_NEW ? "NEW" : "REUSED", sock->name);
} else {
struct ETCP_LINK* link = etcp_link_new(conn, sock, &cl->remote_addr, 0);
DEBUG_INFO(DEBUG_CATEGORY_ETCP, "client %s added link link_id=%d sock=%s", client->name, link ? link->local_link_id : -1, sock->name);
}
}
if (handle) {
struct CONFIG_CONN_HANDLE* ch = u_calloc(1, sizeof(*ch));
if (ch) { ch->node_id = node_id; strncpy(ch->name, client->name, MAX_CONN_NAME_LEN - 1);
ch->handle = handle; ch->next = instance->config_conn_handles;
instance->config_conn_handles = ch;
DEBUG_DEBUG(DEBUG_CATEGORY_ETCP, "client %s saved handle to config_conn_handles", client->name); }
}
client = client->next;
}
// If there are clients configured but no connections created, that's an error
// If there are no clients (server-only mode), 0 connections is OK (server will accept incoming)
int total_conns = queue_entry_count(instance->connections);
if (total_conns == 0 && config->clients != NULL) {
DEBUG_ERROR(DEBUG_CATEGORY_ETCP, "Clients configured but no connections initialized");
return -1;
}
DEBUG_INFO(DEBUG_CATEGORY_CONNECTION, "Initialized %d connections", total_conns);
if (init_client_connections(instance, config->clients) < 0) return -1;
// Return 1 if there was a partial socket initialization error
if (socket_result == 1) return 1;
return 0;
+2 -4
View File
@@ -354,10 +354,8 @@ int init_sockets(struct UTUN_INSTANCE* instance);
// Создаёт listen-сокеты и client connections из конфига
int init_connections(struct UTUN_INSTANCE* instance);
// Создать TCP-линк(и) с REALITY-камуфляжем для [client] с reality=1.
// local_srv у link опционален (bind на интерфейс); возвращает NCD-handle или NULL.
struct NODE_CONN_DIRECT* etcp_config_client_reality(struct UTUN_INSTANCE* instance, struct CFG_CLIENT* client,
const uint8_t pubkey_bin[SC_PUBKEY_SIZE], uint64_t node_id);
// Заменяет принадлежащие конфигурации NCD handles. Новые приобретаются до освобождения старых.
int init_client_connections(struct UTUN_INSTANCE* instance, struct CFG_CLIENT* clients);
// SOCKET FUNCTIONS
// добавляет новый версер (сокет для приёма и отправки кодограмм. обслуживает много подключений)
+38 -6
View File
@@ -195,8 +195,19 @@ static int ncd_same_address(const struct sockaddr_storage* a, const struct socka
static int ncd_add_tcp_link(struct ETCP_CONN* conn, struct ETCP_SOCKET* sock,
struct sockaddr_storage* addr, const struct TOPO_REALITY_SOCK* reality) {
for (struct ETCP_LINK* link = conn->links; link; link = link->next)
if (link->is_tcp && !link->is_server && link->conn == sock && ncd_same_address(&link->remote_addr, addr)) return 0;
for (struct ETCP_LINK* link = conn->links; link; link = link->next) {
if (!link->is_tcp || link->is_server || link->conn != sock || !ncd_same_address(&link->remote_addr, addr)) continue;
struct reality_client_config before; memcpy(&before, &link->reality, sizeof(before));
int had_reality = link->reality_set;
if (reality) topo_node_apply_reality(link, reality);
else { memset(&link->reality, 0, sizeof(link->reality)); link->reality_set = 0; }
if (had_reality != link->reality_set || memcmp(&before, &link->reality, sizeof(before))) {
DEBUG_INFO(DEBUG_CATEGORY_NCD, "[ncd] TCP parameters updated node=%016llx link=%d up=%d",
(unsigned long long)conn->peer_node_id, link->local_link_id, link->link_status);
if (!link->link_status) etcp_tcp_link_start_reconnect(link);
}
return 0;
}
struct ETCP_LINK* link = etcp_link_new(conn, sock, addr, 0);
if (!link) { DEBUG_ERROR(DEBUG_CATEGORY_NCD, "[ncd] TCP link allocation failed"); return -1; }
topo_node_apply_reality(link, reality);
@@ -243,12 +254,23 @@ static int ncd_create_links(struct ncd_entry* entry, struct TOPO_NODE* ni,
uint8_t tnat = m ? m->nat_type : NAT_TYPE_UNKNOWN;
if (a->protocol & TOPO_PROTO_TCP) {
if ((a->protocol & TOPO_PROTO_REALITY) && !topo_node_find_reality_sock(ni, a->socket_id)) {
DEBUG_WARN(DEBUG_CATEGORY_NCD, "[ncd] missing REALITY metadata node=%016llx socket=%u",
(unsigned long long)nid, a->socket_id); continue;
}
int tcp_sockets = 0;
for (int vi = 0; vi < nviews; vi++) {
const struct sock_view* v = &views[vi];
if (!v->is_tcp || (specific_sock && v->sock != specific_sock) || !sock_match(v, a->type, tcfg, tnat, tip)) continue;
if (!v->is_tcp) continue;
tcp_sockets++;
if (specific_sock ? v->sock != specific_sock : !sock_match(v, a->type, tcfg, tnat, tip)) continue;
int added = ncd_add_tcp_link(conn, v->sock, &sa, topo_node_find_reality_sock(ni, a->socket_id));
if (added > 0) link_count += added;
}
if (!specific_sock && !tcp_sockets) {
int added = ncd_add_tcp_link(conn, NULL, &sa, topo_node_find_reality_sock(ni, a->socket_id));
if (added > 0) link_count += added;
}
}
if (a->protocol & TOPO_PROTO_UDP) {
if (specific_sock && !specific_sock->is_tcp && specific_sock->local_addr.ss_family == AF_INET) {
@@ -274,16 +296,26 @@ static int ncd_create_links(struct ncd_entry* entry, struct TOPO_NODE* ni,
memcpy(&sin6.sin6_addr, a->addr, 16); sin6.sin6_port = htons(a->port);
if (a->protocol & TOPO_PROTO_TCP) {
if ((a->protocol & TOPO_PROTO_REALITY) && !topo_node_find_reality_sock(ni, a->socket_id)) {
DEBUG_WARN(DEBUG_CATEGORY_NCD, "[ncd] missing REALITY metadata node=%016llx socket=%u",
(unsigned long long)nid, a->socket_id); continue;
}
int tcp_sockets = 0;
if (is_ll) { struct ETCP_SOCKET* sv = inst->etcp_sockets;
while (sv) { if (sv->local_addr.ss_family == AF_INET6 && sv->netif_index) { sin6.sin6_scope_id = sv->netif_index; break; } sv = sv->next; } }
struct sockaddr_storage sa; memcpy(&sa, &sin6, sizeof(sin6));
for (struct ETCP_SOCKET* s = inst->etcp_sockets; s; s = s->next) {
if (!s->is_tcp || (specific_sock && s != specific_sock) || s->local_addr.ss_family != AF_INET6 ||
s->type == CFG_SERVER_TYPE_PRIVATE) continue;
if (!s->is_tcp || s->local_addr.ss_family != AF_INET6) continue;
tcp_sockets++;
if (specific_sock ? s != specific_sock : s->type == CFG_SERVER_TYPE_PRIVATE) continue;
if (is_ll) { sin6.sin6_scope_id = s->netif_index; memcpy(&sa, &sin6, sizeof(sin6)); }
int added = ncd_add_tcp_link(conn, s, &sa, topo_node_find_reality_sock(ni, a->socket_id));
if (added > 0) link_count += added;
}
if (!specific_sock && !tcp_sockets && !is_ll) {
int added = ncd_add_tcp_link(conn, NULL, &sa, topo_node_find_reality_sock(ni, a->socket_id));
if (added > 0) link_count += added;
}
}
if (a->protocol & TOPO_PROTO_UDP) {
if (specific_sock && !specific_sock->is_tcp && specific_sock->local_addr.ss_family == AF_INET6) {
@@ -971,7 +1003,7 @@ int node_conn_direct_open_node(struct UTUN_INSTANCE* inst, uint64_t node_id,
}
struct TOPO_NODE* known = ncd_lookup_node(inst, node_id);
if (ni->timestamp && (!known || ni->timestamp > known->timestamp)) {
uint8_t wire[4096];
uint8_t wire[TOPO_NODE_WIRE_MAX_SIZE];
struct TOPO_GROUP_NODE nq = {0};
struct TOPO_GROUP group = { .instance = inst };
struct TOPO_NODE* copy = NULL;
+5 -59
View File
@@ -1009,64 +1009,9 @@ struct UTUN_INSTANCE *utun_instance_reload(struct UTUN_INSTANCE *instance, struc
}
os = next;
}
// Clients [client:] — close old config handles, rebuild from new config
struct CONFIG_CONN_HANDLE* ch = instance->config_conn_handles;
while (ch) { struct CONFIG_CONN_HANDLE* next = ch->next; node_conn_direct_close(ch->handle); u_free(ch); ch = next; }
instance->config_conn_handles = NULL;
for (struct CFG_CLIENT *nc = new_config->clients; nc; nc = nc->next) {
if (strlen(nc->peer_public_key_hex) == 0) { DEBUG_WARN(DEBUG_CATEGORY_CONFIG, "client %s no peer pubkey", nc->name); continue; }
uint8_t pubkey_bin[SC_PUBKEY_SIZE];
if (sc_hex_to_binary(nc->peer_public_key_hex, pubkey_bin, SC_PUBKEY_SIZE) != 0) { DEBUG_ERROR(DEBUG_CATEGORY_ETCP, "invalid pubkey hex client %s", nc->name); continue; }
uint64_t node_id = sc_derive_node_id_from_pubkey(pubkey_bin);
if (nc->reality_enabled) {
struct NODE_CONN_DIRECT* rhandle = etcp_config_client_reality(instance, nc, pubkey_bin, node_id);
if (rhandle) {
ch = u_calloc(1, sizeof(*ch));
if (ch) { ch->node_id = node_id; strncpy(ch->name, nc->name, MAX_CONN_NAME_LEN - 1);
ch->handle = rhandle; ch->next = instance->config_conn_handles; instance->config_conn_handles = ch; }
}
continue;
}
struct NODE_CONN_DIRECT* handle = NULL;
struct ETCP_CONN* conn = NULL;
for (struct CFG_CLIENT_LINK *nl = nc->links; nl; nl = nl->next) {
struct ETCP_SOCKET* sock = NULL;
for (struct ETCP_SOCKET* s = instance->etcp_sockets; s; s = s->next)
if (nl->local_srv && strcmp(nl->local_srv->name, s->name) == 0) { sock = s; break; }
if (!sock) { DEBUG_ERROR(DEBUG_CATEGORY_ETCP, "no socket for server '%s' client %s", nl->local_srv ? nl->local_srv->name : "?", nc->name); continue; }
if (!handle) {
struct TOPO_ADDR4 v4_addr; struct TOPO_ADDR6 v6_addr;
struct TOPO_NODE ni_tmp; memset(&ni_tmp, 0, sizeof(ni_tmp));
ni_tmp.node_id = node_id; ni_tmp.node_name = nc->name;
memcpy(ni_tmp.public_key, pubkey_bin, SC_PUBKEY_SIZE);
if (nl->remote_addr.ss_family == AF_INET) {
struct sockaddr_in* sin = (struct sockaddr_in*)&nl->remote_addr;
v4_addr = (struct TOPO_ADDR4){0}; memcpy(v4_addr.addr, &sin->sin_addr.s_addr, 4);
v4_addr.port = ntohs(sin->sin_port); v4_addr.type = TOPO_ADDR_INTERFACE; v4_addr.protocol = TOPO_PROTO_UDP;
ni_tmp.v4_addrs = &v4_addr;
} else if (nl->remote_addr.ss_family == AF_INET6) {
struct sockaddr_in6* sin6 = (struct sockaddr_in6*)&nl->remote_addr;
v6_addr = (struct TOPO_ADDR6){0}; memcpy(v6_addr.addr, &sin6->sin6_addr, 16);
v6_addr.port = ntohs(sin6->sin6_port); v6_addr.type = TOPO_ADDR_INTERFACE; v6_addr.protocol = TOPO_PROTO_UDP;
ni_tmp.v6_addrs = &v6_addr;
}
int rc = node_conn_direct_open_node(instance, node_id, NULL, NULL, &handle, &ni_tmp, sock);
if (rc == NCD_ERR || !handle) { DEBUG_ERROR(DEBUG_CATEGORY_ETCP, "ncd reload open failed client %s", nc->name); break; }
conn = node_conn_direct_get_conn(handle);
DEBUG_INFO(DEBUG_CATEGORY_CONNECTION, "reload: client %s ncd %s node=0x%016llx", nc->name, rc == NCD_NEW ? "NEW" : "REUSED", (unsigned long long)node_id);
} else {
etcp_link_new(conn, sock, &nl->remote_addr, 0);
}
}
if (handle) {
ch = u_calloc(1, sizeof(*ch));
if (ch) { ch->node_id = node_id; strncpy(ch->name, nc->name, MAX_CONN_NAME_LEN - 1);
ch->handle = handle; ch->next = instance->config_conn_handles; instance->config_conn_handles = ch; }
}
}
int client_result = init_client_connections(instance, new_config->clients);
if (client_result < 0)
DEBUG_ERROR(DEBUG_CATEGORY_CONFIG, "reload: failed to replace client handles; previous connections retained");
// Reload networks: clear and repopulate
if (instance->networks) {
struct ll_entry *entry;
@@ -1103,7 +1048,8 @@ struct UTUN_INSTANCE *utun_instance_reload(struct UTUN_INSTANCE *instance, struc
// routing_create(instance); // commented per request
if (instance->config) free_config(instance->config);
instance->config = new_config;
DEBUG_INFO(DEBUG_CATEGORY_CONFIG, "Selective partial reload completed (only changed updated, unchanged untouched, [debug] applied)");
if (client_result < 0) DEBUG_WARN(DEBUG_CATEGORY_CONFIG, "Reload completed with client errors; previous client handles retained");
else DEBUG_INFO(DEBUG_CATEGORY_CONFIG, "Selective partial reload completed (only changed updated, unchanged untouched, [debug] applied)");
return instance;
}