Просмотр исходного кода

wgengine/magicsock: add DERP homeless debug mode for testing

In DERP homeless mode, a DERP home connection is not sought or
maintained and the local node is not reachable.

Updates #3363
Updates tailscale/corp#396

Change-Id: Ibc30488ac2e3cfe4810733b96c2c9f10a51b8331
Signed-off-by: Brad Fitzpatrick <[email protected]>
Brad Fitzpatrick 2 лет назад
Родитель
Сommit
3bd382f369

+ 10 - 0
cmd/tailscale/cli/debug.go

@@ -129,6 +129,16 @@ var debugCmd = &ffcli.Command{
 			Exec:      localAPIAction("rebind"),
 			ShortHelp: "force a magicsock rebind",
 		},
+		{
+			Name:      "derp-set-homeless",
+			Exec:      localAPIAction("derp-set-homeless"),
+			ShortHelp: "enable DERP homeless mode (breaks reachablility)",
+		},
+		{
+			Name:      "derp-unset-homeless",
+			Exec:      localAPIAction("derp-unset-homeless"),
+			ShortHelp: "disable DERP homeless mode",
+		},
 		{
 			Name:      "break-tcp-conns",
 			Exec:      localAPIAction("break-tcp-conns"),

+ 14 - 13
ipn/ipnlocal/local.go

@@ -449,7 +449,7 @@ func (b *LocalBackend) SetComponentDebugLogging(component string, until time.Tim
 	var setEnabled func(bool)
 	switch component {
 	case "magicsock":
-		setEnabled = b.magicConn().SetDebugLoggingEnabled
+		setEnabled = b.MagicConn().SetDebugLoggingEnabled
 	case "sockstats":
 		if b.sockstatLogger != nil {
 			setEnabled = func(v bool) {
@@ -1181,7 +1181,7 @@ func (b *LocalBackend) SetControlClientStatus(c controlclient.Client, st control
 		}
 
 		b.e.SetNetworkMap(st.NetMap)
-		b.magicConn().SetDERPMap(st.NetMap.DERPMap)
+		b.MagicConn().SetDERPMap(st.NetMap.DERPMap)
 
 		// Update our cached DERP map
 		dnsfallback.UpdateCache(st.NetMap.DERPMap, b.logf)
@@ -1204,7 +1204,7 @@ var _ controlclient.NetmapDeltaUpdater = (*LocalBackend)(nil)
 
 // UpdateNetmapDelta implements controlclient.NetmapDeltaUpdater.
 func (b *LocalBackend) UpdateNetmapDelta(muts []netmap.NodeMutation) (handled bool) {
-	if !b.magicConn().UpdateNetmapDelta(muts) {
+	if !b.MagicConn().UpdateNetmapDelta(muts) {
 		return false
 	}
 
@@ -1624,7 +1624,7 @@ func (b *LocalBackend) Start(opts ipn.Options) error {
 		})
 	}
 
-	discoPublic := b.magicConn().DiscoPublicKey()
+	discoPublic := b.MagicConn().DiscoPublicKey()
 
 	var err error
 
@@ -1703,7 +1703,7 @@ func (b *LocalBackend) Start(opts ipn.Options) error {
 	}
 	cc.SetTKAHead(tkaHead)
 
-	b.magicConn().SetNetInfoCallback(b.setNetInfo)
+	b.MagicConn().SetNetInfoCallback(b.setNetInfo)
 
 	blid := b.backendLogID.String()
 	b.logf("Backend: logs: be:%v fe:%v", blid, opts.FrontendLogID)
@@ -2241,7 +2241,7 @@ func (b *LocalBackend) DebugForceNetmapUpdate() {
 	nm := b.netMap
 	b.e.SetNetworkMap(nm)
 	if nm != nil {
-		b.magicConn().SetDERPMap(nm.DERPMap)
+		b.MagicConn().SetDERPMap(nm.DERPMap)
 	}
 	b.setNetMapLocked(nm)
 }
@@ -3071,7 +3071,7 @@ func (b *LocalBackend) setPrefsLockedOnEntry(caller string, newp *ipn.Prefs) ipn
 	}
 
 	if netMap != nil {
-		b.magicConn().SetDERPMap(netMap.DERPMap)
+		b.MagicConn().SetDERPMap(netMap.DERPMap)
 	}
 
 	if !oldp.WantRunning() && newp.WantRunning {
@@ -4378,7 +4378,7 @@ func (b *LocalBackend) setNetMapLocked(nm *netmap.NetworkMap) {
 	}
 	b.capFileSharing = fs
 
-	b.magicConn().SetSilentDisco(b.ControlKnobs().SilentDisco.Load())
+	b.MagicConn().SetSilentDisco(b.ControlKnobs().SilentDisco.Load())
 
 	b.setDebugLogsByCapabilityLocked(nm)
 
@@ -5073,12 +5073,12 @@ func peerCanProxyDNS(p tailcfg.NodeView) bool {
 }
 
 func (b *LocalBackend) DebugRebind() error {
-	b.magicConn().Rebind()
+	b.MagicConn().Rebind()
 	return nil
 }
 
 func (b *LocalBackend) DebugReSTUN() error {
-	b.magicConn().ReSTUN("explicit-debug")
+	b.MagicConn().ReSTUN("explicit-debug")
 	return nil
 }
 
@@ -5087,7 +5087,8 @@ func (b *LocalBackend) ControlKnobs() *controlknobs.Knobs {
 	return b.sys.ControlKnobs()
 }
 
-func (b *LocalBackend) magicConn() *magicsock.Conn {
+// MagicConn returns the backend's *magicsock.Conn.
+func (b *LocalBackend) MagicConn() *magicsock.Conn {
 	return b.sys.MagicSock.Get()
 }
 
@@ -5532,7 +5533,7 @@ func (b *LocalBackend) GetPeerEndpointChanges(ctx context.Context, ip netip.Addr
 	}
 	peer := pip.Node
 
-	chs, err := b.magicConn().GetEndpointChanges(peer)
+	chs, err := b.MagicConn().GetEndpointChanges(peer)
 	if err != nil {
 		return nil, fmt.Errorf("getting endpoint changes: %w", err)
 	}
@@ -5549,7 +5550,7 @@ func (b *LocalBackend) DebugBreakTCPConns() error {
 }
 
 func (b *LocalBackend) DebugBreakDERPConns() error {
-	return b.magicConn().DebugBreakDERPConns()
+	return b.MagicConn().DebugBreakDERPConns()
 }
 
 func (b *LocalBackend) pushSelfUpdateProgress(up ipnstate.UpdateProgress) {

+ 1 - 1
ipn/ipnlocal/peerapi.go

@@ -778,7 +778,7 @@ func (h *peerAPIHandler) handleServeMagicsock(w http.ResponseWriter, r *http.Req
 		http.Error(w, "denied; no debug access", http.StatusForbidden)
 		return
 	}
-	h.ps.b.magicConn().ServeHTTPDebug(w, r)
+	h.ps.b.MagicConn().ServeHTTPDebug(w, r)
 }
 
 func (h *peerAPIHandler) handleServeMetrics(w http.ResponseWriter, r *http.Request) {

+ 4 - 0
ipn/localapi/localapi.go

@@ -564,6 +564,10 @@ func (h *Handler) serveDebug(w http.ResponseWriter, r *http.Request) {
 	}
 	var err error
 	switch action {
+	case "derp-set-homeless":
+		h.b.MagicConn().SetHomeless(true)
+	case "derp-unset-homeless":
+		h.b.MagicConn().SetHomeless(false)
 	case "rebind":
 		err = h.b.DebugRebind()
 	case "restun":

+ 4 - 0
wgengine/magicsock/derp.go

@@ -144,6 +144,10 @@ func (c *Conn) setNearestDERP(derpNum int) (wantDERP bool) {
 		health.SetMagicSockDERPHome(0)
 		return false
 	}
+	if c.homeless {
+		c.myDerp = 0
+		return false
+	}
 	if derpNum == c.myDerp {
 		// No change.
 		return true

+ 20 - 1
wgengine/magicsock/magicsock.go

@@ -270,6 +270,7 @@ type Conn struct {
 	privateKey       key.NodePrivate               // WireGuard private key for this node
 	everHadKey       bool                          // whether we ever had a non-zero private key
 	myDerp           int                           // nearest DERP region ID; 0 means none/unknown
+	homeless         bool                          // if true, don't try to find & stay conneted to a DERP home (myDerp will stay 0)
 	derpStarted      chan struct{}                 // closed on first connection to DERP; for tests & cleaner Close
 	activeDerp       map[int]activeDerp            // DERP regionID -> connection to a node in that region
 	prevDerp         map[int]*syncs.WaitGroupChan
@@ -2202,7 +2203,7 @@ func (c *Conn) goroutinesRunningLocked() bool {
 }
 
 func (c *Conn) shouldDoPeriodicReSTUNLocked() bool {
-	if c.networkDown() {
+	if c.networkDown() || c.homeless {
 		return false
 	}
 	if len(c.peerSet) == 0 || c.privateKey.IsZero() {
@@ -2686,6 +2687,24 @@ func (c *Conn) SetStatistics(stats *connstats.Statistics) {
 	c.stats.Store(stats)
 }
 
+// SetHomeless sets whether magicsock should idle harder and not have a DERP
+// home connection active and not search for its nearest DERP home. In this
+// homeless mode, the node is unreachable by others.
+func (c *Conn) SetHomeless(v bool) {
+	c.mu.Lock()
+	defer c.mu.Unlock()
+	c.homeless = v
+
+	if v && c.myDerp != 0 {
+		oldHome := c.myDerp
+		c.myDerp = 0
+		c.closeDerpLocked(oldHome, "set-homeless")
+	}
+	if !v {
+		go c.updateEndpoints("set-homeless-disabled")
+	}
+}
+
 const (
 	// sessionActiveTimeout is how long since the last activity we
 	// try to keep an established endpoint peering alive.