From 5e2e6c15cfd13a96279649a323bdd916bea20943 Mon Sep 17 00:00:00 2001 From: evgeny Date: Wed, 21 Oct 2026 22:31:55 +0200 Subject: [PATCH] Preserve NAT index invariants across transport and reuse tests --- src/nat_transport.c | 2 ++ tests/test_nat_stress.c | 4 ++-- tests/test_nat_transport.c | 23 +++++++---------------- 3 files changed, 11 insertions(+), 18 deletions(-) diff --git a/src/nat_transport.c b/src/nat_transport.c index 8630f1b8..9c31060b 100644 --- a/src/nat_transport.c +++ b/src/nat_transport.c @@ -102,9 +102,11 @@ static struct nat_peer* nat_peer_get(struct UTUN_INSTANCE* inst, uint64_t node_i return NULL; } queue_set_callback(peer->packets, nat_peer_take, peer); + struct ll_queue* packets = peer->packets; if (queue_data_put_with_index(tr->peers, &peer->ll) != 0) { // put принимает/освобождает peer; отдельная очередь принадлежит вызывающему до успеха. DEBUG_ERROR(DEBUG_CATEGORY_NAT, "NAT peer index insertion failed node=%016llx", (unsigned long long)node_id); + queue_free(packets); return NULL; } return peer; diff --git a/tests/test_nat_stress.c b/tests/test_nat_stress.c index 94229b0c..08c1ead1 100644 --- a/tests/test_nat_stress.c +++ b/tests/test_nat_stress.c @@ -179,6 +179,8 @@ static void stress_egress_ingress_cycle(void) { ASSERT_EQ(eim_nat_init_ctx(&ctx, &g), 0, "init"); const int CYCLES = 5000; + ASSERT_EQ(eim_nat_add_forward(&ctx, IPPROTO_UDP_UINT8, 0x0a000003, htobe16(50000), 1001, 2), + 0, "reserve second port for cyclic reuse"); size_t len; for (int cycle = 0; cycle < CYCLES; cycle++) { uint8_t* pkt = make_udp_pkt(0x0A000002, 40000, 0x08080808, 53, &len); @@ -205,8 +207,6 @@ static void stress_egress_ingress_cycle(void) { // Free entry for next cycle ASSERT_EQ(eim_nat_remove(&ctx, 1000), 1, "remove updates hash and free pool"); - ASSERT_EQ(eim_nat_add_forward(&ctx, IPPROTO_UDP_UINT8, 0x0a000003, htobe16(50000), 1001, 2), - cycle == 0 ? 0 : -1, "other port remains reserved"); } eim_nat_destroy_ctx(&ctx); diff --git a/tests/test_nat_transport.c b/tests/test_nat_transport.c index e4d5197b..18aef915 100644 --- a/tests/test_nat_transport.c +++ b/tests/test_nat_transport.c @@ -461,14 +461,7 @@ static int test_provider_ingress_response(void) { static int test_send_failure_ownership(int provider) { struct UTUN_INSTANCE* sender = provider ? inst_provider : inst_client; struct tun_if* tun = sender->nat_tr.nat_tun; - struct eim_nat_entry saved = {0}; uint16_t external_port = inst_provider->nat.port_start; - if (provider) { - saved = inst_provider->nat.table[external_port]; - inst_provider->nat.table[external_port] = (struct eim_nat_entry){.internal_ip = 0x0a0000cd, - .internal_port = htobe16(44444), .proto = IPPROTO_UDP_UINT8, - .state = EIM_NAT_ENTRY_ACTIVE, .src_node_id = g_client_node_id}; - } size_t ip_len; uint8_t* ip = provider ? build_udp_pkt(0x08080808, 80, inst_provider->nat.gateway_ip, external_port, &ip_len) : build_udp_pkt(0x0a0000cd, 44444, 0x08080808, 80, &ip_len); @@ -487,7 +480,6 @@ static int test_send_failure_ownership(int provider) { if (!send_test_valid || queue_entry_count(tun->output_queue)) ok = 0; send_test_mode = 0; send_test_instance = NULL; free(ip); - if (provider) inst_provider->nat.table[external_port] = saved; if (ok) DEBUG_INFO(DEBUG_CATEGORY_NAT, "PASS NAT %s send failure: no double free/leak, next packet accepted", provider ? "provider" : "client"); else DEBUG_ERROR(DEBUG_CATEGORY_NAT, "FAIL NAT %s send failure: calls=%d valid=%d live_blocks=%zu baseline=%zu", @@ -548,10 +540,9 @@ static int test_full_roundtrip(void) { struct tun_if* client_tun = inst_client->nat_tr.nat_tun; if (!client_tun) { DEBUG_ERROR(DEBUG_CATEGORY_NAT, "No client TUN"); return 0; } - // Clean NAT table from previous test - for (uint16_t p = inst_provider->nat.port_start; p <= inst_provider->nat.port_end; p++) - memset(&inst_provider->nat.table[p], 0, sizeof(struct eim_nat_entry)); - inst_provider->nat.next_port = inst_provider->nat.port_start; + // Создать новый mapping без перезапуска/обнуления внутренних индексов после проверок отказов. + uint16_t external_port = inst_provider->nat.next_port; + if (!external_port) { DEBUG_ERROR(DEBUG_CATEGORY_NAT, "No free port for roundtrip"); return 0; } // === Step 1: ОС клиента отправляет пакет в NAT TUN === size_t ip_len; @@ -564,18 +555,18 @@ static int test_full_roundtrip(void) { int cycles = 0; while (cycles < 200 && !test_timed_out) { uasync_poll(ua, 5); cycles++; - if (inst_provider->nat.table[inst_provider->nat.port_start].state != EIM_NAT_ENTRY_FREE) break; + if (inst_provider->nat.table[external_port].state != EIM_NAT_ENTRY_FREE) break; } // Verify NAT entry - struct eim_nat_entry* e = &inst_provider->nat.table[inst_provider->nat.port_start]; + struct eim_nat_entry* e = &inst_provider->nat.table[external_port]; if (e->state != EIM_NAT_ENTRY_ACTIVE || e->internal_ip != 0x0A0000CD || e->internal_port != htobe16(44444)) { DEBUG_ERROR(DEBUG_CATEGORY_NAT, "Egress entry not found: ip=%08x port=%u state=%d", e->internal_ip, be16toh(e->internal_port), e->state); return 0; } DEBUG_INFO(DEBUG_CATEGORY_NAT, "Egress entry ok: %08x:%u ext=%u proto=%u", - e->internal_ip, be16toh(e->internal_port), inst_provider->nat.port_start, e->proto); + e->internal_ip, be16toh(e->internal_port), external_port, e->proto); // === Step 2: Manually inject internet response into provider's TUN === memset(&test_state.captured, 0, sizeof(test_state.captured)); @@ -584,7 +575,7 @@ static int test_full_roundtrip(void) { uint32_t gw_ip_host = inst_provider->nat.gateway_ip; size_t rip_len; - uint8_t* resp_ip = build_udp_pkt(0x08080808, 80, gw_ip_host, inst_provider->nat.port_start, &rip_len); + uint8_t* resp_ip = build_udp_pkt(0x08080808, 80, gw_ip_host, external_port, &rip_len); if (tun_inject_packet(inst_provider->nat_tr.nat_tun, resp_ip, rip_len) != 0) { DEBUG_ERROR(DEBUG_CATEGORY_NAT, "tun_inject response failed"); free(resp_ip); return 0;