Skip to content

tcp: forward SYN in TIME_WAIT to the protocol handler - #4

Draft
mafredri wants to merge 2 commits into
new-coder-mainfrom
mafredri/forward-timewait-syn
Draft

mafredri wants to merge 2 commits into
new-coder-mainfrom
mafredri/forward-timewait-syn

Conversation

@mafredri

@mafredri mafredri commented Oct 7, 2026 •

Copy link
Copy Markdown
Member

Cherry-picks the two commits of google#15019 onto the commit coder/coder pins, 7a658db7b714. google#15019 is an open PR from an outside contributor, and no gVisor maintainer has reviewed it.

With a tcp.Forwarder as the TCP protocol handler, which is how tailscale's netstack accepts connections on Coder workspace agents, a new SYN that reopens a 4-tuple in TIME_WAIT was only handed to a listening endpoint. A forwarder stack has none, so the SYN and every retransmit were dropped until TIME_WAIT expired, and the client's dial hung for up to about 63s (google#15013). The first commit hands that SYN to the protocol's default handler. The second sends a RST when the handler declines the segment.

The changed lines match google#15019 exactly. Its e2e test is left out because this branch is built from the test-free go branch, which lacks the packages it imports. Coverage lives in coder/coder: coder/coder#30471 adds a tailnet test that reopens a connection from the same source port after the agent closed it, and bumps coder/coder to this branch's head, ebe33a4c2cb5.

The first commit keeps the PR's Fixes #15013. That refers to google#15013; this repository has issues disabled.

Tracked in PLAT-717.

When squash-merging: the default message lists both commit messages. Replace each Upstream: https://github.com/google/gvisor/pull/15019 trailer with Cherry-picked from https://github.com/google/gvisor/pull/15019 (unmerged).

🤖 This PR was created with the help of Coder Agents, and will be reviewed by a human. 🏂🏻

When a new SYN reopens a connection while the old incarnation is in
TIME_WAIT (RFC 1122), handleTimeWaitSegments only looks for a bound
listening endpoint to hand the segment to. A stack that accepts
connections through a tcp.Forwarder has no such endpoint, so the SYN
is dropped for the rest of the TIME_WAIT period. Fall back to the
protocol's default handler, giving the forwarder the same chance a
listening endpoint gets.

Fixes google#15013

Signed-off-by: drakeo338 <paranoyouz@gmail.com>
Upstream: google#15019
…ment

The reuseTW closure in handleTimeWaitSegments discarded the handler's
return value. tcp.Forwarder.HandlePacket returns false for anything that
is not a bare SYN, but a SYN-ACK with a higher sequence number still
counts as a new SYN in TIME_WAIT, so it reached the handler, was
declined, and was dropped without a reply. Send a RST as
nic.DeliverTransportPacket does when the handler returns false.

Adds a forwarder e2e test that sends a SYN-ACK during TIME_WAIT and
expects a RST.

[coder/gvisor: the e2e test is omitted. This branch is built from the
test-free go branch and lacks the packages the test imports.]

Signed-off-by: drakeo338 <paranoyouz@gmail.com>
Upstream: google#15019
@linear-code

linear-code Bot commented Oct 7, 2026

Copy link
Copy Markdown

PLAT-717

mafredri added a commit to coder/coder that referenced this pull request Oct 8, 2026
The agent netstack accepts connections through tcp.Forwarder and has
no listening endpoint, so gVisor drops a SYN that reuses a 4-tuple the
agent holds in TIME_WAIT, and the client's dial hangs for about 63s.

The new pin is the head of coder/gvisor#4, which cherry-picks
google/gvisor#15019. That PR hands the SYN to the forwarder. It is
open, from an outside contributor, and no gVisor maintainer has
reviewed it.
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.

3 participants