From fe9be74e29b34456cdaf579e824bc01a1df2a3b5 Mon Sep 17 00:00:00 2001 From: evgeny Date: Fri, 31 Jul 2026 16:01:47 +0300 Subject: [PATCH] topo refactoring (WIP): topo_node.h + AGENTS.md safety rules --- AGENTS.md | 13 ++ src/routing_layer/topo_node.h | 245 ++++++++++------------------------ 2 files changed, 84 insertions(+), 174 deletions(-) diff --git a/AGENTS.md b/AGENTS.md index a9b467bd..c2860d8b 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -446,6 +446,19 @@ KEEPALIVE=21, ETCPROUTE=22, BBR=23, ETCP_DUMP=24 Диагностические сообщения - важный момент. надо про диагностику помнить. Для хорошей диагностики надо проанализировать как архитектор - какие есть нюансы в архитектуре и что полезно будет видеть в дебаг выводе чтобы понять полную картину происходящего. +### Запрещено (опыт 2026-07-31 — потеря всех некоммиченных изменений): +- `git checkout -- ` / `git checkout -- src/` — безвозвратный откат ВСЕХ файлов +- `sed` для массовых правок исходников — замены разъезжаются, ломают код, часы на восстановление +- Пакетные замены через Bash в 10+ файлах одновременно +- Повторять эти ошибки: потеря + попытка отката git checkout = двойная потеря + +### Правила безопасного рефакторинга: +1. Правки ТОЛЬКО через Edit tool — одна точечная замена с контекстом +2. После каждого файла → `git add ` +3. Каждые 2-3 файла → `git commit -m "..."` (не дожидаясь сборки) +4. Никакого `sed` / `awk` / `perl -i` для правки исходников +5. Перед откатом → сначала коммит всего, потом `git revert` конкретного коммита + ### Прочие правила: - sed для редактирования исходников - запрещено - Проверяй на дублирование кода - не сделано ли это уже в другом месте diff --git a/src/routing_layer/topo_node.h b/src/routing_layer/topo_node.h index c6efd9b7..3efb6a3a 100644 --- a/src/routing_layer/topo_node.h +++ b/src/routing_layer/topo_node.h @@ -3,20 +3,20 @@ * @brief Модель данных узла сети — структуры, сериализация, wire-формат. * * Этот модуль определяет: - * - Как узел представлен в памяти (TOPO_NODE, TOPO_NODEQ, адреса, подсети) + * - Как узел представлен в памяти (TOPO_NODE, TOPO_GROUP_NODE, адреса, подсети) * - Как узел передаётся по сети (packed-структуры TOPOMSG_*) * - Как узел сериализуется/десериализуется (topo_node_serialize/deserialize) * - * Модуль не содержит сетевой логики и BGP-обмена — этим занимается topo_group. - * - * Хранение: данные узлов сохраняются в SQLite (таблицы nodes, node_addresses) - * через модуль topo_node_sqlite. + * Хранение: + * - Идентичность узла (pubkeys, адреса, имя) — в глобальном реестре TOPO_GROUPS->node_registry + * Единственный экземпляр TOPO_NODE, разделяется между группами через group_ref_count. + * - Per-group данные (node_id, paths, subnets, connectivity) — в TOPO_GROUP->nodes (очередь TOPO_GROUP_NODE) * * Основные понятия: - * TOPO_NODE — идентичность узла (pubkeys, имя, версия, списки адресов) - * TOPO_NODEQ — запись в очереди группы (плюс пути, hop_list, связность) - * TOPO_NODEPATH — путь до узла через конкретное ETCP-соединение - * TOPOMSG_NODE — wire-формат, передаваемый по NODEINFO + * TOPO_NODE — глобальная идентичность узла. Встроен в node_registry как queue entry. + * TOPO_GROUP_NODE — узел в контексте группы: node_id + paths, hop_list, связность + * TOPO_NODEPATH — путь до узла через конкретное ETCP-соединение + * TOPOMSG_NODE — wire-формат, передаваемый по NODEINFO */ #ifndef TOPO_NODE_H #define TOPO_NODE_H @@ -33,29 +33,20 @@ extern "C" { struct TOPO_GROUP; struct TOPO_GROUPS; struct ETCP_SOCKET; +struct ETCP_CONN; struct UTUN_INSTANCE; -// ---- типы адресов в плоском списке TOPOMSG_ADDR4 ---- - -#define TOPO_ADDR_INTERFACE 0 // interface_addr сокета (LAN) -#define TOPO_ADDR_NAT 1 // nat_addr после детекции NAT -#define TOPO_ADDR_REAL 2 // подтверждённый прямой интернет-адрес (nat проверка показала совпадение с interface_addr) +#define TOPO_ADDR_INTERFACE 0 +#define TOPO_ADDR_NAT 1 +#define TOPO_ADDR_REAL 2 -// ---- протоколы транспорта (поле protocol в TOPOMSG_ADDR4/TOPOMSG_ADDR6) ---- #define TOPO_PROTO_UDP 0x01 #define TOPO_PROTO_TCP 0x02 -// ---- типы NAT (etcp_connections.h) ---- -// NAT_TYPE_UNKNOWN(0), NAT_TYPE_EIM(1), NAT_TYPE_STRICT(2) -// NAT_VERIFIED_UNKNOWN(4), NAT_VERIFIED_EIM(5), NAT_VERIFIED_STRICT(6), NAT_VERIFIED_DIRECT(7) - -// ---- типы адресов для БД (node_addresses.addr_type) ---- -#define ADDR_TYPE_NETIF 0 // interface / auto-detected -#define ADDR_TYPE_DIRECT 1 // прямой публичный адрес (detected no NAT) -#define ADDR_TYPE_NAT_EIM 2 // EIM NAT -#define ADDR_TYPE_NAT_STRICT 3 // strict NAT - -// ---- статусы зондирования связности ---- +#define ADDR_TYPE_NETIF 0 +#define ADDR_TYPE_DIRECT 1 +#define ADDR_TYPE_NAT_EIM 2 +#define ADDR_TYPE_NAT_STRICT 3 #define PROBE_STATUS_NONE 0 #define PROBE_STATUS_IN_PROGRESS 1 @@ -74,18 +65,11 @@ struct UTUN_INSTANCE; #define CONN_TYPE_INDIRECT 3 #define CONN_MGR_MAX_INTERMEDIARIES 3 -// ---- флаги nodeinfo ---- #define TOPO_FLAG_SEND_SUBNETS 0x01 - -// ---- group_id по умолчанию ---- #define TOPO_GROUP_UTUN 0x8000000000000000ULL - -// ---- типы групп ---- #define TOPO_GROUP_TYPE_UTUN 1 #define TOPO_GROUP_TYPE_CHAT 2 -// ---- состояние связности с удалённым узлом (локальное, не передаётся по BGP) ---- - struct TOPO_CONNECTIVITY { uint8_t probe_status; uint8_t pending_count; @@ -101,88 +85,30 @@ struct TOPO_CONNECTIVITY { uint64_t nat_probe_time; uint64_t real_probe_time; uint64_t ping_req_time; - uint64_t last_ping_time; // время последнего успешного пинга (0.1ms timebase) + uint64_t last_ping_time; void* probe_list; }; -#define PING_TTL_TB (86400ULL * 10000ULL) // 1 день в 0.1ms units - -// ======================================================================= -// Протокольные структуры (wire-format, packed, без next) -// ======================================================================= - -struct TOPOMSG_SOCKMETA4 { - uint8_t id; - uint8_t config_type; - uint8_t nat_type; -} __attribute__((packed)); - -struct TOPOMSG_ADDR4 { - uint8_t addr[4]; - uint16_t port; - uint8_t type; - uint8_t socket_id; - uint8_t protocol; -} __attribute__((packed)); - -struct TOPOMSG_SOCKMETA6 { - uint8_t id; - uint8_t config_type; - uint8_t nat_type; -} __attribute__((packed)); - -struct TOPOMSG_ADDR6 { - uint8_t addr[16]; - uint16_t port; - uint8_t type; - uint8_t socket_id; - uint8_t protocol; -} __attribute__((packed)); - -struct TOPOMSG_SUBNET4 { - uint8_t addr[4]; - uint8_t prefix_length; -} __attribute__((packed)); +#define PING_TTL_TB (86400ULL * 10000ULL) -struct TOPOMSG_SUBNET6 { - uint8_t addr[16]; - uint8_t prefix_length; -} __attribute__((packed)); - -struct TOPOMSG_TRANZIT { - uint64_t node_id; - uint16_t rtt; - uint16_t link_q; -} __attribute__((packed)); +struct TOPOMSG_SOCKMETA4 { uint8_t id, config_type, nat_type; } __attribute__((packed)); +struct TOPOMSG_ADDR4 { uint8_t addr[4]; uint16_t port; uint8_t type, socket_id, protocol; } __attribute__((packed)); +struct TOPOMSG_SOCKMETA6 { uint8_t id, config_type, nat_type; } __attribute__((packed)); +struct TOPOMSG_ADDR6 { uint8_t addr[16]; uint16_t port; uint8_t type, socket_id, protocol; } __attribute__((packed)); +struct TOPOMSG_SUBNET4 { uint8_t addr[4]; uint8_t prefix_length; } __attribute__((packed)); +struct TOPOMSG_SUBNET6 { uint8_t addr[16]; uint8_t prefix_length; } __attribute__((packed)); +struct TOPOMSG_TRANZIT { uint64_t node_id; uint16_t rtt, link_q; } __attribute__((packed)); -/** - * @brief Протокольный формат NODEINFO сообщения (wire-format) - */ struct TOPOMSG_NODE { - uint8_t flags; - uint64_t group_id; - uint64_t node_id; - uint8_t ver; - uint8_t public_key[SC_PUBKEY_SIZE]; - uint8_t ed25519_public_key[SC_PUBKEY_SIZE]; - uint8_t x25519_self_sig[64]; // Ed25519_sign(ed25519_privkey, x25519_pubkey, 32) - uint8_t node_name_len; - uint8_t local_v4_sockets; - uint8_t local_v4_addrs; - uint8_t local_v6_sockets; - uint8_t local_v6_addrs; - uint8_t local_v4_subnets; - uint8_t local_v6_subnets; - uint8_t tranzit_nodes; - uint8_t hop_count; - uint16_t cumulative_rtt; // сумма RTT upstream (0.1ms), без последнего линка + uint8_t flags; uint64_t group_id, node_id; uint8_t ver; + uint8_t public_key[SC_PUBKEY_SIZE], ed25519_public_key[SC_PUBKEY_SIZE]; + uint8_t x25519_self_sig[64]; + uint8_t node_name_len, local_v4_sockets, local_v4_addrs, local_v6_sockets, local_v6_addrs; + uint8_t local_v4_subnets, local_v6_subnets, tranzit_nodes, hop_count; + uint16_t cumulative_rtt; } __attribute__((packed)); -#define TOPOMSG_NODE_HDR_SIZE sizeof(struct TOPOMSG_NODE) - -// ======================================================================= -// Структуры для хранения в памяти (linked list с next* первым полем) -// ======================================================================= +#define TOPOMSG_NODE_HDR_SIZE sizeof(struct TOPOMSG_NODE) struct TOPO_SOCKMETA4 { struct TOPO_SOCKMETA4* next; uint8_t id, config_type, nat_type; }; struct TOPO_ADDR4 { struct TOPO_ADDR4* next; uint8_t addr[4]; uint16_t port; uint8_t type, socket_id, protocol; }; @@ -191,35 +117,25 @@ struct TOPO_ADDR6 { struct TOPO_ADDR6* next; uint8_t addr[16]; uint16_t port struct TOPO_SUBNET4 { struct TOPO_SUBNET4* next; uint8_t addr[4]; uint8_t prefix_length; }; struct TOPO_SUBNET6 { struct TOPO_SUBNET6* next; uint8_t addr[16]; uint8_t prefix_length; }; -// Универсальная голова списка (next — первое поле во всех TOPO_*) struct _topo_head { struct _topo_head* next; }; - static inline int topo_list_count(struct _topo_head* head) { int n = 0; while (head) { n++; head = head->next; } return n; } -/** - * @brief Идентификационная информация об узле (память, разделяется между группами) - */ +/** Глобальная идентичность узла. Встроен в TOPO_GROUPS->node_registry как queue entry. */ struct TOPO_NODE { - uint32_t ref_count; - uint8_t flags; - uint64_t group_id; - uint64_t node_id; - uint8_t ver; - uint8_t public_key[SC_PUBKEY_SIZE]; - uint8_t ed25519_public_key[SC_PUBKEY_SIZE]; - uint8_t x25519_self_sig[64]; // Ed25519_sign(ed25519_privkey, x25519_pubkey, 32) - char* node_name; // отдельный u_strdup/u_free - struct TOPO_SOCKMETA4* v4_sock_meta; // linked list head - struct TOPO_ADDR4* v4_addrs; // linked list head + struct ll_entry ll; + uint32_t group_ref_count; + uint8_t flags; uint64_t group_id, node_id; uint8_t ver; + uint8_t public_key[SC_PUBKEY_SIZE], ed25519_public_key[SC_PUBKEY_SIZE]; + uint8_t x25519_self_sig[64]; + char* node_name; + struct TOPO_SOCKMETA4* v4_sock_meta; + struct TOPO_ADDR4* v4_addrs; struct TOPO_SOCKMETA6* v6_sock_meta; struct TOPO_ADDR6* v6_addrs; }; -/** - * @brief Подсети узла (отдельный malloc, NULL если нет подсетей) - */ struct TOPO_NODESUBNETS { - struct TOPO_SUBNET4* v4_subnets; // linked list head + struct TOPO_SUBNET4* v4_subnets; struct TOPO_SUBNET6* v6_subnets; }; @@ -227,76 +143,57 @@ struct TOPO_NODEPATH { struct ll_entry ll; struct ETCP_CONN* conn; uint8_t hop_count; - uint16_t cumulative_rtt; // сумма RTT upstream (0.1ms) для этого пути + uint16_t cumulative_rtt; }; -struct TOPO_NODEQ { +/** Узел в контексте группы (per-group данные). Хранится в TOPO_GROUP->nodes. */ +struct TOPO_GROUP_NODE { struct ll_entry ll; - uint64_t hash_node_id; // копия node_id для хеш-индекса - struct TOPO_NODE* node; // отдельный malloc, ref_count - struct TOPO_NODESUBNETS* subnets; // отдельный malloc (NULL если без подсетей) - struct TOPOMSG_TRANZIT* tranzit_data; // отдельный malloc - uint64_t* hop_list; // отдельный malloc - uint8_t tranzit_count; - uint8_t hop_count; - uint16_t cumulative_rtt; // база из последнего NODEINFO для пересылки + uint64_t node_id; + struct TOPO_NODESUBNETS* subnets; + struct TOPOMSG_TRANZIT* tranzit_data; + uint64_t* hop_list; + uint8_t tranzit_count, hop_count; + uint16_t cumulative_rtt; struct ll_queue* paths; - uint8_t dirty; - uint8_t last_ver; - uint8_t conn_mgr_type; + uint8_t dirty, last_ver, conn_mgr_type; uint64_t conn_mgr_intermediaries[CONN_MGR_MAX_INTERMEDIARIES]; uint8_t conn_mgr_intermediariy_count; struct TOPO_CONNECTIVITY connectivity; struct ETCP_SOCKET* best_socket; }; +// API — глобальный реестр TOPO_NODE +struct TOPO_NODE* topo_node_registry_find(struct TOPO_GROUPS* groups, uint64_t node_id); +struct TOPO_NODE* topo_node_registry_store(struct TOPO_GROUPS* groups, struct TOPO_NODE* ni); +void topo_node_registry_ref(struct TOPO_GROUPS* groups, uint64_t node_id); +void topo_node_registry_unref(struct TOPO_GROUPS* groups, uint64_t node_id); +void topo_node_destroy(struct TOPO_GROUPS* groups, struct TOPO_NODE* ni); -// ======================================================================= -// API -// ======================================================================= - -struct TOPO_NODEQ* topo_node_find_by_id(struct TOPO_GROUP* group, uint64_t node_id); - -// Владение TOPO_NODE: создаётся с ref_count=0. Владелец вызывает topo_node_ref() чтобы завладеть. -// При отказе от владения — topo_node_unref(). При ref_count=0 узел удаляется. -void topo_node_ref(struct TOPO_NODE* ni); -void topo_node_unref(struct TOPO_NODE* ni); - -// Глобальный реестр TOPO_NODE* (по node_id). Каждый узел хранится один раз. -// При помещении в реестр и при получении из реестра владение берётся (ref++). -// При освобождении из реестра владение возвращается (unref, при ref=0 — удаление). -struct TOPO_NODE* topo_node_registry_acquire(struct TOPO_GROUPS* groups, struct TOPO_NODE* ni); -void topo_node_registry_release(struct TOPO_GROUPS* groups, struct TOPO_NODE* ni); +// API — per-group +struct TOPO_GROUP_NODE* topo_node_find_by_id(struct TOPO_GROUP* group, uint64_t node_id); +void topo_nodeq_free_group_fields(struct TOPO_GROUPS* groups, struct TOPO_GROUP_NODE* nq); -// Освободить узел без учёта ref_count (для ошибок после deserialize, пока ref=0). -void topo_node_free_raw(struct TOPO_GROUPS* groups, struct TOPO_NODE* ni); - -void topo_node_free_lists(struct TOPO_GROUP* group, struct TOPO_NODEQ* nq); int topo_node_dyn_size(const struct TOPOMSG_NODE* msg); - -// convenience accessors -static inline const struct TOPO_SOCKMETA4* topo_v4_sock_meta(const struct TOPO_NODE* ni) { return ni->v4_sock_meta; } -static inline const struct TOPO_ADDR4* topo_v4_addrs(const struct TOPO_NODE* ni) { return ni->v4_addrs; } -static inline const struct TOPO_SOCKMETA6* topo_v6_sock_meta(const struct TOPO_NODE* ni) { return ni->v6_sock_meta; } -static inline const struct TOPO_ADDR6* topo_v6_addrs(const struct TOPO_NODE* ni) { return ni->v6_addrs; } -static inline const struct TOPO_SUBNET4* topo_v4_subnets(const struct TOPO_NODESUBNETS* r) { return r ? r->v4_subnets : NULL; } -static inline const struct TOPO_SUBNET6* topo_v6_subnets(const struct TOPO_NODESUBNETS* r) { return r ? r->v6_subnets : NULL; } - -int topo_node_serialize(struct TOPO_GROUP* group, struct TOPO_NODEQ* nq, uint8_t* out, size_t out_max, uint16_t cumulative_rtt); +int topo_node_serialize(struct TOPO_NODE* ni, struct TOPO_GROUP_NODE* nq, uint8_t* out, size_t out_max, uint16_t cumulative_rtt); int topo_node_deserialize(struct TOPO_GROUP* group, const uint8_t* data, size_t len, struct TOPO_NODE** out_ni, struct TOPO_NODESUBNETS** out_subnets, struct TOPOMSG_TRANZIT** out_tranzit, uint8_t* out_tranzit_count, uint64_t** out_hop_list, uint8_t* out_hop_count, uint16_t* out_cumulative_rtt); int topo_group_update_my_nodeinfo(struct UTUN_INSTANCE* instance, struct TOPO_GROUP* group); - void topo_node_dump_all(struct TOPO_GROUP* group); int topo_node_format_all(struct TOPO_GROUP* group, char* buf, size_t buf_size); - int topo_node_ping_request_cbk(struct TOPO_GROUPS* groups, uint64_t node_id); void topo_node_ping_update_rtt(struct TOPO_GROUPS* groups, uint64_t node_id, uint16_t rtt); +static inline const struct TOPO_SOCKMETA4* topo_v4_sock_meta(const struct TOPO_NODE* ni) { return ni->v4_sock_meta; } +static inline const struct TOPO_ADDR4* topo_v4_addrs(const struct TOPO_NODE* ni) { return ni->v4_addrs; } +static inline const struct TOPO_SOCKMETA6* topo_v6_sock_meta(const struct TOPO_NODE* ni) { return ni->v6_sock_meta; } +static inline const struct TOPO_ADDR6* topo_v6_addrs(const struct TOPO_NODE* ni) { return ni->v6_addrs; } +static inline const struct TOPO_SUBNET4* topo_v4_subnets(const struct TOPO_NODESUBNETS* r) { return r ? r->v4_subnets : NULL; } +static inline const struct TOPO_SUBNET6* topo_v6_subnets(const struct TOPO_NODESUBNETS* r) { return r ? r->v6_subnets : NULL; } + #ifdef __cplusplus } #endif - -#endif // TOPO_NODE_H +#endif