100 lines
9.7 KiB
Markdown
100 lines
9.7 KiB
Markdown
# Независимая 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, его порт и обработчики. NAT_DETECTION использует его
|
||
оценку типа при NO_OBSERVER/NO_HELPER, помечает source=STUN и публикует подписанный профиль.
|
||
Действующий результат source=ETCP имеет приоритет, включая частичный. Мapped endpoint
|
||
тестового порта не переносится в nat_addr или NODEINFO рабочего сокета.
|
||
Поведение NAT на рабочем порту может отличаться; source показывает происхождение оценки.
|
||
|
||
## Конфигурация
|
||
|
||
```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, отмена и повторная проверка, изменение состава сокетов.
|
||
Каждый сценарий проверяет неизменность исходного ETCP-сокета и освобождение таймеров.
|
||
|
||
Ссылки: [RFC 5780](https://www.rfc-editor.org/rfc/rfc5780), [STUN RFC 8489](https://www.rfc-editor.org/rfc/rfc8489).
|