debugportmapper.go 4.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204
  1. // Copyright (c) Tailscale Inc & contributors
  2. // SPDX-License-Identifier: BSD-3-Clause
  3. // Package debugportmapper registers support for debugging Tailscale's
  4. // portmapping support.
  5. package debugportmapper
  6. import (
  7. "context"
  8. "fmt"
  9. "net"
  10. "net/http"
  11. "net/netip"
  12. "strconv"
  13. "strings"
  14. "sync"
  15. "time"
  16. "tailscale.com/ipn/localapi"
  17. "tailscale.com/net/netmon"
  18. "tailscale.com/net/portmapper"
  19. "tailscale.com/types/logger"
  20. "tailscale.com/util/eventbus"
  21. )
  22. func init() {
  23. localapi.Register("debug-portmap", serveDebugPortmap)
  24. }
  25. func serveDebugPortmap(h *localapi.Handler, w http.ResponseWriter, r *http.Request) {
  26. if !h.PermitWrite {
  27. http.Error(w, "debug access denied", http.StatusForbidden)
  28. return
  29. }
  30. w.Header().Set("Content-Type", "text/plain")
  31. dur, err := time.ParseDuration(r.FormValue("duration"))
  32. if err != nil {
  33. http.Error(w, err.Error(), http.StatusBadRequest)
  34. return
  35. }
  36. gwSelf := r.FormValue("gateway_and_self")
  37. trueFunc := func() bool { return true }
  38. // Update portmapper debug flags
  39. debugKnobs := &portmapper.DebugKnobs{VerboseLogs: true}
  40. switch r.FormValue("type") {
  41. case "":
  42. case "pmp":
  43. debugKnobs.DisablePCPFunc = trueFunc
  44. debugKnobs.DisableUPnPFunc = trueFunc
  45. case "pcp":
  46. debugKnobs.DisablePMPFunc = trueFunc
  47. debugKnobs.DisableUPnPFunc = trueFunc
  48. case "upnp":
  49. debugKnobs.DisablePCPFunc = trueFunc
  50. debugKnobs.DisablePMPFunc = trueFunc
  51. default:
  52. http.Error(w, "unknown portmap debug type", http.StatusBadRequest)
  53. return
  54. }
  55. if k := h.LocalBackend().ControlKnobs(); k != nil {
  56. if k.DisableUPnP.Load() {
  57. debugKnobs.DisableUPnPFunc = trueFunc
  58. }
  59. }
  60. if defBool(r.FormValue("log_http"), false) {
  61. debugKnobs.LogHTTP = true
  62. }
  63. var (
  64. logLock sync.Mutex
  65. handlerDone bool
  66. )
  67. logf := func(format string, args ...any) {
  68. if !strings.HasSuffix(format, "\n") {
  69. format = format + "\n"
  70. }
  71. logLock.Lock()
  72. defer logLock.Unlock()
  73. // The portmapper can call this log function after the HTTP
  74. // handler returns, which is not allowed and can cause a panic.
  75. // If this happens, ignore the log lines since this typically
  76. // occurs due to a client disconnect.
  77. if handlerDone {
  78. return
  79. }
  80. // Write and flush each line to the client so that output is streamed
  81. fmt.Fprintf(w, format, args...)
  82. if f, ok := w.(http.Flusher); ok {
  83. f.Flush()
  84. }
  85. }
  86. defer func() {
  87. logLock.Lock()
  88. handlerDone = true
  89. logLock.Unlock()
  90. }()
  91. ctx, cancel := context.WithTimeout(r.Context(), dur)
  92. defer cancel()
  93. done := make(chan bool, 1)
  94. var c *portmapper.Client
  95. c = portmapper.NewClient(portmapper.Config{
  96. Logf: logger.WithPrefix(logf, "portmapper: "),
  97. NetMon: h.LocalBackend().NetMon(),
  98. DebugKnobs: debugKnobs,
  99. EventBus: h.LocalBackend().EventBus(),
  100. OnChange: func() {
  101. logf("portmapping changed.")
  102. logf("have mapping: %v", c.HaveMapping())
  103. if ext, ok := c.GetCachedMappingOrStartCreatingOne(); ok {
  104. logf("cb: mapping: %v", ext)
  105. select {
  106. case done <- true:
  107. default:
  108. }
  109. return
  110. }
  111. logf("cb: no mapping")
  112. },
  113. })
  114. defer c.Close()
  115. bus := eventbus.New()
  116. defer bus.Close()
  117. netMon, err := netmon.New(bus, logger.WithPrefix(logf, "monitor: "))
  118. if err != nil {
  119. logf("error creating monitor: %v", err)
  120. return
  121. }
  122. gatewayAndSelfIP := func() (gw, self netip.Addr, ok bool) {
  123. if a, b, ok := strings.Cut(gwSelf, "/"); ok {
  124. gw = netip.MustParseAddr(a)
  125. self = netip.MustParseAddr(b)
  126. return gw, self, true
  127. }
  128. return netMon.GatewayAndSelfIP()
  129. }
  130. c.SetGatewayLookupFunc(gatewayAndSelfIP)
  131. gw, selfIP, ok := gatewayAndSelfIP()
  132. if !ok {
  133. logf("no gateway or self IP; %v", netMon.InterfaceState())
  134. return
  135. }
  136. logf("gw=%v; self=%v", gw, selfIP)
  137. uc, err := net.ListenPacket("udp", "0.0.0.0:0")
  138. if err != nil {
  139. return
  140. }
  141. defer uc.Close()
  142. c.SetLocalPort(uint16(uc.LocalAddr().(*net.UDPAddr).Port))
  143. res, err := c.Probe(ctx)
  144. if err != nil {
  145. logf("error in Probe: %v", err)
  146. return
  147. }
  148. logf("Probe: %+v", res)
  149. if !res.PCP && !res.PMP && !res.UPnP {
  150. logf("no portmapping services available")
  151. return
  152. }
  153. if ext, ok := c.GetCachedMappingOrStartCreatingOne(); ok {
  154. logf("mapping: %v", ext)
  155. } else {
  156. logf("no mapping")
  157. }
  158. select {
  159. case <-done:
  160. case <-ctx.Done():
  161. if r.Context().Err() == nil {
  162. logf("serveDebugPortmap: context done: %v", ctx.Err())
  163. } else {
  164. h.Logf("serveDebugPortmap: context done: %v", ctx.Err())
  165. }
  166. }
  167. }
  168. func defBool(a string, def bool) bool {
  169. if a == "" {
  170. return def
  171. }
  172. v, err := strconv.ParseBool(a)
  173. if err != nil {
  174. return def
  175. }
  176. return v
  177. }