Route UTUN configuration and group ownership through NCD
This commit is contained in:
1 parent
9745a987ac
commit
bce17d9cfc
21 files changed
+592
-332
No files matched your search
@@ -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
@@ -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) {
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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 */
|
||||
|
||||
@@ -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; }
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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
|
||||
// добавляет новый версер (сокет для приёма и отправки кодограмм. обслуживает много подключений)
|
||||
|
||||
@@ -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
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
Reference in new issue
Block a user