summaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authorBrad Fitzpatrick <bradfitz@tailscale.com>2023-08-10 21:45:16 -0700
committerBrad Fitzpatrick <brad@danga.com>2023-08-11 06:37:26 -0700
commit92fc9a01fa2930e86175f2989ebdd83a9d5d94b4 (patch)
treee08bd83ed4e254485ca0f181397a6a1970bec26e
parent99e06d3544b6d48dc0482b76f63f58f4841a6d25 (diff)
downloadtailscale-92fc9a01fa2930e86175f2989ebdd83a9d5d94b4.tar.xz
tailscale-92fc9a01fa2930e86175f2989ebdd83a9d5d94b4.zip
cmd/tailscale: add debug commands to break connections
For testing reconnects. Updates tailscale/corp#5761 Signed-off-by: Brad Fitzpatrick <bradfitz@tailscale.com>
-rw-r--r--cmd/tailscale/cli/debug.go10
-rw-r--r--ipn/ipnlocal/breaktcp_darwin.go30
-rw-r--r--ipn/ipnlocal/breaktcp_linux.go30
-rw-r--r--ipn/ipnlocal/local.go17
-rw-r--r--ipn/localapi/localapi.go5
-rw-r--r--wgengine/magicsock/derp.go13
6 files changed, 104 insertions, 1 deletions
diff --git a/cmd/tailscale/cli/debug.go b/cmd/tailscale/cli/debug.go
index 851ab5a93..40152ca9e 100644
--- a/cmd/tailscale/cli/debug.go
+++ b/cmd/tailscale/cli/debug.go
@@ -128,6 +128,16 @@ var debugCmd = &ffcli.Command{
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,
ShortHelp: "print prefs",
diff --git a/ipn/ipnlocal/breaktcp_darwin.go b/ipn/ipnlocal/breaktcp_darwin.go
new file mode 100644
index 000000000..13566198c
--- /dev/null
+++ b/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
+}
diff --git a/ipn/ipnlocal/breaktcp_linux.go b/ipn/ipnlocal/breaktcp_linux.go
new file mode 100644
index 000000000..b82f65212
--- /dev/null
+++ b/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
+}
diff --git a/ipn/ipnlocal/local.go b/ipn/ipnlocal/local.go
index b37ca3194..1c2bff8f5 100644
--- a/ipn/ipnlocal/local.go
+++ b/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()
+}
diff --git a/ipn/localapi/localapi.go b/ipn/localapi/localapi.go
index 527519d5e..a3bda73f7 100644
--- a/ipn/localapi/localapi.go
+++ b/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:
diff --git a/wgengine/magicsock/derp.go b/wgengine/magicsock/derp.go
index 28d19daad..23ae90add 100644
--- a/wgengine/magicsock/derp.go
+++ b/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.