Describe bounded control flow and corrected monitor behavior
This commit is contained in:
1 parent
cb50e268a4
commit
0132a984dd
2 files changed
+89
-19
No files matched your search
@@ -5,16 +5,28 @@ TCP-сервер для мониторинга ETCP-соединений. При
|
||||
|
||||
## 2. Как пользоваться
|
||||
1. Вызвать `control_server_init()` — создаёт слушающий TCP-сокет на адресе из конфига `control_sock`, регистрирует accept-колбэк в uasync.
|
||||
2. Периодически вызывать `control_server_process_updates()` для обработки накопившихся данных клиентов.
|
||||
3. При изменении/удалении узла BGP-топологии вызывать `control_server_notify_node_change()` / `control_server_notify_node_removed()` — сервер сам разошлёт уведомления подписанным клиентам.
|
||||
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-клиенты отклоняются.
|
||||
- Клиенты отключаются по idle-таймауту (30 секунд без команд). Каждая обработанная команда перезапускает таймер.
|
||||
- В покое клиент отключается после 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 категорий.
|
||||
@@ -35,7 +47,7 @@ TCP-сервер для мониторинга ETCP-соединений. При
|
||||
|---------|-----------|
|
||||
| `control_server_init(server, ua, instance, bind_addr, max_clients)` | Создать слушающий сокет, зарегистрировать в uasync. Возвращает 0 / -1. |
|
||||
| `control_server_shutdown(server)` | Закрыть всех клиентов, освободить слушающий сокет, закрыть лог-файл. |
|
||||
| `control_server_process_updates(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)` | Разослать подписанным клиентам уведомление об удалении узла. |
|
||||
@@ -48,8 +60,16 @@ TCP-сервер для мониторинга 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` | Для `"nodes"` — `RSP_ACTION_RESULT`; для `"nat"` — без ответа | `"nat"` — запрос NAT-проверки; `"nodes"` — дамп BGP-узлов текстом. |
|
||||
| `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.
|
||||
@@ -2,7 +2,10 @@
|
||||
|
||||
## 1. Назначение
|
||||
|
||||
Windows GUI монитор для ETCP соединений uTun в реальном времени (100ms refresh). Подключается по TCP к control_server внутри uTun и отображает метрики соединений, линков, TUN-интерфейса, роутера и дебаг-уровней. Позволяет менять debug levels на лету через GUI.
|
||||
Windows GUI монитор для ETCP соединений uTun. Подключается по обычному IPv4 TCP к control_server внутри uTun
|
||||
и отображает метрики соединений, линков, TUN, роутера и уровней отладки. Все сетевые вызовы и callbacks
|
||||
выполняются в GUI-потоке: WM_TIMER установлен на 10 мс, автоматический запрос метрик — раз в 100 мс
|
||||
при отсутствии предыдущего запроса в pending. Частота полученных сэмплов зависит от ответов сервера.
|
||||
|
||||
Connections и локальные сокеты опрашиваются раз в секунду, даже при пустом списке соединений.
|
||||
Выбор сохраняется по ID узла при изменении порядка и имён. При удалении выбранного соединения
|
||||
@@ -14,16 +17,28 @@ Connections и локальные сокеты опрашиваются раз
|
||||
|
||||
### Типовой сценарий
|
||||
|
||||
1. В конфиге uTun (`utun.conf`): `control_ip=127.0.0.1`, `control_port=9090`
|
||||
1. В конфиге uTun настроить `[control]` (без `allow` сервер отклоняет все подключения):
|
||||
|
||||
```ini
|
||||
[control]
|
||||
ip=127.0.0.1
|
||||
port=9090
|
||||
allow=127.0.0.1/32
|
||||
```
|
||||
2. Запустить uTun → он поднимает TCP control server на указанном порту
|
||||
3. Запустить `etcpmon.exe` (Windows, MinGW/MSYS2 сборка)
|
||||
4. Ввести IP:порт сервера (по умолчанию `192.168.40.1:9091`), нажать Connect
|
||||
5. Выбрать соединение из списка Connections — начнут поступать метрики
|
||||
4. Ввести числовой IPv4 и порт сервера, нажать Connect. При создании окна уже начинается попытка
|
||||
подключения к `192.168.40.1:9091`; для смены адреса сначала нажать Disconnect.
|
||||
5. Первое доступное соединение выбирается автоматически; другое можно выбрать в Connections.
|
||||
|
||||
Канал управления не шифруется и не проверяет ключи ETCP; `allow` фильтрует только IPv4 источника.
|
||||
Для удалённого управления требуется доверенный защищённый канал. DNS-имена и IPv6 клиент не принимает.
|
||||
|
||||
### Что отображает
|
||||
|
||||
**ETCP Connection:**
|
||||
- RTT (last / avg10 / avg100, в 0.1ms), jitter, bytes sent, retransmissions, ACKs, unacked bytes, optimal inflight, links count
|
||||
- RTT last / avg10 и jitter (wire: 0.1 мс, GUI: мс), bytes sent, retransmissions, ACKs, unacked bytes, optimal inflight, links count
|
||||
- Поле RTT Avg100 в окне есть, но соответствующего значения в протоколе нет: оно не заполняется
|
||||
- RX/TX дубликаты, таймеры (retrans/ack_resp), WaitAck состояние (suspended/cb/timer)
|
||||
- ID Sequence: next_tx_id, last_rx_id, last_delivered_id, rx_ack_till
|
||||
- Счётчики ошибок: reinit, reset, pkt_format_errors
|
||||
@@ -36,7 +51,7 @@ Connections и локальные сокеты опрашиваются раз
|
||||
**Links:**
|
||||
- Статус (UP/DOWN), encrypt/decrypt/send/recv ошибки
|
||||
- Total encrypted/decrypted bytes, bandwidth (Kbps), NAT changes
|
||||
- RTT (last/avg10), transmit time, keepalive counters (sent/recv)
|
||||
- RTT last, поле с подписью avg10 (фактически BBR minRTT, 0 без BBR), transmit time, keepalive counters (sent/recv)
|
||||
- Inflight bytes/packets/limit, таймеры (init/keepalive/shaper)
|
||||
|
||||
**BBR per link:**
|
||||
@@ -44,13 +59,13 @@ Connections и локальные сокеты опрашиваются раз
|
||||
- pacing_rate (bytes/sec), min_rtt (us), bw_hi/bw_lo (bytes/sec), inflight_hi/lo, pacing_gain
|
||||
|
||||
**Router Congestion (агрегированные):**
|
||||
- total_conns, total_inflight, total_send_q, total_recv_q
|
||||
- total_conns, total_inflight, total_send_q, total_recv_q (счётчики записей очередей, не байтов)
|
||||
- pkts sent/recv, send err, ACK sent/recv, dup_dropped, oob_dropped, stale_ack, sign_fail
|
||||
- RTT max/avg, jitter max, minRTT min
|
||||
|
||||
**Графики BBR (scrolling canvas, 860 сэмплов, 1px=1sample):**
|
||||
- Inflight Bytes, Cwnd (inflight_lim), minRTT, bwHi, Pacing Rate, bwLo, inflight Hi, BBR Mode
|
||||
- 8 каналов с индивидуальными галочками включения
|
||||
- История первого линка, до 860 сэмплов; если галочек нет, отображаются первые четыре канала
|
||||
- Вертикальная линия курсора с показом значений в блоках под графиком
|
||||
|
||||
**Очереди (Queues & Errors):**
|
||||
@@ -65,8 +80,11 @@ Connections и локальные сокеты опрашиваются раз
|
||||
- Radio-кнопки отправляют CMD_SET_DEBUG_CONFIG на сервер при клике
|
||||
|
||||
**Action:**
|
||||
- Текстовое поле + кнопка Send — отправка произвольной команды (CMD_ACTION)
|
||||
- Результат открывается в отдельном окне с моноширинным шрифтом Consolas
|
||||
- Текстовое поле + кнопка Send отправляют CMD_ACTION (до 31 байта текста + NUL)
|
||||
- Сервер распознаёт префиксы `nodes` (текстовый дамп узлов) и `nat` (перезапуск NAT/STUN-проверок)
|
||||
- `nodes` возвращает текст в отдельное окно Consolas, `nat` — подтверждение запуска проверки
|
||||
- Имена сравниваются целиком. Неизвестная команда или недоступная служба возвращают `RSP_ERROR`
|
||||
с исходным seq; GUI показывает ошибку и сохраняет TCP-соединение
|
||||
|
||||
### Сборка
|
||||
|
||||
@@ -80,12 +98,20 @@ make # MinGW Makefile
|
||||
make check # WinAPI/Winsock регрессии списка, выбора и переподключения
|
||||
```
|
||||
|
||||
На Linux с MinGW и Wine из каталога `tools/etcpmon`:
|
||||
|
||||
```bash
|
||||
CC=x86_64-w64-mingw32-gcc bash build.sh
|
||||
make CC=x86_64-w64-mingw32-gcc TEST_RUNNER=wine check
|
||||
wine etcpmon.exe
|
||||
```
|
||||
|
||||
Серверная часть (control_server) встроена в utun, собирается стандартным `./configure && make`.
|
||||
|
||||
### Горячие клавиши
|
||||
|
||||
- Tab в поле Server/Port — переключение между полями
|
||||
- Enter в поле Server/Port — Connect/Disconnect
|
||||
- Enter в поле Server/Port — Connect, если отключён; Escape — Disconnect
|
||||
- Enter в поле Action — отправка команды
|
||||
|
||||
## 3. API / Структура
|
||||
@@ -96,7 +122,7 @@ make check # WinAPI/Winsock регрессии списка, выбор
|
||||
|------|-----------|
|
||||
| `etcpmon_main.c` | WinMain: инициализация GUI, создание окна, message loop |
|
||||
| `etcpmon_client.h/c` | TCP клиент (Winsock2): connect, send/recv, парсинг протокола, history, pending requests с коллбэками |
|
||||
| `etcpmon_gui.h/c` | WinAPI GUI: окно 1000×1400, все контролы (~200 edit-полей, списки, кнопки, radio, graph), тултипы |
|
||||
| `etcpmon_gui.h/c` | WinAPI GUI: заданный размер окна 1250×1400, контролы, таймер, callbacks, тултипы |
|
||||
| `etcpmon_graph.h/c` | Real-time график: 8 каналов, off-screen буфер, пересчёт min/max, курсор, обновление channel values |
|
||||
| `etcpmon_protocol.h` | Бинарный протокол, общие структуры, shared между клиентом и сервером |
|
||||
|
||||
@@ -104,6 +130,13 @@ make check # WinAPI/Winsock регрессии списка, выбор
|
||||
|
||||
Формат: `[size:2][type:1][seq_id:1][payload...]`
|
||||
|
||||
`size` включает 4-байтовый заголовок, диапазон 4..32768. Все структуры packed(1), целые передаются
|
||||
в порядке байтов хоста. Константа VERSION=3 не передаётся по сети: клиенту и серверу нужны одинаковые
|
||||
форматы структур и порядок байтов. TCP-чтение может содержать часть кадра или несколько кадров.
|
||||
`seq=0` не создаёт ожидания в клиенте; `seq=1..255` сопоставляет ответ с pending по одному номеру.
|
||||
SELECT_CONN, SET_DEBUG_CONFIG и DISCONNECT не подтверждаются. GUI отправляет списки с seq=0,
|
||||
а GET_DEBUG_CONFIG, автоопрос метрик и Action — с ненулевым seq и таймаутом.
|
||||
|
||||
**Клиент → Сервер:**
|
||||
- `0x01` CMD_LIST_CONN — запрос списка соединений (без payload)
|
||||
- `0x02` CMD_SELECT_CONN — выбор соединения: `peer_node_id` (uint64_t)
|
||||
@@ -120,8 +153,25 @@ make check # WinAPI/Winsock регрессии списка, выбор
|
||||
- `0x82` RSP_METRICS: `etcp_metrics + tun_metrics + router_metrics + N × link_metrics`
|
||||
- `0x83` RSP_SOCKET_LIST: `count(1) + N × socket_info`
|
||||
- `0x84` RSP_DEBUG_CONFIG: `global_level(1) + cat_count(1) + names_csv(NUL-term) + levels[N]`
|
||||
- `0x85` RSP_NODE_INFO — один узел (raw BGP_NODEINFO_PACKET)
|
||||
- `0x86` RSP_NODE_INFO_END — конец списка узлов
|
||||
- `0x85` RSP_NODE_INFO — префикс `01 04` + `topo_node_serialize()` для UTUN с подсетями, seq=0
|
||||
- `0x86` RSP_NODE_INFO_END — конец списка узлов с seq запроса подписки
|
||||
- `0x88` RSP_NODE_REMOVED — узел удалён: `node_id(8)`
|
||||
- `0x89` RSP_ACTION_RESULT — результат команды: `code(1) + text(NUL-term)`
|
||||
- `0xFF` RSP_ERROR: `error_code(1) + message(NUL-term)`
|
||||
|
||||
Подписку на узлы поддерживает сервер, но GUI etcpmon её не запрашивает и эти уведомления не отображает.
|
||||
|
||||
### Очереди, таймауты и единицы
|
||||
|
||||
- Все команды клиента копируются в общий TX FIFO (до 64 КиБ/256 кадров). Short send и WSAEWOULDBLOCK
|
||||
сохраняют смещение для следующего прохода. Переполнение не принимает новую команду и возвращает ошибку.
|
||||
- Pending регистрируется вместе с кадром, срок ответа 5 секунд начинается после его полной отправки.
|
||||
Тип и payload ответа проверяются до снятия pending/callback; `RSP_ERROR` завершает любой запрос.
|
||||
- Connect и отсутствие прогресса TX также ограничены 5 секундами. Ответы на списки с seq=0 отдельно
|
||||
не отслеживаются; EOF/фатальная ошибка/таймаут закрывают соединение с диагностикой.
|
||||
- Сервер держит один готовый кадр до 32 КиБ. Чтение и разбор запросов ждут его отправки через
|
||||
отменяемый deferred waiter. Для начального списка узлов хранятся ID, сериализация выполняется порционно.
|
||||
- Live-изменения UTUN объединяются по ID (до 256); переполнение отключает подписчика с WARN.
|
||||
Серверный таймаут 30 секунд во время TX продлевается только положительным send, иначе — командами.
|
||||
- История хранит байты, байты/с и minRTT в мс. При выводе используются B/KiB/MiB и B/s/KiB/s/MiB/s,
|
||||
включая строки BBR. Сравнение идёт с текстом самого контрола, поэтому очистка/reconnect не скрывают прежние значения.
|
||||
Reference in new issue
Block a user