/** * @file topo_group.h * @brief BGP-подобный обмен топологией узлов между пирами через ETCP. * * Механика: * - Узлы обмениваются информацией друг о друге (pubkey, адреса, подсети) * через NODEINFO/WITHDRAW сообщения, маршрутизируемые по ETCP. * - Каждый узел хранит полную таблицу известных узлов (структура TOPO_NODEQ). * - При подключении нового пира — полная синхронизация таблицы. * - При отключении пира — withdraw всех узлов, достижимых только через него. * * Группы (TOPO_GROUP): * - TOPO_GROUP_TYPE_UTUN — основная VPN-сеть (обмен маршрутами, подсетями) * - TOPO_GROUP_TYPE_CHAT — чат-группа (только узлы, без подсетей) * - Каждая группа изолирована: узлы из utun-группы не видны в чат-группе и наоборот * * Дополнительные функции: * - NAT-детекция и проверка связности (NAT_INFO, NAT_CHECK_REQ, PING) * - Поиск оптимального маршрута до узла (topo_group_find_conn_for_node) * * Хранение: все узлы сохраняются в SQLite (таблицы nodes, node_addresses, * channels, peers_). База открывается в topo_groups_init() * если в конфиге задан db_path. */ #ifndef TOPO_GROUP_H #define TOPO_GROUP_H #ifdef __cplusplus extern "C" { #endif #include #include #include "../lib/ll_queue.h" #include "../lib/memory_pool.h" #include "../lib/sqlite3.h" #include "route_lib.h" #include "topo_node.h" #include "secure_channel.h" struct UTUN_INSTANCE; /** Callback when a node's info (pubkeys, addresses) is persisted in the DB */ typedef void (*topo_node_updated_fn)(struct UTUN_INSTANCE* inst, uint64_t node_id, const uint8_t* x25519_pubkey, const uint8_t* ed25519_pubkey); // ETCP ID для пакетов топологии #define ETCP_ID_TOPO_ENTRY 0x01 // Sub-команды #define TOPO_SUBCMD_NODEINFO 0x04 // полная информация об узле + подсети #define TOPO_SUBCMD_REQUEST_TABLE 0x05 // запрос полной таблицы #define TOPO_SUBCMD_WITHDRAW 0x06 // узел стал недоступен #define TOPO_SUBCMD_NAT_INFO 0x09 // информация о типе NAT клиента #define TOPO_SUBCMD_NAT_CHECK_REQ 0x0A // запрос от клиента на проверку NAT для сокета #define TOPO_SUBCMD_TABLE_COMPLETE 0x0B // завершение начальной синхронизации таблицы #define TOPO_SUBCMD_ERR_GROUP_MISMATCH 0x0C // ошибка несоответствия типа группы #define MAX_HOPS 16 #define BGP_NODES_HASH_SIZE 256 /** * @brief Пакет с информацией об узле (NODEINFO) */ struct TOPOMSG_NODEINFO_PKT { uint8_t cmd; // ETCP_ID_TOPO_ENTRY uint8_t subcmd; // TOPO_SUBCMD_NODEINFO struct TOPOMSG_NODE node; } __attribute__((packed)); /** * @brief Пакет WITHDRAW (фиксированный) */ struct TOPOMSG_WITHDRAW_PKT { uint8_t cmd; uint8_t subcmd; uint64_t node_id; // удаляемый узел (который стал недоступен) uint64_t wd_source; // узел который инициировал withdraw (при удалении он должен быть в hoplist или = current node_id) } __attribute__((packed)); /** * @brief Пакет запроса полной таблицы */ struct TOPOMSG_TABLE_REQ { uint8_t cmd; uint8_t subcmd; } __attribute__((packed)); /** * @brief Пакет ошибки несоответствия типа группы */ struct TOPOMSG_ERR_GROUP_MISMATCH { uint8_t cmd; uint8_t subcmd; uint8_t expected_type; uint8_t received_flags; } __attribute__((packed)); struct TOPO_GROUP_CONN_ITEM { struct ll_entry ll; struct ETCP_CONN* conn; }; /** * @brief Пакет NAT_INFO (фиксированный) */ struct TOPOMSG_NAT_INFO { uint8_t cmd; uint8_t subcmd; uint8_t socket_id; // MY socket id (link->conn->sock_id) uint8_t nat_ip[4]; // network byte order uint16_t nat_port; // network byte order uint8_t nat_type; // NAT_TYPE_* } __attribute__((packed)); /** * @brief Пакет запроса на проверку NAT от клиента к серверу */ struct TOPOMSG_NAT_CHECK_REQ { uint8_t cmd; uint8_t subcmd; // TOPO_SUBCMD_NAT_CHECK_REQ uint8_t socket_id; // ID сокета клиента, который надо проверить uint32_t interface_ip; // interface_addr IP клиента (network byte order) uint16_t interface_port; // interface_addr port клиента (network byte order) } __attribute__((packed)); struct route_ping_pending; struct ETCP_LINK; /** * @brief Группа топологии (одна на каждый group_id: utun, чат-группа, …) */ struct TOPO_GROUP { struct ll_entry ll; uint64_t group_id; // уникальный идентификатор группы uint8_t group_type; // TOPO_GROUP_TYPE_* struct UTUN_INSTANCE* instance; struct ll_queue* senders_list; struct ll_queue* nodes; struct TOPO_NODEQ* local_node; struct route_ping_pending* ping_pending; uint32_t next_ping_req_id; uint8_t allow_nat_check_local; uint8_t ed25519_public_key[SC_PUBKEY_SIZE]; char channel_id[64]; // channel_id для групп типа CHAT }; /** * @brief Контейнер всех групп топологии экземпляра */ struct TOPO_GROUPS { struct UTUN_INSTANCE* instance; struct ll_queue* group_list; // ll_queue of TOPO_GROUP entries struct memory_pool* v4_sock_meta_pool; struct memory_pool* v4_addr_pool; struct memory_pool* v6_sock_meta_pool; struct memory_pool* v6_addr_pool; struct memory_pool* v4_subnet_pool; struct memory_pool* v6_subnet_pool; sqlite3* topo_sqlite_db; topo_node_updated_fn node_updated_cb; /* optional — set by chatgui's member_sync */ }; /** * @brief Создаёт контейнер групп и группу utun по умолчанию (group_id=1). * * @param instance экземпляр utun с node_id и конфигом * @return TOPO_GROUPS или NULL при ошибке */ struct TOPO_GROUPS* topo_groups_init(struct UTUN_INSTANCE* instance); /** * @brief Освобождает все группы и контейнер. * * @param instance экземпляр utun */ void topo_groups_destroy(struct UTUN_INSTANCE* instance); /** * @brief Возвращает группу по умолчанию (group_id=TOPO_GROUP_UTUN=0x8000000000000000). */ struct TOPO_GROUP* topo_groups_get_default(struct TOPO_GROUPS* g); /** * @brief Находит группу по id. */ struct TOPO_GROUP* topo_groups_find(struct TOPO_GROUPS* g, uint64_t group_id); /** * @brief Создаёт новую группу с указанным типом. * * @param g контейнер групп * @param group_id уникальный идентификатор * @param group_type TOPO_GROUP_TYPE_UTUN или TOPO_GROUP_TYPE_CHAT * @param channel_id идентификатор канала для групп типа CHAT (может быть NULL) * @return TOPO_GROUP или NULL при ошибке */ struct TOPO_GROUP* topo_groups_create_group(struct TOPO_GROUPS* g, uint64_t group_id, uint8_t group_type, const char* channel_id); /** * @brief Добавляет conn в senders_list (если нет), отправляет запрос таблицы (nodeinfo). * * Вызывается при ETCP on_up. */ void topo_group_new_conn(struct TOPO_GROUP* group, struct ETCP_CONN* conn); /** * @brief Удаляет conn из senders_list, очищает paths во всех nodes, отправляет withdraw если node unreachable. * * Вызывается при ETCP on_down. */ void topo_group_remove_conn(struct TOPO_GROUP* group, struct ETCP_CONN* conn); /** * @brief Обрабатывает пакет NODEINFO. * * Проверка версии, обновление или создание TOPO_NODEQ, добавление пути, * вставка в роутинг, broadcast если не max hops. * * @return 0 при успехе */ int topo_group_process_nodeinfo(struct TOPO_GROUP* group, struct ETCP_CONN* from, const uint8_t* data, size_t len); /** * @brief Обрабатывает WITHDRAW. * * Удаляет node из роутинга и nodes, broadcast withdraw. * * @return 0 при успехе */ int topo_group_process_withdraw(struct TOPO_GROUP* group, struct ETCP_CONN* sender, const uint8_t* data, size_t len); /** * @brief Отправляет NODEINFO пакет одному conn (всегда local_node). */ void topo_group_send_nodeinfo(struct TOPO_GROUP* group, struct TOPO_NODEQ* node, struct ETCP_CONN* conn); /** * @brief Отправляет WITHDRAW для node_id (вызывает broadcast_withdraw). */ void topo_group_send_withdraw(struct TOPO_GROUP* group, uint64_t node_id); /** * @brief Поиск оптимального ETCP соединения для указанного node_id. * * Перебирает paths узла, выбирает путь с минимальным hop_count. * * @param group указатель на TOPO_GROUP * @param node_id целевой узел * @return оптимальный ETCP_CONN* или NULL */ struct ETCP_CONN* topo_group_find_conn_for_node(struct TOPO_GROUP* group, uint64_t node_id); /** * @brief Добавляет путь (conn) в paths узла. * * @return 0 при успехе */ int topo_group_add_path(struct TOPO_NODEQ* nq, struct ETCP_CONN* conn, uint64_t* hop_list, uint8_t hop_count); /** * @brief Удаляет conn из paths узла. * * @return 1 если путей не осталось (unreachable) */ int topo_group_remove_path(struct TOPO_NODEQ* nq, struct ETCP_CONN* conn); /** * @brief Отправляет NAT_INFO клиенту с его типом NAT. * * @param conn соединение к клиенту * @param nat_ip IP клиента (network byte order) * @param nat_port порт клиента (network byte order) * @param nat_type NAT_TYPE_EIM или NAT_TYPE_STRICT */ void topo_group_send_nat_info(struct ETCP_CONN* conn, uint8_t socket_id, uint32_t nat_ip, uint16_t nat_port, uint8_t nat_type); /** * @brief Отправляет запрос на проверку NAT для указанного socket_id серверу. * * @param conn соединение к серверу * @param socket_id ID сокета для проверки */ void topo_group_send_nat_check_req(struct ETCP_CONN* conn, uint8_t socket_id); /** * @brief Разрешить/запретить NAT check для локальных подсетей (127.0.0.1, 10.x.x.x и т.д.) * По умолчанию запрещено. Полезно для тестов. */ void topo_group_set_nat_check_local(struct TOPO_GROUP* group, int allow); void topo_groups_set_node_updated_cb(struct TOPO_GROUPS* groups, topo_node_updated_fn fn); /** * @brief Запускает NAT check для всех линков всех соединений. */ void topo_group_request_nat_check_all(struct TOPO_GROUP* group); /** * @brief Запускает NAT check для одного линка. */ void topo_group_start_link_nat_check(struct TOPO_GROUP* group, struct ETCP_LINK* link); /* Аргумент callback NAT check */ struct nat_check_arg { struct ETCP_LINK* link; uint32_t nat_ip; uint16_t nat_port; }; void topo_groups_set_sqlite_db(struct TOPO_GROUPS* g, sqlite3* db); #ifdef __cplusplus } #endif #endif // TOPO_GROUP_H