Skip to content

tcp: retransmit queue + window-gated TX for TcpFlow - #55

Draft
sarsanaee wants to merge 1 commit into
tcp-7-02-2026from
tcp-retransmit-wnd
Draft

tcp: retransmit queue + window-gated TX for TcpFlow#55
sarsanaee wants to merge 1 commit into
tcp-7-02-2026from
tcp-retransmit-wnd

Conversation

@sarsanaee

Copy link
Copy Markdown
Collaborator

Builds on #54. Fixes the three ship-blocking TX-path defects from the review of the native TCP stack.

What was broken

  • No data retransmission. OutputMessage segmented and freed the source buffers immediately; PeriodicCheck did nothing for established data. A single lost segment was permanent data loss, and the flow was torn down silently (the app was never notified).
  • No TX flow control. Transmission ignored the peer's snd_wnd_ and bytes-in-flight, so any message larger than the peer window (≤64 KB) deterministically lost data — with zero network loss.
  • Alloc failure corrupted the stream. A mid-message PacketAlloc failure left a partial frame on the wire (never completed), leaked the MsgBuf chain, and desynced all subsequent messages.

What this does

Introduces a per-flow byte-stream send buffer (snd_buf_, spanning [snd_una_, snd_una_+size)). OutputMessage now frames the message into that buffer and calls PumpSend, which transmits only up to min(buffered end, snd_una_ + snd_wnd_) in EffectiveMSS-sized segments. Buffered bytes are retained until acknowledged.

  • Retransmission: PeriodicCheck does go-back-N from snd_una_ on RTO in ESTABLISHED/CLOSE_WAIT/FIN_WAIT_1/LAST_ACK; teardown on max-retransmit now notifies the app from any state. AdvanceSndUna drops acked bytes and reopens the window.
  • Window gating: PumpSend honors the peer window and re-runs on every received ACK (data drains as the window opens). A minimal zero-window persist probe prevents a new deadlock.
  • Alloc-failure safety: SendDataSegment returns false instead of crashing or emitting a short frame; unsent bytes stay buffered. Every OutputMessage exit path frees the chain; a msg_length that disagrees with the chain is dropped, not framed.

FIN is deferred behind buffered data (fin_pending_/fin_sent_/snd_fin_seq_) so its sequence number is correct when the app closes with data still queued; HandleFinWait1's our_fin_acked check is updated to match. As a side effect the RTO stays armed after FIN, so a lost FIN now retransmits. EffectiveMSS caps segments at kDefaultMSS, incidentally defusing the over-large-peer-MSS allocation abort on the data path.

Tests

Adds 7 white-box tests to tcp_flow_test.cc: window-gated buffering, window-open drain, RTO data retransmit, ACK frees buffer + disarms RTO, established RTO exhaustion notifies app, FIN deferred behind window-limited data, msg_length-mismatch drop.

⚠️ Not yet built

This is a Linux + DPDK target; it was authored on macOS and has not been compiled or run. It needs a build + ctest pass (sudo/hugepages) on a DPDK host before merge.

Out of scope (follow-ups from the same review)

SYN-retransmit sequence bug, TCP checksum offload not enabled on the port, RX-side buffer-exhaustion desync, per-flow close (TCP_DESTROY_FLOW no-op), passive-flow port release.

🤖 Generated with Claude Code

Addresses the three ship-blocking TX-path defects from the PR #54 review:
lost data was unrecoverable, transmission ignored the peer window, and an
allocation failure mid-message corrupted the wire stream and leaked buffers.

Core change: OutputMessage no longer segments-and-forgets. It frames the
message (4-byte length prefix + payload) into a per-flow byte-stream send
buffer (snd_buf_, spanning [snd_una_, snd_una_+size)) and calls PumpSend,
which transmits only up to min(buffered end, snd_una_+snd_wnd_) in
EffectiveMSS-sized segments. Buffered bytes are retained until acknowledged.

- Data retransmission (#1): PeriodicCheck now retransmits buffered data
  (go-back-N from snd_una_) on RTO in ESTABLISHED/CLOSE_WAIT/FIN_WAIT_1/
  LAST_ACK, and notifies the app (callback false) on teardown from ANY
  state, not just SYN_SENT. AdvanceSndUna drops acked bytes from snd_buf_
  and reopens the window.
- Window-gated TX (#5): PumpSend honors snd_wnd_ and bytes-in-flight; it is
  re-run on every received ACK (via InputPacket) so data drains as the
  window opens. A minimal zero-window persist probe prevents the deadlock
  that strict window-gating would otherwise introduce.
- No-corruption alloc failure (#6): SendDataSegment returns false instead of
  CHECK-crashing or emitting a short frame; unsent bytes stay buffered for
  the next pump. Every OutputMessage exit path frees the MsgBuf chain, and a
  msg_length that disagrees with the chain is dropped rather than framed.

FIN is deferred behind buffered data (fin_pending_/fin_sent_/snd_fin_seq_)
so its sequence number is correct even when the app closes with data still
queued; HandleFinWait1's our_fin_acked check is updated accordingly. As a
side effect the RTO now stays armed after FIN, so a lost FIN retransmits.
EffectiveMSS caps segments at kDefaultMSS, defusing the over-large-peer-MSS
allocation abort on the data path.

Adds 7 white-box tests: window gating buffers excess, window-open drains,
RTO retransmits unacked data, ACK frees the buffer and disarms the RTO,
established RTO exhaustion notifies the app, FIN deferred behind
window-limited data, and msg_length-mismatch drop.

Not yet compiled/run: this is a Linux+DPDK target and was authored on macOS.
Needs a build + `ctest` pass (sudo/hugepages) on a DPDK host before merge.
Out of scope here and left for follow-up: SYN-retransmit sequence bug,
checksum offload, RX-side desync, per-flow close (DESTROY_FLOW).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019xDGAYTziq2pwPgsEaqzEM
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant