3 changed files with 162 additions and 315 deletions
@ -1,315 +1,151 @@
|
||||
Детектор свойств endpoint |
||||
======================== |
||||
Детектор рабочего UDP endpoint |
||||
============================== |
||||
|
||||
Владелец рабочего сокета единственный выбирает и публикует его профиль. |
||||
Помощники возвращают наблюдения и выполняют прямые испытания. Профиль относится |
||||
Владелец сокета единственный выбирает и публикует его профиль. Он относится |
||||
к протоколу, семейству, фактическому локальному IP:port и поколению сокета. |
||||
PUBLIC/NAT/PRIVATE/LOCAL — политика конфигурации, а не результат измерения. |
||||
source=ETCP означает измерение рабочего endpoint; source=STUN — резервную оценку |
||||
по его IP/netif через другой порт. Успешный ETCP имеет приоритет до явного сброса endpoint. |
||||
|
||||
Модель: src/routing_layer/address_profile.h. |
||||
mapping: UNKNOWN, UNCHANGED, EIM, DEPENDENT. |
||||
inbound: UNKNOWN, OPEN, CONTACT_REQUIRED. |
||||
source: NONE, ETCP, STUN. |
||||
round/generation: локальные ID раунда и жизненного цикла endpoint; в NODEINFO не входят. |
||||
|
||||
UNCHANGED означает совпадение наблюдаемых и локального адресов на испытанных |
||||
направлениях. Оно не доказывает свободный вход. EIM означает одинаковый внешний |
||||
endpoint на испытанных разных IP и портах; оно также не доказывает свободный вход. |
||||
DEPENDENT означает различие mapping на испытанных назначениях, без общего |
||||
внешнего адреса. Дальнейшее разделение ADM/APDM не используется для выбора NCD. |
||||
PUBLIC/NAT/PRIVATE/LOCAL — политика конфигурации, не измеренный NAT-тип. |
||||
|
||||
Независимые факты в src/routing_layer/address_profile.h: |
||||
mapping: UNKNOWN, UNCHANGED, EIM, DEPENDENT; |
||||
inbound: UNKNOWN, OPEN, CONTACT_REQUIRED; |
||||
source: ETCP — измерен рабочий endpoint. |
||||
|
||||
Практические классы: strict — стабильные разные mapping к разным IP; |
||||
eim open — одинаковый mapping и полученная чистая проба незнакомого входа; |
||||
eim filtered — одинаковый mapping и вход, полученный только после контакта. |
||||
UNCHANGED отличается от EIM отсутствием трансляции IP:port. |
||||
DEPENDENT не означает запрета всех входящих. Дополнительные порты одного IP |
||||
не сравниваются. Разные Node ID за одним IP не дают независимого испытания. |
||||
OPEN подтверждается пакетом; CONTACT_REQUIRED — оценка по серии до/после: |
||||
потерю первых UDP-проб нельзя абсолютно отличить от их блокировки. |
||||
Таймаут, отсутствие помощника и противоречия не означают strict. |
||||
|
||||
Участники и каналы |
||||
------------------ |
||||
|
||||
S — рабочий сокет владельца A; B — наблюдатель; C — помощник с другим IP. |
||||
Выбираются все активные UTUN/CHAT-группы с READY-маршрутами управления; |
||||
прямые UDP-линки A-C не обязательны. Управление допускает транзит и TCP. |
||||
Перебираются все подходящие UDP endpoint узла, включая адрес работающего линка. |
||||
Таймаут одного адреса не исключает остальные адреса того же узла. |
||||
|
||||
OBSERVE принимается по UDP, фактический source IP:port возвращается через |
||||
подписанный зашифрованный router-отчёт OBSERVATION. Прямой PONG не требуется. |
||||
Отдельный короткий UDP-обмен с B поддерживает mapping и контролирует входящий путь. |
||||
Его отсутствие запрещает отрицательный вывод о filtering, но не стирает mapping. |
||||
|
||||
У source-сокета C один отдельный probe_only UDP-сокет помощника. Он не входит |
||||
в NODEINFO/NCD и не открывает фильтр собственного рабочего endpoint C. |
||||
Дополнительного сравнительного порта нет. |
||||
|
||||
UDP-раунд |
||||
--------- |
||||
|
||||
1. Владелец A рассматривает узлы всех активных UTUN/CHAT-групп и выбирает одну группу |
||||
с маршрутами к наблюдателю B и отдельному помощнику C. Следующие хопы управления |
||||
должны быть READY. Прямые ETCP/UDP-линки к B/C не обязательны; B нужен доступный UDP endpoint. |
||||
Через проверяемый сокет S выполняет OBSERVE. Ответ содержит фактический |
||||
source IP:port, наблюдаемый B, и исходный тег S/generation/round/group. |
||||
2. A выбирает C по подписанному NODEINFO выбранной группы. Его рабочий UDP-сокет |
||||
задаёт интерфейс/семейство; C использует два отдельных probe_only порта, |
||||
чтобы помощь другому узлу не открывала фильтр его собственного рабочего endpoint. |
||||
C может находиться за NAT, независимо от политики PUBLIC/NAT и собственного профиля. |
||||
3. Управление RUN/RESULT идёт A -> B -> C через router в одной группе с подписью |
||||
и шифрованием. C сначала отправляет ADDRESS_CHECK с primary-порта к endpoint A, |
||||
реально наблюдавшемуся B. Аутентичный ingress на S до исходящего контакта подтверждает OPEN. |
||||
PONG возвращает наблюдаемый внешний endpoint C. При отсутствии PONG C узнаёт mapping |
||||
своего primary-порта через B, после чего выполняется контролируемый PRE из трёх проб. |
||||
4. Если внешний IP C совпадает с IP B или S ранее контактировал с C, A пробует |
||||
другие пары и группы. Совпадающие Node ID и разные порты одного IP не дают |
||||
независимого IP-наблюдения. При отсутствии второго IP результат неполный. |
||||
При выборе пары сначала сохраняется возможность свежего помощника; ранее контактировавшая |
||||
пара используется для mapping после исчерпания свежих. Отчёт о PONG не заменяет ingress на S. |
||||
5. После первого испытания S выполняет OBSERVE к primary C. При различии mapping |
||||
с B уже известен DEPENDENT. Иначе C отдельно измеряет внешний endpoint второго порта |
||||
через A; если фильтр A мешает — через B. S выполняет OBSERVE к этому endpoint. |
||||
Оба порта C скрыты из NODEINFO и исключены из NCD. Проверяемый S не меняется. |
||||
6. Если первый ingress отсутствовал, C здоров и mapping к C совпал с mapping к B, |
||||
после исходящего контакта выполняется второе входящее испытание от того же C. |
||||
Контролируемый переход до/после контакта даёт CONTACT_REQUIRED. |
||||
7. S повторяет исходное наблюдение у B. Изменение этого mapping во время раунда |
||||
даёт UNKNOWN/address_changed и инвалидирует прежний общий внешний адрес. |
||||
8. При совпадении наблюдений B/C/C-alt выбирается UNCHANGED или EIM; различие |
||||
назначений даёт DEPENDENT. Первый чистый ingress даёт OPEN; подтверждённый |
||||
переход после контакта при неизменном endpoint — CONTACT_REQUIRED. |
||||
|
||||
Если независимых от истории C больше нет, уже контактировавший C может измерить |
||||
mapping. Входящая часть остаётся UNKNOWN/contact_history. Отказ проверки одного |
||||
facet не превращается в ложный STRICT. Потери, ошибка отправки, отсутствие |
||||
помощника, разрыв управления и неправильный ответ имеют отдельную причину. |
||||
|
||||
Контакты и повторы |
||||
----------------- |
||||
|
||||
История IP назначения учитывает INIT, DATA, ACK, keepalive, PING/PONG и другие |
||||
отправки с S в двух общих точках UDP-send. Любая неожиданная отправка к C до |
||||
получения первого ingress делает входящее испытание загрязнённым. |
||||
|
||||
История записывается в nat_contacts общей SQLite один раз на новый IP. Ключ — |
||||
семейство и фактический локальный IP:port. Перезапуск на том же endpoint её не |
||||
очищает. Максимум 256 IP на endpoint; переполнение/ошибка сохранения отмечаются |
||||
постоянным маркером неполной истории. Неизвестная история запрещает вывод OPEN |
||||
по испытанию, которое может использовать старое разрешение фильтра. |
||||
|
||||
История характеризует наблюдаемый приложением трафик. Удаление БД, использование |
||||
того же tuple другим процессом или изменение NAT вне наблюдаемого раунда нельзя |
||||
превратить в доказательство полного отсутствия прежнего состояния. Результаты |
||||
остаются подсказками для попыток, подтверждение связи даёт handshake с нужным ключом. |
||||
|
||||
Один активный раунд на S. Таймаут пробы — max(1 секунда, 4*RTT), не более 3 секунд; |
||||
три попытки, бюджет раунда 30 секунд. До первого результата повтор после ошибки |
||||
через 30 секунд или после появления подходящего транспорта; штатный refresh через 30 минут. |
||||
Успешный профиль не протухает по таймеру. Ошибка перепроверки сохраняет прежний результат |
||||
и редкий период; неизвестный inbound при том же mapping/endpoint сохраняет прежний ETCP inbound. |
||||
Свойства разных источников не объединяются в один ETCP-профиль. Смена адреса отменяет |
||||
старый раунд, сбрасывает профиль и назначает скорую проверку. Таймеры используют monotonic timebase. |
||||
|
||||
После таймаута NCD перепроверка назначается только для реально отправленных native UDP INIT |
||||
к OPEN endpoint из текущего подписанного NODEINFO. CONTACT допускает такой повод после |
||||
исчерпания цикла CM вместе с reverse, а UNKNOWN/приватный адрес и SOCKS5 его не дают. |
||||
Активный раунд не перезапускается; повторные ошибки объединяются, минимум пять минут |
||||
между такими перепроверками одного рабочего сокета. Последний успех сохраняется. |
||||
Начальные измерения имеют короткий приоритет перед фоновыми проверками; завершённый |
||||
частичный результат освобождает его, чтобы соседи могли выполнить свои наблюдения. |
||||
Если выбранная группа ещё доставляет управление пиру, ему передаётся подписанный |
||||
зашифрованный RECHECK размером 23 байта (команда/версия/socket ID/endpoint). |
||||
Пир проверяет членство отправителя и совпадение endpoint со своим текущим сокетом; |
||||
тот же лимит действует на удалённые запросы. ID раунда и время в NODEINFO не возвращаются. |
||||
|
||||
TCP и прокси |
||||
------------- |
||||
|
||||
TCP inbound проверяется отдельным STCP handshake к объявленному настоящему |
||||
listener с проверкой ключа владельца. Его target должен присутствовать в |
||||
подписанном snapshot. Внешний порт исходящего TCP-соединения не рекламируется |
||||
как listener. Без публичного кандидата профиль остаётся UNKNOWN/no_listener. |
||||
UDP-факты не переносятся на TCP. Входящий TCP source port не определяет профиль |
||||
listener удалённого клиента. SOCKS5 UDP не классифицируется как native endpoint. |
||||
|
||||
Корреляция и владение |
||||
-------------------- |
||||
|
||||
Транспортный PONG сопоставляется по криптографически случайному nonce, ожидаемому |
||||
ключу, source IP:port, ID и поколению принимающего сокета. Входящее испытание |
||||
дополнительно проверяет владельца, round, step и token. Перед callback PING |
||||
удаляется из pending. Отмена не вызывает callback и не интерпретирует void* arg |
||||
как контекст другого типа. Произвольный выбор первого IPv4/TCP-сокета запрещён. |
||||
|
||||
Broker принимает RUN только для записанного им UDP-наблюдения данного владельца. |
||||
Участники и ключи берутся из подписанного реестра выбранной группы. SOURCE читается |
||||
из проверенного router-заголовка, не из ETCP next hop. Управление занимает 136 байт, |
||||
не содержит списков исключений и копий ключей. Размер, версия, enum и ненулевые |
||||
group/generation/round/token проверяются до обработки. Числа BE, IP — сетевые байты. |
||||
|
||||
На смене адреса, закрытии сокета, reset/DELETE транспорта и остановке ядра |
||||
отменяются собственные раунды и связанные helper jobs. Поздние ответы не |
||||
применяются к новому socket generation даже при повторном ID и IP:port. |
||||
Между попытками разных пар локальный статус cycling отличает паузу от завершения измерения. |
||||
Удаление выбранной группы отменяет её раунды, jobs и наблюдения до закрытия router-каналов; |
||||
другие группы и последний успешный профиль сохраняются. Детектор останавливается |
||||
до разборки групп, сокетов и соединений. |
||||
|
||||
Публикация и NCD |
||||
--------------- |
||||
|
||||
Профиль публикуется только владельцем в общей подписанной записи NODEINFO. |
||||
Списки и их границы входят в подпись; mapping/inbound/source |
||||
сохраняются и загружаются вместе. node_addresses — производная проекция: |
||||
nat_mapping, nat_inbound, nat_source, address_kind. |
||||
При изменении формата старый кэш адресов пересоздаётся; старый NAT_TYPE не |
||||
интерпретируется как новая модель. При сокращении SOCKMETA старые snapshots инвалидируются |
||||
с сохранением timestamp; адреса и REALITY остаются bootstrap-сведениями. Записи чата не изменяются. |
||||
|
||||
Общий NAT-адрес рекламируется только после EIM. DEPENDENT не рекламирует |
||||
универсальный внешний endpoint. NTP.reachable не управляет рекламой адресов. |
||||
Ошибки публикации логируются; детектор повторяет сохранение/публикацию. |
||||
|
||||
NCD различает OPEN, CONTACT и непроверенные публичные кандидаты. UNKNOWN допускает |
||||
ограниченные попытки. Контактирующие EIM используют обычные встречные INIT-пробы |
||||
NCD и reverse. Одинаковый внешний IP не запрещает hairpin и не доказывает общую |
||||
LAN; частные адреса проверяются маршрутизацией и аутентификацией нужного пира. |
||||
Профиль не закрывает уже работающий транспорт. Признак PUBLIC конфигурации |
||||
может влиять на очередность попыток CM, но не назначает подтверждённый NAT-тип. |
||||
Стриминг сохраняет raw NCD + REVERSE_REQ через группу, без routed DATA fallback. |
||||
1. S отправляет B односторонний OBSERVE; через управление получает M_B. |
||||
2. A просит B делегировать C входящее испытание на M_B. B разрешает делегацию |
||||
только для действительного записанного наблюдения этого владельца и раунда. |
||||
3. C отправляет начальную серию входящих проб, без автоматического UDP-ответа. |
||||
Затем через B измеряет mapping своего отдельного сокета; отчёт также идёт |
||||
через router. Начальная серия могла уже подтвердить OPEN. |
||||
4. Незнакомость проверяет получатель S по фактическому source IP. IP B, |
||||
собственный внешний IP S (hairpin) и прежний исходящий контакт не подходят: |
||||
A запрашивает другого помощника. Объявленный IP и Node ID не заменяют проверку. |
||||
5. Если начального входа нет, C выполняет полную серию PRE до исходящего |
||||
контакта S с C. После отчёта выдерживается окно доставки последней пробы: |
||||
управляющее сообщение может обогнать UDP. |
||||
6. S фиксирует границу контакта и отправляет OBSERVE на C. Через управление |
||||
получает M_C. Source полученной аутентичной входящей пробы имеет приоритет |
||||
перед предварительно объявленным endpoint помощника. |
||||
7. При M_C = M_B, полном чистом PRE без входа и исправном контроле B выполняется |
||||
POST: серия с того же endpoint C на исходный M_B. Source POST должен совпасть |
||||
с endpoint, к которому обращался S. История проверяется до намеренного контакта, |
||||
включая посторонние отправки во время PRE. Смена source не подтверждает filtering. |
||||
8. S повторяет OBSERVE к B и C. Изменение наблюдений одного направления |
||||
инвалидирует профиль и даёт address_changed. Этапы разных состояний не смешиваются. |
||||
9. Стабильные M_B != M_C дают DEPENDENT; равенство — UNCHANGED/EIM. |
||||
Чистый вход до контакта даёт OPEN. Полный PRE без входа, успешный POST |
||||
и свежий прямой контроль B дают CONTACT_REQUIRED. Незавершённый ingress |
||||
оставляет inbound UNKNOWN, сохраняя измеренный mapping. |
||||
|
||||
Новый помощник получает новый round/token. Поздние пакеты предыдущей попытки |
||||
не используются. После исчерпания свежих IP допускается mapping-раунд с прежним |
||||
помощником, без обновления незнакомой входящей доступности. |
||||
|
||||
История, время и отмена |
||||
---------------------- |
||||
|
||||
История по IP учитывает INIT, DATA, ACK, keepalive, PING/PONG и другие отправки |
||||
в двух общих точках UDP-send. Проверяется на момент первого ingress, не только |
||||
на старте. Случайная отправка к C во время PRE делает опыт загрязнённым. |
||||
Для POST сохраняется граница истории до намеренного OBSERVE к C. |
||||
|
||||
nat_contacts общей SQLite хранит семейство и фактический локальный IP:port |
||||
и один элемент на IP назначения. Перезапуск на том же endpoint историю не очищает. |
||||
Максимум 256 IP; переполнение, повреждение и ошибка записи отмечают её неполной. |
||||
Неполная история запрещает доказательство незнакомости. История описывает только |
||||
наблюдаемый приложением трафик: удаление БД или чужое использование tuple |
||||
не доказывает отсутствия состояния NAT. |
||||
|
||||
Начальный раунд имеет короткий приоритет перед фоновыми NCD-пробами. |
||||
Уже известные IP не блокируются; работающий транспорт сохраняется. |
||||
Все операции выполняются в uasync-потоке. Один активный раунд на S. |
||||
Ожидание пробы — max(1 секунда, 4*RTT к B), серия — три отправки. |
||||
Цикл перебора ограничен 30 секундами. Истечение бюджета не назначает NAT-тип. |
||||
Повтор неполного EIM/inbound — через 30 секунд; новый подходящий endpoint |
||||
может разбудить раньше. Полный профиль обновляется через 30 минут. |
||||
Неудачная перепроверка сохраняет прежний успех. Частичный результат того же |
||||
mapping/endpoint сохраняет прежний inbound, но не переносит его с другого endpoint. |
||||
|
||||
Смена адреса сбрасывает профиль и отменяет раунд. Закрытие сокета, удаление группы |
||||
и остановка ядра отменяют UDP-пробы, heartbeat и helper jobs до закрытия транспорта. |
||||
Generation и токены исключают применение позднего ответа к новому socket ID/fd. |
||||
Неудачные реально отправленные native INIT к подписанному OPEN назначают recheck; |
||||
CONTACT допускает его после исчерпания reverse. Ошибки объединяются, минимум |
||||
пять минут между такими перепроверками рабочего сокета. |
||||
|
||||
Wire v4 |
||||
------- |
||||
|
||||
Команды: NAT_RUN, NAT_RESULT, NAT_RECHECK, NAT_OBSERVATION. |
||||
SOURCE/group берутся из проверенного router-заголовка, не из физического next hop. |
||||
Разрешены только участники выбранной группы; управление подписано и зашифровано. |
||||
|
||||
Тег OBSERVE: "NAT4UDP!", socket ID, generation, round, group. |
||||
Односторонняя проба добавляет этап (1 байт) и token (8 байт). |
||||
OBSERVATION проверяется по ожидаемому наблюдателю, socket ID/generation, |
||||
group, round, этапу и token. Каждый повторный UDP-запрос получает новый token. |
||||
Ingress дополнительно проверяет ключ C и фактический source endpoint. |
||||
Wire содержит только uint8_t; числа BE, адреса — сетевые байты. |
||||
|
||||
ETCP_PING_FLAG_NO_REPLY разрешён для OBSERVE/ADDRESS_CHECK с полным NAT-тегом. |
||||
Он запрещает автоматический PONG: приём пробы не создаёт исходящий контакт. |
||||
Обычные транспортные PING/PONG сохраняются. v3 несовместим с v4: |
||||
участвующие в измерении узлы обновляются вместе. |
||||
|
||||
Публикация, STUN и TCP |
||||
--------------------- |
||||
|
||||
Владелец публикует профиль в подписанном NODEINFO. Универсальный внешний |
||||
endpoint рекламируется после EIM; UNCHANGED использует собственный адрес. |
||||
DEPENDENT не рекламирует универсальный NAT endpoint. NCD использует профиль |
||||
для выбора попыток; работоспособность конкретной связи подтверждает handshake. |
||||
|
||||
STUN другого порта остаётся диагностикой IP/netif и никогда не назначает |
||||
профиль рабочему S. Результаты доступны через stun_nat API. |
||||
UDP-профиль не переносится на TCP. TCP inbound проверяется STCP handshake |
||||
к объявленному listener с проверкой ключа. Без публичного кандидата TCP |
||||
остаётся UNKNOWN/no_listener. SOCKS UDP не классифицируется как native endpoint. |
||||
|
||||
Проверки |
||||
-------- |
||||
|
||||
test_route_ping: реальные UDP-наблюдения, эталонные байты IPv4/IPv6, неправильный |
||||
round, аутентичный PONG чужого ключа, старое поколение, отмена без callback. |
||||
test_nat_detection: разные socket ID, два рабочих UDP endpoint, TCP listener, |
||||
замена помощника с тем же IP, повтор по старому контакту, подпись/SQLite профиля, |
||||
закрытие S посреди раунда и повторный ID/порт. Утечки/таймеры/сокеты проверяются. |
||||
Недостаточная UTUN-группа и достаточная CHAT-группа проверяются отдельно с TCP next hop |
||||
и отсутствием прямого транспорта A-C. Неудачные native INIT проверяют отсутствие |
||||
лишних перепроверок для UNKNOWN, ожидание reverse для CONTACT и пятиминутный лимит. |
||||
test_sock_match: независимость политики от фактов, UNKNOWN, CONTACT, давность, |
||||
частные адреса и разрешённая попытка hairpin; профили не протухают по времени. |
||||
|
||||
tests/stream_netns --nat-only: 16 настоящих uTun с 10 NAT, проверка всех UDP/TCP |
||||
профилей и точного собственного внешнего адреса. --nat-check включает эту фазу |
||||
до обычных JOIN/Merkle/BGP/streaming. Главный network namespace не меняется. |
||||
|
||||
Резервное определение типа NAT через STUN: реализация IPv4 2026-10-12 |
||||
------------------------------------------------------------------ |
||||
|
||||
Уточнение пользователя: STUN группирует рабочие сокеты по одинаковым IP/netif, |
||||
создаёт аналогичный UDP-сокет с другим портом и определяет тип NAT для группы. |
||||
Когда ETCP не может выполнить проверку из-за нехватки узлов, используется STUN. |
||||
Это заменяет прежнее предложение использовать STUN непосредственно на ETCP fd. |
||||
Резервная схема реализована для native IPv4 UDP. IPv6 остаётся отдельным этапом. |
||||
|
||||
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 предоставляет кэш по ключу группы, |
||||
NAT_DETECTION проверяет его готовность/инвалидацию раз в секунду; сам 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. |
||||
test_route_ping: UDP-наблюдения, IPv4/IPv6 encoding, чужой ключ, неверный round, |
||||
старое поколение и отмена без callback. |
||||
test_nat_detection: несколько endpoint, одинаковые IP узлов, подпись/SQLite, |
||||
TCP listener, транзитная CHAT-группа, recheck и teardown. |
||||
test_nat_detection_faults (Linux): реальные UDP/router-пакеты, потерянные PONG, |
||||
PRE/POST, поздний PRE после контакта, посторонняя отправка, пропавший отчёт, |
||||
разные/нестабильные mapping, совпавший IP B, hairpin и перебор IP наблюдателя. |
||||
Только часы тестового poll ускоряются; сокеты, шифрование и протокол настоящие. |
||||
tests/stream_netns/run.py --nat-only: 16 ядер, 6 настоящих NAT-шлюзов, |
||||
EIM open/filtered/strict. Главный network namespace проверяется на неизменность. |
||||
|
||||
Loading…
Reference in new issue