wgengine: add Engine.SetLinkChangeCallback

Start of making the IPN state machine react to link changes and down
its DNS & routes if necessary to unblock proxy resolution (e.g. for
transitioning from public to corp networks where the corp network has
mandatory proxies and WPAD PAC files that can't be resolved while
using the DNS/routes configured previously)

This change should be a no-op. Just some callback plumbing.
reviewable/pr727/r1
Brad Fitzpatrick 4 years ago
parent bd37e40d2b
commit 9b07517f18

@ -19,6 +19,7 @@ import (
"tailscale.com/internal/deepprint" "tailscale.com/internal/deepprint"
"tailscale.com/ipn/ipnstate" "tailscale.com/ipn/ipnstate"
"tailscale.com/ipn/policy" "tailscale.com/ipn/policy"
"tailscale.com/net/interfaces"
"tailscale.com/net/tsaddr" "tailscale.com/net/tsaddr"
"tailscale.com/portlist" "tailscale.com/portlist"
"tailscale.com/tailcfg" "tailscale.com/tailcfg"
@ -109,11 +110,23 @@ func NewLocalBackend(logf logger.Logf, logid string, store StateStore, e wgengin
state: NoState, state: NoState,
portpoll: portpoll, portpoll: portpoll,
} }
e.SetLinkChangeCallback(b.linkChange)
b.statusChanged = sync.NewCond(&b.statusLock) b.statusChanged = sync.NewCond(&b.statusLock)
return b, nil return b, nil
} }
func (b *LocalBackend) linkChange(major bool, ifst *interfaces.State) {
// TODO(bradfitz): on a major link change, ask controlclient
// whether its host (e.g. login.tailscale.com) is reachable.
// If not, down the world and poll for a bit. Windows' WinHTTP
// service might be unable to resolve its WPAD PAC URL if we
// have DNS/routes configured. So we need to remove that DNS
// and those routes to let it figure out its proxy
// settings. Once it's back up and happy, then we can resume
// and our connection to the control server would work again.
}
// Shutdown halts the backend and all its sub-components. The backend // Shutdown halts the backend and all its sub-components. The backend
// can no longer be used after Shutdown returns. // can no longer be used after Shutdown returns.
func (b *LocalBackend) Shutdown() { func (b *LocalBackend) Shutdown() {

@ -112,6 +112,7 @@ 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 []wgcfg.Key peerSequence []wgcfg.Key
endpoints []string endpoints []string
pingers map[wgcfg.Key]*pinger // legacy pingers for pre-discovery peers pingers map[wgcfg.Key]*pinger // legacy pingers for pre-discovery peers
@ -1110,15 +1111,15 @@ func (e *userspaceEngine) Wait() {
<-e.waitCh <-e.waitCh
} }
func (e *userspaceEngine) setLinkState(st *interfaces.State) (changed bool) { func (e *userspaceEngine) setLinkState(st *interfaces.State) (changed bool, cb func(major bool, newState *interfaces.State)) {
if st == nil { if st == nil {
return false return false, nil
} }
e.mu.Lock() e.mu.Lock()
defer e.mu.Unlock() defer e.mu.Unlock()
changed = e.linkState == nil || !st.Equal(e.linkState) changed = e.linkState == nil || !st.Equal(e.linkState)
e.linkState = st e.linkState = st
return changed return changed, e.linkChangeCallback
} }
func (e *userspaceEngine) LinkChange(isExpensive bool) { func (e *userspaceEngine) LinkChange(isExpensive bool) {
@ -1128,7 +1129,7 @@ func (e *userspaceEngine) LinkChange(isExpensive bool) {
return return
} }
cur.IsExpensive = isExpensive cur.IsExpensive = isExpensive
needRebind := e.setLinkState(cur) needRebind, linkChangeCallback := e.setLinkState(cur)
if needRebind { if needRebind {
e.logf("LinkChange: major, rebinding. New state: %+v", cur) e.logf("LinkChange: major, rebinding. New state: %+v", cur)
@ -1142,6 +1143,15 @@ func (e *userspaceEngine) LinkChange(isExpensive bool) {
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
} }
func getLinkState() (*interfaces.State, error) { func getLinkState() (*interfaces.State, error) {

@ -16,6 +16,7 @@ import (
"inet.af/netaddr" "inet.af/netaddr"
"tailscale.com/control/controlclient" "tailscale.com/control/controlclient"
"tailscale.com/ipn/ipnstate" "tailscale.com/ipn/ipnstate"
"tailscale.com/net/interfaces"
"tailscale.com/tailcfg" "tailscale.com/tailcfg"
"tailscale.com/wgengine/filter" "tailscale.com/wgengine/filter"
"tailscale.com/wgengine/router" "tailscale.com/wgengine/router"
@ -100,6 +101,9 @@ func (e *watchdogEngine) RequestStatus() {
func (e *watchdogEngine) LinkChange(isExpensive bool) { func (e *watchdogEngine) LinkChange(isExpensive bool) {
e.watchdog("LinkChange", func() { e.wrap.LinkChange(isExpensive) }) 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) })
} }

@ -12,6 +12,7 @@ import (
"inet.af/netaddr" "inet.af/netaddr"
"tailscale.com/control/controlclient" "tailscale.com/control/controlclient"
"tailscale.com/ipn/ipnstate" "tailscale.com/ipn/ipnstate"
"tailscale.com/net/interfaces"
"tailscale.com/tailcfg" "tailscale.com/tailcfg"
"tailscale.com/wgengine/filter" "tailscale.com/wgengine/filter"
"tailscale.com/wgengine/router" "tailscale.com/wgengine/router"
@ -117,6 +118,10 @@ 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.
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