Skip to content

fix(network-stack): drop guest-closed sessions once our FIN is acked - #157

Open
TadahiroYamamura wants to merge 2 commits into
earendil-works:mainfrom
TadahiroYamamura:fix/tcp-delete-session-after-last-ack
Open

TadahiroYamamura wants to merge 2 commits into
earendil-works:mainfrom
TadahiroYamamura:fix/tcp-delete-session-after-last-ack

Conversation

@TadahiroYamamura

@TadahiroYamamura TadahiroYamamura commented Oct 6, 2026 •

Copy link
Copy Markdown

A2 of #155.

Problem
When the guest sends its FIN first, the connection is not removed from natTable even after Gondolin's FIN is ACKed. When the guest reconnects on the same 4-tuple, the new SYN hits the leftover session and is ignored, and connect() hangs for 132–134s and fails (reproduced with curl --local-port).

Fix
Record "received the guest's FIN" and "sent our FIN" on the session, separately from state, which gets overwritten. Once both are set and our FIN is ACKed (vmAck === mySeq), remove the session.

Tests

  • A connection the guest closed first is removed once our FIN is ACKed (two cases: with and without the host socket's close)
  • It stays while our FIN is not yet ACKed
  • Control: a connection Gondolin closed first stays until the guest's FIN arrives (the existing path)

Please check

  • We kept the state strings and added two flags. Replacing state with flags would be cleaner, but we avoided it because the diff would get larger. B2 in the issue can probably be fixed with the same flags
  • onTcpClose is not called when the session is removed. At that point the host socket is closed in both directions, and net.ts cleans up its own bookkeeping on the socket's close. This matches the existing "remove when the guest's FIN arrives in FIN_WAIT" path
  • This conflicts with fix(net): send our FIN when the guest closes before any payload #159 on the lines where the guest's FIN is accepted (the same few lines in network-stack.ts). If both are merged, the correct order is "advance myAck → guestFinReceived = true → onTcpClose"

Suggested CHANGELOG entry
Fix TCP sessions the guest closed first never being removed, which made a later connection on the same 4-tuple hang until its SYN retries ran out.

🤖 AI (Claude Code) was used for the investigation and for writing the code.

Contribution Agreement

Please ensure this PR follows the guidelines in CONTRIBUTING.md.

By submitting this pull request, I confirm the following:

I understand that the entity Earendil Inc. (incorporated in the state of Delaware in 2025) needs some rights from me in order to utilize my contributions in this PR. As a contributor I retain all rights, title and interest in and to my contributions, and by keeping this boilerplate intact I confirm that Earendil Inc. can use, modify, copy, and redistribute my contributions, under Earendil Inc.'s choice of terms.

When the guest sent its FIN first, the session stayed in natTable even
after the guest acked our FIN, and a later SYN on the same 4-tuple was
ignored. Track receiving the guest's FIN and sending our own separately
from `state`, which gets overwritten, and drop the session once both
are set and our FIN is acked.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…FIN is acked

When the guest's FIN arrived in FIN_WAIT, the session was dropped even
if our FIN was still unacked (CLOSING in RFC 9293), so a retransmitted
FIN or the final ACK hit no session. Drop it right away only when our
FIN is already acked; otherwise leave it to the ACK handling added in
the previous commit. Combined with earendil-works#159, every guest-initiated close of
a connection without an upstream takes this path.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

This branch has not been deployed

No deployments
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