Files
utun2/src/routing_layer/etcp_router.h
T

305 lines
23 KiB
C
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.
// etcp_router.h — Сервисный слой маршрутизации поверх ETCP
// Маршрутизирует сервисные пакеты до целевой ноды, на промежуточных нодах ретранслирует
// Упрощённый TCP поверх ETCP: восстановление порядка, дедупликация, ретрансмиты, inflight-контроль.
// Подпись/шифрование вынесены в автономный модуль route_crypto (encode/decode).
// Логический канал задаётся (group_id, node_id, svc_id), физический транспорт берётся у топологии.
// Сервисы регистрируют приём через bind, отправляют через route_send и соблюдают backpressure.
// Все операции и callbacks — в потоке uasync. Закрытие группы удаляет её каналы и ожидающий транзит.
//
// Построение маршрутов описано в topo_group.h. Роутер берёт текущий живой next hop
// из таблицы группы; при ожидании свободной очереди проверяет выбор повторно.
// Во время сходимости BGP возможны временные петли. Источник записывает свой ID,
// каждый транзитный узел проверяет список пройденных узлов и дописывает свой ID.
// Повторное посещение запрещено для всех пакетов, включая ACK и handshake.
// Ожидание в очереди не добавляет ID повторно; ретрансмит источника начинает новый список.
//
// Формат 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...] [signature?:64] [visited_ids:8*count] [count:1]
// count=1..ROUTER_MAX_VISITED; IDs идут от источника до отправителя текущего хопа.
// Изменяемый список находится вне end-to-end подписи/шифрования. Получатель
// проверяет его границы, уникальность, первый ID=src и последний ID=ETCP peer.
// Это защита от ошибок маршрутизации, а не доказательство пути от недоверенного пира.
// 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
#define ROUTER_MAX_VISITED 64 // предел размера списка, не замена проверки повторных ID
#define ROUTER_PATH_MAX_SIZE (8 * ROUTER_MAX_VISITED + 1)
// Единый формат доставки сервисной кодограммы (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 ещё не переданных транспорту пакетов
struct queue_waiter_handle waiter; // ожидание свободной conn->send_input_q
struct ETCP_CONN* conn; // владелец очереди и waiter; текущий маршрут проверяется при выгрузке
void* route_timer; // повторный выбор пути, даже если прежняя очередь остаётся занятой
};
// Состояние одного логического подключения (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; // время отправки запроса, 0.1 ms
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_CALL_NO_ACK_MAX_RETRANS 67 // 20с: запас для 10с перебоя звонка и восстановления
#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 хранит подписанное/зашифрованное тело. Каждый ретрансмит добавляет новый список переходов.
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?], без visited_ids/count
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 во всех исходах, включая ошибку.
// entry->dgram = svc_id[1] || payload; 0=принято в очередь, <0=ошибка, не подтверждение доставки.
// force разрешает превысить лимит числа пакетов в send_q.
// 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);
// Найти/создать логический канал. Указатель заимствован до close/destroy, NULL при ошибке.
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 = найден транспорт (для self всегда 1); это не гарантия UP/готовности логического канала.
int etcp_router_has_route(struct UTUN_INSTANCE* inst, uint64_t group_id, uint64_t dst_node_id);
// Снимок текущего пути отправки сервиса, только uasync-поток; без создания канала/изменения очередей.
// type — CONN_TYPE_*; отсутствие живого пути: NONE и next_hop_node_id=0.
struct ETCP_ROUTER_PATH { uint8_t type; uint64_t next_hop_node_id; };
struct ETCP_ROUTER_PATH etcp_router_get_path(struct UTUN_INSTANCE* inst, uint64_t group_id,
uint64_t dst_node_id, uint8_t svc_id);
// Отправить данные с авто-seq и контролем inflight
// data: payload без svc_id, mode: ROUTE_CRYPTO_SIGN / ROUTE_CRYPTO_ENCRYPT (0 = обычный)
// Копирует data; 0=принято в очередь, <0=ошибка. Доставка подтверждается отдельно протоколом.
int etcp_router_conn_send(struct ETCP_ROUTER_CONN* rconn,
const uint8_t* data, size_t len, int mode);
// Закрыть канал и уведомить сервис. После вызова rconn использовать нельзя.
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);
// Дождаться порога send_q. h должен быть обнулён и жить до callback/отмены.
// Callback может выполниться внутри вызова. Повторная регистрация отменяет прежнюю.
// Если канала нет, ожидание не регистрируется. Перед освобождением arg отменить waiter.
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);
// Закрыть все каналы и ожидающий транзит группы. Сначала сервисы отменяют свои producers/waiters.
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