derpserver_linux.go 1.9 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091
  1. // Copyright (c) Tailscale Inc & AUTHORS
  2. // SPDX-License-Identifier: BSD-3-Clause
  3. //go:build linux && !android
  4. package derpserver
  5. import (
  6. "context"
  7. "crypto/tls"
  8. "net"
  9. "time"
  10. "tailscale.com/net/tcpinfo"
  11. )
  12. func (c *sclient) startStatsLoop(ctx context.Context) {
  13. // Get the RTT initially to verify it's supported.
  14. conn := c.tcpConn()
  15. if conn == nil {
  16. c.s.tcpRtt.Add("non-tcp", 1)
  17. return
  18. }
  19. if _, err := tcpinfo.RTT(conn); err != nil {
  20. c.logf("error fetching initial RTT: %v", err)
  21. c.s.tcpRtt.Add("error", 1)
  22. return
  23. }
  24. const statsInterval = 10 * time.Second
  25. // Don't launch a goroutine; use a timer instead.
  26. var gatherStats func()
  27. gatherStats = func() {
  28. // Do nothing if the context is finished.
  29. if ctx.Err() != nil {
  30. return
  31. }
  32. // Reschedule ourselves when this stats gathering is finished.
  33. defer c.s.clock.AfterFunc(statsInterval, gatherStats)
  34. // Gather TCP RTT information.
  35. rtt, err := tcpinfo.RTT(conn)
  36. if err == nil {
  37. c.s.tcpRtt.Add(durationToLabel(rtt), 1)
  38. }
  39. // TODO(andrew): more metrics?
  40. }
  41. // Kick off the initial timer.
  42. c.s.clock.AfterFunc(statsInterval, gatherStats)
  43. }
  44. // tcpConn attempts to get the underlying *net.TCPConn from this client's
  45. // Conn; if it cannot, then it will return nil.
  46. func (c *sclient) tcpConn() *net.TCPConn {
  47. nc := c.nc
  48. for {
  49. switch v := nc.(type) {
  50. case *net.TCPConn:
  51. return v
  52. case *tls.Conn:
  53. nc = v.NetConn()
  54. default:
  55. return nil
  56. }
  57. }
  58. }
  59. func durationToLabel(dur time.Duration) string {
  60. switch {
  61. case dur <= 10*time.Millisecond:
  62. return "10ms"
  63. case dur <= 20*time.Millisecond:
  64. return "20ms"
  65. case dur <= 50*time.Millisecond:
  66. return "50ms"
  67. case dur <= 100*time.Millisecond:
  68. return "100ms"
  69. case dur <= 150*time.Millisecond:
  70. return "150ms"
  71. case dur <= 250*time.Millisecond:
  72. return "250ms"
  73. case dur <= 500*time.Millisecond:
  74. return "500ms"
  75. default:
  76. return "inf"
  77. }
  78. }