mirror of
https://github.com/juanfont/headscale.git
synced 2025-11-09 05:34:51 -05:00
state/nodestore: in memory representation of nodes
Initial work on a nodestore which stores all of the nodes and their relations in memory with relationship for peers precalculated. It is a copy-on-write structure, replacing the "snapshot" when a change to the structure occurs. It is optimised for reads, and while batches are not fast, they are grouped together to do less of the expensive peer calculation if there are many changes rapidly. Writes will block until commited, while reads are never blocked. Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>
This commit is contained in:
committed by
Kristoffer Dalby
parent
38be30b6d4
commit
9d236571f4
@@ -7,6 +7,7 @@ import (
|
||||
"github.com/juanfont/headscale/hscontrol/policy/matcher"
|
||||
"github.com/juanfont/headscale/hscontrol/types"
|
||||
"github.com/juanfont/headscale/hscontrol/util"
|
||||
"github.com/rs/zerolog/log"
|
||||
"github.com/samber/lo"
|
||||
"tailscale.com/net/tsaddr"
|
||||
"tailscale.com/tailcfg"
|
||||
@@ -138,39 +139,74 @@ func ReduceFilterRules(node types.NodeView, rules []tailcfg.FilterRule) []tailcf
|
||||
return ret
|
||||
}
|
||||
|
||||
// AutoApproveRoutes approves any route that can be autoapproved from
|
||||
// the nodes perspective according to the given policy.
|
||||
// It reports true if any routes were approved.
|
||||
// Note: This function now takes a pointer to the actual node to modify ApprovedRoutes.
|
||||
func AutoApproveRoutes(pm PolicyManager, node *types.Node) bool {
|
||||
// ApproveRoutesWithPolicy checks if the node can approve the announced routes
|
||||
// and returns the new list of approved routes.
|
||||
// The approved routes will include:
|
||||
// 1. ALL previously approved routes (regardless of whether they're still advertised)
|
||||
// 2. New routes from announcedRoutes that can be auto-approved by policy
|
||||
// This ensures that:
|
||||
// - Previously approved routes are ALWAYS preserved (auto-approval never removes routes)
|
||||
// - New routes can be auto-approved according to policy
|
||||
// - Routes can only be removed by explicit admin action (not by auto-approval).
|
||||
func ApproveRoutesWithPolicy(pm PolicyManager, nv types.NodeView, currentApproved, announcedRoutes []netip.Prefix) ([]netip.Prefix, bool) {
|
||||
if pm == nil {
|
||||
return false
|
||||
return currentApproved, false
|
||||
}
|
||||
nodeView := node.View()
|
||||
var newApproved []netip.Prefix
|
||||
for _, route := range nodeView.AnnouncedRoutes() {
|
||||
if pm.NodeCanApproveRoute(nodeView, route) {
|
||||
|
||||
// Start with ALL currently approved routes - we never remove approved routes
|
||||
newApproved := make([]netip.Prefix, len(currentApproved))
|
||||
copy(newApproved, currentApproved)
|
||||
|
||||
// Then, check for new routes that can be auto-approved
|
||||
for _, route := range announcedRoutes {
|
||||
// Skip if already approved
|
||||
if slices.Contains(newApproved, route) {
|
||||
continue
|
||||
}
|
||||
|
||||
// Check if this new route can be auto-approved by policy
|
||||
canApprove := pm.NodeCanApproveRoute(nv, route)
|
||||
if canApprove {
|
||||
newApproved = append(newApproved, route)
|
||||
}
|
||||
}
|
||||
|
||||
// Only modify ApprovedRoutes if we have new routes to approve.
|
||||
// This prevents clearing existing approved routes when nodes
|
||||
// temporarily don't have announced routes during policy changes.
|
||||
if len(newApproved) > 0 {
|
||||
combined := append(newApproved, node.ApprovedRoutes...)
|
||||
tsaddr.SortPrefixes(combined)
|
||||
combined = slices.Compact(combined)
|
||||
combined = lo.Filter(combined, func(route netip.Prefix, index int) bool {
|
||||
return route.IsValid()
|
||||
})
|
||||
// Sort and deduplicate
|
||||
tsaddr.SortPrefixes(newApproved)
|
||||
newApproved = slices.Compact(newApproved)
|
||||
newApproved = lo.Filter(newApproved, func(route netip.Prefix, index int) bool {
|
||||
return route.IsValid()
|
||||
})
|
||||
|
||||
// Only update if the routes actually changed
|
||||
if !slices.Equal(node.ApprovedRoutes, combined) {
|
||||
node.ApprovedRoutes = combined
|
||||
return true
|
||||
// Sort the current approved for comparison
|
||||
sortedCurrent := make([]netip.Prefix, len(currentApproved))
|
||||
copy(sortedCurrent, currentApproved)
|
||||
tsaddr.SortPrefixes(sortedCurrent)
|
||||
|
||||
// Only update if the routes actually changed
|
||||
if !slices.Equal(sortedCurrent, newApproved) {
|
||||
// Log what changed
|
||||
var added, kept []netip.Prefix
|
||||
for _, route := range newApproved {
|
||||
if !slices.Contains(sortedCurrent, route) {
|
||||
added = append(added, route)
|
||||
} else {
|
||||
kept = append(kept, route)
|
||||
}
|
||||
}
|
||||
|
||||
if len(added) > 0 {
|
||||
log.Debug().
|
||||
Uint64("node.id", nv.ID().Uint64()).
|
||||
Str("node.name", nv.Hostname()).
|
||||
Strs("routes.added", util.PrefixesToString(added)).
|
||||
Strs("routes.kept", util.PrefixesToString(kept)).
|
||||
Int("routes.total", len(newApproved)).
|
||||
Msg("Routes auto-approved by policy")
|
||||
}
|
||||
|
||||
return newApproved, true
|
||||
}
|
||||
|
||||
return false
|
||||
return newApproved, false
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user