From 5bd653e9d2fce4220009baa85edb103f722061ca Mon Sep 17 00:00:00 2001 From: ospab Date: Sun, 17 May 2026 02:56:16 +0300 Subject: [PATCH] fix: immediately ACK duplicate packets instead of silently dropping them to unblock client retries when ACKs are lost --- Cargo.lock | 10 +++++----- ostp-core/src/protocol.rs | 19 ++++++++++++++++++- 2 files changed, 23 insertions(+), 6 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 07a345e..df1b0a3 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -745,7 +745,7 @@ checksum = "c08d65885ee38876c4f86fa503fb49d7b507c2b62552df7c70b2fce627e06381" [[package]] name = "ostp" -version = "0.1.57" +version = "0.1.58" dependencies = [ "anyhow", "base64", @@ -762,7 +762,7 @@ dependencies = [ [[package]] name = "ostp-client" -version = "0.1.57" +version = "0.1.58" dependencies = [ "anyhow", "bytes", @@ -780,7 +780,7 @@ dependencies = [ [[package]] name = "ostp-core" -version = "0.1.57" +version = "0.1.58" dependencies = [ "anyhow", "async-trait", @@ -813,7 +813,7 @@ dependencies = [ [[package]] name = "ostp-server" -version = "0.1.57" +version = "0.1.58" dependencies = [ "anyhow", "bytes", @@ -828,7 +828,7 @@ dependencies = [ [[package]] name = "ostp-tun-helper" -version = "0.1.57" +version = "0.1.58" dependencies = [ "anyhow", "chrono", diff --git a/ostp-core/src/protocol.rs b/ostp-core/src/protocol.rs index e2e982d..f3b6d48 100644 --- a/ostp-core/src/protocol.rs +++ b/ostp-core/src/protocol.rs @@ -229,7 +229,11 @@ impl ProtocolMachine { let nonce = u64::from_be_bytes(raw_vec[4..12].try_into().unwrap()); if nonce < self.expected_recv_nonce { - // Duplicate or delayed packet already processed, drop silently + // Duplicate packet! The ACK we sent was likely lost or delayed. + // We MUST trigger an immediate ACK to unblock the sender's congestion window. + if let Some(ack_frame) = self.force_build_ack()? { + return Ok(ProtocolAction::SendDatagram(ack_frame)); + } return Ok(ProtocolAction::Noop); } @@ -469,6 +473,19 @@ impl ProtocolMachine { Ok(Some(frame)) } + fn force_build_ack(&mut self) -> Result, ProtocolError> { + let payload = self.build_ack_payload(); + if payload.is_empty() { + self.ack_pending = false; + return Ok(None); + } + + let frame = self.build_control_datagram(0, FrameKind::Ack, payload)?; + self.ack_pending = false; + self.last_ack_sent = Instant::now(); + Ok(Some(frame)) + } + fn build_ack_payload(&self) -> Bytes { const MAX_RANGES: usize = 8; let mut ranges = Vec::new();