NAT
Public vs Private IP, back in Module 1, introduced NAT as the bridge between your laptop's private address and the single public address your ISP hands your router — and it deliberately left two things for later: exactly what that translation table looks like on the inside, and what happens when a connection needs to start from outside rather than inside. Both are worth a proper look now, because they're where NAT stops being a one-line explanation and starts being the thing that actually breaks in practice.
NAPT: translating ports, not just addresses
Strictly, what a home router does is NAPT — Network Address and Port Translation, sometimes just called PAT (Port Address Translation) — rather than the simpler address-only NAT that RFC 3022 calls "Basic NAT." Basic NAT would need one public address per private device, which defeats the entire purpose of sharing one address across a household. NAPT solves that by translating the port as well as the address, which is what lets dozens of internal devices share a single public IP simultaneously.
Every outbound connection gets an entry in the router's translation table, keyed on the full 5-tuple: protocol, source address, source port, destination address, destination port.
Proto Inside (private) Outside (as seen by internet) Destination
tcp 192.168.1.10:51342 <--> 203.0.113.7:61001 93.184.216.34:443
tcp 192.168.1.23:44210 <--> 203.0.113.7:61002 93.184.216.34:443
udp 192.168.1.10:53211 <--> 203.0.113.7:61003 8.8.8.8:53
Two internal devices reaching the same destination — even the identical destination and port, as the first two rows show — get distinct entries because the router assigns each a different public source port. That's the entire trick that makes NAPT scale: the public IP is shared, but the (address, port) pair on the outside is always unique, so a reply always has exactly one matching table entry to come back to.
This table is conceptually the same connection-tracking structure a firewall already keeps for stateful inspection — on a Linux router, it's the same conntrack subsystem doing both jobs at once:
tcp 6 108 ESTABLISHED src=192.168.1.10 dst=93.184.216.34 sport=51342 dport=443 src=93.184.216.34 dst=203.0.113.7 sport=443 dport=61001 [ASSURED] mark=0 use=1
Read the two src=...dst=... pairs as the two sides of the translation: the first is the packet as your laptop sent it, the second is how it looks after the router rewrote it for the public internet. Everything downstream of the router only ever sees the second pair.
Why an entry only gets created for outbound traffic
Here's the detail that Module 1 flagged as "the harder half of the problem," and it follows directly from the table above: an entry only gets created when a packet leaves the private network and asks to be translated. If an unsolicited packet arrives from the internet with no matching entry, the router has no idea which internal device — if any — it's meant for, so it drops it.
A NAT table entry only exists for a connection that was initiated from inside it — which is why nothing on the internet can simply connect in, and why every inbound service needs an explicit rule to exist at all.
That's genuinely useful as an accidental security property, exactly as Module 1's warning noted — but it also means anything that legitimately needs to receive a connection from outside has to work around it deliberately, in one of a few ways:
- Port forwarding — a static rule that says "always treat inbound traffic on public port X as if it belonged to this internal device's port Y," effectively pre-creating the entry a normal outbound connection would have created automatically. Public vs Private IP already covered this from the "how do I reach my own server" angle.
- A tunnel — the internal device makes the first, outbound connection to a relay, and inbound traffic rides back down that already-open connection instead of arriving cold.
- NAT traversal protocols like STUN, which let two devices behind separate NATs discover their own public (address, port) pair and exchange it out-of-band, so each can send an outbound packet toward the other at roughly the same moment — a technique called hole punching, common in VoIP and peer-to-peer applications.
Not all NAT behaves the same way for hole punching
Hole punching only works if the router's translation is predictable, and routers vary in exactly how strict they are about which external hosts are allowed to send back through a table entry:
| NAT type | Behavior |
|---|---|
| Full cone | Once an internal device has sent one packet out through a given public port, any external host can send a packet back to that port and it reaches the device. |
| Restricted cone | An external host can send back only if the internal device has previously sent a packet to that host's IP address (any port). |
| Port-restricted cone | Same as restricted cone, but the external host's port must match too — the strictest of the three cone types. |
| Symmetric | The router assigns a different public port for every distinct destination the internal device talks to, rather than reusing one port. This is the type that breaks hole punching outright, because the external peer can't predict which port to send to — it's different for every destination. |
Consumer routers overwhelmingly implement one of the cone types, but symmetric NAT shows up often enough — particularly on some mobile carrier networks and certain enterprise firewalls — that peer-to-peer applications need a fallback. When STUN-based hole punching fails because one or both sides are behind symmetric NAT, the application falls back to TURN, which relays all traffic through a public server that both sides can reach normally — slower, and a real bandwidth cost for whoever runs the relay, but the only option once direct hole punching is off the table.
Carrier-grade NAT: a second layer you don't control
Everything above assumes your router owns a genuinely public IP address. Increasingly, especially on mobile networks, it doesn't: your ISP performs its own NAT, translating your router's "public" address — which is actually still private from the ISP's point of view — into a real, globally routable one shared across many customers. This is CGNAT (Carrier-Grade NAT), and its practical consequence is that port forwarding on your own router accomplishes nothing, because your router's public-facing address was never actually public to begin with. There's no rule you can add on equipment you own that reaches past a translation layer you don't control.
Practical scenario: the port-forwarded service that only fails from inside
A small team sets up a self-hosted service on a home lab server, port-forwards 80 and 443 on the router to reach it, and confirms it works — from a phone on cellular data, outside the house entirely. Testing from a laptop on the same home Wi-Fi, using the exact same public domain name and address, the connection times out. Testing that same laptop against the server's private IP directly works fine.
This is a NAT hairpinning (also called NAT loopback) problem, and it's a different failure from anything covered so far, because it isn't about a missing table entry at all — it's about a router that doesn't support a specific edge case in its own translation logic. The request leaves the laptop already knowing the destination is the router's own public IP. Some consumer routers, when they see a packet from an internal device addressed to their own external address, don't know what to do with it: rewriting it back around to the internal server, so the reply can retrace the same path, requires the router to recognize "this public IP is mine" and hairpin the traffic back inside — and not every router implements that.
The fix isn't the port-forwarding rule, which is already correct — it's either enabling NAT hairpinning/loopback support if the router offers it, or, more reliably, using split-horizon DNS so that devices on the internal network resolve the service's name to its private IP directly, bypassing the round trip through the router's public address entirely. External clients, who were never going to hairpin through anything, are unaffected either way.