debug.go 7.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287
  1. // Copyright (c) Tailscale Inc & AUTHORS
  2. // SPDX-License-Identifier: BSD-3-Clause
  3. //go:build !ts_omit_debug
  4. package main
  5. import (
  6. "context"
  7. "crypto/tls"
  8. "encoding/json"
  9. "errors"
  10. "flag"
  11. "fmt"
  12. "io"
  13. "log"
  14. "net/http"
  15. "net/http/httptrace"
  16. "net/http/pprof"
  17. "net/url"
  18. "os"
  19. "time"
  20. "tailscale.com/derp/derphttp"
  21. "tailscale.com/feature"
  22. "tailscale.com/feature/buildfeatures"
  23. "tailscale.com/health"
  24. "tailscale.com/ipn"
  25. "tailscale.com/net/netmon"
  26. "tailscale.com/tailcfg"
  27. "tailscale.com/tsweb/varz"
  28. "tailscale.com/types/key"
  29. "tailscale.com/util/clientmetric"
  30. "tailscale.com/util/eventbus"
  31. )
  32. var debugArgs struct {
  33. ifconfig bool // print network state once and exit
  34. monitor bool
  35. getURL string
  36. derpCheck string
  37. portmap bool
  38. }
  39. func init() {
  40. debugModeFunc := debugMode // to be addressable
  41. subCommands["debug"] = &debugModeFunc
  42. hookNewDebugMux.Set(newDebugMux)
  43. }
  44. func newDebugMux() *http.ServeMux {
  45. mux := http.NewServeMux()
  46. mux.HandleFunc("/debug/metrics", servePrometheusMetrics)
  47. mux.HandleFunc("/debug/pprof/", pprof.Index)
  48. mux.HandleFunc("/debug/pprof/cmdline", pprof.Cmdline)
  49. mux.HandleFunc("/debug/pprof/profile", pprof.Profile)
  50. mux.HandleFunc("/debug/pprof/symbol", pprof.Symbol)
  51. mux.HandleFunc("/debug/pprof/trace", pprof.Trace)
  52. return mux
  53. }
  54. func servePrometheusMetrics(w http.ResponseWriter, r *http.Request) {
  55. w.Header().Set("Content-Type", "text/plain")
  56. varz.Handler(w, r)
  57. clientmetric.WritePrometheusExpositionFormat(w)
  58. }
  59. func debugMode(args []string) error {
  60. fs := flag.NewFlagSet("debug", flag.ExitOnError)
  61. fs.BoolVar(&debugArgs.ifconfig, "ifconfig", false, "If true, print network interface state")
  62. fs.BoolVar(&debugArgs.monitor, "monitor", false, "If true, run network monitor forever. Precludes all other options.")
  63. fs.BoolVar(&debugArgs.portmap, "portmap", false, "If true, run portmap debugging. Precludes all other options.")
  64. fs.StringVar(&debugArgs.getURL, "get-url", "", "If non-empty, fetch provided URL.")
  65. fs.StringVar(&debugArgs.derpCheck, "derp", "", "if non-empty, test a DERP ping via named region code")
  66. if err := fs.Parse(args); err != nil {
  67. return err
  68. }
  69. if len(fs.Args()) > 0 {
  70. return errors.New("unknown non-flag debug subcommand arguments")
  71. }
  72. ctx := context.Background()
  73. if debugArgs.derpCheck != "" {
  74. return checkDerp(ctx, debugArgs.derpCheck)
  75. }
  76. if debugArgs.ifconfig {
  77. return runMonitor(ctx, false)
  78. }
  79. if debugArgs.monitor {
  80. return runMonitor(ctx, true)
  81. }
  82. if debugArgs.portmap {
  83. return debugPortmap(ctx)
  84. }
  85. if debugArgs.getURL != "" {
  86. return getURL(ctx, debugArgs.getURL)
  87. }
  88. return errors.New("only --monitor is available at the moment")
  89. }
  90. func runMonitor(ctx context.Context, loop bool) error {
  91. b := eventbus.New()
  92. defer b.Close()
  93. dump := func(st *netmon.State) {
  94. j, _ := json.MarshalIndent(st, "", " ")
  95. os.Stderr.Write(j)
  96. }
  97. mon, err := netmon.New(b, log.Printf)
  98. if err != nil {
  99. return err
  100. }
  101. defer mon.Close()
  102. eventClient := b.Client("debug.runMonitor")
  103. m := eventClient.Monitor(changeDeltaWatcher(eventClient, ctx, dump))
  104. defer m.Close()
  105. if loop {
  106. log.Printf("Starting link change monitor; initial state:")
  107. }
  108. dump(mon.InterfaceState())
  109. if !loop {
  110. return nil
  111. }
  112. mon.Start()
  113. log.Printf("Started link change monitor; waiting...")
  114. select {}
  115. }
  116. func changeDeltaWatcher(ec *eventbus.Client, ctx context.Context, dump func(st *netmon.State)) func(*eventbus.Client) {
  117. changeSub := eventbus.Subscribe[netmon.ChangeDelta](ec)
  118. return func(ec *eventbus.Client) {
  119. for {
  120. select {
  121. case <-ctx.Done():
  122. return
  123. case <-ec.Done():
  124. return
  125. case delta := <-changeSub.Events():
  126. if !delta.Major {
  127. log.Printf("Network monitor fired; not a major change")
  128. return
  129. }
  130. log.Printf("Network monitor fired. New state:")
  131. dump(delta.New)
  132. }
  133. }
  134. }
  135. }
  136. func getURL(ctx context.Context, urlStr string) error {
  137. if urlStr == "login" {
  138. urlStr = "https://login.tailscale.com"
  139. }
  140. log.SetOutput(os.Stdout)
  141. ctx = httptrace.WithClientTrace(ctx, &httptrace.ClientTrace{
  142. GetConn: func(hostPort string) { log.Printf("GetConn(%q)", hostPort) },
  143. GotConn: func(info httptrace.GotConnInfo) { log.Printf("GotConn: %+v", info) },
  144. DNSStart: func(info httptrace.DNSStartInfo) { log.Printf("DNSStart: %+v", info) },
  145. DNSDone: func(info httptrace.DNSDoneInfo) { log.Printf("DNSDoneInfo: %+v", info) },
  146. TLSHandshakeStart: func() { log.Printf("TLSHandshakeStart") },
  147. TLSHandshakeDone: func(cs tls.ConnectionState, err error) { log.Printf("TLSHandshakeDone: %+v, %v", cs, err) },
  148. WroteRequest: func(info httptrace.WroteRequestInfo) { log.Printf("WroteRequest: %+v", info) },
  149. })
  150. req, err := http.NewRequestWithContext(ctx, "GET", urlStr, nil)
  151. if err != nil {
  152. return fmt.Errorf("http.NewRequestWithContext: %v", err)
  153. }
  154. var proxyURL *url.URL
  155. if buildfeatures.HasUseProxy {
  156. if proxyFromEnv, ok := feature.HookProxyFromEnvironment.GetOk(); ok {
  157. proxyURL, err = proxyFromEnv(req)
  158. if err != nil {
  159. return fmt.Errorf("tshttpproxy.ProxyFromEnvironment: %v", err)
  160. }
  161. }
  162. }
  163. log.Printf("proxy: %v", proxyURL)
  164. tr := &http.Transport{
  165. Proxy: func(*http.Request) (*url.URL, error) { return proxyURL, nil },
  166. ProxyConnectHeader: http.Header{},
  167. DisableKeepAlives: true,
  168. }
  169. if proxyURL != nil {
  170. var auth string
  171. if f, ok := feature.HookProxyGetAuthHeader.GetOk(); ok {
  172. auth, err = f(proxyURL)
  173. }
  174. if err == nil && auth != "" {
  175. tr.ProxyConnectHeader.Set("Proxy-Authorization", auth)
  176. }
  177. log.Printf("tshttpproxy.GetAuthHeader(%v) got: auth of %d bytes, err=%v", proxyURL, len(auth), err)
  178. const truncLen = 20
  179. if len(auth) > truncLen {
  180. auth = fmt.Sprintf("%s...(%d total bytes)", auth[:truncLen], len(auth))
  181. }
  182. if auth != "" {
  183. // We used log.Printf above (for timestamps).
  184. // Use fmt.Printf here instead just to appease
  185. // a security scanner, despite log.Printf only
  186. // going to stdout.
  187. fmt.Printf("... Proxy-Authorization = %q\n", auth)
  188. }
  189. }
  190. res, err := tr.RoundTrip(req)
  191. if err != nil {
  192. return fmt.Errorf("Transport.RoundTrip: %v", err)
  193. }
  194. defer res.Body.Close()
  195. return res.Write(os.Stdout)
  196. }
  197. func checkDerp(ctx context.Context, derpRegion string) (err error) {
  198. bus := eventbus.New()
  199. defer bus.Close()
  200. ht := health.NewTracker(bus)
  201. req, err := http.NewRequestWithContext(ctx, "GET", ipn.DefaultControlURL+"/derpmap/default", nil)
  202. if err != nil {
  203. return fmt.Errorf("create derp map request: %w", err)
  204. }
  205. res, err := http.DefaultClient.Do(req)
  206. if err != nil {
  207. return fmt.Errorf("fetch derp map failed: %w", err)
  208. }
  209. defer res.Body.Close()
  210. b, err := io.ReadAll(io.LimitReader(res.Body, 1<<20))
  211. if err != nil {
  212. return fmt.Errorf("fetch derp map failed: %w", err)
  213. }
  214. if res.StatusCode != 200 {
  215. return fmt.Errorf("fetch derp map: %v: %s", res.Status, b)
  216. }
  217. var dmap tailcfg.DERPMap
  218. if err = json.Unmarshal(b, &dmap); err != nil {
  219. return fmt.Errorf("fetch DERP map: %w", err)
  220. }
  221. getRegion := func() *tailcfg.DERPRegion {
  222. for _, r := range dmap.Regions {
  223. if r.RegionCode == derpRegion {
  224. return r
  225. }
  226. }
  227. for _, r := range dmap.Regions {
  228. log.Printf("Known region: %q", r.RegionCode)
  229. }
  230. log.Fatalf("unknown region %q", derpRegion)
  231. panic("unreachable")
  232. }
  233. priv1 := key.NewNode()
  234. priv2 := key.NewNode()
  235. c1 := derphttp.NewRegionClient(priv1, log.Printf, nil, getRegion)
  236. c2 := derphttp.NewRegionClient(priv2, log.Printf, nil, getRegion)
  237. c1.HealthTracker = ht
  238. c2.HealthTracker = ht
  239. defer func() {
  240. if err != nil {
  241. c1.Close()
  242. c2.Close()
  243. }
  244. }()
  245. c2.NotePreferred(true) // just to open it
  246. m, err := c2.Recv()
  247. log.Printf("c2 got %T, %v", m, err)
  248. t0 := time.Now()
  249. if err := c1.Send(priv2.Public(), []byte("hello")); err != nil {
  250. return err
  251. }
  252. fmt.Println(time.Since(t0))
  253. m, err = c2.Recv()
  254. log.Printf("c2 got %T, %v", m, err)
  255. if err != nil {
  256. return err
  257. }
  258. log.Printf("ok")
  259. return err
  260. }
  261. func debugPortmap(ctx context.Context) error {
  262. return fmt.Errorf("this flag has been deprecated in favour of 'tailscale debug portmap'")
  263. }