summaryrefslogtreecommitdiffhomepage
path: root/derp/derp_server_linux.go
blob: 48da8ed30a40d80b63d9b915c18f50a39ab46637 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
// Copyright (c) Tailscale Inc & AUTHORS
// SPDX-License-Identifier: BSD-3-Clause

package derp

import (
	"context"
	"crypto/tls"
	"net"
	"time"

	"tailscale.com/net/tcpinfo"
)

func (c *sclient) statsLoop(ctx context.Context) error {
	// Get the RTT initially to verify it's supported.
	conn := c.tcpConn()
	if conn == nil {
		c.s.tcpRtt.Add("non-tcp", 1)
		return nil
	}
	if _, err := tcpinfo.RTT(conn); err != nil {
		c.logf("error fetching initial RTT: %v", err)
		c.s.tcpRtt.Add("error", 1)
		return nil
	}

	const statsInterval = 10 * time.Second

	ticker, tickerChannel := c.s.clock.NewTicker(statsInterval)
	defer ticker.Stop()

statsLoop:
	for {
		select {
		case <-tickerChannel:
			rtt, err := tcpinfo.RTT(conn)
			if err != nil {
				continue statsLoop
			}

			// TODO(andrew): more metrics?
			c.s.tcpRtt.Add(durationToLabel(rtt), 1)

		case <-ctx.Done():
			return ctx.Err()
		}
	}
}

// tcpConn attempts to get the underlying *net.TCPConn from this client's
// Conn; if it cannot, then it will return nil.
func (c *sclient) tcpConn() *net.TCPConn {
	nc := c.nc
	for {
		switch v := nc.(type) {
		case *net.TCPConn:
			return v
		case *tls.Conn:
			nc = v.NetConn()
		default:
			return nil
		}
	}
}

func durationToLabel(dur time.Duration) string {
	switch {
	case dur <= 10*time.Millisecond:
		return "10ms"
	case dur <= 20*time.Millisecond:
		return "20ms"
	case dur <= 50*time.Millisecond:
		return "50ms"
	case dur <= 100*time.Millisecond:
		return "100ms"
	case dur <= 150*time.Millisecond:
		return "150ms"
	case dur <= 250*time.Millisecond:
		return "250ms"
	case dur <= 500*time.Millisecond:
		return "500ms"
	default:
		return "inf"
	}
}