# 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_resume_timer: void* retry send_q 50ms │ │ 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() │ ещё нет │ └────────┬────────────┘ │ ▼ ┌──────────────────────────────┐ │ inflight < ROUTER_MAX(256)? │ │ tx_seq - tx_acked < 256 ? │ └──────┬───────────┬───────────┘ │ YES │ NO (inflight полон) ▼ ▼ ┌──────────────┐ ┌─────────────────────┐ │router_send_one│ │ router_enqueue_send │ → send_q (FIFO) │ (прямая) │ │ (в очередь) │ send_blocked = 1 └──────┬────────┘ └─────────┬───────────┘ send_resume_timer (50ms) │ │ ▼ │ ┌────────────────────┐ │ │ tx_seq++ │ │ │ hdr.dst = dst │ │ │ hdr.src = node_id │ │ │ hdr.seq = tx_seq │ │ │ hdr.svc_id = svc │ │ │ │ │ │ etcp_send(conn, e) │ │ └────────────────────┘ │ │ ┌────────────────────────────┘ │ (при получении ACK от remote) ▼ ┌────────────────────┐ │ router_drain_send_q│ вызывается из ACK handler │ │ или по send_resume_timer │ while inflight < 256│ │ e = send_q.pop() │ │ router_send_one(e)│ │ │ │ если send_q пуст: │ │ send_blocked = 0 │ └────────────────────┘ ``` ## 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? │ │ │ (только ││ │ │ │ вперёд) ││ Дубликат? │ │ │ drain_q ││ 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: │ │ router_drain_send_q(rconn) │ └────────────────────────────────────────┘ ``` ## Inflight Control ``` ROUTER_MAX_INFLIGHT = 256 Отправка возможна когда: tx_seq - tx_acked < 256 Отправка заблокирована: tx_seq - tx_acked >= 256 При блокировке: данные → send_q (FIFO, без дропов) send_blocked = 1 send_resume_timer = 50ms retry Разблокировка: приход ACK от remote → tx_acked обновляется → router_drain_send_q() разгребает send_q или send_resume_timer (50ms) → router_drain_send_q() 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) │ │ │ │ send_resume_timer ─ 50ms ─────▶ router_send_resume │ │ (retry когда send_q не пуст) │ │ │ └─────────────────────────────────────────────────────┘ ``` ## Флаги 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 │ └──────────────────────────────────────────────────┘ ```