From 70b55e46265d47a2f798c0b74de30e75a686ac41 Mon Sep 17 00:00:00 2001 From: evgeny Date: Sat, 10 Oct 2026 16:40:56 +0200 Subject: [PATCH] tests: assign CM phase listeners and failure endpoints dynamically --- tests/test_conn_mgr_phases.c | 65 +++++++++++++++++++++--------------- 1 file changed, 39 insertions(+), 26 deletions(-) diff --git a/tests/test_conn_mgr_phases.c b/tests/test_conn_mgr_phases.c index e0ab0caa..9e4fdf74 100644 --- a/tests/test_conn_mgr_phases.c +++ b/tests/test_conn_mgr_phases.c @@ -38,6 +38,7 @@ #include "../lib/debug_config.h" #include "../lib/mem.h" #include "test_clock.h" +#include "test_net.h" #define POLL_MS 5 #define LINK_TIMEOUT_MS 8000 @@ -85,7 +86,8 @@ static int call_media_delivered(void) { return call_media_frames > 0; } static char temp_dir[] = "/tmp/utun_cm_phases_XXXXXX"; #define TEMP_DIR_TEMPLATE "/tmp/utun_cm_phases_XXXXXX" static char cfg_a[256], cfg_b[256], cfg_c[256]; -static int base_port = 0; +static uint16_t wrong_port; +static socket_t wrong_socket = SOCKET_INVALID; /* ─── утилиты ─── */ @@ -203,19 +205,17 @@ static int write_node_config(const char* path, const char* tun_ip, const char* t static int create_configs(const char* type_a, const char* type_b) { strcpy(temp_dir, TEMP_DIR_TEMPLATE); if (test_mkdtemp(temp_dir) != 0) { fprintf(stderr, "mkdtemp failed\n"); return -1; } - base_port = 43000 + (getpid() % 12000); - int pc = base_port, pa = base_port + 1, pb = base_port + 2; snprintf(cfg_c, sizeof(cfg_c), "%s/c.conf", temp_dir); snprintf(cfg_a, sizeof(cfg_a), "%s/a.conf", temp_dir); snprintf(cfg_b, sizeof(cfg_b), "%s/b.conf", temp_dir); - if (write_node_config(cfg_c, "10.90.0.3", "tun_c", pc, "public", NULL, 0) != 0) return -1; + if (write_node_config(cfg_c, "10.90.0.3", "tun_c", 0, "public", NULL, 0) != 0) return -1; if (config_ensure_keys_and_node_id(cfg_c) != 0) return -1; char* pub_c = get_pubkey(cfg_c); if (!pub_c) return -1; - if (write_node_config(cfg_a, "10.90.0.1", "tun_a", pa, type_a, pub_c, pc) != 0) { u_free(pub_c); return -1; } - if (write_node_config(cfg_b, "10.90.0.2", "tun_b", pb, type_b, pub_c, pc) != 0) { u_free(pub_c); return -1; } + if (write_node_config(cfg_a, "10.90.0.1", "tun_a", 0, type_a, pub_c, 0) != 0) { u_free(pub_c); return -1; } + if (write_node_config(cfg_b, "10.90.0.2", "tun_b", 0, type_b, pub_c, 0) != 0) { u_free(pub_c); return -1; } u_free(pub_c); if (config_ensure_keys_and_node_id(cfg_a) != 0) return -1; if (config_ensure_keys_and_node_id(cfg_b) != 0) return -1; @@ -240,6 +240,8 @@ static int scenario_setup(void) { inst_a = utun_instance_create(ua, cfg_a); inst_b = utun_instance_create(ua, cfg_b); if (!inst_c || !inst_a || !inst_b) { fail("instance create"); return -1; } + test_etcp_set_peer(inst_a, "to_relay", inst_c, "srv"); + test_etcp_set_peer(inst_b, "to_relay", inst_c, "srv"); if (utun_instance_init(inst_c) != 0 || utun_instance_init(inst_a) != 0 || utun_instance_init(inst_b) != 0) { fail("init_connections"); return -1; @@ -290,7 +292,7 @@ static int run_reverse(void) { if (scenario_setup() != 0) return -1; /* Испортим адреса B в реестре A — DIRECT провалится, A уйдёт в REVERSE. */ - mangle_addrs(inst_a, nid_b, (uint16_t)(base_port + 1000)); + mangle_addrs(inst_a, nid_b, wrong_port); inst_a->etcp_connect_timeout_tb = 3000; /* 300мс — быстрый провал DIRECT */ ev_a = -1; @@ -315,8 +317,8 @@ static int run_indirect(void) { if (!poll_until(candidates_a_and_b, 8000)) { fail("INDIRECT: no candidates (C)"); return -1; } /* Испортим адреса цели с обеих сторон — DIRECT провалится у обоих. */ - mangle_addrs(inst_a, nid_b, (uint16_t)(base_port + 1000)); - mangle_addrs(inst_b, nid_a, (uint16_t)(base_port + 1000)); + mangle_addrs(inst_a, nid_b, wrong_port); + mangle_addrs(inst_b, nid_a, wrong_port); inst_a->etcp_connect_timeout_tb = 3000; inst_b->etcp_connect_timeout_tb = 3000; @@ -374,8 +376,8 @@ static int run_schedule(void) { fprintf(stderr, "\n=== Scenario 4: 500/1000ms schedule ===\n"); if (create_configs("public", "public") || scenario_setup()) return -1; if (!poll_until(candidates_a_and_b, 8000)) { fail("SCHEDULE: no relay candidates"); return -1; } - mangle_addrs(inst_a, nid_b, (uint16_t)(base_port + 1000)); - mangle_addrs(inst_b, nid_a, (uint16_t)(base_port + 1000)); + mangle_addrs(inst_a, nid_b, wrong_port); + mangle_addrs(inst_b, nid_a, wrong_port); // DIRECT остаётся pending в момент запуска обоих дополнительных вариантов. inst_a->etcp_connect_timeout_tb = 100000; if (conn_mgr_open(inst_a, TOPO_GROUP_UTUN, nid_b, conn_cb_a, NULL, &h_a)) return -1; @@ -406,8 +408,8 @@ static int run_upgrade(void) { if (fallback.type != CONN_TYPE_INDIRECT || fallback.next_hop_node_id != nid_c || cm_find_entry(defgrp(inst_a)->conn_mgr, nid_b)) { fail("UPGRADE: missing BGP fallback snapshot before CM open"); return -1; } - mangle_addrs(inst_a, nid_b, (uint16_t)(base_port + 1000)); - mangle_addrs(inst_b, nid_a, (uint16_t)(base_port + 1000)); + mangle_addrs(inst_a, nid_b, wrong_port); + mangle_addrs(inst_b, nid_a, wrong_port); inst_a->etcp_connect_timeout_tb = 1000; if (conn_mgr_open(inst_a, TOPO_GROUP_UTUN, nid_b, conn_cb_a, NULL, &h_a)) return -1; struct CONN_MGR_ENTRY* entry = h_a->entry; @@ -455,7 +457,7 @@ static int run_upgrade(void) { fail("CALL PATH: relay media delivery failed"); return -1; } // Появился пригодный адрес B. Обновление дополняет существующий NCD. - mangle_addrs(inst_a, nid_b, (uint16_t)(base_port + 2)); + mangle_addrs(inst_a, nid_b, ss_get_port(&test_etcp_bound_socket(inst_b, "srv")->local_addr)); if (node_conn_direct_update_node(inst_a, nid_b) < 0 || !poll_until(direct_a_ready, 4000)) { fail("UPGRADE: no late direct READY path"); return -1; } @@ -469,8 +471,8 @@ static int run_upgrade(void) { poll_ms(1100); if (call_connection_events != events) { fail("CALL PATH: duplicate unchanged status"); return -1; } // Потеря прямого транспорта должна вернуть передачу на проверенный резерв без DOWN. - mangle_addrs(inst_a, nid_b, (uint16_t)(base_port + 1000)); - mangle_addrs(inst_b, nid_a, (uint16_t)(base_port + 1000)); + mangle_addrs(inst_a, nid_b, wrong_port); + mangle_addrs(inst_b, nid_a, wrong_port); struct ETCP_CONN* direct = node_conn_direct_get_conn(original); if (etcp_conn_ref_take(direct)) { fail("UPGRADE: cannot hold direct transport"); return -1; } while (direct->links) etcp_link_close(direct->links); @@ -513,8 +515,8 @@ static int run_invalid_candidates(void) { fprintf(stderr, "\n=== Scenario 7: candidate validation and cleanup ===\n"); if (create_configs("nat", "nat") || scenario_setup()) return -1; if (!poll_until(candidates_a_and_b, 8000)) return -1; - mangle_addrs(inst_a, nid_b, (uint16_t)(base_port + 1000)); - mangle_addrs(inst_b, nid_a, (uint16_t)(base_port + 1000)); + mangle_addrs(inst_a, nid_b, wrong_port); + mangle_addrs(inst_b, nid_a, wrong_port); if (conn_mgr_open(inst_a, TOPO_GROUP_UTUN, nid_b, conn_cb_a, NULL, &h_a)) return -1; struct CONN_MGR_ENTRY* entry = h_a->entry; struct CM_EXCHANGE_RESP resp = {0}; @@ -555,14 +557,14 @@ static int run_recovery(void) { fprintf(stderr, "\n=== Scenario 8: persistent recovery and address change ===\n"); if (create_configs("nat", "nat") || scenario_setup()) return -1; if (!poll_until(candidates_a_and_b, 8000)) return -1; - uint16_t bad_port = (uint16_t)(base_port + 1000); + uint16_t bad_port = wrong_port; mangle_addrs(inst_a, nid_b, bad_port); mangle_addrs(inst_b, nid_a, bad_port); if (conn_mgr_open(inst_a, TOPO_GROUP_UTUN, nid_b, conn_cb_a, NULL, &h_a) || !poll_until(ev_a_up, 3000)) return -1; struct CONN_MGR_ENTRY* entry = h_a->entry; uint32_t stale_request = entry->exchange_request_id; struct NODE_CONN_DIRECT* original = entry->ncd_handle; if (etcp_router_bind(inst_b, TEST_CM_SERVICE, data_recv) < 0 || send_data(1, nid_c)) return -1; - mangle_addrs(inst_a, nid_b, (uint16_t)(base_port + 2)); + mangle_addrs(inst_a, nid_b, ss_get_port(&test_etcp_bound_socket(inst_b, "srv")->local_addr)); node_conn_direct_update_node(inst_a, nid_b); if (!poll_until(direct_a_ready, 4000)) { fail("RECOVERY: no initial direct path"); return -1; } // Истёк первоначальный бюджет. Работающее соединение не требует новых проб. @@ -599,7 +601,7 @@ static int run_recovery(void) { } entry->deadline_tb = get_time_tb() + 100; cm_arm_timer(entry); poll_ms(30); uint64_t start = entry->start_tb; - mangle_addrs(inst_a, nid_b, (uint16_t)(base_port + 2)); + mangle_addrs(inst_a, nid_b, ss_get_port(&test_etcp_bound_socket(inst_b, "srv")->local_addr)); conn_mgr_routes_changed(defgrp(inst_a)->conn_mgr); if (!poll_until(direct_a_ready, 2000) || entry->start_tb == start || entry->ncd_handle != original || up_count != 1 || down_count || timeout_count || entry->attempt_active || entry->timer) { @@ -616,7 +618,7 @@ static int run_recovery(void) { entry->deadline_tb = get_time_tb() + 100; cm_arm_timer(entry); poll_ms(30); entry->retry_tb = get_time_tb() + 150000; cm_arm_timer(entry); start = entry->start_tb; - mangle_addrs(inst_a, nid_b, (uint16_t)(base_port + 2)); + mangle_addrs(inst_a, nid_b, ss_get_port(&test_etcp_bound_socket(inst_b, "srv")->local_addr)); entry->node_timestamp = topo_node_registry_find(inst_a->topo_groups, nid_b)->timestamp; conn_mgr_network_changed(defgrp(inst_a)->conn_mgr); if (!poll_until(direct_a_ready, 2000) || entry->start_tb == start) { fail("RECOVERY: local network change did not retry"); return -1; } @@ -640,8 +642,8 @@ static int run_recovery(void) { static int run_relay_recovery(void) { fprintf(stderr, "\n=== Scenario 9: lost relay route and new exchange ===\n"); if (create_configs("nat", "nat") || scenario_setup() || !poll_until(candidates_a_and_b, 8000)) return -1; - mangle_addrs(inst_a, nid_b, (uint16_t)(base_port + 1000)); - mangle_addrs(inst_b, nid_a, (uint16_t)(base_port + 1000)); + mangle_addrs(inst_a, nid_b, wrong_port); + mangle_addrs(inst_b, nid_a, wrong_port); if (conn_mgr_open(inst_a, TOPO_GROUP_UTUN, nid_b, conn_cb_a, NULL, &h_a) || !poll_until(ev_a_up, 3000)) return -1; struct CONN_MGR_ENTRY* entry = h_a->entry; uint32_t old_request = entry->exchange_request_id; @@ -689,8 +691,8 @@ static int run_relay_recovery(void) { static int run_initial_timeout(void) { fprintf(stderr, "\n=== Scenario 10: initial TIMEOUT keeps demand and callback close ===\n"); if (create_configs("nat", "nat") || scenario_setup() || !poll_until(candidates_a_and_b, 8000)) return -1; - mangle_addrs(inst_a, nid_b, (uint16_t)(base_port + 1000)); - mangle_addrs(inst_b, nid_a, (uint16_t)(base_port + 1000)); + mangle_addrs(inst_a, nid_b, wrong_port); + mangle_addrs(inst_b, nid_a, wrong_port); struct TOPO_GROUP_NODE* target = topo_node_find_by_id(defgrp(inst_a), nid_b); while (target && target->paths && target->paths->head) topo_group_remove_path(target, ((struct TOPO_NODEPATH*)target->paths->head)->conn); @@ -753,6 +755,16 @@ int main(void) { ua = uasync_create(); if (!ua) { fprintf(stderr, "uasync_create failed\n"); return 1; } + wrong_socket = socket_create_udp(AF_INET); + if (wrong_socket == SOCKET_INVALID) { + DEBUG_ERROR(DEBUG_CATEGORY_SOCKET, "CM test blackhole socket failed"); uasync_destroy(ua, 0); return 1; + } + struct sockaddr_in blackhole = {.sin_family = AF_INET, .sin_addr.s_addr = htonl(INADDR_LOOPBACK)}; + if (bind(wrong_socket, (struct sockaddr*)&blackhole, sizeof(blackhole)) != 0) { + DEBUG_ERROR(DEBUG_CATEGORY_SOCKET, "CM test blackhole bind failed err=%d", socket_get_error()); + socket_close_wrapper(wrong_socket); uasync_destroy(ua, 0); return 1; + } + wrong_port = test_bound_port(wrong_socket); // Порт удерживается, но ответы INIT не отправляются. if (run_direct() != 0) g_failed = 1; scenario_teardown(); @@ -781,6 +793,7 @@ int main(void) { scenario_teardown(); if (ua) { uasync_poll(ua, 0); uasync_destroy(ua, 0); ua = NULL; } + socket_close_wrapper(wrong_socket); test_clock_finish(); fprintf(stderr, "%s\n", g_failed ? "RESULT: FAIL" : "RESULT: PASS"); fflush(stderr);