ipn, wgengine, magicsock, tsdns: be quieter and less aggressive when offline
If no interfaces are up, calm down and stop spamming so much. It was noticed as especially bad on Windows, but probably was bad everywhere. I just have the best network conditions testing on a Windows VM. Updates #604
This commit is contained in:
parent
7616acd118
commit
6ee219a25d
|
@ -129,6 +129,11 @@ func (b *LocalBackend) linkChange(major bool, ifst *interfaces.State) {
|
|||
hadPAC := b.prevIfState.HasPAC()
|
||||
b.prevIfState = ifst
|
||||
|
||||
networkUp := ifst.AnyInterfaceUp()
|
||||
if b.c != nil {
|
||||
go b.c.SetPaused(b.state == Stopped || !networkUp)
|
||||
}
|
||||
|
||||
// If the PAC-ness of the network changed, reconfig wireguard+route to
|
||||
// add/remove subnets.
|
||||
if hadPAC != ifst.HasPAC() {
|
||||
|
@ -1197,6 +1202,7 @@ func (b *LocalBackend) enterState(newState State) {
|
|||
prefs := b.prefs
|
||||
notify := b.notify
|
||||
bc := b.c
|
||||
networkUp := b.prevIfState.AnyInterfaceUp()
|
||||
b.mu.Unlock()
|
||||
|
||||
if state == newState {
|
||||
|
@ -1209,7 +1215,7 @@ func (b *LocalBackend) enterState(newState State) {
|
|||
}
|
||||
|
||||
if bc != nil {
|
||||
bc.SetPaused(newState == Stopped)
|
||||
bc.SetPaused(newState == Stopped || !networkUp)
|
||||
}
|
||||
|
||||
switch newState {
|
||||
|
|
|
@ -161,10 +161,11 @@ type State struct {
|
|||
InterfaceUp map[string]bool
|
||||
|
||||
// HaveV6Global is whether this machine has an IPv6 global address
|
||||
// on some interface.
|
||||
// on some non-Tailscale interface that's up.
|
||||
HaveV6Global bool
|
||||
|
||||
// HaveV4 is whether the machine has some non-localhost IPv4 address.
|
||||
// HaveV4 is whether the machine has some non-localhost,
|
||||
// non-link-local IPv4 address on a non-Tailscale interface that's up.
|
||||
HaveV4 bool
|
||||
|
||||
// IsExpensive is whether the current network interface is
|
||||
|
@ -174,6 +175,8 @@ type State struct {
|
|||
|
||||
// DefaultRouteInterface is the interface name for the machine's default route.
|
||||
// It is not yet populated on all OSes.
|
||||
// Its exact value should not be assumed to be a map key for
|
||||
// the Interface maps above; it's only used for debugging.
|
||||
DefaultRouteInterface string
|
||||
|
||||
// HTTPProxy is the HTTP proxy to use.
|
||||
|
@ -244,20 +247,29 @@ func (s *State) Equal(s2 *State) bool {
|
|||
|
||||
func (s *State) HasPAC() bool { return s != nil && s.PAC != "" }
|
||||
|
||||
// AnyInterfaceUp reports whether any interface seems like it has Internet access.
|
||||
func (s *State) AnyInterfaceUp() bool {
|
||||
return s != nil && (s.HaveV4 || s.HaveV6Global)
|
||||
}
|
||||
|
||||
// RemoveTailscaleInterfaces modifes s to remove any interfaces that
|
||||
// are owned by this process. (TODO: make this true; currently it
|
||||
// makes the Linux-only assumption that the interface is named
|
||||
// /^tailscale/)
|
||||
func (s *State) RemoveTailscaleInterfaces() {
|
||||
for name := range s.InterfaceIPs {
|
||||
if name == "Tailscale" || // as it is on Windows
|
||||
strings.HasPrefix(name, "tailscale") { // TODO: use --tun flag value, etc; see TODO in method doc
|
||||
if isTailscaleInterfaceName(name) {
|
||||
delete(s.InterfaceIPs, name)
|
||||
delete(s.InterfaceUp, name)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func isTailscaleInterfaceName(name string) bool {
|
||||
return name == "Tailscale" || // as it is on Windows
|
||||
strings.HasPrefix(name, "tailscale") // TODO: use --tun flag value, etc; see TODO in method doc
|
||||
}
|
||||
|
||||
// getPAC, if non-nil, returns the current PAC file URL.
|
||||
var getPAC func() string
|
||||
|
||||
|
@ -270,24 +282,29 @@ func GetState() (*State, error) {
|
|||
InterfaceUp: make(map[string]bool),
|
||||
}
|
||||
if err := ForeachInterfaceAddress(func(ni Interface, ip netaddr.IP) {
|
||||
ifUp := ni.IsUp()
|
||||
s.InterfaceIPs[ni.Name] = append(s.InterfaceIPs[ni.Name], ip)
|
||||
s.InterfaceUp[ni.Name] = ni.IsUp()
|
||||
s.HaveV6Global = s.HaveV6Global || isGlobalV6(ip)
|
||||
s.HaveV4 = s.HaveV4 || (ip.Is4() && !ip.IsLoopback())
|
||||
s.InterfaceUp[ni.Name] = ifUp
|
||||
if ifUp && !ip.IsLoopback() && !ip.IsLinkLocalUnicast() && !isTailscaleInterfaceName(ni.Name) {
|
||||
s.HaveV6Global = s.HaveV6Global || isGlobalV6(ip)
|
||||
s.HaveV4 = s.HaveV4 || ip.Is4()
|
||||
}
|
||||
}); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
s.DefaultRouteInterface, _ = DefaultRouteInterface()
|
||||
|
||||
req, err := http.NewRequest("GET", LoginEndpointForProxyDetermination, nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if u, err := tshttpproxy.ProxyFromEnvironment(req); err == nil && u != nil {
|
||||
s.HTTPProxy = u.String()
|
||||
}
|
||||
if getPAC != nil {
|
||||
s.PAC = getPAC()
|
||||
if s.AnyInterfaceUp() {
|
||||
req, err := http.NewRequest("GET", LoginEndpointForProxyDetermination, nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if u, err := tshttpproxy.ProxyFromEnvironment(req); err == nil && u != nil {
|
||||
s.HTTPProxy = u.String()
|
||||
}
|
||||
if getPAC != nil {
|
||||
s.PAC = getPAC()
|
||||
}
|
||||
}
|
||||
|
||||
return s, nil
|
||||
|
|
|
@ -208,6 +208,11 @@ type Conn struct {
|
|||
// necessarily have a netcheck.Report and don't want to skip
|
||||
// logging.
|
||||
noV4, noV6 syncs.AtomicBool
|
||||
|
||||
// networkUp is whether the network is up (some interface is up
|
||||
// with IPv4 or IPv6). It's used to suppress log spam and prevent
|
||||
// new connection that'll fail.
|
||||
networkUp syncs.AtomicBool
|
||||
}
|
||||
|
||||
// derpRoute is a route entry for a public key, saying that a certain
|
||||
|
@ -345,6 +350,7 @@ func newConn() *Conn {
|
|||
discoOfAddr: make(map[netaddr.IPPort]tailcfg.DiscoKey),
|
||||
}
|
||||
c.muCond = sync.NewCond(&c.mu)
|
||||
c.networkUp.Set(true) // assume up until told otherwise
|
||||
return c
|
||||
}
|
||||
|
||||
|
@ -969,8 +975,15 @@ func (as *AddrSet) appendDests(dsts []netaddr.IPPort, b []byte) (_ []netaddr.IPP
|
|||
}
|
||||
|
||||
var errNoDestinations = errors.New("magicsock: no destinations")
|
||||
var errNetworkDown = errors.New("magicsock: network down")
|
||||
|
||||
func (c *Conn) networkDown() bool { return !c.networkUp.Get() }
|
||||
|
||||
func (c *Conn) Send(b []byte, ep conn.Endpoint) error {
|
||||
if c.networkDown() {
|
||||
return errNetworkDown
|
||||
}
|
||||
|
||||
var as *AddrSet
|
||||
switch v := ep.(type) {
|
||||
default:
|
||||
|
@ -1111,6 +1124,10 @@ func (c *Conn) derpWriteChanOfAddr(addr netaddr.IPPort, peer key.Public) chan<-
|
|||
}
|
||||
regionID := int(addr.Port)
|
||||
|
||||
if c.networkDown() {
|
||||
return nil
|
||||
}
|
||||
|
||||
c.mu.Lock()
|
||||
defer c.mu.Unlock()
|
||||
if !c.wantDerpLocked() || c.closed {
|
||||
|
@ -1304,15 +1321,19 @@ func (c *Conn) runDerpReader(ctx context.Context, derpFakeAddr netaddr.IPPort, d
|
|||
|
||||
for {
|
||||
msg, err := dc.Recv()
|
||||
if err == derphttp.ErrClientClosed {
|
||||
return
|
||||
}
|
||||
if err != nil {
|
||||
// Forget that all these peers have routes.
|
||||
for peer := range peerPresent {
|
||||
delete(peerPresent, peer)
|
||||
c.removeDerpPeerRoute(peer, regionID, dc)
|
||||
}
|
||||
if err == derphttp.ErrClientClosed {
|
||||
return
|
||||
}
|
||||
if c.networkDown() {
|
||||
c.logf("magicsock: derp.Recv(derp-%d): network down, closing", regionID)
|
||||
return
|
||||
}
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
return
|
||||
|
@ -1691,7 +1712,9 @@ func (c *Conn) sendDiscoMessage(dst netaddr.IPPort, dstKey tailcfg.NodeKey, dstD
|
|||
} else if err == nil {
|
||||
// Can't send. (e.g. no IPv6 locally)
|
||||
} else {
|
||||
c.logf("magicsock: disco: failed to send %T to %v: %v", m, dst, err)
|
||||
if !c.networkDown() {
|
||||
c.logf("magicsock: disco: failed to send %T to %v: %v", m, dst, err)
|
||||
}
|
||||
}
|
||||
return sent, err
|
||||
}
|
||||
|
@ -1956,6 +1979,21 @@ func (c *Conn) sharedDiscoKeyLocked(k tailcfg.DiscoKey) *[32]byte {
|
|||
return shared
|
||||
}
|
||||
|
||||
func (c *Conn) SetNetworkUp(up bool) {
|
||||
c.mu.Lock()
|
||||
defer c.mu.Unlock()
|
||||
if c.networkUp.Get() == up {
|
||||
return
|
||||
}
|
||||
|
||||
c.logf("magicsock: SetNetworkUp(%v)", up)
|
||||
c.networkUp.Set(up)
|
||||
|
||||
if !up {
|
||||
c.closeAllDerpLocked("network-down")
|
||||
}
|
||||
}
|
||||
|
||||
// SetPrivateKey sets the connection's private key.
|
||||
//
|
||||
// This is only used to be able prove our identity when connecting to
|
||||
|
@ -2282,6 +2320,10 @@ func maxIdleBeforeSTUNShutdown() time.Duration {
|
|||
}
|
||||
|
||||
func (c *Conn) shouldDoPeriodicReSTUN() bool {
|
||||
if c.networkDown() {
|
||||
return false
|
||||
}
|
||||
|
||||
c.mu.Lock()
|
||||
defer c.mu.Unlock()
|
||||
if len(c.peerSet) == 0 {
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// +build !darwin
|
||||
// +build !darwin,!windows
|
||||
|
||||
package tsdns
|
||||
|
||||
|
|
|
@ -0,0 +1,29 @@
|
|||
// Copyright (c) 2020 Tailscale Inc & AUTHORS All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package tsdns
|
||||
|
||||
import (
|
||||
"net"
|
||||
"os"
|
||||
|
||||
"golang.org/x/sys/windows"
|
||||
)
|
||||
|
||||
func networkIsDown(err error) bool {
|
||||
if oe, ok := err.(*net.OpError); ok && oe.Op == "write" {
|
||||
if se, ok := oe.Err.(*os.SyscallError); ok {
|
||||
if se.Syscall == "wsasendto" && se.Err == windows.WSAENETUNREACH {
|
||||
return true
|
||||
}
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func networkIsUnreachable(err error) bool {
|
||||
// TODO(bradfitz,josharian): something here? what is the
|
||||
// difference between down and unreachable? Add comments.
|
||||
return false
|
||||
}
|
|
@ -246,6 +246,7 @@ func newUserspaceEngineAdvanced(conf EngineConfig) (_ Engine, reterr error) {
|
|||
e.tundev.Close()
|
||||
return nil, fmt.Errorf("wgengine: %v", err)
|
||||
}
|
||||
e.magicConn.SetNetworkUp(e.linkState.AnyInterfaceUp())
|
||||
|
||||
// flags==0 because logf is already nested in another logger.
|
||||
// The outer one can display the preferred log prefixes, etc.
|
||||
|
@ -1139,12 +1140,17 @@ func (e *userspaceEngine) LinkChange(isExpensive bool) {
|
|||
cur.IsExpensive = isExpensive
|
||||
needRebind, linkChangeCallback := e.setLinkState(cur)
|
||||
|
||||
if needRebind {
|
||||
e.logf("LinkChange: major, rebinding. New state: %+v", cur)
|
||||
up := cur.AnyInterfaceUp()
|
||||
if !up {
|
||||
e.logf("LinkChange: all links down; pausing: %v", cur)
|
||||
} else if needRebind {
|
||||
e.logf("LinkChange: major, rebinding. New state: %v", cur)
|
||||
} else {
|
||||
e.logf("LinkChange: minor")
|
||||
}
|
||||
|
||||
e.magicConn.SetNetworkUp(up)
|
||||
|
||||
why := "link-change-minor"
|
||||
if needRebind {
|
||||
why = "link-change-major"
|
||||
|
|
Loading…
Reference in New Issue