|
|
|
|
@ -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 |
|
|
|
|
|