wgengine{,/monitor}: move interface state fetching/comparing to monitor

Gets it out of wgengine so the Engine isn't responsible for being a
callback registration hub for it.

This also removes the Engine.LinkChange method, as it's no longer
necessary.  The monitor tells us about changes; it doesn't seem to
need any help. (Currently it was only used by Swift, but as of
14dc790137 we just do the same from Go)

Signed-off-by: Brad Fitzpatrick <bradfitz@tailscale.com>
naman/netstack-incoming
Brad Fitzpatrick 4 years ago
parent a038e8690c
commit e3df29d488

@ -60,12 +60,7 @@ func debugMode(args []string) error {
} }
func runMonitor(ctx context.Context) error { func runMonitor(ctx context.Context) error {
dump := func() { dump := func(st *interfaces.State) {
st, err := interfaces.GetState()
if err != nil {
log.Printf("error getting state: %v", err)
return
}
j, _ := json.MarshalIndent(st, "", " ") j, _ := json.MarshalIndent(st, "", " ")
os.Stderr.Write(j) os.Stderr.Write(j)
} }
@ -73,12 +68,16 @@ func runMonitor(ctx context.Context) error {
if err != nil { if err != nil {
return err return err
} }
mon.RegisterChangeCallback(func() { mon.RegisterChangeCallback(func(changed bool, st *interfaces.State) {
log.Printf("Link monitor fired. State:") if changed {
dump() log.Printf("Link monitor fired; no change")
return
}
log.Printf("Link monitor fired. New state:")
dump(st)
}) })
log.Printf("Starting link change monitor; initial state:") log.Printf("Starting link change monitor; initial state:")
dump() dump(mon.InterfaceState())
mon.Start() mon.Start()
log.Printf("Started link change monitor; waiting...") log.Printf("Started link change monitor; waiting...")
select {} select {}

@ -64,19 +64,20 @@ func getControlDebugFlags() []string {
// state machine generates events back out to zero or more components. // state machine generates events back out to zero or more components.
type LocalBackend struct { type LocalBackend struct {
// Elements that are thread-safe or constant after construction. // Elements that are thread-safe or constant after construction.
ctx context.Context // canceled by Close ctx context.Context // canceled by Close
ctxCancel context.CancelFunc // cancels ctx ctxCancel context.CancelFunc // cancels ctx
logf logger.Logf // general logging logf logger.Logf // general logging
keyLogf logger.Logf // for printing list of peers on change keyLogf logger.Logf // for printing list of peers on change
statsLogf logger.Logf // for printing peers stats on change statsLogf logger.Logf // for printing peers stats on change
e wgengine.Engine e wgengine.Engine
store ipn.StateStore store ipn.StateStore
backendLogID string backendLogID string
portpoll *portlist.Poller // may be nil unregisterLinkMon func()
portpollOnce sync.Once // guards starting readPoller portpoll *portlist.Poller // may be nil
gotPortPollRes chan struct{} // closed upon first readPoller result portpollOnce sync.Once // guards starting readPoller
serverURL string // tailcontrol URL gotPortPollRes chan struct{} // closed upon first readPoller result
newDecompressor func() (controlclient.Decompressor, error) serverURL string // tailcontrol URL
newDecompressor func() (controlclient.Decompressor, error)
filterHash string filterHash string
@ -138,7 +139,7 @@ func NewLocalBackend(logf logger.Logf, logid string, store ipn.StateStore, e wge
portpoll: portpoll, portpoll: portpoll,
gotPortPollRes: make(chan struct{}), gotPortPollRes: make(chan struct{}),
} }
e.SetLinkChangeCallback(b.linkChange) b.unregisterLinkMon = e.GetLinkMonitor().RegisterChangeCallback(b.linkChange)
b.statusChanged = sync.NewCond(&b.statusLock) b.statusChanged = sync.NewCond(&b.statusLock)
return b, nil return b, nil
@ -178,6 +179,7 @@ func (b *LocalBackend) Shutdown() {
cli := b.c cli := b.c
b.mu.Unlock() b.mu.Unlock()
b.unregisterLinkMon()
if cli != nil { if cli != nil {
cli.Shutdown() cli.Shutdown()
} }

@ -33,9 +33,11 @@ type osMon interface {
Receive() (message, error) Receive() (message, error)
} }
// ChangeFunc is a callback function that's called when // ChangeFunc is a callback function that's called when the network
// an interface status changes. // changed. The changed parameter is whether the network changed
type ChangeFunc func() // enough for interfaces.State to have changed since the last
// callback.
type ChangeFunc func(changed bool, state *interfaces.State)
// An allocated callbackHandle's address is the Mon.cbs map key. // An allocated callbackHandle's address is the Mon.cbs map key.
type callbackHandle byte type callbackHandle byte
@ -47,8 +49,9 @@ type Mon struct {
change chan struct{} change chan struct{}
stop chan struct{} stop chan struct{}
mu sync.Mutex // guards cbs mu sync.Mutex // guards cbs
cbs map[*callbackHandle]ChangeFunc cbs map[*callbackHandle]ChangeFunc
ifState *interfaces.State
onceStart sync.Once onceStart sync.Once
started bool started bool
@ -64,18 +67,30 @@ func New(logf logger.Logf) (*Mon, error) {
if err != nil { if err != nil {
return nil, err return nil, err
} }
return &Mon{ m := &Mon{
logf: logf, logf: logf,
cbs: map[*callbackHandle]ChangeFunc{}, cbs: map[*callbackHandle]ChangeFunc{},
om: om, om: om,
change: make(chan struct{}, 1), change: make(chan struct{}, 1),
stop: make(chan struct{}), stop: make(chan struct{}),
}, nil }
st, err := m.interfaceStateUncached()
if err != nil {
return nil, err
}
m.ifState = st
return m, nil
} }
// InterfaceState returns the state of the machine's network interfaces, // InterfaceState returns the state of the machine's network interfaces,
// without any Tailscale ones. // without any Tailscale ones.
func (m *Mon) InterfaceState() (*interfaces.State, error) { func (m *Mon) InterfaceState() *interfaces.State {
m.mu.Lock()
defer m.mu.Unlock()
return m.ifState
}
func (m *Mon) interfaceStateUncached() (*interfaces.State, error) {
s, err := interfaces.GetState() s, err := interfaces.GetState()
if s != nil { if s != nil {
s.RemoveTailscaleInterfaces() s.RemoveTailscaleInterfaces()
@ -167,11 +182,19 @@ func (m *Mon) debounce() {
case <-m.change: case <-m.change:
} }
m.mu.Lock() if curState, err := m.interfaceStateUncached(); err != nil {
for _, cb := range m.cbs { m.logf("interfaces.State: %v", err)
go cb() } else {
m.mu.Lock()
changed := !curState.Equal(m.ifState)
if changed {
m.ifState = curState
}
for _, cb := range m.cbs {
go cb(changed, m.ifState)
}
m.mu.Unlock()
} }
m.mu.Unlock()
select { select {
case <-m.stop: case <-m.stop:

@ -120,11 +120,9 @@ type userspaceEngine struct {
mu sync.Mutex // guards following; see lock order comment below mu sync.Mutex // guards following; see lock order comment below
closing bool // Close was called (even if we're still closing) closing bool // Close was called (even if we're still closing)
statusCallback StatusCallback statusCallback StatusCallback
linkChangeCallback func(major bool, newState *interfaces.State)
peerSequence []wgkey.Key peerSequence []wgkey.Key
endpoints []string endpoints []string
pingers map[wgkey.Key]*pinger // legacy pingers for pre-discovery peers pingers map[wgkey.Key]*pinger // legacy pingers for pre-discovery peers
linkState *interfaces.State
pendOpen map[flowtrack.Tuple]*pendingOpenFlow // see pendopen.go pendOpen map[flowtrack.Tuple]*pendingOpenFlow // see pendopen.go
networkMapCallbacks map[*someHandle]NetworkMapCallback networkMapCallbacks map[*someHandle]NetworkMapCallback
@ -248,12 +246,11 @@ func newUserspaceEngine(logf logger.Logf, rawTUNDev tun.Device, conf Config) (_
e.linkMonOwned = true e.linkMonOwned = true
} }
e.linkState, _ = e.linkMon.InterfaceState() logf("link state: %+v", e.linkMon.InterfaceState())
logf("link state: %+v", e.linkState)
unregisterMonWatch := e.linkMon.RegisterChangeCallback(func() { unregisterMonWatch := e.linkMon.RegisterChangeCallback(func(changed bool, st *interfaces.State) {
e.LinkChange(false)
tshttpproxy.InvalidateCache() tshttpproxy.InvalidateCache()
e.linkChange(false, st)
}) })
closePool.addFunc(unregisterMonWatch) closePool.addFunc(unregisterMonWatch)
e.linkMonUnregister = unregisterMonWatch e.linkMonUnregister = unregisterMonWatch
@ -279,7 +276,7 @@ func newUserspaceEngine(logf logger.Logf, rawTUNDev tun.Device, conf Config) (_
return nil, fmt.Errorf("wgengine: %v", err) return nil, fmt.Errorf("wgengine: %v", err)
} }
closePool.add(e.magicConn) closePool.add(e.magicConn)
e.magicConn.SetNetworkUp(e.linkState.AnyInterfaceUp()) e.magicConn.SetNetworkUp(e.linkMon.InterfaceState().AnyInterfaceUp())
// Respond to all pings only in fake mode. // Respond to all pings only in fake mode.
if conf.Fake { if conf.Fake {
@ -1250,30 +1247,15 @@ func (e *userspaceEngine) Wait() {
<-e.waitCh <-e.waitCh
} }
func (e *userspaceEngine) setLinkState(st *interfaces.State) (changed bool, cb func(major bool, newState *interfaces.State)) { func (e *userspaceEngine) GetLinkMonitor() *monitor.Mon {
if st == nil { return e.linkMon
return false, nil
}
e.mu.Lock()
defer e.mu.Unlock()
changed = e.linkState == nil || !st.Equal(e.linkState)
e.linkState = st
return changed, e.linkChangeCallback
} }
func (e *userspaceEngine) LinkChange(isExpensive bool) { func (e *userspaceEngine) linkChange(changed bool, cur *interfaces.State) {
cur, err := e.linkMon.InterfaceState()
if err != nil {
e.logf("LinkChange: interfaces.GetState: %v", err)
return
}
cur.IsExpensive = isExpensive
needRebind, linkChangeCallback := e.setLinkState(cur)
up := cur.AnyInterfaceUp() up := cur.AnyInterfaceUp()
if !up { if !up {
e.logf("LinkChange: all links down; pausing: %v", cur) e.logf("LinkChange: all links down; pausing: %v", cur)
} else if needRebind { } else if changed {
e.logf("LinkChange: major, rebinding. New state: %v", cur) e.logf("LinkChange: major, rebinding. New state: %v", cur)
} else { } else {
e.logf("[v1] LinkChange: minor") e.logf("[v1] LinkChange: minor")
@ -1282,23 +1264,11 @@ func (e *userspaceEngine) LinkChange(isExpensive bool) {
e.magicConn.SetNetworkUp(up) e.magicConn.SetNetworkUp(up)
why := "link-change-minor" why := "link-change-minor"
if needRebind { if changed {
why = "link-change-major" why = "link-change-major"
e.magicConn.Rebind() e.magicConn.Rebind()
} }
e.magicConn.ReSTUN(why) e.magicConn.ReSTUN(why)
if linkChangeCallback != nil {
go linkChangeCallback(needRebind, cur)
}
}
func (e *userspaceEngine) SetLinkChangeCallback(cb func(major bool, newState *interfaces.State)) {
e.mu.Lock()
defer e.mu.Unlock()
e.linkChangeCallback = cb
if e.linkState != nil {
go cb(false, e.linkState)
}
} }
func (e *userspaceEngine) AddNetworkMapCallback(cb NetworkMapCallback) func() { func (e *userspaceEngine) AddNetworkMapCallback(cb NetworkMapCallback) func() {

@ -14,10 +14,10 @@ import (
"inet.af/netaddr" "inet.af/netaddr"
"tailscale.com/ipn/ipnstate" "tailscale.com/ipn/ipnstate"
"tailscale.com/net/interfaces"
"tailscale.com/tailcfg" "tailscale.com/tailcfg"
"tailscale.com/types/netmap" "tailscale.com/types/netmap"
"tailscale.com/wgengine/filter" "tailscale.com/wgengine/filter"
"tailscale.com/wgengine/monitor"
"tailscale.com/wgengine/router" "tailscale.com/wgengine/router"
"tailscale.com/wgengine/tsdns" "tailscale.com/wgengine/tsdns"
"tailscale.com/wgengine/wgcfg" "tailscale.com/wgengine/wgcfg"
@ -75,10 +75,11 @@ func (e *watchdogEngine) watchdog(name string, fn func()) {
func (e *watchdogEngine) Reconfig(cfg *wgcfg.Config, routerCfg *router.Config) error { func (e *watchdogEngine) Reconfig(cfg *wgcfg.Config, routerCfg *router.Config) error {
return e.watchdogErr("Reconfig", func() error { return e.wrap.Reconfig(cfg, routerCfg) }) return e.watchdogErr("Reconfig", func() error { return e.wrap.Reconfig(cfg, routerCfg) })
} }
func (e *watchdogEngine) GetLinkMonitor() *monitor.Mon {
return e.wrap.GetLinkMonitor()
}
func (e *watchdogEngine) GetFilter() *filter.Filter { func (e *watchdogEngine) GetFilter() *filter.Filter {
var x *filter.Filter return e.wrap.GetFilter()
e.watchdog("GetFilter", func() { x = e.wrap.GetFilter() })
return x
} }
func (e *watchdogEngine) SetFilter(filt *filter.Filter) { func (e *watchdogEngine) SetFilter(filt *filter.Filter) {
e.watchdog("SetFilter", func() { e.wrap.SetFilter(filt) }) e.watchdog("SetFilter", func() { e.wrap.SetFilter(filt) })
@ -98,12 +99,6 @@ func (e *watchdogEngine) SetNetInfoCallback(cb NetInfoCallback) {
func (e *watchdogEngine) RequestStatus() { func (e *watchdogEngine) RequestStatus() {
e.watchdog("RequestStatus", func() { e.wrap.RequestStatus() }) e.watchdog("RequestStatus", func() { e.wrap.RequestStatus() })
} }
func (e *watchdogEngine) LinkChange(isExpensive bool) {
e.watchdog("LinkChange", func() { e.wrap.LinkChange(isExpensive) })
}
func (e *watchdogEngine) SetLinkChangeCallback(cb func(major bool, newState *interfaces.State)) {
e.watchdog("SetLinkChangeCallback", func() { e.wrap.SetLinkChangeCallback(cb) })
}
func (e *watchdogEngine) SetDERPMap(m *tailcfg.DERPMap) { func (e *watchdogEngine) SetDERPMap(m *tailcfg.DERPMap) {
e.watchdog("SetDERPMap", func() { e.wrap.SetDERPMap(m) }) e.watchdog("SetDERPMap", func() { e.wrap.SetDERPMap(m) })
} }

@ -9,10 +9,10 @@ import (
"inet.af/netaddr" "inet.af/netaddr"
"tailscale.com/ipn/ipnstate" "tailscale.com/ipn/ipnstate"
"tailscale.com/net/interfaces"
"tailscale.com/tailcfg" "tailscale.com/tailcfg"
"tailscale.com/types/netmap" "tailscale.com/types/netmap"
"tailscale.com/wgengine/filter" "tailscale.com/wgengine/filter"
"tailscale.com/wgengine/monitor"
"tailscale.com/wgengine/router" "tailscale.com/wgengine/router"
"tailscale.com/wgengine/tsdns" "tailscale.com/wgengine/tsdns"
"tailscale.com/wgengine/wgcfg" "tailscale.com/wgengine/wgcfg"
@ -72,6 +72,9 @@ type Engine interface {
// WireGuard status changes. // WireGuard status changes.
SetStatusCallback(StatusCallback) SetStatusCallback(StatusCallback)
// GetLinkMonitor returns the link monitor.
GetLinkMonitor() *monitor.Mon
// RequestStatus requests a WireGuard status update right // RequestStatus requests a WireGuard status update right
// away, sent to the callback registered via SetStatusCallback. // away, sent to the callback registered via SetStatusCallback.
RequestStatus() RequestStatus()
@ -86,18 +89,6 @@ type Engine interface {
// TODO: return an error? // TODO: return an error?
Wait() Wait()
// LinkChange informs the engine that the system network
// link has changed. The isExpensive parameter is set on links
// where sending packets uses substantial power or money,
// such as mobile data on a phone.
//
// LinkChange should be called whenever something changed with
// the network, no matter how minor. The implementation should
// look at the state of the network and decide whether the
// change from before is interesting enough to warrant taking
// action on.
LinkChange(isExpensive bool)
// SetDERPMap controls which (if any) DERP servers are used. // SetDERPMap controls which (if any) DERP servers are used.
// If nil, DERP is disabled. It starts disabled until a DERP map // If nil, DERP is disabled. It starts disabled until a DERP map
// is configured. // is configured.
@ -120,13 +111,6 @@ type Engine interface {
// new NetInfo summary is available. // new NetInfo summary is available.
SetNetInfoCallback(NetInfoCallback) SetNetInfoCallback(NetInfoCallback)
// SetLinkChangeCallback sets the function to call when the
// link state changes.
// The provided function is run in a new goroutine once upon
// initial call (if the engine has a known link state) and
// upon any change.
SetLinkChangeCallback(func(major bool, newState *interfaces.State))
// DiscoPublicKey gets the public key used for path discovery // DiscoPublicKey gets the public key used for path discovery
// messages. // messages.
DiscoPublicKey() tailcfg.DiscoKey DiscoPublicKey() tailcfg.DiscoKey

Loading…
Cancel
Save