# topo_node ## 1. Назначение Модель данных узла топологии — структуры для представления узла в памяти, сериализации/ десериализации в бинарный wire-формат для BGP-обмена, глобальный реестр идентичностей (refcounting) и диагностический дамп. Модуль определяет два слоя: - **Память:** связанные списки (`TOPO_ADDR4`, `TOPO_SOCKMETA4`, `TOPO_SUBNET4` и др.) и две структуры узла — `TOPO_NODE` (глобальная идентичность) и `TOPO_GROUP_NODE` (per-group данные). - **Wire-формат:** packed-структуры (`TOPOMSG_NODE`, `TOPOMSG_ADDR4` и др.) для передачи по сети. Сам модуль не содержит сетевой логики BGP-обмена — этим занимается `topo_group`. Здесь только данные и их упаковка/распаковка. Персистентность — в `topo_node_sqlite`. Два уровня представления узла: - **TOPO_NODE** — глобальная идентичность (pubkeys, имя, адреса, self-signature). Хранится один раз в глобальном реестре `TOPO_GROUPS->node_registry` (hash по node_id), разделяется между группами через `group_ref_count`. - **TOPO_GROUP_NODE** — узел в контексте группы: `node_id`, `paths`, `subnets`, `last_ver`, `connectivity`, `conn_presence/conn_up`, `handle`. Хранится в `TOPO_GROUP->nodes`. ## 2. Как пользоваться ### Типовые сценарии **Сборка информации о себе:** ```c topo_group_update_my_nodeinfo(instance, group); ``` Строит `group->local_node` (`TOPO_GROUP_NODE`) из конфига: сокеты, адреса (interface/NAT/real), подсети; при изменениях инкрементирует `ver`. Для chat-групп (`TOPO_GROUP_TYPE_CHAT`) подсети не включаются. **Сериализация для отправки по BGP:** ```c struct TOPO_NODE* ni = topo_node_registry_find(groups, nq->node_id); uint8_t buf[4096]; int len = topo_node_serialize(ni, nq, group_id, flags, buf, sizeof(buf), cumulative_rtt); ``` Запаковывает `TOPO_NODE` + per-group данные (`TOPO_GROUP_NODE`) в буфер: заголовок `TOPOMSG_NODE`, затем имя узла, v4/v6 sock_meta, v4/v6 адреса, подсети (если `TOPO_FLAG_SEND_SUBNETS`) и hop-лист (через `topo_node_best_hop_list`). **Десериализация при получении NODEINFO:** ```c struct TOPO_NODE* ni = NULL; struct TOPO_NODESUBNETS* subnets = NULL; uint64_t* hop_list = NULL; uint8_t hop_count = 0; uint16_t cumulative_rtt = 0; topo_node_deserialize(group, data, data_len, &ni, &subnets, &hop_list, &hop_count, &cumulative_rtt); ``` Восстанавливает `TOPO_NODE` (через `u_calloc`), подсети и hop-лист. Освобождение: - `topo_node_destroy(groups, ni)` — освободить `TOPO_NODE` (списки адресов + имя). - `topo_nodeq_free_group_fields(groups, nq)` — освободить per-group поля (`subnets`, `paths`-элементы). **Поиск узла по ID:** ```c struct TOPO_GROUP_NODE* nq = topo_node_find_by_id(group, node_id); // per-group struct TOPO_NODE* ni = topo_node_registry_find(groups, node_id); // глобальная идентичность ``` **Дамп всех узлов:** ```c topo_node_dump_all(group); // в лог topo_node_format_all(group, buf, size); // в строку ``` ### Ключевые концепции - **Глобальный реестр**: `TOPO_NODE` хранится один раз в `node_registry`; `topo_node_registry_store` дедуплицирует по `node_id` (возвращает существующий при совпадении). `registry_ref/unref` управляют `group_ref_count`; при 0 — `topo_node_destroy`. - **Путь**: `TOPO_NODEPATH = {conn, hop_count, cumulative_rtt}` + inline `hop_list[hop_count]` сразу после структуры. Полный hop-лист для отправки — `topo_node_best_hop_list`. - **Self-signature**: `x25519_self_sig` — Ed25519-подпись над каноническим сообщением (`topo_node_build_sig_msg`: pubkey || имя || client_type || client_activity || адреса). Ставится `topo_node_sign_self`, проверяется в `topo_group_process_nodeinfo`. - **Адреса трёх типов**: `TOPO_ADDR_INTERFACE` (LAN), `TOPO_ADDR_NAT` (после детекции), `TOPO_ADDR_REAL` (подтверждённый прямой). Для каждого сокета — до 3 адресов. - **TOPO_CONNECTIVITY** — локальное состояние связности (не передаётся по BGP): probe_status (NONE/IN_PROGRESS/DONE/DEFERRED), RTT и статусы interface/nat/real, `probe_deferred_wait`. - **TOPO_FLAG_SEND_SUBNETS** — если установлен, подсети сериализуются; для chat-групп сброшен. - **Wire-протокол**: заголовок `TOPOMSG_NODE` (packed) + динамика: `node_name`, `SOCKMETA4*N`, `ADDR4*N`, `SOCKMETA6*N`, `ADDR6*N`, опционально подсети, hop-лист. Размер динамики — `topo_node_dyn_size()`. ## 3. API ### Основные структуры | Структура | Назначение | |-----------|-----------| | `struct TOPO_NODE` | Глобальная идентичность узла: `flags`, `group_id`, `node_id`, `ver`, `public_key`, `ed25519_public_key`, `x25519_self_sig`, `client_type`, `client_activity`, `node_name`, списки v4/v6 sock_meta и addrs, `group_ref_count` | | `struct TOPO_GROUP_NODE` | Per-group запись: `node_id`, `subnets`, `paths` (queue TOPO_NODEPATH), `last_ver`, `conn_mgr_*`, `connectivity`, `conn_presence`, `conn_up`, `handle` (ncd) | | `struct TOPO_NODEPATH` | Путь через ETCP_CONN: `{conn, hop_count, cumulative_rtt}` + inline `hop_list[]` | | `struct TOPO_NODESUBNETS` | v4/v6 подсети узла (отдельный malloc, может быть NULL) | | `struct TOPO_CONNECTIVITY` | Связность: probe_status, RTT, статусы interface/nat/real, probe-тайминги | | `struct TOPOMSG_NODE` | Packed-заголовок wire-формата: счётчики, `hop_count`, `client_type/activity`, `cumulative_rtt` | | `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 TOPO_SOCKMETA4/6` | Элемент списка сокет-метаданных (next*, id, config_type, nat_type) | | `struct TOPO_ADDR4/6` | Элемент списка адресов (next*, addr, port, type, socket_id, protocol) | | `struct TOPO_SUBNET4/6` | Элемент списка подсетей (next*, addr, prefix_length) | Элементы списков аллоцируются из memory pool'ов (`topo_groups`) через `memory_pool_alloc/free`. ### Константы | Константа | Значение | Назначение | |-----------|---------|-----------| | `TOPO_ADDR_INTERFACE` / `TOPO_ADDR_NAT` / `TOPO_ADDR_REAL` | 0/1/2 | Тип адреса | | `TOPO_PROTO_UDP` / `TOPO_PROTO_TCP` | 0x01/0x02 | Транспорт | | `ADDR_TYPE_NETIF/DIRECT/NAT_EIM/NAT_STRICT` | 0/1/2/3 | Классификация адреса | | `PROBE_STATUS_NONE/IN_PROGRESS/DONE/DEFERRED` | 0/1/2/3 | Состояние пробы | | `PROBE_RESULT_UNKNOWN/REACHABLE/UNREACHABLE` | 0/1/2 | Результат пробы | | `CONN_TYPE_NONE/DIRECT/REVERSE/INDIRECT` | 0/1/2/3 | Стратегия соединения | | `CLIENT_TYPE_SERVER/DESKTOP/MOBILE` | 0/1/2 | Тип клиента | | `CLIENT_ACTIVITY_STANDBY/ACTIVE` | 0/1 | Режим | | `NCONN_DIRECT/INDIRECT/BGP` | 1/2/4 | Биты наличия соединений | | `TOPO_FLAG_SEND_SUBNETS` | 0x01 | Отправлять подсети | | `TOPO_GROUP_UTUN` | `0x8000000000000000ULL` | ID utun-группы по умолчанию | | `TOPO_GROUP_TYPE_UTUN/CHAT` | 1/2 | Тип группы | | `CONN_MGR_MAX_INTERMEDIARIES` | 3 | Максимум посредников | ### Функции **Реестр и refcounting:** - `topo_node_registry_find(groups, node_id)` — найти `TOPO_NODE` в глобальном реестре. - `topo_node_registry_store(groups, ni)` — сохранить с дедупликацией (возвращает существующий при совпадении). - `topo_node_registry_ref/unref(groups, node_id)` — инкремент/декремент `group_ref_count`. - `topo_node_destroy(groups, ni)` — освободить `TOPO_NODE` (списки адресов + имя). **Per-group:** - `topo_node_find_by_id(group, node_id)` — найти `TOPO_GROUP_NODE` по node_id (hash). - `topo_nodeq_free_group_fields(groups, nq)` — освободить per-group поля (subnets, paths) + unref глобального TOPO_NODE. **Сериализация:** - `topo_node_dyn_size(msg)` — размер динамической части по счётчикам заголовка. - `topo_node_serialize(ni, nq, group_id, flags, out, out_max, cumulative_rtt)` — сериализовать `TOPO_NODE` + per-group в буфер. - `topo_node_deserialize(group, data, len, &out_ni, &out_subnets, &out_hop_list, &out_hop_count, &out_cumulative_rtt)` — восстановить из wire-формата. **Пути:** - `topo_node_best_hop_list(nq, &out_count, &out_rtt)` — лучший hop-лист (по min hop_count). **Подпись:** - `topo_node_build_sig_msg(ni, buf, buf_size)` — каноническое сообщение для Ed25519-подписи. - `topo_node_sign_self(instance, ni)` — подписать свой NODEINFO (`x25519_self_sig`). **Обновление о себе:** - `topo_group_update_my_nodeinfo(instance, group)` — перестроить `local_node`. - `topo_node_update_my_addresses(instance)` — обновить адреса. - `topo_node_on_socket_changed(sock, event, arg)` — обработчик изменений сокетов. **Диагностика:** - `topo_node_dump_all(group)` / `topo_node_format_all(group, buf, size)` — дамп узлов. **Inline-аксессоры (.h):** - `topo_v4_sock_meta/addr`, `topo_v6_sock_meta/addr`, `topo_v4_subnets/topo_v6_subnets`, `topo_list_count`. ### Зависимости | Модуль | Использование | |--------|--------------| | `ll_queue.h` | `TOPO_GROUP_NODE` — запись в очереди (hash по node_id), `paths` — вложенная очередь | | `secure_channel.h` | `SC_PUBKEY_SIZE` (32) | | `mem.h` | `u_malloc`, `u_calloc`, `u_free` | | `memory_pool.h` | Элементы списков из пулов `topo_groups` | | `debug_config.h` | Логирование (BGP) | | `topo_group.h` | `struct TOPO_GROUP`, `TOPO_GROUPS` | | `route_lib.h` | `route_insert/route_delete` | | `utun_instance.h` | `struct UTUN_INSTANCE` |