From 5914e26579316be38de05a5636bdf8d99f52c4e0 Mon Sep 17 00:00:00 2001 From: evgeny Date: Sat, 24 Oct 2026 13:42:20 +0200 Subject: [PATCH] Update datagram and TCP exit documentation for explicit ownership --- src/proxy/icmp_proxy_doc.md | 21 ++++++++++++--------- src/proxy/tcp_proxy_server_doc.md | 10 +++++----- src/proxy/udp_proxy_doc.md | 14 ++++++++++---- 3 files changed, 27 insertions(+), 18 deletions(-) diff --git a/src/proxy/icmp_proxy_doc.md b/src/proxy/icmp_proxy_doc.md index d2bbc085..152249d6 100644 --- a/src/proxy/icmp_proxy_doc.md +++ b/src/proxy/icmp_proxy_doc.md @@ -9,18 +9,19 @@ Каждый `UTUN_INSTANCE` владеет отдельным `inst->icmp_proxy`, глобального контекста нет. `init(inst, ua)` задаёт таймаут 50000 tb (5 с), случайное начало `next_token` и -сохраняет `server.enabled` в `is_exit`. Для exit открывает один неблокирующий -`AF_INET/SOCK_RAW/IPPROTO_ICMP` и регистрирует его чтение в uasync. +регистрирует общий binding. Raw socket отдельно запускает `icmp_proxy_enable_exit(inst)`: +один неблокирующий `AF_INET/SOCK_RAW/IPPROTO_ICMP` регистрируется в uasync до публикации. Повторный init сохраняет существующий контекст и возвращает 0. Ошибка создания -контекста или случайного token возвращает -1. Ошибка raw socket/регистрации -оставляет контекст без рабочего сокета, но init возвращает 0 и запрашивает router binding; -результат bind также не пробрасывается. Поэтому успешный init сам по себе не подтверждает -возможность отправки реального ping. Raw socket требует соответствующих прав ОС. +контекста/случайного 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**; открытый сокет имеет приоритет, -флаг его не отключает. Без контекста setter ничего не делает. +виртуальный REPLY явно, закрывая ранее открытый raw socket. Переключение при pending-запросах +отклоняется с WARN; без контекста setter ничего не делает. Production fallback без сети отсутствует. ## Запрос и сопоставление ответа @@ -35,11 +36,13 @@ Raw reader проверяет IPv4 IHL/протокол, Echo Reply type/code и Данные ответа берутся из raw пакета. Сохранённый в request префикс payload до 1500 байт для формирования ответа не используется; он не ограничивает длину реального echo payload. -Таймер запускается после первого отправленного запроса, затем каждые 5 с, пока pending +Таймер резервируется до первого raw send, затем продлевается каждые 5 с, пока pending не пуст. Удаляет записи с возрастом **больше** 5 с на очередном обходе; удаление не происходит точно в момент достижения порога. `destroy` снимает binding, отменяет таймер, закрывает raw socket/регистрацию, освобождает pending и обнуляет `inst->icmp_proxy`. Все действия — в uasync; `inst` и `ua` заимствуются до destroy. +Отказ таймера до отправки отклоняет запрос; отказ продления освобождает все pending с ERROR. +Запрос без действующего таймера в pending не публикуется. ## Сообщения и доставка diff --git a/src/proxy/tcp_proxy_server_doc.md b/src/proxy/tcp_proxy_server_doc.md index 6c15fa94..cf0058e4 100644 --- a/src/proxy/tcp_proxy_server_doc.md +++ b/src/proxy/tcp_proxy_server_doc.md @@ -9,10 +9,10 @@ Контекст встроен в `inst->tcp_proxy_server`. `init(inst)` обнуляет его, читает `config->global.tcp_proxy_server_enabled`, при включении регистрирует сервис -`ETCP_RT_ID_TCP_PROXY_SERVER` (0x04) и вызывает UDP/ICMP init независимо от наличия клиента. +`ETCP_RT_ID_TCP_PROXY_SERVER` (0x04). NULL inst даёт -1; отсутствие config или отключённый сервер дают 0 без binding. -Результаты bind/UDP/ICMP init не пробрасываются: 0 не гарантирует готовность всех сервисов. -Повторный init при живых потоках недопустим. +Занятый binding/ошибка bind возвращает -1; enabled выставляется только после успеха. +Повторный init отклоняется. Общие UDP/ICMP создаёт и уничтожает instance независимо от TCP-модулей. INI-парсер включает сервер при обработке параметра секции `[tcp_proxy_server]`. Поддержанные ключи: `tcp_recv_buf`, `fib`, `so_mark`, `netif`, `bind_ip`, @@ -79,7 +79,7 @@ DATA retry и диагностический таймер, уничтожает через uasync_call_soon, чтобы текущая синхронная доставка могла проверить freed. Указатель нельзя использовать как живой поток после conn_free. -destroy выключает сервер, снимает binding и уничтожает общие UDP/ICMP-контексты. -Клиент того же instance должен быть остановлен раньше; ua должен обработать отложенные free. +destroy выключает сервер и снимает только его binding; общие UDP/ICMP-контексты сохраняются. +Instance останавливает proxy до разборки UTUN-группы и транспорта; ua обрабатывает отложенные free. Все операции выполняются в uasync. Каждую секунду диагностический таймер пишет размеры TCP-очередей, наличие write_buf, кеши пулов и OS socket buffers на уровне proxy=trace. diff --git a/src/proxy/udp_proxy_doc.md b/src/proxy/udp_proxy_doc.md index 160aab94..88f69a43 100644 --- a/src/proxy/udp_proxy_doc.md +++ b/src/proxy/udp_proxy_doc.md @@ -8,8 +8,8 @@ ## Контекст и жизненный цикл Контекст принадлежит `inst->udp_proxy`, глобального контекста нет. `init(inst, ua)` -создаёт его и запрашивает router binding, повторный init возвращает 0, сохраняя контекст. -`is_exit` — снимок роли при init, но диспетчер проверяет текущее +создаёт его вместе с router binding, повторный init с тем же ua возвращает 0, сохраняя контекст. +Контекст публикуется только после успеха; TCP client/server им не управляют. Диспетчер проверяет текущее `inst->tcp_proxy_server.enabled` для REQUEST. REPLY принимается только при наличии `tcp_proxy_client` и совпадении peer с его `via_node_id`. Один instance может совмещать роли. @@ -18,12 +18,18 @@ Exit индексирует flows линейным списком по неблокирующий `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, не оставляя их без механизма очистки. ## Сообщения и доставка @@ -44,5 +50,5 @@ router перед подкомандой, поэтому заголовок пр Возврат 0 означает постановку в очередь; -1 — отсутствие TUN, недопустимый размер или ошибку выделения. Входной payload остаётся у вызывающего кода. -`recv_cb` принимает владение entry/dgram при любом исходе. `init` не пробрасывает -ошибку router bind, поэтому 0 не подтверждает работоспособность регистрации. +`recv_cb` принимает владение entry/dgram при любом исходе. `init` возвращает -1 при +неверных аргументах, другом ua, занятом binding или ошибке выделения/регистрации.