Files
tailscale/net/routecheck/routes.go
T
Simon Law 28801674a6 net/routecheck: introduce new package for checking peer reachability (#19639)
The routecheck package parallels the netcheck package, where the
former checks routes and routers while the latter checks networks.
Like netcheck, it compiles reports for other systems to consume.

Historically, the client has never known whether a peer is actually
reachable. Most of the time this doesn’t matter, since the client will
want to establish a WireGuard tunnel to any given destination.
However, if the client needs to choose between two or more nodes,
then it should try to choose a node that it can reach.

Suggested exit nodes are one such example, where the client filters
out any nodes that aren’t connected to the control plane. Sometimes an
exit node will get disconnected from the control plane: when the
network between the two is unreliable or when the exit node is too
busy to keep its control connection alive. In these cases, Control
disables the Node.Online flag for the exit node and broadcasts this
across the tailnet. Arguably, the client should never have relied on
this flag, since it only makes sense in the admin console.

This patch implements an initial routecheck client that can probe
every node that your client knows about. You should not ping scan your
visible tailnet, this method is for debugging only.

This patch also introduces a new OnNetMapToggle hook, which fires when
the netmap transitions from nil to non-nil, or vice versa. This
happens either when the client receives its first MapResponse after
connecting to the control plane, or when it clears the netmap while it
is disconnecting. Routecheck uses this to wait for a valid netmap
so it knows which peers to probe.

Updates #17366
Updates tailscale/corp#33033

Signed-off-by: Simon Law <sfllaw@tailscale.com>
2026-06-01 10:33:08 -07:00

52 lines
1.7 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
// Copyright (c) Tailscale Inc & contributors
// SPDX-License-Identifier: BSD-3-Clause
package routecheck
import (
"net/netip"
"tailscale.com/tailcfg"
"tailscale.com/types/views"
"tailscale.com/util/mak"
)
// RoutersByPrefix represents a map of nodes grouped by the subnet that they route.
// Nodes that route for /0 prefixes are exit nodes, their subnet is the Internet.
// The result omits any prefix that is one of a nodes local addresses.
//
// Note: Fallback routes are not supported by design. If a subnet prefix
// contained within another more general prefix has no reachable routers,
// traffic is still sent to one of those unreachable routers.
// Routers for the general prefix arent candidates. See tailscale/tailscale#18550.
type RoutersByPrefix map[netip.Prefix][]tailcfg.NodeView
// RoutersByPrefix returns a map of nodes grouped by the subnet that they route.
// See [RoutersByPrefix] for more detail.
func (c *Client) RoutersByPrefix() RoutersByPrefix {
var routers RoutersByPrefix
for _, n := range c.nb.NodeBackend().Peers() {
for _, pfx := range routes(n) {
mak.Set(&routers, pfx, append(routers[pfx], n))
}
}
return routers
}
// Routes returns a slice of subnets that the given node will route.
// If the node is an exit node, the result will contain at least one /0 prefix.
// If the node is a subnet router, the result will contain a smaller prefix.
// The result omits any prefix that is one of the nodes local addresses.
func routes(n tailcfg.NodeView) []netip.Prefix {
var routes []netip.Prefix
AllowedIPs:
for _, pfx := range n.AllowedIPs().All() {
// Routers never forward their own local addresses.
if views.SliceContains(n.Addresses(), pfx) {
continue AllowedIPs
}
routes = append(routes, pfx)
}
return routes
}