Files
tomswall/internal/config/nesting.go
T
unkinben 8d9a76c751 Add comprehensive nftables compiler with shorewall feature parity
Rewrites the compiler from ~440 to ~1700 lines covering all major shorewall
firewall features: loopback, conntrack fast-path, anti-spoof, DHCP, intra-zone,
blacklist/whitelist, conntrack notrack, tunnels (13 types), rules with sections,
DNAT/redirect, SNAT/masquerade, static NAT, policies with zone exclusions,
MSS clamping, rate limiting, connection limiting, negated addresses, ICMP type
matching, TCP RST reject, user/UID matching, mark match/set, NFQUEUE, NONAT,
and policy-level rate/conn limiting.

Adds full config types for all shorewall subsystems (mangle, accounting, maclist,
netmap, providers, tunnels, conntrack, blrules, proxyarp/ndp, routes, tc, secmarks),
shorewall migration tooling, expanded CLI commands, expression-level diff engine,
and 49 unit tests.
2026-07-01 23:56:44 +10:00

103 lines
2.3 KiB
Go

package config
import "fmt"
// ResolveNesting computes the effective zone order, ensuring child zones
// appear before their parents. This determines the order in which packets
// are matched against zones — more specific (child) zones are checked first.
func (c *Config) ResolveNesting() ([]string, error) {
resolved := make(map[string]bool)
var order []string
for range c.Zones {
progress := false
for name, zone := range c.Zones {
if resolved[name] {
continue
}
ready := true
for _, parent := range zone.Parents {
if c.Zones[parent].Type == ZoneFirewall {
continue
}
for childName, childZone := range c.Zones {
if childName == name || resolved[childName] {
continue
}
for _, cp := range childZone.Parents {
if cp == parent && !resolved[childName] {
ready = false
}
}
}
}
_ = ready
if !resolved[name] && allParentsDepsResolved(name, c.Zones, resolved) {
resolved[name] = true
order = append(order, name)
progress = true
}
}
if !progress {
break
}
}
if len(order) != len(c.Zones) {
return nil, fmt.Errorf("circular zone nesting detected")
}
return order, nil
}
func allParentsDepsResolved(name string, zones map[string]Zone, resolved map[string]bool) bool {
zone := zones[name]
if len(zone.Parents) == 0 {
return true
}
for _, parent := range zone.Parents {
for childName, childZone := range zones {
if childName == name || childName == parent {
continue
}
for _, cp := range childZone.Parents {
if cp == parent && !resolved[childName] {
return false
}
}
}
}
return true
}
// ChildZones returns all zones that list the given zone as a parent.
func (c *Config) ChildZones(parent string) []string {
var children []string
for name, zone := range c.Zones {
for _, p := range zone.Parents {
if p == parent {
children = append(children, name)
break
}
}
}
return children
}
// IsSubZone returns true if child is a sub-zone of parent (directly or transitively).
func (c *Config) IsSubZone(child, parent string) bool {
zone, ok := c.Zones[child]
if !ok {
return false
}
for _, p := range zone.Parents {
if p == parent {
return true
}
if c.IsSubZone(p, parent) {
return true
}
}
return false
}