diff --git a/src/routing_layer/nat_detection.c b/src/routing_layer/nat_detection.c index 95c83c85..b231a2a0 100644 --- a/src/routing_layer/nat_detection.c +++ b/src/routing_layer/nat_detection.c @@ -16,6 +16,7 @@ #include "etcp_router.h" #include "route_crypto.h" #include "stcp_client.h" +#include "stun_nat.h" #include "sock_match.h" #include "../lib/debug_config.h" #include "../lib/mem.h" @@ -574,7 +575,7 @@ static void clear_profile(struct nat_socket *state, uint8_t reason) { } } -/* Публикуется только измерение рабочего endpoint; отдельный STUN-порт остаётся диагностикой. */ +/* Измерение рабочего endpoint приоритетно; STUN того же пути даёт только предварительный EIM?. */ static void select_profile(struct nat_socket *state) { const struct address_profile *current = &state->socket->address_profile; struct address_profile selected = state->measured_profile; @@ -586,6 +587,15 @@ static void select_profile(struct nat_socket *state) { if (!selected.mapping && state->observed_address.ss_family) { selected.source = ADDRESS_SOURCE_ETCP; selected_address = state->observed_address; } + struct stun_nat_result stun; + uint64_t now = get_time_tb(); + if (!selected.mapping && !stun_nat_get_socket_result(state->detector->instance, state->socket, &stun) && + (stun.status == STUN_NAT_COMPLETE || stun.status == STUN_NAT_PARTIAL) && + stun.measured_at_tb && now >= stun.measured_at_tb && now - stun.measured_at_tb < STUN_NAT_TTL_TB && + stun.server_count == 2 && stun.translated == 1 && (stun.mapping == STUN_MAP_MATCH || stun.mapping == STUN_MAP_EIM) && + (!selected_address.ss_family || route_ping_addr_equal(&selected_address, &stun.mapped[0], 0))) { + selected.mapping = ADDRESS_MAP_EIM; selected.inbound = ADDRESS_IN_UNKNOWN; selected.source = ADDRESS_SOURCE_STUN; + } int address_changed = selected_address.ss_family != state->socket->nat_addr.ss_family || (selected_address.ss_family && !route_ping_addr_equal(&selected_address, &state->socket->nat_addr, 1)); if (selected.mapping == current->mapping && selected.inbound == current->inbound && diff --git a/src/transport_layer/stun_nat.c b/src/transport_layer/stun_nat.c index 9fa18cf2..24ade510 100644 --- a/src/transport_layer/stun_nat.c +++ b/src/transport_layer/stun_nat.c @@ -25,7 +25,6 @@ #define STUN_OTHER 0x802c #define STUN_REQUESTS 7 #define STUN_SCAN_TB 30000 -#define STUN_TTL_TB UINT64_C(3000000) #define STUN_REFRESH_TB UINT64_C(1800000) #define STUN_RETRY_TB UINT64_C(300000) @@ -850,7 +849,7 @@ int stun_nat_get_socket_result(const struct UTUN_INSTANCE* inst, const struct ET for (const struct stun_probe* p = inst->stun_nat->probes; p; p = p->next) { if (!key_equal(&p->source.key, &source.key) || (!p->conflict && !policy_equal(&p->source, &source))) continue; uint64_t now = get_time_tb(); - int fresh = p->cached.measured_at_tb && now >= p->cached.measured_at_tb && now - p->cached.measured_at_tb < STUN_TTL_TB; + int fresh = p->cached.measured_at_tb && now >= p->cached.measured_at_tb && now - p->cached.measured_at_tb < STUN_NAT_TTL_TB; *result = fresh ? p->cached : p->result; result->refreshing = fresh && p->result.status == STUN_NAT_PENDING; result->members = p->members; diff --git a/src/transport_layer/stun_nat.h b/src/transport_layer/stun_nat.h index 79f7fd9a..7e14bceb 100644 --- a/src/transport_layer/stun_nat.h +++ b/src/transport_layer/stun_nat.h @@ -1,12 +1,15 @@ -/* Независимая STUN-диагностика интерфейсов существующих IPv4 UDP-сокетов. - * [stun] enabled=yes, повторяющийся server=host[:port]; нужны два разных IP. - * Обычный STUN сравнивает mapping; RFC 5780 дополнительно проверяет mapping/filtering. - * MATCH означает совпадение для двух серверов, а не доказанный EIM. - * Результат локальный: nat_detection, ETCP и NODEINFO не изменяются. - * Модуль создаёт собственный UDP-сокет на том же адресе/интерфейсе, с теми же mark/FIB, - * самостоятельно принимает STUN и закрывает fd. Результат относится к тестовому порту. - * Периодическая сверка замечает добавление/удаление/изменение исходных сокетов. - * Все вызовы выполняются в uasync-потоке; destroy отменяет DNS и таймеры до освобождения config. */ +/* Определяет поведение NAT через внешние STUN-серверы: совпадает ли внешний IP:порт + * при обращении к разным IP и доступны ли входящие без предварительного контакта. + * mapping: MATCH — два сервера видят одинаковый адрес; DEPENDENT — разные; + * RFC 5780 уточняет EIM/ADM/APDM и filtering (EIF/ADF/APDF). UNKNOWN — данных недостаточно. + * Для работы: [stun] enabled=yes и минимум две строки server=host[:port] с разными IP. + * Ядро вызывает init(instance)/destroy(instance), модуль сам измеряет и обновляет результат. + * get_socket_result(instance, socket, &result) возвращает снимок; recheck(instance) повторяет проверку. + * nat_detection использует совпавший mapping как предварительный EIM? для рабочих UDP-сокетов + * с теми же IP, интерфейсом и routing options; измерение рабочего сокета имеет приоритет. + * Проверка идёт с отдельного тестового порта: его внешний IP:порт не публикуется как адрес рабочего сокета, + * а filtering не переносится на рабочий сокет. Без RFC 5780 входящие остаются UNKNOWN. + * Все вызовы — в uasync-потоке ядра; destroy отменяет DNS/таймеры до освобождения config. */ #ifndef STUN_NAT_H #define STUN_NAT_H @@ -22,6 +25,8 @@ struct UTUN_INSTANCE; struct STUN_NAT; struct ETCP_SOCKET; +#define STUN_NAT_TTL_TB UINT64_C(3000000) /* Результат действителен 300 секунд; единица — 100 мкс. */ + /* Ключ пути не содержит порт/имя рабочего сокета. family — 4 или 6. */ struct stun_nat_key { uint8_t family, address[16]; uint32_t netif_index; };