Files
utun2/src/proxy/udp_proxy_doc.md
T

5.0 KiB
Raw Blame History

UDP proxy

udp_proxy.c/h передаёт UDP между TUN клиента и обычными UDP-сокетами exit через ETCP_RT_ID_UDP_PROXY в TOPO_GROUP_UTUN. Общие форматы и ограничения: doc/proxy_protocol.md. Контракты API и полей — в udp_proxy.h.

Контекст и жизненный цикл

Контекст принадлежит inst->udp_proxy, глобального контекста нет. init(inst, ua) создаёт его вместе с router binding, повторный init с тем же ua возвращает 0, сохраняя контекст. Контекст публикуется только после успеха; TCP client/server им не управляют. Диспетчер проверяет текущее inst->tcp_proxy_server.enabled для REQUEST. REPLY принимается только при наличии tcp_proxy_client и совпадении peer с его via_node_id. Один instance может совмещать роли.

Exit индексирует flows линейным списком по client_node_id + src_ip:src_port + dst_ip:dst_port. У каждого flow собственный неблокирующий AF_INET/SOCK_DGRAM, привязанный к INADDR_ANY:0, и read callback в uasync. Ответ принимается только от исходного dst_ip:dst_port; в REPLY адреса и порты меняются местами. REQUEST и принятый ответ обновляют last_activity_tb. Новый flow публикуется после socket/nonblocking/bind/read registration и создания таймера. Отказ любого этапа освобождает новый flow целиком. Результат sendto проверяется по точной длине: нулевая датаграмма с sent=0 успешна, EINTR повторяется, остальные ошибки/короткий send логируются с peer/tuple/длиной/errno. Датаграмма отбрасывается без добавления очереди повторов; рабочий flow сохраняется, невалидный сокет удаляется.

Таймер запускается при первом flow и проверяет список каждые 600000 tb (60 с), пока flows остаются. Удаляет flow при неактивности больше 60 с; удаление происходит на очередном обходе, а не точно в момент достижения порога. destroy снимает binding, отменяет таймер, закрывает сокеты/регистрации и обнуляет inst->udp_proxy. Все операции выполняются в uasync; inst и ua заимствуются до destroy. Отказ продления таймера закрывает оставшиеся flows с ERROR, не оставляя их без механизма очистки.

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

На вход etcp_route_send передаётся: svc:1, cmd:1, src_ip:4, src_port:2, dst_ip:4, dst_port:2, payload. REQUEST=1, REPLY=2. Заголовок отправки — 14 байт. Router callback получает 26 байт метаданных router перед подкомандой, поэтому заголовок приёма — 39 байт. Отдельного sender_node_id в UDP payload нет: peer берётся из метаданных router. Адреса и порты — network byte order.

send_to_exit требует созданный UDP-контекст, копирует заимствованный payload длиной 0..65507 и возвращает результат router: 0 означает принятие в очередь, отрицательное значение — ошибку. Собственного окна/повтора UDP у модуля нет; отправка использует force=0, mode=0.

deliver_reply требует inst->tcp_proxy_client->tun, допускает payload 0..65506, собирает IPv4/UDP с TTL 64, пересчитывает IPv4 checksum и оставляет UDP checksum=0. Пакет получает однобайтовый префикс версии 4 и ставится в tun->input_queue. Возврат 0 означает постановку в очередь; -1 — отсутствие TUN, недопустимый размер или ошибку выделения. Входной payload остаётся у вызывающего кода.

recv_cb принимает владение entry/dgram при любом исходе. init возвращает -1 при неверных аргументах, другом ua, занятом binding или ошибке выделения/регистрации.