Files
utun2/src/proxy/tcp_proxy_server_doc.md
T
evgeny f47ad7d87f tcp_proxy: fix throughput (tx_queue backpressure) + connect timeout + integration test
- Router: unified service delivery format [svc_id][src][dst][payload] (end-to-end src/dst from SVC_ROUTE header)
- tcp_proxy_client: replace single tx_buf drop-on-backpressure with bounded tx_queue + send_q waiter + retry timer (no data loss, bulk window reopen — removes 62.5ms delayed-ACK stall)
- tcp_io: configurable connect timeout (tcp_conn_set_connect_timeout)
- tcp_proxy_server: connect_timeout config option (ms, default 2000, 0=off)
- check.sh: run tcp_proxy_full integration test with sudo (skip if passwordless sudo unavailable)
- test_tcp_io: connect timeout unit test; docs (lwip_nuances.md)
2026-09-03 13:49:09 +03:00

11 KiB
Raw Blame History

tcp_proxy_server

1. Назначение

TCP прокси-сервер на exit node. Принимает CONNECT-запросы от клиента через ETCP, создаёт OS TCP сокет к реальному адресату, релеит данные в обе стороны: клиент↔ETCP↔сервер↔OS TCP↔destination. Поддерживает backpressure (пауза чтения из OS сокета при заполнении ETCP очереди), graceful CLOSE, экспоненциальный ретрай управляющих сообщений, диагностические таймеры.

Работает в паре с tcp_proxy_client (на ingress node) и lwip_tcp (lwIP-стек для обработки TCP-сессий).

При старте сервера автоматически инициализируются udp_proxy и icmp_proxy, если клиентский TCP прокси не активен (чтобы exit node обслуживал все виды трафика).

2. Как пользоваться

Включение

В конфиге: tcp_proxy_server_enabled = yes — сервер регистрирует обработчик ETCP_RT_ID_TCP_PROXY через etcp_router_bind() и готов принимать команды от клиентов.

Типовой сценарий

  1. CONNECT (0x01): клиент шлёт dest_ip(4) + dest_port(2), сервер создаёт tcp_proxy_server_conn, открывает OS TCP сокет, делает connect() к destination.
  2. DATA (0x03): клиент шлёт данные → сервер пишет в OS write_queue через pool-аллокацию.
  3. Чтение из destination: данные с OS сокета попадают в read_queue, оттуда read_queue_drain_cb релеит их клиенту через ETCP.
  4. CLOSE (0x04): клиент закрыл соединение → сервер паузит чтение, сливает read_queue, шлёт shutdown(SHUT_WR) в OS сокет.
  5. FIN (0x06): клиент подтверждает получение FIN от destination → сервер push-ит FIN в OS сокет (завершение полного закрытия).
  6. ERROR (0x05): ошибка на клиенте → сервер форсированно закрывает соединение.
  7. Очистка: по on_closed_cb (OS сокет закрыт полностью) → tcp_proxy_server_conn_free() освобождает все ресурсы.

Ключевые нюансы

  • Backpressure: если etcp_route_send() вернула ошибку — данные буферизуются в tx_buf, регистрируется pause_waiter (ожидание освобождения ETCP канала), запускается fallback retry_timer (500ms, force=1). При возобновлении — сначала делается повтор tx_buf, потом продолжается дрейн очереди.
  • FIN handling: FIN от destination не отправляется клиенту пока есть pending writes в OS сокет (ждём on_flushed_cb) или pending reads (tx_buf / read_queue не пусты — флаг dst_fin_deferred). При опустошении очередей — FIN отправляется немедленно.
  • Graceful CLOSE: при получении CLOSE от клиента чтение из OS сокета паузится через tcp_conn_pause_read(), read_queue сливается (данные отбрасываются), push-ится close в OS сокет.
  • Ретрай CLOSE/ERROR: при неудачной отправке send_msg() — экспоненциальный backoff (50→100→200→…→5000 tb = 5ms→500ms) через close_timer + close_retry_cb.
  • Диагностический таймер (1s): логирует TCP буферы ОС, размеры read/write очередей, free counts entry/data pool-ов.
  • Connect-таймаут: connect() к destination асинхронный; если за connect_timeout мс (конфиг [tcp_proxy_server], по умолчанию 2000, 0 = без таймаута) соединение не установилось — срабатывает tcp_conn_handle_error(ETIMEDOUT) → клиенту уходит ERROR, соединение освобождается (защита от «чёрной дыры» и утечки fd).
  • CLOSE_ALL: специальный 9-байтовый пакет без валидного conn удаляет все соединения для заданного peer.

3. API

Структуры

Структура Назначение
tcp_proxy_server Корневой контекст: enabled, связный список conns, conn_count, ссылка на UTUN_INSTANCE
tcp_proxy_server_conn Состояние одного прокси-соединения: stream_id, peer_node_id, dest_ip:port, tcp_conn, буферы/счётчики/таймеры для backpressure и закрытия

