NAT and Firewalls

Why everyone is behind a NAT, how it breaks the end-to-end principle, and the firewall stances (stateful vs stateless) you'll meet.

Building Block Intermediate
9 min read
nat firewall network-layer security

What it is#

NAT (Network Address Translation, RFC 3022) is the mechanism that lets many devices share a single public IPv4 address. A NAT box — usually the router on your home or office Internet uplink — sits between an internal network using private addresses (10.0.0.0/8, 172.16.0.0/12, 192.168.0.0/16) and the public Internet. It rewrites the source IP and port on outbound packets to a single public IP and a per-flow port, remembers the mapping in a translation table, and rewrites the destination on the return packets back to the original private IP.

A firewall, in contrast, is a packet-filtering and connection-tracking gatekeeper. It permits or denies traffic based on rules — IP, port, protocol, and (for stateful firewalls) connection state. NAT and firewalling are conceptually distinct but almost always live in the same device, because both require deep packet inspection and connection tracking.

NAT solved IPv4 exhaustion at the cost of breaking the end-to-end principle. Every connection becomes asymmetric: outbound is easy, inbound requires explicit port forwarding or hole-punching. IPv6 with its huge address space restores end-to-end reachability and removes the need for NAT, but firewalls are forever.

When to use it#

NAT is not so much chosen as inherited — almost every IPv4 network sits behind one:

  • Home and SOHO routers. Always NAT, always stateful firewall, usually with UPnP for opportunistic port forwarding.
  • Enterprise edge. NAT plus a layered firewall (perimeter, then DMZ, then internal); often combined with proxies for outbound filtering.
  • Cloud egress gateways. AWS NAT Gateway, GCP Cloud NAT, Azure NAT Gateway — let a fleet of private-IP VMs reach the Internet over a small pool of public IPs.
  • Carrier-grade NAT. ISPs no longer give residential customers a public IPv4; they live behind a shared CGNAT in the 100.64.0.0/10 range.
  • Container fabrics. Docker uses MASQUERADE to NAT pod traffic onto the host’s IP; Kubernetes Service-of-type-LoadBalancer typically chains NAT through the cloud LB.

Firewalls are universal: a stateless firewall (ACL) on every router interface, a stateful firewall on every host (iptables / nftables / Windows Defender Firewall), and dedicated firewall appliances at security perimeters. The questions are which model and at which layer, not whether.

How it works#

Source NAT (the common case)#

Internal host NAT box Internet
192.168.1.10:54321 Public: 203.0.113.5
| | |
| TCP src 192.168.1.10:54321 dst 8.8.8.8:443 ---> |
| | |
| | rewrite src -> |
| | 203.0.113.5:40001 |
| | record mapping |
| | |
| | -- TCP src 203.0.113.5:40001 ----> |
| | |
| | <-- TCP dst 203.0.113.5:40001 ----- |
| | look up mapping |
| | rewrite dst -> |
| | 192.168.1.10:54321 |
| <-- TCP dst 192.168.1.10:54321 src 8.8.8.8:443 -- |

The NAT’s translation table looks like:

proto internal external peer state timeout
tcp 192.168.1.10:54321 203.0.113.5:40001 8.8.8.8:443 ESTAB 432000s
tcp 192.168.1.11:33102 203.0.113.5:40002 151.101.1.69:443 ESTAB 432000s
udp 192.168.1.20:51820 203.0.113.5:40050 1.1.1.1:53 -- 180s
icmp 192.168.1.10:id=100 203.0.113.5:id=2050 8.8.8.8 -- 60s

The mapping is keyed on the 5-tuple. UDP entries time out in ~3 minutes by default; TCP entries hold for ~5 days unless cleaned up by RST or FIN; ICMP echo uses the ICMP identifier as a faux port and expires within a minute.

NAT behaviours (RFC 4787)#

For UDP, NAT boxes are classified by two independent axes:

  • Mapping behaviour — whether two outbound flows from the same internal IP:port share an external port. Endpoint-independent mapping reuses the port for any peer; address-dependent changes it per peer IP; address-and-port-dependent changes it per peer 5-tuple.
  • Filtering behaviour — which return packets are allowed back. Endpoint-independent accepts return packets from any source; address-dependent only from peers the internal host has talked to; address-and-port-dependent only from the exact 5-tuple.

WebRTC / STUN / ICE care intensely about this. Modern carrier NATs are mostly endpoint-independent mapping + address-and-port-dependent filtering (“full-cone NAT” in older terminology).

Port forwarding and hole-punching#

Inbound connections to a NATed host need help. Three mechanisms:

  • Static port forward. Operator configures “TCP/8080 on public IP → 192.168.1.50:80 on private”. The NAT keeps a permanent entry.
  • UPnP / NAT-PMP / PCP. Application asks the NAT to open a port programmatically. Disabled in most enterprise environments because it’s an obvious security hole.
  • STUN + ICE hole-punching. Two NATed peers contact a public rendezvous server (STUN), discover each other’s external mappings, and send simultaneously through their own NATs — many NATs accept the inbound after seeing the outbound. The basis of WebRTC.

Firewall models#

A stateless ACL filters every packet independently — no memory of past flows. Cheap and simple, but cannot distinguish “ACK from inside a session I permitted” from “ACK injected by an attacker”.

A stateful firewall (Linux netfilter, BSD pf, Cisco ASA) tracks connections through a state machine. A SYN going out creates a NEW entry; the SYN-ACK back creates ESTABLISHED; further packets match the existing entry without re-evaluating rules. Closing a connection (FIN/RST) deletes the entry. Inbound traffic that does not match an entry is filtered against the inbound rule set.

