From 6e9bacd84307f51ac65d09a7c3bcf789c752cf8e Mon Sep 17 00:00:00 2001 From: evgeny Date: Wed, 21 Oct 2026 21:48:28 +0200 Subject: [PATCH] Fix NAT flow lookup at the maximum port boundary --- src/eim_nat.c | 3 ++- src/eim_nat.h | 4 ++-- tests/test_nat_engine.c | 53 +++++++++++++++++++++++++++++++++++++++++ 3 files changed, 57 insertions(+), 3 deletions(-) diff --git a/src/eim_nat.c b/src/eim_nat.c index e7e84184..4d567706 100644 --- a/src/eim_nat.c +++ b/src/eim_nat.c @@ -67,9 +67,10 @@ static void csum_update_icmp(uint8_t* icmp, uint16_t old_word, uint16_t new_word memcpy(icmp + ICMP_CHECKSUM_OFFSET, &new_csum, 2); } +// Поиск в замкнутом диапазоне; индекс должен вмещать 65536 после последнего порта. static struct eim_nat_entry* eim_nat_find_egress(struct eim_nat_ctx* ctx, uint8_t proto, uint32_t ip_host, uint16_t port_net) { - for (uint16_t i = ctx->port_start; i <= ctx->port_end; i++) { + for (uint32_t i = ctx->port_start; i <= ctx->port_end; i++) { struct eim_nat_entry* e = &ctx->table[i]; if (e->state != EIM_NAT_ENTRY_FREE && e->proto == proto && e->internal_ip == ip_host && e->internal_port == port_net) return e; diff --git a/src/eim_nat.h b/src/eim_nat.h index 90623108..70677523 100644 --- a/src/eim_nat.h +++ b/src/eim_nat.h @@ -40,8 +40,8 @@ struct eim_nat_entry { // Состояние преобразования; TUN и транспортные ресурсы принадлежат nat_transport. struct eim_nat_ctx { uint32_t gateway_ip; - uint16_t port_start; - uint16_t port_end; + uint16_t port_start; // Начало динамического диапазона включительно; 1 <= port_start < port_end. + uint16_t port_end; // Конец диапазона включительно; допускается 65535. uint16_t next_port; struct eim_nat_entry* table; size_t table_size; diff --git a/tests/test_nat_engine.c b/tests/test_nat_engine.c index 601377f5..4c0efdd4 100644 --- a/tests/test_nat_engine.c +++ b/tests/test_nat_engine.c @@ -297,6 +297,58 @@ static void test_port_alloc(void) { PASS(); } +/* Верхняя граница 65535: поиск отсутствующего потока, оба порта, wrap, reuse и исчерпание. */ +static void test_port_limit(void) { + TEST("port_range_ending_at_65535"); + struct global_config g = make_global_config(); + g.nat_port_start = 65534; g.nat_port_end = 65535; + struct eim_nat_ctx ctx; + ASSERT_EQ(eim_nat_init_ctx(&ctx, &g), 0, "init maximum port range"); + const size_t len = 28 + TEST_PAYLOAD_LEN; + for (unsigned int i = 0; i < 2; i++) { + uint8_t* pkt = make_udp_pkt(TEST_IP_SRC_HOST, TEST_SRC_PORT + i, TEST_IP_DST_HOST, TEST_DST_PORT); + int r = eim_nat_egress(&ctx, pkt, len, 1, get_mock_conn()); + uint16_t port; memcpy(&port, pkt + 20, 2); + free(pkt); + ASSERT_EQ(r, 0, "new flow completes at maximum range"); + ASSERT_EQ(be16toh(port), 65534 + i, "both boundary ports allocated"); + ASSERT_EQ(ctx.table[65534 + i].state, EIM_NAT_ENTRY_ACTIVE, "boundary entry active"); + } + ASSERT_EQ(ctx.next_port, 65534, "allocator wraps from 65535 to port_start"); + uint8_t* pkt = make_udp_pkt(TEST_IP_SRC_HOST, TEST_SRC_PORT + 2, TEST_IP_DST_HOST, TEST_DST_PORT); + uint8_t original[28 + TEST_PAYLOAD_LEN]; memcpy(original, pkt, len); + int r = eim_nat_egress(&ctx, pkt, len, 1, get_mock_conn()); + int unchanged = memcmp(pkt, original, len) == 0; + free(pkt); + ASSERT_EQ(r, -1, "exhausted maximum range returns error"); + ASSERT(unchanged, "exhaustion preserves packet"); + for (unsigned int i = 0; i < 2; i++) { + pkt = make_udp_pkt(TEST_IP_SRC_HOST, TEST_SRC_PORT + i, 0x08080404, TEST_DST_PORT); + r = eim_nat_egress(&ctx, pkt, len, 1, get_mock_conn()); + uint16_t port; memcpy(&port, pkt + 20, 2); + free(pkt); + ASSERT_EQ(r, 0, "existing flow works after exhaustion"); + ASSERT_EQ(be16toh(port), 65534 + i, "existing flow keeps boundary port"); + } + pkt = make_udp_pkt(TEST_IP_DST_HOST, TEST_DST_PORT, TEST_GW_HOST, 65535); + struct eim_nat_entry* entry = NULL; + r = eim_nat_ingress(&ctx, pkt, len, &entry); + uint16_t port; memcpy(&port, pkt + 22, 2); + free(pkt); + ASSERT_EQ(r, 0, "reply to 65535 accepted"); + ASSERT(entry == &ctx.table[65535], "reply finds last table entry"); + ASSERT_EQ(be16toh(port), TEST_SRC_PORT + 1, "reply restores internal port"); + ctx.table[65535].state = EIM_NAT_ENTRY_FREE; + pkt = make_udp_pkt(TEST_IP_SRC_HOST, TEST_SRC_PORT + 2, TEST_IP_DST_HOST, TEST_DST_PORT); + r = eim_nat_egress(&ctx, pkt, len, 1, get_mock_conn()); + memcpy(&port, pkt + 20, 2); + free(pkt); + ASSERT_EQ(r, 0, "allocator skips occupied port after wrap"); + ASSERT_EQ(be16toh(port), 65535, "allocator reuses free maximum port"); + eim_nat_destroy_ctx(&ctx); + PASS(); +} + /* ================================================================ * Test 3: Egress NAT — UDP * ================================================================ */ @@ -775,6 +827,7 @@ int main(void) { test_init_destroy(); test_port_alloc(); + test_port_limit(); test_egress_udp(); test_egress_tcp(); test_egress_icmp();