From 4daf7da383dc51802fe13ab1e3f20f76c25b7033 Mon Sep 17 00:00:00 2001 From: riccardom Date: Fri, 7 Aug 2026 09:42:27 +0200 Subject: [PATCH] [client] peer: re-arm the WireGuard watcher after a lazy wake MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The Conn struct is reused across lazy-connection deactivate/activate. Close cancels the WireGuard watcher (via wgWatcherCancel, and ctxCancel also tears down its context) but left conn.wgWatcher pointing at the stopped instance. enableWgWatcherIfNeeded skips while conn.wgWatcher is non-nil, so the next Open never started a fresh watcher: once a lazy connection had idled and woken, the peer ran with no watcher at all — no WireGuard handshake-timeout detection and none of the escalation that depends on it. Clear conn.wgWatcher and conn.wgWatcherCancel in Close so the next Open re-arms a fresh watcher. --- client/internal/peer/conn.go | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/client/internal/peer/conn.go b/client/internal/peer/conn.go index 09a4e8b02..62ef8d4f0 100644 --- a/client/internal/peer/conn.go +++ b/client/internal/peer/conn.go @@ -307,6 +307,14 @@ func (conn *Conn) Close(signalToRemote bool) { if conn.wgWatcherCancel != nil { conn.wgWatcherCancel() + // The Conn struct is reused across lazy deactivate/activate. ctxCancel above + // already stopped the watcher goroutine (its ctx derives from conn.ctx), but + // enableWgWatcherIfNeeded skips while conn.wgWatcher is non-nil — so a stale + // pointer here would leave the peer with no watcher after the next Open, and thus + // no WireGuard handshake-timeout detection once a lazy connection has idled and + // woken. Clear it so the next Open starts a fresh watcher. + conn.wgWatcher = nil + conn.wgWatcherCancel = nil } conn.workerRelay.CloseConn() if conn.workerICE != nil {