diff --git a/cmd/tailscale/cli/debug.go b/cmd/tailscale/cli/debug.go index f6aafd883..7873a65de 100644 --- a/cmd/tailscale/cli/debug.go +++ b/cmd/tailscale/cli/debug.go @@ -16,10 +16,15 @@ import ( "net/http/httptrace" "net/url" "os" + "time" "github.com/peterbourgon/ff/v2/ffcli" + "tailscale.com/derp/derphttp" + "tailscale.com/derp/derpmap" "tailscale.com/net/interfaces" "tailscale.com/net/tshttpproxy" + "tailscale.com/tailcfg" + "tailscale.com/types/key" "tailscale.com/wgengine/monitor" ) @@ -30,19 +35,24 @@ var debugCmd = &ffcli.Command{ fs := flag.NewFlagSet("debug", flag.ExitOnError) fs.BoolVar(&debugArgs.monitor, "monitor", false, "If true, run link monitor forever. Precludes all other options.") fs.StringVar(&debugArgs.getURL, "get-url", "", "If non-empty, fetch provided URL.") + fs.StringVar(&debugArgs.derpCheck, "derp", "", "if non-empty, test a DERP ping via named region code") return fs })(), } var debugArgs struct { - monitor bool - getURL string + monitor bool + getURL string + derpCheck string } func runDebug(ctx context.Context, args []string) error { if len(args) > 0 { return errors.New("unknown arguments") } + if debugArgs.derpCheck != "" { + return checkDerp(ctx, debugArgs.derpCheck) + } if debugArgs.monitor { return runMonitor(ctx) } @@ -122,3 +132,44 @@ func getURL(ctx context.Context, urlStr string) error { defer res.Body.Close() return res.Write(os.Stdout) } + +func checkDerp(ctx context.Context, derpRegion string) error { + dmap := derpmap.Prod() + getRegion := func() *tailcfg.DERPRegion { + for _, r := range dmap.Regions { + if r.RegionCode == derpRegion { + return r + } + } + for _, r := range dmap.Regions { + log.Printf("Known region: %q", r.RegionCode) + } + log.Fatalf("unknown region %q", derpRegion) + panic("unreachable") + } + + priv1 := key.NewPrivate() + priv2 := key.NewPrivate() + + c1 := derphttp.NewRegionClient(priv1, log.Printf, getRegion) + c2 := derphttp.NewRegionClient(priv2, log.Printf, getRegion) + + c2.NotePreferred(true) // just to open it + + m, err := c2.Recv() + log.Printf("c2 got %T, %v", m, err) + + t0 := time.Now() + if err := c1.Send(priv2.Public(), []byte("hello")); err != nil { + return err + } + fmt.Println(time.Since(t0)) + + m, err = c2.Recv() + log.Printf("c2 got %T, %v", m, err) + if err != nil { + return err + } + log.Printf("ok") + return err +}