nat: use same-interface STUN mapping as tentative EIM

This commit is contained in:
evgeny committed 2026-10-17 18:45:02 +02:00
1 parent aede36388f
commit 88ac08333e
3 files changed
+26 -12

No files matched your search

+11 -1
View File
@@ -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 &&
+1 -2
View File
@@ -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;
+14 -9
View File
@@ -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; };