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.
272 lines
18 KiB
272 lines
18 KiB
// etcp_router.h — Сервисный слой маршрутизации поверх ETCP |
|
// Маршрутизирует сервисные пакеты до целевой ноды, на промежуточных нодах ретранслирует |
|
// Упрощённый TCP поверх ETCP: восстановление порядка, дедупликация, ретрансмиты, inflight-контроль. |
|
// Подпись/шифрование вынесены в автономный модуль route_crypto (encode/decode). |
|
// |
|
// Формат SVC_ROUTE пакета: |
|
// [cmd:1] [group_id:8] [dst_node_id:8] [src_node_id:8] [seq:4] [svc_id:1] [flags:1] [timestamp:2] [reset_id:8] |
|
// [peer_reset_id:8] [challenge:8] [payload...] |
|
// flags: bit7=START, bit6=RST, bit3=SIGNED, bit2=ENCRYPTED, bit1=CLOSE |
|
// reset_id/peer_reset_id — идентификаторы отправителя/получателя во всех пакетах. |
|
// START=HELLO, RST=CHALLENGE, START|RST=CONFIRM; challenge используется только в handshake. |
|
// ACK-пакет: тот же заголовок, seq=rx_seq, payload_len=0 |
|
#ifndef ETCP_ROUTER_H |
|
#define ETCP_ROUTER_H |
|
|
|
#ifdef __cplusplus |
|
extern "C" { |
|
#endif |
|
|
|
|
|
#include <stdint.h> |
|
#include "etcp_api.h" |
|
|
|
// ==================================================================== |
|
// 1. Протокол SVC_ROUTE (wire-формат и формат доставки в сервис) |
|
// ==================================================================== |
|
|
|
#pragma pack(push, 1) |
|
struct SVC_ROUTE_HDR { |
|
uint8_t cmd; // ETCP_ID_SVC_ROUTE (0x03) / ETCP_RT_ID_SVC_ROUTE |
|
uint64_t group_id; // идентификатор группы |
|
uint64_t dst_node_id; |
|
uint64_t src_node_id; |
|
uint32_t seq; // data: tx_seq; ACK: rx_seq (ожидаемый seq) |
|
uint8_t svc_id; // идентификатор сервиса |
|
uint8_t flags; // bit7=START, bit6=RST, bit3=SIGNED, bit2=ENCRYPTED, bit1=CLOSE |
|
uint16_t timestamp; // часы отправителя (0.1ms), для RTT; ACK: эхо последнего data-пакета |
|
uint64_t reset_id; // случайный идентификатор локального экземпляра router_conn |
|
uint64_t peer_reset_id; // идентификатор экземпляра получателя (0 только в HELLO) |
|
uint64_t challenge; // случайный запрос/эхо для подтверждения актуальности пира |
|
}; |
|
#pragma pack(pop) |
|
#define SVC_ROUTE_HDR_SIZE sizeof(struct SVC_ROUTE_HDR) // 57 |
|
#define SVC_ROUTE_MAX_BINDINGS 256 |
|
|
|
// Единый формат доставки сервисной кодограммы (router → сервис): |
|
// [svc_id:1][src_node_id:8][dst_node_id:8][rx_flags:1][group_id:8][payload...] |
|
// src/dst — реальные end-to-end узлы из SVC_ROUTE заголовка (не промежуточные). |
|
// rx_flags — биты ROUTER_FLAG_ENCRYPTED/SIGNED, бывшие на wire (статус для сервиса). |
|
// group_id — группа SVC_ROUTE-пакета (для per-group сервисов вроде conn_mgr). |
|
#define ROUTER_SVC_SRC_OFF 1 |
|
#define ROUTER_SVC_DST_OFF 9 |
|
#define ROUTER_SVC_FLAGS_OFF 17 |
|
#define ROUTER_SVC_GROUP_OFF 18 |
|
#define ROUTER_SVC_PAYLOAD_OFF 26 |
|
#define ROUTER_SVC_HDR_SIZE 26 // svc_id(1) + src_node_id(8) + dst_node_id(8) + rx_flags(1) + group_id(8) |
|
|
|
// Биты в flags |
|
#define ROUTER_FLAG_START 0x80 |
|
#define ROUTER_FLAG_RST 0x40 |
|
#define ROUTER_FLAG_ENCRYPTED 0x04 // payload зашифрован AES-128-CCM (nonce+tag+crc) |
|
#define ROUTER_FLAG_SIGNED 0x08 // пакет содержит Ed25519 подпись (64 байта после payload) |
|
#define ROUTER_FLAG_CLOSE 0x02 // нормальное закрытие conn |
|
|
|
// ==================================================================== |
|
// 2. Внутренние структуры и константы (etcp_router.c, control_server.c) |
|
// ==================================================================== |
|
|
|
#define TRANSIT_QUEUE_HASH 256 |
|
|
|
// Очередь транзитных пакетов для пары (group_id, src_node_id, dst_node_id) |
|
// Создаётся когда не получается отправить пакет в ETCP напрямую, |
|
// удаляется когда очередь становится пустой. |
|
// ll.data[0..23] = group_id:8 + src:8 + dst:8 — совпадает с полями ниже по памяти |
|
struct TRANSIT_QUEUE { |
|
struct ll_entry ll; // data[0..23] = group_id:8 + src_node_id:8 + dst_node_id:8 (24-byte hash index) |
|
uint64_t group_id; // = data[0..7] |
|
uint64_t src_node_id; // = data[8..15] |
|
uint64_t dst_node_id; // = data[16..23] |
|
struct ll_queue* q; // FIFO транзитных пакетов (ll_entry с dgram), начиная с data[16] |
|
struct queue_waiter_handle waiter; // backpressure waiter на bp-очередь (normalizer->input или tx_queue) |
|
struct ETCP_CONN* conn; // next_hop (для etcp_send в drain_cb) |
|
}; |
|
|
|
// Состояние одного логического подключения (group_id + remote_node_id + svc_id) |
|
struct ETCP_ROUTER_CONN { |
|
struct ll_entry ll; // data[0..15]=group_id:8+remote_node_id:8, data[16]=svc_id — хеш-индекс |
|
uint64_t group_id; // = data[0..7] |
|
uint64_t remote_node_id; // = data[8..15] |
|
uint8_t svc_id; // = data[16] |
|
uint16_t last_dgram_ts; // последний timestamp датаграммы (rx или tx, timebase 0.1ms) |
|
uint16_t rtt; // измеренный RTT (0.1ms) |
|
int32_t rtt_jitter; // EMA jitter, fixed-point *65536 (как в ETCP) |
|
uint16_t max_inflight; // настраиваемый рабочий максимум (default=ROUTER_MAX_INFLIGHT) |
|
uint16_t inflight_limit; // текущий эффективный лимит (=max_inflight или MINRTT_PROBE_MAX_INFLIGHT) |
|
uint16_t minrtt; // скользящее среднее minRTT (0.1ms), default=100 (10ms) |
|
uint16_t minrtt_window[10]; // окно 10 последних замеров minRTT |
|
uint64_t minrtt_window_tb[10];// таймстемпы замеров (timebase 0.1ms) |
|
uint8_t minrtt_window_idx; // cyclic index 0..9 |
|
uint8_t minrtt_window_count; // активных записей в окне (0..10) |
|
uint8_t minrtt_probe; // режим измерения (inflight capped at 4) |
|
uint64_t last_inflight_loaded_tb; // когда inflight >= threshold |
|
uint64_t last_inflight_unloaded_tb; // когда inflight < threshold |
|
uint8_t inflight_was_loaded; // 1=канал загружен |
|
uint16_t last_recv_pkt_ts; // timestamp пира из последнего data-пакета |
|
uint64_t last_recv_pkt_local_tb; // наше время приёма последнего data-пакета (0.1ms timebase) |
|
uint8_t last_recv_updated; // флаг: есть данные для эха в ACK |
|
|
|
uint32_t tx_seq; // следующий seq для отправки |
|
uint32_t tx_sent; // граница фактически отправленных пакетов (ACK <= tx_sent) |
|
uint32_t rx_seq; // ожидаемый seq для сборки (next expected) |
|
uint32_t tx_acked; // сколько наших пакетов подтвердил remote (для inflight) |
|
uint32_t last_sent_ack_seq; // последний отправленный ACK (= rx_seq на момент отправки) |
|
uint64_t last_ack_sent_tb; // время отправки последнего ACK (timebase 0.1ms) |
|
|
|
struct UTUN_INSTANCE* inst; |
|
struct ETCP_CONN* recv_conn; // физический conn последнего принятого data-пакета (для deliver, не NULL) |
|
struct ll_queue* recv_q; // reorder очередь: hash по seq (4 байта, offset 0) |
|
void* ack_timer; // периодический таймер (10ms при consumer_ack) |
|
void* idle_ack_timer; // idle таймер (дослать последний ack) |
|
|
|
struct ll_queue* send_q; // очередь ожидающих отправки (inflight полон) |
|
struct queue_waiter_handle send_waiter; // waiter на send_input_q (по 1 пакету, round-robin) |
|
struct ll_queue* send_waiter_q; // очередь регистрации, не результат повторного поиска маршрута |
|
uint32_t retrans_seq; // следующий seq текущего прохода ретрансмитов |
|
uint32_t retrans_end; // конец прохода (исключительно) |
|
void* watchdog_timer; // DEBUG-watchdog: медленная проверка инварианта drain |
|
uint8_t send_blocked; // 1 = inflight полон, ждём ack |
|
uint8_t no_route; // 1 = нет BGP-маршрута, передача приостановлена |
|
void* no_route_timer; // таймер 20ms проверки появления маршрута |
|
uint8_t no_ack_count; // счётчик последовательных ретрансмиссий без ACK |
|
uint8_t closed; // 1 = в процессе закрытия, таймеры игнорируют |
|
uint64_t reset_id; // собственный идентификатор; не меняется при рестарте пира |
|
uint64_t peer_reset_id; // подтверждённый идентификатор пира (0 = handshake не завершён) |
|
uint64_t pending_peer_id; // кандидат, ещё не имеющий права менять состояние сессии |
|
uint64_t pending_challenge; |
|
uint64_t challenge_sent_tb; |
|
void* handshake_timer; |
|
uint8_t start_sent; // получен первый ACK данных |
|
uint8_t peer_sync_done; // 1 = завершено подтверждение peer ID |
|
|
|
// Ретрансмиты: inflight очередь |
|
struct ll_queue* inflight_q; // хеш по seq (4 байта), непрерывный блок без SACK |
|
void* retrans_timer; // таймер проверки застоя ACK |
|
uint64_t last_ack_changed_tb; // когда tx_acked последний раз менялся (0.1ms) |
|
|
|
// Очередь приёма входящего трафика (между сетью и recv_q) |
|
struct ll_queue* incoming_q; // FIFO очередь входящих пакетов |
|
uint8_t incoming_data_ready; // 1 = данные в incoming_q и callback был вызван |
|
|
|
uint32_t c_pkts_sent; // успешные отправки данных |
|
uint32_t c_pkts_send_err; // ошибки отправки |
|
uint32_t c_pkts_rcvd; // получено и доставлено данных |
|
uint32_t c_ack_sent; // отправлено ACK |
|
uint32_t c_ack_recv; // получено ACK (последовательных) |
|
uint32_t c_retrans_done; // число выполненых ретрансмитов |
|
uint32_t c_dup_dropped; // дропнуто дубликатов seq |
|
uint32_t c_oob_dropped; // дропнуто out-of-bounds seq |
|
uint32_t c_stale_ack; // устаревших ACK |
|
uint32_t c_sign_fail; // signature verification failures |
|
}; |
|
|
|
#define ROUTER_CONN_HASH_SIZE 256 |
|
#define ROUTER_RECVQ_HASH_SIZE 1024 |
|
#define ROUTER_MAX_INFLIGHT 256 // макс пакетов в полёте (для контроля inflight) |
|
#define ROUTER_ACK_INTERVAL_TB 100 // интервал ACK: 10ms в timebase (0.1ms) |
|
#define ROUTER_ACK_IDLE_TB 5000 // idle таймаут: 500ms |
|
#define ROUTER_MAX_SEND_Q_PACKETS 64 // порог backpressure на send_q |
|
#define ROUTER_SEND_WATCHDOG_TB 5000 // DEBUG-watchdog: 500ms проверка инварианта drain |
|
|
|
// Ретрансмиты |
|
#define ROUTER_RETRANS_TIMEOUT_TB 3000 // 300ms в timebase (0.1ms) |
|
#define ROUTER_INFLIGHT_HASH_SIZE 1024 |
|
#define ROUTER_NO_ROUTE_RETRY_TB 200 // 20ms проверка появления маршрута |
|
#define ROUTER_NO_ACK_MAX_RETRANS 17 // 17 × 300ms ≈ 5s таймаут без ACK |
|
|
|
// minRTT measurement |
|
#define MINRTT_UNLOADED_MIN_TB 500 // 50ms минимальное время разгрузки канала |
|
#define MINRTT_PROBE_TIMEOUT_TB 100000 // 10 сек без свежих замеров → probe |
|
#define MINRTT_PROBE_MAX_INFLIGHT 4 // лимит inflight в режиме probe |
|
#define MINRTT_WINDOW_SIZE 10 |
|
#define MINRTT_DEFAULT_TB 100 // default minRTT = 10ms (заменяется первым RTT) |
|
|
|
// Inflight запись — копия финального wire-пакета (уже закодирован encode'ом) для ретрансмита |
|
struct ROUTER_INFLIGHT { |
|
struct ll_entry ll; // индекс по seq (4 байта, offset 0) |
|
uint32_t seq; |
|
uint64_t last_sent_tb; // время последней отправки (0.1ms) |
|
uint8_t send_count; // число переотправок |
|
uint8_t* dgram; // копия финального пакета [SVC_ROUTE_HDR][payload][sig?] |
|
size_t dgram_len; |
|
}; |
|
|
|
// Bindings для сервисов внутри etcp_router (аналогично ETCP_BINDINGS) |
|
struct ETCP_ROUTER_BINDINGS { |
|
etcp_recv_fn callbacks[SVC_ROUTE_MAX_BINDINGS]; |
|
}; |
|
|
|
// ==================================================================== |
|
// 3. Публичное API (для сервисов) |
|
// ==================================================================== |
|
|
|
// Инициализация: etcp_bind(ETCP_ID_SVC_ROUTE) + очистка bindings + создание router_conns |
|
int etcp_router_init(struct UTUN_INSTANCE* inst); |
|
|
|
// Деинициализация |
|
void etcp_router_destroy(struct UTUN_INSTANCE* inst); |
|
|
|
// Зарегистрировать обработчик сервиса |
|
int etcp_router_bind(struct UTUN_INSTANCE* inst, uint8_t svc_id, etcp_recv_fn callback); |
|
int etcp_router_unbind(struct UTUN_INSTANCE* inst, uint8_t svc_id); |
|
|
|
// Отправить сервисный пакет (авто-conn, seq, inflight-контроль через send_q). |
|
// Принимает владение entry во всех исходах, включая ошибку. |
|
// mode — битовая маска ROUTE_CRYPTO_SIGN / ROUTE_CRYPTO_ENCRYPT (см. route_crypto.h), 0 = обычный пакет. |
|
int etcp_route_send(struct UTUN_INSTANCE* inst, uint64_t group_id, uint64_t dst_node_id, struct ll_entry* entry, int force, int mode); |
|
|
|
// Найти/создать состояние seq-подключения по (group_id, remote_node_id, svc_id) |
|
struct ETCP_ROUTER_CONN* etcp_router_conn_get(struct UTUN_INSTANCE* inst, |
|
uint64_t group_id, uint64_t remote_node_id, uint8_t svc_id); |
|
|
|
// Есть ли сейчас физический маршрут до узла (прямой, indirect-посредник или глобальный). |
|
// 1 = есть ETCP-соединение для отправки, 0 = нет (передача встанет в no_route). |
|
int etcp_router_has_route(struct UTUN_INSTANCE* inst, uint64_t group_id, uint64_t dst_node_id); |
|
|
|
// Отправить данные с авто-seq и контролем inflight |
|
// data: payload без svc_id, mode: ROUTE_CRYPTO_SIGN / ROUTE_CRYPTO_ENCRYPT (0 = обычный) |
|
int etcp_router_conn_send(struct ETCP_ROUTER_CONN* rconn, |
|
const uint8_t* data, size_t len, int mode); |
|
|
|
// Закрыть seq-подключение |
|
void etcp_router_conn_close(struct ETCP_ROUTER_CONN* rconn); |
|
|
|
// Установить рабочий max_inflight, пересчитывает inflight_limit и state machine |
|
void etcp_router_set_max_inflight(struct ETCP_ROUTER_CONN* rconn, uint16_t new_max); |
|
|
|
// Backpressure: зарегистрировать/отменить waiter на send_q очереди |
|
void etcp_router_on_send_ready(struct UTUN_INSTANCE* inst, uint64_t group_id, uint64_t node_id, uint8_t svc_id, |
|
struct queue_waiter_handle* h, |
|
queue_threshold_callback_fn callback, void* arg); |
|
void etcp_router_cancel_send_ready(struct UTUN_INSTANCE* inst, uint64_t group_id, uint64_t node_id, uint8_t svc_id, |
|
struct queue_waiter_handle* h); |
|
|
|
// Backpressure: проверить есть ли место в send_q (без регистрации waiter) |
|
int etcp_router_send_q_has_room(struct UTUN_INSTANCE* inst, uint64_t group_id, uint64_t node_id, uint8_t svc_id); |
|
|
|
// ==================================================================== |
|
// 4. Служебные функции (для каркаса: etcp.c, topo_group.c) |
|
// ==================================================================== |
|
|
|
// Отвязать отправку от сбрасываемого транспорта; сохранить inflight и ретрансмиты |
|
// Используется при etcp_conn_reinit чтобы избежать гонки с очисткой ETCP-очередей |
|
void etcp_router_pause_retrans_for_node(struct UTUN_INSTANCE* inst, uint64_t remote_node_id); |
|
|
|
// Закрыть все router_conn для указанного (group_id, remote_node_id) (peer умер) |
|
void etcp_router_close_group(struct UTUN_INSTANCE* inst, uint64_t group_id); |
|
void etcp_router_conn_close_all_for_node(struct UTUN_INSTANCE* inst, uint64_t group_id, uint64_t remote_node_id); |
|
|
|
// Начать новую локальную сессию для конкретного peer+svc в группе и уведомить сервис. |
|
void etcp_router_conn_restart(struct UTUN_INSTANCE* inst, uint64_t group_id, uint64_t remote_node_id, uint8_t svc_id); |
|
|
|
// Удалить все транзитные очереди соединения (вызывается при закрытии ETCP_CONN) |
|
void etcp_router_transit_queues_destroy(struct ETCP_CONN* conn); |
|
|
|
// Сбросить recv_conn у всех ROUTER_CONN, ссылающихся на уничтожаемый ETCP_CONN. |
|
void etcp_router_conn_destroyed(struct ETCP_CONN* conn); |
|
|
|
|
|
#ifdef __cplusplus |
|
} |
|
#endif |
|
#endif // ETCP_ROUTER_H
|
|
|