Fix NAT flow lookup at the maximum port boundary
This commit is contained in:
1 parent
fbbd62acc4
commit
6e9bacd843
3 files changed
+57
-3
No files matched your search
+2
-1
@@ -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;
|
||||
|
||||
+2
-2
@@ -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;
|
||||
|
||||
@@ -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();
|
||||
|
||||
Reference in new issue
Block a user