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>
pull/1506/head
Brad Fitzpatrick 4 years ago
parent d580b3f09e
commit 44ab0acbdb

@ -50,7 +50,7 @@ tailscale.com/cmd/tailscale dependencies: (generated by github.com/tailscale/dep
tailscale.com/types/wgkey from tailscale.com/types/netmap+
tailscale.com/util/dnsname from tailscale.com/cmd/tailscale/cli+
W tailscale.com/util/endian from tailscale.com/net/netns
tailscale.com/util/lineread from tailscale.com/net/interfaces
LW tailscale.com/util/lineread from tailscale.com/net/interfaces
tailscale.com/version from tailscale.com/cmd/tailscale/cli+
tailscale.com/version/distro from tailscale.com/cmd/tailscale/cli
tailscale.com/wgengine/filter from tailscale.com/types/netmap

@ -126,7 +126,7 @@ tailscale.com/cmd/tailscaled dependencies: (generated by github.com/tailscale/de
tailscale.com/types/wgkey from tailscale.com/control/controlclient+
tailscale.com/util/dnsname from tailscale.com/wgengine/tsdns+
LW tailscale.com/util/endian from tailscale.com/net/netns+
tailscale.com/util/lineread from tailscale.com/control/controlclient+
LW tailscale.com/util/lineread from tailscale.com/control/controlclient+
tailscale.com/util/pidowner from tailscale.com/ipn/ipnserver
tailscale.com/util/racebuild from tailscale.com/logpolicy
tailscale.com/util/systemd from tailscale.com/control/controlclient+

@ -43,6 +43,7 @@ const trustServiceStillAvailableDuration = 10 * time.Minute
// Client is a port mapping client.
type Client struct {
logf logger.Logf
ipAndGateway func() (gw, ip netaddr.IP, ok bool)
mu sync.Mutex // guards following, and all fields thereof
@ -101,9 +102,17 @@ func (m *pmpMapping) release() {
func NewClient(logf logger.Logf) *Client {
return &Client{
logf: logf,
ipAndGateway: interfaces.LikelyHomeRouterIP,
}
}
// SetGatewayLookupFunc set the func that returns the machine's default gateway IP, and
// the primary IP address for that gateway. It must be called before the client is used.
// If not called, interfaces.LikelyHomeRouterIP is used.
func (c *Client) SetGatewayLookupFunc(f func() (gw, myIP netaddr.IP, ok bool)) {
c.ipAndGateway = f
}
// NoteNetworkDown should be called when the network has transitioned to a down state.
// It's too late to release port mappings at this point (the user might've just turned off
// their wifi), but we can make sure we invalidate mappings for later when the network
@ -140,7 +149,7 @@ func (c *Client) SetLocalPort(localPort uint16) {
}
func (c *Client) gatewayAndSelfIP() (gw, myIP netaddr.IP, ok bool) {
gw, myIP, ok = interfaces.LikelyHomeRouterIP()
gw, myIP, ok = c.ipAndGateway()
if !ok {
gw = netaddr.IP{}
myIP = netaddr.IP{}

@ -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

@ -13,6 +13,7 @@ import (
"sync"
"time"
"inet.af/netaddr"
"tailscale.com/net/interfaces"
"tailscale.com/types/logger"
)
@ -54,6 +55,9 @@ type Mon struct {
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 {

@ -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)

Loading…
Cancel
Save