From deac82231cfcf76c9d6f320cdd69a0f868a4008f Mon Sep 17 00:00:00 2001 From: Brad Fitzpatrick Date: Wed, 19 Oct 2022 21:30:50 -0700 Subject: [PATCH] wgengine/magicsock: add start of alternate send path During development of silent disco (#540), an alternate send policy for magicsock that doesn't wake up the radio frequently with heartbeats, we want the old & new policies to coexist, like we did previously pre- and post-disco. We started to do that earlier in 5c42990c2fc01 but only set up the env+control knob plumbing to set a bool about which path should be used. This starts to add a way for the silent disco code to update the send path from a separate goroutine. (Part of the effort is going to de-state-machinify the event based soup that is the current disco code and make it more Go synchronous style.) So far this does nothing. (It does add an atomic load on each send but that should be noise in the grand scheme of things, and a even more rare atomic store of nil on node config changes.) Baby steps. Updates #540 Co-authored-by: Jenny Zhang Signed-off-by: Brad Fitzpatrick --- wgengine/magicsock/magicsock.go | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/wgengine/magicsock/magicsock.go b/wgengine/magicsock/magicsock.go index ff2d93f31..13cf3c8fb 100644 --- a/wgengine/magicsock/magicsock.go +++ b/wgengine/magicsock/magicsock.go @@ -3304,6 +3304,19 @@ func ippDebugString(ua netip.AddrPort) string { return ua.String() } +// endpointSendFunc is a func that writes an encrypted Wireguard payload from +// WireGuard to a peer. It might write via UDP, DERP, both, or neither. +// +// What these funcs should NOT do is too much work. Minimize use of mutexes, map +// lookups, etc. The idea is that selecting the path to use is done infrequently +// and mostly async from sending packets. When conditions change (including the +// passing of time and loss of confidence in certain routes), then a new send +// func gets set on an sendpoint. +// +// A nil value means the current fast path has expired and needs to be +// recalculated. +type endpointSendFunc func([]byte) error + // discoEndpoint is a wireguard/conn.Endpoint that picks the best // available path to communicate with a peer, based on network // conditions and what the peer supports. @@ -3311,6 +3324,7 @@ type endpoint struct { // atomically accessed; declared first for alignment reasons lastRecv mono.Time numStopAndResetAtomic int64 + sendFunc syncs.AtomicValue[endpointSendFunc] // nil or unset means unused // These fields are initialized once and never modified. c *Conn @@ -3630,6 +3644,10 @@ func (de *endpoint) cliPing(res *ipnstate.PingResult, cb func(*ipnstate.PingResu } func (de *endpoint) send(b []byte) error { + if fn := de.sendFunc.Load(); fn != nil { + return fn(b) + } + now := mono.Now() de.mu.Lock() @@ -3836,6 +3854,9 @@ func (de *endpoint) updateFromNode(n *tailcfg.Node, heartbeatDisabled bool) { de.deleteEndpointLocked(ep) } } + + // Node changed. Invalidate its sending fast path, if any. + de.sendFunc.Store(nil) } // addCandidateEndpoint adds ep as an endpoint to which we should send