schema: rm is_dm, rename name→userinfo (json_flat), fix join_msg format
- Remove is_dm column from channels table (dm detection by member count) - Rename member name column to userinfo — extensible json_flat format - Add json_flat.c/h library for compact JSON serialization - Fix join_sig message format: use ch_x25519+ch_ed25519 instead of channel_id string - Remove name from join_msg body (now in userinfo via json_flat) - Update chat_core, chat_sync, member_sync, db_manager to new format - Add topo_strategy.txt — архитектурные заметки по топологии
This commit is contained in:
1 parent
88d51349ea
commit
3dda701c67
26 files changed
+494
-325
No files matched your search
@@ -201,7 +201,6 @@ int conn_mgr_connect_node(struct CONN_MGR* mgr, uint64_t node_id, uint32_t idle_
|
||||
entry->state = CONN_MGR_STATE_CONNECTED;
|
||||
entry->conn_type = CONN_TYPE_DIRECT;
|
||||
entry->idle_timeout_ms = idle_timeout_ms;
|
||||
entry->alien = target->alien;
|
||||
entry->last_traffic_tb = get_time_tb();
|
||||
{ struct cm_cb_node* cn = u_calloc(1, sizeof(struct cm_cb_node));
|
||||
if (cn) { cn->cb = cb; cn->arg = cb_arg; entry->cb_list = cn; } }
|
||||
@@ -213,7 +212,6 @@ int conn_mgr_connect_node(struct CONN_MGR* mgr, uint64_t node_id, uint32_t idle_
|
||||
entry = cm_ensure_entry(mgr, node_id);
|
||||
if (!entry) return CONN_MGR_ERR_INTERNAL;
|
||||
entry->state = CONN_MGR_STATE_CONNECTING;
|
||||
entry->alien = target->alien;
|
||||
{ struct cm_cb_node* cn = u_calloc(1, sizeof(struct cm_cb_node));
|
||||
if (cn) { cn->cb = cb; cn->arg = cb_arg; cn->next = entry->cb_list; entry->cb_list = cn; } }
|
||||
entry->idle_timeout_ms = idle_timeout_ms;
|
||||
@@ -269,29 +267,6 @@ int conn_mgr_get_status(struct CONN_MGR* mgr, uint64_t node_id, uint8_t* out_sta
|
||||
return CONN_MGR_OK;
|
||||
}
|
||||
|
||||
int conn_mgr_add_alien_node(struct CONN_MGR* mgr, const uint8_t* nodeinfo_data, size_t len) {
|
||||
if (!mgr || !nodeinfo_data || len < sizeof(struct TOPO_NODE)) return CONN_MGR_ERR_INTERNAL;
|
||||
struct TOPO_GROUP* group = topo_groups_get_default(mgr->instance->topo_groups);
|
||||
if (!group) return CONN_MGR_ERR_INTERNAL;
|
||||
const struct TOPO_NODE* src = (const struct TOPO_NODE*)nodeinfo_data;
|
||||
uint64_t node_id = src->node_id;
|
||||
struct TOPO_NODEQ* existing = topo_node_find_by_id(group, node_id);
|
||||
if (existing) { DEBUG_INFO(DEBUG_CATEGORY_GENERAL, "conn_mgr: alien node 0x%016llx already exists, ignoring", (unsigned long long)node_id); return CONN_MGR_OK; }
|
||||
struct TOPO_NODE* ni = u_calloc(1, sizeof(struct TOPO_NODE));
|
||||
if (!ni) return CONN_MGR_ERR_INTERNAL;
|
||||
memcpy(ni, nodeinfo_data, len < sizeof(*ni) ? len : sizeof(*ni));
|
||||
ni->ref_count = 1;
|
||||
struct TOPO_NODEQ* nq = (struct TOPO_NODEQ*)queue_entry_new(sizeof(struct TOPO_NODEQ));
|
||||
if (!nq) { u_free(ni); return CONN_MGR_ERR_INTERNAL; }
|
||||
nq->node = ni; topo_node_ref(ni);
|
||||
nq->hash_node_id = ni->node_id;
|
||||
nq->alien = 1; nq->dirty = 0; nq->last_ver = ni->ver;
|
||||
nq->connectivity.ping_req_time = 0;
|
||||
if (group->nodes) queue_data_put_with_index(group->nodes, &nq->ll);
|
||||
DEBUG_INFO(DEBUG_CATEGORY_GENERAL, "conn_mgr: added alien node 0x%016llx", (unsigned long long)node_id);
|
||||
return CONN_MGR_OK;
|
||||
}
|
||||
|
||||
int conn_mgr_send(struct CONN_MGR* mgr, uint64_t node_id, struct ll_entry* entry) {
|
||||
if (!mgr || !entry) return -1;
|
||||
struct CONN_MGR_ENTRY* e = cm_find_entry(mgr, node_id);
|
||||
|
||||
+1
-20
@@ -49,9 +49,7 @@ struct TOPO_GROUP;
|
||||
* дополнительный callback в список
|
||||
* 4. conn_mgr_disconnect_node() отправляет DISCONNECT-пакет целевой ноде и
|
||||
* сбрасывает conn_mgr_type в NODEINFO_Q
|
||||
* 5. Для alien-нод (подключившихся к нам самостоятельно) используется
|
||||
* conn_mgr_add_alien_node() вместо conn_mgr_connect_node()
|
||||
* 6. conn_mgr_update_best_candidates() вызывается извне (обычно из route_connectivity)
|
||||
* 5. conn_mgr_update_best_candidates() вызывается извне (обычно из route_connectivity)
|
||||
* для обновления RTT кандидатов
|
||||
*
|
||||
* @note Внутренние таймеры (всё в timebase-единицах, 1 tb = 0.1 мс):
|
||||
@@ -266,7 +264,6 @@ struct CONN_MGR_ENTRY {
|
||||
uint64_t node_id; ///< Идентификатор целевой ноды
|
||||
uint8_t state; ///< Текущее состояние (enum CONN_MGR_STATE)
|
||||
uint8_t conn_type; ///< Тип соединения (CONN_TYPE_DIRECT/REVERSE/INDIRECT)
|
||||
uint8_t alien; ///< 1 — alien-нода (сама подключилась к нам), 0 — мы инициаторы
|
||||
uint32_t idle_timeout_ms; ///< Таймаут неактивности (мс), 0 — без таймаута
|
||||
uint64_t last_traffic_tb; ///< Время последнего трафика (tb) для idle-проверки
|
||||
void* idle_timer; ///< Хендл таймера idle-проверки
|
||||
@@ -355,7 +352,6 @@ void conn_mgr_destroy(struct CONN_MGR* mgr);
|
||||
* @note Если нода уже подключена — сразу вызывает callback с CONN_MGR_OK и возвращает CONN_MGR_OK.
|
||||
* @note Если нода в процессе подключения — добавляет callback в список ожидания.
|
||||
* @note Автоматически определяет наличие BGP-соединения (уже существующий ETCP-линк).
|
||||
* @note Для alien-нод используйте conn_mgr_add_alien_node().
|
||||
*/
|
||||
int conn_mgr_connect_node(struct CONN_MGR* mgr, uint64_t node_id,
|
||||
uint32_t idle_timeout_ms,
|
||||
@@ -397,21 +393,6 @@ int conn_mgr_set_idle_timeout(struct CONN_MGR* mgr, uint64_t node_id, uint32_t t
|
||||
int conn_mgr_get_status(struct CONN_MGR* mgr, uint64_t node_id,
|
||||
uint8_t* out_state, uint8_t* out_conn_type);
|
||||
|
||||
/* ==================== Alien-ноды ==================== */
|
||||
|
||||
/**
|
||||
* @brief Регистрирует alien-ноду (подключившуюся к нам самостоятельно).
|
||||
* @param mgr менеджер
|
||||
* @param nodeinfo_data сырые данные NODEINFO (BGP-пакет)
|
||||
* @param len длина данных
|
||||
* @return CONN_MGR_OK или CONN_MGR_ERR_INTERNAL
|
||||
*
|
||||
* Alien-нода — это нода, которая сама инициировала INIT-подключение к нам.
|
||||
* Для неё не запускаются фазы подключения, сразу устанавливается CONN_MGR_STATE_CONNECTED.
|
||||
* Создаёт entry с флагом alien=1.
|
||||
*/
|
||||
int conn_mgr_add_alien_node(struct CONN_MGR* mgr, const uint8_t* nodeinfo_data, size_t len);
|
||||
|
||||
/**
|
||||
* @brief Отправляет данные подключённой ноде через etcp_router.
|
||||
* @param mgr менеджер
|
||||
|
||||
+1
-10
@@ -40,9 +40,6 @@ entry->dgram = data; entry->len = len;
|
||||
conn_mgr_send(mgr, node_id, entry); // entry освобождается внутри
|
||||
```
|
||||
|
||||
### Alien-ноды
|
||||
Для нод, которые сами подключились к нам, используется `conn_mgr_add_alien_node()` — не запускает фазы подключения, сразу устанавливает `CONNECTED`.
|
||||
|
||||
### Кандидаты-посредники
|
||||
Внешний код (обычно `route_connectivity`) вызывает `conn_mgr_update_best_candidates()` для заполнения списка лучших посредников (топ-3 по RTT). Список используется фазой INDIRECT.
|
||||
|
||||
@@ -59,7 +56,7 @@ conn_mgr_send(mgr, node_id, entry); // entry освобождается внут
|
||||
| Структура | Описание |
|
||||
|-----------|----------|
|
||||
| `CONN_MGR` | Корневая структура менеджера. Хранит массив `entries[]`, топ-3 `best_candidates`, таймеры (`bg_ping_timer`, `candidate_ping_timer`), списки `reverse_pending`/`exchange_pending`, `next_request_id` |
|
||||
| `CONN_MGR_ENTRY` | Запись о подключении к одной ноде: `state`, `conn_type`, `alien`, `idle_timeout_ms`, список коллбэков `cb_list`, массив `intermediaries[]`, состояния `local_scan_state`/`main_connect_state`, текущая `main.phase` |
|
||||
| `CONN_MGR_ENTRY` | Запись о подключении к одной ноде: `state`, `conn_type`, `idle_timeout_ms`, список коллбэков `cb_list`, массив `intermediaries[]`, состояния `local_scan_state`/`main_connect_state`, текущая `main.phase` |
|
||||
| `CONN_MGR_CANDIDATE` | Пара `{node_id, rtt}` — запись о кандидате-посреднике (packed) |
|
||||
| `cm_cb_node` | Узел связного списка коллбэков для асинхронного результата подключения |
|
||||
| `cm_reverse_pending` | Запись об ожидании REVERSE-подключения (связный список) |
|
||||
@@ -82,12 +79,6 @@ conn_mgr_send(mgr, node_id, entry); // entry освобождается внут
|
||||
| `conn_mgr_set_idle_timeout(mgr, id, ms)` | Устанавливает/снимает idle-таймер для ноды. При неактивности дольше `ms` — автоотключение |
|
||||
| `conn_mgr_get_status(mgr, id, &state, &type)` | Возвращает текущее состояние и тип соединения |
|
||||
|
||||
### Alien-ноды
|
||||
|
||||
| Функция | Описание |
|
||||
|---------|----------|
|
||||
| `conn_mgr_add_alien_node(mgr, data, len)` | Регистрирует чужую ноду (сама подключилась к нам): копирует `TOPO_NODE`, добавляет в группу, помечает `alien=1` |
|
||||
|
||||
### Кандидаты-посредники
|
||||
|
||||
| Функция | Описание |
|
||||
|
||||
@@ -1,3 +1,15 @@
|
||||
/**
|
||||
* @file route_ping.h
|
||||
* @brief Удалённый пинг через BGP-посредника.
|
||||
*
|
||||
* A просит B пропинговать C и вернуть сводную статистику:
|
||||
* A --PING_REQ--> B --ETCP ping--> C
|
||||
* A <--PING_RESP-- B (sent/ok/avg_rtt)
|
||||
*
|
||||
* Промежуточный узел задаётся ЯВНО в to_conn — вызывающий сам выбирает,
|
||||
* через кого слать запрос. Автоподбор посредника (nat_check_find_third_node)
|
||||
* находится в nat_detection.c, не здесь.
|
||||
*/
|
||||
#ifndef ROUTE_PING_H
|
||||
#define ROUTE_PING_H
|
||||
|
||||
@@ -9,6 +21,11 @@ extern "C" {
|
||||
#include <stddef.h>
|
||||
#include "nat_detection.h"
|
||||
|
||||
/** Запросить у to_conn серию пингов на target_ip:target_port.
|
||||
* cb вызывается всегда ровно 1 раз:
|
||||
* success=1 если хоть один пинг успешен (PONG получен)
|
||||
* success=0 при общем таймауте или все пинги провалились
|
||||
* @param to_conn промежуточный узел — выбирается вызывающим явно */
|
||||
int route_ping_send_req_addr(struct NAT_DETECTION* nd, struct TOPO_GROUP* group,
|
||||
struct ETCP_CONN* to_conn,
|
||||
uint32_t target_ip, uint16_t target_port,
|
||||
@@ -16,11 +33,17 @@ int route_ping_send_req_addr(struct NAT_DETECTION* nd, struct TOPO_GROUP* group
|
||||
uint16_t wait_timeout_ms, route_ping_callback_t cb,
|
||||
void* arg, const uint8_t* pubkey);
|
||||
|
||||
/** Отменить все ожидающие ответы (при остановке NAT_DETECTION). */
|
||||
void route_ping_destroy_pending(struct NAT_DETECTION* nd);
|
||||
|
||||
/** Отменить pending для conn (при разрыве/реините соединения). */
|
||||
void route_ping_cancel_for_conn(struct NAT_DETECTION* nd, struct ETCP_CONN* conn);
|
||||
|
||||
/** Входящий PING_REQ — мы посредник: пингуем цель, шлём PING_RESP. */
|
||||
void route_ping_handle_req(struct NAT_DETECTION* nd, struct TOPO_GROUP* group,
|
||||
struct ETCP_CONN* from_conn, const uint8_t* data, size_t len);
|
||||
|
||||
/** Входящий PING_RESP — мы инициатор: находим pending по request_id, вызываем cb. */
|
||||
void route_ping_handle_resp(struct NAT_DETECTION* nd,
|
||||
struct ETCP_CONN* from_conn, const uint8_t* data, size_t len);
|
||||
|
||||
|
||||
+1
-3
@@ -356,9 +356,8 @@ void topo_group_new_conn(struct TOPO_GROUP* group, struct ETCP_CONN* conn) {
|
||||
|
||||
struct TOPO_NODEQ* peer_nq = topo_node_find_by_id(group, conn->peer_node_id);
|
||||
topo_group_add_to_senders(group, conn);
|
||||
if (peer_nq && peer_nq->alien) { DEBUG_INFO(DEBUG_CATEGORY_BGP, "peer 0x%016llx is alien, skipping route exchange", (unsigned long long)conn->peer_node_id); DEBUG_DEBUG(DEBUG_CATEGORY_DEBUG, "topo_group_new_conn: peer=%016llx group=%016llx type=%d ch=%s ALIEN=1 — SKIP", (unsigned long long)conn->peer_node_id, (unsigned long long)group->group_id, group->group_type, group->channel_id); return; }
|
||||
|
||||
DEBUG_DEBUG(DEBUG_CATEGORY_DEBUG, "topo_group_new_conn: peer=%016llx group=%016llx type=%d ch=%s alien=%d", (unsigned long long)conn->peer_node_id, (unsigned long long)group->group_id, group->group_type, group->channel_id, peer_nq ? peer_nq->alien : -1);
|
||||
DEBUG_DEBUG(DEBUG_CATEGORY_DEBUG, "topo_group_new_conn: peer=%016llx group=%016llx type=%d ch=%s", (unsigned long long)conn->peer_node_id, (unsigned long long)group->group_id, group->group_type, group->channel_id);
|
||||
|
||||
topo_group_send_table_request(group, conn);
|
||||
}
|
||||
@@ -561,7 +560,6 @@ int topo_group_process_nodeinfo(struct TOPO_GROUP* group, struct ETCP_CONN* from
|
||||
nodeinfo1->connectivity.nat_status = PROBE_RESULT_UNKNOWN;
|
||||
nodeinfo1->connectivity.real_status = PROBE_RESULT_UNKNOWN;
|
||||
nodeinfo1->connectivity.ping_req_time = 0;
|
||||
nodeinfo1->alien = 0;
|
||||
queue_data_put_with_index(group->nodes, &nodeinfo1->ll);
|
||||
}
|
||||
|
||||
|
||||
+92
-84
@@ -11,7 +11,8 @@ BGP-подобный обмен топологией узлов между пи
|
||||
Группа по умолчанию (UTUN) создаётся автоматически при `topo_groups_init()`. Дополнительные группы создаются через `topo_groups_create_group()` (для chatgui). Узлы разных групп изолированы: peer из utun-группы не видит узлы чат-группы и наоборот.
|
||||
|
||||
Помимо обмена топологией, модуль выполняет:
|
||||
- **NAT-детекцию** (NAT_INFO / NAT_CHECK_REQ): клиент запрашивает проверку своего NAT у сервера; сервер через третий узел пингует клиента и сообщает тип NAT (EIM/STRICT)
|
||||
- **Сборку local_node** (`topo_group_update_my_nodeinfo`) — при старте и при изменениях конфигурации сокетов собирает информацию о себе (адреса, подсети, ключи)
|
||||
- **NAT-детекцию** — выделена в отдельный самодостаточный модуль `nat_detection.h` (свой ETCP_ID 0x02, своя обработка пакетов); topo_group предоставляет делегат `topo_group_set_nat_check_local` для тестовых нужд
|
||||
- **Поиск оптимального маршрута** до узла (`topo_group_find_conn_for_node`) — по min hop_count среди live-путей (ETCP соединений)
|
||||
- **Зондирование связности** — для новых узлов запускает `route_connectivity_probe_node()`
|
||||
- **Персистентность в SQLite** — синхронизация узлов и membership в БД (таблицы nodes, node_addresses, peers_*)
|
||||
@@ -26,47 +27,49 @@ struct TOPO_GROUPS* g = topo_groups_init(instance); // создаёт конт
|
||||
Здесь же:
|
||||
- `etcp_bind(instance, ETCP_ID_TOPO_ENTRY, topo_group_receive_cbk)` — регистрирует приёмник пакетов топологии
|
||||
- `etcp_add_new_conn_cbk(instance, topo_group_etcp_conn_cbk, NULL)` — подписывается на события on_up/on_down всех новых ETCP-соединений
|
||||
- Открывается SQLite (если указан `db_path` в конфиге)
|
||||
- Открывается SQLite (если указан `db_path` в конфиге), БД сохраняется в `instance->topo_sqlite_db`
|
||||
- NAT-детекция инициализируется отдельно в `utun_instance.c` через `nat_detection_create(instance)`
|
||||
|
||||
### 2.2. Новое ETCP-соединение
|
||||
|
||||
При ETCP on_up автоматически вызывается `topo_group_new_conn(group, conn)`:
|
||||
1. Добавляет conn в `senders_list` (через `topo_group_add_to_senders`)
|
||||
2. Запускает NAT check для всех линков всех соединений
|
||||
3. Отправляет `TOPO_SUBCMD_REQUEST_TABLE` — запрос полной таблицы
|
||||
2. Отправляет `TOPO_SUBCMD_REQUEST_TABLE` — запрос полной таблицы
|
||||
|
||||
При приёме `TOPO_SUBCMD_REQUEST_TABLE` peer отправляет:
|
||||
- NODEINFO c локальным узлом (local_node)
|
||||
- Full table sync — все известные узлы, кроме тех, чей путь уже включает этого пира (защита от петель)
|
||||
- `TOPO_SUBCMD_TABLE_COMPLETE` — метка завершения начальной синхронизации
|
||||
- Full table sync — все известные узлы (с hop_count > 0), кроме тех, чей путь уже включает этого пира (защита от петель)
|
||||
- `TOPO_SUBCMD_TABLE_COMPLETE` — метка завершения начальной синхронизации; при получении вызывает `etcp_set_routing_exchange_state(from_conn, 3)`
|
||||
|
||||
### 2.3. NODEINFO (обновление/создание узла)
|
||||
|
||||
`topo_group_process_nodeinfo(group, from, data, len)`:
|
||||
1. Проверка соответствия типа группы по флагу `TOPO_FLAG_SEND_SUBNETS`
|
||||
1. Проверка соответствия типа группы по флагу `TOPO_FLAG_SEND_SUBNETS`; при несовпадении — отправка `TOPO_SUBCMD_ERR_GROUP_MISMATCH`
|
||||
2. Проверка `hop_count < MAX_HOPS(16)`
|
||||
3. Проверка версии узла — если версия не новее (stale), только обновляет пути (hop_list) и выходит
|
||||
4. Десериализация wire-формата → обновление/создание `TOPO_NODEQ`
|
||||
5. Добавление пути (`topo_group_add_path`) через текущее соединение
|
||||
6. Вставка в `routing` (`route_insert`) — только для UTUN-групп
|
||||
7. Сохранение в SQLite (если есть)
|
||||
8. Callback `node_updated_cb` (для chatgui member_sync)
|
||||
9. Broadcast всем пирам, кроме hop_list (защита от петель)
|
||||
10. Запуск зондирования связности для новых узлов с адресами
|
||||
4. Десериализация wire-формата → обновление/создание `TOPO_NODEQ` (через `topo_node_deserialize`)
|
||||
5. При создании нового узла: инициализация `connectivity` (PROBE_STATUS_NONE, PROBE_RESULT_UNKNOWN)
|
||||
6. Добавление пути (`topo_group_add_path`) через текущее соединение, с очисткой старых путей содержащих peer_node_id (`topo_group_remove_path_by_hop`)
|
||||
7. Вставка в `routing` (`route_insert`) — только для UTUN-групп
|
||||
8. Сохранение в SQLite (если есть): `topo_node_sqlite_node_put` + для CHAT групп — `topo_node_sqlite_member_put`
|
||||
9. Уведомление control_server (`control_server_notify_node_change`)
|
||||
10. Callback `node_updated_cb` (для chatgui member_sync) — вызывается и при создании, и при обновлении
|
||||
11. Broadcast всем пирам, кроме hop_list (защита от петель)
|
||||
12. Запуск зондирования связности для новых узлов с адресами (`route_connectivity_probe_node`)
|
||||
|
||||
### 2.4. WITHDRAW (удаление узла)
|
||||
|
||||
`topo_group_process_withdraw(group, sender, data, len)`:
|
||||
1. Поиск узла по `node_id`
|
||||
2. `topo_group_remove_path_by_hop(nq, wd_source)` — удаление всех путей, содержащих wd_source в hop_list
|
||||
3. Если путей не осталось (ret == 1): удаление из routing, уведомление control_server, broadcast withdraw дальше
|
||||
4. Если группа CHAT — удаление member из SQLite
|
||||
3. Если путей не осталось (ret == 1): удаление из routing, уведомление control_server, установка `dirty = 1`, отмена connectivity probe (`route_connectivity_cancel_node`), освобождение paths, `topo_node_free_lists`, закрытие виртуальных каналов (`etcp_router_conn_close_all_for_node`), broadcast withdraw дальше
|
||||
4. Если группа CHAT — удаление member из SQLite через `topo_node_sqlite_member_del`
|
||||
|
||||
### 2.5. Отключение соединения
|
||||
|
||||
При ETCP on_down вызывается `topo_group_remove_conn(group, conn)`:
|
||||
1. Отмена pending ping-запросов для этого conn
|
||||
2. Для всех узлов с путями через этот conn — `topo_group_remove_path`. Если узел стал полностью недоступен (нет путей) — удаляется из routing и nodes, с broadcast withdraw
|
||||
1. Проверка наличия conn в senders_list (если уже удалён — выход)
|
||||
2. Для всех узлов с путями через этот conn — `topo_group_remove_path`. Если узел стал полностью недоступен (нет путей) — удаляется из routing и nodes, устанавливается `dirty = 1`, отменяется connectivity probe, закрываются виртуальные каналы, broadcast withdraw
|
||||
3. Удаление conn из `senders_list`
|
||||
|
||||
### 2.6. Поиск пути до узла
|
||||
@@ -74,17 +77,19 @@ struct TOPO_GROUPS* g = topo_groups_init(instance); // создаёт конт
|
||||
`topo_group_find_conn_for_node(group, node_id)` — перебирает `path`'ы узла, выбирает с минимальным `hop_count`. Предпочитает live-пути (links_up > 0). Если live-путей нет, fallback на любой путь с минимальным hop_count.
|
||||
|
||||
Интенсивно используется `etcp_router`:
|
||||
- При маршрутизации пакетов к удалённому узлу (`src/etcp_router.c:262`)
|
||||
- При маршрутизации пакетов к удалённому узлу
|
||||
- При поиске next-hop для multihop соединений
|
||||
|
||||
### 2.7. NAT-детекция
|
||||
|
||||
1. При новом соединении вызывается `topo_group_start_link_nat_check` для каждого линка
|
||||
2. Сервер ищет «третий узел» через `topo_group_find_third_node` — произвольный conn кроме проверяемого
|
||||
3. Отправляет `PING_REQ` через третий узел на IP:port линка проверяемого клиента
|
||||
4. По результату пинга: `NAT_TYPE_EIM` (если успех) или `NAT_TYPE_STRICT`
|
||||
5. Результат сохраняется в `link->nat_type` и отправляется клиенту через `topo_group_send_nat_info`
|
||||
6. Клиент обновляет свой `local_node` через `topo_group_handle_nat_info` и broadcast-ит изменения
|
||||
NAT-детекция выделена в отдельный самодостаточный модуль `nat_detection.h`:
|
||||
- Свой ETCP ID: `ETCP_ID_NAT_DETECTION` (0x02)
|
||||
- Сам подписывается на conn_up/conn_down, сам принимает свои пакеты
|
||||
- При поднятии линка (INIT handshake) вызывается `nat_detection_link_ready()`
|
||||
- Ищет третий узел напрямую в `instance->connections`, обновляет `ETCP_SOCKET` и broadcast-ит NODEINFO через `topo_group_update_my_nodeinfo()`
|
||||
|
||||
Из topo_group в NAT-детекцию остался только делегат:
|
||||
- `topo_group_set_nat_check_local(group, allow)` → вызывает `nat_detection_set_allow_local(instance->nat_det, allow)`
|
||||
|
||||
### 2.8. Широковещательная рассылка
|
||||
|
||||
@@ -98,26 +103,25 @@ struct TOPO_GROUPS* g = topo_groups_init(instance); // создаёт конт
|
||||
- CHAT-группа ожидает флаг = 0 (без подсетей)
|
||||
- При несовпадении отправляется `TOPO_SUBCMD_ERR_GROUP_MISMATCH` и пакет игнорируется
|
||||
|
||||
### 2.10. Alien-узлы
|
||||
|
||||
Узлы, помеченные `alien=1`, не участвуют в обмене маршрутами: при `topo_group_new_conn` для alien-пира обмен пропускается.
|
||||
|
||||
## 3. API
|
||||
|
||||
### 3.1. Структуры
|
||||
|
||||
| Структура | Назначение |
|
||||
|---|---|
|
||||
| `TOPO_GROUPS` | Контейнер всех групп топологии экземпляра. Хранит `group_list` (ll_queue), memory_pool'ы для сериализации, sqlite3, callback `node_updated_cb` |
|
||||
| `TOPO_GROUP` | Одна группа топологии. `group_id`, `group_type`, `nodes` (ll_queue узлов), `senders_list` (ll_queue conn'ов), `local_node` (TOPO_NODEQ) |
|
||||
| `TOPO_GROUPS` | Контейнер всех групп топологии экземпляра. Хранит `group_list` (ll_queue), memory_pool'ы для сериализации (v4_sock_meta, v4_addr, v6_sock_meta, v6_addr, v4_subnet, v6_subnet), callback `node_updated_cb` |
|
||||
| `TOPO_GROUP` | Одна группа топологии. `group_id`, `group_type`, `nodes` (ll_queue узлов), `senders_list` (ll_queue conn'ов), `local_node` (TOPO_NODEQ), `ed25519_public_key` (производный из X25519), `channel_id[64]` (для CHAT-групп) |
|
||||
| `TOPO_GROUP_CONN_ITEM` | Элемент списка senders: conn + ll_entry — связка с senders_list |
|
||||
| `TOPO_NODE` | Идентичность узла: pubkeys, имя, версия (ver), списки адресов (v4/v6 sock_meta, addrs). Разделяется между группами через ref_count |
|
||||
| `TOPO_NODEQ` | Запись в очереди группы: указатель на TOPO_NODE, пути (paths), hop_list, tranzit_data, subnets, connectivity, conn_mgr (тип + intermediaries), best_socket, dirty, last_ver |
|
||||
| `TOPO_NODEPATH` | Путь до узла через конкретное ETCP_CONN: conn + hop_count |
|
||||
| `TOPO_NODESUBNETS` | Подсети узла: списки TOPO_SUBNET4 / TOPO_SUBNET6 |
|
||||
| `TOPO_CONNECTIVITY` | Состояние связности с узлом: probe_status, pending_count, rtt-метрики, статусы (interface/nat/real), probe_list |
|
||||
| `TOPOMSG_NODEINFO_PKT` | Wire-формат пакета NODEINFO: cmd + subcmd + TOPOMSG_NODE + переменная часть |
|
||||
| `TOPOMSG_WITHDRAW_PKT` | Wire-формат WITHDRAW: node_id (удаляемый) + wd_source (инициатор) |
|
||||
| `TOPOMSG_NAT_INFO` | Wire-формат NAT_INFO: socket_id, IP, port (network byte order), nat_type |
|
||||
| `TOPOMSG_NAT_CHECK_REQ` | Запрос проверки NAT от клиента к серверу: socket_id + interface IP:port |
|
||||
| `TOPOMSG_TABLE_REQ` | Wire-формат запроса/завершения таблицы: cmd + subcmd |
|
||||
| `TOPOMSG_ERR_GROUP_MISMATCH` | Wire-формат ошибки несоответствия типа группы: expected_type + received_flags |
|
||||
| `nat_check_arg` | Контекст callback NAT-проверки: link + nat_ip + nat_port |
|
||||
| `NAT_DETECTION` | Контекст NAT-детекции (отдельный модуль): ping_pending, next_ping_req_id, allow_nat_check_local |
|
||||
|
||||
### 3.2. Константы
|
||||
|
||||
@@ -127,22 +131,29 @@ struct TOPO_GROUPS* g = topo_groups_init(instance); // создаёт конт
|
||||
| `TOPO_SUBCMD_NODEINFO` | 0x04 | Полная информация об узле + подсети |
|
||||
| `TOPO_SUBCMD_REQUEST_TABLE` | 0x05 | Запрос полной таблицы |
|
||||
| `TOPO_SUBCMD_WITHDRAW` | 0x06 | Узел стал недоступен |
|
||||
| `TOPO_SUBCMD_NAT_INFO` | 0x09 | Информация о типе NAT клиента |
|
||||
| `TOPO_SUBCMD_NAT_CHECK_REQ` | 0x0A | Запрос проверки NAT для сокета |
|
||||
| `TOPO_SUBCMD_TABLE_COMPLETE` | 0x0B | Завершение начальной синхронизации |
|
||||
| `TOPO_SUBCMD_ERR_GROUP_MISMATCH` | 0x0C | Ошибка несоответствия типа группы |
|
||||
| `MAX_HOPS` | 16 | Максимальная длина hop-листа |
|
||||
| `BGP_NODES_HASH_SIZE` | 256 | Размер хеш-таблицы nodes |
|
||||
| `TOPO_GROUP_UTUN` | 0x8000000000000000ULL | group_id по умолчанию для utun-группы |
|
||||
| `TOPO_GROUP_TYPE_UTUN` | 1 | Тип группы: VPN-сеть (подсети, маршруты) |
|
||||
| `TOPO_GROUP_TYPE_CHAT` | 2 | Тип группы: чат (без подсетей) |
|
||||
| `TOPO_FLAG_SEND_SUBNETS` | 0x01 | Флаг NODEINFO: узел передаёт подсети |
|
||||
| `PROBE_STATUS_NONE` | 0 | Связность: не проверялась |
|
||||
| `PROBE_STATUS_IN_PROGRESS` | 1 | Связность: проверка идёт |
|
||||
| `PROBE_STATUS_DONE` | 2 | Связность: проверка завершена |
|
||||
| `PROBE_RESULT_UNKNOWN` | 0 | Результат проверки: неизвестно |
|
||||
| `PROBE_RESULT_REACHABLE` | 1 | Результат проверки: доступен |
|
||||
| `PROBE_RESULT_UNREACHABLE` | 2 | Результат проверки: недоступен |
|
||||
|
||||
### 3.3. Функции инициализации/завершения
|
||||
|
||||
| Функция | Назначение |
|
||||
|---|---|
|
||||
| `topo_groups_init(instance)` | Создаёт TOPO_GROUPS, memory_pool'ы, открывает SQLite, создаёт utun-группу по умолчанию, регистрирует ETCP-коллбэки |
|
||||
| `topo_groups_destroy(instance)` | Освобождает все группы, memory_pool'ы, закрывает SQLite; снимает ETCP-регистрацию |
|
||||
| `topo_group_create(instance, group_id, group_type)` | Создаёт TOPO_GROUP: nodes (hash queue), senders_list, local_node; вызывается из topo_groups_init / topo_groups_create_group |
|
||||
| `topo_group_destroy(group)` | Освобождает senders_list, local_node, nodes очереди; узлы должны быть удалены заранее |
|
||||
| `topo_groups_init(instance)` | Создаёт TOPO_GROUPS, memory_pool'ы, открывает SQLite (если db_path в конфиге), создаёт utun-группу по умолчанию, регистрирует ETCP-коллбэки |
|
||||
| `topo_groups_destroy(instance)` | Освобождает все группы, memory_pool'ы, закрывает SQLite, отменяет connectivity probes, снимает ETCP-регистрацию |
|
||||
| `topo_group_create(instance, group_id, group_type)` | Создаёт TOPO_GROUP: nodes (hash queue), senders_list, local_node (через update_my_nodeinfo), ed25519_pubkey; вызывается из topo_groups_init / topo_groups_create_group |
|
||||
| `topo_group_destroy(group)` | Освобождает senders_list, local_node (free_lists), nodes очереди |
|
||||
| `topo_groups_create_group(g, group_id, group_type, channel_id)` | Создаёт новую группу и добавляет в group_list; для CHAT устанавливает channel_id |
|
||||
| `topo_groups_find(g, group_id)` | Поиск группы по group_id через хеш-индекс group_list |
|
||||
| `topo_groups_get_default(g)` | Возвращает utun-группу по умолчанию (group_id=TOPO_GROUP_UTUN) |
|
||||
@@ -151,8 +162,8 @@ struct TOPO_GROUPS* g = topo_groups_init(instance); // создаёт конт
|
||||
|
||||
| Функция | Назначение |
|
||||
|---|---|
|
||||
| `topo_group_new_conn(group, conn)` | Добавляет conn в senders_list, запускает NAT check для всех линков, отправляет REQUEST_TABLE; alien-пиры пропускаются |
|
||||
| `topo_group_remove_conn(group, conn)` | Отменяет NAT check и ping pending, удаляет все пути через conn, удаляет недоступные узлы, broadcast withdraw, убирает conn из senders_list |
|
||||
| `topo_group_new_conn(group, conn)` | Добавляет conn в senders_list, отправляет REQUEST_TABLE |
|
||||
| `topo_group_remove_conn(group, conn)` | Удаляет все пути через conn, удаляет недоступные узлы (route_delete, cancel_connectivity, etcp_router_conn_close_all_for_node, broadcast withdraw), убирает conn из senders_list |
|
||||
| `topo_group_etcp_conn_cbk(conn, arg)` | Callback при создании нового ETCP-соединения: подписывается на on_up/on_down через etcp_conn_add_up_cbk/down_cbk |
|
||||
| `topo_group_on_conn_up(conn, arg)` | При on_up: вызывает topo_group_new_conn |
|
||||
| `topo_group_on_conn_down(conn, arg)` | При on_down: вызывает topo_group_remove_conn |
|
||||
@@ -161,41 +172,41 @@ struct TOPO_GROUPS* g = topo_groups_init(instance); // создаёт конт
|
||||
|
||||
| Функция | Назначение |
|
||||
|---|---|
|
||||
| `topo_group_receive_cbk(from_conn, entry)` | Точка входа приёма пакетов топологии: диспетчеризация по subcmd (NODEINFO/WITHDRAW/REQUEST_TABLE/PING_REQ/PING_RESP/NAT_INFO/NAT_CHECK_REQ/TABLE_COMPLETE/ERR_GROUP_MISMATCH) |
|
||||
| `topo_group_process_nodeinfo(group, from, data, len)` | Обработка NODEINFO: проверка группы/версии/hops, десериализация, обновление/создание TOPO_NODEQ, пути, routing, SQLite, broadcast, зондирование связности |
|
||||
| `topo_group_process_withdraw(group, sender, data, len)` | Обработка WITHDRAW: удаление путей с wd_source, если узел недоступен — удаление из routing, broadcast |
|
||||
| `topo_group_handle_request_table(group, conn)` | Обработка REQUEST_TABLE: отправляет NODEINFO (local_node) + full table sync + TABLE_COMPLETE |
|
||||
| `topo_group_handle_nat_info(group, from_conn, data, len)` | Обработка NAT_INFO: обновляет nat_type в local_node (TOPO_SOCKMETA4), ETCP_SOCKET, при изменениях broadcast NODEINFO |
|
||||
| `topo_group_handle_nat_check_req(group, from_conn, data, len)` | Обработка NAT_CHECK_REQ от клиента: находит линк и запускает NAT-проверку |
|
||||
| `topo_group_receive_cbk(from_conn, entry)` | Точка входа приёма пакетов топологии: работает только с default-группой, диспетчеризация по subcmd (NODEINFO/WITHDRAW/REQUEST_TABLE/TABLE_COMPLETE/ERR_GROUP_MISMATCH) |
|
||||
| `topo_group_process_nodeinfo(group, from, data, len)` | Обработка NODEINFO: проверка группы/версии/hops, десериализация, обновление/создание TOPO_NODEQ, пути, routing, SQLite, broadcast, зондирование связности, node_updated_cb |
|
||||
| `topo_group_process_withdraw(group, sender, data, len)` | Обработка WITHDRAW: удаление путей с wd_source, если узел недоступен — полная очистка (routing, connectivity, paths, etcp_router), broadcast |
|
||||
| `topo_group_handle_request_table(group, conn)` | Обработка REQUEST_TABLE: отправляет NODEINFO (local_node) + full table sync + add_to_senders + TABLE_COMPLETE |
|
||||
|
||||
### 3.6. Функции поиска
|
||||
|
||||
| Функция | Назначение |
|
||||
|---|---|
|
||||
| `topo_group_find_conn_for_node(group, node_id)` | Поиск оптимального ETCP_CONN до узла: min hop_count, предпочтение live-путям |
|
||||
| `topo_group_find_third_node(group, exclude)` | Поиск произвольного conn'а (не exclude) для использования как посредника при NAT-детекции; исключает private/local conn'ы |
|
||||
| `topo_node_find_by_id(group, node_id)` | Поиск TOPO_NODEQ в nodes по node_id через хеш-индекс |
|
||||
| `topo_groups_find(g, group_id)` | Поиск группы по group_id |
|
||||
|
||||
### 3.7. Функции broadcast
|
||||
|
||||
| Функция | Назначение |
|
||||
|---|---|
|
||||
| `topo_group_send_nodeinfo(group, node, conn)` | Сериализует TOPO_NODEQ и отправляет NODEINFO одному conn |
|
||||
| `topo_group_send_withdraw(group, node_id)` | Broadcast WITHDRAW всем пирам (without exclude) |
|
||||
| `topo_group_send_withdraw(group, node_id)` | Broadcast WITHDRAW всем пирам (без exclude) |
|
||||
| `topo_group_broadcast_withdraw(group, node_id, wd_source, exclude)` | Рассылает WITHDRAW всем conn в senders_list, кроме exclude |
|
||||
| `topo_group_send_full_table(group, conn)` | Отправляет все известные узлы (кроме тех, чей путь включает target) указанному conn |
|
||||
| `topo_group_send_full_table(group, conn)` | Отправляет все известные узлы (с hop_count > 0), кроме тех, чей путь включает target, указанному conn |
|
||||
| `topo_group_send_table_request(group, conn)` | Отправляет запрос полной таблицы (TOPO_SUBCMD_REQUEST_TABLE) |
|
||||
| `topo_group_send_table_complete(group, conn)` | Отправляет метку завершения синхронизации (TOPO_SUBCMD_TABLE_COMPLETE) |
|
||||
| `topo_group_send_err_group_mismatch(group, conn, expected_type, received_flags)` | Отправляет ошибку несоответствия типа группы |
|
||||
|
||||
### 3.8. Функции NAT
|
||||
### 3.8. Функции NAT (делегаты в nat_detection)
|
||||
|
||||
| Функция | Назначение |
|
||||
|---|---|
|
||||
| `topo_group_send_nat_info(conn, socket_id, nat_ip, nat_port, nat_type)` | Отправляет клиенту информацию о его NAT (результат проверки) |
|
||||
| `topo_group_send_nat_check_req(conn, socket_id)` | Отправляет серверу запрос на проверку NAT для указанного socket_id |
|
||||
| `topo_group_start_link_nat_check(group, link)` | Запускает NAT-проверку для одного линка через третий узел; проверяет is_local_subnet, пропускает private/local линки |
|
||||
| `topo_group_request_nat_check_all(group)` | Запускает NAT check для всех линков всех соединений |
|
||||
| `topo_group_set_nat_check_local(group, allow)` | Разрешить/запретить NAT check для локальных подсетей (127.x, 10.x, 192.168.x, 172.16-31.x) |
|
||||
| `topo_group_set_nat_check_local(group, allow)` | Делегат в `nat_detection_set_allow_local(instance->nat_det, allow)` — разрешить/запретить NAT check для локальных подсетей (тесты) |
|
||||
| `nat_detection_create(inst)` | Создаёт модуль NAT-детекции: etcp_bind на 0x02, подписка на conn_up/conn_down |
|
||||
| `nat_detection_destroy(nd)` | Уничтожает модуль NAT-детекции |
|
||||
| `nat_detection_link_ready(nd, link)` | Запускает NAT-чек для линка после INIT handshake |
|
||||
| `nat_detection_cancel_for_conn(nd, conn)` | Отменяет все pending NAT-пинги для соединения |
|
||||
| `nat_detection_request_check_all(nd)` | Принудительная перепроверка NAT для всех линков |
|
||||
|
||||
### 3.9. Функции путей
|
||||
|
||||
@@ -205,15 +216,14 @@ struct TOPO_GROUPS* g = topo_groups_init(instance); // создаёт конт
|
||||
| `topo_group_remove_path(nq, conn)` | Удаляет путь через указанный conn; возвращает 1 если путей не осталось (unreachable) |
|
||||
| `topo_group_remove_path_by_hop(nq, wd_source)` | Удаляет все пути, содержащие wd_source в hop_list (используется в WITHDRAW) |
|
||||
| `topo_group_add_to_senders(group, conn)` | Добавляет conn в senders_list (с проверкой дубликатов) |
|
||||
| `topo_group_should_send_to(nq, target_id)` | Проверяет, нужно ли отправлять NODEINFO узла указанному пиру (target_id не в hop_list) |
|
||||
| `topo_group_should_send_to(nq, target_id)` | Проверяет, нужно ли отправлять NODEINFO узла указанному пиру (target_id не во всех путях) |
|
||||
|
||||
### 3.10. Callbacks/вспомогательные
|
||||
|
||||
| Функция | Назначение |
|
||||
|---|---|
|
||||
| `topo_groups_set_node_updated_cb(groups, fn)` | Устанавливает callback при обновлении узла (используется chatgui для member_sync) |
|
||||
| `topo_groups_set_sqlite_db(g, db)` | Устанавливает внешнюю БД SQLite |
|
||||
| `nat_link_check_cb(success, avg_rtt, count_sent, count_ok, arg)` | Callback завершения NAT-проверки: устанавливает nat_type линка и отправляет результат клиенту |
|
||||
| `topo_groups_set_node_updated_cb(groups, fn)` | Устанавливает callback при создании/обновлении узла (используется chatgui для member_sync) |
|
||||
| `topo_group_update_my_nodeinfo(instance, group)` | Собирает информацию о локальном узле из конфига и ключей: адреса сокетов, подсети, pubkeys, имя; обновляет group->local_node; вызывается при старте и при изменениях сокетов |
|
||||
| `nodeinfo_dump_log(data, len)` | Логирует краткую сводку NODEINFO (node_id, ver, счётчики, hops) |
|
||||
| `nodeinfo_format(data, len)` | Форматирует полный NODEINFO с именем, подсетями в читаемую строку (аллокация через u_malloc) |
|
||||
| `group_subcmd_name(subcmd)` | Возвращает строковое имя subcmd для логов |
|
||||
@@ -230,36 +240,39 @@ struct TOPO_GROUPS* g = topo_groups_init(instance); // создаёт конт
|
||||
|
||||
| Модуль | Как используется |
|
||||
|---|---|
|
||||
| `topo_node.c/h` | Модель данных узла: TOPO_NODE, TOPO_NODEQ, ser/deserialize, find_by_id, update_my_nodeinfo, free_lists |
|
||||
| `topo_node.c/h` | Модель данных узла: TOPO_NODE, TOPO_NODEQ, ser/deserialize, find_by_id, update_my_nodeinfo, free_lists, dump_all, format_all |
|
||||
| `topo_node_sqlite.h` | Персистентность в SQLite: topo_node_sqlite_node_put, member_put, member_del, init |
|
||||
| `nat_detection.h` | NAT-детекция: отдельный модуль с собственным ETCP_ID 0x02; делегат set_nat_check_local |
|
||||
| `route_lib.h` | Таблица маршрутов: route_insert, route_delete |
|
||||
| `route_ping.h` | NAT-детекция и проверка связности: route_ping_send_req_addr, handle_req, handle_resp, cancel_for_conn |
|
||||
| `route_connectivity.h` | Зондирование связности узла: route_connectivity_probe_node, cancel_node, cancel_all |
|
||||
| `etcp_api.h` | Регистрация приёмника: etcp_bind, etcp_add_new_conn_cbk, etcp_unbind |
|
||||
| `etcp_connections.h` | Управление соединениями: etcp_conn_add_up_cbk, etcp_conn_add_down_cbk, ETCP_CONN, ETCP_LINK |
|
||||
| `etcp.h` | Отправка пакетов: etcp_send |
|
||||
| `etcp.h` | Отправка пакетов: etcp_send, etcp_set_routing_exchange_state |
|
||||
| `etcp_debug.h` | Логирование ETCP: log_name |
|
||||
| `etcp_router.h` | Закрытие виртуальных каналов: etcp_router_conn_close_all_for_node |
|
||||
| `control_server.h` | Уведомления мониторинга: control_server_notify_node_change, control_server_notify_node_removed |
|
||||
| `conn_mgr.h` | Менеджер соединений: использует topo_group_find_conn_for_node для поиска путей |
|
||||
| `utun_instance.h` | UTUN_INSTANCE: конфиг, node_id, ключи, rt, topo_groups |
|
||||
| `utun_instance.h` | UTUN_INSTANCE: конфиг, node_id, ключи, rt, topo_groups, nat_det, topo_sqlite_db, control_srv |
|
||||
| `secure_channel.h` | Ключи: sc_derive_ed25519_pubkey |
|
||||
| `memory_pool.h` | Память для сериализации: memory_pool_init / destroy |
|
||||
| `debug_config.h` | Логирование: DEBUG_INFO/WARN/ERROR/DEBUG с категорией BGP |
|
||||
| `mem.h` | Аллокация: u_malloc, u_free, u_calloc |
|
||||
| `platform_compat.h` | Кроссплатформенная совместимость |
|
||||
|
||||
### 4.2. Кто вызывает topo_group
|
||||
|
||||
| Вызывающий | Что вызывает | Для чего |
|
||||
|---|---|---|
|
||||
| `utun_instance.c` | `topo_groups_init`, `topo_groups_destroy` | Инициализация/завершение при старте/остановке utun |
|
||||
| `etcp_connections.c` | `topo_group_send_nodeinfo` | При изменении конфигурации сокетов — broadcast обновлённого local_node |
|
||||
| `etcp_connections.c` | `topo_group_update_my_nodeinfo` | При изменении конфигурации сокетов — обновление local_node |
|
||||
| `etcp_router.c` | `topo_group_find_conn_for_node` | Поиск next-hop для маршрутов и multihop-соединений (~15 вызовов) |
|
||||
| `conn_mgr.c` | `topo_group_find_conn_for_node` | Поиск пути для установки прямого/обратного/indirect соединения |
|
||||
| `nat_detection.c` | `topo_group_update_my_nodeinfo` | При обновлении NAT-типа сокета — обновление local_node и broadcast |
|
||||
| Тесты | `topo_groups_get_default`, `topo_group_find_conn_for_node`, `topo_node_find_by_id`, `topo_group_set_nat_check_local` | 9 тестовых файлов |
|
||||
|
||||
### 4.3. ETCP-коллбэки (верхний уровень)
|
||||
|
||||
Точка входа для пакетов топологии — `etcp_bind(instance, ETCP_ID_TOPO_ENTRY, topo_group_receive_cbk)` в `topo_groups_init`. Единственный обработчик на весь ETCP ID 0x01. Диспетчеризация внутри по subcmd (byte data[1]).
|
||||
Точка входа для пакетов топологии — `etcp_bind(instance, ETCP_ID_TOPO_ENTRY, topo_group_receive_cbk)` в `topo_groups_init`. Единственный обработчик на весь ETCP ID 0x01. Диспетчеризация внутри по subcmd (byte data[1]). Обработка всегда идёт через default-группу (TOPO_GROUP_UTUN).
|
||||
|
||||
Коллбэки on_up/on_down новых соединений регистрируются через `etcp_add_new_conn_cbk(instance, topo_group_etcp_conn_cbk, NULL)`, которая при создании каждого нового ETCP_CONN подписывается на его события.
|
||||
|
||||
@@ -280,26 +293,21 @@ cmd(1) | subcmd(1) | node_id(8) | wd_source(8) = 18 байт
|
||||
cmd(1) | subcmd(1) = 2 байта
|
||||
```
|
||||
|
||||
### 5.4. NAT_INFO
|
||||
```
|
||||
cmd(1) | subcmd(1) | socket_id(1) | nat_ip(4) | nat_port(2) | nat_type(1) = 10 байт
|
||||
```
|
||||
|
||||
### 5.5. NAT_CHECK_REQ
|
||||
```
|
||||
cmd(1) | subcmd(1) | socket_id(1) | interface_ip(4) | interface_port(2) = 9 байт
|
||||
```
|
||||
|
||||
### 5.6. ERR_GROUP_MISMATCH
|
||||
### 5.4. ERR_GROUP_MISMATCH
|
||||
```
|
||||
cmd(1) | subcmd(1) | expected_type(1) | received_flags(1) = 4 байта
|
||||
```
|
||||
|
||||
## 6. Примечания
|
||||
|
||||
- **Хранилище памяти**: В отличие от STCP, модуль не имеет отдельной БД. Использует внешний SQLite3 pointer (`topo_groups->topo_sqlite_db`), который может быть открыт самим модулем (если указан `db_path`) или передан из chatgui через `topo_groups_set_sqlite_db`.
|
||||
- **Версионирование NODEINFO**: Версия (ver, uint8_t 1-255) сравнивается через циклическую арифметику `(int8_t)(cur_ver - new_ver) >= 0`. Версия 0 не используется (нет данных).
|
||||
- **Защита от петель**: NODEINFO рассылается только тем пирам, чей node_id отсутствует в hop_list. WITHDRAW broadcast всем кроме отправителя.
|
||||
- **NAT check для локальных сетей**: По умолчанию запрещён (`allow_nat_check_local=0`), можно включить через `topo_group_set_nat_check_local(group, 1)` для тестов.
|
||||
- **Aliens**: Узлы с `alien=1` хранятся в nodes, имеют пути, но не инициируют обмен маршрутами при подключении (topo_group_new_conn пропускает).
|
||||
- **Обновление при NAT-детекции**: При получении NAT_INFO, если изменился nat_type сокета с не-EIM на EIM (или наоборот) — обновляется `local_node`, увеличивается версия и broadcast-ится всем пирам.
|
||||
- **Хранилище SQLite**: Открывается в `topo_groups_init` если в конфиге задан `db_path`. БД хранится как `instance->topo_sqlite_db` (sqlite3*), используется во всех группах. Файл: `{db_path}/chats.db`. Режим: WAL, foreign_keys=ON, synchronous=NORMAL.
|
||||
- **Версионирование NODEINFO**: Версия (ver, uint8_t 1-255) сравнивается через циклическую арифметику `(int8_t)(cur_ver - new_ver) >= 0`. Версия 0 не используется (нет данных). Если версия stale — обновляются только пути без замены данных узла.
|
||||
- **Защита от петель**: NODEINFO рассылается только тем пирам, чей node_id отсутствует во всех путях (hop_list). WITHDRAW broadcast всем кроме отправителя.
|
||||
- **Связность узлов**: Для новых/обновлённых узлов с адресами автоматически запускается зондирование (`route_connectivity_probe_node`). При удалении узла (withdraw/remove_conn) пробы отменяются (`route_connectivity_cancel_node`).
|
||||
- **Виртуальные каналы**: При удалении узла закрываются все виртуальные ETCP-каналы через `etcp_router_conn_close_all_for_node`.
|
||||
- **Dirty-флаг**: При withdraw/remove_conn узлы помечаются `dirty = 1` перед удалением.
|
||||
- **NAT check для локальных сетей**: По умолчанию запрещён (`allow_nat_check_local=0`), можно включить через `topo_group_set_nat_check_local(group, 1)` для тестов (делегирует в `nat_detection_set_allow_local`).
|
||||
- **Conn_mgr поля**: `conn_mgr_type`, `conn_mgr_intermediaries[]`, `conn_mgr_intermediariy_count` — используются менеджером соединений для стратегии установки связи с узлом (direct/reverse/indirect).
|
||||
- **Обновление local_node при NAT-детекции**: Модуль `nat_detection` после определения типа NAT вызывает `topo_group_update_my_nodeinfo()` для обновления local_node с новыми данными сокетов — это автоматически увеличивает версию, и NODEINFO broadcast-ится всем пирам через механизм изменения конфигурации сокетов.
|
||||
|
||||
(End of file - total 302 lines)
|
||||
+4
-4
@@ -297,8 +297,8 @@ void topo_node_dump_all(struct TOPO_GROUP* group) {
|
||||
int is_local = (group->local_node == nq);
|
||||
const char* name_s = ni->node_name ? ni->node_name : "";
|
||||
|
||||
DEBUG_INFO(DEBUG_CATEGORY_BGP, "--- Node #%d: id=%016llx name=\"%s\" ver=%u dirty=%u alien=%u grp=%llu%s ---",
|
||||
idx, (unsigned long long)ni->node_id, name_s, ni->ver, nq->dirty, nq->alien,
|
||||
DEBUG_INFO(DEBUG_CATEGORY_BGP, "--- Node #%d: id=%016llx name=\"%s\" ver=%u dirty=%u grp=%llu%s ---",
|
||||
idx, (unsigned long long)ni->node_id, name_s, ni->ver, nq->dirty,
|
||||
(unsigned long long)ni->group_id, is_local ? " [LOCAL]" : "");
|
||||
|
||||
if (nq->best_socket) DEBUG_INFO(DEBUG_CATEGORY_BGP, " best_socket=%p", (void*)nq->best_socket);
|
||||
@@ -395,8 +395,8 @@ int topo_node_format_all(struct TOPO_GROUP* group, char* buf, size_t buf_size) {
|
||||
|
||||
#define FMT_ADD(fmt, ...) pos += snprintf(buf + pos, buf_size > (size_t)pos ? buf_size - pos : 0, fmt, ##__VA_ARGS__)
|
||||
|
||||
FMT_ADD("--- Node #%d: id=%016llx name=\"%s\" ver=%u dirty=%u alien=%u grp=%llu%s ---\n",
|
||||
idx, (unsigned long long)ni->node_id, name_s, ni->ver, nq->dirty, nq->alien,
|
||||
FMT_ADD("--- Node #%d: id=%016llx name=\"%s\" ver=%u dirty=%u grp=%llu%s ---\n",
|
||||
idx, (unsigned long long)ni->node_id, name_s, ni->ver, nq->dirty,
|
||||
(unsigned long long)ni->group_id, is_local ? " [LOCAL]" : "");
|
||||
|
||||
if (nq->best_socket) FMT_ADD(" best_socket=%p\n", (void*)nq->best_socket);
|
||||
|
||||
@@ -234,7 +234,6 @@ struct TOPO_NODEQ {
|
||||
struct ll_queue* paths;
|
||||
uint8_t dirty;
|
||||
uint8_t last_ver;
|
||||
uint8_t alien;
|
||||
uint8_t conn_mgr_type;
|
||||
uint64_t conn_mgr_intermediaries[CONN_MGR_MAX_INTERMEDIARIES];
|
||||
uint8_t conn_mgr_intermediariy_count;
|
||||
|
||||
+46
-56
@@ -57,18 +57,17 @@ int topo_node_sqlite_init(sqlite3* db) {
|
||||
"CREATE UNIQUE INDEX IF NOT EXISTS idx_na_unique ON node_addresses(node_id, family, socket_id, addr_type);"
|
||||
|
||||
"CREATE TABLE IF NOT EXISTS channels ("
|
||||
" channel_id TEXT PRIMARY KEY,"
|
||||
" name TEXT NOT NULL,"
|
||||
" owner_node_id INTEGER,"
|
||||
" is_dm INTEGER DEFAULT 0,"
|
||||
" x25519_pubkey BLOB NOT NULL,"
|
||||
" x25519_privkey BLOB,"
|
||||
" ed25519_pubkey BLOB NOT NULL,"
|
||||
" ed25519_privkey BLOB,"
|
||||
" signature BLOB NOT NULL,"
|
||||
" last_read_msg_id INTEGER,"
|
||||
" last_pos_msg_id INTEGER,"
|
||||
" created_at INTEGER DEFAULT (unixepoch())"
|
||||
" channel_id TEXT PRIMARY KEY," // ID канала
|
||||
" name TEXT NOT NULL," // Название канала
|
||||
" owner_node_id INTEGER," // node id создателя (надо выпилить)
|
||||
" x25519_pubkey BLOB NOT NULL," // ключ для шифрования канала
|
||||
" x25519_privkey BLOB," // если админ - то ключ канала
|
||||
" ed25519_pubkey BLOB NOT NULL," // ключ для проверки подписи
|
||||
" ed25519_privkey BLOB," // ключ для создания подписи
|
||||
" signature BLOB NOT NULL," // подпись при создании канал приватным ключом канала channel_id(NUL-terminated) || name(NUL-terminated) || owner_node_id(8 байт, LE uint64) || x25519_pubkey(32 байта) || ed25519_pubkey(32 байта)
|
||||
" last_read_msg_id INTEGER," // для скроллинга чата
|
||||
" last_pos_msg_id INTEGER," // для скроллинга чата
|
||||
" created_at INTEGER DEFAULT (unixepoch())" // когда создан
|
||||
");";
|
||||
|
||||
char* err = NULL;
|
||||
@@ -79,14 +78,6 @@ int topo_node_sqlite_init(sqlite3* db) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* migration: add socket_id column if not exists (ignore error for existing DBs) */
|
||||
sqlite3_exec(db,
|
||||
"ALTER TABLE node_addresses ADD COLUMN socket_id INTEGER NOT NULL DEFAULT 0",
|
||||
NULL, NULL, NULL);
|
||||
sqlite3_exec(db,
|
||||
"CREATE UNIQUE INDEX IF NOT EXISTS idx_na_unique ON node_addresses(node_id, family, socket_id, addr_type)",
|
||||
NULL, NULL, NULL);
|
||||
|
||||
DEBUG_INFO(DEBUG_CATEGORY_BGP, "topo_node_sqlite tables initialized");
|
||||
return 0;
|
||||
}
|
||||
@@ -234,7 +225,7 @@ int topo_node_sqlite_node_put(sqlite3* db, struct TOPO_NODEQ* nq) {
|
||||
}
|
||||
|
||||
int topo_node_sqlite_channel_put(sqlite3* db, const char* channel_id,
|
||||
const char* name, int is_dm, uint64_t owner_node_id,
|
||||
const char* name, uint64_t owner_node_id,
|
||||
const uint8_t* x25519_pub, const uint8_t* x25519_priv,
|
||||
const uint8_t* ed25519_pub, const uint8_t* ed25519_priv,
|
||||
const uint8_t* signature) {
|
||||
@@ -243,18 +234,19 @@ int topo_node_sqlite_channel_put(sqlite3* db, const char* channel_id,
|
||||
char peers_tbl[80]; peers_table_name(channel_id, peers_tbl, sizeof(peers_tbl));
|
||||
char sql[512];
|
||||
snprintf(sql, sizeof(sql),
|
||||
"CREATE TABLE IF NOT EXISTS \"%s\" ("
|
||||
" node_id INTEGER NOT NULL,"
|
||||
" x25519_pubkey BLOB NOT NULL,"
|
||||
" ed25519_pubkey BLOB NOT NULL,"
|
||||
" join_sig BLOB NOT NULL,"
|
||||
" join_ts INTEGER NOT NULL,"
|
||||
" update_sig BLOB,"
|
||||
" update_ts INTEGER DEFAULT 0,"
|
||||
" name TEXT NOT NULL DEFAULT '',"
|
||||
" creator_sig BLOB,"
|
||||
" comment TEXT,"
|
||||
" joined_at INTEGER DEFAULT (unixepoch()),"
|
||||
"CREATE TABLE IF NOT EXISTS \"%s\" (" // таблица мемберов канала
|
||||
" node_id INTEGER NOT NULL," // ид меббера группы
|
||||
" x25519_pubkey BLOB NOT NULL," // ключ мембера для шифрования
|
||||
" ed25519_pubkey BLOB NOT NULL," // ключ мембера для подписи
|
||||
" join_sig BLOB NOT NULL," // подпись мембера неизменяемая (при присоединении) сh_x25519_pubkey(32) || ch_ed25519_pubkey(32) || node_id(8 LE) || node_x25519_pubkey(32) || join_ts(8 LE)
|
||||
" join_ts INTEGER NOT NULL," // время присоединения
|
||||
" update_sig BLOB," // подпись мембера изменяемая join_sig(64) || update_ts(8) || userinfo (NUL-terminated)
|
||||
" update_ts INTEGER DEFAULT 0,"// время подписи
|
||||
" userinfo TEXT NOT NULL DEFAULT ''," // имя/метаданные узла в канале (json_flat style)
|
||||
" creator_sig BLOB," // валидация мембера админом канала
|
||||
" adm_tags TEXT," // тэги которые может менять админ (json_flat style)
|
||||
" adm_tags_sig BLOB," // подпись тэгов adm_tags (может быть подписана ключом суперадмина или админов - проверяем перебором)
|
||||
" node_type INTEGER NOT NULL," // ENUM(0-'PRIV',1-'NAT',2-'EIM-NAT',3-'PUBLIC',4-SUPERNODE) тип ноды - private (только нода подключается), nat (односторонние подключения), eim-nat (доступны подключения но надо запрашивать ip), public - на прямых ip (но может быть недоступна в чебурнетах), supernode - на прямых ip, рекомендуемая для подключения
|
||||
" PRIMARY KEY (node_id))", peers_tbl);
|
||||
char* err = NULL;
|
||||
sqlite3_exec(db, sql, NULL, NULL, &err);
|
||||
@@ -262,20 +254,19 @@ int topo_node_sqlite_channel_put(sqlite3* db, const char* channel_id,
|
||||
|
||||
sqlite3_stmt* stmt = NULL;
|
||||
if (sqlite3_prepare_v2(db,
|
||||
"INSERT OR REPLACE INTO channels(channel_id, name, owner_node_id, is_dm,"
|
||||
"INSERT OR REPLACE INTO channels(channel_id, name, owner_node_id,"
|
||||
" x25519_pubkey, x25519_privkey, ed25519_pubkey, ed25519_privkey, signature)"
|
||||
" VALUES(?,?,?,?,?,?,?,?,?)", -1, &stmt, NULL) != SQLITE_OK) return -1;
|
||||
" VALUES(?,?,?,?,?,?,?,?)", -1, &stmt, NULL) != SQLITE_OK) return -1;
|
||||
sqlite3_bind_text(stmt, 1, channel_id, -1, SQLITE_STATIC);
|
||||
sqlite3_bind_text(stmt, 2, name, -1, SQLITE_STATIC);
|
||||
sqlite3_bind_int64(stmt, 3, (sqlite3_int64)owner_node_id);
|
||||
sqlite3_bind_int(stmt, 4, is_dm);
|
||||
sqlite3_bind_blob(stmt, 5, x25519_pub, 32, SQLITE_STATIC);
|
||||
if (x25519_priv) sqlite3_bind_blob(stmt, 6, x25519_priv, 32, SQLITE_STATIC);
|
||||
else sqlite3_bind_null(stmt, 6);
|
||||
sqlite3_bind_blob(stmt, 7, ed25519_pub, 32, SQLITE_STATIC);
|
||||
if (ed25519_priv) sqlite3_bind_blob(stmt, 8, ed25519_priv, 32, SQLITE_STATIC);
|
||||
else sqlite3_bind_null(stmt, 8);
|
||||
sqlite3_bind_blob(stmt, 9, signature, 64, SQLITE_STATIC);
|
||||
sqlite3_bind_blob(stmt, 4, x25519_pub, 32, SQLITE_STATIC);
|
||||
if (x25519_priv) sqlite3_bind_blob(stmt, 5, x25519_priv, 32, SQLITE_STATIC);
|
||||
else sqlite3_bind_null(stmt, 5);
|
||||
sqlite3_bind_blob(stmt, 6, ed25519_pub, 32, SQLITE_STATIC);
|
||||
if (ed25519_priv) sqlite3_bind_blob(stmt, 7, ed25519_priv, 32, SQLITE_STATIC);
|
||||
else sqlite3_bind_null(stmt, 7);
|
||||
sqlite3_bind_blob(stmt, 8, signature, 64, SQLITE_STATIC);
|
||||
int rc = sqlite3_step(stmt); sqlite3_finalize(stmt);
|
||||
return rc == SQLITE_DONE ? 0 : -1;
|
||||
}
|
||||
@@ -283,14 +274,14 @@ int topo_node_sqlite_channel_put(sqlite3* db, const char* channel_id,
|
||||
int topo_node_sqlite_member_put(sqlite3* db, const char* channel_id, uint64_t node_id,
|
||||
const uint8_t* join_sig, uint64_t join_ts,
|
||||
const uint8_t* update_sig, uint64_t update_ts,
|
||||
const uint8_t* x25519_pubkey, const uint8_t* ed25519_pubkey, const char* name,
|
||||
const uint8_t* x25519_pubkey, const uint8_t* ed25519_pubkey, const char* userinfo,
|
||||
const uint8_t* creator_sig) {
|
||||
if (!db || !channel_id || !x25519_pubkey || !ed25519_pubkey) return -1;
|
||||
char peers_tbl[80]; peers_table_name(channel_id, peers_tbl, sizeof(peers_tbl));
|
||||
char sql[256];
|
||||
snprintf(sql, sizeof(sql),
|
||||
"INSERT OR REPLACE INTO \"%s\"(node_id, x25519_pubkey, ed25519_pubkey,"
|
||||
" join_sig, join_ts, update_sig, update_ts, name, creator_sig)"
|
||||
" join_sig, join_ts, update_sig, update_ts, userinfo, creator_sig)"
|
||||
" VALUES(?,?,?,?,?,?,?,?,?)", peers_tbl);
|
||||
sqlite3_stmt* stmt = NULL;
|
||||
if (sqlite3_prepare_v2(db, sql, -1, &stmt, NULL) != SQLITE_OK) return -1;
|
||||
@@ -303,7 +294,7 @@ int topo_node_sqlite_member_put(sqlite3* db, const char* channel_id, uint64_t no
|
||||
if (update_sig) sqlite3_bind_blob(stmt, 6, update_sig, PEERS_UPDATE_SIG_SIZE, SQLITE_STATIC);
|
||||
else sqlite3_bind_null(stmt, 6);
|
||||
sqlite3_bind_int64(stmt, 7, (sqlite3_int64)update_ts);
|
||||
sqlite3_bind_text(stmt, 8, name ? name : "", -1, SQLITE_STATIC);
|
||||
sqlite3_bind_text(stmt, 8, userinfo ? userinfo : "", -1, SQLITE_STATIC);
|
||||
if (creator_sig) sqlite3_bind_blob(stmt, 9, creator_sig, PEERS_JOIN_SIG_SIZE, SQLITE_STATIC);
|
||||
else sqlite3_bind_null(stmt, 9);
|
||||
int rc = sqlite3_step(stmt); sqlite3_finalize(stmt);
|
||||
@@ -359,12 +350,12 @@ int topo_node_sqlite_member_del(sqlite3* db, const char* channel_id, uint64_t no
|
||||
}
|
||||
|
||||
int topo_node_sqlite_channel_get(sqlite3* db, const char* channel_id,
|
||||
char* name_out, int name_sz, int* is_dm, uint64_t* owner_node_id,
|
||||
char* name_out, int name_sz, uint64_t* owner_node_id,
|
||||
uint8_t* x25519_pub, uint8_t* ed25519_pub, uint8_t* signature) {
|
||||
if (!db || !channel_id) return -1;
|
||||
sqlite3_stmt* stmt = NULL;
|
||||
if (sqlite3_prepare_v2(db,
|
||||
"SELECT name, owner_node_id, is_dm, x25519_pubkey, ed25519_pubkey, signature"
|
||||
"SELECT name, owner_node_id, x25519_pubkey, ed25519_pubkey, signature"
|
||||
" FROM channels WHERE channel_id=?", -1, &stmt, NULL) != SQLITE_OK) return -1;
|
||||
sqlite3_bind_text(stmt, 1, channel_id, -1, SQLITE_STATIC);
|
||||
if (sqlite3_step(stmt) != SQLITE_ROW) { sqlite3_finalize(stmt); return -1; }
|
||||
@@ -373,10 +364,9 @@ int topo_node_sqlite_channel_get(sqlite3* db, const char* channel_id,
|
||||
if (txt) snprintf(name_out, name_sz, "%s", txt); else name_out[0] = '\0';
|
||||
}
|
||||
if (owner_node_id) *owner_node_id = (uint64_t)sqlite3_column_int64(stmt, 1);
|
||||
if (is_dm) *is_dm = sqlite3_column_int(stmt, 2);
|
||||
if (x25519_pub) memcpy(x25519_pub, sqlite3_column_blob(stmt, 3), 32);
|
||||
if (ed25519_pub) memcpy(ed25519_pub, sqlite3_column_blob(stmt, 4), 32);
|
||||
if (signature) memcpy(signature, sqlite3_column_blob(stmt, 5), 64);
|
||||
if (x25519_pub) memcpy(x25519_pub, sqlite3_column_blob(stmt, 2), 32);
|
||||
if (ed25519_pub) memcpy(ed25519_pub, sqlite3_column_blob(stmt, 3), 32);
|
||||
if (signature) memcpy(signature, sqlite3_column_blob(stmt, 4), 64);
|
||||
sqlite3_finalize(stmt);
|
||||
return 0;
|
||||
}
|
||||
@@ -390,7 +380,7 @@ int topo_node_sqlite_channel_peers_all(sqlite3* db, const char* channel_id,
|
||||
char sql[512];
|
||||
snprintf(sql, sizeof(sql),
|
||||
"SELECT node_id, x25519_pubkey, ed25519_pubkey,"
|
||||
" join_sig, join_ts, update_sig, update_ts, name"
|
||||
" join_sig, join_ts, update_sig, update_ts, userinfo"
|
||||
" FROM \"%s\" ORDER BY node_id ASC", peers_tbl);
|
||||
if (sqlite3_prepare_v2(db, sql, -1, &stmt, NULL) != SQLITE_OK) return -1;
|
||||
|
||||
@@ -407,9 +397,9 @@ int topo_node_sqlite_channel_peers_all(sqlite3* db, const char* channel_id,
|
||||
uint64_t join_ts = (uint64_t)sqlite3_column_int64(stmt, 4);
|
||||
const uint8_t* update_sig = (const uint8_t*)sqlite3_column_blob(stmt, 5);
|
||||
uint64_t update_ts = (uint64_t)sqlite3_column_int64(stmt, 6);
|
||||
const char* pname = (const char*)sqlite3_column_text(stmt, 7);
|
||||
const char* puserinfo = (const char*)sqlite3_column_text(stmt, 7);
|
||||
if (!x25519 || !ed_pub) continue;
|
||||
uint8_t nl = pname ? (uint8_t)strnlen(pname, 255) : 0;
|
||||
uint8_t nl = puserinfo ? (uint8_t)strnlen(puserinfo, 255) : 0;
|
||||
|
||||
sqlite3_stmt* ast = NULL;
|
||||
snprintf(sql, sizeof(sql),
|
||||
@@ -464,7 +454,7 @@ int topo_node_sqlite_channel_peers_all(sqlite3* db, const char* channel_id,
|
||||
uint64_t z = 0; memcpy(buf + off, &z, 8); off += 8;
|
||||
}
|
||||
buf[off++] = nl;
|
||||
if (nl) { memcpy(buf + off, pname, nl); off += nl; }
|
||||
if (nl) { memcpy(buf + off, puserinfo, nl); off += nl; }
|
||||
buf[off++] = addr_cnt;
|
||||
off += addr_sz;
|
||||
(*cnt_ptr)++;
|
||||
|
||||
@@ -19,7 +19,7 @@ int topo_node_sqlite_init(sqlite3* db);
|
||||
int topo_node_sqlite_node_put(sqlite3* db, struct TOPO_NODEQ* nq);
|
||||
|
||||
int topo_node_sqlite_channel_put(sqlite3* db, const char* channel_id,
|
||||
const char* name, int is_dm, uint64_t owner_node_id,
|
||||
const char* name, uint64_t owner_node_id,
|
||||
const uint8_t* x25519_pub, const uint8_t* x25519_priv,
|
||||
const uint8_t* ed25519_pub, const uint8_t* ed25519_priv,
|
||||
const uint8_t* signature);
|
||||
@@ -27,7 +27,7 @@ int topo_node_sqlite_channel_put(sqlite3* db, const char* channel_id,
|
||||
int topo_node_sqlite_member_put(sqlite3* db, const char* channel_id, uint64_t node_id,
|
||||
const uint8_t* join_sig, uint64_t join_ts,
|
||||
const uint8_t* update_sig, uint64_t update_ts,
|
||||
const uint8_t* x25519_pubkey, const uint8_t* ed25519_pubkey, const char* name,
|
||||
const uint8_t* x25519_pubkey, const uint8_t* ed25519_pubkey, const char* userinfo,
|
||||
const uint8_t* creator_sig);
|
||||
|
||||
int topo_node_sqlite_member_get_join(sqlite3* db, const char* channel_id, uint64_t node_id,
|
||||
@@ -39,7 +39,7 @@ int topo_node_sqlite_node_update_verified(sqlite3* db, uint64_t node_id,
|
||||
const char* name, const uint8_t* x25519, const uint8_t* ed25519, uint64_t update_ts);
|
||||
|
||||
int topo_node_sqlite_channel_get(sqlite3* db, const char* channel_id,
|
||||
char* name_out, int name_sz, int* is_dm, uint64_t* owner_node_id,
|
||||
char* name_out, int name_sz, uint64_t* owner_node_id,
|
||||
uint8_t* x25519_pub, uint8_t* ed25519_pub, uint8_t* signature);
|
||||
|
||||
int topo_node_sqlite_channel_peers_all(sqlite3* db, const char* channel_id,
|
||||
|
||||
@@ -0,0 +1,89 @@
|
||||
++ OOP:
|
||||
class Animal {
|
||||
public: // доступен всем
|
||||
//private: // только внутри класса
|
||||
//protected: // внутри класса и наследников
|
||||
virtual void sound() {play(...);}// virtual — разрешает переопределение.
|
||||
|
||||
virtual void sound2() = 0;// если =0 - метод обязательно должен быть реализован в наследнике, а сам класс становится абстрактным и его экземпляры создавать нельзя.
|
||||
|
||||
Animal() {// конструктор - совпадает с именем класса
|
||||
cout << "Created";
|
||||
}
|
||||
~Animal() {// деструктор
|
||||
cout << "Deleted";
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
class Dog : public Animal {// наследование
|
||||
public:
|
||||
void sound() override {}// override — говорит компилятору, что метод действительно переопределяет базовый.
|
||||
};
|
||||
|
||||
|
||||
|
||||
|
||||
Стратегия обмена
|
||||
|
||||
узлы которые online живут только в пемяти. и синхронизируемся с ними используя topo_group.
|
||||
|
||||
routing layer:
|
||||
- topo_group - обеспечивает корректное построение оптимальной таблицы роутинга (per group) между узлами в рамках установленных подключений. И корректировку этой таблицы при перестроении подключений.
|
||||
|
||||
- topo_group_discovery - обеспечивает оптимизацию подключений, а именно:
|
||||
- периодически пингует узлы проверяя их доступность
|
||||
|
||||
|
||||
для каждой группы админ назначает суперузлы: a b c ... стать усуперузлом - получить разрешение с админской подписью.
|
||||
|
||||
суперузел активирует bgp обмен с остаьлными суперузлами.
|
||||
как:
|
||||
|
||||
суперузел - это по сути сервер для группы. который также синхронизируется с другими суперузлами
|
||||
|
||||
клиенты приоритетно подключаются к суперузлам:
|
||||
- подключаются к кому-то
|
||||
- запрашивают таблицу маршрутизации
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
1. Узлы за nat сами выбирают подходящие для себя точки подключения к сети.
|
||||
2 шт с минимальным rtt
|
||||
2 шт с минимальной загруженностью
|
||||
|
||||
в чате (gui) можно выбрать предпочитаемые узлы для подключения
|
||||
|
||||
|
||||
2. узлы с eim nat
|
||||
??? если не хватает узлов прямым адресом
|
||||
|
||||
3. узлы с прямыми ип
|
||||
суть - выстраивают линки между собой для надежного обмена конфигурацией сети
|
||||
как:
|
||||
- в таблице узлов для каждой группы
|
||||
|
||||
У каждого узла проставляется доступность (можем ли мы каким-либо образом подключиться): acessible, reverse (при подключении с другой стороны), no
|
||||
|
||||
|
||||
Единое обновление инфы об узле (per-group):
|
||||
- обновляем тип узла в каждой группе где есть узел при обовлении типа нат узла. прямая доступность узла: yes/no.
|
||||
|
||||
|
||||
|
||||
|
||||
=======================================
|
||||
функционал группы.
|
||||
|
||||
Есть суперадмины группы - владеют приватным ключом суперадмина (один на группу).
|
||||
Есть админы - владеют сгенерированным приватным ключом админа и подписанным суперадмином.
|
||||
суперадмины могут делать revoke админу.
|
||||
|
||||
админские действия также распространяются по чату как и обычные сообщения.
|
||||
ид группы = пубкей группы
|
||||
|
||||
|
||||
юзер - join по ссылке (приватный ключ пира). получает ключи других пиров. Может только читать.
|
||||
админ подписывает разрешение на писать (как и другие разрешения). подпись храним в таблице юзеров
|
||||
Reference in new issue
Block a user