net/portmapper, wgengine/monitor: cache gateway IP info until link changes

Cuts down allocs & CPU in steady state (on regular STUN probes) when network
is unchanging.

Signed-off-by: Brad Fitzpatrick <bradfitz@tailscale.com>
This commit is contained in:
Brad Fitzpatrick
2021-03-15 13:58:10 -07:00
parent d580b3f09e
commit 44ab0acbdb
6 changed files with 49 additions and 8 deletions

View File

@@ -55,6 +55,7 @@ import (
"tailscale.com/types/pad32"
"tailscale.com/types/wgkey"
"tailscale.com/version"
"tailscale.com/wgengine/monitor"
"tailscale.com/wgengine/wgcfg"
)
@@ -423,6 +424,10 @@ type Options struct {
// enabled, only active discovery-aware nodes will be able to
// communicate with Conn.
DisableLegacyNetworking bool
// LinkMonitor is the link monitor to use.
// With one, the portmapper won't be used.
LinkMonitor *monitor.Mon
}
func (o *Options) logf() logger.Logf {
@@ -483,6 +488,9 @@ func NewConn(opts Options) (*Conn, error) {
c.simulatedNetwork = opts.SimulatedNetwork
c.disableLegacy = opts.DisableLegacyNetworking
c.portMapper = portmapper.NewClient(logger.WithPrefix(c.logf, "portmapper: "))
if opts.LinkMonitor != nil {
c.portMapper.SetGatewayLookupFunc(opts.LinkMonitor.GatewayAndSelfIP)
}
if err := c.initialBind(); err != nil {
return nil, err

View File

@@ -13,6 +13,7 @@ import (
"sync"
"time"
"inet.af/netaddr"
"tailscale.com/net/interfaces"
"tailscale.com/types/logger"
)
@@ -51,9 +52,12 @@ type Mon struct {
change chan struct{}
stop chan struct{}
mu sync.Mutex // guards cbs
cbs map[*callbackHandle]ChangeFunc
ifState *interfaces.State
mu sync.Mutex // guards cbs
cbs map[*callbackHandle]ChangeFunc
ifState *interfaces.State
gwValid bool // whether gw and gwSelfIP are valid (cached)x
gw netaddr.IP
gwSelfIP netaddr.IP
onceStart sync.Once
started bool
@@ -105,6 +109,24 @@ func (m *Mon) interfaceStateUncached() (*interfaces.State, error) {
return s, err
}
// GatewayAndSelfIP returns the current network's default gateway, and
// the machine's default IP for that gateway.
//
// It's the same as interfaces.LikelyHomeRouterIP, but it caches the
// result until the monitor detects a network change.
func (m *Mon) GatewayAndSelfIP() (gw, myIP netaddr.IP, ok bool) {
m.mu.Lock()
defer m.mu.Unlock()
if m.gwValid {
return m.gw, m.gwSelfIP, true
}
gw, myIP, ok = interfaces.LikelyHomeRouterIP()
if ok {
m.gw, m.gwSelfIP, m.gwValid = gw, myIP, true
}
return gw, myIP, ok
}
// RegisterChangeCallback adds callback to the set of parties to be
// notified (in their own goroutine) when the network state changes.
// To remove this callback, call unregister (or close the monitor).
@@ -213,6 +235,7 @@ func (m *Mon) debounce() {
oldState := m.ifState
changed := !curState.Equal(oldState)
if changed {
m.gwValid = false
m.ifState = curState
if s1, s2 := oldState.String(), curState.String(); s1 == s2 {

View File

@@ -270,6 +270,7 @@ func newUserspaceEngine(logf logger.Logf, rawTUNDev tun.Device, conf Config) (_
DERPActiveFunc: e.RequestStatus,
IdleFunc: e.tundev.IdleDuration,
NoteRecvActivity: e.noteReceiveActivity,
LinkMonitor: e.linkMon,
}
var err error
e.magicConn, err = magicsock.NewConn(magicsockOpts)