add L2 reliability: seq + cumulative ACK + retransmit (protocol v2)

DATA frames now carry seq:4 and ack_seq:4 in a 16-byte extended
header. Both sides maintain per-session send/recv state:

Sender:
- Monotonic seq counter, retransmit buffer (seq -> frame bytes)
- Retransmit timer: 5ms timeout, 10 max retries -> CLOSE
- Window advances on cumulative ACK

Receiver:
- In-order delivery to TCP socket (expected_seq)
- Out-of-order buffering (SortedList by seq)
- Duplicate detection (seq < expected -> discard + re-ACK)
- Pure ACK frames (empty-payload DATA) for duplicate/OOO responses

This prevents lost Ethernet frames from permanently corrupting TCP
sessions, which was the key v1 limitation. The local kernel TCP stack
ACKs data before we chunk it into DATA frames; without L2 reliability
a dropped frame creates an unrecoverable gap.

Version bumped to 2. Both sides must speak v2; no negotiation.

Updated: PROTOCOL.md (full v2 spec), README.md, Rust frame.rs/
session.rs/main.rs, C# Frame.cs/SessionManager.cs/TunnelLink.cs.
This commit is contained in:
2026-08-13 09:08:05 +00:00
parent 2f61f2bb1b
commit eb8994d1e1
8 changed files with 721 additions and 102 deletions
+8 -6
View File
@@ -39,13 +39,16 @@ See [`PROTOCOL.md`](PROTOCOL.md) for the full wire format. Summary:
- 8-byte header, big-endian:
`[ version:1 ][ type:1 ][ session_id:4 ][ payload_len:2 ][ payload:N ]`.
- `version` = `1`. `payload_len` lets the receiver ignore Ethernet padding
- `version` = `2`. `payload_len` lets the receiver ignore Ethernet padding
(frames under 60 bytes are zero-padded by the NIC).
- DATA frames carry an extended 16-byte header with `seq` and `ack_seq` fields
for L2-level reliability (retransmit + in-order delivery), preventing lost
Ethernet frames from corrupting TCP sessions.
- Discovery: client broadcasts `DISCOVER`; server unicasts `MANIFEST` (with
hostname and upstream list) back.
- Sessions: `OPEN``OPEN_ACK` (or `OPEN_NAK`) → `DATA`* ↔ `DATA`* → `CLOSE`.
- Network test: `PING` (with 8-byte nonce) → `PONG` (nonce echoed).
- v1 ships TCP only. UDP frame types are reserved but unimplemented.
- TCP only. UDP frame types are reserved but unimplemented.
## `gatunad` usage
@@ -142,15 +145,14 @@ silent.
**Client:** status line in the UI. Errors are not logged to disk.
## v1 limitations
## Limitations
- TCP only. UDP wire types reserved, code paths stubbed.
- No retransmit at the L2 layer. A dropped `DATA` frame breaks the TCP session
irrecoverably because the localhost socket already ACKed the bytes. Acceptable
on a healthy switched link.
- No auth/crypto. Anyone on the same L2 segment can `DISCOVER` and `OPEN`.
- Single server instance per interface.
- One outstanding `OPEN` at a time on the client (serialized via queue).
- L2 retransmit caps at 10 retries × 5 ms = 50 ms. A permanently dead link
closes the session with `reason = max_retries`.
## Repository layout