From 83925d4e56604277cb333826408584e7e8d89e89 Mon Sep 17 00:00:00 2001 From: evgeny Date: Tue, 3 Nov 2026 19:54:45 +0100 Subject: [PATCH] Authorize CHAT connection attempts and cancel revoked owners --- src/routing_layer/conn_mgr.h | 7 +++- src/routing_layer/conn_mgr_core.c | 69 +++++++++++++++++++++++++++---- src/utun_instance.c | 7 +++- 3 files changed, 71 insertions(+), 12 deletions(-) diff --git a/src/routing_layer/conn_mgr.h b/src/routing_layer/conn_mgr.h index 89e3ff065..49f747ec2 100644 --- a/src/routing_layer/conn_mgr.h +++ b/src/routing_layer/conn_mgr.h @@ -32,7 +32,7 @@ struct TOPO_GROUP; enum conn_mgr_event { CONN_EVENT_UP = 0, /* связь есть. handle жив, можно отправлять данные */ CONN_EVENT_DOWN = 1, /* все пути потеряны; восстановление продолжается, пока жив handle */ - CONN_EVENT_TIMEOUT = 2, /* подключение не удалось. handle жив, закройте сами */ + CONN_EVENT_TIMEOUT = 2, /* подключение не удалось или допуск отозван; handle закрывает владелец */ }; /** @@ -53,6 +53,9 @@ struct CONN_MGR* conn_mgr_init(struct TOPO_GROUP* group); /* Остановить проверки и освободить менеджер вне его callbacks. * Живые клиентские handles отсоединяются, callbacks отменяются; владельцы закрывают их сами. */ void conn_mgr_destroy(struct CONN_MGR* mgr); +/* После commit членства CHAT: TIMEOUT запрещённым владельцам, отмена попыток и отсоединение handles. + * Владельцы закрывают handles сами; транспорт других групп сохраняется. UTUN не затрагивается. */ +void conn_mgr_revalidate_members(struct CONN_MGR* mgr); /* Приём управляющего пакета от etcp_router; забирает entry. */ void conn_mgr_router_recv_handler(struct ETCP_CONN* conn, struct ll_entry* entry); @@ -62,7 +65,7 @@ void conn_mgr_router_recv_handler(struct ETCP_CONN* conn, struct ll_entry* entry * Параллельные пробы: DIRECT сразу, REVERSE через 0/500мс, INDIRECT через 0/1000мс от общего старта. * Посредник используется сразу после проверки; поздний прямой путь заменяет его без повторного UP. * Возврат 0 — запрос принят, -1 — ошибка запуска. cb обязателен, cb_arg заимствован. - * Отсутствующая группа создаётся как CHAT с десятичным channel_id=group_id. + * Группа должна существовать; для CHAT требуется актуальное членство self/peer до запуска попыток. * При отсутствии узла загружает запись из общей SQLite во временный групповой узел; * отказ запуска очищает приобретённые ссылки. Последний CM-владелец убирает такой * узел, если у него не появились BGP-пути. diff --git a/src/routing_layer/conn_mgr_core.c b/src/routing_layer/conn_mgr_core.c index 62a80443c..1886d2f0b 100644 --- a/src/routing_layer/conn_mgr_core.c +++ b/src/routing_layer/conn_mgr_core.c @@ -39,6 +39,25 @@ static const char* cm_path_name(uint8_t type) { /* ═══════ утилиты ═══════ */ +/* Допуск не зависит от READY: CM сам строит групповой путь. UTUN сохраняет прежние правила. */ +static int cm_peer_allowed(struct CONN_MGR* mgr, uint64_t node) { + if (mgr && mgr->group && (mgr->group->group_type == TOPO_GROUP_TYPE_UTUN || + topo_group_chat_allowed(mgr->instance, mgr->group->group_id, node))) return 1; + DEBUG_WARN(DEBUG_CATEGORY_GENERAL, "conn_mgr: access denied group=%016llx peer=%016llx", + (unsigned long long)(mgr && mgr->group ? mgr->group->group_id : 0), (unsigned long long)node); + return 0; +} + +/* Сначала запретить UP/повторы, затем уведомить владельцев; callbacks вправе закрыть handles и соседние entries. */ +static void cm_revoke(struct CONN_MGR_ENTRY* entry) { + if (!entry || entry->state == CONN_MGR_STATE_DISCONNECTED) return; + DEBUG_INFO(DEBUG_CATEGORY_GENERAL, "conn_mgr: revoke group=%016llx peer=%016llx handles=%d", + (unsigned long long)entry->mgr->group->group_id, (unsigned long long)entry->node_id, entry->handles != NULL); + entry->state = CONN_MGR_STATE_DISCONNECTED; + cm_deliver_event(entry, CONN_EVENT_TIMEOUT); + cm_entry_cleanup(entry); +} + static struct CONN_MGR_HANDLE* cm_handle_new(struct CONN_MGR_ENTRY* entry, uint64_t group_id, conn_mgr_cb_t cb, void* cb_arg) { struct CONN_MGR_HANDLE* h = u_calloc(1, sizeof(*h)); @@ -61,6 +80,7 @@ static struct CONN_MGR_HANDLE* cm_handle_new(struct CONN_MGR_ENTRY* entry, uint6 static void cm_deliver_up_cb(void* arg) { struct CONN_MGR_HANDLE* h = (struct CONN_MGR_HANDLE*)arg; h->deliver_up_token = NULL; + if (!h->closed && h->entry && !cm_peer_allowed(h->entry->mgr, h->node_id)) { cm_revoke(h->entry); return; } if (!h->closed && h->entry && h->entry->state == CONN_MGR_STATE_CONNECTED && h->cb) h->cb(h, h->node_id, h->group_id, CONN_EVENT_UP, h->cb_arg); } @@ -78,6 +98,7 @@ void cm_deliver_event(struct CONN_MGR_ENTRY* entry, enum conn_mgr_event event) { uint64_t gid = entry->mgr->group->group_id; struct CONN_MGR_HANDLE* h = entry->handles; while (h) { + if (event == CONN_EVENT_UP && !cm_peer_allowed(entry->mgr, entry->node_id)) { cm_revoke(entry); return; } struct CONN_MGR_HANDLE* next = h->next; uint64_t node_id = h->node_id; conn_mgr_cb_t cb = h->cb; @@ -150,6 +171,7 @@ struct CONN_MGR_ENTRY* cm_find_entry(struct CONN_MGR* mgr, uint64_t node_id) { } struct CONN_MGR_ENTRY* cm_ensure_entry(struct CONN_MGR* mgr, uint64_t node_id) { + if (!cm_peer_allowed(mgr, node_id)) return NULL; struct CONN_MGR_ENTRY* e = cm_find_entry(mgr, node_id); if (e) return e; struct ll_entry* qe = queue_entry_new(sizeof(struct CONN_MGR_ENTRY) - sizeof(struct ll_entry)); @@ -235,6 +257,7 @@ void cm_cleanup_db_node(struct CONN_MGR_ENTRY* entry) { /* Прямой транспорт пригоден только вместе с READY-сессией этой группы. */ struct ETCP_CONN* cm_direct_conn(struct CONN_MGR* mgr, uint64_t node_id) { + if (!cm_peer_allowed(mgr, node_id)) return NULL; struct ETCP_CONN* conn = topo_group_find_conn_for_node(mgr->group, node_id); return conn && conn->peer_node_id == node_id && conn->links_up && !conn->close_requested && topo_group_peer_ready(mgr->group, node_id) ? conn : NULL; @@ -271,6 +294,7 @@ uint16_t cm_candidate_rtt(struct CONN_MGR* mgr, uint64_t node_id) { struct ETCP_CONN* conn_mgr_route_conn(struct CONN_MGR* mgr, uint64_t node_id, int allow_probe) { struct CONN_MGR_ENTRY* entry = cm_find_entry(mgr, node_id); if (!entry || entry->state == CONN_MGR_STATE_DISCONNECTED) return NULL; + if (!cm_peer_allowed(mgr, node_id)) return NULL; struct ETCP_CONN* direct = cm_direct_conn(mgr, node_id); if (direct) return direct; for (uint8_t i = 0; entry->indirect_confirmed && i < entry->intermediariy_count; i++) { @@ -301,6 +325,7 @@ uint8_t conn_mgr_connection_type(const struct ETCP_CONN* conn, uint64_t node_id) /* Смена пути не сбрасывает router-очереди и не повторяет UP при улучшении. */ void cm_refresh_path(struct CONN_MGR_ENTRY* entry) { if (entry->state == CONN_MGR_STATE_DISCONNECTED) return; + if (!cm_peer_allowed(entry->mgr, entry->node_id)) { cm_revoke(entry); return; } uint8_t type = CONN_TYPE_NONE; struct ETCP_CONN* direct = cm_direct_conn(entry->mgr, entry->node_id); if (direct) type = conn_mgr_connection_type(direct, entry->node_id); @@ -376,6 +401,7 @@ void conn_mgr_network_changed(struct CONN_MGR* mgr) { void cm_ncd_callback(struct NODE_CONN_DIRECT* ncd_h, enum ncd_event ev, void* arg) { struct CONN_MGR_ENTRY* entry = arg; if (!entry || entry->state == CONN_MGR_STATE_DISCONNECTED) return; + if (!cm_peer_allowed(entry->mgr, entry->node_id)) { cm_revoke(entry); return; } DEBUG_DEBUG(DEBUG_CATEGORY_GENERAL, "conn_mgr: NCD event=%d peer=%016llx path=%u elapsed=%llums", ev, (unsigned long long)entry->node_id, entry->conn_type, (unsigned long long)((get_time_tb() - entry->start_tb) / 10)); @@ -403,10 +429,12 @@ void cm_ncd_callback(struct NODE_CONN_DIRECT* ncd_h, enum ncd_event ev, void* ar } int cm_send_control(struct CONN_MGR* mgr, uint64_t node_id, const void* packet, size_t len) { + if (!cm_peer_allowed(mgr, node_id)) return -1; struct ll_entry* qe = ll_alloc_lldgram(1 + len); if (!qe) { DEBUG_ERROR(DEBUG_CATEGORY_GENERAL, "conn_mgr: control allocation failed peer=%016llx", (unsigned long long)node_id); return -1; } qe->dgram[0] = ETCP_RT_ID_CONN_MGR; memcpy(qe->dgram + 1, packet, len); qe->len = (uint16_t)(1 + len); - int ret = etcp_route_send(mgr->instance, mgr->group->group_id, node_id, qe, 1, 0); + int mode = mgr->group->group_type == TOPO_GROUP_TYPE_CHAT ? ROUTER_FLAG_SIGNED : 0; + int ret = etcp_route_send(mgr->instance, mgr->group->group_id, node_id, qe, 1, mode); if (ret < 0) DEBUG_WARN(DEBUG_CATEGORY_GENERAL, "conn_mgr: control send failed peer=%016llx ret=%d", (unsigned long long)node_id, ret); return ret; @@ -569,6 +597,7 @@ int cm_open(struct CONN_MGR* mgr, uint64_t node_id, if (cb) cb(NULL, node_id, 0, CONN_EVENT_TIMEOUT, cb_arg); return -1; } + if (!cm_peer_allowed(mgr, node_id)) return -1; uint64_t gid = group->group_id; struct TOPO_GROUP_NODE* target = topo_node_find_by_id(group, node_id); int loaded_from_db = 0; @@ -683,6 +712,7 @@ void cm_start_local_scan(struct CONN_MGR_ENTRY* entry) { } void cm_start_phase_direct(struct CONN_MGR_ENTRY* entry) { + if (!cm_peer_allowed(entry->mgr, entry->node_id)) { cm_revoke(entry); return; } if (!entry->ncd_handle && node_conn_direct_open(entry->mgr->instance, entry->node_id, cm_ncd_callback, entry, &entry->ncd_handle, NULL) == NCD_ERR) { DEBUG_ERROR(DEBUG_CATEGORY_GENERAL, "conn_mgr: NCD open failed peer=%016llx", (unsigned long long)entry->node_id); @@ -744,7 +774,7 @@ void cm_handle_direct_req(uint64_t src, struct CONN_MGR* mgr, const uint8_t* dat /* ═══════ disconnect / recv ═══════ */ void conn_mgr_router_recv_handler(struct ETCP_CONN* conn, struct ll_entry* entry) { - if (!entry || !entry->dgram || entry->len < ROUTER_SVC_PAYLOAD_OFF + 2) { + if (!entry || !entry->dgram || entry->len < ROUTER_SVC_PAYLOAD_OFF) { if (entry) { queue_dgram_free(entry); queue_entry_free(entry); } return; } @@ -753,9 +783,6 @@ void conn_mgr_router_recv_handler(struct ETCP_CONN* conn, struct ll_entry* entry queue_dgram_free(entry); queue_entry_free(entry); return; } - uint8_t* d = entry->dgram + ROUTER_SVC_PAYLOAD_OFF; size_t len = entry->len - ROUTER_SVC_PAYLOAD_OFF; - if (d[0] != ETCP_RT_ID_CONN_MGR) { queue_dgram_free(entry); queue_entry_free(entry); return; } - uint8_t sub = d[1]; /* conn_mgr — per-group: резолвим группу из заголовка доставки, а не instance->conn_mgr */ uint64_t group_id; memcpy(&group_id, entry->dgram + ROUTER_SVC_GROUP_OFF, 8); uint64_t src; memcpy(&src, entry->dgram + ROUTER_SVC_SRC_OFF, 8); @@ -766,6 +793,19 @@ void conn_mgr_router_recv_handler(struct ETCP_CONN* conn, struct ll_entry* entry (unsigned long long)group_id, (unsigned long long)src); queue_dgram_free(entry); queue_entry_free(entry); return; } + if (!cm_peer_allowed(mgr, src)) { + cm_revoke(cm_find_entry(mgr, src)); + queue_dgram_free(entry); queue_entry_free(entry); return; + } + size_t len = entry->len - ROUTER_SVC_PAYLOAD_OFF; + if (!len) { queue_dgram_free(entry); queue_entry_free(entry); return; } + uint8_t* d = entry->dgram + ROUTER_SVC_PAYLOAD_OFF; + if (len < 2 || d[0] != ETCP_RT_ID_CONN_MGR) { + DEBUG_WARN(DEBUG_CATEGORY_GENERAL, "conn_mgr: malformed command group=%016llx peer=%016llx bytes=%zu", + (unsigned long long)group_id, (unsigned long long)src, len); + queue_dgram_free(entry); queue_entry_free(entry); return; + } + uint8_t sub = d[1]; if (mgr) { struct CONN_MGR_ENTRY* e = cm_find_entry(mgr, src); if (e && e->state == CONN_MGR_STATE_CONNECTED) e->last_traffic_tb = get_time_tb(); } switch (sub) { case CM_SUBCMD_DIRECT_REQ: cm_handle_direct_req(src, mgr, d, len); break; @@ -791,12 +831,25 @@ int conn_mgr_open(struct UTUN_INSTANCE* inst, struct TOPO_GROUP* group = topo_groups_find(inst->topo_groups, group_id); if (!group) { - char ch[32]; snprintf(ch, sizeof(ch), "%llu", (unsigned long long)group_id); - group = topo_groups_create_group(inst->topo_groups, group_id, TOPO_GROUP_TYPE_CHAT, ch); - if (!group) return -1; + DEBUG_WARN(DEBUG_CATEGORY_GENERAL, "conn_mgr: open without active group=%016llx peer=%016llx", + (unsigned long long)group_id, (unsigned long long)node_id); return -1; } struct CONN_MGR* mgr = group->conn_mgr; if (!mgr) return -1; return cm_open(mgr, node_id, cb, cb_arg, out_handle); } + +/* Отзыв может вызвать callbacks и удалить следующий entry; каждый раз начинаем обход заново. */ +void conn_mgr_revalidate_members(struct CONN_MGR* mgr) { + if (!mgr || !mgr->group || mgr->group->group_type != TOPO_GROUP_TYPE_CHAT) return; + for (;;) { + struct CONN_MGR_ENTRY* denied = NULL; + for (struct ll_entry* e = mgr->entries->head; e; e = e->next) { + struct CONN_MGR_ENTRY* entry = (struct CONN_MGR_ENTRY*)e; + if (entry->state != CONN_MGR_STATE_DISCONNECTED && !cm_peer_allowed(mgr, entry->node_id)) { denied = entry; break; } + } + if (!denied) return; + cm_revoke(denied); + } +} diff --git a/src/utun_instance.c b/src/utun_instance.c index b5074e3b1..9612b9893 100644 --- a/src/utun_instance.c +++ b/src/utun_instance.c @@ -279,8 +279,11 @@ static int instance_init_common(struct UTUN_INSTANCE* instance, struct UASYNC* u } // Bind CONN_MGR handler AFTER etcp_router_init (init затирает router_bindings) - if (g_topo_group_enabled && instance->topo_groups) - etcp_router_bind(instance, ETCP_RT_ID_CONN_MGR, conn_mgr_router_recv_handler); + if (g_topo_group_enabled && instance->topo_groups && + (etcp_router_bind(instance, ETCP_RT_ID_CONN_MGR, conn_mgr_router_recv_handler) || + etcp_router_set_chat_policy(instance, ETCP_RT_ID_CONN_MGR, ROUTER_FLAG_SIGNED, topo_group_chat_allowed))) { + DEBUG_ERROR(DEBUG_CATEGORY_CONNECTION, "cannot initialize CONN_MGR access policy"); return -1; + } // TCP client/server имеют отдельные bindings; общими датаграммными контекстами владеет instance. #ifndef _WIN32