Network address translation lets many hosts share one public address, and lets you publish a service running on an internal machine. Both need the kernel to forward packets, a source or destination rewrite, and usually a rule that decides what may cross.
Outgoing LAN traffic gets the WAN address as its source. The router remembers each connection and sends the replies back to the right host. Masquerade (rather than a fixed snat) suits a WAN address that changes.
table ip router_nat {
chain postrouting {
type nat hook postrouting priority srcnat; policy accept;
oifname "eth0" ip saddr { 192.168.1.0/24 } masquerade
}
}A rewrite on the way in: traffic arriving on a WAN port gets a new destination inside the LAN. The reply path is handled automatically by connection tracking.
iifname "eth0" tcp dport 8080 dnat to 192.168.1.10:80None of this works unless the kernel forwards packets: net.ipv4.ip_forward must be 1. Put it in /etc/sysctl.d so it survives a reboot.
net.ipv4.ip_forward = 1A LAN host that connects to the router's public address expects the forwarded service, but the packet never leaves the LAN. A second rewrite on the way back, and a masquerade for the LAN-to-LAN leg, fixes it.
With the forward chain set to drop by default, allow replies, LAN to WAN, and traffic that was DNAT-ed. Rules in another table that drop a packet still win, because a packet must be accepted by every table that sees it.
Open the NAT & port forwarding generator
Traffic to the WAN address on port 22 is rewritten to the internal host, so SSH to the router itself no longer answers there. Keep another way in first.
IPv6 does not need NAT: hosts get global addresses. Filter them with the firewall instead.
They use their own tables, so they coexist, but a drop in the other firewall still blocks the forward.