147 lines
14 KiB
Plaintext
147 lines
14 KiB
Plaintext
Как работает детектор NAT
|
||
========================
|
||
|
||
Детектор выясняет две вещи: какой внешний IP:порт получает рабочий UDP-сокет
|
||
при обращении к разным IP и проходят ли к нему пакеты от незнакомого IP.
|
||
Проверяется именно сокет, через который работает приложение.
|
||
|
||
Есть быстрый путь без помощников: если рабочий сокет принял подключение
|
||
и локальный IP и фактический IP подключившегося узла публичные,
|
||
ему сразу ставится direct/open (UNCHANGED + OPEN). Это правило действует
|
||
для UDP и TCP, даже если с IP пира раньше уже был исходящий обмен.
|
||
Локальные подключения и SOCKS интернет-вход не подтверждают. Для TCP
|
||
фактический локальный IP должен совпадать с IP интерфейса сокета.
|
||
|
||
Нужны три узла: A проверяет себя, B наблюдает его внешний адрес,
|
||
C помогает проверить входящие пакеты. Команды и результаты передаются
|
||
по уже работающей сети uTun, в том числе через TCP или промежуточные узлы.
|
||
Сами тестовые пакеты идут напрямую по UDP.
|
||
|
||
Порядок проверки
|
||
---------------
|
||
|
||
1. A отправляет пакет узлу B. Тот сообщает, с какого IP:порта его получил.
|
||
Ответ идёт по управляющему соединению, поэтому блокировка прямого
|
||
UDP-ответа не мешает узнать внешний адрес A.
|
||
|
||
2. Через B узел A просит C отправить несколько пакетов на этот внешний адрес.
|
||
До этого A не должен отправлять ничего на IP узла C с проверяемого сокета.
|
||
Приложение хранит историю таких отправок и учитывает новые во время проверки.
|
||
На входящие тестовые пакеты A автоматически не отвечает.
|
||
|
||
3. Получатель проверяет настоящий адрес отправителя каждого тестового пакета.
|
||
Локальный или другой непубличный IP не подходит для интернет-проверки.
|
||
Для проверки незнакомого входа также не подходят IP узла B, собственный
|
||
внешний IP узла A и IP, на который A уже отправлял пакеты.
|
||
Если помощник не подходит, A выбирает другого. Разные узлы с одним IP
|
||
не считаются разными источниками. Проверяются также подлинность пакета
|
||
и принадлежность текущей попытке, чтобы чужой или старый пакет не влиял на результат.
|
||
|
||
4. Затем A сам отправляет пакет C и узнаёт, какой внешний IP:порт видит C.
|
||
Если входящие до этого не проходили и внешний адрес совпал с увиденным B,
|
||
C повторяет отправку с того же IP:порта на исходный внешний адрес A.
|
||
Так проверяется, открыл ли исходящий контакт путь для входящих.
|
||
Параллельный обмен с B подтверждает, что UDP-связь в целом работает.
|
||
|
||
5. A повторяет измерение внешнего адреса через B и C. Результат принимается,
|
||
только если адрес для каждого направления остаётся стабильным.
|
||
Сравниваются разные IP назначения; другие порты того же IP не проверяются.
|
||
|
||
Что означает результат
|
||
----------------------
|
||
|
||
strict — B и C стабильно видят разные внешние IP:порты узла A.
|
||
Единого внешнего адреса для подключения с любого IP нет.
|
||
|
||
eim open — B и C видят одинаковый внешний IP:порт, и пакет от C пришёл
|
||
ещё до исходящего контакта A с его IP.
|
||
|
||
eim filtered — внешний IP:порт одинаковый, серия пакетов до контакта не прошла,
|
||
а после исходящего контакта пакеты с того же адреса C начали приходить.
|
||
Это оценка: потерю UDP-пакетов нельзя абсолютно отличить от блокировки.
|
||
|
||
EIM? — два направления видят одинаковый внешний IP:порт, но незнакомый вход
|
||
проверить не удалось, например все доступные помощники уже были знакомы.
|
||
Это EIM с неизвестной доступностью входящих, без обещания открытого входа.
|
||
Такой же предварительный EIM? дают два STUN-сервера, видящие одинаковый внешний
|
||
IP:порт тестового сокета на том же локальном IP и интерфейсе. Поддержка запроса
|
||
с незнакомого IP для этого не нужна. Источник оценки — STUN, вход остаётся UNKNOWN.
|
||
Измерение рабочего сокета имеет приоритет; STUN-оценка действует до пяти минут
|
||
и обновляется автоматически. Публичный порт для подключения берётся только
|
||
из наблюдения рабочего сокета: внешний порт STUN в него не подставляется.
|
||
|
||
Если внешний IP:порт совпадает с локальным, отдельно отмечается отсутствие
|
||
трансляции адреса. Доступность входящих всё равно проверяется.
|
||
|
||
Если данных недостаточно, соответствующая часть результата остаётся UNKNOWN.
|
||
Известный тип NAT можно сохранить, даже если входящие проверить не удалось.
|
||
Таймаут и отсутствие подходящих узлов сами по себе не означают strict.
|
||
|
||
Если один пир уже увидел публичный IP:порт, отличный от локального, этот адрес
|
||
сразу сохраняется как NAT с type=UNKNOWN. Он публикуется отдельно от адреса
|
||
интерфейса и остаётся кандидатом для пробного подключения: одного наблюдения
|
||
недостаточно, чтобы обещать тот же адрес для других IP или открытый вход.
|
||
Уже знакомый помощник пригоден для измерения типа NAT, хотя проверить незнакомый
|
||
вход через него нельзя. Потеря результата входящих не останавливает сравнение
|
||
адресов через доступные публичные узлы; ожидание одного этапа ограничено по времени.
|
||
Измеренный mapping сохраняется сразу, а проверка незнакомого входа продолжается
|
||
через остальных помощников. Порядок обхода фиксирован на его начало; фактический
|
||
source всегда проверяется по актуальной истории. При паузе по общему бюджету
|
||
незавершённая попытка повторяется с новыми идентификаторами, без сброса обхода.
|
||
Если попытка уже получила полный бюджет, его исчерпание считается таймаутом:
|
||
обход переходит дальше, а не повторяет одного помощника бесконечно.
|
||
Старый сохранённый результат входящих не считается успехом новой проверки.
|
||
|
||
Соединения по LAN работают независимо и не сбрасывают интернет-профиль.
|
||
Если доступны только локальные узлы, измерить тип интернет-NAT для UDP нельзя.
|
||
STUN на другом порту даёт предварительную оценку типа NAT для того же IP/интерфейса.
|
||
Работа TCP не заменяет проверку UDP.
|
||
|
||
Для TCP определяется только наличие NAT. Непубличный локальный IP сразу означает
|
||
NAT с неизвестным типом. Если локальный IP публичный, достаточно одного публичного
|
||
TCP-пира: по готовому защищённому соединению он сообщает увиденный IP.
|
||
IP совпали — трансляция IP не обнаружена; отличаются — NAT есть, тип UNKNOWN.
|
||
Порты при сравнении не учитываются. Публичный внешний IP сохраняется с портом 0:
|
||
входящий порт неизвестен, поэтому NCD не использует эту запись для подключения.
|
||
Активных проб типа NAT и TCP-входа нет; OPEN устанавливается по принятому
|
||
публичному подключению и сохраняется при последующих совпавших наблюдениях IP.
|
||
Через SOCKS внешний IP прокси
|
||
не записывается как собственный адрес.
|
||
|
||
Управляющий обмен и ресурсы
|
||
--------------------------
|
||
|
||
Команды идут через сервис NAT_DETECTION ETCP-router с подписью и шифрованием.
|
||
Используются узлы группы с рабочим маршрутом через READY-соседа; это не требует
|
||
прямого UDP-соединения A с C. CHAT-допуск требует полной неудалённой записи участника.
|
||
Источник и группа берутся из проверенного заголовка router, а не из next hop.
|
||
|
||
Версия управляющего формата — 4. Основное сообщение занимает 117 байт:
|
||
10 однобайтовых полей команды/сокетов/этапа/результата, шесть uint64
|
||
(owner, generation, round, token, observer, helper), uint16 timeout_ms
|
||
и три адреса по 19 байт (target, primary, observer_address).
|
||
RECHECK занимает 23 байта: cmd, command, version, socket_id и адрес 19 байт.
|
||
Многобайтовые числа кодируются вручную в big-endian; адреса — route_ping_addr_encode.
|
||
Это размеры сообщения после служебного заголовка router. Приём требует точного
|
||
размера; старые generation/round/token и чужие делегирования результата отклоняются.
|
||
|
||
Детектор работает с шагом 100 мс. Помощник отправляет до трёх проб на этап;
|
||
таймаут одной пробы — не меньше секунды и не меньше четырёх RTT наблюдателя.
|
||
Общий бюджет обхода — 30 секунд; незавершённый обход может продолжиться после паузы.
|
||
Полный результат перепроверяется через 30 минут, неполный — через 30 секунд.
|
||
Повтор по неудаче подключения ограничен одним запросом за пять минут на endpoint.
|
||
Сохранённый успешный ETCP-профиль не истекает по таймеру; изменение endpoint его сбрасывает.
|
||
|
||
История исходящих хранит до 256 IP для каждого локального endpoint (IP:порт).
|
||
Она сохраняется в SQLite. Переполнение или ошибка загрузки/записи помечают историю
|
||
неполной: UNKNOWN сохраняется до достоверного измерения, OPEN по незнакомому входу
|
||
с неполной историей не подтверждается. Кеш наблюдений ограничен 128 записями,
|
||
одновременно выполняется не более 64 задач помощи.
|
||
|
||
Детектор владеет таймерами, подписками и изолированными probe_only UDP-сокетами.
|
||
Эти сокеты не публикуются как рабочие адреса. Их отдельное удаление снимает ссылку
|
||
в состоянии родителя, отменяет зависимые пробы и допускает обычный последующий teardown.
|
||
Ошибка регистрации любой подписки или начального таймера отменяет весь запуск.
|
||
Ядро сохраняет контекст в instance->nat_det; останавливает его до ETCP/router
|
||
и обнуляет поле после destroy. Уничтожать ядро из callback детектора нельзя.
|