diff --git a/src/routing_layer/nat_detection.h b/src/routing_layer/nat_detection.h index 0368c918..69b3b953 100644 --- a/src/routing_layer/nat_detection.h +++ b/src/routing_layer/nat_detection.h @@ -1,8 +1,14 @@ -/* Владелец измеряет рабочий UDP endpoint по разным IP, без сравнения портов одного IP. - * Наблюдения возвращаются через router. Прямые входящие пробы не получают UDP-ответа; - * незнакомость фактического source IP проверяет получатель до исходящего контакта. - * Mapping и inbound завершаются независимо; CONTACT — оценка по сериям до/после контакта. - * Все операции и callbacks выполняются в uasync-потоке ядра. */ +/* Определяет NAT и доступность незнакомых входящих для рабочего UDP-сокета. + * Два узла сообщают, какой внешний IP:порт видят: стабильное равенство означает EIM + * (совпадение с локальным — отсутствие трансляции), различие — strict. + * Помощник отправляет пакеты до и после исходящего контакта: + * вход до контакта означает open, только после — оценку filtered. + * Получатель проверяет настоящий IP отправителя; непубличный адрес не подходит. + * Команды и результаты идут по работающей сети uTun, тестовые пакеты — напрямую. + * Недостаток данных оставляет соответствующую часть результата UNKNOWN. + * TCP-вход проверяется отдельно подключением к listener, без переноса UDP-профиля. + * Все функции вызываются в uasync-потоке ядра; detector ниже — контекст этого ядра. + * Общее описание алгоритма: doc/nat_detection.txt. */ #ifndef NAT_DETECTION_H #define NAT_DETECTION_H @@ -19,55 +25,124 @@ struct UTUN_INSTANCE; struct ETCP_CONN; struct ETCP_LINK; struct ETCP_SOCKET; + +/* Закрытый контекст: проверки сокетов, история исходящих IP, задачи помощника и таймеры. */ struct NAT_DETECTION; +/* Почему проверка не завершилась полностью; отсутствие ответа само по себе не определяет NAT-тип. */ enum nat_check_reason { - NAT_REASON_NONE, NAT_REASON_NO_OBSERVER, NAT_REASON_NO_HELPER, NAT_REASON_CONTROL_LOST, - NAT_REASON_SEND_ERROR, NAT_REASON_TIMEOUT, NAT_REASON_ADDRESS_CHANGED, NAT_REASON_CONTACT_HISTORY, - NAT_REASON_PROXY, NAT_REASON_NO_LISTENER, NAT_REASON_CANCELLED, NAT_REASON_INVALID_OBSERVATION, NAT_REASON_DEPENDENT_MAPPING + NAT_REASON_NONE, /* Нет причины ошибки. */ + NAT_REASON_NO_OBSERVER, /* Нет подходящего узла для наблюдения внешнего адреса. */ + NAT_REASON_NO_HELPER, /* Нет подходящего помощника для проверки входящих. */ + NAT_REASON_CONTROL_LOST, /* Потерян маршрут для команд и результатов. */ + NAT_REASON_SEND_ERROR, /* Не удалось отправить тестовый пакет. */ + NAT_REASON_TIMEOUT, /* Проверка не завершилась за отведённое время. */ + NAT_REASON_ADDRESS_CHANGED, /* Наблюдаемый адрес изменился; прежний профиль сброшен. */ + NAT_REASON_CONTACT_HISTORY, /* IP уже был знаком или история неполна: вход не подтверждён как незнакомый. */ + NAT_REASON_PROXY, /* Сокет работает через SOCKS: его собственный NAT не измеряется. */ + NAT_REASON_NO_LISTENER, /* Нет публичного TCP-listener для проверки входящих. */ + NAT_REASON_CANCELLED, /* Проверка отменена. */ + NAT_REASON_INVALID_OBSERVATION, /* Наблюдение непригодно, в том числе из-за непубличного source IP. */ + NAT_REASON_DEPENDENT_MAPPING /* Определён strict, входящая доступность осталась неизвестной. */ }; +/* Снимок состояния одного сокета для диагностики; получение снимка не запускает проверку. */ struct nat_check_status { - struct address_profile measured_profile, effective_profile; - uint64_t generation, round; - uint64_t group_id, observer, helper; - uint32_t rounds, helper_reselections; - uint8_t phase, reason, history_complete, cycling; + struct address_profile measured_profile; /* Сохранённый результат измерения NAT и входящих этого сокета. */ + struct address_profile effective_profile; /* Профиль, выбранный для использования и публикации узлом. */ + uint64_t generation; /* Поколение сокета: отличает переоткрытие с тем же socket ID. */ + uint64_t round; /* Уникальный ID текущей или последней попытки. */ + uint64_t group_id; /* ID группы, через которую передаются команды и результаты. */ + uint64_t observer; /* ID узла, наблюдающего внешний адрес (B в описании алгоритма). */ + uint64_t helper; /* ID помощника, проверяющего входящие (C). */ + uint32_t rounds; /* Сколько попыток начато за время жизни состояния этого сокета. */ + uint32_t helper_reselections; /* Сколько раз назначен переход к следующей паре кандидатов. */ + uint8_t phase; /* 0 — ожидание; иначе внутренний номер текущего этапа. */ + uint8_t reason; /* Причина завершения или неполного результата: enum nat_check_reason. */ + uint8_t history_complete; /* 1 — история исходящих IP загружена без известных потерь. */ + uint8_t cycling; /* 1 — идёт цикл проверки с перебором кандидатов. */ }; +/* Создать детектор для instance с готовым маршрутизатором и группами; автоматические проверки включены. + * Возвращает контекст или NULL при ошибке; instance должен существовать до destroy(). */ struct NAT_DETECTION *nat_detection_create(struct UTUN_INSTANCE *instance); + +/* Остановить проверки, снять обработчики и освободить detector; NULL допустим. Ядро и его рабочие сокеты не освобождаются. */ void nat_detection_destroy(struct NAT_DETECTION *detector); -/* Объединить изменения NODEINFO/READY и пересмотреть доступные группы в следующем uasync-вызове. */ + +/* Сообщить detector об изменении адресов/готовности групп; пересмотр кандидатов отложен и объединяет повторные вызовы. */ void nat_detection_topology_changed(struct NAT_DETECTION *detector); -/* Отменить раунды, задачи помощников и наблюдения до закрытия router-каналов группы. */ + +/* Отменить проверки, помощь другим узлам и наблюдения в группе group_id до закрытия её управляющих каналов. */ void nat_detection_cancel_group(struct NAT_DETECTION *detector, uint64_t group_id); -/* Короткий приоритет начального измерения перед фоновыми UDP-пробами достижимости; задержка в timebase. */ + +/* На сколько отложить фоновые UDP-пробы, чтобы сохранить незнакомые IP для измерения detector. + * Возвращает задержку в timebase (1 единица = 100 мкс), 0 — откладывать не нужно. */ uint64_t nat_detection_probe_delay(struct NAT_DETECTION *detector); -/* Фоновый UDP-ответ к незнакомому IP может откладываться; односторонние NAT-пробы принимаются независимо. */ + +/* Можно ли сейчас обрабатывать PING на socket с ответом, не испортив проверку незнакомого входа. + * peer_key — проверенный ключ отправителя (32 байта), source — фактический IP:порт, flags — флаги PING, + * tag/length — его пользовательские данные/размер. Возвращает 1 — разрешено, 0 — пропустить без ответа. */ int nat_detection_ping_allowed(struct NAT_DETECTION *detector, struct ETCP_SOCKET *socket, const uint8_t peer_key[32], const struct sockaddr_storage *source, uint8_t flags, const uint8_t *tag, size_t length); + +/* Сообщить о готовности link; при отсутствии результата запланировать измерение его локального сокета. */ void nat_detection_link_ready(struct NAT_DETECTION *detector, struct ETCP_LINK *link); + +/* Сообщить об утрате/перезапуске connection; запланировать пересмотр маршрутов и отмену зависимых проверок при их потере. */ void nat_detection_cancel_for_conn(struct NAT_DETECTION *detector, struct ETCP_CONN *connection); -/* Перепроверить после реально отправленных INIT к OPEN; CONTACT — только после исчерпания reverse. */ + +/* Сообщить о неудаче прямого подключения connection к узлу с известной входящей доступностью. + * reverse_done != 0 — обратное подключение тоже исчерпано; только тогда перепроверяется filtered (CONTACT). */ void nat_detection_connection_failed(struct NAT_DETECTION *detector, struct ETCP_CONN *connection, int reverse_done); + +/* Удалить состояние socket и отменить связанные задачи; вызвать до освобождения самого сокета. */ void nat_detection_socket_removed(struct NAT_DETECTION *detector, struct ETCP_SOCKET *socket); + +/* Учесть исходящую отправку с socket на destination (IP:порт) до передачи пакета транспорту. + * История хранится по IP; контакт с помощником во время проверки лишает вход статуса незнакомого. */ void nat_detection_note_send(struct NAT_DETECTION *detector, struct ETCP_SOCKET *socket, const struct sockaddr_storage *destination); + +/* Учесть observed — наш IP:порт, сообщённый пиром при установлении link. + * Непубличное наблюдение не меняет интернет-профиль; изменение внешнего адреса сбрасывает прежнее измерение. */ void nat_detection_observed_by_peer(struct NAT_DETECTION *detector, struct ETCP_LINK *link, const struct sockaddr_storage *observed); + +/* Обработать запрос наблюдения, полученный на socket: source — реальный IP:порт отправителя, + * peer_key — его проверенный ключ (32 байта), tag/length — тег попытки/его размер в байтах. + * Непубличный source отклоняется; результат возвращается через защищённый управляющий канал. */ void nat_detection_record_observation(struct NAT_DETECTION *detector, struct ETCP_SOCKET *socket, const uint8_t peer_key[32], const struct sockaddr_storage *source, const uint8_t *tag, size_t length); + +/* Обработать входящую пробу помощника на проверяемом socket, не отвечая ему по UDP. + * peer_key — проверенный ключ (32 байта), source — фактический IP:порт, tag/length — тег попытки/размер в байтах. + * Проверяет подлинность, текущую попытку, публичность и незнакомость IP; неподходящего помощника заменяет. */ void nat_detection_receive_ingress(struct NAT_DETECTION *detector, struct ETCP_SOCKET *socket, const uint8_t peer_key[32], const struct sockaddr_storage *source, const uint8_t *tag, size_t length); + +/* Запланировать новые проверки всех рабочих сокетов detector, отменив текущие; действует и в ручном режиме. */ void nat_detection_request_check_all(struct NAT_DETECTION *detector); + +/* Запланировать проверку socket через конкретный observer (ненулевой ID узла), заменив текущую попытку. + * Возвращает 0 — запрос принят, -1 — сокет/детектор недоступен или observer равен 0; результат придёт позже. */ int nat_detection_request_check_socket(struct NAT_DETECTION *detector, struct ETCP_SOCKET *socket, uint64_t observer); + +/* Только для тестов: allow != 0 допускает непубличные IPv4 и IPv6 loopback; по умолчанию такие источники запрещены. */ void nat_detection_set_allow_local(struct NAT_DETECTION *detector, int allow); -/* При ручном режиме помощники продолжают работать, раунды запускает request_check_all. */ + +/* automatic != 0 включает фоновые проверки (по умолчанию); 0 оставляет ручной запуск request_check_*(). + * Уже начатые проверки и помощь другим узлам продолжаются в обоих режимах. */ void nat_detection_set_automatic(struct NAT_DETECTION *detector, int automatic); + +/* Записать в status снимок состояния socket; не создаёт состояние и не начинает проверку. + * Возвращает 0 — снимок заполнен, -1 — состояния нет или аргументы непригодны. */ int nat_detection_get_status(struct NAT_DETECTION *detector, struct ETCP_SOCKET *socket, struct nat_check_status *status); + +/* Вернуть постоянную строку для reason (enum nat_check_reason); неизвестному значению соответствует "invalid_reason". */ const char *nat_check_reason_name(uint8_t reason); #ifdef __cplusplus