Files
utun2/src/proxy/tcp_proxy_server_doc.md
T

7.3 KiB
Raw Blame History

TCP proxy server (exit)

tcp_proxy_server.c/h открывает обычный TCP к IPv4-назначению по CONNECT ingress-узла и передаёт данные через UTUN-router. Общий протокол — doc/proxy_protocol.md, контракт API — tcp_proxy_server.h.

Инициализация и ресурсы

Контекст встроен в inst->tcp_proxy_server. init(inst) обнуляет его, читает config->global.tcp_proxy_server_enabled, при включении регистрирует сервис ETCP_RT_ID_TCP_PROXY_SERVER (0x04). NULL inst даёт -1; отсутствие config или отключённый сервер дают 0 без binding. Занятый binding/ошибка bind возвращает -1; enabled выставляется только после успеха. Повторный init отклоняется. Общие UDP/ICMP создаёт и уничтожает instance независимо от TCP-модулей.

INI-парсер включает сервер при обработке параметра секции [tcp_proxy_server]. Поддержанные ключи: tcp_recv_buf, fib, so_mark, netif, bind_ip, connect_timeout. Ключ enabled в этой секции текущим парсером не поддерживается; для включения достаточно, например, connect_timeout=2000. Пустая секция без параметров сама по себе флаг не выставляет.

Каждый поток принадлежит серверу, содержит собственный tcp_conn и proxy_flow. Поиск выполняется по паре peer_node_id/stream_id; одинаковые sid разных пиров допустимы. Повторный CONNECT существующей пары отклоняется с ERROR.

Connect и обмен данными

CONNECT содержит IPv4 и порт в сетевом порядке. Создаётся неблокирующий OS-сокет, применяются bind_ip, SO_MARK, FreeBSD FIB и привязка к интерфейсу там, где поддержаны, затем создаётся tcp_conn и вызывается connect. Эти настройки относятся к TCP-сокетам, UDP/ICMP их не наследуют. Таймаут connect — connect_timeout мс, по умолчанию 2000; 0 отключает таймаут. Асинхронная ошибка/таймаут приводит к ERROR и очистке.

Реальный успех connect ставит pending CONNECTED. Только после принятия CONNECTED router flow становится ready. Сервер отправляет ответы в ETCP_RT_ID_TCP_PROXY_CLIENT (0x08). Все proxy send используют TOPO_GROUP_UTUN и mode=0.

DATA клиента проверяется через proxy_flow_receive, копируется в pool-backed write_queue TCP. Кредит WINDOW возвращается после опустошения и write_queue, и write_buf, то есть после передачи принятой порции в OS TCP, а не при получении ETCP-пакета. Счётчик bytes_sent увеличивается до проверки/постановки в очередь и не является числом реально доставленных destination байтов.

Данные destination берутся из read_queue и отправляются через proxy_flow_send (force=0). При блокировке entry возвращается в начало read_queue. Для ошибки router ставится send-ready waiter; отсутствие ready/кредита (-2) ждёт CONNECTED/WINDOW. Safety-net 500 мс возобновляет drain с обычным force=0, не обходит окно/очередь router. Чтение tcp_io ограничивается его очередью. Отдельных tx_buf/pause_buf у сервера нет.

FIN, CLOSE и ERROR

FIN destination отправляется клиенту только после опустошения read_queue. Ожидание write_queue противоположного направления для этого не требуется. FIN клиента отмечает flow.fin_received и ставит FIN-маркер в OS write_queue: shutdown(SHUT_WR) следует за всеми ранее принятыми DATA. Дальнейшие DATA после FIN отклоняются. Когда FIN отправлен, принят встречный FIN и OS TCP отправил локальный FIN, в write_queue ставится close-маркер.

CLOSE клиента устанавливает cli_closed, паузит OS-чтение и отбрасывает read_queue. При установленном TCP close-маркер ждёт ранее принятые записи write_queue; ещё не подключённый поток освобождается сразу. ERROR немедленно уничтожает поток, не ожидая дренажа потенциально зависшего upload.

Общий proxy_flow повторяет только pending CONNECTED/WINDOW/FIN через 500 мс. CLOSE/ERROR не имеют экспоненциального backoff или собственной очереди server retry. OS error callback удаляет поток и делает отдельную попытку отправки ERROR; conn_free сам по себе не отправляет control-сообщение.

Диспетчеризация и очистка

recv_cb принимает владение entry/dgram, CONNECT/DATA handlers также освобождают их. Peer берётся из метаданных router, а не из физического conn. Уведомление router без payload (26 байт) удаляет только потоки данного peer. Это локальное CLOSE_ALL/restart/close-событие канала, не отдельная 9-байтовая команда proxy.

conn_free сразу помечает freed, удаляет поток из списка, отменяет waiter/control retry, DATA retry и диагностический таймер, уничтожает TCP. Память структуры освобождается через uasync_call_soon, чтобы текущая синхронная доставка могла проверить freed. Указатель нельзя использовать как живой поток после conn_free.

destroy выключает сервер и снимает только его binding; общие UDP/ICMP-контексты сохраняются. Instance останавливает proxy до разборки UTUN-группы и транспорта; ua обрабатывает отложенные free. Все операции выполняются в uasync. Каждую секунду диагностический таймер пишет размеры TCP-очередей, наличие write_buf, кеши пулов и OS socket buffers на уровне proxy=trace.