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.
 
 
 
 
 
 

145 lines
13 KiB

Детектор свойств endpoint
========================
Владелец рабочего сокета единственный вычисляет и публикует его профиль.
Помощники возвращают наблюдения и выполняют прямые испытания. Профиль относится
к протоколу, семейству, фактическому локальному IP:port и поколению сокета.
PUBLIC/NAT/PRIVATE/LOCAL — политика конфигурации, а не результат измерения.
Модель: src/routing_layer/address_profile.h.
mapping: UNKNOWN, UNCHANGED, EIM, DEPENDENT.
inbound: UNKNOWN, OPEN, CONTACT_REQUIRED.
evidence_id: случайный 64-битный ID завершённого раунда владельца.
checked_at: время измерения владельца, Unix microseconds.
generation: случайный 64-битный ID жизненного цикла локального endpoint.
UNCHANGED означает совпадение наблюдаемых и локального адресов на испытанных
направлениях. Оно не доказывает свободный вход. EIM означает одинаковый внешний
endpoint на испытанных разных IP и портах; оно также не доказывает свободный вход.
DEPENDENT означает различие mapping на испытанных назначениях, без общего
внешнего адреса. Дальнейшее разделение ADM/APDM не используется для выбора NCD.
UDP-раунд
---------
1. Владелец A выбирает живого наблюдателя B с доступным native UDP endpoint.
Через проверяемый сокет S выполняет OBSERVE. Ответ содержит фактический
source IP:port, наблюдаемый B, и исходный тег S/generation/round.
2. Через уже установленный ETCP к B запрашивает поиск помощника C.
B выбирает C из своих работающих UDP-пиров и возвращает его ключ и endpoint.
Собственный подтверждённый NAT-профиль B/C не требуется: иначе bootstrap
зависел бы от уже завершённой детекции. Пригодность подтверждают реальные пробы.
3. Если внешний IP C совпадает с IP B, A исключает C и командует B искать
следующего помощника. Совпадающие Node ID и разные порты одного IP не дают
независимого IP-наблюдения. При отсутствии второго IP результат неполный.
4. Для входящего испытания выбирается C, с IP которого S ранее не контактировал.
Все команды C идут A -> B -> C. До первого испытания A не шлёт UDP к C.
C отправляет три аутентифицированные ADDRESS_CHECK к endpoint из наблюдения B.
A регистрирует фактический ingress на S; один отчёт о PONG не заменяет ingress.
5. После завершения первого испытания S выполняет OBSERVE к основному порту C,
затем к другому UDP-порту того же помощника. Вспомогательный порт принадлежит
наблюдателю, скрыт из 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 через 180 секунд.
Срок фактов — 300 секунд. Таймеры владельца используют monotonic timebase;
checked_at не обновляется повторным NODEINFO и не зависит от ответа сервера NTP.
При несогласованных часах получатель использует осторожные попытки, не отказ.
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-наблюдения данного владельца.
Размер wire, версия, enum, границы списка исключений и ненулевые поколения/раунды
проверяются до обработки. Числа новых сообщений — BE, IP — сетевые байты.
owner_key и helper_key имеют разные поля и никогда не меняют значение по роли.
На смене адреса, закрытии сокета, reset/DELETE транспорта и остановке ядра
отменяются собственные раунды и связанные helper jobs. Поздние ответы не
применяются к новому socket generation даже при повторном ID и IP:port.
Детектор останавливается до разборки групп, сокетов и соединений.
Публикация и NCD
---------------
Профиль публикуется только владельцем в общей подписанной записи NODEINFO.
Списки и их границы входят в подпись; mapping/inbound/evidence/checked_at
сохраняются и загружаются вместе. node_addresses — производная проекция:
nat_mapping, nat_inbound, nat_evidence, nat_checked, address_kind.
При изменении формата старый кэш адресов пересоздаётся; старый NAT_TYPE не
интерпретируется как новая модель. Подписанные snapshots и записи чата не удаляются.
Общий 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.
Проверки
--------
test_route_ping: реальные UDP-наблюдения, эталонные байты IPv4/IPv6, неправильный
round, аутентичный PONG чужого ключа, старое поколение, отмена без callback.
test_nat_detection: разные socket ID, два рабочих UDP endpoint, TCP listener,
замена помощника с тем же IP, повтор по старому контакту, подпись/SQLite профиля,
закрытие S посреди раунда и повторный ID/порт. Утечки/таймеры/сокеты проверяются.
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 не меняется.