[PR #6828] [client] Drop stale connection-cycle callbacks after peer conn reopen #29084

Open
opened 2026-08-05 08:07:25 -04:00 by saavagebueno · 0 comments
Owner

📋 Pull Request Information

Original PR: https://github.com/netbirdio/netbird/pull/6828
Author: @MichaelUray
Created: 7/19/2026
Status: 🔄 Open

Base: mainHead: fix/peer-stale-cycle-callbacks


📝 Commits (1)

  • 62b062f [client] Drop stale connection-cycle callbacks after peer conn reopen

📊 Changes

4 files changed (+193 additions, -12 deletions)

View changed files

📝 client/internal/peer/conn.go (+22 -8)
📝 client/internal/peer/conn_test.go (+167 -0)
📝 client/internal/peer/worker_ice.go (+2 -2)
📝 client/internal/peer/worker_relay.go (+2 -2)

📄 Description

Describe your changes

Conn.Close() cancels conn.ctx; Conn.Open() replaces it with a fresh
context. Several worker callbacks spawned in the previous connection cycle
can fire after such a Close+Open pair, and their only staleness guard is
conn.ctx.Err() - which belongs to the NEW cycle and therefore passes:

  • onICEConnectionIsReady, called from the untracked go w.connect(...)
    goroutine: a dial that succeeded just before Close can configure the
    WireGuard endpoint of the fresh cycle to a dead remote connection and
    mark statusICE connected.
  • onICEStateDisconnected, called from the old agent's pion
    connection-state callback: resets the fresh cycle's priority to None,
    removes the live WG endpoint and triggers a spurious guard reconnect.
  • onRelayConnectionIsReady, dispatched via go from the relay worker.
  • onRelayDisconnected, reachable both through the wgproxy disconnect
    listener and through relay-manager close listeners, which are never
    deregistered on Conn.Close and can fire arbitrarily late.

Fix: thread the connection-cycle context (the workers' creation context;
WorkerICE.ctx / WorkerRelay.peerCtx are the conn.ctx of the cycle
that created them) into these four callbacks and drop the callback when
that context is cancelled. This is the same pattern onWGDisconnected
already uses with watcherCtx. Close() cancels the cycle context while
holding conn.mu, so the check is race-free with respect to teardown and
reopen.

No behavior change within a live cycle: there the cycle context and
conn.ctx are the same object, so the combined check is equivalent to the
existing one.

Tests: deterministic callback-level tests (no sleeps, -race clean)
pinning for each callback that a cancelled-cycle invocation leaves the
live state untouched (status, priority, endpoint calls) while a live-cycle
invocation still performs the normal transition; the stale relay-ready
test also pins that the relayed connection is closed.


View with Codesmith Autofix with Codesmith
Need help on this PR? Tag /codesmith with what you need. Autofix is disabled.

Summary by CodeRabbit

  • Bug Fixes
    • Prevented delayed connection and relay events from previous connection cycles from changing current connection state.
    • Improved handling of stale callbacks after a connection is closed and reopened.
    • Ensured obsolete relay connections are closed safely when their callbacks arrive late.
  • Tests
    • Added coverage verifying that stale events are ignored while current-cycle events continue to work correctly.

🔄 This issue represents a GitHub Pull Request. It cannot be merged through Gitea due to API limitations.

## 📋 Pull Request Information **Original PR:** https://github.com/netbirdio/netbird/pull/6828 **Author:** [@MichaelUray](https://github.com/MichaelUray) **Created:** 7/19/2026 **Status:** 🔄 Open **Base:** `main` ← **Head:** `fix/peer-stale-cycle-callbacks` --- ### 📝 Commits (1) - [`62b062f`](https://github.com/netbirdio/netbird/commit/62b062fa3448292355849d1fce5ed46f5bbc889c) [client] Drop stale connection-cycle callbacks after peer conn reopen ### 📊 Changes **4 files changed** (+193 additions, -12 deletions) <details> <summary>View changed files</summary> 📝 `client/internal/peer/conn.go` (+22 -8) 📝 `client/internal/peer/conn_test.go` (+167 -0) 📝 `client/internal/peer/worker_ice.go` (+2 -2) 📝 `client/internal/peer/worker_relay.go` (+2 -2) </details> ### 📄 Description ## Describe your changes `Conn.Close()` cancels `conn.ctx`; `Conn.Open()` replaces it with a fresh context. Several worker callbacks spawned in the previous connection cycle can fire after such a Close+Open pair, and their only staleness guard is `conn.ctx.Err()` - which belongs to the NEW cycle and therefore passes: - `onICEConnectionIsReady`, called from the untracked `go w.connect(...)` goroutine: a dial that succeeded just before Close can configure the WireGuard endpoint of the fresh cycle to a dead remote connection and mark `statusICE` connected. - `onICEStateDisconnected`, called from the old agent's pion connection-state callback: resets the fresh cycle's priority to None, removes the live WG endpoint and triggers a spurious guard reconnect. - `onRelayConnectionIsReady`, dispatched via `go` from the relay worker. - `onRelayDisconnected`, reachable both through the wgproxy disconnect listener and through relay-manager close listeners, which are never deregistered on `Conn.Close` and can fire arbitrarily late. Fix: thread the connection-cycle context (the workers' creation context; `WorkerICE.ctx` / `WorkerRelay.peerCtx` are the `conn.ctx` of the cycle that created them) into these four callbacks and drop the callback when that context is cancelled. This is the same pattern `onWGDisconnected` already uses with `watcherCtx`. `Close()` cancels the cycle context while holding `conn.mu`, so the check is race-free with respect to teardown and reopen. No behavior change within a live cycle: there the cycle context and `conn.ctx` are the same object, so the combined check is equivalent to the existing one. Tests: deterministic callback-level tests (no sleeps, `-race` clean) pinning for each callback that a cancelled-cycle invocation leaves the live state untouched (status, priority, endpoint calls) while a live-cycle invocation still performs the normal transition; the stale relay-ready test also pins that the relayed connection is closed. ## Issue ticket number and link - <!-- codesmith:footer --> --- <a href="https://app.blacksmith.sh/netbirdio/codesmith/netbird/pr/6828"><picture><source media="(prefers-color-scheme: dark)" srcset="https://pr-comments-assets.blacksmith.sh/codesmith/view-with-codesmith-dark-v2.svg"><source media="(prefers-color-scheme: light)" srcset="https://pr-comments-assets.blacksmith.sh/codesmith/view-with-codesmith-light-v2.svg"><img alt="View with Codesmith" src="https://pr-comments-assets.blacksmith.sh/codesmith/view-with-codesmith-dark-v2.svg"></picture></a> <a href="https://backend.blacksmith.sh/track/enable-autofix?expires=1787060582&installation_id=146802194&pr_number=6828&repository=netbirdio%2Fnetbird&return_to=https%3A%2F%2Fgithub.com%2Fnetbirdio%2Fnetbird%2Fpull%2F6828&signature=b78d85f4540f608efd5f6c77cef1f59c91c24530363acd80ea29feb7b5019edf"><picture><source media="(prefers-color-scheme: dark)" srcset="https://pr-comments-assets.blacksmith.sh/codesmith/autofix-with-codesmith-dark.svg"><source media="(prefers-color-scheme: light)" srcset="https://pr-comments-assets.blacksmith.sh/codesmith/autofix-with-codesmith-light.svg"><img alt="Autofix with Codesmith" src="https://pr-comments-assets.blacksmith.sh/codesmith/autofix-with-codesmith-dark.svg"></picture></a> <sup>Need help on this PR? Tag <code>/codesmith</code> with what you need. Autofix is disabled.</sup> <!-- codesmith:autofix:disabled --> <!-- /codesmith:footer --> <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit * **Bug Fixes** * Prevented delayed connection and relay events from previous connection cycles from changing current connection state. * Improved handling of stale callbacks after a connection is closed and reopened. * Ensured obsolete relay connections are closed safely when their callbacks arrive late. * **Tests** * Added coverage verifying that stale events are ignored while current-cycle events continue to work correctly. <!-- end of auto-generated comment: release notes by coderabbit.ai --> --- <sub>🔄 This issue represents a GitHub Pull Request. It cannot be merged through Gitea due to API limitations.</sub>
saavagebueno added the pull-request label 2026-08-05 08:07:25 -04:00
Sign in to join this conversation.
No Label pull-request
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: DYNR/netbird#29084