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:
+11
-4
@@ -22,7 +22,7 @@ use crate::frame::{
|
||||
Frame, REASON_CONNECT_FAILED, REASON_UNKNOWN_SESSION, REASON_UNKNOWN_UPSTREAM,
|
||||
};
|
||||
use crate::link::Link;
|
||||
use crate::session::{spawn_pump, write_to_session, SessionHandle, SessionStore};
|
||||
use crate::session::{spawn_pump, handle_data, send_pure_ack, SessionHandle, SessionStore};
|
||||
use crate::upstream::build_table;
|
||||
|
||||
#[derive(Parser)]
|
||||
@@ -166,6 +166,9 @@ async fn handle_frame(
|
||||
SessionHandle {
|
||||
upstream_id,
|
||||
write: w,
|
||||
send_state: Arc::new(Mutex::new(session::SendState::new())),
|
||||
recv_state: Arc::new(Mutex::new(session::RecvState::new())),
|
||||
peer_mac: src,
|
||||
},
|
||||
);
|
||||
let ack = Frame::OpenAck { session_id: sid, upstream_id };
|
||||
@@ -187,9 +190,13 @@ async fn handle_frame(
|
||||
}
|
||||
}
|
||||
}
|
||||
Frame::Data { session_id, payload } => {
|
||||
match write_to_session(store, session_id, &payload).await {
|
||||
Ok(()) => {}
|
||||
Frame::Data { session_id, seq, ack_seq, payload } => {
|
||||
match handle_data(store, session_id, seq, ack_seq, &payload, tx).await {
|
||||
Ok(need_ack) => {
|
||||
if need_ack {
|
||||
send_pure_ack(store, session_id, tx);
|
||||
}
|
||||
}
|
||||
Err(session::WriteError::UnknownSession) => {
|
||||
let close = Frame::Close {
|
||||
session_id,
|
||||
|
||||
Reference in New Issue
Block a user