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 <bradfitz@tailscale.com>
pull/10276/head
Brad Fitzpatrick 7 months ago committed by Brad Fitzpatrick
parent 2ff54f9d12
commit 3bd382f369

@ -129,6 +129,16 @@ var debugCmd = &ffcli.Command{
Exec: localAPIAction("rebind"), Exec: localAPIAction("rebind"),
ShortHelp: "force a magicsock 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", Name: "break-tcp-conns",
Exec: localAPIAction("break-tcp-conns"), Exec: localAPIAction("break-tcp-conns"),

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

@ -778,7 +778,7 @@ func (h *peerAPIHandler) handleServeMagicsock(w http.ResponseWriter, r *http.Req
http.Error(w, "denied; no debug access", http.StatusForbidden) http.Error(w, "denied; no debug access", http.StatusForbidden)
return 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) { func (h *peerAPIHandler) handleServeMetrics(w http.ResponseWriter, r *http.Request) {

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

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

@ -270,6 +270,7 @@ type Conn struct {
privateKey key.NodePrivate // WireGuard private key for this node privateKey key.NodePrivate // WireGuard private key for this node
everHadKey bool // whether we ever had a non-zero private key everHadKey bool // whether we ever had a non-zero private key
myDerp int // nearest DERP region ID; 0 means none/unknown 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 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 activeDerp map[int]activeDerp // DERP regionID -> connection to a node in that region
prevDerp map[int]*syncs.WaitGroupChan prevDerp map[int]*syncs.WaitGroupChan
@ -2202,7 +2203,7 @@ func (c *Conn) goroutinesRunningLocked() bool {
} }
func (c *Conn) shouldDoPeriodicReSTUNLocked() bool { func (c *Conn) shouldDoPeriodicReSTUNLocked() bool {
if c.networkDown() { if c.networkDown() || c.homeless {
return false return false
} }
if len(c.peerSet) == 0 || c.privateKey.IsZero() { if len(c.peerSet) == 0 || c.privateKey.IsZero() {
@ -2686,6 +2687,24 @@ func (c *Conn) SetStatistics(stats *connstats.Statistics) {
c.stats.Store(stats) 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 ( const (
// sessionActiveTimeout is how long since the last activity we // sessionActiveTimeout is how long since the last activity we
// try to keep an established endpoint peering alive. // try to keep an established endpoint peering alive.

Loading…
Cancel
Save