|
|
|
|
@ -14,6 +14,7 @@ import ( |
|
|
|
|
"io" |
|
|
|
|
"io/ioutil" |
|
|
|
|
"log" |
|
|
|
|
"net" |
|
|
|
|
"net/http" |
|
|
|
|
"net/http/httptrace" |
|
|
|
|
"net/url" |
|
|
|
|
@ -23,9 +24,11 @@ import ( |
|
|
|
|
"tailscale.com/derp/derphttp" |
|
|
|
|
"tailscale.com/ipn" |
|
|
|
|
"tailscale.com/net/interfaces" |
|
|
|
|
"tailscale.com/net/portmapper" |
|
|
|
|
"tailscale.com/net/tshttpproxy" |
|
|
|
|
"tailscale.com/tailcfg" |
|
|
|
|
"tailscale.com/types/key" |
|
|
|
|
"tailscale.com/types/logger" |
|
|
|
|
"tailscale.com/wgengine/monitor" |
|
|
|
|
) |
|
|
|
|
|
|
|
|
|
@ -33,6 +36,7 @@ var debugArgs struct { |
|
|
|
|
monitor bool |
|
|
|
|
getURL string |
|
|
|
|
derpCheck string |
|
|
|
|
portmap bool |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
var debugModeFunc = debugMode // so it can be addressable
|
|
|
|
|
@ -40,6 +44,7 @@ var debugModeFunc = debugMode // so it can be addressable |
|
|
|
|
func debugMode(args []string) error { |
|
|
|
|
fs := flag.NewFlagSet("debug", flag.ExitOnError) |
|
|
|
|
fs.BoolVar(&debugArgs.monitor, "monitor", false, "If true, run link monitor forever. Precludes all other options.") |
|
|
|
|
fs.BoolVar(&debugArgs.portmap, "portmap", false, "If true, run portmap debugging. 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") |
|
|
|
|
if err := fs.Parse(args); err != nil { |
|
|
|
|
@ -55,6 +60,9 @@ func debugMode(args []string) error { |
|
|
|
|
if debugArgs.monitor { |
|
|
|
|
return runMonitor(ctx) |
|
|
|
|
} |
|
|
|
|
if debugArgs.portmap { |
|
|
|
|
return debugPortmap(ctx) |
|
|
|
|
} |
|
|
|
|
if debugArgs.getURL != "" { |
|
|
|
|
return getURL(ctx, debugArgs.getURL) |
|
|
|
|
} |
|
|
|
|
@ -191,3 +199,63 @@ func checkDerp(ctx context.Context, derpRegion string) error { |
|
|
|
|
log.Printf("ok") |
|
|
|
|
return err |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
func debugPortmap(ctx context.Context) error { |
|
|
|
|
ctx, cancel := context.WithTimeout(ctx, 3*time.Second) |
|
|
|
|
defer cancel() |
|
|
|
|
|
|
|
|
|
done := make(chan bool, 1) |
|
|
|
|
|
|
|
|
|
var c *portmapper.Client |
|
|
|
|
logf := log.Printf |
|
|
|
|
c = portmapper.NewClient(logger.WithPrefix(logf, "portmapper: "), func() { |
|
|
|
|
logf("portmapping changed.") |
|
|
|
|
logf("have mapping: %v", c.HaveMapping()) |
|
|
|
|
|
|
|
|
|
if ext, ok := c.GetCachedMappingOrStartCreatingOne(); ok { |
|
|
|
|
logf("cb: mapping: %v", ext) |
|
|
|
|
select { |
|
|
|
|
case done <- true: |
|
|
|
|
default: |
|
|
|
|
} |
|
|
|
|
return |
|
|
|
|
} |
|
|
|
|
logf("cb: no mapping") |
|
|
|
|
}) |
|
|
|
|
linkMon, err := monitor.New(logger.WithPrefix(logf, "monitor: ")) |
|
|
|
|
if err != nil { |
|
|
|
|
return err |
|
|
|
|
} |
|
|
|
|
c.SetGatewayLookupFunc(linkMon.GatewayAndSelfIP) |
|
|
|
|
|
|
|
|
|
res, err := c.Probe(ctx) |
|
|
|
|
if err != nil { |
|
|
|
|
return fmt.Errorf("Probe: %v", err) |
|
|
|
|
} |
|
|
|
|
logf("Probe: %+v", res) |
|
|
|
|
|
|
|
|
|
if !res.PCP && !res.PMP && !res.UPnP { |
|
|
|
|
logf("no portmapping services available") |
|
|
|
|
return nil |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
uc, err := net.ListenPacket("udp", "0.0.0.0:0") |
|
|
|
|
if err != nil { |
|
|
|
|
return err |
|
|
|
|
} |
|
|
|
|
defer uc.Close() |
|
|
|
|
c.SetLocalPort(uint16(uc.LocalAddr().(*net.UDPAddr).Port)) |
|
|
|
|
|
|
|
|
|
if ext, ok := c.GetCachedMappingOrStartCreatingOne(); ok { |
|
|
|
|
logf("mapping: %v", ext) |
|
|
|
|
} else { |
|
|
|
|
logf("no mapping") |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
select { |
|
|
|
|
case <-done: |
|
|
|
|
return nil |
|
|
|
|
case <-ctx.Done(): |
|
|
|
|
return ctx.Err() |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|