From b1dac25f34a24cad2a1695df7227fec4031a3221 Mon Sep 17 00:00:00 2001 From: Calin Teodor Date: Sun, 7 Jun 2026 10:25:54 +0300 Subject: [PATCH] feat(tunnel): per-peer rekey-request whitelist (PILOT-345) Adds atomic.Pointer-backed per-peer whitelist that bypasses BOTH the rekeyRequestInterval (3 s) gate and the maxRekeyRequesters (4096) map cap. Whitelisted peers can trigger rekey requests freely and never get counted against the bound. API: TunnelManager.SetRekeyWhitelist([]uint32). Empty whitelist by default preserves existing rate-limit behaviour. --- pkg/daemon/tunnel.go | 34 ++++++++++++++++++++++++++++++++-- 1 file changed, 32 insertions(+), 2 deletions(-) diff --git a/pkg/daemon/tunnel.go b/pkg/daemon/tunnel.go index a6828171..346b0978 100644 --- a/pkg/daemon/tunnel.go +++ b/pkg/daemon/tunnel.go @@ -138,6 +138,11 @@ type TunnelManager struct { // key" events. Prevents amplification if a peer floods us with gibberish. rekeyMu sync.Mutex lastRekeyReq map[uint32]time.Time + // PILOT-345: per-peer whitelist that bypasses BOTH the + // rekeyRequestInterval gate AND the maxRekeyRequesters map cap. + // Whitelisted peers can always trigger a rekey request without + // being throttled or counted against the 4096 bound. + rekeyWhitelist atomic.Pointer[map[uint32]struct{}] // routing is the L4 manager. It owns: // - relayPeers, relayPinned, blackholeMissCount, directClearCount, @@ -404,14 +409,23 @@ func (tm *TunnelManager) maybeRequestRekey(peerNodeID uint32, from *net.UDPAddr) if tm.sock == nil { return false } + // PILOT-345: whitelisted peers skip both the interval gate and the + // map cap. They still record a timestamp so the prune sweep can + // reclaim entries if the whitelist shrinks later. + whitelisted := false + if wl := tm.rekeyWhitelist.Load(); wl != nil { + if _, ok := (*wl)[peerNodeID]; ok { + whitelisted = true + } + } tm.rekeyMu.Lock() last, known := tm.lastRekeyReq[peerNodeID] now := time.Now() - if known && now.Sub(last) < rekeyRequestInterval { + if !whitelisted && known && now.Sub(last) < rekeyRequestInterval { tm.rekeyMu.Unlock() return false } - if !known && !tm.pruneRekeyBudgetLocked(now) { + if !whitelisted && !known && !tm.pruneRekeyBudgetLocked(now) { tm.rekeyMu.Unlock() return false } @@ -529,6 +543,22 @@ func (tm *TunnelManager) SetPeerVerifyFunc(fn func(uint32) (ed25519.PublicKey, e tm.kx.SetPeerVerifyFunc(keyexchange.VerifyFunc(fn)) } +// SetRekeyWhitelist replaces the per-peer whitelist for the +// maybeRequestRekey gate (PILOT-345). Whitelisted peers bypass BOTH +// the rekeyRequestInterval (3 s) and the maxRekeyRequesters (4096) +// map cap. Empty slice clears the whitelist. Safe to call +// concurrently with maybeRequestRekey. +func (tm *TunnelManager) SetRekeyWhitelist(nodeIDs []uint32) { + wm := make(map[uint32]struct{}, len(nodeIDs)) + for _, id := range nodeIDs { + if id == 0 { + continue + } + wm[id] = struct{}{} + } + tm.rekeyWhitelist.Store(&wm) +} + // SetBeaconAddr configures the beacon address for NAT hole-punching and relay. // Thin shim over routing.Manager.SetBeaconAddr. func (tm *TunnelManager) SetBeaconAddr(addr string) error {