fix: immediately ACK duplicate packets instead of silently dropping them to unblock client retries when ACKs are lost

This commit is contained in:
ospab 2026-05-17 02:56:16 +03:00
parent b670ba9e48
commit 5bd653e9d2
2 changed files with 23 additions and 6 deletions

10
Cargo.lock generated
View File

@ -745,7 +745,7 @@ checksum = "c08d65885ee38876c4f86fa503fb49d7b507c2b62552df7c70b2fce627e06381"
[[package]] [[package]]
name = "ostp" name = "ostp"
version = "0.1.57" version = "0.1.58"
dependencies = [ dependencies = [
"anyhow", "anyhow",
"base64", "base64",
@ -762,7 +762,7 @@ dependencies = [
[[package]] [[package]]
name = "ostp-client" name = "ostp-client"
version = "0.1.57" version = "0.1.58"
dependencies = [ dependencies = [
"anyhow", "anyhow",
"bytes", "bytes",
@ -780,7 +780,7 @@ dependencies = [
[[package]] [[package]]
name = "ostp-core" name = "ostp-core"
version = "0.1.57" version = "0.1.58"
dependencies = [ dependencies = [
"anyhow", "anyhow",
"async-trait", "async-trait",
@ -813,7 +813,7 @@ dependencies = [
[[package]] [[package]]
name = "ostp-server" name = "ostp-server"
version = "0.1.57" version = "0.1.58"
dependencies = [ dependencies = [
"anyhow", "anyhow",
"bytes", "bytes",
@ -828,7 +828,7 @@ dependencies = [
[[package]] [[package]]
name = "ostp-tun-helper" name = "ostp-tun-helper"
version = "0.1.57" version = "0.1.58"
dependencies = [ dependencies = [
"anyhow", "anyhow",
"chrono", "chrono",

View File

@ -229,7 +229,11 @@ impl ProtocolMachine {
let nonce = u64::from_be_bytes(raw_vec[4..12].try_into().unwrap()); let nonce = u64::from_be_bytes(raw_vec[4..12].try_into().unwrap());
if nonce < self.expected_recv_nonce { 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); return Ok(ProtocolAction::Noop);
} }
@ -469,6 +473,19 @@ impl ProtocolMachine {
Ok(Some(frame)) Ok(Some(frame))
} }
fn force_build_ack(&mut self) -> Result<Option<Bytes>, 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 { fn build_ack_payload(&self) -> Bytes {
const MAX_RANGES: usize = 8; const MAX_RANGES: usize = 8;
let mut ranges = Vec::new(); let mut ranges = Vec::new();