15 KiB
topo_node
1. Назначение
Модель данных узла топологии — структуры данных для представления узла в памяти, сериализации/десериализации в бинарный wire-формат для BGP-обмена, учёт ссылок (refcounting) и диагностический дамп.
Модуль определяет два слоя представлений:
- Память: связанные списки (
TOPO_ADDR4,TOPO_SOCKMETA4,TOPO_SUBNET4и др.) для удобной модификации. - Wire-формат: packed-структуры (
TOPOMSG_NODE,TOPOMSG_ADDR4и др.) для передачи по сети без выравнивания.
Сам модуль не содержит сетевой логики BGP-обмена — этим занимается topo_group. Здесь только данные и их упаковка/распаковка.
Узлы персистентно хранятся в SQLite через модуль topo_node_sqlite. Память для элементов списков (адреса, сокет-мета, подсети) выделяется из memory pool'ов из topo_groups.
2. Как пользоваться
Типовые сценарии
Создание/обновление информации о себе:
topo_group_update_my_nodeinfo(instance, group);
Строит TOPO_NODEQ (в group->local_node) из конфига: собирает список сокетов, адресов (interface/NAT/real), подсетей; при изменениях инкрементирует ver и помечает dirty=1. Для chat-групп (TOPO_GROUP_TYPE_CHAT) подсети не включаются.
Сериализация для отправки по BGP:
uint8_t buf[2048];
int len = topo_node_serialize(group, nq, buf, sizeof(buf));
Запаковывает TOPO_NODEQ → бинарный буфер: заголовок TOPOMSG_NODE, затем имя узла, v4/v6 sock_meta, v4/v6 адреса, подсети (если TOPO_FLAG_SEND_SUBNETS), транзитные узлы и hop-лист.
Десериализация при получении NODEINFO:
struct TOPO_NODE *ni = NULL;
struct TOPO_NODESUBNETS *subnets = NULL;
struct TOPOMSG_TRANZIT *tranzit = NULL;
uint8_t tranzit_count = 0;
uint64_t *hop_list = NULL;
uint8_t hop_count = 0;
topo_node_deserialize(group, data, data_len, &ni, &subnets, &tranzit, &tranzit_count, &hop_list, &hop_count);
Восстанавливает TOPO_NODE, подсети, транзит и hop-лист из бинарного буфера. Элементы списков аллоцируются из memory pool'ов. Выделенная память должна быть освобождена:
topo_node_unref(ni)— освобождаетTOPO_NODEиnode_nametopo_node_free_lists(group, nq)— освобождает списки обратно в пулы, а такжеsubnets,tranzit_data,hop_list
Поиск узла по ID:
struct TOPO_NODEQ *nq = topo_node_find_by_id(group, node_id);
Использует хеш-индекс очереди group->nodes (ключ — node_id, 8 байт).
Дамп всех узлов:
topo_node_dump_all(group); // в лог
topo_node_format_all(group, buf, size); // в строку
Ключевые концепции
-
TOPO_NODE — базовые данные узла: pubkey (X25519), ed25519_pubkey, имя, версия, групповой ID. Разделяется между группами через счётчик ссылок.
-
TOPO_NODEQ — запись в очереди узлов группы. Содержит
TOPO_NODE*(общий), плюс динамические пути, подсети, транзитные узлы, hop-лист, связность, conn_mgr. Все поля аллоцированы отдельно и освобождаются черезtopo_node_free_lists(). -
TOPO_NODEPATH — путь до узла через конкретное ETCP-соединение. Хранится в
paths-очереди типаll_queue. После самой структуры идёт массивuint64_t hop[hop_count](variable-length). -
Счётчик ссылок:
topo_node_ref()/topo_node_unref(). При падении счётчика до 0 освобождаетсяnode_nameи самTOPO_NODE. Сами списки адресов и sock_meta освобождаются отдельно черезfree_lists(). -
Адреса бывают трёх типов:
TOPO_ADDR_INTERFACE(LAN-адрес),TOPO_ADDR_NAT(после детекции NAT),TOPO_ADDR_REAL(подтверждённый прямой адрес). Для каждого сокета может быть до 3 адресов в списке. -
TOPO_CONNECTIVITY — локальное состояние связности (не передаётся по BGP): статус зондирования (NONE/IN_PROGRESS/DONE), результаты для interface/nat/real адресов, минимальные RTT, время зондирования.
-
TOPO_FLAG_SEND_SUBNETS — флаг в
TOPO_NODE.flags: если установлен, подсети сериализуются и включаются в wire-формат. Для chat-групп сбрасывается в 0. -
Wire-протокол: заголовок
TOPOMSG_NODE(75 байт packed) + динамическая часть:node_name(0-255 байт),SOCKMETA4*N,ADDR4*N,SOCKMETA6*N,ADDR6*N, опционально подсети, транзит, hop-лист. Размер динамической части вычисляется функциейtopo_node_dyn_size().
3. API
Основные структуры
| Структура | Назначение |
|---|---|
struct TOPO_NODE |
Идентичность узла: pubkeys (X25519+Ed25519), имя, версия, group_id, node_id, списки v4/v6 сокет-метаданных и адресов. Счётчик ссылок. |
struct TOPO_NODEQ |
Запись в очереди группы. Содержит TOPO_NODE*, подсети, транзитные данные, hop-лист, очередь paths (TOPO_NODEPATH), связность, conn_mgr. best_socket — указатель на лучший ETCP-сокет для связи. |
struct TOPO_NODEPATH |
Путь до узла через ETCP-соединение с hop_count. Variable-length: после структуры идёт uint64_t hop[]. |
struct TOPO_NODESUBNETS |
Списки v4 и v6 подсетей, анонсируемых узлом. Отдельный malloc, может быть NULL. |
struct TOPO_CONNECTIVITY |
Локальное состояние проверки связности: статусы зондирования, RTT для interface/nat/real адресов. |
struct TOPOMSG_NODE |
Packed-заголовок wire-формата (75 байт). Содержит счётчики элементов динамической части. |
struct TOPOMSG_ADDR4/6 |
Packed wire-формат одного адреса (addr, port, type, socket_id, protocol). |
struct TOPOMSG_SOCKMETA4/6 |
Packed wire-формат метаданных сокета (id, config_type, nat_type). |
struct TOPOMSG_SUBNET4/6 |
Packed wire-формат подсети (addr, prefix_length). |
struct TOPOMSG_TRANZIT |
Packed wire-формат транзитного узла (node_id, rtt, link_q). |
Вспомогательные типы в памяти (связанные списки)
| Тип | Назначение |
|---|---|
struct TOPO_SOCKMETA4 |
Элемент списка v4 сокет-метаданных (next*, id, config_type, nat_type). |
struct TOPO_ADDR4 |
Элемент списка v4 адресов (next*, addr[4], port, type, socket_id, protocol). |
struct TOPO_SOCKMETA6 |
Элемент списка v6 сокет-метаданных. |
struct TOPO_ADDR6 |
Элемент списка v6 адресов. |
struct TOPO_SUBNET4 |
Элемент списка v4 подсетей (next*, addr[4], prefix_length). |
struct TOPO_SUBNET6 |
Элемент списка v6 подсетей. |
Все элементы списков аллоцируются из memory pool'ов и освобождаются через memory_pool_free().
Константы
| Константа | Значение | Назначение |
|---|---|---|
TOPO_ADDR_INTERFACE |
0 | Интерфейсный (LAN) адрес сокета |
TOPO_ADDR_NAT |
1 | NAT-адрес после детекции |
TOPO_ADDR_REAL |
2 | Подтверждённый прямой адрес |
TOPO_PROTO_UDP |
0x01 | UDP-транспорт |
TOPO_PROTO_TCP |
0x02 | TCP-транспорт |
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 | Адрес недостижим |
CONN_TYPE_DIRECT |
1 | Прямое соединение |
CONN_TYPE_REVERSE |
2 | Обратное соединение |
CONN_TYPE_INDIRECT |
3 | Соединение через посредника |
TOPO_FLAG_SEND_SUBNETS |
0x01 | Флаг: отправлять подсети |
TOPO_GROUP_UTUN |
0x8000000000000000ULL |
ID группы uTun по умолчанию |
Функции
Управление памятью и refcounting:
topo_node_ref(ni)— увеличить счётчик ссылок наTOPO_NODE.topo_node_unref(ni)— уменьшить счётчик; при 0 освободитьnode_nameиTOPO_NODE(но не списки — их надо предварительно освободить черезtopo_node_free_lists).topo_node_free_lists(group, nq)— освободить все списки адресов/sock_meta обратно в memory pool'ы, освободитьsubnets,tranzit_data,hop_list, сделатьunrefнаnode, обнулить указатели.
Сериализация:
topo_node_dyn_size(msg)— вычислить размер динамической части wire-формата по полям-счётчикам заголовкаTOPOMSG_NODE. Не учитывает сам заголовок.topo_node_serialize(group, nq, out, out_max)— сериализоватьTOPO_NODEQв бинарный буфер. Возвращает количество записанных байт или -1 при ошибке (переполнение или невалидные аргументы). Формат: заголовокTOPOMSG_NODE, затем name, v4 sock_meta, v4 addrs, v6 sock_meta, v6 addrs, подсети (опционально), tranzit, hop_list.
Десериализация:
topo_node_deserialize(group, data, len, &out_ni, &out_subnets, &out_tranzit, &out_tranzit_count, &out_hop_list, &out_hop_count)— восстановитьTOPO_NODE, подсети, транзит и hop-лист из бинарного wire-формата. АллоцируетTOPO_NODEчерезu_calloc(ref_count=1), элементы списков — из memory pool'ов,subnets,tranzit_data,hop_list— отдельнымиu_malloc. Возвращает 0 при успехе, -1 при несоответствии размера или ошибке аллокации. Если подсети есть в wire-формате но флагTOPO_FLAG_SEND_SUBNETSне установлен, они пропускаются.
Поиск:
topo_node_find_by_id(group, node_id)— найтиTOPO_NODEQв очереди группы поnode_idчерез хеш-индекс. Возвращает NULL если не найден.
Обновление информации о себе:
topo_group_update_my_nodeinfo(instance, group)— перестроитьgroup->local_node(TOPO_NODEQ) из текущих данных инстанса (сокеты, NAT, адреса, подсети, ключи). Сравнивает количество элементов с предыдущим значением; если есть изменения — освобождает старыйlocal_node, создаёт новый, инкрементируетver, помечаетdirty=1. Обновляет таблицу маршрутов (route_delete/route_insert). Для chat-групп подсети не включаются. Возвращает количество v4 подсетей.
Диагностика:
topo_node_dump_all(group)— дамп всех узлов группы в лог (DEBUG_CATEGORY_BGP). Для каждого узла выводит: ID, имя, версию, pubkeys, v4/v6 сокеты и адреса, подсети, транзитные узлы, пути (с hop-ами), статус связности, conn_mgr. Пропускает узлы сnode==NULL.topo_node_format_all(group, buf, buf_size)— аналогичный дамп в строковый буфер. Возвращает количество записанных байт. При переполнении добавляет[TRUNCATED].
Inline-аксессоры (из .h):
topo_v4_sock_meta(ni)/topo_v4_addrs(ni)/topo_v6_sock_meta(ni)/topo_v6_addrs(ni)— получить списки изTOPO_NODE.topo_v4_subnets(r)/topo_v6_subnets(r)— получить списки подсетей изTOPO_NODESUBNETS*(NULL-safe).topo_list_count(head)— посчитать количество элементов в любом из списков (next — первое поле).
Зависимости
| Модуль | Использование |
|---|---|
ll_queue.h |
TOPO_NODEQ — запись в очереди, хеш-индекс по node_id, paths — вложенная очередь |
secure_channel.h |
SC_PUBKEY_SIZE (32) для массивов pubkey/ed25519 |
mem.h |
u_malloc, u_calloc, u_free, u_strdup |
memory_pool.h |
memory_pool_alloc/memory_pool_free для элементов списков (пулы из topo_groups) |
debug_config.h |
DEBUG_INFO, DEBUG_WARN, DEBUG_ERROR, log_dump |
topo_group.h |
struct TOPO_GROUP (аргумент большинства функций) |
etcp.h |
struct ETCP_CONN, struct ETCP_SOCKET |
config_parser.h |
struct CFG_ROUTE_ENTRY, struct CFG_SERVER (при построении local_node) |
route_lib.h |
route_insert(), route_delete() (при обновлении local_node) |
etcp_debug.h |
ip_to_str() (для дампа адресов и подсетей) |
utun_instance.h |
struct UTUN_INSTANCE (аргумент topo_group_update_my_nodeinfo) |