all: implement PingRequest for node connectivity checking

Implement tailcfg.PingRequest support so the control server can verify
whether a connected node is still reachable. This is the foundation for
faster offline detection (currently ~16min due to Go HTTP/2 TCP retransmit
behavior) and future C2N communication.

The server sends a PingRequest via MapResponse with a unique callback
URL. The Tailscale client responds with a HEAD request to that URL,
proving connectivity. Round-trip latency is measured.

Wire PingRequest through the Change → Batcher → MapResponse pipeline,
add a ping tracker on State for correlating requests with responses,
add ResolveNode for looking up nodes by ID/IP/hostname, and expose a
/debug/ping page (elem-go form UI) and /machine/ping-response endpoint.

Updates #2902
Updates #2129
This commit is contained in:
Kristoffer Dalby 2026-04-10 12:46:19 +00:00
parent 32e1d77663
commit b113655b71
9 changed files with 478 additions and 0 deletions

View file

@ -277,6 +277,12 @@ func (b *MapResponseBuilder) buildTailPeers(peers views.Slice[types.NodeView]) (
return tailPeers, nil
}
// WithPingRequest adds a PingRequest to the response.
func (b *MapResponseBuilder) WithPingRequest(pr *tailcfg.PingRequest) *MapResponseBuilder {
b.resp.PingRequest = pr
return b
}
// WithPeerChangedPatch adds peer change patches.
func (b *MapResponseBuilder) WithPeerChangedPatch(changes []*tailcfg.PeerChange) *MapResponseBuilder {
b.resp.PeersChangedPatch = changes

View file

@ -308,6 +308,10 @@ func (m *mapper) buildFromChange(
builder.WithPeerChangedPatch(resp.PeerPatches)
}
if resp.PingRequest != nil {
builder.WithPingRequest(resp.PingRequest)
}
return builder.Build()
}