iptables -A INPUT -m state --state ESTABLISHED,RELATED -j ACCEPT
iptables -A INPUT -p tcp --dport 22 -m state --state NEW -j ACCEPT
iptables -A INPUT -j DROP

That ruleset says: “permit packets belonging to existing sessions; permit new SSH sessions; drop everything else.” It is what runs on most Linux servers.

Variants#

  • NAT44 — classic IPv4 source NAT. What home and enterprise routers do. Also called NAPT (port-translating NAT) since plain 1:1 NAT (RFC 1631) is now rare.
  • CGNAT — Carrier-Grade NAT. Two layers of NAT — customer behind their home NAT, behind the carrier’s. Causes “two homes share the same public IP” effects on geolocation and IP-rate-limited services.
  • NAT64 (RFC 6146). Translates IPv6 source to a synthesised IPv4 destination via a 64:ff9b::/96 prefix. DNS64 synthesises AAAA records for IPv4-only destinations. Common on iOS and inside large clouds.
  • Hairpin / loopback NAT. Internal host reaches its own public IP from inside; NAT must rewrite both source and destination correctly. Many cheap routers fail this case.
  • Stateless firewall (ACL). Per-packet match. Fast, no state to exhaust, hard to write correctly for connection-oriented protocols.
  • Stateful firewall. Connection-tracked. The default everywhere serious.
  • Application-layer firewall / WAF. Inspects HTTP / TLS-SNI / application protocols, blocks payloads. Cloudflare, AWS WAF, ModSecurity. Operates above L4.
  • Security groups (cloud). AWS / GCP / Azure call their stateful firewalls “security groups” or “VPC firewall rules”. Same primitives, different ergonomics.

Trade-offs#

NAT (IPv4) — extends a small public address pool to many internal devices, incidentally hides internal topology, blocks unsolicited inbound by default. Breaks end-to-end addressing, complicates listening services, makes peer-to-peer hard, mangles IP headers (some application protocols need ALGs).
No NAT (IPv6 or public IPv4) — restores end-to-end reachability, simpler protocols, easier diagnostics, no ALG hacks. Every internal host is dialable; security must be explicit (firewall) rather than incidental.

Other tensions:

  • Stateful vs stateless filtering. Stateful is easier to write correctly (ESTABLISHED matches return traffic automatically) but has a finite table and a per-packet lookup cost. Stateless ACLs scale infinitely on a router fast-path but require explicit reverse rules.
  • Centralised vs distributed firewalls. Datacenter perimeter firewall is easy to audit but a bottleneck and a single point of failure. Distributed micro-segmentation (host firewalls on every VM) is more resilient and granular but much harder to manage at scale. Service meshes push this idea to the application layer.
  • ALGs (Application-Layer Gateways). FTP, SIP, H.323 carry IP addresses inside the application payload. The NAT must inspect and rewrite — when the ALG is buggy (and they often are) it breaks the protocol in mysterious ways. Disable when possible; modern apps avoid the pattern.
  • Stateful timeouts. Too short and long-idle SSH sessions break; too long and the state table fills with zombie entries. Defaults are reasonable; tune for known workloads.
Why doesn't NAT need to rewrite the IP checksum from scratch?

RFC 1624’s incremental checksum update lets you compute the new checksum from the old one and the changed bytes in O(1) — you only need to subtract the old IP/port and add the new. The same algorithm works for the TCP and UDP checksums, which cover a pseudo-header containing the source and destination IPs. Without this, every NATed packet would cost a full header scan; with it, NAT runs at line rate on modest hardware.

Common pitfalls#

  • Believing NAT is a firewall. NAT incidentally blocks unsolicited inbound but does not inspect anything. A stateful firewall is still required. Many home routers conflate the two; enterprise gear separates them.
  • Hairpin failure. Internal host reaches its own external IP and the NAT does not rewrite the source — return traffic skips the NAT and the connection breaks. Test before shipping.
  • CGNAT and geolocation. IP-based geolocation and reputation systems often classify CGNAT addresses as “shared / suspicious”; a single bad actor on the same NAT can degrade service for everyone else.
  • Port exhaustion. A single public IP has ~65k source ports. A NAT serving thousands of clients each opening hundreds of connections runs out. Symptom: new TCP connections fail with no obvious error. Mitigation: multiple public IPs, IP overload-pool, or per-protocol port-block allocation.
  • State-table exhaustion. A SYN flood or scanner can fill nf_conntrack and start dropping legitimate traffic. Monitor nf_conntrack_count; rate-limit at the edge.
  • ALG bugs. SIP-over-NAT with an ALG that mis-rewrites payload IPs causes “phones ring but no audio” symptoms. Often the fix is to disable the ALG and let the application handle it (most modern SIP clients do).
  • Stateful rules that forget UDP. Engineers write SSH and HTTP rules but forget DNS, NTP, or VPN UDP traffic. The default ESTABLISHED,RELATED match handles UDP too as long as connection tracking is enabled.
  • Trusting IPv6 to be “naturally firewalled”. IPv6 restores reachability; without explicit firewall rules every internal host is dialable from outside. Enable ip6tables / equivalent.
  • One direction filtered, the other allowed. Asymmetric routing can cause the SYN to go through one path (and create state) while the SYN-ACK goes through another (which has no state) — drops follow. Either symmetrise routing or share state between firewall pairs.
Search ESC

Keyboard shortcuts

Shortcuts are disabled while typing in inputs.