mirror of
https://github.com/tailscale/tailscale.git
synced 2025-08-12 05:37:32 +00:00
wgengine,ipn,cmd/tailscale: add size option to ping (#8739)
This adds the capability to pad disco ping message payloads to reach a specified size. It also plumbs it through to the tailscale ping -size flag. Disco pings used for actual endpoint discovery do not use this yet. Updates #311. Signed-off-by: salman <salman@tailscale.com> Co-authored-by: Val <valerie@tailscale.com>
This commit is contained in:
@@ -19,10 +19,12 @@ import (
|
||||
"sync/atomic"
|
||||
"time"
|
||||
|
||||
"golang.org/x/crypto/poly1305"
|
||||
"golang.org/x/exp/maps"
|
||||
"tailscale.com/disco"
|
||||
"tailscale.com/ipn/ipnstate"
|
||||
"tailscale.com/net/stun"
|
||||
"tailscale.com/net/tstun"
|
||||
"tailscale.com/tailcfg"
|
||||
"tailscale.com/tstime/mono"
|
||||
"tailscale.com/types/key"
|
||||
@@ -361,7 +363,7 @@ func (de *endpoint) heartbeat() {
|
||||
udpAddr, _, _ := de.addrForSendLocked(now)
|
||||
if udpAddr.IsValid() {
|
||||
// We have a preferred path. Ping that every 2 seconds.
|
||||
de.startDiscoPingLocked(udpAddr, now, pingHeartbeat)
|
||||
de.startDiscoPingLocked(udpAddr, now, pingHeartbeat, 0)
|
||||
}
|
||||
|
||||
if de.wantFullPingLocked(now) {
|
||||
@@ -403,7 +405,7 @@ func (de *endpoint) noteActiveLocked() {
|
||||
|
||||
// cliPing starts a ping for the "tailscale ping" command. res is value to call cb with,
|
||||
// already partially filled.
|
||||
func (de *endpoint) cliPing(res *ipnstate.PingResult, cb func(*ipnstate.PingResult)) {
|
||||
func (de *endpoint) cliPing(res *ipnstate.PingResult, size int, cb func(*ipnstate.PingResult)) {
|
||||
de.mu.Lock()
|
||||
defer de.mu.Unlock()
|
||||
|
||||
@@ -418,17 +420,17 @@ func (de *endpoint) cliPing(res *ipnstate.PingResult, cb func(*ipnstate.PingResu
|
||||
now := mono.Now()
|
||||
udpAddr, derpAddr, _ := de.addrForSendLocked(now)
|
||||
if derpAddr.IsValid() {
|
||||
de.startDiscoPingLocked(derpAddr, now, pingCLI)
|
||||
de.startDiscoPingLocked(derpAddr, now, pingCLI, size)
|
||||
}
|
||||
if udpAddr.IsValid() && now.Before(de.trustBestAddrUntil) {
|
||||
// Already have an active session, so just ping the address we're using.
|
||||
// Otherwise "tailscale ping" results to a node on the local network
|
||||
// can look like they're bouncing between, say 10.0.0.0/9 and the peer's
|
||||
// IPv6 address, both 1ms away, and it's random who replies first.
|
||||
de.startDiscoPingLocked(udpAddr, now, pingCLI)
|
||||
de.startDiscoPingLocked(udpAddr, now, pingCLI, size)
|
||||
} else {
|
||||
for ep := range de.endpointState {
|
||||
de.startDiscoPingLocked(ep, now, pingCLI)
|
||||
de.startDiscoPingLocked(ep, now, pingCLI, size)
|
||||
}
|
||||
}
|
||||
de.noteActiveLocked()
|
||||
@@ -522,17 +524,31 @@ func (de *endpoint) removeSentDiscoPingLocked(txid stun.TxID, sp sentPing) {
|
||||
delete(de.sentPing, txid)
|
||||
}
|
||||
|
||||
// sendDiscoPing sends a ping with the provided txid to ep using de's discoKey.
|
||||
// discoPingSize is the size of a complete disco ping packet, without any padding.
|
||||
const discoPingSize = len(disco.Magic) + key.DiscoPublicRawLen + disco.NonceLen +
|
||||
poly1305.TagSize + disco.MessageHeaderLen + disco.PingLen
|
||||
|
||||
// sendDiscoPing sends a ping with the provided txid to ep using de's discoKey. size
|
||||
// is the desired disco message size, including all disco headers but excluding IP/UDP
|
||||
// headers.
|
||||
//
|
||||
// The caller (startPingLocked) should've already recorded the ping in
|
||||
// sentPing and set up the timer.
|
||||
//
|
||||
// The caller should use de.discoKey as the discoKey argument.
|
||||
// It is passed in so that sendDiscoPing doesn't need to lock de.mu.
|
||||
func (de *endpoint) sendDiscoPing(ep netip.AddrPort, discoKey key.DiscoPublic, txid stun.TxID, logLevel discoLogLevel) {
|
||||
func (de *endpoint) sendDiscoPing(ep netip.AddrPort, discoKey key.DiscoPublic, txid stun.TxID, size int, logLevel discoLogLevel) {
|
||||
padding := 0
|
||||
if size > int(tstun.DefaultMTU()) {
|
||||
size = int(tstun.DefaultMTU())
|
||||
}
|
||||
if size-discoPingSize > 0 {
|
||||
padding = size - discoPingSize
|
||||
}
|
||||
sent, _ := de.c.sendDiscoMessage(ep, de.publicKey, discoKey, &disco.Ping{
|
||||
TxID: [12]byte(txid),
|
||||
NodeKey: de.c.publicKeyAtomic.Load(),
|
||||
Padding: padding,
|
||||
}, logLevel)
|
||||
if !sent {
|
||||
de.forgetDiscoPing(txid)
|
||||
@@ -557,7 +573,8 @@ const (
|
||||
pingCLI
|
||||
)
|
||||
|
||||
func (de *endpoint) startDiscoPingLocked(ep netip.AddrPort, now mono.Time, purpose discoPingPurpose) {
|
||||
// startDiscoPingLocked sends a disco ping to ep in a separate goroutine.
|
||||
func (de *endpoint) startDiscoPingLocked(ep netip.AddrPort, now mono.Time, purpose discoPingPurpose, size int) {
|
||||
if runtime.GOOS == "js" {
|
||||
return
|
||||
}
|
||||
@@ -587,9 +604,10 @@ func (de *endpoint) startDiscoPingLocked(ep netip.AddrPort, now mono.Time, purpo
|
||||
if purpose == pingHeartbeat {
|
||||
logLevel = discoVerboseLog
|
||||
}
|
||||
go de.sendDiscoPing(ep, epDisco.key, txid, logLevel)
|
||||
go de.sendDiscoPing(ep, epDisco.key, txid, size, logLevel)
|
||||
}
|
||||
|
||||
// sendDiscoPingsLocked starts pinging all of ep's endpoints.
|
||||
func (de *endpoint) sendDiscoPingsLocked(now mono.Time, sendCallMeMaybe bool) {
|
||||
de.lastFullPing = now
|
||||
var sentAny bool
|
||||
@@ -612,7 +630,7 @@ func (de *endpoint) sendDiscoPingsLocked(now mono.Time, sendCallMeMaybe bool) {
|
||||
de.c.dlogf("[v1] magicsock: disco: send, starting discovery for %v (%v)", de.publicKey.ShortString(), de.discoShort())
|
||||
}
|
||||
|
||||
de.startDiscoPingLocked(ep, now, pingDiscovery)
|
||||
de.startDiscoPingLocked(ep, now, pingDiscovery, 0)
|
||||
}
|
||||
derpAddr := de.derpAddr
|
||||
if sentAny && sendCallMeMaybe && derpAddr.IsValid() {
|
||||
|
@@ -720,7 +720,7 @@ func (c *Conn) LastRecvActivityOfNodeKey(nk key.NodePublic) string {
|
||||
}
|
||||
|
||||
// Ping handles a "tailscale ping" CLI query.
|
||||
func (c *Conn) Ping(peer *tailcfg.Node, res *ipnstate.PingResult, cb func(*ipnstate.PingResult)) {
|
||||
func (c *Conn) Ping(peer *tailcfg.Node, res *ipnstate.PingResult, size int, cb func(*ipnstate.PingResult)) {
|
||||
c.mu.Lock()
|
||||
defer c.mu.Unlock()
|
||||
if c.privateKey.IsZero() {
|
||||
@@ -744,7 +744,7 @@ func (c *Conn) Ping(peer *tailcfg.Node, res *ipnstate.PingResult, cb func(*ipnst
|
||||
cb(res)
|
||||
return
|
||||
}
|
||||
ep.cliPing(res, cb)
|
||||
ep.cliPing(res, size, cb)
|
||||
}
|
||||
|
||||
// c.mu must be held
|
||||
@@ -1262,7 +1262,7 @@ func (c *Conn) sendDiscoMessage(dst netip.AddrPort, dstKey key.NodePublic, dstDi
|
||||
if !dstKey.IsZero() {
|
||||
node = dstKey.ShortString()
|
||||
}
|
||||
c.dlogf("[v1] magicsock: disco: %v->%v (%v, %v) sent %v", c.discoShort, dstDisco.ShortString(), node, derpStr(dst.String()), disco.MessageSummary(m))
|
||||
c.dlogf("[v1] magicsock: disco: %v->%v (%v, %v) sent %v len %v\n", c.discoShort, dstDisco.ShortString(), node, derpStr(dst.String()), disco.MessageSummary(m), len(pkt))
|
||||
}
|
||||
if isDERP {
|
||||
metricSentDiscoDERP.Add(1)
|
||||
@@ -1330,7 +1330,7 @@ func (c *Conn) handleDiscoMessage(msg []byte, src netip.AddrPort, derpNodeSrc ke
|
||||
return
|
||||
}
|
||||
if debugDisco() {
|
||||
c.logf("magicsock: disco: got disco-looking frame from %v via %s", sender.ShortString(), via)
|
||||
c.logf("magicsock: disco: got disco-looking frame from %v via %s len %v", sender.ShortString(), via, len(msg))
|
||||
}
|
||||
if c.privateKey.IsZero() {
|
||||
// Ignore disco messages when we're stopped.
|
||||
|
Reference in New Issue
Block a user