Функции инициализации/завершения

Функция Описание
tcp_proxy_server_init(inst) Регистрирует ETCP биндинг на ETCP_RT_ID_TCP_PROXY, авто-инициализирует UDP/ICMP прокси если tcp_proxy_client не активен
tcp_proxy_server_destroy(inst) Освобождает все соединения, уничтожает UDP/ICMP прокси

Обработчики команд (вызываются из tcp_proxy_server_recv_cb)

Функция Описание
tcp_proxy_server_recv_cb(conn, entry) Главный ETCP-коллбэк: диспетчеризует subcmd → handle_connect/data/close/error/fin, обрабатывает CLOSE_ALL
tcp_proxy_server_handle_connect(inst,entry,sid,src) Создаёт OS TCP сокет, connect() к destination, аллоцирует tcp_proxy_server_conn, регистрирует diag_timer и коллбэки tcp_conn
tcp_proxy_server_handle_data(inst,conn,entry,sid) Копирует данные клиента в write_queue OS сокета через pool-аллокацию
tcp_proxy_server_handle_close(inst, sid) Паузит чтение, сливает read_queue, отправляет shutdown в OS сокет, освобождает соединение если не connected
tcp_proxy_server_handle_error(inst, sid) Делегирует в handle_close (обрабатывается идентично CLOSE)
tcp_proxy_server_handle_fin(inst, sid) Push-ит FIN в write_queue OS сокета (клиент подтвердил получение FIN от destination)

Управление соединениями

Функция Описание
tcp_proxy_server_find_conn(ctx, sid) Линейный поиск соединения по stream_id в связном списке
tcp_proxy_server_conn_free(rc) Полное освобождение: защита от double-free, отмена таймеров, отправка pending close/error если надо, удаление из списка, tcp_conn_destroy(), u_free

Внутренние callback-и (статические)

Функция Описание
send_msg(inst, dst, subcmd, sid, data, len, force) Формирует ETCP-пакет (svc_id + subcmd + sid + data) и отправляет через etcp_route_send()
send_close(rc) Отправляет CLOSE клиенту, при неудаче запускает экспоненциальный ретрай через close_timer
send_error(rc) Аналогично send_close, но шлёт ERROR
send_fin(rc) Отправляет FIN клиенту (без ретрая)
on_fin_cb(tc, arg) OS сокет получил FIN → машина состояний: если локальный FIN тоже → close; если write pending → ждать flush; если read pending → отложить FIN
on_flushed_cb(tc, arg) Write очередь OS сокета опустела → отправить FIN клиенту или закрыть если оба FIN получены
on_error_cb(tc, err, arg) Ошибка OS сокета → отправить ERROR клиенту, освободить соединение
on_closed_cb(tc, arg) OS сокет полностью закрыт → освободить соединение
read_queue_drain_cb(q, arg) Дрейн read_queue OS сокета → отправка данных клиенту через ETCP; backpressure: буферизация в tx_buf + pause_waiter + retry_timer
pause_resume_cb(q, arg) ETCP канал освободился → возобновить дрейн read_queue
retry_timer_cb(arg) Fallback-таймер 500ms: повтор tx_buf с force=1, при успехе возобновляет read_queue
close_retry_cb(arg) Экспоненциальный backoff для повторной отправки CLOSE/ERROR (50→5000 tb)
diag_timer_cb(arg) 1-секундный таймер: логирует TCP буферы ОС, размеры очередей, free-counts pool-ов

Константы

Макрос Значение Описание
TCP_PROXY_HDR_SIZE 6 svc_id(1) + subcmd(1) + stream_id(4)
TCP_PROXY_CONNECT_HDR_SIZE 12 HDR_SIZE + dest_ip(4) + dest_port(2)
TCP_PROXY_SUBCMD_CONNECT 0x01 Запрос на создание TCP соединения к destination
TCP_PROXY_SUBCMD_DATA 0x03 Данные для отправки в destination
TCP_PROXY_SUBCMD_CLOSE 0x04 Клиент закрывает прокси-соединение
TCP_PROXY_SUBCMD_ERROR 0x05 Ошибка на стороне клиента
TCP_PROXY_SUBCMD_FIN 0x06 Клиент подтвердил FIN от destination

Зависимости

Модуль Использование
tcp_io.h tcp_conn — абстракция OS TCP сокета с read/write очередями и memory pool-ами
ll_queue.h Очереди read_queue/write_queue OS сокета, pause_waiter для backpressure
etcp_router.h etcp_router_bind(), etcp_route_send(), etcp_router_on_send_ready(), etcp_router_cancel_send_ready()
u_async.h Таймеры (uasync_set_timeout, uasync_cancel_timeout)
udp_proxy.h Авто-инициализация при старте сервера
icmp_proxy.h Авто-инициализация при старте сервера
config_parser.h Флаг tcp_proxy_server_enabled
mem.h u_malloc/u_free/u_calloc для tx_buf и структур