You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

20 KiB

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       │
   └──────────────────────────────────────────────────┘