mirror of
https://github.com/tailscale/tailscale.git
synced 2025-08-12 05:37:32 +00:00
cmd/tailscale, cmd/tailscaled: move portmapper debugging into tailscale CLI
The debug flag on tailscaled isn't available in the macOS App Store build, since we don't have a tailscaled binary; move it to the 'tailscale debug' CLI that is available on all platforms instead, accessed over LocalAPI. Updates #7377 Signed-off-by: Andrew Dunham <andrew@du.nham.ca> Change-Id: I47bffe4461e036fab577c2e51e173f4003592ff7
This commit is contained in:
@@ -249,7 +249,7 @@ func (d *TestIGD) handlePCPQuery(pkt []byte, src netip.AddrPort) {
|
||||
|
||||
func newTestClient(t *testing.T, igd *TestIGD) *Client {
|
||||
var c *Client
|
||||
c = NewClient(t.Logf, func() {
|
||||
c = NewClient(t.Logf, nil, func() {
|
||||
t.Logf("port map changed")
|
||||
t.Logf("have mapping: %v", c.HaveMapping())
|
||||
})
|
||||
|
@@ -28,16 +28,18 @@ import (
|
||||
"tailscale.com/util/clientmetric"
|
||||
)
|
||||
|
||||
// Debug knobs for "tailscaled debug --portmap".
|
||||
var (
|
||||
// DebugKnobs contains debug configuration that can be provided when creating a
|
||||
// Client. The zero value is valid for use.
|
||||
type DebugKnobs struct {
|
||||
// VerboseLogs tells the Client to print additional debug information
|
||||
// to its logger.
|
||||
VerboseLogs bool
|
||||
|
||||
// Disable* disables a specific service from mapping.
|
||||
|
||||
DisableUPnP bool
|
||||
DisablePMP bool
|
||||
DisablePCP bool
|
||||
)
|
||||
}
|
||||
|
||||
// References:
|
||||
//
|
||||
@@ -59,6 +61,7 @@ type Client struct {
|
||||
logf logger.Logf
|
||||
ipAndGateway func() (gw, ip netip.Addr, ok bool)
|
||||
onChange func() // or nil
|
||||
debug DebugKnobs
|
||||
testPxPPort uint16 // if non-zero, pxpPort to use for tests
|
||||
testUPnPPort uint16 // if non-zero, uPnPPort to use for tests
|
||||
|
||||
@@ -150,15 +153,22 @@ func (m *pmpMapping) Release(ctx context.Context) {
|
||||
|
||||
// NewClient returns a new portmapping client.
|
||||
//
|
||||
// The debug argument allows configuring the behaviour of the portmapper for
|
||||
// debugging; if nil, a sensible set of defaults will be used.
|
||||
//
|
||||
// The optional onChange argument specifies a func to run in a new
|
||||
// goroutine whenever the port mapping status has changed. If nil,
|
||||
// it doesn't make a callback.
|
||||
func NewClient(logf logger.Logf, onChange func()) *Client {
|
||||
return &Client{
|
||||
func NewClient(logf logger.Logf, debug *DebugKnobs, onChange func()) *Client {
|
||||
ret := &Client{
|
||||
logf: logf,
|
||||
ipAndGateway: interfaces.LikelyHomeRouterIP,
|
||||
onChange: onChange,
|
||||
}
|
||||
if debug != nil {
|
||||
ret.debug = *debug
|
||||
}
|
||||
return ret
|
||||
}
|
||||
|
||||
// SetGatewayLookupFunc set the func that returns the machine's default gateway IP, and
|
||||
@@ -407,7 +417,7 @@ var wildcardIP = netip.MustParseAddr("0.0.0.0")
|
||||
// If no mapping is available, the error will be of type
|
||||
// NoMappingError; see IsNoMappingError.
|
||||
func (c *Client) createOrGetMapping(ctx context.Context) (external netip.AddrPort, err error) {
|
||||
if DisableUPnP && DisablePCP && DisablePMP {
|
||||
if c.debug.DisableUPnP && c.debug.DisablePCP && c.debug.DisablePMP {
|
||||
return netip.AddrPort{}, NoMappingError{ErrNoPortMappingServices}
|
||||
}
|
||||
gw, myIP, ok := c.gatewayAndSelfIP()
|
||||
@@ -437,7 +447,7 @@ func (c *Client) createOrGetMapping(ctx context.Context) (external netip.AddrPor
|
||||
prevPort = m.External().Port()
|
||||
}
|
||||
|
||||
if DisablePCP && DisablePMP {
|
||||
if c.debug.DisablePCP && c.debug.DisablePMP {
|
||||
c.mu.Unlock()
|
||||
if external, ok := c.getUPnPPortMapping(ctx, gw, internalAddr, prevPort); ok {
|
||||
return external, nil
|
||||
@@ -486,7 +496,7 @@ func (c *Client) createOrGetMapping(ctx context.Context) (external netip.AddrPor
|
||||
|
||||
pxpAddr := netip.AddrPortFrom(gw, c.pxpPort())
|
||||
|
||||
preferPCP := !DisablePCP && (DisablePMP || (!haveRecentPMP && haveRecentPCP))
|
||||
preferPCP := !c.debug.DisablePCP && (c.debug.DisablePMP || (!haveRecentPMP && haveRecentPCP))
|
||||
|
||||
// Create a mapping, defaulting to PMP unless only PCP was seen recently.
|
||||
if preferPCP {
|
||||
@@ -712,19 +722,19 @@ func (c *Client) Probe(ctx context.Context) (res ProbeResult, err error) {
|
||||
// https://github.com/tailscale/tailscale/issues/1001
|
||||
if c.sawPMPRecently() {
|
||||
res.PMP = true
|
||||
} else if !DisablePMP {
|
||||
} else if !c.debug.DisablePMP {
|
||||
metricPMPSent.Add(1)
|
||||
uc.WriteToUDPAddrPort(pmpReqExternalAddrPacket, pxpAddr)
|
||||
}
|
||||
if c.sawPCPRecently() {
|
||||
res.PCP = true
|
||||
} else if !DisablePCP {
|
||||
} else if !c.debug.DisablePCP {
|
||||
metricPCPSent.Add(1)
|
||||
uc.WriteToUDPAddrPort(pcpAnnounceRequest(myIP), pxpAddr)
|
||||
}
|
||||
if c.sawUPnPRecently() {
|
||||
res.UPnP = true
|
||||
} else if !DisableUPnP {
|
||||
} else if !c.debug.DisableUPnP {
|
||||
// Strictly speaking, you discover UPnP services by sending an
|
||||
// SSDP query (which uPnPPacket is) to udp/1900 on the SSDP
|
||||
// multicast address, and then get a flood of responses back
|
||||
|
@@ -16,7 +16,7 @@ func TestCreateOrGetMapping(t *testing.T) {
|
||||
if v, _ := strconv.ParseBool(os.Getenv("HIT_NETWORK")); !v {
|
||||
t.Skip("skipping test without HIT_NETWORK=1")
|
||||
}
|
||||
c := NewClient(t.Logf, nil)
|
||||
c := NewClient(t.Logf, nil, nil)
|
||||
defer c.Close()
|
||||
c.SetLocalPort(1234)
|
||||
for i := 0; i < 2; i++ {
|
||||
@@ -32,7 +32,7 @@ func TestClientProbe(t *testing.T) {
|
||||
if v, _ := strconv.ParseBool(os.Getenv("HIT_NETWORK")); !v {
|
||||
t.Skip("skipping test without HIT_NETWORK=1")
|
||||
}
|
||||
c := NewClient(t.Logf, nil)
|
||||
c := NewClient(t.Logf, nil, nil)
|
||||
defer c.Close()
|
||||
for i := 0; i < 3; i++ {
|
||||
if i > 0 {
|
||||
@@ -47,7 +47,7 @@ func TestClientProbeThenMap(t *testing.T) {
|
||||
if v, _ := strconv.ParseBool(os.Getenv("HIT_NETWORK")); !v {
|
||||
t.Skip("skipping test without HIT_NETWORK=1")
|
||||
}
|
||||
c := NewClient(t.Logf, nil)
|
||||
c := NewClient(t.Logf, nil, nil)
|
||||
defer c.Close()
|
||||
c.SetLocalPort(1234)
|
||||
res, err := c.Probe(context.Background())
|
||||
|
@@ -152,8 +152,8 @@ func addAnyPortMapping(
|
||||
//
|
||||
// The provided ctx is not retained in the returned upnpClient, but
|
||||
// its associated HTTP client is (if set via goupnp.WithHTTPClient).
|
||||
func getUPnPClient(ctx context.Context, logf logger.Logf, gw netip.Addr, meta uPnPDiscoResponse) (client upnpClient, err error) {
|
||||
if controlknobs.DisableUPnP() || DisableUPnP {
|
||||
func getUPnPClient(ctx context.Context, logf logger.Logf, debug DebugKnobs, gw netip.Addr, meta uPnPDiscoResponse) (client upnpClient, err error) {
|
||||
if controlknobs.DisableUPnP() || debug.DisableUPnP {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
@@ -161,7 +161,7 @@ func getUPnPClient(ctx context.Context, logf logger.Logf, gw netip.Addr, meta uP
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
if VerboseLogs {
|
||||
if debug.VerboseLogs {
|
||||
logf("fetching %v", meta.Location)
|
||||
}
|
||||
u, err := url.Parse(meta.Location)
|
||||
@@ -237,9 +237,10 @@ func (c *Client) getUPnPPortMapping(
|
||||
internal netip.AddrPort,
|
||||
prevPort uint16,
|
||||
) (external netip.AddrPort, ok bool) {
|
||||
if controlknobs.DisableUPnP() || DisableUPnP {
|
||||
if controlknobs.DisableUPnP() || c.debug.DisableUPnP {
|
||||
return netip.AddrPort{}, false
|
||||
}
|
||||
|
||||
now := time.Now()
|
||||
upnp := &upnpMapping{
|
||||
gw: gw,
|
||||
@@ -257,8 +258,8 @@ func (c *Client) getUPnPPortMapping(
|
||||
client = oldMapping.client
|
||||
} else {
|
||||
ctx := goupnp.WithHTTPClient(ctx, httpClient)
|
||||
client, err = getUPnPClient(ctx, c.logf, gw, meta)
|
||||
if VerboseLogs {
|
||||
client, err = getUPnPClient(ctx, c.logf, c.debug, gw, meta)
|
||||
if c.debug.VerboseLogs {
|
||||
c.logf("getUPnPClient: %T, %v", client, err)
|
||||
}
|
||||
if err != nil {
|
||||
@@ -278,15 +279,15 @@ func (c *Client) getUPnPPortMapping(
|
||||
internal.Addr().String(),
|
||||
time.Second*pmpMapLifetimeSec,
|
||||
)
|
||||
if VerboseLogs {
|
||||
c.logf("addAnyPortMapping: %v, %v", newPort, err)
|
||||
if c.debug.VerboseLogs {
|
||||
c.logf("addAnyPortMapping: %v, err=%q", newPort, err)
|
||||
}
|
||||
if err != nil {
|
||||
return netip.AddrPort{}, false
|
||||
}
|
||||
// TODO cache this ip somewhere?
|
||||
extIP, err := client.GetExternalIPAddress(ctx)
|
||||
if VerboseLogs {
|
||||
if c.debug.VerboseLogs {
|
||||
c.logf("client.GetExternalIPAddress: %v, %v", extIP, err)
|
||||
}
|
||||
if err != nil {
|
||||
|
@@ -112,7 +112,7 @@ func TestGetUPnPClient(t *testing.T) {
|
||||
gw, _ := netip.AddrFromSlice(ts.Listener.Addr().(*net.TCPAddr).IP)
|
||||
gw = gw.Unmap()
|
||||
var logBuf tstest.MemLogger
|
||||
c, err := getUPnPClient(context.Background(), logBuf.Logf, gw, uPnPDiscoResponse{
|
||||
c, err := getUPnPClient(context.Background(), logBuf.Logf, DebugKnobs{}, gw, uPnPDiscoResponse{
|
||||
Location: ts.URL + "/rootDesc.xml",
|
||||
})
|
||||
if err != nil {
|
||||
|
Reference in New Issue
Block a user