diff --git a/doc/nat_detection.txt b/doc/nat_detection.txt index 2df1f3f3..ce97051d 100644 --- a/doc/nat_detection.txt +++ b/doc/nat_detection.txt @@ -144,78 +144,143 @@ tests/stream_netns --nat-only: 16 настоящих uTun с 10 NAT, прове профилей и точного собственного внешнего адреса. --nat-check включает эту фазу до обычных JOIN/Merkle/BGP/streaming. Главный network namespace не меняется. -Объединение со STUN: проект доработки после rebase 2026-10-12 ------------------------------------------------------------ - -Сейчас stun_nat — дополнительная локальная диагностика другого UDP-порта. -Она возвращает mapped адреса, MATCH/EIM/ADM/APDM и EIF/ADF/APDF, но не меняет -address_profile или NODEINFO. Это корректная граница текущего модуля: общий -интерфейс и одинаковый SO_MARK/FIB не означают одинаковое состояние двух портов. -В ходе rebase тест переведён на address_profile, probe_only исключён из сканирования. -Сборка и полный основной набор из 145 тестов прошли. - -Варианты: - 1. Оставить независимый порт: показывать STUN рядом с профилем ETCP, с точным - тестовым endpoint и временем. Полезно для диагностики расхождений, но ни - внешний порт, ни классификация не становятся фактами рабочего сокета. - 2. Для участия STUN в рабочем профиле использовать фактический ETCP UDP fd. - Рекомендуемый вариант для объединённого детектора; требует доработок ниже. - -Владение и алгоритм варианта 2: - - NAT_DETECTION остаётся единственным планировщиком раундов и владельцем - address_profile/nat_addr/публикации. STUN выполняет транзакции и отдаёт - наблюдения; самостоятельно не назначает профиль и не закрывает ETCP fd. - - Каждое наблюдение содержит actual local IP:port, socket ID, generation, - round, endpoint наблюдателя, observed endpoint, monotonic time и причину. - Имя сокета — только метка для человека. Диагностика другого порта хранится - отдельно и не проходит проверку принадлежности рабочему endpoint. - - В прямом UDP read_callback распознавать ответы STUN до ETCP decrypt: - формат/cookie, активная 96-битная транзакция, ожидаемый source, receiving - socket/generation/round. Единственный читатель fd остаётся у транспорта; - STUN не добавляет второго uasync-reader. Отправки проходят общий учёт контактов. - - Раунд выбирает доступные ETCP или настроенные STUN-наблюдатели. Разные - имена серверов не заменяют разные реальные IP. Для обычного STUN нужны - независимые IP; расширенные пробы требуют фактически доступных alternate IP/port. - - До mapping-проб выполнять чистые ingress/CHANGE-пробы, затем наблюдения - на разных назначениях, затем повтор исходного наблюдения. Обе проверки - используют общую историю всех UDP-отправок, а не отдельную STUN history. - Загрязнение теста или отсутствие независимого адреса даёт частичный результат. - - Обычный Binding дополняет наблюдения. MATCH сам по себе не назначает EIM; - стабильное расхождение назначений даёт DEPENDENT. RFC5780 EIM соответствует - EIM либо UNCHANGED при отсутствии трансляции, ADM/APDM — DEPENDENT. - - Положительный чистый ingress может подтвердить OPEN. ADF/APDF сохранять - в диагностике; CONTACT_REQUIRED публиковать после подтверждённого перехода - до/после контакта на неизменном endpoint. Таймаут CHANGE без доказательства - работы альтернативной отправки не отличает фильтрацию от поведения сервера. - - У STUN нет проверки ключа ETCP; FINGERPRINT — CRC, не аутентификация. - Расхождение с действующими аутентифицированными фактами требует повторного - раунда и лога обоих наблюдений, а не перезаписи по принципу «последний ответ». - Таймаут дополнительной проверки не сбрасывает ещё действующий профиль. - - Свежесть проверяется по локальному monotonic времени. checked_at/evidence_id - формирует владелец при применении результата, в единой транзакции публикации. - Смена endpoint/маршрута, generation, закрытие, reload и stop отменяют все пробы. - TCP и SOCKS5 не наследуют native UDP-факты; существующая listener-проверка остаётся. - -Порядок реализации: (1) общий контракт наблюдения и жизненного цикла, -(2) STUN-транзакции через рабочий UDP и безопасный demux, -(3) единое расписание/история контактов и применение результатов, -(4) локальные тесты и RFC5780-серверы в существующем изолированном стенде. - -Обязательные новые тесты: разные NAT-свойства тестового и рабочего портов; -ETCP DATA/INIT во время STUN; чужие/поздние ID и reused socket generation; -контакт к alternate IP через INIT/ACK/keepalive перед CHANGE; отсутствие RFC5780, -молчаливое игнорирование CHANGE, потери и недоступные альтернативы; -расхождения STUN/ETCP, TTL, reload, смена маршрута без смены IP, shutdown/leaks; -реальные EIM/EIF, EIM/ADF, EIM/APDF, ADM/APDM на netns NAT с RTT/jitter/loss. - -В текущей независимой диагностике до объединения остаются ограничения: -source_snapshot обнуляет исходный порт и не сохраняет generation; сканирование -не замечает пересоздание рабочего сокета с тем же именем/IP/настройками; -неизменившийся источник не обновляется без явного recheck; свежий тест исключает -только непосредственно предыдущий порт, а не всю возможную историю NAT этого tuple. -Эти результаты нельзя автоматически использовать как актуальные рабочие факты. - -Нормативные протоколы: https://www.rfc-editor.org/rfc/rfc8489 (Binding, transaction ID), -https://www.rfc-editor.org/rfc/rfc5780#section-4.3 (mapping), #section-4.4 (filtering), -#section-4.5 (порядок и зависимость от прежнего состояния). Приведённая схема -владения и согласования результатов — проект интеграции uTun, а не требование RFC. +Резервное определение типа NAT через STUN: план 2026-10-12 +------------------------------------------------------- + +Уточнение пользователя: STUN группирует рабочие сокеты по одинаковым IP/netif, +создаёт аналогичный UDP-сокет с другим портом и определяет тип NAT для группы. +Когда ETCP не может выполнить проверку из-за нехватки узлов, используется STUN. +Это заменяет прежнее предложение использовать STUN непосредственно на ETCP fd. +Далее описан план; работающая резервная интеграция ещё не реализована. + +1. Реестр уникальных путей + + Ключ группы — (address family, фактический local IP, netif_index), без порта, + имени и socket ID. Несколько рабочих портов одного IP/netif дают одну группу. + Одинаковый IP на разных netif и разные IP одного netif — разные группы. + netif=0 сохраняет смысл автоматической маршрутизации, не приравнивается + произвольно к явно привязанному интерфейсу. Wildcard не становится отдельным IP: + сначала нужен фактический адрес пути. Неопределённый путь получает UNKNOWN. + + Сканировать native UDP, исключать probe_only, SOCKS5 и only_local. TCP listener + сохраняет отдельную проверку STCP: UDP-результат ему не присваивается. + Начать с IPv4, уже поддержанного stun_nat; IPv6 требует отдельного этапа: + XOR-MAPPED IPv6, IPv6 server/config/DNS и scope/interface, без переноса IPv4-фактов. + + Выбрать представителя группы, скопировать фактические bind IP/device/mark/FIB. + Порты исходных сокетов не копировать; проба владеет собственным fd/reader. + Разные mark/FIB/device внутри одного ключа означают неоднозначный путь: + группа получает UNKNOWN/incompatible_route_policy с логом, а не случайный + результат первого сокета. Ключ IP/netif при этом не усложняется. + +2. Измерение и кэш STUN + + Одна активная проба и один результат на группу, независимо от числа её сокетов. + Тестовый порт случайный, отличается от рабочих портов. В пределах жизни ядра + не переиспользовать ранее измеренные порты на этом IP/netif; при исчерпании + не выполнять заведомо загрязнённое входящее испытание, вернуть явную причину. + Самостоятельно закрывать только пробный fd, отменять его DNS и таймеры. + + STUN запускается при появлении группы, при наличии enabled и настроенных серверов. + Разные имена серверов с одинаковым IP не считаются независимыми наблюдателями. + На чистом тестовом порту сначала проверять filtering, потом mapping и стабильность + исходного наблюдения. Переносить в кэш только завершённые пригодные свойства; + потери, ошибки, отсутствие RFC5780 и сомнительный CHANGE дают UNKNOWN/причину. + + В результате хранить ключ группы, её generation, случайный evidence ID, + test IP:port, фактические серверы, mapped адреса, mapping/filtering, причины, + время измерения monotonic и Unix microseconds. TTL=300с, refresh=180с, + повтор неполной проверки через 30с; темп новых транзакций остаётся ограниченным. + Применение готового кэша к новому сокету не обновляет время измерения. + +3. Выбор результата NAT_DETECTION + + NAT_DETECTION остаётся единственным владельцем эффективного socket profile, + nat_addr и публикации NODEINFO. STUN предоставляет кэш по ключу группы, + уведомляет о завершении/инвалидации; сам не меняет профили рабочих сокетов. + Реестр групп не держит указателей на рабочие сокеты: потребитель проверяет + собственные socket generation и актуальную принадлежность группе перед применением. + + Хранить отдельно собственный результат ETCP и выбранный эффективный профиль. + Добавить profile source: NONE / ETCP / STUN, в том числе в подпись, wire, + SQLite и диагностические API. STUN означает резервную оценку по интерфейсу, + ETCP — измерение рабочего endpoint. Подписывает публикацию только сам узел. + + Приоритет: действующий измеренный ETCP -> пригодный STUN при невозможности + ETCP из-за отсутствия наблюдателя/независимого помощника -> UNKNOWN. + Для STUN использовать реальные NO_OBSERVER/NO_HELPER, не общее число узлов: + третий узел может быть offline, несовместимым или иметь тот же IP наблюдателя. + После исчерпания замены таких помощников разрешать резервное применение. + Пока STUN не готов, оставить UNKNOWN или ещё действующий профиль ETCP. + + Частичный, но измеренный ETCP не заменять целиком STUN: DEPENDENT с неизвестным + ingress является полезным результатом. В первой версии не смешивать facets + разных источников. Неудача нового раунда не стирает ещё действующий ETCP. + Появление подходящего помощника ускоряет повтор ETCP; его успешный результат + заменяет резервный без ожидания TTL. Не сбрасывать ETCP при завершении STUN. + +4. Преобразование и использование типа + + RFC5780 EIM -> EIM; отсутствие трансляции в согласованном измерении -> UNCHANGED; + ADM/APDM и устойчивое расхождение обычных серверов -> DEPENDENT. + EIF -> OPEN; ADF/APDF -> CONTACT_REQUIRED; неизвестный facet остаётся UNKNOWN. + Это преобразование применяется с source=STUN и описывает резервную оценку. + Обычный MATCH остаётся неполной диагностикой, сам по себе не назначает EIM. + Для полного определения типа необходимы доступные серверы с RFC5780. + + Тип STUN помогает выбирать прямые/reverse попытки, но не доказывает их успех. + Различное поведение NAT для рабочих и тестовых портов допустимо: источник + оценки виден в логах и метаданных, реальные факты ETCP имеют приоритет. + Не запрещать LAN/hairpin или закрывать работающую связь по резервной оценке. + NCD не объявляет рабочий endpoint недоступным только на основании STUN. + + mapped IP:port тестового сокета никогда не копировать в nat_addr рабочего. + Не подставлять туда рабочий local port: NAT может изменить его независимо. + Общий внешний адрес рабочего сокета публиковать только с самостоятельным + подтверждением его EIM/endpoint средствами ETCP. При наличии одного наблюдателя + его адрес остаётся наблюдением для данного назначения, не общим NAT-адресом. + +5. Жизненный цикл и диагностика + + Добавление/удаление дубликата не повторяет STUN для неизменившейся группы. + Исчезновение последнего рабочего сокета удаляет группу и отменяет пробу. + Смена IP/interface/параметров маршрутизации инвалидирует соответствующий кэш; + смена маршрута без смены IP и явный recheck запускают новое измерение. + Generation относится к пути и раунду STUN, а не к имени представителя. + Поздние ответы не применяются к пересозданной группе. Reload/stop отменяют всё + до освобождения config/сокетов; порядок уничтожения не вызывает публикацию. + + INFO: создание/удаление группы, начало/результат пробы, выбор source, возврат + к ETCP. DEBUG: объединённые дубликаты, transaction/generation/время/причины. + WARN/ERROR: неподходящие помощники, неоднозначный путь, timeout/ошибка/несогласованность. + В статусе доступны эффективный профиль, оба результата и причина выбора STUN. + +Этапы реализации: (1) группировка и API STUN по ключу IP/netif, +(2) generation/TTL/обновление и завершение проб, +(3) source в модели/подписи/wire/SQLite/статусе, +(4) выбор резерва и возврат к ETCP, независимое хранение внешних адресов, +(5) локальные и изолированные сетевые тесты. Изменять ETCP UDP demux не требуется. + +План тестов +----------- + + - Два порта одного IP/netif: одна проба, применение резерва обоим сокетам. + Разные IP/netif: отдельные пробы; разные mark/FIB внутри группы: явный UNKNOWN. + - Смена представителя и удаление одного дубликата не теряют результат; удаление + последнего, новая группа и поздний ответ проверяют владение и generation. + - Нет узлов / один наблюдатель / все помощники за одним IP: STUN fallback. + Живой независимый помощник: ETCP; появление/исчезновение, TTL и обе очередности + завершения проверяют приоритет. Частичный ETCP не затирается полным STUN. + - RFC5780 EIM/EIF, EIM/ADF, EIM/APDF, ADM/APDM; обычный MATCH; недоступные + альтернативы, ignored CHANGE, DNS, потери, нестабильность и ограничение повторов. + - NAT по-разному обрабатывает тестовый и рабочий порты: резерв помечен STUN, + реальный ETCP его заменяет; тестовый mapped port не попадает в NODEINFO/NCD. + - Смена маршрута без смены IP, refresh, reload, stop, callbacks и утечки. + Общий evidence/time кэша не омолаживается при новом socket или NODEINFO. + - В tests/stream_netns разместить RFC5780-серверы с двумя IP/двумя портами, + сценарий двух uTun, затем добавить третьего. Проверить переключение источника + типа при RTT/jitter/loss; всю сетевую настройку выполнять в отдельных namespace. + +Основания: https://www.rfc-editor.org/rfc/rfc5780#section-4.1 (выбор тестового порта +и ограниченность переноса результата), #section-4.3/#section-4.4 (классы). +Приоритет и применение резерва — согласованный проект uTun, не гарантия RFC. diff --git a/doc/tasks.md b/doc/tasks.md index e7f0efd1..03a3dad9 100644 --- a/doc/tasks.md +++ b/doc/tasks.md @@ -4,12 +4,12 @@ ## Текущие задачи -[ ] **STUN: объединённый детектор рабочего endpoint** — план и варианты в - `doc/nat_detection.txt`, раздел после rebase 12.10.2026. Текущий STUN измеряет - другой порт, его результат нельзя переносить в ETCP/NODEINFO. Нужны единый - владелец профиля, STUN через рабочий UDP fd, общая история контактов и отмена - по socket generation. Дополнительно проверить молчаливое игнорирование CHANGE, - повторное использование тестовых портов, давность и смену маршрута без смены IP. +[ ] **STUN: резервное определение типа NAT по IP/netif** — уточнённый план + 12.10.2026 в `doc/nat_detection.txt`. Одна группа и отдельный тестовый UDP-порт + на уникальный IP/netif, общий кэш с generation/TTL. При NO_OBSERVER/NO_HELPER + применять пригодную оценку STUN с явным source; измеренный ETCP приоритетен. + mapped адрес тестового сокета не переносить на рабочий. Проверить дубликаты, + переход STUN -> ETCP, различия по портам и жизненный цикл; namespace-стенд. [ ] **check.sh: изоляция сетевых integration-тестов** — после основного набора автоматически запускает check-proxy/burst/load при доступном sudo. Скрипты