Hold outgoing NAT mappings through asynchronous TUN delivery
This commit is contained in:
1 parent
824c663ebd
commit
dea768c21a
2 files changed
+56
-1
No files matched your search
+23
-1
@@ -178,11 +178,32 @@ static void nat_tun_flush(struct UTUN_INSTANCE* inst) {
|
||||
struct ll_entry* entry = tr->tun_pending;
|
||||
if (!entry) return;
|
||||
size_t len = entry->len - ROUTER_SVC_PAYLOAD_OFF;
|
||||
if (tun_write(tr->nat_tun, entry->dgram + ROUTER_SVC_PAYLOAD_OFF, len) < 0 && (errno == EAGAIN || errno == EWOULDBLOCK)) {
|
||||
const uint8_t* ip = entry->dgram + ROUTER_SVC_PAYLOAD_OFF;
|
||||
ssize_t written = tun_write(tr->nat_tun, ip, len);
|
||||
if (written < 0 && (errno == EAGAIN || errno == EWOULDBLOCK)) {
|
||||
if (!tr->nat_via_node_id && !tr->tun_pending_port) {
|
||||
uint16_t port;
|
||||
memcpy(&port, ip + (ip[0] & 15) * 4 + (ip[9] == IPPROTO_ICMP_UINT8 ? 4 : 0), 2);
|
||||
port = ntohs(port);
|
||||
if (eim_nat_hold(&inst->nat, port)) {
|
||||
nat_drop(inst, entry, 0, "cannot hold outgoing mapping"); tr->tun_pending = NULL;
|
||||
return;
|
||||
}
|
||||
tr->tun_pending_port = port;
|
||||
DEBUG_DEBUG(DEBUG_CATEGORY_NAT, "NAT TUN blocked: held mapping external=%u", port);
|
||||
}
|
||||
if (!tr->tun_retry_timer) tr->tun_retry_timer = uasync_set_timeout(inst->ua, NAT_WRITE_RETRY_TB, inst, nat_tun_retry, "nat_tun_retry");
|
||||
if (!tr->tun_retry_timer) DEBUG_ERROR(DEBUG_CATEGORY_NAT, "NAT TUN retry allocation failed; maintenance will retry");
|
||||
return;
|
||||
}
|
||||
if (tr->tun_pending_port) {
|
||||
eim_nat_release(&inst->nat, tr->tun_pending_port, written >= 0);
|
||||
tr->tun_pending_port = 0;
|
||||
}
|
||||
if (tr->tun_retry_timer) {
|
||||
uasync_cancel_timeout(inst->ua, tr->tun_retry_timer);
|
||||
tr->tun_retry_timer = NULL;
|
||||
}
|
||||
nat_packet_free(entry); tr->tun_pending = NULL;
|
||||
if (inst->router_conns) {
|
||||
for (struct ll_entry* e = inst->router_conns->head; e; e = e->next) {
|
||||
@@ -325,6 +346,7 @@ void nat_transport_destroy(struct UTUN_INSTANCE* inst) {
|
||||
if (!route) break;
|
||||
etcp_router_conn_close(route);
|
||||
}
|
||||
if (tr->tun_pending_port) eim_nat_release(&inst->nat, tr->tun_pending_port, 0);
|
||||
nat_packet_free(tr->tun_pending);
|
||||
tun_close(tr->nat_tun);
|
||||
eim_nat_destroy_ctx(&inst->nat);
|
||||
|
||||
@@ -1220,6 +1220,38 @@ static void test_mapping_expiry(void) {
|
||||
#endif
|
||||
}
|
||||
|
||||
/* Каждый успешный пакет обновляет idle-time; удержанный внешний порт не может перейти другому владельцу. */
|
||||
static void test_refresh_and_holds(void) {
|
||||
TEST("idle_refresh_and_pending_mapping_hold");
|
||||
struct global_config g = make_global_config(); struct eim_nat_ctx ctx;
|
||||
ASSERT_EQ(eim_nat_init_ctx(&ctx, &g), 0, "hold context initialized");
|
||||
uint8_t* packet = make_udp_pkt(TEST_IP_SRC_HOST, TEST_SRC_PORT, TEST_IP_DST_HOST, TEST_DST_PORT);
|
||||
ASSERT_EQ(eim_nat_egress(&ctx, packet, 42, 1, NULL), 0, "create held mapping");
|
||||
free(packet);
|
||||
uint16_t external = TEST_PORT_START;
|
||||
struct eim_nat_entry* entry = &ctx.table[external];
|
||||
entry->last_seen = 0; // Управляем только возрастом; ключ/индексы остаются неизменными.
|
||||
packet = make_udp_pkt(TEST_IP_SRC_HOST, TEST_SRC_PORT, TEST_IP_DST_HOST, TEST_DST_PORT);
|
||||
ASSERT_EQ(eim_nat_egress(&ctx, packet, 42, 1, NULL), 0, "refresh outbound mapping"); free(packet);
|
||||
ASSERT(entry->last_seen > 0, "outbound refreshes last_seen");
|
||||
entry->last_seen = 0;
|
||||
packet = make_udp_pkt(TEST_IP_DST_HOST, TEST_DST_PORT, TEST_GW_HOST, external);
|
||||
struct eim_nat_entry* incoming = NULL;
|
||||
ASSERT_EQ(eim_nat_ingress(&ctx, packet, 42, &incoming), 0, "refresh inbound mapping"); free(packet);
|
||||
ASSERT(incoming == entry && entry->last_seen > 0, "inbound refreshes last_seen");
|
||||
ASSERT_EQ(eim_nat_hold(&ctx, external), 0, "hold external port");
|
||||
ASSERT_EQ(eim_nat_expire(&ctx, entry->last_seen + EIM_NAT_UDP_IDLE_TB, 65535), 0, "pending transmission prevents expiry");
|
||||
ASSERT_EQ(eim_nat_remove(&ctx, external), -1, "pending transmission prevents explicit reuse");
|
||||
entry->last_seen = 0;
|
||||
ASSERT_EQ(eim_nat_release(&ctx, external, 1), 0, "successful transmission releases hold");
|
||||
ASSERT(entry->last_seen > 0 && !entry->holds, "release restarts idle timer at actual send");
|
||||
ASSERT_EQ(eim_nat_expire(&ctx, entry->last_seen + EIM_NAT_UDP_IDLE_TB, 65535), 1, "released mapping expires");
|
||||
ASSERT_EQ(eim_nat_release(&ctx, external, 0), -1, "unpaired release rejected");
|
||||
ASSERT(check_indices(&ctx), "holds preserve indices and free count");
|
||||
eim_nat_destroy_ctx(&ctx);
|
||||
PASS();
|
||||
}
|
||||
|
||||
/* ================================================================
|
||||
* Main
|
||||
* ================================================================ */
|
||||
@@ -1247,6 +1279,7 @@ int main(void) {
|
||||
test_packet_boundaries();
|
||||
test_hash_lifecycle();
|
||||
test_mapping_expiry();
|
||||
test_refresh_and_holds();
|
||||
|
||||
printf("\n=== Results: %d run, %d passed, %d failed ===\n",
|
||||
stats.run, stats.passed, stats.failed);
|
||||
|
||||
Reference in new issue
Block a user