Диаграмма вызовов функций при определении типа 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]
