package config import "fmt" type SNATAction string const ( SNATMasquerade SNATAction = "masquerade" SNATAddress SNATAction = "snat" SNATContinue SNATAction = "continue" SNATLog SNATAction = "log" ) // SNATRule defines a source NAT or masquerade rule. // Rules are evaluated in order — first match wins. type SNATRule struct { Action SNATAction `yaml:"action"` // For SNAT: the source address (or address range first-last) to rewrite to. // Supports: single IP, IP range (1.2.3.4-1.2.3.7), or "detect" (use interface addresses). Address string `yaml:"address,omitempty"` // Port remapping for SNAT/MASQUERADE. Format: lowport-highport or single port. // Requires proto to be tcp, udp, dccp, or sctp. PortRange string `yaml:"port_range,omitempty"` // Randomize port mapping. Random bool `yaml:"random,omitempty"` // Give a client the same source/destination IP pair (only with address ranges). Persistent bool `yaml:"persistent,omitempty"` // Source addresses/networks to match for masquerading. // Supports: CIDR, host address, comma-separated list, ipset (+name). Source string `yaml:"source,omitempty"` // Outgoing interface(s) and optional destination address qualification. // Format: interface, interface:dest-address, or comma-separated interfaces. // Use "$FW" for SNAT in the INPUT chain. Dest string `yaml:"dest"` // Protocol restriction. Comma-separated list allowed. Proto string `yaml:"proto,omitempty"` // Destination port(s). DPort PortSpec `yaml:"dport,omitempty"` // Source port(s). SPort PortSpec `yaml:"sport,omitempty"` // Packet/connection mark test. Format: [!]value[/mask][:C] Mark string `yaml:"mark,omitempty"` // Original destination address filter — match only connections that were // previously DNAT'd to these addresses. OrigDest string `yaml:"origdest,omitempty"` // Random matching probability (0 < p <= 1) for load-balancing across // multiple SNAT addresses. Probability float64 `yaml:"probability,omitempty"` // Log level (for log action, or appended to other actions). Log string `yaml:"log,omitempty"` Comment string `yaml:"comment,omitempty"` } func (c *Config) validateSNAT() error { for i, s := range c.SNAT { switch s.Action { case SNATMasquerade, SNATAddress, SNATContinue, SNATLog: default: return fmt.Errorf("snat[%d]: unknown action %q", i, s.Action) } if s.Action == SNATAddress && s.Address == "" { return fmt.Errorf("snat[%d]: address required for snat action", i) } if s.Dest == "" { return fmt.Errorf("snat[%d]: dest required", i) } if s.PortRange != "" && s.Proto == "" { return fmt.Errorf("snat[%d]: port_range requires proto (tcp, udp, dccp, or sctp)", i) } if s.Persistent && s.Address == "" { return fmt.Errorf("snat[%d]: persistent requires an address or address range", i) } if s.Probability != 0 && (s.Probability <= 0 || s.Probability > 1) { return fmt.Errorf("snat[%d]: probability must be between 0 (exclusive) and 1 (inclusive)", i) } } return nil }