Files
utun2/doc/etcp_router_arch.md
T

330 lines
20 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
> Исторический документ. Актуальная реализация и 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 │
└──────────────────────────────────────────────────┘
```