Skip to content

Commit

Permalink
wgengine/magicsock: implement probing of UDP path lifetime (tailscale…
Browse files Browse the repository at this point in the history
…#10844)

This commit implements probing of UDP path lifetime on the tail end of
an active direct connection. Probing configuration has two parts -
Cliffs, which are various timeout cliffs of interest, and
CycleCanStartEvery, which limits how often a probing cycle can start,
per-endpoint. Initially a statically defined default configuration will
be used. The default configuration has cliffs of 10s, 30s, and 60s,
with a CycleCanStartEvery of 24h. Probing results are communicated via
clientmetric counters. Probing is off by default, and can be enabled
via control knob. Probing is purely informational and does not yet
drive any magicsock behaviors.

Updates tailscale#540

Signed-off-by: Jordan Whited <jordan@tailscale.com>
  • Loading branch information
jwhited committed Jan 23, 2024
1 parent 0e2cb76 commit 8b47322
Show file tree
Hide file tree
Showing 11 changed files with 903 additions and 50 deletions.
7 changes: 7 additions & 0 deletions control/controlknobs/controlknobs.go
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,10 @@ type Knobs struct {
// renewing node keys without breaking connections.
// http://go/seamless-key-renewal
SeamlessKeyRenewal atomic.Bool

// ProbeUDPLifetime is whether the node should probe UDP path lifetime on
// the tail end of an active direct connection in magicsock.
ProbeUDPLifetime atomic.Bool
}

// UpdateFromNodeAttributes updates k (if non-nil) based on the provided self
Expand All @@ -95,6 +99,7 @@ func (k *Knobs) UpdateFromNodeAttributes(selfNodeAttrs []tailcfg.NodeCapability,
forceIPTables = has(tailcfg.NodeAttrLinuxMustUseIPTables)
forceNfTables = has(tailcfg.NodeAttrLinuxMustUseNfTables)
seamlessKeyRenewal = has(tailcfg.NodeAttrSeamlessKeyRenewal)
probeUDPLifetime = has(tailcfg.NodeAttrProbeUDPLifetime)
)

if has(tailcfg.NodeAttrOneCGNATEnable) {
Expand All @@ -116,6 +121,7 @@ func (k *Knobs) UpdateFromNodeAttributes(selfNodeAttrs []tailcfg.NodeCapability,
k.LinuxForceIPTables.Store(forceIPTables)
k.LinuxForceNfTables.Store(forceNfTables)
k.SeamlessKeyRenewal.Store(seamlessKeyRenewal)
k.ProbeUDPLifetime.Store(probeUDPLifetime)
}

// AsDebugJSON returns k as something that can be marshalled with json.Marshal
Expand All @@ -138,5 +144,6 @@ func (k *Knobs) AsDebugJSON() map[string]any {
"LinuxForceIPTables": k.LinuxForceIPTables.Load(),
"LinuxForceNfTables": k.LinuxForceNfTables.Load(),
"SeamlessKeyRenewal": k.SeamlessKeyRenewal.Load(),
"ProbeUDPLifetime": k.ProbeUDPLifetime.Load(),
}
}
1 change: 1 addition & 0 deletions ipn/ipnlocal/local.go
Original file line number Diff line number Diff line change
Expand Up @@ -4556,6 +4556,7 @@ func (b *LocalBackend) setNetMapLocked(nm *netmap.NetworkMap) {
}

b.MagicConn().SetSilentDisco(b.ControlKnobs().SilentDisco.Load())
b.MagicConn().SetProbeUDPLifetime(b.ControlKnobs().ProbeUDPLifetime.Load())

b.setDebugLogsByCapabilityLocked(nm)

Expand Down
7 changes: 6 additions & 1 deletion tailcfg/tailcfg.go
Original file line number Diff line number Diff line change
Expand Up @@ -126,7 +126,8 @@ type CapabilityVersion int
// - 83: 2023-12-18: Client understands DefaultAutoUpdate
// - 84: 2024-01-04: Client understands SeamlessKeyRenewal
// - 85: 2024-01-05: Client understands MaxKeyDuration
const CurrentCapabilityVersion CapabilityVersion = 85
// - 86: 2024-01-23: Client understands NodeAttrProbeUDPLifetime
const CurrentCapabilityVersion CapabilityVersion = 86

type StableID string

Expand Down Expand Up @@ -2203,6 +2204,10 @@ const (
// NodeAttrSeamlessKeyRenewal makes clients enable beta functionality
// of renewing node keys without breaking connections.
NodeAttrSeamlessKeyRenewal NodeCapability = "seamless-key-renewal"

// NodeAttrProbeUDPLifetime makes the client probe UDP path lifetime at the
// tail end of an active direct connection in magicsock.
NodeAttrProbeUDPLifetime NodeCapability = "probe-udp-lifetime"
)

// SetDNSRequest is a request to add a DNS record.
Expand Down
9 changes: 9 additions & 0 deletions types/key/disco.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
package key

import (
"bytes"
"crypto/subtle"
"fmt"

Expand Down Expand Up @@ -127,6 +128,14 @@ func (k DiscoPublic) String() string {
return string(bs)
}

// Compare returns an integer comparing DiscoPublic k and l lexicographically.
// The result will be 0 if k == l, -1 if k < l, and +1 if k > l. This is useful
// for situations requiring only one node in a pair to perform some operation,
// e.g. probing UDP path lifetime.
func (k DiscoPublic) Compare(l DiscoPublic) int {
return bytes.Compare(k.k[:], l.k[:])
}

// AppendText implements encoding.TextAppender.
func (k DiscoPublic) AppendText(b []byte) ([]byte, error) {
return appendHexKey(b, discoPublicHexPrefix, k.k[:]), nil
Expand Down
6 changes: 3 additions & 3 deletions wgengine/magicsock/debughttp.go
Original file line number Diff line number Diff line change
Expand Up @@ -123,11 +123,11 @@ func (c *Conn) ServeHTTPDebug(w http.ResponseWriter, r *http.Request) {
}

func printEndpointHTML(w io.Writer, ep *endpoint) {
lastRecv := ep.lastRecv.LoadAtomic()
lastRecv := ep.lastRecvWG.LoadAtomic()

ep.mu.Lock()
defer ep.mu.Unlock()
if ep.lastSend == 0 && lastRecv == 0 {
if ep.lastSendExt == 0 && lastRecv == 0 {
return // no activity ever
}

Expand All @@ -142,7 +142,7 @@ func printEndpointHTML(w io.Writer, ep *endpoint) {

fmt.Fprintf(w, "<p>Best: <b>%+v</b>, %v ago (for %v)</p>\n", ep.bestAddr, fmtMono(ep.bestAddrAt), ep.trustBestAddrUntil.Sub(mnow).Round(time.Millisecond))
fmt.Fprintf(w, "<p>heartbeating: %v</p>\n", ep.heartBeatTimer != nil)
fmt.Fprintf(w, "<p>lastSend: %v ago</p>\n", fmtMono(ep.lastSend))
fmt.Fprintf(w, "<p>lastSend: %v ago</p>\n", fmtMono(ep.lastSendExt))
fmt.Fprintf(w, "<p>lastFullPing: %v ago</p>\n", fmtMono(ep.lastFullPing))

eps := make([]netip.AddrPort, 0, len(ep.endpointState))
Expand Down
3 changes: 2 additions & 1 deletion wgengine/magicsock/derp.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ import (
"tailscale.com/net/tsaddr"
"tailscale.com/syncs"
"tailscale.com/tailcfg"
"tailscale.com/tstime/mono"
"tailscale.com/types/key"
"tailscale.com/types/logger"
"tailscale.com/util/mak"
Expand Down Expand Up @@ -668,7 +669,7 @@ func (c *Conn) processDERPReadResult(dm derpReadResult, b []byte) (n int, ep *en
return 0, nil
}

ep.noteRecvActivity(ipp)
ep.noteRecvActivity(ipp, mono.Now())
if stats := c.stats.Load(); stats != nil {
stats.UpdateRxPhysical(ep.nodeAddr, ipp, dm.n)
}
Expand Down
5 changes: 3 additions & 2 deletions wgengine/magicsock/discopingpurpose_string.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

0 comments on commit 8b47322

Please sign in to comment.