- etcp_router.h: 4 секции (протокол / внутренние структуры / API / служебные) - переименование router_set_max_inflight -> etcp_router_set_max_inflight - удалены неиспользуемые: input_q_count, waiter_register/cancel, conn_close_async (+ поля close_callback/close_callback_arg) - docs: актуализировать backpressure (on_send_ready на send_q) вместо удалённого waiter API
8.7 KiB
ETCP Router (etcp_router)
1. Назначение
Сервисный слой маршрутизации поверх ETCP — упрощённый TCP с восстановлением порядка доставки, дедупликацией и контролем inflight. Мультиплексирует до 256 сервисов (svc_id) на одном ETCP-соединении. Поддерживает transit (многошаговую маршрутизацию через промежуточные узлы), ретрансмиты, minRTT-измерения и подписи Ed25519.
2. Как пользоваться
Инициализация
// В utun_instance_init():
etcp_router_init(inst);
// Зарегистрировать обработчик сервиса (svc_id 0..255)
etcp_router_bind(inst, svc_id, my_service_callback);
Отправка
// Простой способ: сформировать ll_entry с svc_id в первом байте
struct ll_entry* e = queue_entry_new(0);
e->dgram = u_malloc(1 + payload_len);
e->dgram[0] = svc_id;
memcpy(e->dgram + 1, payload, payload_len);
e->len = 1 + payload_len;
etcp_route_send(inst, group_id, dst_node_id, e, force, mode); // mode: 0 / ROUTE_CRYPTO_SIGN / ROUTE_CRYPTO_ENCRYPT
// Или через существующий ROUTER_CONN:
struct ETCP_ROUTER_CONN* rconn = etcp_router_conn_get(inst, group_id, remote_node_id, svc_id);
etcp_router_conn_send(rconn, data, len, mode);
Приём
void my_service_callback(struct ETCP_CONN* conn, struct ll_entry* entry) {
if (!entry) { /* соединение закрыто */ return; }
uint8_t svc_id = entry->dgram[0];
uint64_t from = *(uint64_t*)(entry->dgram + ROUTER_SVC_SRC_OFF); // remote_node_id
uint8_t rx_flags = entry->dgram[ROUTER_SVC_FLAGS_OFF]; // был ли SIGNED/ENCRYPTED на wire
// ... данные начиная с entry->dgram[ROUTER_SVC_PAYLOAD_OFF] ...
queue_dgram_free(entry);
queue_entry_free(entry);
}
Backpressure
// На send_q (inflight переполнен):
struct queue_waiter_handle h;
etcp_router_on_send_ready(inst, group_id, node_id, svc_id, &h, my_ready_cb, my_arg);
// Когда send_q освободится — вызовется my_ready_cb
Подпись / шифрование (модуль route_crypto)
// mode — битовая маска, применяется в начале функции отправки (route_crypto_encode),
// при приёме проверяется/расшифровывается перед вызовом коллбэка (route_crypto_decode).
#include "route_crypto.h"
etcp_route_send(inst, group_id, dst, e, force, ROUTE_CRYPTO_SIGN | ROUTE_CRYPTO_ENCRYPT);
3. API
Ключевые структуры
SVC_ROUTE_HDR (33 байта) — заголовок пакета:
cmd(1) + group_id(8) + dst_node_id(8) + src_node_id(8) + seq(4) + svc_id(1) + flags(1) + timestamp(2)
Флаги: ROUTER_FLAG_START (0x80), ROUTER_FLAG_RST (0x40), ROUTER_FLAG_SIGNED (0x08), ROUTER_FLAG_ENCRYPTED (0x04), ROUTER_FLAG_CLOSE (0x02).
Формат доставки сервису (router → callback):
[svc_id:1][src_node_id:8][dst_node_id:8][rx_flags:1][payload...] (ROUTER_SVC_HDR_SIZE = 18).
ETCP_ROUTER_CONN — состояние логического подключения (group_id + remote_node_id + svc_id):
tx_seq,rx_seq,tx_acked— seq-нумерация для порядка и inflightrecv_q— reorder-очередь с хеш-индексом по seq (восстановление порядка)send_q— очередь ожидания при полном inflight (backpressure)send_waiter— waiter на send_input_q (по 1 пакету, round-robin между сервисами)watchdog_timer— медленная проверка инварианта drain (страховка от заклинивания)inflight_q— копии финальных отправленных пакетов для ретрансмита (хеш по seq)incoming_q— FIFO между сетевым приёмом и recv_q (защита от гонок)rtt,rtt_jitter,minrtt— измерения задержкиinflight_limit— текущий лимит пакетов в полёте (minrtt_probe снижает до 4)no_ack_count— последовательные ретрансмиты без ACK (при 17 — закрытие)start_sent,peer_sync_done— синхронизация после (пере)подключенияclosed— флаг закрытия (игнорирование таймеров)
ROUTER_INFLIGHT — запись в inflight_q: seq, last_sent_tb, send_count, dgram* (финальный wire-пакет)
TRANSIT_QUEUE — per (group_id, src, dst) пара на промежуточном узле:
q— FIFO транзитных пакетовwaiter— backpressure на send_input_q next_hop'аconn— ETCP_CONN следующего шага
Внутренняя архитектура
Приём: сеть → incoming_q (FIFO) → router_incoming_q_cb → recv_q (хеш по seq)
↓
router_try_assembly → deliver → route_crypto_decode → cb
Отправка: router_enqueue_send → router_build_packet (seq + route_crypto_encode) → send_q (FIFO, backpressure)
router_send_kick → waiter на send_input_q → router_send_drain_cb (по 1 пакету, round-robin)
→ etcp_send + inflight_q (копия финального пакета)
↓
router_track_inflight_state + retrans_schedule
ACK: периодический (10ms) + idle (500ms) → router_send_ack(rx_seq)
Константы
| Константа | Значение | Описание |
|---|---|---|
ROUTER_MAX_INFLIGHT |
256 | Макс. пакетов в полёте |
ROUTER_ACK_INTERVAL_TB |
100 | ACK интервал 10ms (0.1ms timebase) |
ROUTER_ACK_IDLE_TB |
5000 | Idle таймаут 500ms |
ROUTER_RETRANS_TIMEOUT_TB |
3000 | Таймаут ретрансмита 300ms |
ROUTER_NO_ACK_MAX_RETRANS |
17 | Макс. ретрансмитов без ACK (≈5s) |
ROUTER_MAX_SEND_Q_PACKETS |
64 | Порог backpressure send_q |
ROUTER_SEND_WATCHDOG_TB |
5000 | Watchdog проверка инварианта drain (500ms) |
Функции
| Функция | Описание |
|---|---|
etcp_router_init(inst) |
Инициализация: bind ETCP_ID_SVC_ROUTE, создание router_conns |
etcp_router_destroy(inst) |
Деинициализация: unbind + close_all + free |
etcp_router_bind(inst, svc_id, cb) |
Зарегистрировать обработчик сервиса |
etcp_router_unbind(inst, svc_id) |
Удалить обработчик сервиса |
etcp_route_send(inst, group, dst, entry, force, mode) |
Отправить пакет (loopback/transit/direct); mode = ROUTE_CRYPTO_SIGN/ENCRYPT |
etcp_router_conn_get(inst, group, remote, svc_id) |
Найти или создать ROUTER_CONN |
etcp_router_conn_send(rconn, data, len, mode) |
Отправить данные с авто-seq и inflight-контролем |
etcp_router_conn_close(rconn) |
Закрыть seq-подключение (CLOSE + уведомление сервиса) |
etcp_router_conn_close_all_for_node(inst, group, node_id) |
Закрыть все conn к узлу |
etcp_router_conn_restart(inst, group, remote, svc_id) |
Сброс состояния (перезапуск удалённой стороны) |
etcp_router_pause_retrans_for_node(inst, node_id) |
Сбросить ретрансмиты без закрытия (для conn reinit) |
etcp_router_set_max_inflight(rconn, new_max) |
Установить рабочий max_inflight |
etcp_router_on_send_ready/cancel_send_ready(inst, group, node, svc, h, cb, arg) |
Backpressure на send_q |
etcp_router_transit_queues_destroy(conn) |
Удалить все транзитные очереди ETCP_CONN |
Модуль route_crypto (пред/пост-обработка пакета)
| Функция | Описание |
|---|---|
route_crypto_encode(inst, peer, base, len, mode, &out, &len) |
Шифрование/подпись в начале отправки → финальный пакет |
route_crypto_decode(inst, wire, len, &out, &len, &rx_flags) |
Проверка подписи/расшифровка перед коллбэком; ошибка → дроп |