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.
118 lines
3.9 KiB
Go
118 lines
3.9 KiB
Go
package config
|
|
|
|
import "fmt"
|
|
|
|
// Provider defines an additional routing table for multi-ISP or policy routing.
|
|
type Provider struct {
|
|
// Provider name. Must be a valid name; "local", "main", "default", "unspec" are reserved.
|
|
Name string `yaml:"name"`
|
|
|
|
// Routing table number (1-252). Must be unique per provider.
|
|
Number int `yaml:"number"`
|
|
|
|
// FWMARK value for directing packets to this provider via mangle rules.
|
|
Mark int `yaml:"mark,omitempty"`
|
|
|
|
// Existing routing table to duplicate (e.g. "main" or another provider name).
|
|
Duplicate string `yaml:"duplicate,omitempty"`
|
|
|
|
// Network interface to the provider. Must be defined in interfaces.
|
|
// Format: interface or interface:address (when multiple providers share an interface).
|
|
Interface string `yaml:"interface"`
|
|
|
|
// Gateway address. Supports: IP address, "detect", "none", or omit for PPP.
|
|
Gateway string `yaml:"gateway,omitempty"`
|
|
|
|
Options ProviderOptions `yaml:"options,omitempty"`
|
|
|
|
// Interfaces to copy routes from when duplicating. Use "none" to only copy
|
|
// routes through the provider's own interface.
|
|
Copy []string `yaml:"copy,omitempty"`
|
|
}
|
|
|
|
type ProviderOptions struct {
|
|
// Track inbound connections so responses route back out this interface.
|
|
Track bool `yaml:"track,omitempty"`
|
|
|
|
// Load-balance outbound traffic across providers with balance set.
|
|
// Set to 1 for equal weight, or higher for more weight.
|
|
Balance int `yaml:"balance,omitempty"`
|
|
|
|
// Alternative load balancing via probability (0 < p <= 1).
|
|
Load float64 `yaml:"load,omitempty"`
|
|
|
|
// Do not create per-address routing rules for this interface.
|
|
Loose bool `yaml:"loose,omitempty"`
|
|
|
|
// Add a default route through this provider to the main routing table.
|
|
// Set to 1 for equal weight, or higher for more weight.
|
|
Fallback int `yaml:"fallback,omitempty"`
|
|
|
|
// Mark this as the primary provider (equivalent to balance=1).
|
|
Primary bool `yaml:"primary,omitempty"`
|
|
|
|
// Source address for traffic routed through this provider.
|
|
Src string `yaml:"src,omitempty"`
|
|
|
|
// MTU override when forwarding through this provider.
|
|
MTU int `yaml:"mtu,omitempty"`
|
|
|
|
// TPROXY provider for transparent proxying. When set, mark/duplicate/gateway
|
|
// should be empty and interface should be "lo".
|
|
TProxy bool `yaml:"tproxy,omitempty"`
|
|
|
|
// Allow the firewall to start even if this provider's interface is not up.
|
|
Optional bool `yaml:"optional,omitempty"`
|
|
|
|
// Provider survives disable — routing table keeps its default route.
|
|
Persistent bool `yaml:"persistent,omitempty"`
|
|
}
|
|
|
|
var reservedProviderNames = map[string]bool{
|
|
"local": true, "main": true, "default": true, "unspec": true,
|
|
}
|
|
|
|
func (c *Config) validateProviders() error {
|
|
seenNumbers := make(map[int]string)
|
|
seenNames := make(map[string]bool)
|
|
|
|
for i, p := range c.Providers {
|
|
if p.Name == "" {
|
|
return fmt.Errorf("provider[%d]: name required", i)
|
|
}
|
|
if err := ValidateName(p.Name, "provider"); err != nil {
|
|
return fmt.Errorf("provider[%d]: %w", i, err)
|
|
}
|
|
if reservedProviderNames[p.Name] {
|
|
return fmt.Errorf("provider[%d]: %q is a reserved name", i, p.Name)
|
|
}
|
|
if seenNames[p.Name] {
|
|
return fmt.Errorf("provider[%d]: duplicate name %q", i, p.Name)
|
|
}
|
|
seenNames[p.Name] = true
|
|
|
|
if p.Number < 1 || p.Number > 252 {
|
|
return fmt.Errorf("provider[%d] %q: number must be between 1 and 252", i, p.Name)
|
|
}
|
|
if existing, ok := seenNumbers[p.Number]; ok {
|
|
return fmt.Errorf("provider[%d] %q: number %d already used by %q", i, p.Name, p.Number, existing)
|
|
}
|
|
seenNumbers[p.Number] = p.Name
|
|
|
|
if p.Interface == "" {
|
|
return fmt.Errorf("provider[%d] %q: interface required", i, p.Name)
|
|
}
|
|
|
|
if p.Options.TProxy {
|
|
if p.Mark != 0 || p.Duplicate != "" || p.Gateway != "" {
|
|
return fmt.Errorf("provider[%d] %q: tproxy provider must have empty mark, duplicate, and gateway", i, p.Name)
|
|
}
|
|
}
|
|
|
|
if p.Options.Load != 0 && (p.Options.Load <= 0 || p.Options.Load > 1) {
|
|
return fmt.Errorf("provider[%d] %q: load probability must be between 0 (exclusive) and 1 (inclusive)", i, p.Name)
|
|
}
|
|
}
|
|
return nil
|
|
}
|