Authorize CHAT connection attempts and cancel revoked owners

This commit is contained in:
evgeny committed 2026-11-03 19:54:45 +01:00
1 parent f2c8993589
commit 83925d4e56
3 files changed
+71 -12

No files matched your search

+5 -2
View File
@@ -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-пути.
+61 -8
View File
@@ -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);
}
}
+5 -2
View File
@@ -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