Browse Source

topo refactoring (WIP): topo_node.h + AGENTS.md safety rules

topo_upd
evgeny 2 months ago
parent
commit
fe9be74e29
  1. 13
      AGENTS.md
  2. 239
      src/routing_layer/topo_node.h

13
AGENTS.md

@ -446,6 +446,19 @@ KEEPALIVE=21, ETCPROUTE=22, BBR=23, ETCP_DUMP=24
Диагностические сообщения - важный момент. надо про диагностику помнить.
Для хорошей диагностики надо проанализировать как архитектор - какие есть нюансы в архитектуре и что полезно будет видеть в дебаг выводе чтобы понять полную картину происходящего.
### Запрещено (опыт 2026-07-31 — потеря всех некоммиченных изменений):
- `git checkout -- <wildcard>` / `git checkout -- src/` — безвозвратный откат ВСЕХ файлов
- `sed` для массовых правок исходников — замены разъезжаются, ломают код, часы на восстановление
- Пакетные замены через Bash в 10+ файлах одновременно
- Повторять эти ошибки: потеря + попытка отката git checkout = двойная потеря
### Правила безопасного рефакторинга:
1. Правки ТОЛЬКО через Edit tool — одна точечная замена с контекстом
2. После каждого файла → `git add <file>`
3. Каждые 2-3 файла → `git commit -m "..."` (не дожидаясь сборки)
4. Никакого `sed` / `awk` / `perl -i` для правки исходников
5. Перед откатом → сначала коммит всего, потом `git revert` конкретного коммита
### Прочие правила:
- sed для редактирования исходников - запрещено
- Проверяй на дублирование кода - не сделано ли это уже в другом месте

239
src/routing_layer/topo_node.h

@ -3,18 +3,18 @@
* @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_NODE — глобальная идентичность узла. Встроен в node_registry как queue entry.
* TOPO_GROUP_NODE — узел в контексте группы: node_id + paths, hop_list, связность
* TOPO_NODEPATH — путь до узла через конкретное ETCP-соединение
* TOPOMSG_NODE — wire-формат, передаваемый по NODEINFO
*/
@ -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,89 +85,31 @@ 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* первым полем)
// =======================================================================
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; };
struct TOPO_SOCKMETA6 { struct TOPO_SOCKMETA6* next; uint8_t id, config_type, nat_type; };
@ -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);
// 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);
// Глобальный реестр 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);
// Освободить узел без учёта 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

Loading…
Cancel
Save