Files
utun2/src/routing_layer/nat_detection.h
T

171 lines
17 KiB
C
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/* Модуль определяет тип 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 <stddef.h>
#include <stdint.h>
#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