Test held NAT mappings under TUN congestion and reject invalid provider input

This commit is contained in:
evgeny committed 2026-10-21 22:54:24 +02:00
1 parent dea768c21a
commit 55cc721c9b
1 file changed
+71 -11
+71 -11
View File
@@ -673,37 +673,47 @@ static int test_router_backpressure(int provider) {
#ifdef __linux__
/* EAGAIN удерживает первый IP-пакет; второй остаётся в router recv_q. После возобновления оба доставляются по порядку. */
static int test_tun_write_backpressure(void) {
write_blocked_tun = inst_client->nat_tr.nat_tun;
static int test_tun_write_backpressure(int provider) {
struct UTUN_INSTANCE* receiver = provider ? inst_provider : inst_client;
struct UTUN_INSTANCE* sender = provider ? inst_client : inst_provider;
write_blocked_tun = receiver->nat_tr.nat_tun;
for (uint32_t i = 1; i <= 2; i++) {
size_t len;
uint8_t* ip = build_udp_pkt(0x08080808, 53, 0x0a0000cd, 44444, &len);
uint8_t* ip = provider ? build_udp_pkt(0x0a0000ab, 40101, 0x08080808, 44444, &len) :
build_udp_pkt(0x08080808, 53, 0x0a0000cd, 44444, &len);
memcpy(ip + 28, &i, 4);
struct ll_entry* packet = ll_alloc_lldgram(len + 1);
if (!packet) { free(ip); write_blocked_tun = NULL; return 0; }
packet->len = len + 1; packet->dgram[0] = ETCP_RT_ID_NAT;
memcpy(packet->dgram + 1, ip, len); free(ip);
if (etcp_route_send(inst_provider, TOPO_GROUP_UTUN, g_client_node_id, packet, 0, 0)) { write_blocked_tun = NULL; return 0; }
if (etcp_route_send(sender, TOPO_GROUP_UTUN, receiver->node_id, packet, 0, 0)) { write_blocked_tun = NULL; return 0; }
}
struct ETCP_ROUTER_CONN* incoming = etcp_router_conn_get(inst_client, TOPO_GROUP_UTUN, g_provider_node_id, ETCP_RT_ID_NAT);
struct ETCP_ROUTER_CONN* incoming = etcp_router_conn_get(receiver, TOPO_GROUP_UTUN, sender->node_id, ETCP_RT_ID_NAT);
for (int cycles = 0; cycles < 500 && !test_timed_out && !incoming->receive_blocked; cycles++) uasync_poll(ua, 5);
int ok = inst_client->nat_tr.tun_pending && incoming->receive_blocked;
int ok = receiver->nat_tr.tun_pending && incoming->receive_blocked;
if (provider && receiver->nat_tr.tun_pending_port) {
uint16_t port = receiver->nat_tr.tun_pending_port;
uint64_t deadline = receiver->nat.table[port].last_seen + EIM_NAT_UDP_IDLE_TB;
eim_nat_expire(&receiver->nat, deadline, 65535);
if (!receiver->nat.table[port].holds || receiver->nat.table[port].state != EIM_NAT_ENTRY_ACTIVE) ok = 0;
} else if (provider) ok = 0;
write_blocked_tun = NULL;
uint32_t received = 0;
for (int cycles = 0; cycles < 500 && !test_timed_out && received < 2; cycles++) {
uasync_poll(ua, 5);
uint8_t data[1500]; ssize_t n;
while ((n = tun_read_packet(inst_client->nat_tr.nat_tun, data, sizeof(data))) > 0) {
while ((n = tun_read_packet(receiver->nat_tr.nat_tun, data, sizeof(data))) > 0) {
uint32_t serial = 0;
if (n >= 32) memcpy(&serial, data + 28, 4);
if (n != 42 || serial != received + 1) ok = 0;
received++;
}
}
if (received != 2 || inst_client->nat_tr.tun_pending || incoming->receive_blocked) ok = 0;
if (ok) DEBUG_INFO(DEBUG_CATEGORY_NAT, "PASS TUN write backpressure: retained packet, router pause and ordered resume");
if (received != 2 || receiver->nat_tr.tun_pending || receiver->nat_tr.tun_pending_port || incoming->receive_blocked) ok = 0;
if (ok) DEBUG_INFO(DEBUG_CATEGORY_NAT, "PASS %s TUN write backpressure: retained packet, held mapping, router pause and ordered resume",
provider ? "provider" : "client");
else DEBUG_ERROR(DEBUG_CATEGORY_NAT, "FAIL TUN write backpressure: received=%u pending=%p blocked=%u", received,
(void*)inst_client->nat_tr.tun_pending, incoming->receive_blocked);
(void*)receiver->nat_tr.tun_pending, incoming->receive_blocked);
return ok;
}
#endif
@@ -772,6 +782,51 @@ static int test_global_backpressure(void) {
return ok;
}
#ifdef __linux__
/* Stop обязан отменить retry и освободить удержанный mapping до уничтожения TUN/таблицы. */
static int test_shutdown_pending_write(void) {
if (nat_transport_init(inst_provider)) return 0;
write_blocked_tun = inst_provider->nat_tr.nat_tun;
size_t len;
uint8_t* ip = build_udp_pkt(0x0a000002, 40000, 0x08080808, 53, &len);
struct ll_entry* packet = ll_alloc_lldgram(len + 1);
if (!packet) { free(ip); write_blocked_tun = NULL; return 0; }
packet->len = len + 1; packet->dgram[0] = ETCP_RT_ID_NAT; memcpy(packet->dgram + 1, ip, len); free(ip);
int result = etcp_route_send(inst_provider, TOPO_GROUP_UTUN, inst_provider->node_id, packet, 0, 0);
int ok = !result && inst_provider->nat_tr.tun_pending && inst_provider->nat_tr.tun_pending_port;
nat_transport_destroy(inst_provider); write_blocked_tun = NULL;
uasync_poll(ua, 0);
if (inst_provider->nat_tr.tun_pending || inst_provider->nat_tr.tun_retry_timer || inst_provider->nat.table) ok = 0;
if (ok) DEBUG_INFO(DEBUG_CATEGORY_NAT, "PASS shutdown with held outgoing mapping and active TUN retry");
else DEBUG_ERROR(DEBUG_CATEGORY_NAT, "FAIL shutdown with pending TUN write");
return ok;
}
#endif
/* Ни повреждённый IPv4, ни фрагмент не попадает в TUN провайдера и не расходует порты. */
static int test_provider_rejects_invalid(void) {
uint8_t drain[1500];
while (tun_read_packet(inst_provider->nat_tr.nat_tun, drain, sizeof(drain)) > 0) {}
uint32_t free_before = inst_provider->nat.free_count;
uint64_t dropped_before = inst_provider->nat_tr.dropped;
for (unsigned int i = 0; i < 3; i++) {
size_t len;
uint8_t* ip = build_udp_pkt(0x0a000002, 40000, 0x08080808, 53, &len);
if (i == 0) ip[0] = 0x65;
if (i == 1) { uint16_t total = htobe16(100); memcpy(ip + 2, &total, 2); }
if (i == 2) ip[6] = 0x20;
struct ll_entry* packet = ll_alloc_lldgram(len + 1);
if (!packet) { free(ip); return 0; }
packet->len = len + 1; packet->dgram[0] = ETCP_RT_ID_NAT; memcpy(packet->dgram + 1, ip, len); free(ip);
if (etcp_route_send(inst_provider, TOPO_GROUP_UTUN, inst_provider->node_id, packet, 0, 0)) return 0;
}
int ok = tun_read_packet(inst_provider->nat_tr.nat_tun, drain, sizeof(drain)) == 0 &&
inst_provider->nat.free_count == free_before && inst_provider->nat_tr.dropped == dropped_before + 3;
if (ok) DEBUG_INFO(DEBUG_CATEGORY_NAT, "PASS invalid/fragmented IP rejected before TUN delivery");
else DEBUG_ERROR(DEBUG_CATEGORY_NAT, "FAIL invalid provider input");
return ok;
}
// ==================== Main ====================
int main(void) {
int test_result = 1;
@@ -845,13 +900,18 @@ int main(void) {
// Реальная доставка после инъекций проверяет восстановление обеих веток без перезапуска NAT.
total++; if (test_full_roundtrip()) passed++; else DEBUG_ERROR(DEBUG_CATEGORY_NAT, "FAIL: full_roundtrip");
total++; if (test_static_forward_delivery()) passed++; else DEBUG_ERROR(DEBUG_CATEGORY_NAT, "FAIL: static_forward");
total++; if (test_provider_rejects_invalid()) passed++; else DEBUG_ERROR(DEBUG_CATEGORY_NAT, "FAIL: provider_invalid");
total++; if (test_router_backpressure(0)) passed++; else DEBUG_ERROR(DEBUG_CATEGORY_NAT, "FAIL: client_backpressure");
total++; if (test_router_backpressure(1)) passed++; else DEBUG_ERROR(DEBUG_CATEGORY_NAT, "FAIL: provider_backpressure");
#ifdef __linux__
total++; if (test_tun_write_backpressure()) passed++; else DEBUG_ERROR(DEBUG_CATEGORY_NAT, "FAIL: tun_write_backpressure");
total++; if (test_tun_write_backpressure(0)) passed++; else DEBUG_ERROR(DEBUG_CATEGORY_NAT, "FAIL: client_tun_backpressure");
total++; if (test_tun_write_backpressure(1)) passed++; else DEBUG_ERROR(DEBUG_CATEGORY_NAT, "FAIL: provider_tun_backpressure");
#endif
total++; if (test_slow_peer_and_shutdown()) passed++; else DEBUG_ERROR(DEBUG_CATEGORY_NAT, "FAIL: slow_peer_shutdown");
total++; if (test_global_backpressure()) passed++; else DEBUG_ERROR(DEBUG_CATEGORY_NAT, "FAIL: global_backpressure");
#ifdef __linux__
total++; if (test_shutdown_pending_write()) passed++; else DEBUG_ERROR(DEBUG_CATEGORY_NAT, "FAIL: shutdown_pending_write");
#endif
printf("\n=== NAT Transport Tests: %d/%d passed ===\n", passed, total);
test_result = (passed == total) ? 0 : 1;