Диаграмма вызовов функций при определении типа NAT (NAT detection) ===================================================================== === ИНИЦИАЛИЗАЦИЯ BGP === route_bgp_init(instance) └── etcp_set_new_conn_cbk(instance, route_bgp_etcp_conn_cbk, NULL) └── При создании нового ETCP_CONN: route_bgp_etcp_conn_cbk(conn, arg) ├── etcp_conn_set_up_cbk(conn, route_bgp_on_conn_up, bgp) └── etcp_conn_set_down_cbk(conn, route_bgp_on_conn_down, bgp) === УСТАНОВКА СОЕДИНЕНИЯ (сервер получает INIT_REQUEST) === etcp_connections_read_callback_socket(sock, arg) └── Получает ETCP_INIT_REQUEST ├── etcp_connection_create(instance, name) [если новый peer] │ └── Вызывает instance->etcp_new_conn_cbk [route_bgp_etcp_conn_cbk] ├── etcp_link_new(etcp, e_sock, remote_addr, is_server=1) ├── Отправляет ETCP_INIT_RESPONSE (с NAT info) ├── link->initialized = 1 └── etcp_conn_ready(link->etcp) └── conn->ready_cbk └── etcp_on_up(etcp) └── etcp->up_cbk = route_bgp_on_conn_up(etcp, bgp) === УСТАНОВКА СОЕДИНЕНИЯ (клиент получает INIT_RESPONSE) === etcp_connections_read_callback_socket(sock, arg) └── Получает ETCP_INIT_RESPONSE ├── Сохраняет NAT address: link->nat_ip, link->nat_port ├── link->initialized = 1 └── etcp_conn_ready(link->etcp) └── etcp_on_up(etcp) └── etcp->up_cbk = route_bgp_on_conn_up(etcp, bgp) === ЗАПУСК ПРОВЕРКИ NAT (при поднятии соединения) === route_bgp_on_conn_up(etcp, arg) └── route_bgp_new_conn(conn) ├── route_bgp_add_to_senders(bgp, conn) └── Сканирует ВСЕ линки ВСЕХ соединений: if (link->initialized && link->conn && link->nat_check_status < NAT_CHECK_IN_PROGRESS) └── route_bgp_start_link_nat_check(bgp, link) ├── if (nat_check_status == IN_PROGRESS) return ├── route_bgp_find_third_node(bgp, exclude=link->etcp) │ └── Ищет другой ETCP_CONN в senders_list ├── Определяет target_ip/target_port: │ ├── Если link->nat_ip != 0: использует nat_ip/nat_port │ └── Иначе: использует link->remote_addr ├── Проверяет is_local_subnet(target_ip) [если запрещено - skip] ├── route_bgp_extract_nat_addr(conn, &nat_ip, &nat_port) │ └── Ищет первый линк с nat_ip != 0 ├── Создает nat_check_arg { link, nat_ip, nat_port } └── route_ping_send_req_addr(bgp, third_conn, target_ip, target_port, count=3, interval=500ms, timeout=1000ms, wait_timeout=5000ms, cb=nat_link_check_cb, arg=nat_check_arg, pubkey=peer_pubkey) === ОТПРАВКА ЗАПРОСА НА ПИНГ (через third node) === route_ping_send_req_addr(bgp, to_conn, target_ip, target_port, ...) ├── Создает BGP_PING_REQUEST { cmd, subcmd=PING_REQ, request_id, count, interval, timeout, target_ipv4, target_port, pubkey } ├── etcp_send(to_conn, entry) [отправляет запрос third node] ├── Создает route_ping_pending { request_id, callback=nat_link_check_cb, arg, timeout_timer } └── Добавляет в bgp->ping_pending === ОБРАБОТКА PING_REQ (на third node) === route_bgp_receive_cbk(data, len, from_conn, bgp) └── ROUTE_SUBCMD_PING_REQ └── route_ping_handle_req(bgp, from_conn, data, len) ├── Создает route_ping_series_ctx { reply_conn, request_id, target_addr, pubkey, local_sock, count_total } ├── Находит первый IPv4 ETCP_SOCKET -> local_sock └── etcp_send_ping_to_socket(instance, local_sock, pubkey, &target_addr, timeout_ms, route_ping_single_cb, ctx, NULL, 0) └── Отправляет ETCP пинг на target_addr === ОТВЕТ НА ПИНГ (целевой узел) === etcp_connections_read_callback_socket() └── Получает ETCP пинг -> отправляет pong === ПОЛУЧЕНИЕ ОТВЕТА НА ПИНГ (third node) === route_ping_single_cb(success, rtt, arg, nonce, resp_data, resp_data_len) ├── Обновляет статистику: count_sent++, count_ok++, sum_rtt += rtt ├── Если count_sent < count_total: │ └── etcp_send_ping_to_socket(...) [следующий пинг] └── Иначе: └── route_ping_series_finish(ctx) ├── Вычисляет avg_rtt └── Создает BGP_PING_RESPONSE { cmd, subcmd=PING_RESP, request_id, count_sent, count_ok, avg_rtt } └── etcp_send(reply_conn, entry) [отправляет результат инициатору] === ПОЛУЧЕНИЕ PING_RESP (на инициаторе) === route_bgp_receive_cbk(data, len, from_conn, bgp) └── ROUTE_SUBCMD_PING_RESP └── route_ping_handle_resp(bgp, from_conn, data, len) ├── Ищет route_ping_pending по request_id ├── Отменяет timeout_timer └── Вызывает callback: nat_link_check_cb(success, avg_rtt, count_sent, count_ok, arg) === CALLBACK ПРОВЕРКИ NAT === nat_link_check_cb(success, avg_rtt, count_sent, count_ok, arg) ├── Определяет nat_type = success ? NAT_TYPE_OPEN : NAT_TYPE_RESTRICTED ├── link->nat_type = nat_type ├── link->nat_check_status = success ? NAT_CHECK_OPEN : NAT_CHECK_RESTRICTED └── route_bgp_send_nat_info(link->etcp, socket_id, nat_ip, nat_port, nat_type) ├── Создает BGP_NAT_INFO { cmd, subcmd=NAT_INFO, socket_id, nat_ip[4], nat_port, nat_type } └── etcp_send(conn, entry) [отправляет результат клиенту] === ПРИЕМ NAT_INFO (на клиенте) === route_bgp_receive_cbk(data, len, from_conn, bgp) └── ROUTE_SUBCMD_NAT_INFO └── route_bgp_handle_nat_info(bgp, from_conn, data, len) ├── Ищет линки по remote_socket_id ├── Устанавливает link->nat_type = info->nat_type └── Логирует результат NAT detection === ЗАПРОС НА ПРОВЕРКУ NAT (NAT_CHECK_REQ - новый механизм) === route_bgp_send_nat_check_req(conn, socket_id) ├── Создает BGP_NAT_CHECK_REQ { cmd, subcmd=NAT_CHECK_REQ, socket_id, interface_ip, interface_port } │ ├── Ищет линк с remote_socket_id == socket_id │ └── Берет interface_addr из линка -> interface_ip/interface_port └── etcp_send(conn, entry) route_bgp_handle_nat_check_req(bgp, from_conn, data, len) ├── Принимает BGP_NAT_CHECK_REQ ├── Сохраняет interface_ip/interface_port в struct sockaddr_storage dummy ├── Ищет линк с remote_socket_id == req->socket_id └── Если линк найден и не в процессе проверки: └── route_bgp_start_link_nat_check(bgp, target_link) [далее по стандартному flow] === СВЯЗЬ СТРУКТУР === struct ETCP_SOCKET ├── local_addr [адрес бинда из конфига] ├── interface_addr [IP интерфейса + порт: интерфейс/default route/конфиг] └── ... struct ETCP_LINK ├── remote_addr [адрес удаленного узла] ├── nat_ip [NAT IP, полученный из INIT_RESPONSE] ├── nat_port [NAT port, полученный из INIT_RESPONSE] ├── nat_type [OPEN/RESTRICTED, результат проверки] ├── nat_check_status [NONE/WAITING/IN_PROGRESS/OPEN/RESTRICTED] ├── initialized [0/1, флаг завершения handshake] └── ... struct ETCP_CONN ├── links [список ETCP_LINK] ├── up_cbk [route_bgp_on_conn_up] ├── down_cbk [route_bgp_on_conn_down] ├── ready_cbk [вызывается при etcp_conn_ready] ├── initialized [0/1, флаг готовности] ├── links_up [0/1, есть ли активные линки] └── ... === СТАТУСЫ NAT CHECK === NAT_CHECK_NONE = 0 [не проверялся] NAT_CHECK_WAITING = 1 [ожидает] NAT_CHECK_IN_PROGRESS = 2 [проверка запущена] NAT_CHECK_OPEN = 3 [результат: OPEN] NAT_CHECK_RESTRICTED = 4 [результат: RESTRICTED] NAT_TYPE_UNKNOWN = 0 NAT_TYPE_OPEN = 1 NAT_TYPE_RESTRICTED = 2 NAT_VERIFIED_UNKNOWN = 4 NAT_VERIFIED_OPEN = 5 NAT_VERIFIED_RESTRICTED = 6 NAT_VERIFIED_DIRECT = 7 === СООБЩЕНИЯ ПРОТОКОЛА === ETCP_INIT_REQUEST (0x02) [клиент -> сервер] ETCP_INIT_RESPONSE (0x03) [сервер -> клиент, включает NAT info] ETCP_INIT_REQUEST_NOINIT (0x04) ETCP_INIT_RESPONSE_NOINIT (0x05) ETCP_PING (0x06) ETCP_PONG (0x07) ROUTE_SUBCMD_NODEINFO (0x04) ROUTE_SUBCMD_REQUEST_TABLE (0x05) ROUTE_SUBCMD_WITHDRAW (0x06) ROUTE_SUBCMD_PING_REQ (0x07) ROUTE_SUBCMD_PING_RESP (0x08) ROUTE_SUBCMD_NAT_INFO (0x09) ROUTE_SUBCMD_NAT_CHECK_REQ (0x0A) === ПОЛНЫЙ FLOW (одна строка) === Handshake: etcp_connections_read_callback_socket -> INIT_REQUEST/RESPONSE -> link->initialized=1 -> etcp_conn_ready -> etcp_on_up -> route_bgp_on_conn_up -> route_bgp_new_conn -> route_bgp_start_link_nat_check -> route_ping_send_req_addr -> (через ETCP) -> route_ping_handle_req -> etcp_send_ping_to_socket -> (UDP) -> route_ping_single_cb -> route_ping_series_finish -> route_ping_handle_resp -> nat_link_check_cb -> route_bgp_send_nat_info -> route_bgp_handle_nat_info NAT_CHECK_REQ flow: route_bgp_send_nat_check_req -> BGP_NAT_CHECK_REQ -> route_bgp_handle_nat_check_req -> route_bgp_start_link_nat_check -> [стандартный ping flow]