From 12188c0ade65d5617abd674c7010a5cca9f8519c Mon Sep 17 00:00:00 2001 From: Simon Law Date: Tue, 10 Feb 2026 18:14:32 -0800 Subject: [PATCH] ipn/ipnlocal: log traffic steering scores and suggested exit nodes (#18681) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit When traffic steering is enabled, some users are suggested an exit node that is inappropriately far from their location. This seems to happen right when the client connects to the control plane and the client eventually fixes itself. But whenever an affected client reconnects, its suggested exit node flaps, and this happens often enough to be noticeable because connections drop whenever the exit node is switched. This should not happen, since the map response that contains the list of suggested exit nodes that the client picks from, also contains the scores for those nodes. Since our current logging and diagnostic tools don’t give us enough insight into what is happening, this PR adds additional logging when: - traffic steering scores are used to suggest an exit node - an exit node is suggested, no matter how it was determined Updates: tailscale/corp#29964 Updates: tailscale/corp#36446 Signed-off-by: Simon Law --- ipn/ipnlocal/local.go | 23 +++++++++++++++++++++-- 1 file changed, 21 insertions(+), 2 deletions(-) diff --git a/ipn/ipnlocal/local.go b/ipn/ipnlocal/local.go index 981e2df73..27858484a 100644 --- a/ipn/ipnlocal/local.go +++ b/ipn/ipnlocal/local.go @@ -6,6 +6,7 @@ package ipnlocal import ( + "bufio" "cmp" "context" "crypto/sha256" @@ -7484,13 +7485,16 @@ func suggestExitNode(report *netcheck.Report, nb *nodeBackend, prevSuggestion ta switch { case nb.SelfHasCap(tailcfg.NodeAttrTrafficSteering): // The traffic-steering feature flag is enabled on this tailnet. - return suggestExitNodeUsingTrafficSteering(nb, allowList) + res, err = suggestExitNodeUsingTrafficSteering(nb, allowList) default: // The control plane will always strip the `traffic-steering` // node attribute if it isn’t enabled for this tailnet, even if // it is set in the policy file: tailscale/corp#34401 - return suggestExitNodeUsingDERP(report, nb, prevSuggestion, selectRegion, selectNode, allowList) + res, err = suggestExitNodeUsingDERP(report, nb, prevSuggestion, selectRegion, selectNode, allowList) } + name, _, _ := strings.Cut(res.Name, ".") + nb.logf("netmap: suggested exit node: %s (%s)", name, res.ID) + return res, err } // suggestExitNodeUsingDERP is the classic algorithm used to suggest exit nodes, @@ -7723,6 +7727,21 @@ func suggestExitNodeUsingTrafficSteering(nb *nodeBackend, allowed set.Set[tailcf pick = nodes[0] } + nb.logf("netmap: traffic steering: exit node scores: %v", logger.ArgWriter(func(bw *bufio.Writer) { + const max = 10 + for i, n := range nodes { + if i == max { + fmt.Fprintf(bw, "... +%d", len(nodes)-max) + return + } + if i > 0 { + bw.WriteString(", ") + } + name, _, _ := strings.Cut(n.Name(), ".") + fmt.Fprintf(bw, "%d:%s", score(n), name) + } + })) + if !pick.Valid() { return apitype.ExitNodeSuggestionResponse{}, nil }