# 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. Как пользоваться ### Типовые сценарии **Создание/обновление информации о себе:** ```c topo_group_update_my_nodeinfo(instance, group); ``` Строит `TOPO_NODEQ` (в `group->local_node`) из конфига: собирает список сокетов, адресов (interface/NAT/real), подсетей; при изменениях инкрементирует `ver` и помечает `dirty=1`. Для chat-групп (`TOPO_GROUP_TYPE_CHAT`) подсети не включаются. **Сериализация для отправки по BGP:** ```c 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:** ```c 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_name` - `topo_node_free_lists(group, nq)` — освобождает списки обратно в пулы, а также `subnets`, `tranzit_data`, `hop_list` **Поиск узла по ID:** ```c struct TOPO_NODEQ *nq = topo_node_find_by_id(group, node_id); ``` Использует хеш-индекс очереди `group->nodes` (ключ — `node_id`, 8 байт). **Дамп всех узлов:** ```c 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`) |