/** * @file test_conn_mgr.c * @brief Connection Manager — smoke test * * Test 1: Direct via existing ETCP conn → CONN_TYPE_DIRECT * Test 2: Status check → state/type * Test 3: Alien node → TOPO_NODEQ.alien=1 * * Pending: * Test 4: New link UP callback (INIT to new peer via conn_mgr) * Test 5: Indirect through intermediary (needs full Phase 3 exchange) * Test 6: Idle timeout */ #include #include #include #include #include "../lib/platform_compat.h" #include "test_utils.h" #ifndef _WIN32 #include #endif #include "../src/etcp.h" #include "../src/etcp_connections.h" #include "../src/config_parser.h" #include "../src/config_updater.h" #include "../src/utun_instance.h" #include "../src/topo_group.h" #include "../src/conn_mgr.h" #include "../lib/u_async.h" #include "../lib/debug_config.h" #include "../lib/mem.h" #define TIMEOUT_TB 300000 #define POLL_MS 5 #define NID_A 0xAAAA000000000001ULL #define NID_B 0xBBBB000000000002ULL static struct UTUN_INSTANCE* g_a = NULL, *g_b = NULL; static struct UASYNC* ua = NULL; static volatile int result = 0; static void* ttimer = NULL; static char tdir[] = "/tmp/utun_cm_XXXXXX"; static char ca[256], cb[256]; static int pa = 0, pb = 0; static volatile int cdone = 0, cresult = 0; static int wf(const char* p, const char* f, ...) { va_list ap; FILE* fp = fopen(p, "w"); if (!fp) return -1; va_start(ap, f); vfprintf(fp, f, ap); va_end(ap); fclose(fp); return 0; } static char* gv(const char* p, const char* k) { struct utun_config* c = parse_config(p); if (!c) return NULL; char* r = (strcmp(k, "pub") == 0) ? u_strdup(c->global.my_public_key_hex) : u_strdup(c->global.my_private_key_hex); free_config(c); return r; } static int lks(struct UTUN_INSTANCE* i) { int n = 0; struct ll_entry* entry = i->connections->head; while (entry) { struct conn_queue_entry* ce = (struct conn_queue_entry*)entry->data; struct ETCP_CONN* c = ce->conn; struct ETCP_LINK* l = c->links; while (l) { if (l->initialized && l->link_status) n++; l = l->next; } entry = entry->next; } return n; } static void fail(const char* msg) { fprintf(stderr, "FAIL: %s\n", msg); fflush(stderr); result = 2; } static void test1(void* arg); static void test2(void* arg); static void test3(void* arg); static void to_cb(void* arg) { (void)arg; fprintf(stderr, "TIMEOUT\n"); result = 2; } static void ccb(int r, uint64_t id, void* arg) { (void)arg; fprintf(stderr, "connect_cb: result=%d node=0x%llx\n", r, (unsigned long long)id); fflush(stderr); cdone = 1; cresult = r; } static void test1(void* arg) { (void)arg; if (result) return; if (!topo_node_find_by_id(topo_groups_get_default(g_a->topo_groups), NID_B)) { uasync_set_timeout(ua, 50, NULL, (timeout_callback_t)test1, "t1"); return; } fprintf(stderr, "Test 1: direct — conn_mgr_connect_node(B)\n"); fflush(stderr); conn_mgr_connect_node(g_a->conn_mgr, NID_B, 0, ccb, NULL); uasync_set_timeout(ua, 50, NULL, (timeout_callback_t)test2, "t2a"); } static void test2(void* arg) { (void)arg; if (result) return; if (!cdone) { uasync_set_timeout(ua, 50, NULL, (timeout_callback_t)test2, "t2b"); return; } if (cresult != CONN_MGR_OK) { fail("connect failed"); return; } uint8_t st, ty; conn_mgr_get_status(g_a->conn_mgr, NID_B, &st, &ty); fprintf(stderr, "Test 2: status state=%d type=%d\n", st, ty); fflush(stderr); if (st == CONN_MGR_STATE_CONNECTED && ty == CONN_TYPE_DIRECT) fprintf(stderr, " OK: DIRECT\n"); else { fail("status mismatch"); return; } test3(NULL); } static void test3(void* arg) { (void)arg; uint64_t aid = 0xEEEE000000000001ULL; struct TOPO_NODE ni; memset(&ni, 0, sizeof(ni)); ni.node_id = aid; ni.ver = 1; conn_mgr_add_alien_node(g_a->conn_mgr, (uint8_t*)&ni, sizeof(ni)); struct TOPO_NODEQ* nq = topo_node_find_by_id(topo_groups_get_default(g_a->topo_groups), aid); fprintf(stderr, "Test 3: alien alien=%d\n", nq ? nq->alien : -1); fflush(stderr); if (nq && nq->alien == 1) fprintf(stderr, " OK: alien flag set\n"); else { fail("alien flag not set"); return; } fprintf(stderr, "=== ALL DONE ===\n"); fflush(stderr); result = (result == 0) ? 1 : 2; } static void setup(void) { test_mkdtemp(tdir); int base = 47000 + (getpid() % 15000); pa = base; pb = base + 1; snprintf(ca, sizeof(ca), "%s/a.conf", tdir); snprintf(cb, sizeof(cb), "%s/b.conf", tdir); wf(ca, "[global]\nmy_node_id=0x%llx\ntun_ip=10.99.0.1/24\ntun_ifname=tun99\n[server: s1]\naddr=127.0.0.1:%d\ntype=public\n[allowed_keys]\nallow_all=1\n", NID_A, pa); wf(cb, "[global]\nmy_node_id=0x%llx\ntun_ip=10.99.0.2/24\ntun_ifname=tun98\n[server: s1]\naddr=127.0.0.1:%d\ntype=public\n[allowed_keys]\nallow_all=1\n", NID_B, pb); config_ensure_keys_and_node_id(ca); config_ensure_keys_and_node_id(cb); char *p0 = gv(ca,"pub"), *r0 = gv(ca,"priv"), *p1 = gv(cb,"pub"), *r1 = gv(cb,"priv"); wf(ca, "[global]\nmy_node_id=0x%llx\nmy_private_key=%s\nmy_public_key=%s\ntun_ip=10.99.0.1/24\ntun_ifname=tun99\n[server: s1]\naddr=127.0.0.1:%d\ntype=public\n[client: to_b]\nkeepalive=1\npeer_public_key=%s\nlink=s1:127.0.0.1:%d\n[allowed_keys]\nallow_all=1\n", NID_A, r0, p0, pa, p1, pb); wf(cb, "[global]\nmy_node_id=0x%llx\nmy_private_key=%s\nmy_public_key=%s\ntun_ip=10.99.0.2/24\ntun_ifname=tun98\n[server: s1]\naddr=127.0.0.1:%d\ntype=public\n[allowed_keys]\nallow_all=1\n", NID_B, r1, p1, pb); u_free(p0); u_free(r0); u_free(p1); u_free(r1); } static void cleanup(void) { test_unlink(ca); test_unlink(cb); test_rmdir(tdir); } int main(void) { debug_config_init(); debug_set_level(DEBUG_LEVEL_WARN); utun_instance_set_tun_init_enabled(0); setup(); ua = uasync_create(); g_a = utun_instance_create(ua, ca); g_b = utun_instance_create(ua, cb); if (!g_a || !g_b) goto done; utun_instance_init(g_a); utun_instance_init(g_b); uasync_call_soon(ua, NULL, test1); ttimer = uasync_set_timeout(ua, TIMEOUT_TB, NULL, to_cb, "to"); { int el = 0; while (!result && el < TIMEOUT_TB + 5000) { uasync_poll(ua, POLL_MS); el += POLL_MS; } } fprintf(stderr, "final result=%d\n", result); fflush(stderr); done: if (ttimer) uasync_cancel_timeout(ua, ttimer); if (g_a) { g_a->running = 0; utun_instance_destroy(g_a); } if (g_b) { g_b->running = 0; utun_instance_destroy(g_b); } if (ua) { uasync_destroy(ua, 0); ua = NULL; } cleanup(); return (result == 1) ? 0 : 1; }