Files
utun2/doc/stun_nat.md
T

105 lines
10 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# Независимая STUN-диагностика NAT
Модуль `src/transport_layer/stun_nat.c/h` измеряет NAT на уникальных путях существующих IPv4 UDP-сокетов.
Ключ группы — семейство, фактический IP и netif; имена и рабочие порты не участвуют.
Для каждой группы создаётся один собственный неблокирующий UDP-сокет с тем же локальным IP,
привязкой к устройству и Linux SO_MARK / FreeBSD FIB. Тестовый порт выбирается случайно из 49152–65535.
Модуль сам регистрирует чтение в uasync, принимает STUN и закрывает свой сокет после измерения.
STUN не меняет исходный ETCP fd, его порт и обработчики. Через `stun_nat` API модуль
`nat_detection` получает предварительную оценку для рабочих сокетов того же IP/netif
с совпадающими mark/FIB/device. Если два разных IP видят одинаковый стабильный внешний
IP:порт и есть трансляция адреса, рабочий профиль получает EIM? с source=STUN и inbound=UNKNOWN.
Это действует и для обычных серверов без запроса с незнакомого IP. Filtering тестового порта
не переносится; измерение mapping рабочего сокета всегда имеет приоритет.
Оценка снимается после TTL, отключения STUN, смены пути или противоречащего внешнего IP.
Тестовый mapped endpoint не копируется в `nat_addr` и NODEINFO: адрес для подключения
должен быть получен наблюдением самого рабочего сокета. Детектор описан в `doc/nat_detection.txt`.
## Конфигурация
```ini
[stun]
enabled=yes
server=stun-a.example.org:3478
server=stun-b.example.org:3478
# Дополнительные строки — резервные серверы.
[debug]
nat=debug
```
Без `enabled=yes` модуль выключен. Без порта используется 3478. Список сохраняет порядок конфига.
При включении нужны минимум две строки `server`; для измерения выбираются два ответивших сервера с различными IP.
Совпадающие IP после DNS пропускаются. DNS асинхронный, с использованием существующего IPv4 A-резолвера.
IPv6, SOCKS UDP и `only_local` пропускаются с явным статусом; TCP не проверяется.
## Результат
`stun_nat_get_result(instance, "имя исходного сокета", &result)` копирует локальный снимок:
Имя сначала разрешается в текущий рабочий сокет, затем в группу. API ядра
`stun_nat_get_socket_result(instance, socket, &result)` дополнительно проверяет
текущие IP/netif и параметры маршрутизации. Несколько рабочих портов получают общий снимок.
- `local` — тестовый IP:port; `servers[2]` и `mapped[2]` — реальные серверы и наблюдавшиеся внешние адреса;
- `translated`: -1 неизвестно, 0 трансляция не наблюдалась, 1 внешний адрес отличается;
- `mapping`: UNKNOWN, MATCH, DEPENDENT, EIM, ADM, APDM;
- `filtering`: UNKNOWN, EIF, ADF, APDF;
- причины неполного результата, статус, время измерения и обнаруженная поддержка RFC 5780.
- ключ группы, generation, evidence_id, measured_at_us, количество рабочих сокетов и признак refresh.
Обычный STUN даёт MATCH при одинаковых адресах и DEPENDENT при разных. MATCH не доказывает EIM,
а обычные Binding-ответы не позволяют определить filtering. Первоначальный сервер опрашивается повторно:
смена его mapping во время проверки инвалидирует классификацию как нестабильную.
## RFC 5780
Корректные OTHER-ADDRESS и RESPONSE-ORIGIN включают автоматическую попытку Behavior Discovery.
Нужны две публичные IP и два порта на серверной стороне, доступные во всех четырёх комбинациях.
1. Обычный Binding получает внешний адрес и альтернативный endpoint.
2. CHANGE-REQUEST с флагами IP+port проверяет EIF.
3. Если ответ не пришёл, контрольный Binding проверяет живость сервера и стабильность mapping.
4. CHANGE-REQUEST только port различает ADF и APDF; после таймаута снова выполняется контрольный Binding.
5. Перед публикацией ADF/APDF альтернативные endpoints проверяются обычными Binding-запросами.
Недоступный альтернативный endpoint даёт UNKNOWN, а не ложную строгую фильтрацию.
6. После filtering проверяется mapping: альтернативный IP с первоначальным портом, затем при необходимости
альтернативный IP с альтернативным портом. Сравнение различает EIM, ADM и APDM.
7. Второй сервер и повтор первого проверяют независимую пару и стабильность результата.
Все filtering-тесты выполняются до открывающих фильтр отправок на альтернативные endpoints.
Собственные предыдущие STUN-отправки учитываются: при пересечении адресов следующего сервера
сомнительный тест filtering пропускается. ERROR 420 и игнорирование CHANGE-REQUEST дают UNKNOWN.
Аутентификация STUN не реализована: требующий её сервер фиксируется как ошибка, затем используется резервный.
Запрос содержит случайный 96-битный transaction ID и FINGERPRINT. Проверяются источник ответа,
transaction ID, границы/выравнивание атрибутов, XOR-MAPPED-ADDRESS, RESPONSE-ORIGIN и FINGERPRINT при его наличии.
Повторы одной транзакции сохраняют ID: RTO от 500 мс удваивается, всего семь отправок, последнее ожидание 8 секунд.
Новые транзакции всех проб ограничены десятью в секунду; ожидания не блокируют event loop.
## Жизненный цикл и проверки
Ядро запускает модуль при `utun_core_start`; остановка уничтожает его до сетевых ресурсов и config.
Перезагрузка конфига отменяет старые DNS/таймеры и создаёт новый модуль.
Раз в три секунды выполняется локальная сверка групп: новые/изменённые проверяются,
группа удаляется после исчезновения последнего рабочего сокета. Смена имени/представителя
или удаление дубликата не теряет кэш. Несовпадение mark/FIB/device внутри одного ключа
даёт UNKNOWN с причиной incompatible routing policy.
TTL результата 300 секунд, refresh 180 секунд; неполные/неудачные проверки повторяются через 30 секунд.
Во время штатного refresh доступен прежний результат до TTL; его возраст не обновляется.
Использованные тестовые порты не переиспользуются в течение жизни ядра, включая reload STUN.
История портов освобождается только при окончательном destroy ядра.
`stun_nat_recheck(instance)` явно повторяет измерение на новых портах, в том числе после смены маршрута без смены IP.
Публичные операции выполняются в uasync-потоке экземпляра.
INFO содержит начало измерения и итог с адресами, классификацией и причинами UNKNOWN.
DEBUG содержит этапы, transaction ID, повторы, RTT, DNS и очистку; WARN/ERROR описывают аномалии.
Linux-тест `test_stun_nat` использует реальные локальные UDP-сокеты и ускоренные часы:
обычный STUN, все классы RFC 5780, недоступные альтернативы, потеря запроса, неверные/поздние ответы,
резервные серверы, DNS, нестабильный mapping, отмена и повторная проверка, изменение состава сокетов.
Сценарии проверяют сохранность рабочего fd/порта, предварительный EIM?, приоритет ETCP,
снятие оценки после TTL, совпадение IP/netif и освобождение таймеров.
Ссылки: [RFC 5780](https://www.rfc-editor.org/rfc/rfc5780), [STUN RFC 8489](https://www.rfc-editor.org/rfc/rfc8489).