mirror of
https://github.com/tailscale/tailscale.git
synced 2025-12-01 17:49:02 +00:00
ipn/ipnlocal, net/netmon: remove netmon.State dependencies
updates tailscale/corp#33891 This further reduces (but does not completely eliminated) the need to pass around netmon.State and leans on the netmon.ChangeDelta and its precomputed fields. Signed-off-by: Jonathan Nobels <jonathan@tailscale.com>
This commit is contained in:
@@ -294,7 +294,7 @@ type LocalBackend struct {
|
||||
authURLTime time.Time // when the authURL was received from the control server; TODO(nickkhyl): move to nodeBackend
|
||||
authActor ipnauth.Actor // an actor who called [LocalBackend.StartLoginInteractive] last, or nil; TODO(nickkhyl): move to nodeBackend
|
||||
egg bool
|
||||
prevIfState *netmon.State
|
||||
interfaceState *netmon.State // latest network interface state or nil
|
||||
peerAPIServer *peerAPIServer // or nil
|
||||
peerAPIListeners []*peerAPIListener
|
||||
loginFlags controlclient.LoginFlags
|
||||
@@ -559,10 +559,10 @@ func NewLocalBackend(logf logger.Logf, logID logid.PublicID, sys *tsd.System, lo
|
||||
|
||||
b.e.SetStatusCallback(b.setWgengineStatus)
|
||||
|
||||
b.prevIfState = netMon.InterfaceState()
|
||||
b.interfaceState = netMon.InterfaceState()
|
||||
// Call our linkChange code once with the current state.
|
||||
// Following changes are triggered via the eventbus.
|
||||
cd := netmon.NewChangeDelta(nil, b.prevIfState, false, netMon.TailscaleInterfaceName())
|
||||
cd := netmon.NewChangeDelta(nil, b.interfaceState, false, netMon.TailscaleInterfaceName())
|
||||
b.linkChange(&cd)
|
||||
|
||||
if buildfeatures.HasPeerAPIServer {
|
||||
@@ -931,7 +931,7 @@ func (b *LocalBackend) pauseOrResumeControlClientLocked() {
|
||||
if b.cc == nil {
|
||||
return
|
||||
}
|
||||
networkUp := b.prevIfState.AnyInterfaceUp()
|
||||
networkUp := b.interfaceState.AnyInterfaceUp()
|
||||
pauseForNetwork := (b.state == ipn.Stopped && b.NetMap() != nil) || (!networkUp && !testenv.InTest() && !assumeNetworkUpdateForTest())
|
||||
|
||||
prefs := b.pm.CurrentPrefs()
|
||||
@@ -958,9 +958,8 @@ func (b *LocalBackend) linkChange(delta *netmon.ChangeDelta) {
|
||||
b.mu.Lock()
|
||||
defer b.mu.Unlock()
|
||||
|
||||
ifst := delta.New
|
||||
hadPAC := b.prevIfState.HasPAC()
|
||||
b.prevIfState = ifst
|
||||
b.interfaceState = delta.New
|
||||
|
||||
b.pauseOrResumeControlClientLocked()
|
||||
prefs := b.pm.CurrentPrefs()
|
||||
if delta.RebindLikelyRequired && prefs.AutoExitNode().IsSet() {
|
||||
@@ -974,8 +973,8 @@ func (b *LocalBackend) linkChange(delta *netmon.ChangeDelta) {
|
||||
needReconfig = true
|
||||
}
|
||||
// If the PAC-ness of the network changed, reconfig wireguard+route to add/remove subnets.
|
||||
if hadPAC != ifst.HasPAC() {
|
||||
b.logf("linkChange: in state %v; PAC changed from %v->%v", b.state, hadPAC, ifst.HasPAC())
|
||||
if delta.HasPACOrProxyConfigChanged {
|
||||
b.logf("linkChange: in state %v; PAC or proxyConfig changed; updating routes", b.state)
|
||||
needReconfig = true
|
||||
}
|
||||
if needReconfig {
|
||||
@@ -5032,7 +5031,7 @@ func (b *LocalBackend) authReconfigLocked() {
|
||||
}
|
||||
|
||||
prefs := b.pm.CurrentPrefs()
|
||||
hasPAC := b.prevIfState.HasPAC()
|
||||
hasPAC := b.interfaceState.HasPAC()
|
||||
disableSubnetsIfPAC := cn.SelfHasCap(tailcfg.NodeAttrDisableSubnetsIfPAC)
|
||||
dohURL, dohURLOK := cn.exitNodeCanProxyDNS(prefs.ExitNodeID())
|
||||
dcfg := cn.dnsConfigForNetmap(prefs, b.keyExpired, version.OS())
|
||||
@@ -5278,7 +5277,7 @@ func (b *LocalBackend) initPeerAPIListenerLocked() {
|
||||
var err error
|
||||
skipListen := i > 0 && isNetstack
|
||||
if !skipListen {
|
||||
ln, err = ps.listen(a.Addr(), b.prevIfState)
|
||||
ln, err = ps.listen(a.Addr(), b.interfaceState.TailscaleInterfaceIndex)
|
||||
if err != nil {
|
||||
if peerAPIListenAsync {
|
||||
b.logf("[v1] possibly transient peerapi listen(%q) error, will try again on linkChange: %v", a.Addr(), err)
|
||||
|
||||
@@ -41,7 +41,7 @@ import (
|
||||
"tailscale.com/wgengine/filter"
|
||||
)
|
||||
|
||||
var initListenConfig func(*net.ListenConfig, netip.Addr, *netmon.State, string) error
|
||||
var initListenConfig func(config *net.ListenConfig, addr netip.Addr, tunIfIndex int) error
|
||||
|
||||
// peerDNSQueryHandler is implemented by tsdns.Resolver.
|
||||
type peerDNSQueryHandler interface {
|
||||
@@ -53,7 +53,7 @@ type peerAPIServer struct {
|
||||
resolver peerDNSQueryHandler
|
||||
}
|
||||
|
||||
func (s *peerAPIServer) listen(ip netip.Addr, ifState *netmon.State) (ln net.Listener, err error) {
|
||||
func (s *peerAPIServer) listen(ip netip.Addr, tunIfIndex int) (ln net.Listener, err error) {
|
||||
// Android for whatever reason often has problems creating the peerapi listener.
|
||||
// But since we started intercepting it with netstack, it's not even important that
|
||||
// we have a real kernel-level listener. So just create a dummy listener on Android
|
||||
@@ -69,7 +69,7 @@ func (s *peerAPIServer) listen(ip netip.Addr, ifState *netmon.State) (ln net.Lis
|
||||
// On iOS/macOS, this sets the lc.Control hook to
|
||||
// setsockopt the interface index to bind to, to get
|
||||
// out of the network sandbox.
|
||||
if err := initListenConfig(&lc, ip, ifState, s.b.dialer.TUNName()); err != nil {
|
||||
if err := initListenConfig(&lc, ip, tunIfIndex); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if runtime.GOOS == "darwin" || runtime.GOOS == "ios" {
|
||||
|
||||
@@ -6,11 +6,9 @@
|
||||
package ipnlocal
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net"
|
||||
"net/netip"
|
||||
|
||||
"tailscale.com/net/netmon"
|
||||
"tailscale.com/net/netns"
|
||||
)
|
||||
|
||||
@@ -21,10 +19,6 @@ func init() {
|
||||
// initListenConfigNetworkExtension configures nc for listening on IP
|
||||
// through the iOS/macOS Network/System Extension (Packet Tunnel
|
||||
// Provider) sandbox.
|
||||
func initListenConfigNetworkExtension(nc *net.ListenConfig, ip netip.Addr, st *netmon.State, tunIfName string) error {
|
||||
tunIf, ok := st.Interface[tunIfName]
|
||||
if !ok {
|
||||
return fmt.Errorf("no interface with name %q", tunIfName)
|
||||
}
|
||||
return netns.SetListenConfigInterfaceIndex(nc, tunIf.Index)
|
||||
func initListenConfigNetworkExtension(nc *net.ListenConfig, ip netip.Addr, ifaceIndex int) error {
|
||||
return netns.SetListenConfigInterfaceIndex(nc, ifaceIndex)
|
||||
}
|
||||
|
||||
@@ -167,7 +167,7 @@ func (s *localListener) Run() {
|
||||
// required by the network sandbox to allow binding to
|
||||
// a specific interface. Without this hook, the system
|
||||
// chooses a default interface to bind to.
|
||||
if err := initListenConfig(&lc, ip, s.b.prevIfState, s.b.dialer.TUNName()); err != nil {
|
||||
if err := initListenConfig(&lc, ip, s.b.interfaceState.TailscaleInterfaceIndex); err != nil {
|
||||
s.logf("localListener failed to init listen config %v, backing off: %v", s.ap, err)
|
||||
s.bo.BackOff(s.ctx, err)
|
||||
continue
|
||||
|
||||
@@ -437,7 +437,7 @@ func (lg *Logger) internetUp() bool {
|
||||
// [netmon.ChangeDelta] events to detect whether the Internet is expected to be
|
||||
// reachable.
|
||||
func (lg *Logger) onChangeDelta(delta *netmon.ChangeDelta) {
|
||||
if delta.New.AnyInterfaceUp() {
|
||||
if delta.AnyInterfaceUp() {
|
||||
fmt.Fprintf(lg.stderr, "logtail: internet back up\n")
|
||||
lg.networkIsUp.Set()
|
||||
} else {
|
||||
@@ -456,7 +456,7 @@ func (lg *Logger) awaitInternetUp(ctx context.Context) {
|
||||
}
|
||||
upc := make(chan bool, 1)
|
||||
defer lg.netMonitor.RegisterChangeCallback(func(delta *netmon.ChangeDelta) {
|
||||
if delta.New.AnyInterfaceUp() {
|
||||
if delta.AnyInterfaceUp() {
|
||||
select {
|
||||
case upc <- true:
|
||||
default:
|
||||
|
||||
@@ -87,7 +87,10 @@ type ChangeFunc func(*ChangeDelta)
|
||||
|
||||
// ChangeDelta describes the difference between two network states.
|
||||
//
|
||||
// Use NewChangeDelta to construct one and compute the cached fields.
|
||||
// Use NewChangeDelta to construct a delta and compute the cached fields.
|
||||
//
|
||||
// TODO (barnstar): make new and old (and netmon.State) private once all consumers are updated
|
||||
// to use the accessor methods.
|
||||
type ChangeDelta struct {
|
||||
// Old is the old interface state, if known.
|
||||
// It's nil if the old state is unknown.
|
||||
@@ -108,6 +111,8 @@ type ChangeDelta struct {
|
||||
// platforms know this or set it. Copied from netmon.Monitor.tsIfName.
|
||||
TailscaleIfaceName string
|
||||
|
||||
DefaultRouteInterface string
|
||||
|
||||
// Computed Fields
|
||||
|
||||
DefaultInterfaceChanged bool // whether default route interface changed
|
||||
@@ -116,16 +121,15 @@ type ChangeDelta struct {
|
||||
InterfaceIPsChanged bool // whether any interface IPs changed in a meaningful way
|
||||
AvailableProtocolsChanged bool // whether we have seen a change in available IPv4/IPv6
|
||||
DefaultInterfaceMaybeViable bool // whether the default interface is potentially viable (has usable IPs, is up and is not the tunnel itself)
|
||||
IsInitialState bool // whether this is the initial state (old == nil)
|
||||
|
||||
// RebindLikelyRequired combines the various fields above to report whether this change likely requires us
|
||||
// to rebind sockets. This is a very conservative estimate and covers a number of
|
||||
// cases where a rebind is not strictly necessary. Consumers of the ChangeDelta should
|
||||
// use this as a hint only. If in doubt, rebind.
|
||||
// to rebind sockets. This is a very conservative estimate and covers a number ofcases where a rebind
|
||||
// may not be strictly necessary. Consumers of the ChangeDelta should consider checking the individual fields
|
||||
// above or the state of their sockets.
|
||||
RebindLikelyRequired bool
|
||||
}
|
||||
|
||||
var skipRebindIfNoDefaultRouteChange = true
|
||||
|
||||
// NewChangeDelta builds a ChangeDelta and eagerly computes the cached fields.
|
||||
func NewChangeDelta(old, new *State, timeJumped bool, tsIfName string) ChangeDelta {
|
||||
cd := ChangeDelta{
|
||||
@@ -142,6 +146,7 @@ func NewChangeDelta(old, new *State, timeJumped bool, tsIfName string) ChangeDel
|
||||
cd.IsLessExpensive = false
|
||||
cd.HasPACOrProxyConfigChanged = true
|
||||
cd.InterfaceIPsChanged = true
|
||||
cd.IsInitialState = true
|
||||
} else {
|
||||
cd.AvailableProtocolsChanged = cd.Old.HaveV4 != cd.New.HaveV4 || cd.Old.HaveV6 != cd.New.HaveV6
|
||||
cd.DefaultInterfaceChanged = cd.Old.DefaultRouteInterface != cd.New.DefaultRouteInterface
|
||||
@@ -152,11 +157,13 @@ func NewChangeDelta(old, new *State, timeJumped bool, tsIfName string) ChangeDel
|
||||
|
||||
// If the default route interface is populated, but it's not up this event signifies that we're in
|
||||
// the process of tearing it down. Rebinds are going to fail so it's flappy to try.
|
||||
defIfName := new.DefaultRouteInterface
|
||||
defIf := new.Interface[defIfName]
|
||||
cd.DefaultRouteInterface = new.DefaultRouteInterface
|
||||
defIf := new.Interface[cd.DefaultRouteInterface]
|
||||
|
||||
cd.DefaultInterfaceMaybeViable = true
|
||||
|
||||
// The default interface is not viable if is down or is the Tailscale interface itself.
|
||||
if !defIf.IsUp() || defIfName == tsIfName {
|
||||
if !defIf.IsUp() || cd.DefaultRouteInterface == tsIfName {
|
||||
cd.DefaultInterfaceMaybeViable = false
|
||||
}
|
||||
|
||||
@@ -181,6 +188,50 @@ func NewChangeDelta(old, new *State, timeJumped bool, tsIfName string) ChangeDel
|
||||
return cd
|
||||
}
|
||||
|
||||
// StateDesc returns a description of the old and new states for logging
|
||||
func (cd *ChangeDelta) StateDesc() string {
|
||||
return fmt.Sprintf("old: %v new: %v", cd.Old, cd.New)
|
||||
}
|
||||
|
||||
// HasPAC reports whether the new state has a PAC configured.
|
||||
func (cd *ChangeDelta) HasPAC() bool {
|
||||
if cd.New == nil {
|
||||
return false
|
||||
}
|
||||
return cd.New.PAC != ""
|
||||
}
|
||||
|
||||
// InterfaceIPAppeared reports whether the given IP address exists on any interface
|
||||
// in the old state, but not in the new state.
|
||||
func (cd *ChangeDelta) InterfaceIPDisppeared(ip netip.Addr) bool {
|
||||
if cd.Old == nil {
|
||||
return false
|
||||
}
|
||||
if cd.New == nil && cd.Old.HasIP(ip) {
|
||||
return true
|
||||
}
|
||||
return cd.New.HasIP(ip) && !cd.Old.HasIP(ip)
|
||||
}
|
||||
|
||||
// InterfaceIPDisappeared reports whether the given IP address existed on any interface
|
||||
// in the old state, but not in the new state.
|
||||
func (cd *ChangeDelta) InterfaceIPDisappeared(ip netip.Addr) bool {
|
||||
return !cd.New.HasIP(ip) && cd.Old.HasIP(ip)
|
||||
}
|
||||
|
||||
// AnyInterfaceUp reports whether any interfaces are up in the new state.
|
||||
func (cd *ChangeDelta) AnyInterfaceUp() bool {
|
||||
if cd.New == nil {
|
||||
return false
|
||||
}
|
||||
for _, ifi := range cd.New.Interface {
|
||||
if ifi.IsUp() {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// isInterestingIntefaceChange reports whether any interfaces have changed in a meaninful way.
|
||||
// This excludes interfaces that are not interesting per IsInterestingInterface and
|
||||
// filters out changes to interface IPs that that are uninteresting (e.g. link-local addresses).
|
||||
|
||||
@@ -642,8 +642,3 @@ func TestForeachInterface(t *testing.T) {
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
type testOSMon struct {
|
||||
osMon
|
||||
Interesting func(name string) bool
|
||||
}
|
||||
|
||||
@@ -287,6 +287,9 @@ type State struct {
|
||||
|
||||
// PAC is the URL to the Proxy Autoconfig URL, if applicable.
|
||||
PAC string
|
||||
|
||||
// TailscaleInterfaceIndex is the index of the Tailscale interface
|
||||
TailscaleInterfaceIndex int
|
||||
}
|
||||
|
||||
func (s *State) String() string {
|
||||
@@ -507,6 +510,10 @@ func getState(optTSInterfaceName string) (*State, error) {
|
||||
return
|
||||
}
|
||||
|
||||
if isTailscaleInterface(ni.Name, pfxs) {
|
||||
s.TailscaleInterfaceIndex = ni.Index
|
||||
}
|
||||
|
||||
if !ifUp || isTSInterfaceName || isTailscaleInterface(ni.Name, pfxs) {
|
||||
return
|
||||
}
|
||||
|
||||
@@ -264,7 +264,7 @@ var (
|
||||
|
||||
func (d *Dialer) linkChanged(delta *netmon.ChangeDelta) {
|
||||
// Track how often we see ChangeDeltas with no DefaultRouteInterface.
|
||||
if delta.New.DefaultRouteInterface == "" {
|
||||
if delta.DefaultRouteInterface == "" {
|
||||
metricChangeDeltaNoDefaultRoute.Add(1)
|
||||
}
|
||||
|
||||
@@ -294,22 +294,23 @@ func changeAffectsConn(delta *netmon.ChangeDelta, conn net.Conn) bool {
|
||||
}
|
||||
lip, rip := la.AddrPort().Addr(), ra.AddrPort().Addr()
|
||||
|
||||
if delta.Old == nil {
|
||||
if delta.IsInitialState {
|
||||
return false
|
||||
}
|
||||
if delta.Old.DefaultRouteInterface != delta.New.DefaultRouteInterface ||
|
||||
delta.Old.HTTPProxy != delta.New.HTTPProxy {
|
||||
|
||||
if delta.DefaultInterfaceChanged ||
|
||||
delta.HasPACOrProxyConfigChanged {
|
||||
return true
|
||||
}
|
||||
|
||||
// In a few cases, we don't have a new DefaultRouteInterface (e.g. on
|
||||
// Android; see tailscale/corp#19124); if so, pessimistically assume
|
||||
// Android and macOS/iOS; see tailscale/corp#19124); if so, pessimistically assume
|
||||
// that all connections are affected.
|
||||
if delta.New.DefaultRouteInterface == "" && runtime.GOOS != "plan9" {
|
||||
if delta.DefaultRouteInterface == "" && runtime.GOOS != "plan9" {
|
||||
return true
|
||||
}
|
||||
|
||||
if !delta.New.HasIP(lip) && delta.Old.HasIP(lip) {
|
||||
if delta.InterfaceIPDisappeared(lip) {
|
||||
// Our interface with this source IP went away.
|
||||
return true
|
||||
}
|
||||
|
||||
@@ -387,6 +387,7 @@ func NewUserspaceEngine(logf logger.Logf, conf Config) (_ Engine, reterr error)
|
||||
conf.Dialer.SetTUNName(tunName)
|
||||
conf.Dialer.SetNetMon(e.netMon)
|
||||
conf.Dialer.SetBus(e.eventBus)
|
||||
|
||||
e.dns = dns.NewManager(logf, conf.DNS, e.health, conf.Dialer, fwdDNSLinkSelector{e, tunName}, conf.ControlKnobs, runtime.GOOS)
|
||||
|
||||
// TODO: there's probably a better place for this
|
||||
@@ -1331,12 +1332,11 @@ func (e *userspaceEngine) Done() <-chan struct{} {
|
||||
|
||||
func (e *userspaceEngine) linkChange(delta *netmon.ChangeDelta) {
|
||||
|
||||
cur := delta.New
|
||||
up := cur.AnyInterfaceUp()
|
||||
up := delta.AnyInterfaceUp()
|
||||
if !up {
|
||||
e.logf("LinkChange: all links down; pausing: %v", cur)
|
||||
e.logf("LinkChange: all links down; pausing: %v", delta.StateDesc())
|
||||
} else if delta.RebindLikelyRequired {
|
||||
e.logf("LinkChange: major, rebinding. New state: %v OldState: %v", cur, delta.Old)
|
||||
e.logf("LinkChange: major, rebinding: %v", delta.StateDesc())
|
||||
} else {
|
||||
e.logf("[v1] LinkChange: minor")
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user