From 35a60acb84f48aa8f7b6e99f4e9a88eef552f78c Mon Sep 17 00:00:00 2001 From: Alex Valiushko Date: Mon, 22 Jun 2026 09:46:46 -0700 Subject: [PATCH] device: set peer to expire unconditionally (#73) e3ac4a0afb4e introduced a lightweight API that can be used instead of UAPI to reconfigure peers. Peer state created via the new PeerLookupFunc is not set to expire until the handshake succeeds, making device leak two goroutines and a set of buffers for each failed handshake. This change arms the expiry timer before the handshake gets to proceed. Updates tailscale/tailscale#20183 Change-Id: Ibc0abb6eec97aca0a10f50515dea9e0d6a6a6964 Signed-off-by: Alex Valiushko --- device/peer.go | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/device/peer.go b/device/peer.go index c0ca59a..14ecdc2 100644 --- a/device/peer.go +++ b/device/peer.go @@ -254,6 +254,14 @@ func (peer *Peer) Start() { go peer.RoutineSequentialReceiver(batchSize) peer.isRunning.Store(true) + + // A lazily-created peer that never completes a handshake otherwise never + // arms its reaping timer. Arm it here, while running under state.Lock, so + // it's reclaimed after RejectAfterTime*3 of no session and is guaranteed to + // be torn down by a matching Stop. A completed handshake re-Mods it. + if peer.deleteOnIdle { + peer.timers.zeroKeyMaterial.Mod(RejectAfterTime * 3) + } } func (peer *Peer) ZeroAndFlushAll() {