Files
utun2/src/proxy/icmp_proxy_doc.md
T

6.0 KiB
Raw Blame History

ICMP proxy

icmp_proxy.c/h передаёт IPv4 Echo Request/Reply через ETCP_RT_ID_ICMP_PROXY в TOPO_GROUP_UTUN. Общий формат router описан в doc/proxy_protocol.md, контракты API — в icmp_proxy.h. Другие типы ICMP модуль не проксирует.

Контекст и raw socket

Каждый UTUN_INSTANCE владеет отдельным inst->icmp_proxy, глобального контекста нет. init(inst, ua) задаёт таймаут 50000 tb (5 с), случайное начало next_token и регистрирует общий binding. Raw socket отдельно запускает icmp_proxy_enable_exit(inst): один неблокирующий AF_INET/SOCK_RAW/IPPROTO_ICMP регистрируется в uasync до публикации.

Повторный init сохраняет существующий контекст и возвращает 0. Ошибка создания контекста/случайного token/binding возвращает отрицательный код без публикации контекста. enable_exit возвращает -errno при отказе raw socket/nonblocking/регистрации, сохраняя общий контекст и приём клиентских REPLY. Instance трактует отсутствие прав/поддержки raw как отключение только ICMP exit с WARN; отказ обязательного ресурса прерывает создание instance. TCP client/server не создают и не уничтожают общий контекст.

test_loopback по умолчанию выключен. icmp_proxy_set_test_loopback(inst, 1) разрешает виртуальный REPLY явно, закрывая ранее открытый raw socket. Переключение при pending-запросах отклоняется с WARN; без контекста setter ничего не делает. Production fallback без сети отсутствует.

Запрос и сопоставление ответа

REQUEST принимается при текущем server.enabled. Exit заменяет исходные echo id/seq 32-битным возрастающим token: старшие 16 бит — wire_id, младшие — wire_seq (в сетевом порядке). При совпадении wire_id/wire_seq/dst_ip с pending выбирает следующий token. Сопоставление ответа использует эту тройку; исходные id/seq и адрес приложения хранятся в icmp_request. Разные клиенты с одинаковыми исходными id/seq не смешиваются.

Raw reader проверяет IPv4 IHL/протокол, Echo Reply type/code и ICMP checksum, ищет pending по wire id/seq и адресу отправителя, удаляет найденный запрос и отправляет REPLY ingress-узлу. Данные ответа берутся из raw пакета. Сохранённый в request префикс payload до 1500 байт для формирования ответа не используется; он не ограничивает длину реального echo payload.

Таймер резервируется до первого raw send, затем продлевается каждые 5 с, пока pending не пуст. Удаляет записи с возрастом больше 5 с на очередном обходе; удаление не происходит точно в момент достижения порога. destroy снимает binding, отменяет таймер, закрывает raw socket/регистрацию, освобождает pending и обнуляет inst->icmp_proxy. Все действия — в uasync; inst и ua заимствуются до destroy. Отказ таймера до отправки отклоняет запрос; отказ продления освобождает все pending с ERROR. Запрос без действующего таймера в pending не публикуется.

Сообщения и доставка

Вход etcp_route_send: svc:1, cmd:1, dst_ip:4, orig_src_ip:4, echo_id:2, echo_seq:2, payload. REQUEST=1, REPLY=2, заголовок отправки — 14 байт. В REPLY dst_ip остаётся адресом цели ping, orig_src_ip — адресом приложения. Router callback получает 26-байтовые метаданные перед cmd, итого заголовок приёма — 39 байт. Peer находится в метаданных router; отдельного sender ID в ICMP payload нет. IP/id/seq передаются в network byte order.

send_to_exit копирует payload 0..65507 и возвращает результат router (0 = принято в очередь, <0 = ошибка). Локальный ICMP-контекст для этой отправки не требуется. Используются force=0, mode=0, собственного повтора REQUEST/REPLY нет.

Клиент допускает REPLY только от tcp_proxy_client->via_node_id. deliver_reply(inst, dst_ip, src_ip, ...) требует клиентский TUN, допускает payload 0..65506, собирает IPv4 src_ip→dst_ip с Echo Reply, TTL 64 и обеими checksum. Нечётный хвост checksum дополняется нулём без чтения за границей. Результат с префиксом версии 4 ставится в tun->input_queue: 0 = поставлен в очередь, -1 = ошибка. Payload заимствован на время вызова. recv_cb принимает владение входным entry/dgram при любом исходе.