From 2c70845bda6d2d61586243eb54730ca4b2a1c27a Mon Sep 17 00:00:00 2001 From: Kristoffer Dalby Date: Sat, 31 Oct 2026 14:11:10 +0000 Subject: [PATCH] policy/v2: count a router in a subnet source as a via viewer SaaS sends it the via route (via-grant-v83). Updates #3626 --- hscontrol/policy/v2/policy.go | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/hscontrol/policy/v2/policy.go b/hscontrol/policy/v2/policy.go index f7da39895..a3c9e8c47 100644 --- a/hscontrol/policy/v2/policy.go +++ b/hscontrol/policy/v2/policy.go @@ -1600,6 +1600,7 @@ func (pm *PolicyManager) ViaRoutesForPeer(viewer, peer types.NodeView) types.Via // grants are only needed once a via grant has matched, and // destinations only for grants that match the viewer. viewerIPs := viewer.IPs() + viewerRoutes := viewer.SubnetRoutes() resolved := make([]bool, len(grants)) viewerMatchesGrant := make([]bool, len(grants)) resolvedDstPrefixes := make([][]netip.Prefix, len(grants)) @@ -1618,7 +1619,16 @@ func (pm *PolicyManager) ViaRoutesForPeer(viewer, peer types.NodeView) types.Via continue } - if ips != nil && slices.ContainsFunc(viewerIPs, ips.Contains) { + if ips == nil { + continue + } + + // A router acts for its subnets, as in [types.Node.CanAccessRoutes], + // so a source naming one of them matches the router. + if slices.ContainsFunc(viewerIPs, ips.Contains) || + slices.ContainsFunc(viewerRoutes, func(route netip.Prefix) bool { + return slices.ContainsFunc(ips.Prefixes(), route.Overlaps) + }) { viewerMatchesGrant[i] = true break