8d9a76c751
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.
103 lines
2.3 KiB
Go
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
|
|
}
|