summaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authorSimon Law <sfllaw@tailscale.com>2025-08-04 11:51:42 -0700
committerSimon Law <sfllaw@tailscale.com>2025-08-04 11:51:42 -0700
commitb280aa9c44844d833d8a27084b4be8ed326fb561 (patch)
treecbd683ad2cfdfd7420b9332bad0dda68212e5166
parent0c6bf305857acbbdb6004f08a77e4a7d36778986 (diff)
downloadtailscale-sfllaw/traffic-steering/suggest-exit-node-steering.tar.xz
tailscale-sfllaw/traffic-steering/suggest-exit-node-steering.zip
ipn/ipnlocal: parse priority out of suggest-exit-node capabilitysfllaw/traffic-steering/suggest-exit-node-steering
When the `experimental:exit-node-steering` attribute is enabled for a node, it is likely that the priority score for an exit node is different when considering it as a suggested exit node versus considering it for the routers / connectors that it hosts. In order to distinguish between these two, the network map can now contain the priority score for suggesting the exit node in the `suggest-exit-node` capability, in that peer’s CapMap: "CapMap": { "suggest-exit-node": [ { "Priority": 42 } ] } Updates tailscale/corp#31011 Signed-off-by: Simon Law <sfllaw@tailscale.com>
-rw-r--r--ipn/ipnlocal/local.go22
-rw-r--r--ipn/ipnlocal/local_test.go70
-rw-r--r--tailcfg/traffic.go13
3 files changed, 104 insertions, 1 deletions
diff --git a/ipn/ipnlocal/local.go b/ipn/ipnlocal/local.go
index 5fb3d5771..6b8b35b53 100644
--- a/ipn/ipnlocal/local.go
+++ b/ipn/ipnlocal/local.go
@@ -7952,12 +7952,32 @@ func suggestExitNodeUsingTrafficSteering(nb *nodeBackend, allowed set.Set[tailcf
id := n.ID()
s, ok := scores[id]
if !ok {
- s = 0 // score of zero means incomparable
+ // score of zero means incomparable
+ s = 0
+
+ // Prefer the priority in the suggest-exit-node peer cap.
+ if caps, ok := n.CapMap().GetOk(tailcfg.NodeAttrSuggestExitNode); ok {
+ for _, cap := range caps.All() {
+ var c tailcfg.SuggestExitNode
+ if err := json.Unmarshal([]byte(cap), &c); err != nil {
+ break
+ }
+ if c.Priority == 0 {
+ break
+ }
+ s = c.Priority
+ goto SetScore
+ }
+ }
+
+ // Fallback on the peer’s location priority.
if hi := n.Hostinfo(); hi.Valid() {
if loc := hi.Location(); loc.Valid() {
s = loc.Priority()
}
}
+
+ SetScore:
scores[id] = s
}
return s
diff --git a/ipn/ipnlocal/local_test.go b/ipn/ipnlocal/local_test.go
index b7fe7f9d7..335c0c1e1 100644
--- a/ipn/ipnlocal/local_test.go
+++ b/ipn/ipnlocal/local_test.go
@@ -5077,6 +5077,76 @@ func TestSuggestExitNodeTrafficSteering(t *testing.T) {
wantName: "peer3",
},
{
+ name: "exit-node-empty-suggestions",
+ netMap: &netmap.NetworkMap{
+ SelfNode: selfNode.View(),
+ Peers: []tailcfg.NodeView{
+ makePeer(1,
+ withExitRoutes(),
+ withSuggest(nil)),
+ makePeer(2,
+ withExitRoutes(),
+ withSuggest([]tailcfg.RawMessage{})),
+ makePeer(3,
+ withExitRoutes(),
+ withSuggest([]tailcfg.RawMessage{
+ `{}`,
+ })),
+ makePeer(4,
+ withExitRoutes(),
+ withSuggest([]tailcfg.RawMessage{
+ `{"Priority": 0}`,
+ })),
+ },
+ },
+ // Change this, if the hashing function changes.
+ wantID: "stable3",
+ wantName: "peer3",
+ },
+ {
+ name: "exit-node-with-suggestion-and-priority",
+ netMap: &netmap.NetworkMap{
+ SelfNode: selfNode.View(),
+ Peers: []tailcfg.NodeView{
+ makePeer(1,
+ withExitRoutes(),
+ withSuggest([]tailcfg.RawMessage{
+ `{"Priority": 3}`,
+ }),
+ withLocationPriority(1)), // overridden
+ makePeer(2,
+ withExitRoutes(),
+ withLocationPriority(2)),
+ },
+ },
+ wantID: "stable1",
+ wantName: "peer1",
+ wantPri: 1, // Location.Priority
+ },
+ {
+ name: "exit-node-with-priority",
+ netMap: &netmap.NetworkMap{
+ SelfNode: selfNode.View(),
+ Peers: []tailcfg.NodeView{
+ makePeer(1,
+ withExitRoutes(),
+ withSuggest(nil)),
+ makePeer(2,
+ withExitRoutes(),
+ withSuggest(nil)),
+ makePeer(3,
+ withExitRoutes(),
+ withSuggest(nil)),
+ makePeer(4,
+ withExitRoutes(),
+ withSuggest(nil)),
+ },
+ },
+ // Change this, if the hashing function changes.
+ wantID: "stable3",
+ wantName: "peer3",
+ },
+ {
name: "exit-nodes-with-and-without-priority",
netMap: &netmap.NetworkMap{
SelfNode: selfNode.View(),
diff --git a/tailcfg/traffic.go b/tailcfg/traffic.go
new file mode 100644
index 000000000..224a39bed
--- /dev/null
+++ b/tailcfg/traffic.go
@@ -0,0 +1,13 @@
+// Copyright (c) Tailscale Inc & AUTHORS
+// SPDX-License-Identifier: BSD-3-Clause
+
+package tailcfg
+
+type SuggestExitNode struct {
+ // Priority is the relative priority of this exit node. Nodes with a
+ // higher priority are preferred over nodes with a lower priority, nodes
+ // of equal probability may be selected arbitrarily. A priority of 0
+ // means the exit node has no a priority preference and a negative
+ // priority is not allowed.
+ Priority int `json:",omitempty"`
+}