# Control Server (control_server) ## 1. Назначение TCP-сервер для мониторинга ETCP-соединений. Принимает подключения от GUI-клиента `etcpmon`, отдаёт по запросу метрики соединений/каналов/TUN/роутера, список узлов BGP-топологии, позволяет менять уровни отладки на лету. Уведомляет подписанных клиентов об изменениях узлов через `control_server_notify_node_change()`. ## 2. Как пользоваться 1. Вызвать `control_server_init()` — создаёт слушающий TCP-сокет на адресе из конфига `control_sock`, регистрирует accept-колбэк в uasync. 2. Выполнять цикл uasync: read-колбэк сам принимает и разбирает команды. Периодический вызов `control_server_process_updates()` не требуется; эта функция лишь повторно разбирает уже накопленные данные. 3. При изменении/удалении узла UTUN-группы вызывать `control_server_notify_node_change()` / `control_server_notify_node_removed()` — сервер отметит ID и отправит актуальное состояние при освобождении TX. 4. При остановке вызвать `control_server_shutdown()`. **Нюансы:** - Доступ разрешён только IPv4-клиентам по `control_allow` в конфиге; без правил доступ запрещён. IPv6-клиенты отклоняются. - В покое клиент отключается после 30 секунд без команд. При активной отправке тот же таймер отсчитывает 30 секунд без положительного `send()`; поступление команд его не продлевает. - Лог пишется в `control_server.log`: дампы содержат первые 256 байт каждого чтения RX и некоторых ответов TX. - `control_server_process_updates()` разбирает команды; метрики отправляются только по `CMD_GET_METRICS`. - Очередь отправки владеет исходными буферами до полной отправки или отключения; частичная отправка хранит отдельное смещение. - В TX помещается один кадр до 32 КиБ. Следующий producer ждёт пустой очереди через deferred `queue_waiter_wait()`. Чтение сокета и разбор уже принятого RX-буфера приостановлены до освобождения TX. - Подписка сохраняет ID текущих узлов (`8 × N` байт), сериализует их по одному при освобождении TX. Узлы повторно ищутся по ID; удалённые до своей очереди пропускаются. После начального списка отправляется `NODE_INFO_END` с seq запроса, затем накопленные live-изменения. - Live-изменения объединяются по ID (последнее обновление/удаление); предел — 256 различных ID. При переполнении подписчик отключается с WARN. CHAT-узлы в эту подписку не входят. - При stop/ошибке отменяются timer и waiter, включая уже запланированный callback, затем освобождаются список ID и TX-буфер. `control_client_send()` сам клиента не освобождает: ошибку обрабатывает владелец callback. - Если список сокетов превышает предел сообщения (32 768 байт; сейчас помещается 230 записей), возвращается `RSP_ERROR` с кодом `SERVER_BUSY`. Список не усекается; сокеты `probe_only` не учитываются. - `SET_DEBUG_CONFIG` требует ровно `2 + category_count` байт, не более 64 категорий. Несоответствие длины закрывает соединение без применения настроек. ## 3. API ### Структуры | Структура | Назначение | |-----------|-----------| | `struct control_server` | Состояние сервера: `listen_fd`, связный список `clients`, uasync-контекст, счётчик клиентов. | | `struct control_client` | Состояние клиента: `fd`, буфер приёма (`recv_buffer`), `output_queue` для асинхронной отправки, `selected_peer_id`, флаг подписки на узлы. | ### Функции | Функция | Назначение | |---------|-----------| | `control_server_init(server, ua, instance, bind_addr, max_clients)` | Создать слушающий сокет, зарегистрировать в uasync. Возвращает 0 / -1. | | `control_server_shutdown(server)` | Закрыть всех клиентов, освободить слушающий сокет, закрыть лог-файл. | | `control_server_process_updates(server)` | Повторно разобрать накопленные данные всех клиентов; вызов необязателен. | | `control_server_get_client_count(server)` | Вернуть количество подключённых клиентов. | | `control_server_notify_node_change(server, node)` | Разослать подписанным клиентам сериализованную информацию об узле. | | `control_server_notify_node_removed(server, node_id)` | Разослать подписанным клиентам уведомление об удалении узла. | ### Поддерживаемые команды ETCPMON | Команда | Ответ | Описание | |---------|-------|----------| | `CMD_LIST_CONN` | `RSP_CONN_LIST` | Список активных ETCP-соединений. | | `CMD_SELECT_CONN` | — | Выбрать соединение по `peer_node_id` для метрик. | | `CMD_GET_METRICS` | `RSP_METRICS` | Полные метрики: ETCP, TUN, router, все каналы с BBR. | | `CMD_LIST_SOCKETS` | `RSP_SOCKET_LIST` | Список локальных сокетов с NAT-типом и NAT-адресом. | | `CMD_ACTION` | `RSP_ACTION_RESULT` или `RSP_ERROR`, с seq запроса | Полные имена `"nodes"`/`"nat"`; `nat` подтверждает запуск проверки, неизвестное имя даёт ошибку. | | `CMD_GET_DEBUG_CONFIG` | `RSP_DEBUG_CONFIG` | Текущие уровни отладки (глобальный + по категориям). | | `CMD_SET_DEBUG_CONFIG` | — | Установить уровни отладки. | | `CMD_SUBSCRIBE_NODES` | `RSP_NODE_INFO` (список) + `RSP_NODE_INFO_END` | Подписка на изменения узлов + текущий список. | | `CMD_DISCONNECT` | — | Отключение клиента. | Общий формат задаёт `tools/etcpmon/etcpmon_protocol.h`: 4-байтовый заголовок с `seq_id`, packed-структуры, целые в порядке байтов хоста, без согласования версии/шифрования. Обычный ответ копирует `seq_id` запроса; снимки `NODE_INFO` имеют `seq=0`, маркер `NODE_INFO_END` — seq запроса подписки. `NODE_INFO` содержит префикс `01 04` и результат `topo_node_serialize()` для UTUN с подсетями. Action требует ровно 32 байта payload с NUL внутри поля. Повреждённая команда закрывает соединение; неизвестное корректное имя или недоступная служба возвращают `RSP_ERROR` без разрыва TCP.