mirror of
https://github.com/tailscale/tailscale.git
synced 2024-11-29 04:55:31 +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:
parent
3e71e0ef68
commit
3f8e8b04fd
@ -367,6 +367,34 @@ func (lc *LocalClient) DebugAction(ctx context.Context, action string) error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// DebugPortmap invokes the debug-portmap endpoint, and returns an
|
||||||
|
// io.ReadCloser that can be used to read the logs that are printed during this
|
||||||
|
// process.
|
||||||
|
func (lc *LocalClient) DebugPortmap(ctx context.Context, duration time.Duration, ty, gwSelf string) (io.ReadCloser, error) {
|
||||||
|
vals := make(url.Values)
|
||||||
|
vals.Set("duration", duration.String())
|
||||||
|
vals.Set("type", ty)
|
||||||
|
if gwSelf != "" {
|
||||||
|
vals.Set("gateway_and_self", gwSelf)
|
||||||
|
}
|
||||||
|
|
||||||
|
req, err := http.NewRequestWithContext(ctx, "GET", "http://"+apitype.LocalAPIHost+"/localapi/v0/debug-portmap?"+vals.Encode(), nil)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
res, err := lc.doLocalRequestNiceError(req)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if res.StatusCode != 200 {
|
||||||
|
body, _ := io.ReadAll(res.Body)
|
||||||
|
res.Body.Close()
|
||||||
|
return nil, fmt.Errorf("HTTP %s: %s", res.Status, body)
|
||||||
|
}
|
||||||
|
|
||||||
|
return res.Body, nil
|
||||||
|
}
|
||||||
|
|
||||||
// SetDevStoreKeyValue set a statestore key/value. It's only meant for development.
|
// SetDevStoreKeyValue set a statestore key/value. It's only meant for development.
|
||||||
// The schema (including when keys are re-read) is not a stable interface.
|
// The schema (including when keys are re-read) is not a stable interface.
|
||||||
func (lc *LocalClient) SetDevStoreKeyValue(ctx context.Context, key, value string) error {
|
func (lc *LocalClient) SetDevStoreKeyValue(ctx context.Context, key, value string) error {
|
||||||
|
@ -201,6 +201,18 @@
|
|||||||
return fs
|
return fs
|
||||||
})(),
|
})(),
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
Name: "portmap",
|
||||||
|
Exec: debugPortmap,
|
||||||
|
ShortHelp: "run portmap debugging debugging",
|
||||||
|
FlagSet: (func() *flag.FlagSet {
|
||||||
|
fs := newFlagSet("portmap")
|
||||||
|
fs.DurationVar(&debugPortmapArgs.duration, "duration", 5*time.Second, "timeout for port mapping")
|
||||||
|
fs.StringVar(&debugPortmapArgs.ty, "type", "", `portmap debug type (one of "", "pmp", "pcp", or "upnp")`)
|
||||||
|
fs.StringVar(&debugPortmapArgs.gwSelf, "gw-self", "", `override gateway and self IP (format: "gatewayIP/selfIP")`)
|
||||||
|
return fs
|
||||||
|
})(),
|
||||||
|
},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -789,3 +801,24 @@ func runCapture(ctx context.Context, args []string) error {
|
|||||||
_, err = io.Copy(f, stream)
|
_, err = io.Copy(f, stream)
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var debugPortmapArgs struct {
|
||||||
|
duration time.Duration
|
||||||
|
gwSelf string
|
||||||
|
ty string
|
||||||
|
}
|
||||||
|
|
||||||
|
func debugPortmap(ctx context.Context, args []string) error {
|
||||||
|
rc, err := localClient.DebugPortmap(ctx,
|
||||||
|
debugPortmapArgs.duration,
|
||||||
|
debugPortmapArgs.ty,
|
||||||
|
debugPortmapArgs.gwSelf,
|
||||||
|
)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
defer rc.Close()
|
||||||
|
|
||||||
|
_, err = io.Copy(os.Stdout, rc)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
@ -47,7 +47,7 @@
|
|||||||
func runNetcheck(ctx context.Context, args []string) error {
|
func runNetcheck(ctx context.Context, args []string) error {
|
||||||
c := &netcheck.Client{
|
c := &netcheck.Client{
|
||||||
UDPBindAddr: envknob.String("TS_DEBUG_NETCHECK_UDP_BIND"),
|
UDPBindAddr: envknob.String("TS_DEBUG_NETCHECK_UDP_BIND"),
|
||||||
PortMapper: portmapper.NewClient(logger.WithPrefix(log.Printf, "portmap: "), nil),
|
PortMapper: portmapper.NewClient(logger.WithPrefix(log.Printf, "portmap: "), nil, nil),
|
||||||
}
|
}
|
||||||
if netcheckArgs.verbose {
|
if netcheckArgs.verbose {
|
||||||
c.Logf = logger.WithPrefix(log.Printf, "netcheck: ")
|
c.Logf = logger.WithPrefix(log.Printf, "netcheck: ")
|
||||||
|
@ -14,24 +14,18 @@
|
|||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
"log"
|
"log"
|
||||||
"net"
|
|
||||||
"net/http"
|
"net/http"
|
||||||
"net/http/httptrace"
|
"net/http/httptrace"
|
||||||
"net/netip"
|
|
||||||
"net/url"
|
"net/url"
|
||||||
"os"
|
"os"
|
||||||
"strings"
|
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"tailscale.com/derp/derphttp"
|
"tailscale.com/derp/derphttp"
|
||||||
"tailscale.com/envknob"
|
|
||||||
"tailscale.com/ipn"
|
"tailscale.com/ipn"
|
||||||
"tailscale.com/net/interfaces"
|
"tailscale.com/net/interfaces"
|
||||||
"tailscale.com/net/portmapper"
|
|
||||||
"tailscale.com/net/tshttpproxy"
|
"tailscale.com/net/tshttpproxy"
|
||||||
"tailscale.com/tailcfg"
|
"tailscale.com/tailcfg"
|
||||||
"tailscale.com/types/key"
|
"tailscale.com/types/key"
|
||||||
"tailscale.com/types/logger"
|
|
||||||
"tailscale.com/wgengine/monitor"
|
"tailscale.com/wgengine/monitor"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -229,95 +223,5 @@ func checkDerp(ctx context.Context, derpRegion string) (err error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func debugPortmap(ctx context.Context) error {
|
func debugPortmap(ctx context.Context) error {
|
||||||
ctx, cancel := context.WithTimeout(ctx, 3*time.Second)
|
return fmt.Errorf("this flag has been deprecated in favour of 'tailscale debug portmap'")
|
||||||
defer cancel()
|
|
||||||
|
|
||||||
portmapper.VerboseLogs = true
|
|
||||||
switch envknob.String("TS_DEBUG_PORTMAP_TYPE") {
|
|
||||||
case "":
|
|
||||||
case "pmp":
|
|
||||||
portmapper.DisablePCP = true
|
|
||||||
portmapper.DisableUPnP = true
|
|
||||||
case "pcp":
|
|
||||||
portmapper.DisablePMP = true
|
|
||||||
portmapper.DisableUPnP = true
|
|
||||||
case "upnp":
|
|
||||||
portmapper.DisablePCP = true
|
|
||||||
portmapper.DisablePMP = true
|
|
||||||
default:
|
|
||||||
log.Fatalf("TS_DEBUG_PORTMAP_TYPE must be one of pmp,pcp,upnp")
|
|
||||||
}
|
|
||||||
|
|
||||||
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
|
|
||||||
}
|
|
||||||
|
|
||||||
gatewayAndSelfIP := func() (gw, self netip.Addr, ok bool) {
|
|
||||||
if v := os.Getenv("TS_DEBUG_GW_SELF"); strings.Contains(v, "/") {
|
|
||||||
i := strings.Index(v, "/")
|
|
||||||
gw = netip.MustParseAddr(v[:i])
|
|
||||||
self = netip.MustParseAddr(v[i+1:])
|
|
||||||
return gw, self, true
|
|
||||||
}
|
|
||||||
return linkMon.GatewayAndSelfIP()
|
|
||||||
}
|
|
||||||
|
|
||||||
c.SetGatewayLookupFunc(gatewayAndSelfIP)
|
|
||||||
|
|
||||||
gw, selfIP, ok := gatewayAndSelfIP()
|
|
||||||
if !ok {
|
|
||||||
logf("no gateway or self IP; %v", linkMon.InterfaceState())
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
logf("gw=%v; self=%v", gw, selfIP)
|
|
||||||
|
|
||||||
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))
|
|
||||||
|
|
||||||
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
|
|
||||||
}
|
|
||||||
|
|
||||||
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()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -35,6 +35,7 @@
|
|||||||
"tailscale.com/ipn/ipnstate"
|
"tailscale.com/ipn/ipnstate"
|
||||||
"tailscale.com/logtail"
|
"tailscale.com/logtail"
|
||||||
"tailscale.com/net/netutil"
|
"tailscale.com/net/netutil"
|
||||||
|
"tailscale.com/net/portmapper"
|
||||||
"tailscale.com/tailcfg"
|
"tailscale.com/tailcfg"
|
||||||
"tailscale.com/tka"
|
"tailscale.com/tka"
|
||||||
"tailscale.com/types/key"
|
"tailscale.com/types/key"
|
||||||
@ -44,6 +45,7 @@
|
|||||||
"tailscale.com/util/httpm"
|
"tailscale.com/util/httpm"
|
||||||
"tailscale.com/util/mak"
|
"tailscale.com/util/mak"
|
||||||
"tailscale.com/version"
|
"tailscale.com/version"
|
||||||
|
"tailscale.com/wgengine/monitor"
|
||||||
)
|
)
|
||||||
|
|
||||||
type localAPIHandler func(*Handler, http.ResponseWriter, *http.Request)
|
type localAPIHandler func(*Handler, http.ResponseWriter, *http.Request)
|
||||||
@ -68,6 +70,7 @@
|
|||||||
"debug-derp-region": (*Handler).serveDebugDERPRegion,
|
"debug-derp-region": (*Handler).serveDebugDERPRegion,
|
||||||
"debug-packet-filter-matches": (*Handler).serveDebugPacketFilterMatches,
|
"debug-packet-filter-matches": (*Handler).serveDebugPacketFilterMatches,
|
||||||
"debug-packet-filter-rules": (*Handler).serveDebugPacketFilterRules,
|
"debug-packet-filter-rules": (*Handler).serveDebugPacketFilterRules,
|
||||||
|
"debug-portmap": (*Handler).serveDebugPortmap,
|
||||||
"debug-capture": (*Handler).serveDebugCapture,
|
"debug-capture": (*Handler).serveDebugCapture,
|
||||||
"derpmap": (*Handler).serveDERPMap,
|
"derpmap": (*Handler).serveDERPMap,
|
||||||
"dev-set-state-store": (*Handler).serveDevSetStateStore,
|
"dev-set-state-store": (*Handler).serveDevSetStateStore,
|
||||||
@ -600,6 +603,135 @@ func (h *Handler) serveDebugPacketFilterMatches(w http.ResponseWriter, r *http.R
|
|||||||
enc.Encode(nm.PacketFilter)
|
enc.Encode(nm.PacketFilter)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (h *Handler) serveDebugPortmap(w http.ResponseWriter, r *http.Request) {
|
||||||
|
if !h.PermitWrite {
|
||||||
|
http.Error(w, "debug access denied", http.StatusForbidden)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
w.Header().Set("Content-Type", "text/plain")
|
||||||
|
|
||||||
|
dur, err := time.ParseDuration(r.FormValue("duration"))
|
||||||
|
if err != nil {
|
||||||
|
http.Error(w, err.Error(), http.StatusBadRequest)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
gwSelf := r.FormValue("gateway_and_self")
|
||||||
|
|
||||||
|
// Update portmapper debug flags
|
||||||
|
debugKnobs := &portmapper.DebugKnobs{VerboseLogs: true}
|
||||||
|
switch r.FormValue("type") {
|
||||||
|
case "":
|
||||||
|
case "pmp":
|
||||||
|
debugKnobs.DisablePCP = true
|
||||||
|
debugKnobs.DisableUPnP = true
|
||||||
|
case "pcp":
|
||||||
|
debugKnobs.DisablePMP = true
|
||||||
|
debugKnobs.DisableUPnP = true
|
||||||
|
case "upnp":
|
||||||
|
debugKnobs.DisablePCP = true
|
||||||
|
debugKnobs.DisablePMP = true
|
||||||
|
default:
|
||||||
|
http.Error(w, "unknown portmap debug type", http.StatusBadRequest)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
var logLock sync.Mutex
|
||||||
|
logf := func(format string, args ...any) {
|
||||||
|
if !strings.HasSuffix(format, "\n") {
|
||||||
|
format = format + "\n"
|
||||||
|
}
|
||||||
|
|
||||||
|
logLock.Lock()
|
||||||
|
defer logLock.Unlock()
|
||||||
|
|
||||||
|
// Write and flush each line to the client so that output is streamed
|
||||||
|
fmt.Fprintf(w, format, args...)
|
||||||
|
if f, ok := w.(http.Flusher); ok {
|
||||||
|
f.Flush()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ctx, cancel := context.WithTimeout(r.Context(), dur)
|
||||||
|
defer cancel()
|
||||||
|
|
||||||
|
done := make(chan bool, 1)
|
||||||
|
|
||||||
|
var c *portmapper.Client
|
||||||
|
c = portmapper.NewClient(logger.WithPrefix(logf, "portmapper: "), debugKnobs, 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 {
|
||||||
|
logf("error creating monitor: %v", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
gatewayAndSelfIP := func() (gw, self netip.Addr, ok bool) {
|
||||||
|
if a, b, ok := strings.Cut(gwSelf, "/"); ok {
|
||||||
|
gw = netip.MustParseAddr(a)
|
||||||
|
self = netip.MustParseAddr(b)
|
||||||
|
return gw, self, true
|
||||||
|
}
|
||||||
|
return linkMon.GatewayAndSelfIP()
|
||||||
|
}
|
||||||
|
|
||||||
|
c.SetGatewayLookupFunc(gatewayAndSelfIP)
|
||||||
|
|
||||||
|
gw, selfIP, ok := gatewayAndSelfIP()
|
||||||
|
if !ok {
|
||||||
|
logf("no gateway or self IP; %v", linkMon.InterfaceState())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
logf("gw=%v; self=%v", gw, selfIP)
|
||||||
|
|
||||||
|
uc, err := net.ListenPacket("udp", "0.0.0.0:0")
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
defer uc.Close()
|
||||||
|
c.SetLocalPort(uint16(uc.LocalAddr().(*net.UDPAddr).Port))
|
||||||
|
|
||||||
|
res, err := c.Probe(ctx)
|
||||||
|
if err != nil {
|
||||||
|
logf("error in Probe: %v", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
logf("Probe: %+v", res)
|
||||||
|
|
||||||
|
if !res.PCP && !res.PMP && !res.UPnP {
|
||||||
|
logf("no portmapping services available")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if ext, ok := c.GetCachedMappingOrStartCreatingOne(); ok {
|
||||||
|
logf("mapping: %v", ext)
|
||||||
|
} else {
|
||||||
|
logf("no mapping")
|
||||||
|
}
|
||||||
|
|
||||||
|
select {
|
||||||
|
case <-done:
|
||||||
|
case <-ctx.Done():
|
||||||
|
if r.Context().Err() == nil {
|
||||||
|
logf("serveDebugPortmap: context done: %v", ctx.Err())
|
||||||
|
} else {
|
||||||
|
h.logf("serveDebugPortmap: context done: %v", ctx.Err())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func (h *Handler) serveComponentDebugLogging(w http.ResponseWriter, r *http.Request) {
|
func (h *Handler) serveComponentDebugLogging(w http.ResponseWriter, r *http.Request) {
|
||||||
if !h.PermitWrite {
|
if !h.PermitWrite {
|
||||||
http.Error(w, "debug access denied", http.StatusForbidden)
|
http.Error(w, "debug access denied", http.StatusForbidden)
|
||||||
|
@ -249,7 +249,7 @@ func (d *TestIGD) handlePCPQuery(pkt []byte, src netip.AddrPort) {
|
|||||||
|
|
||||||
func newTestClient(t *testing.T, igd *TestIGD) *Client {
|
func newTestClient(t *testing.T, igd *TestIGD) *Client {
|
||||||
var c *Client
|
var c *Client
|
||||||
c = NewClient(t.Logf, func() {
|
c = NewClient(t.Logf, nil, func() {
|
||||||
t.Logf("port map changed")
|
t.Logf("port map changed")
|
||||||
t.Logf("have mapping: %v", c.HaveMapping())
|
t.Logf("have mapping: %v", c.HaveMapping())
|
||||||
})
|
})
|
||||||
|
@ -28,16 +28,18 @@
|
|||||||
"tailscale.com/util/clientmetric"
|
"tailscale.com/util/clientmetric"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Debug knobs for "tailscaled debug --portmap".
|
// DebugKnobs contains debug configuration that can be provided when creating a
|
||||||
var (
|
// 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
|
VerboseLogs bool
|
||||||
|
|
||||||
// Disable* disables a specific service from mapping.
|
// Disable* disables a specific service from mapping.
|
||||||
|
|
||||||
DisableUPnP bool
|
DisableUPnP bool
|
||||||
DisablePMP bool
|
DisablePMP bool
|
||||||
DisablePCP bool
|
DisablePCP bool
|
||||||
)
|
}
|
||||||
|
|
||||||
// References:
|
// References:
|
||||||
//
|
//
|
||||||
@ -59,6 +61,7 @@ type Client struct {
|
|||||||
logf logger.Logf
|
logf logger.Logf
|
||||||
ipAndGateway func() (gw, ip netip.Addr, ok bool)
|
ipAndGateway func() (gw, ip netip.Addr, ok bool)
|
||||||
onChange func() // or nil
|
onChange func() // or nil
|
||||||
|
debug DebugKnobs
|
||||||
testPxPPort uint16 // if non-zero, pxpPort to use for tests
|
testPxPPort uint16 // if non-zero, pxpPort to use for tests
|
||||||
testUPnPPort uint16 // if non-zero, uPnPPort 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.
|
// 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
|
// The optional onChange argument specifies a func to run in a new
|
||||||
// goroutine whenever the port mapping status has changed. If nil,
|
// goroutine whenever the port mapping status has changed. If nil,
|
||||||
// it doesn't make a callback.
|
// it doesn't make a callback.
|
||||||
func NewClient(logf logger.Logf, onChange func()) *Client {
|
func NewClient(logf logger.Logf, debug *DebugKnobs, onChange func()) *Client {
|
||||||
return &Client{
|
ret := &Client{
|
||||||
logf: logf,
|
logf: logf,
|
||||||
ipAndGateway: interfaces.LikelyHomeRouterIP,
|
ipAndGateway: interfaces.LikelyHomeRouterIP,
|
||||||
onChange: onChange,
|
onChange: onChange,
|
||||||
}
|
}
|
||||||
|
if debug != nil {
|
||||||
|
ret.debug = *debug
|
||||||
|
}
|
||||||
|
return ret
|
||||||
}
|
}
|
||||||
|
|
||||||
// SetGatewayLookupFunc set the func that returns the machine's default gateway IP, and
|
// SetGatewayLookupFunc set the func that returns the machine's default gateway IP, and
|
||||||
@ -407,7 +417,7 @@ func (c *Client) createMapping() {
|
|||||||
// If no mapping is available, the error will be of type
|
// If no mapping is available, the error will be of type
|
||||||
// NoMappingError; see IsNoMappingError.
|
// NoMappingError; see IsNoMappingError.
|
||||||
func (c *Client) createOrGetMapping(ctx context.Context) (external netip.AddrPort, err error) {
|
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}
|
return netip.AddrPort{}, NoMappingError{ErrNoPortMappingServices}
|
||||||
}
|
}
|
||||||
gw, myIP, ok := c.gatewayAndSelfIP()
|
gw, myIP, ok := c.gatewayAndSelfIP()
|
||||||
@ -437,7 +447,7 @@ func (c *Client) createOrGetMapping(ctx context.Context) (external netip.AddrPor
|
|||||||
prevPort = m.External().Port()
|
prevPort = m.External().Port()
|
||||||
}
|
}
|
||||||
|
|
||||||
if DisablePCP && DisablePMP {
|
if c.debug.DisablePCP && c.debug.DisablePMP {
|
||||||
c.mu.Unlock()
|
c.mu.Unlock()
|
||||||
if external, ok := c.getUPnPPortMapping(ctx, gw, internalAddr, prevPort); ok {
|
if external, ok := c.getUPnPPortMapping(ctx, gw, internalAddr, prevPort); ok {
|
||||||
return external, nil
|
return external, nil
|
||||||
@ -486,7 +496,7 @@ func (c *Client) createOrGetMapping(ctx context.Context) (external netip.AddrPor
|
|||||||
|
|
||||||
pxpAddr := netip.AddrPortFrom(gw, c.pxpPort())
|
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.
|
// Create a mapping, defaulting to PMP unless only PCP was seen recently.
|
||||||
if preferPCP {
|
if preferPCP {
|
||||||
@ -712,19 +722,19 @@ func (c *Client) Probe(ctx context.Context) (res ProbeResult, err error) {
|
|||||||
// https://github.com/tailscale/tailscale/issues/1001
|
// https://github.com/tailscale/tailscale/issues/1001
|
||||||
if c.sawPMPRecently() {
|
if c.sawPMPRecently() {
|
||||||
res.PMP = true
|
res.PMP = true
|
||||||
} else if !DisablePMP {
|
} else if !c.debug.DisablePMP {
|
||||||
metricPMPSent.Add(1)
|
metricPMPSent.Add(1)
|
||||||
uc.WriteToUDPAddrPort(pmpReqExternalAddrPacket, pxpAddr)
|
uc.WriteToUDPAddrPort(pmpReqExternalAddrPacket, pxpAddr)
|
||||||
}
|
}
|
||||||
if c.sawPCPRecently() {
|
if c.sawPCPRecently() {
|
||||||
res.PCP = true
|
res.PCP = true
|
||||||
} else if !DisablePCP {
|
} else if !c.debug.DisablePCP {
|
||||||
metricPCPSent.Add(1)
|
metricPCPSent.Add(1)
|
||||||
uc.WriteToUDPAddrPort(pcpAnnounceRequest(myIP), pxpAddr)
|
uc.WriteToUDPAddrPort(pcpAnnounceRequest(myIP), pxpAddr)
|
||||||
}
|
}
|
||||||
if c.sawUPnPRecently() {
|
if c.sawUPnPRecently() {
|
||||||
res.UPnP = true
|
res.UPnP = true
|
||||||
} else if !DisableUPnP {
|
} else if !c.debug.DisableUPnP {
|
||||||
// Strictly speaking, you discover UPnP services by sending an
|
// Strictly speaking, you discover UPnP services by sending an
|
||||||
// SSDP query (which uPnPPacket is) to udp/1900 on the SSDP
|
// SSDP query (which uPnPPacket is) to udp/1900 on the SSDP
|
||||||
// multicast address, and then get a flood of responses back
|
// 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 {
|
if v, _ := strconv.ParseBool(os.Getenv("HIT_NETWORK")); !v {
|
||||||
t.Skip("skipping test without HIT_NETWORK=1")
|
t.Skip("skipping test without HIT_NETWORK=1")
|
||||||
}
|
}
|
||||||
c := NewClient(t.Logf, nil)
|
c := NewClient(t.Logf, nil, nil)
|
||||||
defer c.Close()
|
defer c.Close()
|
||||||
c.SetLocalPort(1234)
|
c.SetLocalPort(1234)
|
||||||
for i := 0; i < 2; i++ {
|
for i := 0; i < 2; i++ {
|
||||||
@ -32,7 +32,7 @@ func TestClientProbe(t *testing.T) {
|
|||||||
if v, _ := strconv.ParseBool(os.Getenv("HIT_NETWORK")); !v {
|
if v, _ := strconv.ParseBool(os.Getenv("HIT_NETWORK")); !v {
|
||||||
t.Skip("skipping test without HIT_NETWORK=1")
|
t.Skip("skipping test without HIT_NETWORK=1")
|
||||||
}
|
}
|
||||||
c := NewClient(t.Logf, nil)
|
c := NewClient(t.Logf, nil, nil)
|
||||||
defer c.Close()
|
defer c.Close()
|
||||||
for i := 0; i < 3; i++ {
|
for i := 0; i < 3; i++ {
|
||||||
if i > 0 {
|
if i > 0 {
|
||||||
@ -47,7 +47,7 @@ func TestClientProbeThenMap(t *testing.T) {
|
|||||||
if v, _ := strconv.ParseBool(os.Getenv("HIT_NETWORK")); !v {
|
if v, _ := strconv.ParseBool(os.Getenv("HIT_NETWORK")); !v {
|
||||||
t.Skip("skipping test without HIT_NETWORK=1")
|
t.Skip("skipping test without HIT_NETWORK=1")
|
||||||
}
|
}
|
||||||
c := NewClient(t.Logf, nil)
|
c := NewClient(t.Logf, nil, nil)
|
||||||
defer c.Close()
|
defer c.Close()
|
||||||
c.SetLocalPort(1234)
|
c.SetLocalPort(1234)
|
||||||
res, err := c.Probe(context.Background())
|
res, err := c.Probe(context.Background())
|
||||||
|
@ -152,8 +152,8 @@ func addAnyPortMapping(
|
|||||||
//
|
//
|
||||||
// The provided ctx is not retained in the returned upnpClient, but
|
// The provided ctx is not retained in the returned upnpClient, but
|
||||||
// its associated HTTP client is (if set via goupnp.WithHTTPClient).
|
// 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) {
|
func getUPnPClient(ctx context.Context, logf logger.Logf, debug DebugKnobs, gw netip.Addr, meta uPnPDiscoResponse) (client upnpClient, err error) {
|
||||||
if controlknobs.DisableUPnP() || DisableUPnP {
|
if controlknobs.DisableUPnP() || debug.DisableUPnP {
|
||||||
return nil, nil
|
return nil, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -161,7 +161,7 @@ func getUPnPClient(ctx context.Context, logf logger.Logf, gw netip.Addr, meta uP
|
|||||||
return nil, nil
|
return nil, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
if VerboseLogs {
|
if debug.VerboseLogs {
|
||||||
logf("fetching %v", meta.Location)
|
logf("fetching %v", meta.Location)
|
||||||
}
|
}
|
||||||
u, err := url.Parse(meta.Location)
|
u, err := url.Parse(meta.Location)
|
||||||
@ -237,9 +237,10 @@ func (c *Client) getUPnPPortMapping(
|
|||||||
internal netip.AddrPort,
|
internal netip.AddrPort,
|
||||||
prevPort uint16,
|
prevPort uint16,
|
||||||
) (external netip.AddrPort, ok bool) {
|
) (external netip.AddrPort, ok bool) {
|
||||||
if controlknobs.DisableUPnP() || DisableUPnP {
|
if controlknobs.DisableUPnP() || c.debug.DisableUPnP {
|
||||||
return netip.AddrPort{}, false
|
return netip.AddrPort{}, false
|
||||||
}
|
}
|
||||||
|
|
||||||
now := time.Now()
|
now := time.Now()
|
||||||
upnp := &upnpMapping{
|
upnp := &upnpMapping{
|
||||||
gw: gw,
|
gw: gw,
|
||||||
@ -257,8 +258,8 @@ func (c *Client) getUPnPPortMapping(
|
|||||||
client = oldMapping.client
|
client = oldMapping.client
|
||||||
} else {
|
} else {
|
||||||
ctx := goupnp.WithHTTPClient(ctx, httpClient)
|
ctx := goupnp.WithHTTPClient(ctx, httpClient)
|
||||||
client, err = getUPnPClient(ctx, c.logf, gw, meta)
|
client, err = getUPnPClient(ctx, c.logf, c.debug, gw, meta)
|
||||||
if VerboseLogs {
|
if c.debug.VerboseLogs {
|
||||||
c.logf("getUPnPClient: %T, %v", client, err)
|
c.logf("getUPnPClient: %T, %v", client, err)
|
||||||
}
|
}
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -278,15 +279,15 @@ func (c *Client) getUPnPPortMapping(
|
|||||||
internal.Addr().String(),
|
internal.Addr().String(),
|
||||||
time.Second*pmpMapLifetimeSec,
|
time.Second*pmpMapLifetimeSec,
|
||||||
)
|
)
|
||||||
if VerboseLogs {
|
if c.debug.VerboseLogs {
|
||||||
c.logf("addAnyPortMapping: %v, %v", newPort, err)
|
c.logf("addAnyPortMapping: %v, err=%q", newPort, err)
|
||||||
}
|
}
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return netip.AddrPort{}, false
|
return netip.AddrPort{}, false
|
||||||
}
|
}
|
||||||
// TODO cache this ip somewhere?
|
// TODO cache this ip somewhere?
|
||||||
extIP, err := client.GetExternalIPAddress(ctx)
|
extIP, err := client.GetExternalIPAddress(ctx)
|
||||||
if VerboseLogs {
|
if c.debug.VerboseLogs {
|
||||||
c.logf("client.GetExternalIPAddress: %v, %v", extIP, err)
|
c.logf("client.GetExternalIPAddress: %v, %v", extIP, err)
|
||||||
}
|
}
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -112,7 +112,7 @@ func TestGetUPnPClient(t *testing.T) {
|
|||||||
gw, _ := netip.AddrFromSlice(ts.Listener.Addr().(*net.TCPAddr).IP)
|
gw, _ := netip.AddrFromSlice(ts.Listener.Addr().(*net.TCPAddr).IP)
|
||||||
gw = gw.Unmap()
|
gw = gw.Unmap()
|
||||||
var logBuf tstest.MemLogger
|
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",
|
Location: ts.URL + "/rootDesc.xml",
|
||||||
})
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -25,7 +25,6 @@
|
|||||||
_ "tailscale.com/net/dnsfallback"
|
_ "tailscale.com/net/dnsfallback"
|
||||||
_ "tailscale.com/net/interfaces"
|
_ "tailscale.com/net/interfaces"
|
||||||
_ "tailscale.com/net/netns"
|
_ "tailscale.com/net/netns"
|
||||||
_ "tailscale.com/net/portmapper"
|
|
||||||
_ "tailscale.com/net/proxymux"
|
_ "tailscale.com/net/proxymux"
|
||||||
_ "tailscale.com/net/socks5"
|
_ "tailscale.com/net/socks5"
|
||||||
_ "tailscale.com/net/tsdial"
|
_ "tailscale.com/net/tsdial"
|
||||||
|
@ -25,7 +25,6 @@
|
|||||||
_ "tailscale.com/net/dnsfallback"
|
_ "tailscale.com/net/dnsfallback"
|
||||||
_ "tailscale.com/net/interfaces"
|
_ "tailscale.com/net/interfaces"
|
||||||
_ "tailscale.com/net/netns"
|
_ "tailscale.com/net/netns"
|
||||||
_ "tailscale.com/net/portmapper"
|
|
||||||
_ "tailscale.com/net/proxymux"
|
_ "tailscale.com/net/proxymux"
|
||||||
_ "tailscale.com/net/socks5"
|
_ "tailscale.com/net/socks5"
|
||||||
_ "tailscale.com/net/tsdial"
|
_ "tailscale.com/net/tsdial"
|
||||||
|
@ -25,7 +25,6 @@
|
|||||||
_ "tailscale.com/net/dnsfallback"
|
_ "tailscale.com/net/dnsfallback"
|
||||||
_ "tailscale.com/net/interfaces"
|
_ "tailscale.com/net/interfaces"
|
||||||
_ "tailscale.com/net/netns"
|
_ "tailscale.com/net/netns"
|
||||||
_ "tailscale.com/net/portmapper"
|
|
||||||
_ "tailscale.com/net/proxymux"
|
_ "tailscale.com/net/proxymux"
|
||||||
_ "tailscale.com/net/socks5"
|
_ "tailscale.com/net/socks5"
|
||||||
_ "tailscale.com/net/tsdial"
|
_ "tailscale.com/net/tsdial"
|
||||||
|
@ -25,7 +25,6 @@
|
|||||||
_ "tailscale.com/net/dnsfallback"
|
_ "tailscale.com/net/dnsfallback"
|
||||||
_ "tailscale.com/net/interfaces"
|
_ "tailscale.com/net/interfaces"
|
||||||
_ "tailscale.com/net/netns"
|
_ "tailscale.com/net/netns"
|
||||||
_ "tailscale.com/net/portmapper"
|
|
||||||
_ "tailscale.com/net/proxymux"
|
_ "tailscale.com/net/proxymux"
|
||||||
_ "tailscale.com/net/socks5"
|
_ "tailscale.com/net/socks5"
|
||||||
_ "tailscale.com/net/tsdial"
|
_ "tailscale.com/net/tsdial"
|
||||||
|
@ -33,7 +33,6 @@
|
|||||||
_ "tailscale.com/net/dnsfallback"
|
_ "tailscale.com/net/dnsfallback"
|
||||||
_ "tailscale.com/net/interfaces"
|
_ "tailscale.com/net/interfaces"
|
||||||
_ "tailscale.com/net/netns"
|
_ "tailscale.com/net/netns"
|
||||||
_ "tailscale.com/net/portmapper"
|
|
||||||
_ "tailscale.com/net/proxymux"
|
_ "tailscale.com/net/proxymux"
|
||||||
_ "tailscale.com/net/socks5"
|
_ "tailscale.com/net/socks5"
|
||||||
_ "tailscale.com/net/tsdial"
|
_ "tailscale.com/net/tsdial"
|
||||||
|
@ -632,7 +632,7 @@ func NewConn(opts Options) (*Conn, error) {
|
|||||||
c.idleFunc = opts.IdleFunc
|
c.idleFunc = opts.IdleFunc
|
||||||
c.testOnlyPacketListener = opts.TestOnlyPacketListener
|
c.testOnlyPacketListener = opts.TestOnlyPacketListener
|
||||||
c.noteRecvActivity = opts.NoteRecvActivity
|
c.noteRecvActivity = opts.NoteRecvActivity
|
||||||
c.portMapper = portmapper.NewClient(logger.WithPrefix(c.logf, "portmapper: "), c.onPortMapChanged)
|
c.portMapper = portmapper.NewClient(logger.WithPrefix(c.logf, "portmapper: "), nil, c.onPortMapChanged)
|
||||||
if opts.LinkMonitor != nil {
|
if opts.LinkMonitor != nil {
|
||||||
c.portMapper.SetGatewayLookupFunc(opts.LinkMonitor.GatewayAndSelfIP)
|
c.portMapper.SetGatewayLookupFunc(opts.LinkMonitor.GatewayAndSelfIP)
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user