derp_server_linux.go 1.8 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394
  1. // Copyright (c) Tailscale Inc & AUTHORS
  2. // SPDX-License-Identifier: BSD-3-Clause
  3. package derp
  4. import (
  5. "context"
  6. "crypto/tls"
  7. "net"
  8. "time"
  9. "golang.org/x/sys/unix"
  10. )
  11. func (c *sclient) statsLoop(ctx context.Context) error {
  12. // If we can't get a TCP socket, then we can't send stats.
  13. tcpConn := c.tcpConn()
  14. if tcpConn == nil {
  15. c.s.tcpRtt.Add("non-tcp", 1)
  16. return nil
  17. }
  18. rawConn, err := tcpConn.SyscallConn()
  19. if err != nil {
  20. c.logf("error getting SyscallConn: %v", err)
  21. c.s.tcpRtt.Add("error", 1)
  22. return nil
  23. }
  24. const statsInterval = 10 * time.Second
  25. ticker := time.NewTicker(statsInterval)
  26. defer ticker.Stop()
  27. var (
  28. tcpInfo *unix.TCPInfo
  29. sysErr error
  30. )
  31. statsLoop:
  32. for {
  33. select {
  34. case <-ticker.C:
  35. err = rawConn.Control(func(fd uintptr) {
  36. tcpInfo, sysErr = unix.GetsockoptTCPInfo(int(fd), unix.IPPROTO_TCP, unix.TCP_INFO)
  37. })
  38. if err != nil || sysErr != nil {
  39. continue statsLoop
  40. }
  41. // TODO(andrew): more metrics?
  42. rtt := time.Duration(tcpInfo.Rtt) * time.Microsecond
  43. c.s.tcpRtt.Add(durationToLabel(rtt), 1)
  44. case <-ctx.Done():
  45. return ctx.Err()
  46. }
  47. }
  48. }
  49. // tcpConn attempts to get the underlying *net.TCPConn from this client's
  50. // Conn; if it cannot, then it will return nil.
  51. func (c *sclient) tcpConn() *net.TCPConn {
  52. nc := c.nc
  53. for {
  54. switch v := nc.(type) {
  55. case *net.TCPConn:
  56. return v
  57. case *tls.Conn:
  58. nc = v.NetConn()
  59. default:
  60. return nil
  61. }
  62. }
  63. }
  64. func durationToLabel(dur time.Duration) string {
  65. switch {
  66. case dur <= 10*time.Millisecond:
  67. return "10ms"
  68. case dur <= 20*time.Millisecond:
  69. return "20ms"
  70. case dur <= 50*time.Millisecond:
  71. return "50ms"
  72. case dur <= 100*time.Millisecond:
  73. return "100ms"
  74. case dur <= 150*time.Millisecond:
  75. return "150ms"
  76. case dur <= 250*time.Millisecond:
  77. return "250ms"
  78. case dur <= 500*time.Millisecond:
  79. return "500ms"
  80. default:
  81. return "inf"
  82. }
  83. }