You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

320 lines
12 KiB

/**
* @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_<channel_id>). База открывается в topo_groups_init()
* если в конфиге задан db_path.
*/
#ifndef TOPO_GROUP_H
#define TOPO_GROUP_H
#ifdef __cplusplus
extern "C" {
#endif
#include <stdint.h>
#include <stddef.h>
#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