Skip to content

tcp: fix SYN-retransmit sequence bug + enable/seed TCP checksum offload - #56

Draft
sarsanaee wants to merge 1 commit into
tcp-retransmit-wndfrom
tcp-handshake-csum
Draft

tcp: fix SYN-retransmit sequence bug + enable/seed TCP checksum offload#56
sarsanaee wants to merge 1 commit into
tcp-retransmit-wndfrom
tcp-handshake-csum

Conversation

@sarsanaee

Copy link
Copy Markdown
Collaborator

Stacked on #55. Fixes the two defects that stop the native TCP handshake from working on real hardware (review items #3 and #2).

#3 — SYN retransmission corrupts the sequence number

SendSyn sent the SYN from snd_nxt_ and then did snd_nxt_++ on every call. PeriodicCheck retransmits via SendSyn, so after a single lost/slow SYN the retransmit carried seq = isn+1 and bumped snd_nxt_ to isn+2. The peer's SYN-ACK (acking isn+1) then failed the seg_ack == snd_nxt_ check in HandleSynSent, and the connection could never establish once one SYN was lost.

Fix: send the SYN from the fixed snd_isn_ and set snd_nxt_ = snd_isn_ + 1 absolutely — mirroring the already-idempotent SendSynAck. Retransmits are now sequence-idempotent.

#2 — TCP checksum offload was never enabled, and the pseudo-header wasn't seeded

The port was configured with only IPV4 | UDP checksum offload, yet the TCP TX path set RTE_MBUF_F_TX_TCP_CKSUM and wrote checksum = 0 with no pseudo-header sum. On any PMD that honors the port config (ixgbe/i40e/virtio), the NIC never computes the TCP checksum, so every segment ships invalid and the Linux peer drops it. It only "worked" on NICs (mlx5) that recompute L4 checksums in hardware regardless.

Fix, two parts:

  • pmd.cc: enable RTE_ETH_TX_OFFLOAD_TCP_CKSUM on the port when the NIC supports it (loud warning otherwise).
  • tcp_flow.h: seed tcph->checksum with rte_ipv4_phdr_cksum() in every TCP sender (control, MSS-option, data, FIN), satisfying the DPDK offload contract (SW pre-loads the pseudo-header sum; HW completes it over the TCP header + payload).

checksum stays a raw uint16_t holding a network-order partial sum — wrapping it in be16_t (as an earlier review comment suggested) would byte-swap it and re-break it.

Test

Adds SynRetransmitIsSequenceIdempotent: forces a SYN retransmit, asserts snd_nxt_ is unchanged, and that a SYN-ACK acking isn+1 then establishes the connection. This fails on the old code.

⚠️ Not yet built

Linux + DPDK target, authored on macOS — not compiled or run. Needs a build + ctest pass, and the checksum path should be validated on real hardware (tcpdump on the peer, or the tcp_msg_gen interop test). A software checksum fallback for NICs lacking TCP offload is a follow-up.

Note on stacking

Based on tcp-retransmit-wnd (#55) because the checksum seeding must also cover the new data-path senders (SendDataSegment/SendFinSegment) introduced there. Merge #55 first, or rebase onto tcp-7-02-2026 if these land independently.

🤖 Generated with Claude Code

These are the two defects that stop the handshake from working on real
hardware (review items #3 and #2 from the PR #54 review).

#3 SYN retransmission (tcp_flow.h): SendSyn sent the SYN from snd_nxt_ and
then did snd_nxt_++ on EVERY call. PeriodicCheck retransmits via SendSyn, so
after a single lost/slow SYN the retransmit carried seq=isn+1 and bumped
snd_nxt_ to isn+2; the peer's SYN-ACK (acking isn+1) then failed the
seg_ack == snd_nxt_ check in HandleSynSent and the connection could never
establish. Fix: send the SYN from the fixed snd_isn_ and set
snd_nxt_ = snd_isn_ + 1 absolutely, mirroring the already-idempotent
SendSynAck. Retransmits are now sequence-idempotent.

#2 TCP checksum offload (pmd.cc + tcp_flow.h): the port was configured with
only IPV4+UDP checksum offload, yet the TCP TX path set RTE_MBUF_F_TX_TCP_CKSUM
and wrote checksum=0 without the pseudo-header sum. On any PMD that honors the
port config (ixgbe/i40e/virtio), the NIC never computes the TCP checksum, so
every segment ships with an invalid checksum and the Linux peer drops it —
the stack only "worked" on NICs (mlx5) that recompute L4 checksums in HW.
Fix: (a) enable RTE_ETH_TX_OFFLOAD_TCP_CKSUM on the port when supported (warn
otherwise), and (b) seed tcph->checksum with rte_ipv4_phdr_cksum() in every
TCP sender (control, MSS-option, data, FIN) so the offload contract is met.
checksum stays a raw uint16_t (network-order partial sum) — wrapping it in
be16_t would byte-swap it.

Adds SynRetransmitIsSequenceIdempotent: forces a SYN retransmit, asserts
snd_nxt_ is unchanged, and that a SYN-ACK acking isn+1 then establishes the
connection (fails on the old code).

Stacked on tcp-retransmit-wnd (#55). Not yet compiled — Linux+DPDK target,
authored on macOS; needs a build + ctest pass, and the checksum path should
be validated on real hardware (e.g. tcpdump on the peer / the tcp_msg_gen
interop test). A software checksum fallback for NICs without TCP offload is
left as a follow-up.

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