From d0029178e7ed574eba55140f83073a18c01b99f4 Mon Sep 17 00:00:00 2001 From: Evgeny Date: Wed, 3 Jun 2026 21:20:05 +0300 Subject: [PATCH] =?UTF-8?q?feat:=201-byte=20flags=20in=20SVC=5FROUTE=5FHDR?= =?UTF-8?q?=20=E2=80=94=20START,=20RST,=202-bit=20sess=5Fid?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit SVC_ROUTE_HDR: +uint8_t flags (23 bytes total). bit7=START (0x80) — first data packet of new session bit6=RST (0x40) — sequence violation detected bit5-4=sess_id (0-3) — cyclically incremented on restart router_send_one_flags: sets START+sess_id on first data packet. router_send_ack: sets sess_id only (no START for ACK). etcp_router_recv_cb: detects restart via sess_id change or START flag. Old seq=0 heuristic replaced with explicit flags. etcp_router_conn_get: init sess_id=0, peer_sess_id=0, start_sent=0. etcp_router_conn_restart: increment sess_id before closing rconn. --- src/etcp_router.c | 43 ++++++++++++++++++++++++++++++++++++------- src/etcp_router.h | 15 +++++++++++++-- 2 files changed, 49 insertions(+), 9 deletions(-) diff --git a/src/etcp_router.c b/src/etcp_router.c index 4aef3f2e..3bf0fb65 100644 --- a/src/etcp_router.c +++ b/src/etcp_router.c @@ -64,6 +64,14 @@ static int router_send_one_flags(struct ETCP_ROUTER_CONN* rconn, const uint8_t* hdr->src_node_id = inst->node_id; hdr->seq = seq; hdr->svc_id = rconn->svc_id; + { + uint8_t f = (rconn->sess_id << ROUTER_SESS_ID_SHIFT); + if (!rconn->start_sent && pl_len > 0 && !(seq_flags & (ROUTER_SEQ_CLOSE_FLAG | ROUTER_SEQ_RST_FLAG))) { + f |= ROUTER_FLAG_START; + rconn->start_sent = 1; + } + hdr->flags = f; + } if (pl_len > 0) memcpy(dgram + SVC_ROUTE_HDR_SIZE, payload, pl_len); struct ll_entry* entry = queue_entry_new(0); @@ -222,6 +230,7 @@ static void router_send_ack(struct ETCP_ROUTER_CONN* rconn) { hdr->src_node_id = inst->node_id; hdr->seq = rconn->rx_seq; hdr->svc_id = rconn->svc_id; + hdr->flags = (rconn->sess_id << ROUTER_SESS_ID_SHIFT); struct ll_entry* entry = queue_entry_new(0); if (!entry) { u_free(hdr); return; } @@ -368,13 +377,27 @@ static void etcp_router_recv_cb(struct ETCP_CONN* conn, struct ll_entry* entry) if (!rconn) { queue_dgram_free(entry); queue_entry_free(entry); return; } } - // Обнаружение перезапуска peer'a: seq=0 при rx_seq >= MAX_INFLIGHT - if (rconn->rx_seq >= (uint32_t)ROUTER_MAX_INFLIGHT && hdr->seq == 0 && pl_len > 0) { - DEBUG_INFO(DEBUG_CATEGORY_ETCPROUTE, "router: peer restart detected svc_id=%u from %016llx — resetting", - hdr->svc_id, (unsigned long long)hdr->src_node_id); - etcp_router_conn_restart(inst, hdr->src_node_id, hdr->svc_id); - rconn = etcp_router_conn_get(inst, hdr->src_node_id, hdr->svc_id); - if (!rconn) { queue_dgram_free(entry); queue_entry_free(entry); return; } + // Обнаружение перезапуска peer'а: новый sess_id или START-флаг + { + uint8_t incoming_sess = (hdr->flags >> ROUTER_SESS_ID_SHIFT) & 0x3; + if (pl_len > 0 && (incoming_sess != rconn->peer_sess_id || (hdr->flags & ROUTER_FLAG_START))) { + DEBUG_INFO(DEBUG_CATEGORY_ETCPROUTE, + "router: peer restart sess_id=%u→%u svc_id=%u from %016llx", + rconn->peer_sess_id, incoming_sess, hdr->svc_id, + (unsigned long long)hdr->src_node_id); + etcp_router_conn_restart(inst, hdr->src_node_id, hdr->svc_id); + rconn = etcp_router_conn_get(inst, hdr->src_node_id, hdr->svc_id); + if (!rconn) { queue_dgram_free(entry); queue_entry_free(entry); return; } + rconn->peer_sess_id = incoming_sess; + } + // RST-флаг — peer сигналит нарушение последовательности + if (hdr->flags & ROUTER_FLAG_RST) { + DEBUG_INFO(DEBUG_CATEGORY_ETCPROUTE, + "router: RST from %016llx svc_id=%u", + (unsigned long long)hdr->src_node_id, hdr->svc_id); + router_close_and_notify(rconn); + rconn = NULL; + } } // Seq-состояние есть — reorder @@ -589,6 +612,9 @@ void etcp_router_conn_restart(struct UTUN_INSTANCE* inst, uint64_t remote_node_i DEBUG_INFO(DEBUG_CATEGORY_ETCPROUTE, "router_restart: svc_id=%u remote=%016llx — notifying service", svc_id, (unsigned long long)remote_node_id); + rconn->sess_id = (rconn->sess_id + 1) & 0x3; + rconn->start_sent = 0; + etcp_recv_fn cb = inst->router_bindings.callbacks[svc_id]; if (cb) { struct ll_entry* e = queue_entry_new(0); @@ -633,6 +659,9 @@ struct ETCP_ROUTER_CONN* etcp_router_conn_get(struct UTUN_INSTANCE* inst, rconn->idle_ack_timer = NULL; rconn->send_blocked = 0; rconn->send_resume_timer = NULL; + rconn->sess_id = 0; + rconn->peer_sess_id = 0; + rconn->start_sent = 0; rconn->recv_q = queue_new(inst->ua, ROUTER_RECVQ_HASH_SIZE, 0, 4, "router_recv_q"); if (!rconn->recv_q) { DEBUG_ERROR(DEBUG_CATEGORY_ETCPROUTE, "router_conn_get: queue_new(recv_q) failed"); queue_entry_free(&rconn->ll); return NULL; } rconn->send_q = queue_new(inst->ua, 0, 0, 0, "router_send_q"); diff --git a/src/etcp_router.h b/src/etcp_router.h index a88cd39c..5399ed1e 100644 --- a/src/etcp_router.h +++ b/src/etcp_router.h @@ -3,7 +3,8 @@ // Упрощённый TCP поверх ETCP: восстановление порядка, дедупликация, без переповторов // // Формат SVC_ROUTE пакета: -// [cmd:1] [dst_node_id:8] [src_node_id:8] [seq:4] [svc_id:1] [payload...] +// [cmd:1] [dst_node_id:8] [src_node_id:8] [seq:4] [svc_id:1] [flags:1] [payload...] +// flags: bit7=START, bit6=RST, bit5-4=sess_id(0-3) // ACK-пакет: тот же заголовок, seq=rx_seq, payload_len=0 #ifndef ETCP_ROUTER_H #define ETCP_ROUTER_H @@ -18,11 +19,18 @@ struct SVC_ROUTE_HDR { 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, bit5-4=sess_id }; #pragma pack(pop) -#define SVC_ROUTE_HDR_SIZE sizeof(struct SVC_ROUTE_HDR) // 22 +#define SVC_ROUTE_HDR_SIZE sizeof(struct SVC_ROUTE_HDR) // 23 #define SVC_ROUTE_MAX_BINDINGS 256 +// Биты в flags +#define ROUTER_FLAG_START 0x80 +#define ROUTER_FLAG_RST 0x40 +#define ROUTER_SESS_ID_SHIFT 4 +#define ROUTER_SESS_ID_MASK 0x30 + // Состояние одного логического подключения (remote_node_id + svc_id) struct ETCP_ROUTER_CONN { struct ll_entry ll; // data[0..7]=remote_node_id, data[8]=svc_id — хеш-индекс @@ -45,6 +53,9 @@ struct ETCP_ROUTER_CONN { struct ll_queue* send_q; // очередь ожидающих отправки (inflight полон) void* send_resume_timer; // таймер возобновления отправки uint8_t send_blocked; // 1 = inflight полон, ждём ack/таймер + uint8_t sess_id; // наш session id (0-3) + uint8_t peer_sess_id; // последний sess_id от peer'а + uint8_t start_sent; // 0 = нужно отправить START в первом data }; #define ROUTER_CONN_HASH_SIZE 256