Explained: NAT

Codelooru: NAT

Your laptop is sitting on a home network. It has an IP address — something like 192.168.1.42. But that address means nothing to the rest of the internet. It's a private address, invisible beyond your router. And yet, you can open a browser, load a webpage hosted on a server in another country, and the response comes back to exactly the right device, on exactly the right tab. How?

The answer is NAT — Network Address Translation. It's one of the most quietly essential pieces of infrastructure in modern networking, operating invisibly on almost every packet that leaves your home, office, or cloud environment. Understanding it explains a lot about why the internet works the way it does — and why peer-to-peer communication is so surprisingly hard.


The problem NAT was built to solve

The internet runs on IPv4 addresses — 32-bit numbers that look like 203.0.113.5. The maximum number of unique IPv4 addresses is about 4.3 billion. That sounds like a lot until you consider that there are now over 15 billion internet-connected devices in the world. We ran out of IPv4 addresses a long time ago.

The long-term fix is IPv6, which has an address space so large it could assign a unique address to every grain of sand on Earth with room to spare. But IPv6 adoption has been slow, and the internet still runs mostly on IPv4. NAT is the stopgap that made this survivable — it allows an entire network of devices to share a single public IP address.

The core idea: Instead of every device having its own public IP, an entire building — or company, or mobile carrier — gets one public IP. A NAT device at the edge translates between the private addresses inside and that single public address outside.

How NAT works — the translation table

When your laptop at 192.168.1.42 sends a request to a web server, the packet travels to your router. The router rewrites the source address in the packet header — replacing your private 192.168.1.42 with its own public IP, say 203.0.113.1. It also notes down the source port your device used — say port 54231 — and records this mapping in a translation table.

The web server sees the request coming from 203.0.113.1:54231. It doesn't know anything about your private address. It sends its response back to 203.0.113.1:54231. When that response arrives at your router, the router consults its translation table, finds the matching entry, rewrites the destination address back to 192.168.1.42, and delivers it to your laptop. The whole translation is invisible to both your laptop and the web server.

The same thing happens simultaneously for every device on the network. Your phone, your smart TV, your work laptop — each outbound connection gets its own entry in the translation table, distinguished by port number. This port-based variant of NAT is technically called NAPT (Network Address Port Translation) but in everyday usage, everyone just calls it NAT.

Private network Laptop 192.168.1.42 port 54231 Phone 192.168.1.55 port 61002 TV 192.168.1.71 port 49887 NAT Router 203.0.113.1 translation table Web Server sees: 203.0.113.1 one public IP

Three devices share one public IP. The router's translation table maps each private address + port to the same public address, with different ports to tell them apart.


The four NAT types — from open to locked down

Not all NAT behaves the same way. The strictness of a NAT — specifically, the rules it applies to incoming packets — varies a lot between implementations. This strictness is what determines whether two devices behind different NATs can ever talk to each other directly. There are four classical types, defined originally in RFC 3489.

Full Cone NAT

The most permissive type. Once an outbound connection creates a mapping — say your device at 192.168.1.42:54231 maps to public 203.0.113.1:54231anyone on the internet can send a packet to 203.0.113.1:54231 and it will be delivered to your device. The NAT doesn't check who's sending. The door is open to the world.

This is the most game-console-friendly type (open NAT in gaming terminology) and the easiest to traverse for peer-to-peer applications. It's also the least common on modern routers because it's the least secure.

Restricted Cone NAT

Same consistent external mapping as Full Cone, but with one added rule: an external host can only send a packet in if your device has previously sent a packet to that host's IP address. You must initiate first. If you've never contacted 8.8.8.8, packets from 8.8.8.8 will be dropped — no entry in the table for that source.

Think of it as a bouncer who only lets in guests you've already waved at from across the room.

Port Restricted Cone NAT

One step stricter. The external host can only send packets in if your device has previously sent a packet to that specific IP address and port number. Contacting 8.8.8.8:443 opens the door only for responses from 8.8.8.8:443 — not from 8.8.8.8:80 or any other port. This is the most common type found in home routers today.

Symmetric NAT

The problematic one for peer-to-peer networking. Unlike the cone variants, symmetric NAT creates a different external mapping for each unique destination. When your device sends to server A, it gets mapped to 203.0.113.1:54231. When the same device sends to server B, it gets mapped to 203.0.113.1:54897 — a completely different port. A third server C gets yet another port.

Why does this matter? Because when two devices try to discover each other's addresses for a direct connection, the address they learn is only valid for the specific server they asked. When they try to use it to talk to each other, the NAT generates a new, different mapping — and the connection fails. Symmetric NAT is common on mobile carrier networks and many corporate firewalls.

Cone NAT — consistent mapping Device 192.168.1.42:5000 pub: 203.0.113.1:9000 → Server A → Server B → Server C 203.0.113.1:9000 203.0.113.1:9000 203.0.113.1:9000 Symmetric NAT — different mapping per destination Device 192.168.1.42:5000 → Server A → Server B → Server C 203.0.113.1:9000 203.0.113.1:54897 ←different 203.0.113.1:31042 ←different

Cone NAT always uses the same external port regardless of destination. Symmetric NAT assigns a new port for each destination — making peer-to-peer connections very difficult.

NAT Type Consistent external port? Who can send inbound? Hole punching possible?
Full Cone Yes Anyone Yes
Restricted Cone Yes Only IPs you've contacted Yes
Port Restricted Cone Yes Only IP + port you've contacted Yes
Symmetric No — new port per destination Only the original destination No

NAT traversal — getting two NATted devices to talk

