From 92fc9a01fa2930e86175f2989ebdd83a9d5d94b4 Mon Sep 17 00:00:00 2001 From: Brad Fitzpatrick Date: Thu, 10 Aug 2023 21:45:16 -0700 Subject: [PATCH] cmd/tailscale: add debug commands to break connections For testing reconnects. Updates tailscale/corp#5761 Signed-off-by: Brad Fitzpatrick --- cmd/tailscale/cli/debug.go | 10 ++++++++++ ipn/ipnlocal/breaktcp_darwin.go | 30 ++++++++++++++++++++++++++++++ ipn/ipnlocal/breaktcp_linux.go | 30 ++++++++++++++++++++++++++++++ ipn/ipnlocal/local.go | 17 +++++++++++++++++ ipn/localapi/localapi.go | 5 ++++- wgengine/magicsock/derp.go | 13 +++++++++++++ 6 files changed, 104 insertions(+), 1 deletion(-) create mode 100644 ipn/ipnlocal/breaktcp_darwin.go create mode 100644 ipn/ipnlocal/breaktcp_linux.go 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 @@ -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, 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.