/** * @file nat_detection.h * @brief NAT-детекция через третьего пира (STUN-like ping). * * Самодостаточный модуль на своём ETCP_ID (0x02): * сам биндится, сам подписывается на conn_up/conn_down, * ищет третий узел прямо в instance->connections, * обновляет ETCP_SOCKET/loc_node и broadcast-ит NODEINFO. */ #ifndef NAT_DETECTION_H #define NAT_DETECTION_H #ifdef __cplusplus extern "C" { #endif #include #include #include "secure_channel.h" struct UTUN_INSTANCE; struct TOPO_GROUP; struct ETCP_CONN; struct ETCP_LINK; struct route_ping_pending; /* ====== Sub-команды протокола ====== */ #define NATDET_SUBCMD_PING_REQ 0x07 // сервер → третий узел: "пропингуй IP:port" #define NATDET_SUBCMD_PING_RESP 0x08 // третий узел → сервер: результат пинга #define NATDET_SUBCMD_NAT_INFO 0x09 // сервер → клиент: "твой NAT — EIM/STRICT/DIRECT" #define NATDET_SUBCMD_NAT_CHECK_REQ 0x0A // клиент → сервер: "проверь мой NAT, вот мой interface_addr" /* ====== Wire-формат сообщений ====== */ /** Сервер → третий узел: запрос серии ETCP-пингов на target_ip:target_port */ struct NATDET_PING_REQ { uint8_t cmd; // ETCP_ID_NAT_DETECTION uint8_t subcmd; // NATDET_SUBCMD_PING_REQ uint16_t reserved; uint32_t request_id; // для корреляции запрос/ответ uint8_t count; // число пингов в серии uint8_t socket_id; // сокет для отправки (на третьем узле) uint16_t interval_ms; // интервал между пингами uint16_t timeout_ms; // таймаут одного пинга uint8_t target_ipv4[4]; // IP цели (network byte order) uint16_t target_port; // порт цели (network byte order) uint8_t pubkey[SC_PUBKEY_SIZE]; // опционально: pubkey цели для шифрования пинга } __attribute__((packed)); /** Третий узел → сервер: агрегированная статистика серии пингов */ struct NATDET_PING_RESP { uint8_t cmd; uint8_t subcmd; // NATDET_SUBCMD_PING_RESP uint16_t reserved; uint32_t request_id; uint8_t count_sent; // всего отправлено пингов uint8_t count_ok; // успешно (получен PONG) uint16_t avg_rtt; // средний RTT в 0.1ms } __attribute__((packed)); /** Сервер → клиент: результат определения типа NAT */ struct NATDET_MSG_NAT_INFO { uint8_t cmd; uint8_t subcmd; // NATDET_SUBCMD_NAT_INFO uint8_t socket_id; // ID сокета клиента uint8_t nat_ip[4]; // NAT-адрес клиента (network byte order) uint16_t nat_port; // NAT-порт клиента (network byte order) uint8_t nat_type; // NAT_TYPE_EIM / NAT_TYPE_STRICT / NAT_TYPE_DIRECT } __attribute__((packed)); /** Клиент → сервер: запрос на проверку NAT для указанного сокета */ struct NATDET_MSG_NAT_CHECK_REQ { uint8_t cmd; uint8_t subcmd; // NATDET_SUBCMD_NAT_CHECK_REQ uint8_t socket_id; // ID сокета который надо проверить uint32_t interface_ip; // IP клиента на интерфейсе (network byte order) uint16_t interface_port; // порт клиента на интерфейсе (network byte order) } __attribute__((packed)); /* ====== Callback для завершения пинга ====== */ typedef void (*route_ping_callback_t)(int success, uint16_t avg_rtt, uint8_t count_sent, uint8_t count_ok, void* arg); /* ====== Контекст NAT-детекции (один на UTUN_INSTANCE) ====== */ struct NAT_DETECTION { struct UTUN_INSTANCE* inst; struct route_ping_pending* ping_pending; // список ожидающих PING_RESP ответов uint32_t next_ping_req_id; // монотонный счётчик request_id uint8_t allow_nat_check_local; // разрешить чек для loopback (тесты) }; /** Аргумент, передаваемый в callback nat_link_check_cb */ struct nat_check_arg { struct ETCP_LINK* link; uint32_t nat_ip; uint16_t nat_port; }; /* ==================== ПУБЛИЧНОЕ API ==================== */ /* -------- Жизненный цикл -------- */ /** Создать модуль: etcp_bind на 0x02, подписка на new_conn (on_up + on_down). */ struct NAT_DETECTION* nat_detection_create(struct UTUN_INSTANCE* inst); /** Уничтожить: отписаться от new_conn, etcp_unbind, очистить pending пинги. * Вызывается из topo_groups_destroy(). */ void nat_detection_destroy(struct NAT_DETECTION* nd); /* -------- Служебные: вызываются другими модулями в ответ на события ETCP -------- */ /** Линк поднялся (INIT handshake завершён) — запустить для него NAT-чек. * Вызывается из etcp_connections: handle_init_response_server(). */ void nat_detection_link_ready(struct NAT_DETECTION* nd, struct ETCP_LINK* link); /** Отменить все pending NAT-пинги для соединения и сбросить nat_check_status. * Вызывается из etcp.c при реините и etcp_connections.c при смене адреса линка. */ void nat_detection_cancel_for_conn(struct NAT_DETECTION* nd, struct ETCP_CONN* conn); /** Отправить клиенту NAT_INFO — результат DIRECT detection (публичный IP без пинга). * Вызывается из etcp_connections: handle_init_response_server(). */ void nat_detection_send_nat_info(struct NAT_DETECTION* nd, struct ETCP_CONN* to_conn, uint8_t socket_id, uint32_t nat_ip, uint16_t nat_port, uint8_t nat_type); /* -------- Control API -------- */ /** Принудительная перепроверка NAT для всех линков (etcpmon команда "nat"). * Вызывается из control_server. */ void nat_detection_request_check_all(struct NAT_DETECTION* nd); /* -------- Тестовые хелперы -------- */ /** Разрешить NAT-чек для локальных подсетей (127.x, 10.x, 192.168.x). * По умолчанию запрещено — только для тестов где узлы на loopback. */ void nat_detection_set_allow_local(struct NAT_DETECTION* nd, int allow); /** Отправить STUN-пинг через BGP-посредника (to_conn) к target_ip:target_port. * pubkey — опционально (NULL = взять из nodeinfo). * count пингов, интервал interval_ms, таймаут одного timeout_ms, общий wait_timeout_ms. */ int route_ping_send_req_addr(struct NAT_DETECTION* nd, struct TOPO_GROUP* group, struct ETCP_CONN* to_conn, uint32_t target_ip, uint16_t target_port, uint8_t count, uint16_t interval_ms, uint16_t timeout_ms, uint16_t wait_timeout_ms, route_ping_callback_t cb, void* arg, const uint8_t* pubkey); #ifdef __cplusplus } #endif #endif /* NAT_DETECTION_H */