Here's the fundamental challenge: if device A is behind NAT and device B is behind NAT, neither has a stable public address the other can reach directly. Both are invisible to the outside world. How do you connect them?

This is the NAT traversal problem, and it's been a central challenge in peer-to-peer networking, VoIP, video calling, online gaming, and virtually any protocol that needs devices to communicate without a server in the middle.

Step 1 — Discovery via STUN

The first step is for each device to find out what its public address actually looks like from the outside. Enter STUN (Session Traversal Utilities for NAT). A STUN server sits on a public IP and does one simple job: when a device connects to it, it replies with the source IP and port it saw on the incoming packet — which is the device's NAT-translated public address. Now both devices know their external coordinates.

Step 2 — Coordinate exchange

Both devices send their STUN-discovered addresses to a signalling server — a mutual third party both can reach. This server passes each device's public address to the other. Now device A knows where device B appears to be from the outside, and vice versa.

Step 3 — Simultaneous hole punching

Here's the clever part. Both devices simultaneously fire packets at each other's public addresses. When device A sends a packet to device B's address, it creates an entry in A's NAT table allowing return traffic from B. When device B sends a packet to A's address at the same moment, it creates an entry in B's NAT table allowing return traffic from A. If both packets arrive before either NAT table entry expires, the two-way path is open — the hole has been punched.

The timing matters enormously, which is why the signalling server coordinates the simultaneous send. In practice this works reliably between cone-type NATs. Against symmetric NAT, the predicted external port doesn't match the actual one assigned, so the packets miss and the hole punch fails.

Device A behind NAT STUN Server public IP Signalling coordinate exchange Device B behind NAT what's my public addr? share addresses simultaneous UDP packets → ← simultaneous UDP packets direct P2P connection open

STUN discovers external addresses. The signalling server exchanges them. Simultaneous packets punch holes in both NATs, opening a direct path.

When hole punching fails — enter TURN (or DERP)

If one or both devices are behind symmetric NAT, hole punching doesn't work. The fallback is a relay server — a publicly reachable machine that both devices connect to and through which packets are forwarded. TURN (Traversal Using Relays around NAT) is the standard protocol for this, used widely in WebRTC. DERP — which we covered in a previous post — is a more modern, cryptographically-blind variant used in overlay networking. Both solve the same problem: when you can't go direct, go through a trusted middleman.


NAT in cloud and Kubernetes environments

Cloud platforms and container orchestration add more layers of NAT on top of the basic picture. Understanding what's being translated where becomes genuinely important when you're debugging connectivity issues.

Cloud VMs and floating IPs

A virtual machine in AWS, GCP, or Azure typically has a private IP within the VPC — say 10.0.1.15. When that VM makes an outbound request, the cloud provider's infrastructure NATs the source to a public IP. When you assign an Elastic IP (AWS) or External IP (GCP), you're creating a static NAT mapping from a fixed public address to your VM's private address. Inbound traffic to that public IP is NATted back to the private IP before it reaches the VM — the VM itself usually never sees its own public IP.

NAT inside Kubernetes

Kubernetes layers NAT in several places simultaneously. When a pod communicates with a Service, it targets the Service's virtual ClusterIP — say 10.96.0.1. kube-proxy (or eBPF in newer setups) intercepts this traffic and rewrites the destination to one of the actual pod IP addresses behind the Service. That's destination NAT, or DNAT — rewriting where traffic is going.

When a pod makes a request to an address outside the cluster, the node running that pod typically applies source NAT — replacing the pod's IP with the node's IP. This is because pod IPs are often not routable outside the cluster. The external server sees the node IP, not the pod IP.

NodePort and LoadBalancer Services add another layer: inbound traffic hits an external IP or a node's port, gets NATted to the Service ClusterIP, then NATted again to a specific pod. Multiple translation hops in sequence, all transparent to the application.

Client external LoadBalancer 203.0.113.1:80 DNAT #1 Service ClusterIP 10.96.0.1:80 DNAT #2 Pod 10.244.1.5:8080 to public IP to ClusterIP to pod IP Traffic passes through two DNAT hops before reaching the pod

Inbound traffic to a Kubernetes LoadBalancer Service passes through two destination NAT translations before reaching the pod.

Why this matters for debugging

When a connection mysteriously fails inside a Kubernetes cluster, NAT is often the invisible culprit. Source IPs get rewritten, so the application sees a node IP instead of the actual client IP — breaking IP-based allowlists or rate limiting. Asymmetric routing (request goes one path, response goes another) can cause stateful NAT to drop packets. And in multi-cloud or hybrid environments, pod IPs from one cluster might not be routable to another without explicit peering or a NAT gateway in between.

Tools like conntrack -L on Linux nodes let you inspect the active NAT translation table — an invaluable view when tracing exactly what's being rewritten and where.


Wrapping up

NAT is one of those technologies that most people never think about until it breaks. It's the reason billions of devices can share a few hundred million public IP addresses. It's the reason your laptop can be on a private network and still reach the whole internet. And it's the reason peer-to-peer communication — something that sounds trivially simple — turns out to be a surprisingly hard engineering problem.

The type of NAT sitting between two devices determines whether they can talk directly at all, or whether they need a relay. The strictness spectrum runs from Full Cone (relaxed, traversal-friendly) all the way to Symmetric (strict, traversal-hostile). In the cloud and Kubernetes, NAT multiplies — you're often passing through three or four translation layers without realising it.

Understanding NAT is foundational to understanding almost everything else in modern networking: STUN, TURN, DERP, service meshes, VPNs, and the plumbing inside every Kubernetes cluster you'll ever work with.



×