5.0 KiB
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 или ошибке выделения/регистрации.