diff --git a/src/transport_layer/dummynet.c b/src/transport_layer/dummynet.c index 707ba1f0..86b13c26 100644 --- a/src/transport_layer/dummynet.c +++ b/src/transport_layer/dummynet.c @@ -110,12 +110,15 @@ static inline uint32_t random_range(uint32_t max) { return (uint32_t)rand() % (max + 1); } -/* Определяет направление по порту источника */ -static int dummynet_get_direction(struct dummynet* dn, uint16_t src_port) { - if (src_port == dn->listen_port - 1) { - return DUMMYNET_FORWARD; - } else if (src_port == dn->listen_port + 1) { - return DUMMYNET_BACKWARD; +/* Источник одного направления — endpoint назначения обратного направления. */ +static int dummynet_get_direction(struct dummynet* dn, const struct sockaddr_storage* source) { + if (source->ss_family != AF_INET) return -1; + const struct sockaddr_in* src = (const struct sockaddr_in*)source; + for (int i = 0; i < DUMMYNET_DIR_COUNT; i++) { + struct dummynet_dir* reverse = &dn->dirs[1 - i]; + const struct sockaddr_in* peer = (const struct sockaddr_in*)&reverse->dest_addr; + if (reverse->configured && peer->sin_family == AF_INET && + peer->sin_addr.s_addr == src->sin_addr.s_addr && peer->sin_port == src->sin_port) return i; } return -1; } @@ -201,17 +204,11 @@ static void dummynet_read_callback(socket_t sock, void* user_arg) { if (n == 0) return; - uint16_t src_port = 0; - if (src_addr.ss_family == AF_INET) { - src_port = ntohs(((struct sockaddr_in*)&src_addr)->sin_port); - } else if (src_addr.ss_family == AF_INET6) { - src_port = ntohs(((struct sockaddr_in6*)&src_addr)->sin6_port); - } - - int dir_idx = dummynet_get_direction(dn, src_port); + int dir_idx = dummynet_get_direction(dn, &src_addr); if (dir_idx < 0) { - DEBUG_WARN(DEBUG_CATEGORY_DUMMYNET, "Packet from unknown port %u, expected %u or %u", - src_port, dn->listen_port - 1, dn->listen_port + 1); + DEBUG_WARN(DEBUG_CATEGORY_DUMMYNET, "Packet from unknown endpoint %s, expected %s or %s", + sockaddr_storage_to_str(&src_addr).str, sockaddr_storage_to_str(&dn->dirs[DUMMYNET_BACKWARD].dest_addr).str, + sockaddr_storage_to_str(&dn->dirs[DUMMYNET_FORWARD].dest_addr).str); return; } @@ -340,7 +337,7 @@ static void dummynet_delay_callback(void* user_arg) { /* Создаёт dummynet контекст */ struct dummynet* dummynet_create(struct UASYNC* ua, const char* bind_ip, uint16_t listen_port) { - if (!ua || listen_port == 0 || listen_port < 2 || listen_port > 65533) { + if (!ua) { DEBUG_ERROR(DEBUG_CATEGORY_DUMMYNET, "Invalid parameters"); return NULL; } @@ -408,6 +405,8 @@ struct dummynet* dummynet_create(struct UASYNC* ua, const char* bind_ip, uint16_ dummynet_destroy(dn); return NULL; } + dn->listen_port = socket_get_bound_port(dn->sock); + if (!dn->listen_port) { dummynet_destroy(dn); return NULL; } /* Добавляем сокет в uasync */ void* sock_id = uasync_add_socket_t(ua, dn->sock, dummynet_read_callback, NULL, NULL, "dummynet", dn); @@ -417,10 +416,8 @@ struct dummynet* dummynet_create(struct UASYNC* ua, const char* bind_ip, uint16_ return NULL; } - DEBUG_INFO(DEBUG_CATEGORY_DUMMYNET, "Dummynet created on %s:%u (forward: %u->%u, backward: %u->%u)", - bind_ip ? bind_ip : "0.0.0.0", listen_port, - listen_port - 1, listen_port + 1, - listen_port + 1, listen_port - 1); + DEBUG_INFO(DEBUG_CATEGORY_DUMMYNET, "Dummynet created on %s:%u; configure both peer endpoints before traffic", + bind_ip ? bind_ip : "0.0.0.0", dn->listen_port); return dn; } diff --git a/src/transport_layer/dummynet.h b/src/transport_layer/dummynet.h index 4231b483..6b40dc96 100644 --- a/src/transport_layer/dummynet.h +++ b/src/transport_layer/dummynet.h @@ -4,7 +4,7 @@ * * Эмулирует сетевые условия: задержку (fixed + random), ограничение пропускной способности * и случайные потери пакетов. Работает как "человек посередине" - принимает пакеты на - * listen_port и пересылает между port-1 и port+1 с эмуляцией условий канала. + * listen_port и пересылает между двумя явно настроенными endpoint с эмуляцией условий канала. * * Архитектура: * [UDP recv] -> [delay timer] -> [check queue/drop] -> [ll_queue] -> [shaper timer] -> [UDP send] @@ -13,8 +13,9 @@ * * Поток данных: * - Слушаем на bind_ip:listen_port - * - Пакеты от listen_port-1 -> отправляем на dest_forward (forward direction) - * - Пакеты от listen_port+1 -> отправляем на dest_backward (backward direction) + * - Пакеты от dest_backward -> отправляем на dest_forward (forward direction) + * - Пакеты от dest_forward -> отправляем на dest_backward (backward direction) + * - Оба направления настраиваются до начала трафика; IP и порты участников независимы * - Каждое направление имеет свою очередь и параметры эмуляции */ @@ -36,8 +37,8 @@ struct ll_queue; struct ll_entry; /* Направления передачи */ -#define DUMMYNET_FORWARD 0 // port-1 -> port+1 -#define DUMMYNET_BACKWARD 1 // port+1 -> port-1 +#define DUMMYNET_FORWARD 0 // client -> server +#define DUMMYNET_BACKWARD 1 // server -> client #define DUMMYNET_DIR_COUNT 2 /* Максимальный размер UDP пакета */ @@ -71,7 +72,7 @@ struct dummynet; * * @param ua Экземпляр uasync (обязателен) * @param bind_ip IP адрес для привязки (NULL или "0.0.0.0" для всех интерфейсов) - * @param listen_port Порт для прослушивания (средний порт, port-1 и port+1 - клиенты) + * @param listen_port Порт для прослушивания (0 выбирает ОС; фактический порт — get_listen_port) * @return Указатель на контекст или NULL при ошибке */ struct dummynet* dummynet_create(struct UASYNC* ua, const char* bind_ip, uint16_t listen_port); diff --git a/tests/bbr_integration/test_bbr_integration.c b/tests/bbr_integration/test_bbr_integration.c index 40dc02e7..a808f503 100644 --- a/tests/bbr_integration/test_bbr_integration.c +++ b/tests/bbr_integration/test_bbr_integration.c @@ -36,12 +36,9 @@ #include "../src/config_updater.h" #include "routing.h" #include "crc32.h" +#include "test_net.h" /* ===== Test constants ===== */ -#define DN_PORT 21001 -#define SRV_PORT 21002 -#define CLI_PORT 21000 - #define PAYLOAD_SIZE 1200 #define TEST_DURATION_MS 3500 #define METRICS_TB 1000 /* 100ms in 0.1ms units */ @@ -343,12 +340,14 @@ int main(void) { ctx.sender->test_user_ptr = &ctx; ctx.receiver->test_user_ptr = &ctx; - if (add_server(ctx.receiver, "srv", SRV_PORT) < 0 || - add_server(ctx.sender, "local", CLI_PORT) < 0 || + if (add_server(ctx.receiver, "srv", 0) < 0 || + add_server(ctx.sender, "local", 0) < 0 || add_client(ctx.sender, s_pub) < 0) { printf("ERROR: config setup failed\n"); return 1; } - add_link(ctx.sender->config->clients, ctx.sender->config->servers, DN_PORT); + ctx.dn = dummynet_create(ctx.ua, "127.0.0.1", 0); + if (!ctx.dn) { printf("ERROR: dummynet_create failed\n"); return 1; } + add_link(ctx.sender->config->clients, ctx.sender->config->servers, dummynet_get_listen_port(ctx.dn)); printf("Init receiver...\n"); if (utun_instance_init(ctx.receiver) < 0) { printf("ERROR: receiver init failed\n"); return 1; } @@ -357,16 +356,16 @@ int main(void) { etcp_bind(ctx.receiver, ETCP_RT_ID_DATA, on_recv); - printf("Creating dummynet on port %d ...\n", DN_PORT); - ctx.dn = dummynet_create(ctx.ua, "127.0.0.1", DN_PORT); - if (!ctx.dn) { printf("ERROR: dummynet_create failed\n"); return 1; } + int receiver_port = ss_get_port(&test_etcp_bound_socket(ctx.receiver, "srv")->local_addr); + int sender_port = ss_get_port(&test_etcp_bound_socket(ctx.sender, "local")->local_addr); + printf("Ports assigned: client=%d dummynet=%u server=%d\n", sender_port, dummynet_get_listen_port(ctx.dn), receiver_port); dummynet_set_direction(ctx.dn, DUMMYNET_FORWARD, EMU_DELAY_MS, EMU_JITTER_MS, EMU_BW_KBPS, EMU_QUEUE_PKTS, EMU_LOSS_PERM, - "127.0.0.1", SRV_PORT); + "127.0.0.1", (uint16_t)receiver_port); dummynet_set_direction(ctx.dn, DUMMYNET_BACKWARD, 5, 1, 0, 200, 0, - "127.0.0.1", CLI_PORT); + "127.0.0.1", (uint16_t)sender_port); /* Wait for connection */ printf("Waiting for connection...\n"); diff --git a/tests/test_etcp_router.c b/tests/test_etcp_router.c index a4b4cdbe..8c458f71 100644 --- a/tests/test_etcp_router.c +++ b/tests/test_etcp_router.c @@ -12,6 +12,7 @@ #include "../lib/platform_compat.h" #include "../lib/socket_compat.h" #include "test_utils.h" +#include "test_net.h" #ifdef _WIN32 #include #include @@ -125,40 +126,6 @@ static double toc_ms(const struct timespec* start) { return (end.tv_sec - start->tv_sec) * 1000.0 + (end.tv_nsec - start->tv_nsec) / 1000000.0; } -// ======================== Port allocation ======================== -static int alloc_consecutive_ports(int* base_port) { - for (int attempt = 0; attempt < 200; attempt++) { - socket_t s = socket_create_udp(AF_INET); - if (s == SOCKET_INVALID) return -1; - struct sockaddr_in a; - memset(&a, 0, sizeof(a)); - a.sin_family = AF_INET; - a.sin_addr.s_addr = inet_addr("127.0.0.1"); - a.sin_port = 0; // auto-assign - if (bind(s, (struct sockaddr*)&a, sizeof(a)) != 0) { socket_close_wrapper(s); continue; } - socklen_t alen = sizeof(a); - getsockname(s, (struct sockaddr*)&a, &alen); - int port = ntohs(a.sin_port); - socket_close_wrapper(s); - // Need port-1, port, port+1 all free - int ok = 1; - for (int p = port - 1; p <= port + 1 && ok; p++) { - if (p < 1024) { ok = 0; break; } - socket_t ts = socket_create_udp(AF_INET); - if (ts == SOCKET_INVALID) { ok = 0; break; } - struct sockaddr_in ta; - memset(&ta, 0, sizeof(ta)); - ta.sin_family = AF_INET; - ta.sin_addr.s_addr = inet_addr("127.0.0.1"); - ta.sin_port = htons((uint16_t)p); - if (bind(ts, (struct sockaddr*)&ta, sizeof(ta)) != 0) ok = 0; - socket_close_wrapper(ts); - } - if (ok) { *base_port = port; return 0; } - } - return -1; -} - // ======================== Config generation ======================== static const char* srv_priv = "38240cb82199e504686507f11f6eaa4f740fde6f0c425c495e49a523019a5d68"; static const char* srv_pub = "ce8871f07fa056c636d297115f231b08c29cdf94e0d440fce83a07c34416d36a"; @@ -393,8 +360,7 @@ static int test_loopback(void) { // ======================== Dummynet setup ======================== static int setup_dummynet(void) { - dn = dummynet_create(ua, "127.0.0.1", (uint16_t)g_dn_port); - if (!dn) { printf("[FAIL] dummynet_create on port %d\n", g_dn_port); return -1; } + if (!dn) { printf("[FAIL] dummynet listener missing\n"); return -1; } // Forward: dummynet → server dummynet_set_direction(dn, DUMMYNET_FORWARD, 0, 0, 0, DUMMY_QUEUE_SIZE, 0, "127.0.0.1", (uint16_t)g_srv_port); @@ -694,20 +660,6 @@ int main(void) { if (!g_reset_mode) g_reset_mode = g_tcp ? "both" : "client"; if (test_mkdtemp(temp_dir) != 0) { fprintf(stderr, "mkdtemp fail\n"); return 1; } - // Allocate consecutive ports - int base_port; - if (alloc_consecutive_ports(&base_port) != 0) { - printf("[FAIL] cannot allocate consecutive ports\n"); - test_rmdir(temp_dir); - return 1; - } - g_dn_port = base_port; - g_srv_port = base_port + 1; - g_cli_port = base_port - 1; - printf("Ports: dummynet=%d server=%d client=%d\n", g_dn_port, g_srv_port, g_cli_port); - - write_configs(); - printf("=== test_etcp_router (%s, bounded backpressure and transport recovery) ===\n", g_tcp ? "TCP" : "UDP"); debug_config_init(); @@ -725,11 +677,23 @@ int main(void) { tic(&t_phase_start); ua = uasync_create(); if (!ua) { printf("[FAIL] uasync_create\n"); goto done; } + if (!g_tcp) { + dn = dummynet_create(ua, "127.0.0.1", 0); + if (!dn) { printf("[FAIL] dummynet create\n"); goto done; } + g_dn_port = dummynet_get_listen_port(dn); + } + write_configs(); srv = utun_instance_create(ua, server_conf); if (!srv || utun_instance_init(srv) < 0) { printf("[FAIL] server create\n"); goto done; } cli = utun_instance_create(ua, client_conf); + if (cli) { + g_srv_port = ss_get_port(&test_etcp_bound_socket(srv, "s1")->local_addr); + g_cli_port = ss_get_port(&test_etcp_bound_socket(cli, "s1")->local_addr); + if (!g_tcp && setup_dummynet() != 0) { printf("[FAIL] dummynet setup\n"); goto done; } + printf("Ports: dummynet=%d server=%d client=%d\n", g_dn_port, g_srv_port, g_cli_port); + } if (!cli || utun_instance_init(cli) < 0) { printf("[FAIL] client create\n"); goto done; } server_node_id = srv->node_id; client_node_id = cli->node_id; @@ -743,9 +707,6 @@ int main(void) { } } - // Create dummynet between client and server - if (!g_tcp && setup_dummynet() != 0) { printf("[FAIL] dummynet setup\n"); goto done; } - g_mon_id = uasync_set_timeout(ua, 100, NULL, monitor, "mon"); void* to_id = uasync_set_timeout(ua, TEST_TIMEOUT_MS * 10, NULL, timeout_cb, "to");