diff --git a/src/routing_layer/topo_node.h b/src/routing_layer/topo_node.h index f9d7f8c0..e3cecd19 100644 --- a/src/routing_layer/topo_node.h +++ b/src/routing_layer/topo_node.h @@ -130,6 +130,9 @@ struct TOPOMSG_REALITY_SOCK { uint8_t server_name[TOPO_REALITY_SNAME_MAX]; } __attribute__((packed)); +/* NODEINFO: этот заголовок, имя без NUL, SOCKMETA4/ADDR4/SOCKMETA6/ADDR6/REALITY, + * опциональные SUBNET4/SUBNET6, затем hop_count ID. Счётчики — число элементов, имя до 63 байт. + * Многобайтные числа wire копируются в нативном порядке; IP — сетевые байты, port в памяти — host order. */ struct TOPOMSG_NODE { uint8_t flags; uint64_t group_id, node_id; uint8_t ver; uint64_t timestamp; // версия всей подписанной записи, Unix microseconds @@ -228,8 +231,10 @@ struct TOPO_GROUP_NODE { // API — глобальный реестр TOPO_NODE /* Заимствованный указатель; ref нужен, если запись должна пережить удаление из группы. */ struct TOPO_NODE* topo_node_registry_find(struct TOPO_GROUPS* groups, uint64_t node_id); -/* Принять ni и вернуть запись с одной новой ссылкой. Может вернуть существующий объект. - * При NULL входной ni остаётся у вызывающего. Подписанные записи сравниваются по timestamp. */ +/* Принять ni и вернуть запись с одной новой ссылкой. При NULL входной ni остаётся у вызывающего. + * Более свежий подписанный снимок заменяет содержимое целиком, сохраняя адрес существующего объекта. + * Старый/равный вход поглощается, возвращается прежняя запись; при двух timestamp=0 сливаются непустые bootstrap-списки. + * Новая запись может быть заменена более свежим снимком SQLite перед регистрацией. */ struct TOPO_NODE* topo_node_registry_store(struct TOPO_GROUPS* groups, struct TOPO_NODE* ni); /* Добавить/снять ссылку; последняя unref удаляет запись из реестра и освобождает её. */ void topo_node_registry_ref(struct TOPO_GROUPS* groups, uint64_t node_id); @@ -301,16 +306,18 @@ uint16_t topo_get_chain_rtt(struct TOPO_GROUP_NODE* nq); /* Лимиты буфера подписываемых данных и полного сетевого NODEINFO. */ #define TOPO_SIG_MSG_MAX_SIZE 8192 #define TOPO_NODE_WIRE_MAX_SIZE 16384 -/* Канонические байты подписи: node_id, ver, timestamp, X25519-ключ, имя, - * тип/активность и полные списки сокетов/адресов/Reality. Длина или -1. */ +/* Канонические байты: node_id(native), ver, timestamp(BE), X25519-ключ, имя до 63 байт + NUL, + * тип/активность, пять счётчиков, SOCKMETA4/ADDR4/SOCKMETA6/ADDR6/REALITY. Порты здесь LE, SNI с NUL. + * Групповые flags/id, подсети, hops и RTT не подписываются. Длина или -1, буфер при ошибке может быть заполнен частично. */ int topo_node_build_sig_msg(struct TOPO_NODE* ni, uint8_t* buf, size_t buf_size); /* Увеличить timestamp (выше текущего/сохранённого), подписать локальный NODEINFO; при наличии SQLite сохранить снимок. * 0/-1; при ошибке возвращает прежние timestamp/подпись, остальные изменённые вызывающим поля не откатывает. */ int topo_node_sign_self(struct UTUN_INSTANCE* instance, struct TOPO_NODE* ni); -/* Проверить timestamp, привязку node_id к ключу и подпись. 0/-1. */ +/* Проверить 0 < timestamp <= INT64_MAX, node_id=derive(X25519) и подпись предоставленным Ed25519-ключом. 0/-1. */ int topo_node_verify(const struct TOPO_NODE* ni); +/* Заимствованные списки: ni обязателен; подсетевые аксессоры допускают r=NULL. */ 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; } diff --git a/tests/test_topo_node_wire.c b/tests/test_topo_node_wire.c index e49f9e08..a3663684 100644 --- a/tests/test_topo_node_wire.c +++ b/tests/test_topo_node_wire.c @@ -2,6 +2,7 @@ #include #include #include +#include #include "utun_instance.h" #include "routing_layer/topo_group.h" #include "routing_layer/topo_node_sqlite.h" @@ -310,8 +311,9 @@ static void test_local_allocation_failures(struct wire_fixture *f) { f->inst.ua = uasync_create(); assert(f->inst.ua); f->groups.node_registry = queue_new(f->inst.ua, 16, 0, 8, "local_wire_test"); assert(f->groups.node_registry); assert(sc_generate_keypair(&f->inst.my_keys) == SC_OK); - memcpy(f->inst.my_ed25519_privkey, f->inst.my_keys.private_key, 32); - sc_derive_ed25519_pubkey(f->inst.my_keys.private_key, f->inst.my_ed25519_pubkey); + uint8_t seed[SHA512_DIGEST_LENGTH]; SHA512(f->inst.my_keys.private_key, 32, seed); + memcpy(f->inst.my_ed25519_privkey, seed, 32); + assert(sc_derive_ed25519_pubkey(f->inst.my_keys.private_key, f->inst.my_ed25519_pubkey) == SC_OK); f->inst.node_id = sc_derive_node_id_from_pubkey(f->inst.my_keys.public_key); f->inst.rt = route_table_create(f->inst.node_id); assert(f->inst.rt); assert(!topo_node_init_self(&f->inst));