types: split SubnetRoutes and ExitRoutes

There are situations where the subnet routes and exit nodes
must be treated differently. This splits it so SubnetRoutes
only returns routes that are not exit routes.

It adds `IsExitRoutes` and `AllApprovedRoutes` for convenience.

Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>
This commit is contained in:
Kristoffer Dalby
2025-11-01 14:25:07 +01:00
committed by Kristoffer Dalby
parent c649c89e00
commit 1c0bb0338d

View File

@@ -269,11 +269,19 @@ func (node *Node) Prefixes() []netip.Prefix {
// node has any exit routes enabled. // node has any exit routes enabled.
// If none are enabled, it will return nil. // If none are enabled, it will return nil.
func (node *Node) ExitRoutes() []netip.Prefix { func (node *Node) ExitRoutes() []netip.Prefix {
if slices.ContainsFunc(node.SubnetRoutes(), tsaddr.IsExitRoute) { var routes []netip.Prefix
return tsaddr.ExitRoutes()
for _, route := range node.AnnouncedRoutes() {
if tsaddr.IsExitRoute(route) && slices.Contains(node.ApprovedRoutes, route) {
routes = append(routes, route)
}
} }
return nil return routes
}
func (node *Node) IsExitNode() bool {
return len(node.ExitRoutes()) > 0
} }
func (node *Node) IPsAsString() []string { func (node *Node) IPsAsString() []string {
@@ -440,16 +448,22 @@ func (node *Node) AnnouncedRoutes() []netip.Prefix {
return node.Hostinfo.RoutableIPs return node.Hostinfo.RoutableIPs
} }
// SubnetRoutes returns the list of routes that the node announces and are approved. // SubnetRoutes returns the list of routes (excluding exit routes) that the node
// announces and are approved.
// //
// IMPORTANT: This method is used for internal data structures and should NOT be used // IMPORTANT: This method is used for internal data structures and should NOT be
// for the gRPC Proto conversion. For Proto, SubnetRoutes must be populated manually // used for the gRPC Proto conversion. For Proto, SubnetRoutes must be populated
// with PrimaryRoutes to ensure it includes only routes actively served by the node. // manually with PrimaryRoutes to ensure it includes only routes actively served
// See the comment in Proto() method and the implementation in grpcv1.go/nodesToProto. // by the node. See the comment in Proto() method and the implementation in
// grpcv1.go/nodesToProto.
func (node *Node) SubnetRoutes() []netip.Prefix { func (node *Node) SubnetRoutes() []netip.Prefix {
var routes []netip.Prefix var routes []netip.Prefix
for _, route := range node.AnnouncedRoutes() { for _, route := range node.AnnouncedRoutes() {
if tsaddr.IsExitRoute(route) {
continue
}
if slices.Contains(node.ApprovedRoutes, route) { if slices.Contains(node.ApprovedRoutes, route) {
routes = append(routes, route) routes = append(routes, route)
} }
@@ -463,6 +477,11 @@ func (node *Node) IsSubnetRouter() bool {
return len(node.SubnetRoutes()) > 0 return len(node.SubnetRoutes()) > 0
} }
// AllApprovedRoutes returns the combination of SubnetRoutes and ExitRoutes
func (node *Node) AllApprovedRoutes() []netip.Prefix {
return append(node.SubnetRoutes(), node.ExitRoutes()...)
}
func (node *Node) String() string { func (node *Node) String() string {
return node.Hostname return node.Hostname
} }
@@ -653,6 +672,7 @@ func (node Node) DebugString() string {
fmt.Fprintf(&sb, "\tApprovedRoutes: %v\n", node.ApprovedRoutes) fmt.Fprintf(&sb, "\tApprovedRoutes: %v\n", node.ApprovedRoutes)
fmt.Fprintf(&sb, "\tAnnouncedRoutes: %v\n", node.AnnouncedRoutes()) fmt.Fprintf(&sb, "\tAnnouncedRoutes: %v\n", node.AnnouncedRoutes())
fmt.Fprintf(&sb, "\tSubnetRoutes: %v\n", node.SubnetRoutes()) fmt.Fprintf(&sb, "\tSubnetRoutes: %v\n", node.SubnetRoutes())
fmt.Fprintf(&sb, "\tExitRoutes: %v\n", node.ExitRoutes())
sb.WriteString("\n") sb.WriteString("\n")
return sb.String() return sb.String()
@@ -730,6 +750,13 @@ func (v NodeView) IsSubnetRouter() bool {
return v.ж.IsSubnetRouter() return v.ж.IsSubnetRouter()
} }
func (v NodeView) AllApprovedRoutes() []netip.Prefix {
if !v.Valid() {
return nil
}
return v.ж.AllApprovedRoutes()
}
func (v NodeView) AppendToIPSet(build *netipx.IPSetBuilder) { func (v NodeView) AppendToIPSet(build *netipx.IPSetBuilder) {
if !v.Valid() { if !v.Valid() {
return return
@@ -808,6 +835,13 @@ func (v NodeView) ExitRoutes() []netip.Prefix {
return v.ж.ExitRoutes() return v.ж.ExitRoutes()
} }
func (v NodeView) IsExitNode() bool {
if !v.Valid() {
return false
}
return v.ж.IsExitNode()
}
// RequestTags returns the ACL tags that the node is requesting. // RequestTags returns the ACL tags that the node is requesting.
func (v NodeView) RequestTags() []string { func (v NodeView) RequestTags() []string {
if !v.Valid() || !v.Hostinfo().Valid() { if !v.Valid() || !v.Hostinfo().Valid() {