330 lines
20 KiB
Markdown
330 lines
20 KiB
Markdown
> Исторический документ. Актуальная реализация и wire-формат описаны в [ETCP Router](../src/routing_layer/etcp_router_doc.md).
|
||
|
||
# ETCP Router Architecture
|
||
|
||
## Структуры данных
|
||
|
||
```
|
||
ETCP_ROUTER_CONN (один на пару remote_node_id + svc_id)
|
||
┌─────────────────────────────────────────────────────┐
|
||
│ ll_entry (хеш-индекс: remote_node_id[8] + svc_id[1]) │
|
||
│ remote_node_id: u64 │
|
||
│ svc_id: u8 │
|
||
│ last_dgram_ts: u16 (timestamp последней датаграммы) │
|
||
│ │
|
||
│ tx_seq: u32 следующий seq для отправки │
|
||
│ rx_seq: u32 ожидаемый seq для сборки │
|
||
│ tx_acked: u32 сколько наших пакетов подтвердил remote │
|
||
│ last_sent_ack_seq: u32 последний отправленный ACK │
|
||
│ │
|
||
│ recv_q: ll_queue* очередь reorder (хеш по seq[4]) │
|
||
│ send_q: ll_queue* очередь при переполнении inflight │
|
||
│ │
|
||
│ ack_timer: void* периодический 100ms │
|
||
│ idle_ack_timer: void* idle таймаут 500ms │
|
||
│ send_waiter: queue_waiter_handle waiter на send_input_q │
|
||
│ watchdog_timer: void* проверка инварианта 500ms │
|
||
│ send_blocked: u8 inflight полон │
|
||
└─────────────────────────────────────────────────────┘
|
||
|
||
SVC_ROUTE_HDR (22 байта)
|
||
┌──────┬────────────────┬────────────────┬───────┬────────┬──────────┐
|
||
│ cmd │ dst_node_id │ src_node_id │ seq │ svc_id │ payload │
|
||
│ u8 │ u64 │ u64 │ u32 │ u8 │ ... │
|
||
└──────┴────────────────┴────────────────┴───────┴────────┴──────────┘
|
||
|
||
seq в data-пакетах = tx_seq (счетчик отправленных)
|
||
seq в ACK-пакетах = rx_seq (ожидаемый следующий)
|
||
CLOSE-флаг = 0x80000000 в seq, RST-флаг = 0x40000000
|
||
```
|
||
|
||
## Send Path (отправка данных)
|
||
|
||
```
|
||
etcp_route_send(inst, dst, entry)
|
||
etcp_router_conn_send(rconn, data, len)
|
||
│
|
||
▼
|
||
┌─────────────────────┐
|
||
│ rconn = find/get │ поиск по (dst, svc_id)
|
||
│ авто-создание если │ или etcp_router_conn_get()
|
||
│ ещё нет │
|
||
└────────┬────────────┘
|
||
│
|
||
▼
|
||
┌──────────────────────────────┐
|
||
│ router_enqueue_send(rconn) │ всегда через send_q (FIFO)
|
||
│ if send_q >= 64 && !force: │ → вернуть -1 (backpressure)
|
||
│ return -1 │
|
||
└──────┬───────────────────────┘
|
||
│ копия в send_q + router_send_kick()
|
||
▼
|
||
┌─────────────────────────────────────────┐
|
||
│ router_send_kick(rconn) │
|
||
│ closed/no_route → return │
|
||
│ send_q пуст → return │
|
||
│ inflight >= limit → send_blocked=1, ret │
|
||
│ waiter уже ждёт → return │
|
||
│ нет conn → set_no_route │
|
||
│ queue_waiter_wait(send_input_q, │
|
||
│ &send_waiter, drain_cb) │
|
||
└──────┬───────────────────────────────────┘
|
||
│ send_input_q опустел (round-robin: FIFO waiter-ов)
|
||
▼
|
||
┌──────────────────────────────┐
|
||
│ router_send_drain_cb(rconn) │ шлём ОДИН пакет
|
||
│ e = send_q.pop() │
|
||
│ router_send_one_flags(e) │
|
||
│ → tx_seq++ │
|
||
│ → etcp_send(conn, entry) │
|
||
│ если send_q ещё непуст: │
|
||
│ inflight полон → blocked=1│
|
||
│ иначе → queue_waiter_wait │ (в хвост → round-robin)
|
||
│ если пуст → send_blocked=0 │
|
||
└──────────────────────────────┘
|
||
|
||
Повторный запуск drain:
|
||
• приход ACK → router_handle_ack → kick
|
||
• маршрут появился → router_no_route_retry_cb → kick
|
||
• рост inflight-лимита → router_update_inflight_limit → kick
|
||
• страховка → router_send_watchdog_cb (500ms) → DEBUG_ERROR + kick
|
||
```
|
||
|
||
## Receive Path (приём данных)
|
||
|
||
```
|
||
etcp_router_recv_cb(conn, entry)
|
||
│
|
||
▼
|
||
┌──────────────────────┐
|
||
│ dst == node_id? │
|
||
└──┬───────────────┬───┘
|
||
│ YES │ NO (транзит)
|
||
▼ ▼
|
||
┌────────────────────┐ ┌──────────────────────────┐
|
||
│ Мы — целевая нода │ │ Транзит: найти next hop │
|
||
└────────┬───────────┘ │ route_bgp_find_conn_for_ │
|
||
│ │ node(bgp, hdr.dst) │
|
||
▼ │ etcp_send(next, entry) │
|
||
┌────────────────────┐ └──────────────────────────┘
|
||
│ pl_len == 0? │
|
||
└──┬───────────┬─────┘
|
||
│ YES │ NO (данные)
|
||
▼ ▼
|
||
┌─────────┐ ┌─────────────────────────┐
|
||
│ CLOSE/ │ │ rconn = find/get │
|
||
│ RST? │ │ (авто-создание если нет) │
|
||
└──┬──┬───┘ └───────────┬─────────────┘
|
||
│ │ │
|
||
│ │ NO (ACK) ▼
|
||
│ ▼ ┌───────────────────────────────────────┐
|
||
│ ┌──────────┐│ Проверка границ seq: │
|
||
│ │ tx_acked ││ d = seq - rx_seq │
|
||
│ │ = hdr.seq││ |d| < ROUTER_MAX_INFLIGHT? │
|
||
│ │ (только ││ │
|
||
│ │ вперёд) ││ Дубликат? │
|
||
│ │ kick ││ rx_seq - seq > 0 ИЛИ │
|
||
│ └──────────┘│ seq уже в recv_q? │
|
||
│ └──────┬─────────────┬──────────────────┘
|
||
│ │ OK │ out-of-bounds / dup
|
||
│ ▼ ▼
|
||
│ ┌──────────────┐ ┌─────────┐
|
||
│ │ recv_q.put() │ │ DROP │
|
||
│ │ хеш по seq │ └─────────┘
|
||
│ └──────┬───────┘
|
||
│ │
|
||
│ ▼
|
||
│ ┌──────────────────────┐
|
||
│ │ seq == rx_seq? │
|
||
│ └──┬───────────────┬───┘
|
||
│ │ YES │ NO
|
||
│ ▼ │
|
||
│ ┌──────────────┐ │
|
||
│ │ try_assembly │ │
|
||
│ │ while next в │ │
|
||
│ │ recv_q: │ │
|
||
│ │ deliver() │ │
|
||
│ │ rx_seq++ │ │
|
||
│ └──────┬───────┘ │
|
||
│ │ │
|
||
│ ▼ │
|
||
│ ┌──────────────────────┴──┐
|
||
│ │ router_deliver() │
|
||
│ │ вызывает callback │
|
||
│ │ сервиса (svc_id) │
|
||
│ │ entry: [svc_id|payload] │
|
||
│ └──────────────┬──────────┘
|
||
│ │
|
||
▼ ▼
|
||
┌────────────────────────────────────┐
|
||
│ router_schedule_ack(rconn) │
|
||
│ запускает ack_timer (100ms) если │
|
||
│ ещё не запущен │
|
||
└────────────────────────────────────┘
|
||
```
|
||
|
||
```
|
||
CLOSE / RST ветка:
|
||
┌────────────────────────────────────────┐
|
||
│ pl_len == 0 && seq & CLOSE/RST │
|
||
│ → router_close_and_notify(rconn) │
|
||
│ ├─ router_send_close_to_service() │
|
||
│ │ (callback с NULL conn) │
|
||
│ ├─ отправить CLOSE удалённой стороне│
|
||
│ ├─ отмена всех таймеров │
|
||
│ ├─ очистка recv_q + send_q │
|
||
│ └─ удаление из inst->router_conns │
|
||
└────────────────────────────────────────┘
|
||
```
|
||
|
||
## ACK Mechanism
|
||
|
||
```
|
||
┌─────────────────────────────────────────┐
|
||
│ router_schedule_ack(rconn) │
|
||
│ (вызывается при получении данных) │
|
||
└────────────────┬────────────────────────┘
|
||
│
|
||
┌────────────────▼────────────────────────┐
|
||
│ если idle_ack_timer активен — отменить │
|
||
│ если ack_timer не активен — запустить │
|
||
│ ROUTER_ACK_INTERVAL_TB = 1000 (100ms) │
|
||
└────────────────┬────────────────────────┘
|
||
│
|
||
▼ (через 100ms)
|
||
┌─────────────────────────────────────────┐
|
||
│ router_ack_timer_cb() │
|
||
│ │
|
||
│ if rx_seq != last_sent_ack_seq: │
|
||
│ router_send_ack(rconn) │
|
||
│ last_sent_ack_seq = rx_seq │
|
||
│ перезапустить ack_timer │
|
||
│ else: │
|
||
│ запустить idle_ack_timer (500ms) │
|
||
└────────────────┬────────────────────────┘
|
||
│
|
||
▼ (через 500ms)
|
||
┌─────────────────────────────────────────┐
|
||
│ router_idle_ack_timer_cb() │
|
||
│ │
|
||
│ if rx_seq != last_sent_ack_seq: │
|
||
│ router_send_ack(rconn) (досылка) │
|
||
│ last_sent_ack_seq = rx_seq │
|
||
└─────────────────────────────────────────┘
|
||
|
||
router_send_ack(rconn):
|
||
hdr.seq = rx_seq
|
||
hdr.svc_id = svc_id
|
||
pl_len = 0 → это ACK-пакет
|
||
etcp_send(conn, entry)
|
||
```
|
||
|
||
```
|
||
Обработка входящего ACK:
|
||
┌────────────────────────────────────────┐
|
||
│ pl_len == 0 && нет CLOSE/RST флагов │
|
||
│ │
|
||
│ if seq >= tx_acked: // только вперёд │
|
||
│ tx_acked = seq // обновить │
|
||
│ else: │
|
||
│ DEBUG_WARN "stale ACK" │
|
||
│ │
|
||
│ if send_blocked: │
|
||
│ send_blocked = 0 │
|
||
│ router_send_kick(rconn) │
|
||
└────────────────────────────────────────┘
|
||
```
|
||
|
||
## Inflight Control
|
||
|
||
```
|
||
ROUTER_MAX_INFLIGHT = 256
|
||
|
||
Отправка возможна когда: tx_seq - tx_acked < 256
|
||
Отправка заблокирована: tx_seq - tx_acked >= 256
|
||
|
||
При блокировке:
|
||
данные → send_q (FIFO, без дропов)
|
||
send_blocked = 1
|
||
|
||
Разблокировка:
|
||
приход ACK от remote → tx_acked обновляется
|
||
→ router_send_kick() → waiter → drain_cb разгребает send_q
|
||
или маршрут появился / inflight-лимит вырос → kick
|
||
страховка: router_send_watchdog_cb (500ms) → force kick
|
||
|
||
send_q полностью разобран → send_blocked = 0
|
||
```
|
||
|
||
## Полная схема потоков
|
||
|
||
```
|
||
┌──────────┐
|
||
│ Сервис │ (routing, proxy, NAT, ...)
|
||
│ (svc_id)│
|
||
└────┬─────┘
|
||
│
|
||
etcp_route_send() │ callback(svc_id, entry)
|
||
▼ ▲
|
||
┌─────────┐ send ┌─────────────────────────────────┐ recv ┌─────────┐
|
||
│ TUN │─────────▶│ ETCP ROUTER │─────────▶│ TUN │
|
||
│ (выход) │ │ │ │ (вход) │
|
||
└─────────┘ │ ┌──────────┐ ┌──────────┐ │ └─────────┘
|
||
│ │ send_q │ │ recv_q │ │
|
||
│ │ (FIFO) │ │ (хеш seq)│ │
|
||
│ └────┬─────┘ └────┬─────┘ │
|
||
│ │ │ │
|
||
│ ▼ ▼ │
|
||
│ ┌─────────────────────────┐ │
|
||
│ │ Inflight / Assembly │ │
|
||
│ │ tx_seq - tx_acked │ │
|
||
│ │ rx_seq → deliver │ │
|
||
│ └───────────┬─────────────┘ │
|
||
│ │ │
|
||
└──────────────┼──────────────────┘
|
||
│
|
||
┌──────▼──────┐
|
||
│ BGP │
|
||
│ (нахождение│
|
||
│ next hop) │
|
||
└──────┬──────┘
|
||
│
|
||
┌──────▼──────┐
|
||
│ ETCP conn │──▶ сеть
|
||
└─────────────┘
|
||
|
||
──▶ send (исходящие данные)
|
||
──▶ recv (входящие данные, callback сервису)
|
||
──▶ транзит (через BGP к следующему hop)
|
||
```
|
||
|
||
## Таймеры (один rconn)
|
||
|
||
```
|
||
┌─────────────────────────────────────────────────────┐
|
||
│ │
|
||
│ ack_timer ──────── 100ms ─────▶ router_ack_timer_cb│
|
||
│ (периодический) │
|
||
│ │
|
||
│ idle_ack_timer ─── 500ms ─────▶ router_idle_ack_cb │
|
||
│ (досылка последнего ACK) │
|
||
│ │
|
||
│ watchdog_timer ── 500ms ─────▶ router_send_watchdog│
|
||
│ (страховка инварианта drain, force kick) │
|
||
│ │
|
||
└─────────────────────────────────────────────────────┘
|
||
```
|
||
|
||
## Флаги seq
|
||
|
||
```
|
||
┌──────────────────────────────────────────────────┐
|
||
│ CLOSE: 0x80000000 нормальное закрытие conn │
|
||
│ RST: 0x40000000 conn не найден (reset) │
|
||
│ │
|
||
│ CLOSE/RST-пакеты: seq=0 | флаг, pl_len=0 │
|
||
│ Data-пакеты: seq=tx_seq, pl_len>0 │
|
||
│ ACK-пакеты: seq=rx_seq, pl_len=0 │
|
||
└──────────────────────────────────────────────────┘
|
||
```
|