Files
tomswall/internal/config/nat.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

63 lines
1.8 KiB
Go

package config
import (
"fmt"
"net"
)
// StaticNAT defines a one-to-one NAT mapping between an external and internal address.
// All traffic to the external address is forwarded to the internal address and vice versa.
// DNAT rules take precedence over static NAT rules.
type StaticNAT struct {
// External IP address. Must not be the primary address of the interface.
// DNS names are not allowed.
External string `yaml:"external"`
// Interface that has the external address.
Interface string `yaml:"interface"`
// Internal IP address. DNS names are not allowed.
Internal string `yaml:"internal"`
// If true, NAT is effective from all hosts (not just those on the named interface).
AllInterfaces bool `yaml:"all_interfaces,omitempty"`
// If true, NAT is effective from the firewall system itself.
Local bool `yaml:"local,omitempty"`
Comment string `yaml:"comment,omitempty"`
}
func (c *Config) validateStaticNAT() error {
for i, n := range c.StaticNAT {
if n.External == "" {
return fmt.Errorf("nat[%d]: external address required", i)
}
if net.ParseIP(n.External) == nil {
return fmt.Errorf("nat[%d]: external must be an IP address, not a DNS name", i)
}
if n.Interface == "" {
return fmt.Errorf("nat[%d]: interface required", i)
}
ifaceFound := false
for _, iface := range c.Interfaces {
if iface.Interface == n.Interface || iface.PhysicalName() == n.Interface {
ifaceFound = true
break
}
}
if !ifaceFound {
return fmt.Errorf("nat[%d]: interface %q not defined in interfaces", i, n.Interface)
}
if n.Internal == "" {
return fmt.Errorf("nat[%d]: internal address required", i)
}
if net.ParseIP(n.Internal) == nil {
return fmt.Errorf("nat[%d]: internal must be an IP address, not a DNS name", i)
}
}
return nil
}