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.
This commit is contained in:
parent
bd37e40d2b
commit
9b07517f18
13
ipn/local.go
13
ipn/local.go
|
@ -19,6 +19,7 @@ import (
|
|||
"tailscale.com/internal/deepprint"
|
||||
"tailscale.com/ipn/ipnstate"
|
||||
"tailscale.com/ipn/policy"
|
||||
"tailscale.com/net/interfaces"
|
||||
"tailscale.com/net/tsaddr"
|
||||
"tailscale.com/portlist"
|
||||
"tailscale.com/tailcfg"
|
||||
|
@ -109,11 +110,23 @@ func NewLocalBackend(logf logger.Logf, logid string, store StateStore, e wgengin
|
|||
state: NoState,
|
||||
portpoll: portpoll,
|
||||
}
|
||||
e.SetLinkChangeCallback(b.linkChange)
|
||||
b.statusChanged = sync.NewCond(&b.statusLock)
|
||||
|
||||
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
|
||||
// can no longer be used after Shutdown returns.
|
||||
func (b *LocalBackend) Shutdown() {
|
||||
|
|
|
@ -109,13 +109,14 @@ type userspaceEngine struct {
|
|||
sentActivityAt map[packet.IP]*int64 // value is atomic int64 of unixtime
|
||||
destIPActivityFuncs map[packet.IP]func()
|
||||
|
||||
mu sync.Mutex // guards following; see lock order comment below
|
||||
closing bool // Close was called (even if we're still closing)
|
||||
statusCallback StatusCallback
|
||||
peerSequence []wgcfg.Key
|
||||
endpoints []string
|
||||
pingers map[wgcfg.Key]*pinger // legacy pingers for pre-discovery peers
|
||||
linkState *interfaces.State
|
||||
mu sync.Mutex // guards following; see lock order comment below
|
||||
closing bool // Close was called (even if we're still closing)
|
||||
statusCallback StatusCallback
|
||||
linkChangeCallback func(major bool, newState *interfaces.State)
|
||||
peerSequence []wgcfg.Key
|
||||
endpoints []string
|
||||
pingers map[wgcfg.Key]*pinger // legacy pingers for pre-discovery peers
|
||||
linkState *interfaces.State
|
||||
|
||||
// Lock ordering: magicsock.Conn.mu, wgLock, then mu.
|
||||
}
|
||||
|
@ -1110,15 +1111,15 @@ func (e *userspaceEngine) Wait() {
|
|||
<-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 {
|
||||
return false
|
||||
return false, nil
|
||||
}
|
||||
e.mu.Lock()
|
||||
defer e.mu.Unlock()
|
||||
changed = e.linkState == nil || !st.Equal(e.linkState)
|
||||
e.linkState = st
|
||||
return changed
|
||||
return changed, e.linkChangeCallback
|
||||
}
|
||||
|
||||
func (e *userspaceEngine) LinkChange(isExpensive bool) {
|
||||
|
@ -1128,7 +1129,7 @@ func (e *userspaceEngine) LinkChange(isExpensive bool) {
|
|||
return
|
||||
}
|
||||
cur.IsExpensive = isExpensive
|
||||
needRebind := e.setLinkState(cur)
|
||||
needRebind, linkChangeCallback := e.setLinkState(cur)
|
||||
|
||||
if needRebind {
|
||||
e.logf("LinkChange: major, rebinding. New state: %+v", cur)
|
||||
|
@ -1142,6 +1143,15 @@ func (e *userspaceEngine) LinkChange(isExpensive bool) {
|
|||
e.magicConn.Rebind()
|
||||
}
|
||||
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) {
|
||||
|
|
|
@ -16,6 +16,7 @@ import (
|
|||
"inet.af/netaddr"
|
||||
"tailscale.com/control/controlclient"
|
||||
"tailscale.com/ipn/ipnstate"
|
||||
"tailscale.com/net/interfaces"
|
||||
"tailscale.com/tailcfg"
|
||||
"tailscale.com/wgengine/filter"
|
||||
"tailscale.com/wgengine/router"
|
||||
|
@ -100,6 +101,9 @@ func (e *watchdogEngine) 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) {
|
||||
e.watchdog("SetDERPMap", func() { e.wrap.SetDERPMap(m) })
|
||||
}
|
||||
|
|
|
@ -12,6 +12,7 @@ import (
|
|||
"inet.af/netaddr"
|
||||
"tailscale.com/control/controlclient"
|
||||
"tailscale.com/ipn/ipnstate"
|
||||
"tailscale.com/net/interfaces"
|
||||
"tailscale.com/tailcfg"
|
||||
"tailscale.com/wgengine/filter"
|
||||
"tailscale.com/wgengine/router"
|
||||
|
@ -117,6 +118,10 @@ type Engine interface {
|
|||
// new NetInfo summary is available.
|
||||
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
|
||||
// messages.
|
||||
DiscoPublicKey() tailcfg.DiscoKey
|
||||
|
|
Loading…
Reference in New Issue