Browse Source

cmd/tailscale: add debug commands to break connections

For testing reconnects.

Updates tailscale/corp#5761

Signed-off-by: Brad Fitzpatrick <[email protected]>
Brad Fitzpatrick 2 years ago
parent
commit
92fc9a01fa

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

@@ -127,6 +127,16 @@ var debugCmd = &ffcli.Command{
 			Exec:      localAPIAction("rebind"),
 			ShortHelp: "force a magicsock rebind",
 		},
+		{
+			Name:      "break-tcp-conns",
+			Exec:      localAPIAction("break-tcp-conns"),
+			ShortHelp: "break any open TCP connections from the daemon",
+		},
+		{
+			Name:      "break-derp-conns",
+			Exec:      localAPIAction("break-derp-conns"),
+			ShortHelp: "break any open DERP connections from the daemon",
+		},
 		{
 			Name:      "prefs",
 			Exec:      runPrefs,

+ 30 - 0
ipn/ipnlocal/breaktcp_darwin.go

@@ -0,0 +1,30 @@
+// Copyright (c) Tailscale Inc & AUTHORS
+// SPDX-License-Identifier: BSD-3-Clause
+
+package ipnlocal
+
+import (
+	"log"
+
+	"golang.org/x/sys/unix"
+)
+
+func init() {
+	breakTCPConns = breakTCPConnsDarwin
+}
+
+func breakTCPConnsDarwin() error {
+	var matched int
+	for fd := 0; fd < 1000; fd++ {
+		_, err := unix.GetsockoptTCPConnectionInfo(fd, unix.IPPROTO_TCP, unix.TCP_CONNECTION_INFO)
+		if err == nil {
+			matched++
+			err = unix.Close(fd)
+			log.Printf("debug: closed TCP fd %v: %v", fd, err)
+		}
+	}
+	if matched == 0 {
+		log.Printf("debug: no TCP connections found")
+	}
+	return nil
+}

+ 30 - 0
ipn/ipnlocal/breaktcp_linux.go

@@ -0,0 +1,30 @@
+// Copyright (c) Tailscale Inc & AUTHORS
+// SPDX-License-Identifier: BSD-3-Clause
+
+package ipnlocal
+
+import (
+	"log"
+
+	"golang.org/x/sys/unix"
+)
+
+func init() {
+	breakTCPConns = breakTCPConnsLinux
+}
+
+func breakTCPConnsLinux() error {
+	var matched int
+	for fd := 0; fd < 1000; fd++ {
+		_, err := unix.GetsockoptTCPInfo(fd, unix.IPPROTO_TCP, unix.TCP_INFO)
+		if err == nil {
+			matched++
+			err = unix.Close(fd)
+			log.Printf("debug: closed TCP fd %v: %v", fd, err)
+		}
+	}
+	if matched == 0 {
+		log.Printf("debug: no TCP connections found")
+	}
+	return nil
+}

+ 17 - 0
ipn/ipnlocal/local.go

@@ -5026,3 +5026,20 @@ func (b *LocalBackend) GetPeerEndpointChanges(ctx context.Context, ip netip.Addr
 	}
 	return chs, nil
 }
+
+var breakTCPConns func() error
+
+func (b *LocalBackend) DebugBreakTCPConns() error {
+	if breakTCPConns == nil {
+		return errors.New("TCP connection breaking not available on this platform")
+	}
+	return breakTCPConns()
+}
+
+func (b *LocalBackend) DebugBreakDERPConns() error {
+	mc, err := b.magicConn()
+	if err != nil {
+		return err
+	}
+	return mc.DebugBreakDERPConns()
+}

+ 4 - 1
ipn/localapi/localapi.go

@@ -559,7 +559,10 @@ func (h *Handler) serveDebug(w http.ResponseWriter, r *http.Request) {
 			break
 		}
 		h.b.DebugNotify(n)
-
+	case "break-tcp-conns":
+		err = h.b.DebugBreakTCPConns()
+	case "break-derp-conns":
+		err = h.b.DebugBreakDERPConns()
 	case "":
 		err = fmt.Errorf("missing parameter 'action'")
 	default:

+ 13 - 0
wgengine/magicsock/derp.go

@@ -746,6 +746,19 @@ func (c *Conn) closeAllDerpLocked(why string) {
 	c.logActiveDerpLocked()
 }
 
+// DebugBreakDERPConns breaks all DERP connections for debug/testing reasons.
+func (c *Conn) DebugBreakDERPConns() error {
+	c.mu.Lock()
+	defer c.mu.Unlock()
+	if len(c.activeDerp) == 0 {
+		c.logf("magicsock: DebugBreakDERPConns: no active DERP connections")
+		return nil
+	}
+	c.closeAllDerpLocked("debug-break-derp")
+	c.startDerpHomeConnectLocked()
+	return nil
+}
+
 // maybeCloseDERPsOnRebind, in response to a rebind, closes all
 // DERP connections that don't have a local address in okayLocalIPs
 // and pings all those that do.