/* Модуль определяет тип NAT и доступность входящих для каждого рабочего сокета. * Результат — address_profile с двумя независимыми свойствами: * mapping: UNCHANGED — адрес не меняется; EIM — внешний IP:порт одинаков для разных IP назначения; * DEPENDENT (strict) — внешний IP:порт зависит от IP назначения. * inbound: OPEN — подтверждён вход; CONTACT — пакеты проходят после исходящего контакта (оценка filtered). * Принятое UDP/TCP-подключение с публичными локальным и удалённым IP сразу даёт UNCHANGED+OPEN (direct/open). * Для этого правила прежний исходящий контакт с IP пира не препятствие; LAN и SOCKS его не подтверждают. * UNKNOWN в любом свойстве означает, что данных для его определения пока недостаточно. * Одно соединение может подтвердить NAT-адрес: он сохраняется в socket->nat_addr с типом UNKNOWN * и публикуется как NAT-кандидат. Это не адрес интерфейса и не подтверждение открытого входа. * Для UDP сочетания EIM+OPEN и EIM+CONTACT означают eim open и eim filtered. * Для TCP непубличный локальный IP сразу означает NAT; публичный IP сравнивается с наблюдением одного пира. * TCP не запускает активных проб входа: OPEN даёт только принятое подключение; внешний IP сохраняется с портом 0. * EIM+UNKNOWN отображается как EIM?: внешний адрес совпал, незнакомый вход не удалось проверить. * Если своего измерения mapping нет, совпадение у двух STUN-серверов на том же IP/интерфейсе даёт предварительный EIM?. * Его source=STUN, inbound=UNKNOWN; внешний порт берётся только из наблюдения рабочего сокета, а не из STUN. * create(instance) запускает автоматические асинхронные проверки через доступные узлы сети. * get_status(detector, socket, &status) возвращает результат в status.effective_profile и ход проверки в остальных полях. * request_check_*() запрашивает повторную проверку; destroy(detector) останавливает модуль и освобождает его ресурсы. * Все вызовы — в uasync-потоке ядра; detector — его контекст NAT. Алгоритм: doc/nat_detection.txt. */ #ifndef NAT_DETECTION_H #define NAT_DETECTION_H #include #include #include "../lib/socket_compat.h" #include "address_profile.h" #ifdef __cplusplus extern "C" { #endif 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, /* 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, входящая доступность осталась неизвестной. */ NAT_REASON_BUDGET /* Локальная пауза по общему бюджету: текущая попытка будет повторена. */ }; /* Снимок состояния одного сокета для диагностики; получение снимка не запускает проверку. */ struct nat_check_status { 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 — идёт обход кандидатов, включая паузу по бюджету; phase=0 во время паузы. */ }; /* Создать детектор для instance с готовым маршрутизатором и группами; автоматические проверки включены. * Возвращает контекст или NULL при ошибке; instance должен существовать до destroy(). */ struct NAT_DETECTION *nat_detection_create(struct UTUN_INSTANCE *instance); /* Остановить проверки, снять обработчики и освободить detector; NULL допустим. Ядро и его рабочие сокеты не освобождаются. */ void nat_detection_destroy(struct NAT_DETECTION *detector); /* Сообщить detector об изменении адресов/готовности групп; пересмотр кандидатов отложен и объединяет повторные вызовы. */ void nat_detection_topology_changed(struct NAT_DETECTION *detector); /* Отменить проверки, помощь другим узлам и наблюдения в группе group_id до закрытия её управляющих каналов. */ void nat_detection_cancel_group(struct NAT_DETECTION *detector, uint64_t group_id); /* На сколько отложить фоновые UDP-пробы, чтобы сохранить незнакомые IP для измерения detector. * Возвращает задержку в timebase (1 единица = 100 мкс), 0 — откладывать не нужно. */ uint64_t nat_detection_probe_delay(struct NAT_DETECTION *detector); /* Можно ли сейчас обрабатывать 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 после UDP INIT или STCP handshake. Принятый вход с двумя публичными IP * подтверждает direct/open у принимающего сокета; иначе при отсутствии результата планируется измерение. * Для TCP фактический локальный IP должен совпадать с IP интерфейса этого сокета. */ 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); /* Сообщить о неудаче прямого подключения 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); /* Учесть IP, сообщённый по защищённому TCP link: local — фактический локальный адрес соединения, * observed — адрес, увиденный пиром. Публичные IP сравниваются без портов; сохраняемый внешний порт всегда 0. * Непубличный источник/наблюдение и другая семья адресов не меняют результат; вызов — в потоке ядра. */ void nat_detection_tcp_observed(struct NAT_DETECTION *detector, struct ETCP_LINK *link, const struct sockaddr_storage *local, 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); /* 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 } #endif #endif