Server (gatunad):
- New --verbose/-v flag: logs lifecycle events at info level
(DISCOVER, MANIFEST sent, OPEN, session established, CLOSE,
OPEN_NAK). Without the flag, errors only as before.
Client (gatuna):
- OPEN_NAK and session CLOSE now send TCP RST to the local app
instead of a graceful FIN. LingerOption(true, 0) causes Winsock
to emit RST on close. The local app (e.g. ssh) sees a broken
connection instead of a clean close, which is more honest about
what happened (upstream was unreachable).
OPEN and OPEN_ACK now carry proto:1 alongside upstream_id:1. The
session is tagged with its transport proto and reliability semantics
switch on the specific proto:
- TCP (proto=1): full L2 reliability (seq, ack, retransmit, in-order)
- UDP (proto=2, reserved): best-effort (no retransmit, no ordering,
no pure ACKs — seq/ack_seq fields present but ignored)
This is architecturally correct: instead of a generic 'reliable:bool'
flag, each proto gets the semantics it needs. Today only TCP exists so
every session is reliable, but the extension point is clean for when
stateless protos are added.
Updated: PROTOCOL.md, Rust frame.rs/session.rs/main.rs,
C# Frame.cs/SessionManager.cs.
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.
New frame types PING (0x0B) and PONG (0x0C), each carrying an 8-byte
nonce. Server echoes PING nonce verbatim in PONG. Client sends pings
at random 10-100ms intervals, correlates nonces to measure RTT.
Stats shown live: sent/recv counts, loss %, average latency, jitter
(mean absolute delta of consecutive RTTs). Test button toggles on/off.
Updated both Rust server (echo in main.rs) and C# client (PingTest.cs,
SessionManager routing, MainForm Test button + stats label).
Server reads its hostname via gethostname(2) and includes it as a
length-prefixed UTF-8 string at the start of the MANIFEST payload.
Client displays 'Server: <hostname> — <MAC>' in a label above the
upstream list. Both sides updated for the new MANIFEST format:
[hostname_len:1][hostname:N][entries...]
Protocol version unchanged (still 1); MANIFEST payload layout change
is backward-incompatible but both sides ship together.