You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

9.7 KiB

Независимая 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 показывает происхождение оценки.

Конфигурация

[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, STUN RFC 8489.