Files
utun2/tests/nat_fixture.h
T

104 lines
6.9 KiB
C
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/* NAT-стенд с настоящими ядрами/UDP/NCD и одной общей uasync: init → node/connect → cleanup.
* До 8 узлов и 24 NCD handles; фикстура владеет ими, caller получает заимствованные указатели.
* Запускает core и создаёт UTUN topo-группу, но не UTUN/chat сервисы. Управление только в общем uasync-потоке.
* Проверки и обязательные вызовы находятся в assert: собирать без NDEBUG. Отказы завершают тест.
* Cleanup вызывается один раз после завершения внешних операций; раннее уничтожение узлов/handles вручную запрещено. */
#ifndef NAT_FIXTURE_H
#define NAT_FIXTURE_H
#include <assert.h>
#include <stdio.h>
#include <string.h>
#include "test_net.h"
#include "utun_instance.h"
#include "config_parser.h"
#include "transport_layer/etcp.h"
#include "transport_layer/node_conn_direct.h"
#include "routing_layer/topo_group.h"
#include "routing_layer/topo_node_sqlite.h"
#include "routing_layer/nat_detection.h"
#include "routing_layer/route_ping.h"
#include "../lib/u_async.h"
#include "../lib/mem.h"
struct nat_fixture {
struct UASYNC *ua; // Собственный event loop для всех узлов.
struct UTUN_INSTANCE *nodes[8]; // Собственные ядра в порядке создания.
struct NODE_CONN_DIRECT *handles[24]; // Собственные NCD handles явных соединений.
unsigned node_count, handle_count; // Число занятых элементов соответствующих массивов.
size_t allocations; // Снимок u_* allocations до init для проверки полного cleanup.
};
// Обнулить свежую фикстуру, сохранить allocation baseline и создать uasync; повторно поверх живого стенда не вызывать.
static inline void nat_fixture_init(struct nat_fixture *fixture) {
memset(fixture, 0, sizeof(*fixture)); fixture->allocations = u_get_allocated_count();
fixture->ua = uasync_create(); assert(fixture->ua);
}
/* Создать/запустить узел: ip — локальный bind IP, порт UDP выбирает ОС; keys=NULL генерирует новую X25519-пару.
* extra — добавляемый INI-текст либо NULL, может задавать дополнительные серверы/настройки. auto_sockets=no, NTP выключен.
* Для NAT разрешаются локальные адреса, создаётся UTUN-группа; возвращённый instance принадлежит fixture. */
static inline struct UTUN_INSTANCE *nat_fixture_node(struct nat_fixture *fixture, const char *ip,
const struct SC_MYKEYS *keys, const char *extra) {
struct SC_MYKEYS generated;
if (!keys) { assert(sc_generate_keypair(&generated) == SC_OK); keys = &generated; }
char private_key[65], public_key[65], config[4096];
for (unsigned i = 0; i < 32; i++) {
snprintf(private_key + i * 2, 3, "%02x", keys->private_key[i]);
snprintf(public_key + i * 2, 3, "%02x", keys->public_key[i]);
}
snprintf(config, sizeof(config), "[global]\nmy_private_key=%s\nmy_public_key=%s\nauto_sockets=no\n"
"[ntp]\nenabled=no\n[server:udp]\naddr=%s:0\ntype=public\n[allowed_keys]\nallow_all=1\n%s",
private_key, public_key, ip, extra ? extra : "");
struct UTUN_INSTANCE *node = utun_instance_create_from_str(fixture->ua, config);
assert(node && fixture->node_count < 8 && utun_core_start(node) == 0);
nat_detection_set_allow_local(node->nat_det, 1);
assert(topo_groups_create_group(node->topo_groups, TOPO_GROUP_UTUN, TOPO_GROUP_TYPE_UTUN, ""));
fixture->nodes[fixture->node_count++] = node; return node;
}
/* Явно соединить from→to через локальный listener from с именем source (UDP либо TCP).
* Удерживает собственный NCD handle, ждёт links_up до 5с, затем READY UTUN-группы с обеих сторон ещё до 5с.
* Возвращает заимствованный ETCP_CONN; ожидания обслуживают общий uasync и не подменяют часы. */
static inline struct ETCP_CONN *nat_fixture_connect(struct nat_fixture *fixture, struct UTUN_INSTANCE *from,
const char *source, struct UTUN_INSTANCE *to) {
assert(fixture->handle_count < 24);
struct TOPO_NODE *peer = topo_node_registry_find(to->topo_groups, to->node_id); assert(peer);
struct NODE_CONN_DIRECT **handle = &fixture->handles[fixture->handle_count++];
assert(node_conn_direct_open_node(from, to->node_id, NULL, NULL, handle, peer,
test_etcp_bound_socket(from, source)) != NCD_ERR);
uint64_t end = get_time_tb() + 50000;
struct ETCP_CONN *connection = NULL;
while (get_time_tb() < end) {
connection = node_conn_direct_get_conn(*handle);
if (connection && connection->links_up) break;
uasync_poll(fixture->ua, 10);
}
assert(connection && connection->links_up);
struct TOPO_GROUP *group = topo_groups_find(from->topo_groups, TOPO_GROUP_UTUN);
struct TOPO_GROUP *remote = topo_groups_find(to->topo_groups, TOPO_GROUP_UTUN);
assert(group && remote && !topo_group_new_conn(group, connection));
end = get_time_tb() + 50000;
while (get_time_tb() < end) {
if (topo_group_peer_ready(group, to->node_id) && topo_group_peer_ready(remote, from->node_id)) return connection;
uasync_poll(fixture->ua, 10);
}
DEBUG_ERROR(DEBUG_CATEGORY_NAT, "fixture group negotiation timed out from=%016llx to=%016llx",
(unsigned long long)from->node_id, (unsigned long long)to->node_id); abort();
}
/* Force-close handles → destroy всех узлов → poll(0) → проверка timer/socket balance → destroy uasync.
* Сравнить u_* allocations с baseline, при расхождении вывести утечки и завершить assert. Не обнуляет fixture. */
static inline void nat_fixture_cleanup(struct nat_fixture *fixture) {
for (unsigned i = 0; i < fixture->handle_count; i++) node_conn_direct_force_close(fixture->handles[i]);
for (unsigned i = 0; i < fixture->node_count; i++) utun_instance_destroy(fixture->nodes[i]);
uasync_poll(fixture->ua, 0);
assert(fixture->ua->timer_alloc_count == fixture->ua->timer_free_count);
assert(fixture->ua->socket_alloc_count == fixture->ua->socket_free_count);
uasync_destroy(fixture->ua, 0);
if (u_get_allocated_count() != fixture->allocations) u_report_unfreed_blocks();
assert(u_get_allocated_count() == fixture->allocations);
}
#endif