client/local,ipn/localapi: add /localapi/v0/routecheck endpoint (#19640)

In order to support a `tailscale routecheck` command, we introduce the
`/localapi/v0/routecheck` endpoint to the local API. This endpoint
returns the most recent report collected by the routecheck client.
If `force=true` is an argument in the query string, then this endpoint
will actively probe before returning the report.

Updates #17366
Updates tailscale/corp#33033

Signed-off-by: Simon Law <sfllaw@tailscale.com>
This commit is contained in:
Simon Law
2026-06-01 11:06:14 -07:00
committed by GitHub
parent 28801674a6
commit 2ee9eacb94
14 changed files with 335 additions and 35 deletions
+16
View File
@@ -50,6 +50,12 @@ func (c *Client) probe(ctx context.Context, nodes iter.Seq[probed], limit int, t
var mu syncs.Mutex
r := &Report{}
timestampProbe := func(n probed) {
mu.Lock()
defer mu.Unlock()
mak.Set(&r.LastProbed, n.ID(), time.Now())
}
markReachable := func(n probed) {
mu.Lock()
defer mu.Unlock()
@@ -81,12 +87,22 @@ func (c *Client) probe(ctx context.Context, nodes iter.Seq[probed], limit int, t
// TODO(sfllaw): Add a mechanism to mark a node as unreachable
// because it fails of establish a new WireGuard connection.
if n.IsWireGuardOnly() {
timestampProbe(n)
markReachable(n)
continue
}
g.Go(func() error {
metricPing.Add(1)
// We record the timestamp of each nodes latest probe
// so we can probe in incremental batches
// and to limit the rate that any given node is pinged.
//
// TODO(sfllaw): We currently record the timestamp
// but havent implemented batching or rate-limiting yet.
defer timestampProbe(n)
// TODO(sfllaw): Why did we choose Disco ping instead of TSMP ping?
// After all, a TSMP ping proves that the peer Tailscale node is there
// and that both nodes know each others WireGuard keys,