/** * @file test_ipv6_sockets.c * @brief Тест IPv6 сокетов: подключение, nodeinfo, BGP-обмен * * Проверяет: * - Создание и бинд AF_INET6 сокета * - IPv6 подключение клиент→сервер * - Заполнение local_v6_sockets/addrs/subnets в nodeinfo * - Корректность NODEINFO_IPV6_SOCKET_META и NODEINFO_IPV6_ADDR * - Передачу IPv6 адресов через BGP соседнему узлу */ #include #include #include #include #include #ifndef _WIN32 #include #endif #include "../lib/platform_compat.h" #include "test_utils.h" #ifdef _WIN32 #include #include #else #include #endif #include "../src/etcp.h" #include "../src/etcp_connections.h" #include "../src/config_parser.h" #include "../src/utun_instance.h" #include "../src/routing.h" #include "../src/route_lib.h" #include "../src/topo_group.h" #include "../src/topo_node.h" #include "../src/tun_if.h" #include "../src/secure_channel.h" #include "../lib/u_async.h" #include "../lib/debug_config.h" #define TEST_TIMEOUT_MS 8000 static struct UTUN_INSTANCE* server_instance = NULL; static struct UTUN_INSTANCE* client_instance = NULL; static struct UASYNC* ua = NULL; static int test_phase = 0; static void* monitor_timer = NULL; static void* timeout_timer = NULL; static char temp_dir[] = "/tmp/utun_v6test_XXXXXX"; static char server_config_path[256]; static char client_config_path[256]; static const char* server_config = "[global]\n" "my_node_name=server_v6\n" "my_node_id=0xAAAA000000000001\n" "my_private_key=38240cb82199e504686507f11f6eaa4f740fde6f0c425c495e49a523019a5d68\n" "my_public_key=ce8871f07fa056c636d297115f231b08c29cdf94e0d440fce83a07c34416d36a\n" "tun_ip=10.99.0.1/24\n" "tun_ifname=tun99\n" "tun_test_mode=1\n" "\n" "[routing]\n" "my_subnet=2001:db8:1::/48\n" "\n" "[server: v6srv]\n" "addr=[::1]:40111\n" "type=public\n" "\n" "[allowed_keys]\n" "allow_all=1\n"; static const char* client_config = "[global]\n" "my_node_name=client_v6\n" "my_node_id=0xBBBB000000000002\n" "my_private_key=704f2e012c8fa8768130cb0f988a997dccb628372bc5ceccacc78dcbfec5916f\n" "my_public_key=b3193173def895bd0fcea6f86af077c7d77216f10395275f627ac18242ec0f01\n" "tun_ip=10.99.0.2/24\n" "tun_ifname=tun98\n" "tun_test_mode=1\n" "\n" "[server: v6srv]\n" "addr=[::1]:40112\n" "type=public\n" "\n" "[client: v6client]\n" "keepalive=1\n" "peer_public_key=ce8871f07fa056c636d297115f231b08c29cdf94e0d440fce83a07c34416d36a\n" "link=v6srv:[::1]:40111\n"; static int create_temp_configs(void) { if (test_mkdtemp(temp_dir) != 0) { fprintf(stderr, "Failed to create temp directory\n"); return -1; } snprintf(server_config_path, sizeof(server_config_path), "%s/server.conf", temp_dir); snprintf(client_config_path, sizeof(client_config_path), "%s/client.conf", temp_dir); FILE* f = fopen(server_config_path, "w"); if (!f) { fprintf(stderr, "Failed to create server config\n"); return -1; } fprintf(f, "%s", server_config); fclose(f); f = fopen(client_config_path, "w"); if (!f) { fprintf(stderr, "Failed to create client config\n"); test_unlink(server_config_path); return -1; } fprintf(f, "%s", client_config); fclose(f); return 0; } static void cleanup_temp_configs(void) { if (server_config_path[0]) test_unlink(server_config_path); if (client_config_path[0]) test_unlink(client_config_path); if (temp_dir[0]) test_rmdir(temp_dir); } static int is_connection_established(struct UTUN_INSTANCE* inst) { if (!inst) return 0; struct ETCP_CONN* conn = inst->connections; while (conn) { struct ETCP_LINK* link = conn->links; while (link) { if (link->initialized) return 1; link = link->next; } conn = conn->next; } return 0; } static int verify_ipv6_local_nodeinfo(const char* name, struct UTUN_INSTANCE* inst, int expect_socks, int expect_addrs, int expect_subnets) { if (!inst || !topo_groups_get_default(inst->topo_groups) || !topo_groups_get_default(inst->topo_groups)->local_node || !topo_groups_get_default(inst->topo_groups)->local_node->node) { printf("FAIL [%s]: no bgp/local_node\n", name); return 0; } struct TOPO_NODE* ni = topo_groups_get_default(inst->topo_groups)->local_node->node; struct ETCP_SOCKET* es = inst->etcp_sockets; int v6_sock_found = 0; while (es) { if (es->local_addr.ss_family == AF_INET6) { v6_sock_found = 1; break; } es = es->next; } if (!v6_sock_found) { printf("FAIL [%s]: no AF_INET6 socket\n", name); return 0; } printf("PASS [%s]: AF_INET6 socket exists\n", name); int v6s = topo_list_count((struct _topo_head*)ni->v6_sock_meta); if (v6s != expect_socks) { printf("FAIL [%s]: v6_socks=%d expected=%d\n", name, v6s, expect_socks); return 0; } printf("PASS [%s]: v6_sockets=%d\n", name, v6s); int v6a = topo_list_count((struct _topo_head*)ni->v6_addrs); if (v6a != expect_addrs) { printf("FAIL [%s]: v6_addrs=%d expected=%d\n", name, v6a, expect_addrs); return 0; } printf("PASS [%s]: v6_addrs=%d\n", name, v6a); int v6sub = topo_groups_get_default(inst->topo_groups)->local_node->subnets ? topo_list_count((struct _topo_head*)topo_groups_get_default(inst->topo_groups)->local_node->subnets->v6_subnets) : 0; if (v6sub != expect_subnets) { printf("FAIL [%s]: v6_subnets=%d expected=%d\n", name, v6sub, expect_subnets); return 0; } printf("PASS [%s]: v6_subnets=%d\n", name, v6sub); { const struct TOPO_SOCKMETA6* m = ni->v6_sock_meta; int i = 0; while (m) { if (m->config_type != CFG_SERVER_TYPE_PUBLIC) { printf("FAIL [%s]: v6 socket meta[%d] config_type=%d expected=%d\n", name, i, m->config_type, CFG_SERVER_TYPE_PUBLIC); return 0; } printf("PASS [%s]: v6 socket meta[%d] id=%d config=%d nat=%d\n", name, i, m->id, m->config_type, m->nat_type); i++; m = m->next; } } { const struct TOPO_ADDR6* a = ni->v6_addrs; int i = 0; while (a) { if (a->port == 0) { printf("FAIL [%s]: v6 addr port=0\n", name); return 0; } if (a->type != TOPO_ADDR_INTERFACE) { printf("FAIL [%s]: v6 addr[%d] type=%d expected=%d(INTERFACE)\n", name, i, a->type, TOPO_ADDR_INTERFACE); return 0; } printf("PASS [%s]: v6 addr[%d] port=%d type=%d socket_id=%d\n", name, i, a->port, a->type, a->socket_id); i++; a = a->next; } } if (expect_subnets > 0 && topo_groups_get_default(inst->topo_groups)->local_node->subnets) { const struct TOPO_SUBNET6* subs = topo_groups_get_default(inst->topo_groups)->local_node->subnets->v6_subnets; if (subs) { uint8_t expected_sub[16] = {0x20,0x01,0x0d,0xb8,0x00,0x01,0,0,0,0,0,0,0,0,0,0}; if (memcmp(subs->addr, expected_sub, 16) != 0) { printf("FAIL [%s]: v6 subnet != 2001:db8:1::/48\n", name); return 0; } if (subs->prefix_length != 48) { printf("FAIL [%s]: v6 subnet prefix=%d expected=48\n", name, subs->prefix_length); return 0; } printf("PASS [%s]: v6 subnet 2001:db8:1::/48\n", name); } } return 1; } static int verify_remote_v6_nodeinfo(const char* name, struct UTUN_INSTANCE* inst, uint64_t peer_node_id) { if (!inst || !topo_groups_get_default(inst->topo_groups)) { printf("FAIL [%s]: no bgp\n", name); return 0; } struct TOPO_NODEQ* nq = topo_node_find_by_id(topo_groups_get_default(inst->topo_groups), peer_node_id); if (!nq || !nq->node) { printf("FAIL [%s]: remote node %016llx not found\n", name, (unsigned long long)peer_node_id); return 0; } printf("PASS [%s]: remote node %016llx found\n", name, (unsigned long long)peer_node_id); if (!nq->node->v6_sock_meta) { printf("FAIL [%s]: remote node has no v6 sockets\n", name); return 0; } printf("PASS [%s]: remote node v6_sockets=%d\n", name, topo_list_count((struct _topo_head*)nq->node->v6_sock_meta)); if (!nq->node->v6_addrs) { printf("FAIL [%s]: remote node has no v6 addrs\n", name); return 0; } printf("PASS [%s]: remote node v6_addrs=%d\n", name, topo_list_count((struct _topo_head*)nq->node->v6_addrs)); const struct TOPO_ADDR6* a = nq->node->v6_addrs; if (a) { uint8_t loopback[16] = {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1}; if (memcmp(a->addr, loopback, 16) != 0) { printf("FAIL [%s]: remote v6 addr not ::1\n", name); return 0; } printf("PASS [%s]: remote v6 addr verified ::1 port=%d\n", name, a->port); } return 1; } static void timeout_cb(void* arg) { (void)arg; if (test_phase < 3) { printf("=== TEST TIMEOUT ===\n"); test_phase = 4; if (monitor_timer) { uasync_cancel_timeout(ua, monitor_timer); monitor_timer = NULL; } } } static void monitor(void* arg) { (void)arg; if (test_phase >= 3) { monitor_timer = NULL; return; } switch (test_phase) { case 0: { int srv_ok = is_connection_established(server_instance); int cli_ok = is_connection_established(client_instance); if (srv_ok && cli_ok) { printf("Connection established, verifying nodeinfo...\n"); test_phase = 1; monitor_timer = uasync_set_timeout(ua, 500, NULL, monitor, "v6_monitor"); return; } break; } case 1: { int server_ok = verify_ipv6_local_nodeinfo("server_local", server_instance, 1, 1, 1); int client_ok = verify_ipv6_local_nodeinfo("client_local", client_instance, 1, 1, 0); if (!server_ok || !client_ok) { test_phase = 4; return; } printf("Local nodeinfo verified, waiting for BGP exchange...\n"); test_phase = 2; monitor_timer = uasync_set_timeout(ua, 500, NULL, monitor, "v6_monitor"); return; } case 2: { int ok = verify_remote_v6_nodeinfo("client_remote", client_instance, 0xAAAA000000000001ULL); if (!ok) { test_phase = 4; return; } printf("=== ALL CHECKS PASSED ===\n"); test_phase = 3; if (timeout_timer) { uasync_cancel_timeout(ua, timeout_timer); timeout_timer = NULL; } return; } } if (test_phase < 3) { monitor_timer = uasync_set_timeout(ua, 100, NULL, monitor, "v6_monitor"); } } int main(void) { printf("=== IPv6 Sockets + Nodeinfo Test ===\n\n"); if (create_temp_configs() != 0) { fprintf(stderr, "Failed to create config files\n"); return 1; } debug_config_init(); debug_set_level(DEBUG_LEVEL_ERROR); utun_instance_set_tun_init_enabled(0); ua = uasync_create(); if (!ua) { fprintf(stderr, "uasync_create failed\n"); return 1; } server_instance = utun_instance_create(ua, server_config_path); if (!server_instance) { fprintf(stderr, "Server instance create failed\n"); return 1; } if (utun_instance_init(server_instance) < 0) { fprintf(stderr, "Server init failed\n"); return 1; } client_instance = utun_instance_create(ua, client_config_path); if (!client_instance) { fprintf(stderr, "Client instance create failed\n"); return 1; } if (utun_instance_init(client_instance) < 0) { fprintf(stderr, "Client init failed\n"); return 1; } monitor_timer = uasync_set_timeout(ua, 100, NULL, monitor, "v6_monitor"); timeout_timer = uasync_set_timeout(ua, TEST_TIMEOUT_MS, NULL, timeout_cb, "v6_timeout"); while (test_phase < 3) uasync_poll(ua, -1); if (timeout_timer) { uasync_cancel_timeout(ua, timeout_timer); timeout_timer = NULL; } if (monitor_timer) { uasync_cancel_timeout(ua, monitor_timer); monitor_timer = NULL; } if (server_instance) { server_instance->running = 0; utun_instance_destroy(server_instance); } if (client_instance) { client_instance->running = 0; utun_instance_destroy(client_instance); } if (ua) { uasync_destroy(ua, 0); ua = NULL; } cleanup_temp_configs(); if (test_phase == 3) { printf("\n=== TEST PASSED ===\n"); return 0; } printf("\n=== TEST FAILED ===\n"); return 1; }