Skip to content

fix(net/portmapper): release mappings and UPnP connections on Close - #135

Open
EhabY wants to merge 3 commits into
mainfrom
fix/upnp-shutdown
Open

EhabY wants to merge 3 commits into
mainfrom
fix/upnp-shutdown

Conversation

@EhabY

@EhabY EhabY commented Oct 13, 2026 •

Copy link
Copy Markdown

Fix flaky tests caused by UPnP state outliving portmapper.Client.Close(), and stop leaking router mappings.

  1. Port upstream 91d10d38ae: query and validate the external IP before AddPortMapping, so a failed lookup cannot leave an untracked mapping on the router. Only the reordering applies; the fork lacks selectBestService and the permanent-lease retry that the rest of that commit touches.
  2. Clean up on Close(): stop starting mapping attempts after Close(), release mappings that complete after it instead of storing them, and close idle UPnP HTTP connections on Close() (and again when a mapping attempt in flight during Close() finishes) so pooled connections and their goroutines do not outlive the client. Connection reuse within an attempt and the 2s idle timeout are unchanged. Not fixed upstream.
  3. Bound UPnP release: limit DeletePortMapping to one second. Release runs while holding Client.mu, so an unresponsive router could block Close, SetLocalPort, and gateway changes indefinitely. Tracked upstream in tailscaled blocks ~35s on shutdown in portmapper UPnP release (context.Background, no timeout) tailscale/tailscale#21625; open fixes there (net/portmapper: add UPnP-IGD release timeout tailscale/tailscale#17615, net/portmapper: put mappings and releases into an execqueue tailscale/tailscale#18460, fix: bound UPnP mapping release with a 5s timeout tailscale/tailscale#21687) are not merged.
Validation
  • Each commit passes go vet and go test -race ./net/portmapper/ on its own; builds pass for ./..., windows, darwin, linux/arm, js/wasm.
  • TestUPnPMappingLifecycle (commit 2) also covers commit 1's failed-IP-lookup cases, since upstream's tests rely on test infrastructure the fork does not have. It uses a transport without an idle timeout, so connections only close if Close() closes them.
  • Mutation checks: removing either CloseIdleConnections call, removing the late-mapping release, restoring the old IP-lookup order, and removing the release timeout each make the new tests fail.

Generated with Coder Agents on behalf of @EhabY.

@EhabY EhabY changed the title fix(net/portmapper): clean up mappings and UPnP connections fix: clean up portmapper shutdown and repair fork CI Oct 13, 2026
@EhabY
EhabY force-pushed the fix/upnp-shutdown branch from f42d674 to 509e691 Compare October 14, 2026 11:29
@EhabY EhabY changed the title fix: clean up portmapper shutdown and repair fork CI fix(net/portmapper): clean up mappings and UPnP connections Oct 14, 2026
@EhabY
EhabY changed the base branch from main to chore/repair-fork-ci October 14, 2026 11:29
@EhabY
EhabY added this pull request to stack #137 October 14, 2026 11:32
@EhabY
EhabY force-pushed the chore/repair-fork-ci branch from 2e8d147 to e5a42bc Compare October 14, 2026 11:49
@EhabY
EhabY force-pushed the fix/upnp-shutdown branch from 509e691 to 5cfc057 Compare October 14, 2026 11:49
@EhabY
EhabY force-pushed the chore/repair-fork-ci branch from e5a42bc to 4701f15 Compare October 14, 2026 11:58
@EhabY
EhabY force-pushed the fix/upnp-shutdown branch from 5cfc057 to 1fe5e63 Compare October 14, 2026 11:59
@EhabY
EhabY force-pushed the chore/repair-fork-ci branch from 4701f15 to ccf2c31 Compare October 14, 2026 12:19
@EhabY
EhabY force-pushed the fix/upnp-shutdown branch 2 times, most recently from 9c3bb27 to c82775e Compare October 14, 2026 13:39
@EhabY
EhabY removed this pull request from stack #137 October 14, 2026 13:47
@EhabY
EhabY changed the base branch from chore/repair-fork-ci to main October 14, 2026 13:47
@EhabY
EhabY force-pushed the fix/upnp-shutdown branch 2 times, most recently from ac58a7f to 0825918 Compare October 14, 2026 19:09
@EhabY EhabY changed the title fix(net/portmapper): clean up mappings and UPnP connections fix(net/portmapper): release mappings and UPnP connections on Close Oct 14, 2026
@EhabY
EhabY marked this pull request as ready for review October 14, 2026 19:12
@EhabY
EhabY requested a review from a team as a code owner October 14, 2026 19:12

@ethanndickson ethanndickson left a comment •

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Curious what led to this change being warranted? My agent couldnt find a linear issue but it looks like it's fixing real issues? some of these issues also appear unfixed on upstream tailscale

For the stuff that is fixed upstream my preference would probably be that we cherry pick fixes from upstream directly

@EhabY

EhabY commented Oct 15, 2026 •

Copy link
Copy Markdown
Author

I encountered this issue while running tests locally, they were flakey because connections stayed open beyond the test and triggered the "flake detection" occasionally.

Is this a real issue that somehow impacts user? Not so sure. But it seems part of this fix is already upstream and the rest have open issues 🙏

@EhabY
EhabY force-pushed the fix/upnp-shutdown branch from 0825918 to 737bde5 Compare October 15, 2026 11:39
Query and validate the external IP before AddPortMapping, so a failed
lookup cannot leave an untracked mapping on the router.

This ports the reordering from upstream commit 91d10d3 ("net/portmapper:
avoid leaking permanent UPnP mappings"). The fork has neither
selectBestService nor the permanent-lease retry, so the rest of that
commit and its tests do not apply; the next commit covers this behavior
in TestUPnPMappingLifecycle.

(cherry picked from commit 91d10d3)
@EhabY
EhabY force-pushed the fix/upnp-shutdown branch from 737bde5 to 64b9cea Compare October 15, 2026 11:49
@EhabY
EhabY requested a review from ethanndickson October 15, 2026 12:02
EhabY added 2 commits October 15, 2026 12:45
Stop starting mapping attempts after Close, and release mappings that
complete after Close instead of storing them.

Close idle UPnP HTTP connections on Close, and again when a mapping
attempt that was in flight during Close finishes, so no pooled
connections outlive the client.
Release runs while holding Client.mu, so an unresponsive router could
block Close, SetLocalPort, and gateway changes indefinitely. Limit
DeletePortMapping to one second, matching the UPnP discovery fetch.
@EhabY
EhabY force-pushed the fix/upnp-shutdown branch from 64b9cea to af40220 Compare October 15, 2026 12:45
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.

2 participants