From 5fbd6fdc6980f0c2237814acbeecd24a4cd811c7 Mon Sep 17 00:00:00 2001 From: riccardom Date: Wed, 29 Jul 2026 10:18:42 +0200 Subject: [PATCH] pqkem: rotate PSK in kernel mode instead of skipping The idle-gate reads LastActivities, which only tracks per-peer data in userspace; in kernel mode it is empty, so the gate treated every kernel peer as idle and disabled data-path rotation entirely. Detect the bind via IsUserspaceBind and, in kernel mode, report zero activity age (always 'active') so rotation runs on every rekey. Lazy back-to-idle is already limited in kernel; the eBPF WG-activity detection will later supply a real signal that excludes handshake/pqkem traffic. --- client/internal/peer/conn.go | 9 +++++++++ client/internal/peer/iface.go | 4 ++++ 2 files changed, 13 insertions(+) diff --git a/client/internal/peer/conn.go b/client/internal/peer/conn.go index f2b5e75e5..412dc8227 100644 --- a/client/internal/peer/conn.go +++ b/client/internal/peer/conn.go @@ -997,7 +997,16 @@ func (conn *Conn) onWGCheckSuccess() { // (WireGuard keepalives excluded), per the same LastActivities signal the // lazy-connection inactivity monitor uses. It reports a very large duration when no // activity has ever been recorded, so the peer is treated as idle. +// +// In kernel mode there is no per-peer data-activity signal (LastActivities is +// userspace-only), so we cannot tell active from idle. We report zero — always +// "active" — so PSK rotation is not disabled in kernel mode. Lazy back-to-idle is +// already limited there; the eBPF WG-activity detection (future) will supply a real +// signal that excludes handshake/pqkem traffic. func (conn *Conn) dataActivityAge() time.Duration { + if !conn.config.WgConfig.WgInterface.IsUserspaceBind() { + return 0 + } last, ok := conn.config.WgConfig.WgInterface.LastActivities()[conn.config.WgConfig.RemoteKey] if !ok { return time.Duration(math.MaxInt64) diff --git a/client/internal/peer/iface.go b/client/internal/peer/iface.go index 47780a4d0..a12a0e181 100644 --- a/client/internal/peer/iface.go +++ b/client/internal/peer/iface.go @@ -23,4 +23,8 @@ type WGIface interface { // LastActivities returns the last real-data activity time per peer (WireGuard // keepalives excluded), used to gate post-quantum PSK rotation on active tunnels. LastActivities() map[string]monotime.Time + // IsUserspaceBind reports whether WireGuard runs in userspace. Only there does + // LastActivities track per-peer data activity; in kernel mode it is unavailable, + // so PSK rotation cannot be gated on activity. + IsUserspaceBind() bool }