Before you read. An office has fifty machines, all with private addresses, and one public address from its provider. Every machine reaches the internet at the same time and nothing collides.
Topic 07 established that no router on the internet will carry a route to a private address, so none of those machines is reachable and none of their replies has anywhere to go.
What makes it work anyway?
This is the mechanism that let IPv4 survive its own exhaustion, and it is worth understanding precisely rather than as “it shares an address”, because almost everything people believe about its security properties is wrong.
Some words you will need
- NAT
- Network address translation. Rewriting addresses in packets as they cross a boundary.
- PAT
- Port address translation. Rewriting the port too, which is what lets many machines share one address.
- masquerade
- Source translation onto whatever address the outgoing interface currently has.
- translation table
- The record of which inside conversation corresponds to which outside one.
- port forwarding
- A rule sending traffic arriving at one outside port to a specific inside machine.
What breaks without this
You cannot explain why inbound is different. Outbound works with no configuration and inbound needs an explicit rule, and the asymmetry confuses people until they know what the table is.
Somebody treats NAT as a firewall. It has a side effect that resembles one and provides none of the guarantees, which is a dangerous thing to be wrong about.
Logs name the wrong machine. Fifty users behind one address means a remote service sees one address, and tracing an event back to a person requires information only the translating device has.
One address, many conversations
The rewriting itself is simple. A packet leaves an inside machine with a private source address, the gateway replaces that source address with its own public one, and sends it on. The reply comes back to the public address, the gateway looks up which inside machine it belongs to, rewrites the destination, and delivers it.
The question that mechanism does not answer is how the gateway knows which inside machine a reply belongs to when fifty of them are sharing one address.
Two private hosts ping the same server at the same time. What does the server see, and how does the gateway tell the replies apart?
# Fedora CoreOS 44.20260707.3.1, kernel 7.1.3-200.fc44.aarch64
# linux network namespaces, topology nat-gateway
# two private hosts, one public address on the gateway
$ ip -n h1 addr show h1eth0 | grep "inet "
inet 10.0.0.11/24 scope global h1eth0
$ ip -n h2 addr show h2eth0 | grep "inet "
inet 10.0.0.12/24 scope global h2eth0
$ ip -n nat addr show nat-out | grep "inet "
inet 203.0.113.1/24 scope global nat-out
# both talk to the same server at the same time
$ (ip netns exec srv timeout 8 tcpdump -i srv-in -n -U icmp > /tmp/srv.txt 2>/dev/null &)
$ sleep 2
$ ip netns exec h1 ping -c 1 203.0.113.9 > /dev/null 2>&1
$ ip netns exec h2 ping -c 1 203.0.113.9 > /dev/null 2>&1
$ sleep 7
# what the server saw
$ cat /tmp/srv.txt
23:39:25.625055 IP 203.0.113.1 > 203.0.113.9: ICMP echo request, id 69, seq 1, length 64
23:39:25.625063 IP 203.0.113.9 > 203.0.113.1: ICMP echo reply, id 69, seq 1, length 64
23:39:25.628663 IP 203.0.113.1 > 203.0.113.9: ICMP echo request, id 71, seq 1, length 64
23:39:25.628670 IP 203.0.113.9 > 203.0.113.1: ICMP echo reply, id 71, seq 1, length 64
# and the table on the gateway that keeps the two apart
$ ip netns exec nat conntrack -L 2>/dev/null | grep icmp
icmp 1 22 src=10.0.0.11 dst=203.0.113.9 type=8 code=0 id=69 src=203.0.113.9 dst=203.0.113.1 type=0 code=0 id=69 mark=0 secctx=system_u:object_r:unlabeled_t:s0 use=1
icmp 1 22 src=10.0.0.12 dst=203.0.113.9 type=8 code=0 id=71 src=203.0.113.9 dst=203.0.113.1 type=0 code=0 id=71 mark=0 secctx=system_u:object_r:unlabeled_t:s0 use=1
The server sees 203.0.113.1 twice, which is the gateway’s address. Both hosts
have vanished as far as it is concerned.
What distinguishes them is in the same output: id 69 and id 71. Those are ICMP
identifiers, and the gateway is rewriting them so that each inside conversation
gets a unique one. For TCP and UDP it rewrites the source port instead, which is
where the name port address translation comes from.
That is the whole trick. The gateway keeps a table mapping each inside conversation, identified by inside address and port, to an outside conversation identified by its own address and a port it chose. A reply arriving for that outside port is looked up and rewritten back.
Since a port number is 16 bits, one public address supports tens of thousands of simultaneous conversations, which is why fifty machines fit behind one address without noticing.
Strictly, translating only the address is NAT and translating the port as well is PAT, sometimes called NAT overload. Almost everything anybody calls NAT today is PAT, because one address per inside machine defeats the purpose.
If you already work on networks: the table is state, and state runs out
The translation table is the part that makes NAT work and the part that makes it a liability, because a stateless forwarding device has just become a stateful one.
Every conversation occupies an entry, and entries persist after the conversation finishes, because the gateway cannot always tell that it has. TCP has a close it can watch for, and UDP has nothing at all, so entries are aged out on a timer. Those timers are long for established TCP, because a connection can be legitimately idle for hours, and short for UDP.
Three consequences.
The table is finite and can fill. A machine opening very many connections, whether through malware, a misconfigured application or a scan, can exhaust it, and when it is full new conversations for everybody fail. That is a denial of service affecting the whole office caused by one machine, with no packet loss and no link problem to find.
Idle connections die silently. A long-lived TCP connection with no traffic can have its entry aged out, and neither end is told. The next packet arrives at the gateway with no matching entry and is dropped, so both ends believe the connection exists and it does not. That is why SSH sessions left overnight are dead in the morning, and why keepalives exist.
The gateway is now a single point of failure with memory. Restart it and every conversation through it breaks, because the table is gone and the replies coming back match nothing. Failing over to a second gateway means replicating that state or accepting the same break.
The counter to look at, when a network is behaving oddly under load and nothing
else explains it, is the number of entries against the maximum. On the topology
behind this page that is conntrack -L, and on any real gateway it is a number
somewhere in the interface. Full is not a subtle failure once you know to check.
The hard direction
Outbound works with no configuration at all. Inbound does not work at all without it, and the reason is the table.
A packet arriving from outside, unsolicited, has a destination of the gateway’s
That is not a security decision. It is an absence of information.
Making inbound work means supplying the missing information in advance, which is port forwarding: a static rule saying traffic arriving on this outside port goes to this inside address and port. It is destination translation rather than source translation, and it is the same mechanism pointed the other way.
The exam’s terms for the two directions are worth keeping straight. Source NAT rewrites where a packet came from, which is the outbound case, and it is what lets many share one address. Destination NAT rewrites where it is going, which is the inbound case, and it is what port forwarding and many load balancers do.
Carrier grade NAT is where this gets painful. Topic 07 introduced the 100.64.0.0/10 range, and a customer behind it has no public address of their own at all. Port forwarding is not available because the outside address is the provider’s and shared, so hosting anything, or any protocol that needs an inbound connection, simply does not work.
If you already work on networks: what NAT breaks, and why it is not a firewall
Two things worth being precise about, and the second is the one people get wrong in a way that matters.
What NAT breaks is any protocol that carries an address inside its payload. NAT rewrites headers, and it has no general way to know that bytes deeper in the packet are also an address. FTP in its active mode tells the server which address and port to connect back to, and that address is the private one. SIP does the same for voice. Various peer to peer protocols do too.
The fix was application layer gateways: code in the NAT device that understands particular protocols, inspects their payloads and rewrites the addresses inside. That works, it is a layering violation of the kind topic 03’s panel described, and it has been a reliable source of subtle bugs for thirty years. The other fix is protocols working out their own public address by asking a server on the outside, which is what STUN does and is how most voice and video now cope.
Why it is not a firewall is the important half.
The property people rely on is real: unsolicited inbound traffic is dropped because there is no table entry for it. That is a side effect of not knowing where to send something.
What makes it not a security control is what it does not do. It has no policy, so you cannot express what should be allowed, only what happens to be in the table. It inspects nothing, so anything an inside machine invites in is delivered whatever it is, which covers essentially all malware, since it connects outward first. It fails open in the sense that a port forwarding rule added for convenience punches a hole with no rules attached to it. And it protects nothing between inside machines, since they reach each other directly without passing through it.
The clearest way to see the difference: a firewall denies traffic it has been told to deny, and NAT drops traffic it cannot place. One is a decision and the other is ignorance.
IPv6 makes this concrete. Every machine gets a globally routable address and there is no translation, so the thing people thought was protecting them is gone. What replaces it is a stateful firewall doing the job explicitly, and networks that were relying on NAT discover during an IPv6 rollout that they had no inbound policy at all.
Prove it
You have this when you can look at a translation table and match an outside conversation to the inside machine that started it.
./blog/scripts/netlab.sh --topo topologies/nat-gateway.sh -- \
'(ip netns exec srv timeout 6 tcpdump -i srv-in -n -U icmp > /tmp/s.txt 2>/dev/null &); sleep 2; ip netns exec h1 ping -c 1 203.0.113.9 > /dev/null 2>&1; ip netns exec h2 ping -c 1 203.0.113.9 > /dev/null 2>&1; sleep 5; cat /tmp/s.txt; ip netns exec nat conntrack -L 2>/dev/null | grep icmp'
Two things to confirm. The server sees one address for both hosts. And the gateway’s table has an entry per conversation, showing the inside address alongside the outside one it was translated to.
On a home router the same table is usually visible somewhere in the status pages, often called active connections or the NAT table, and it is worth looking at once to see how many entries an ordinary household generates.
What trips people up
1. Believing NAT is a security control
It drops unsolicited inbound traffic because it has no idea where to send it, not because it decided to. It has no policy, inspects nothing, allows anything an inside machine invites in, and does nothing between inside machines.
2. Confusing NAT and PAT
NAT rewrites the address. PAT rewrites the port as well, which is what lets many machines share one address. Nearly everything called NAT in practice is PAT.
3. Expecting inbound to work like outbound
Outbound creates a table entry as a side effect. Inbound has no entry to match, so it needs an explicit rule saying which inside machine it belongs to.
4. Forgetting the table is finite
One machine opening enough conversations can fill it, and when it is full new conversations fail for everybody. There is no packet loss and no link fault to find.
5. Assuming an idle connection survives
Entries age out, and neither end is told. Both believe the connection exists and the next packet is dropped, which is why long SSH sessions die overnight without keepalives.
6. Promising port forwarding behind carrier grade NAT
The outside address belongs to the provider and is shared, so there is nothing to forward from. Seeing a 100.64 address on the outside interface is the sign.
Work it through
A company’s monitoring system reports that their public address is generating an unusual volume of connections, and the provider has sent an abuse notice naming that address. Internally, users report that new websites sometimes fail to load for a minute and then work.
Two symptoms and one cause.
The abuse notice names the public address, which is every machine in the building as far as anybody outside is concerned. That is the identification problem NAT creates: the provider can see one address behaving badly and cannot see which of the fifty machines behind it is responsible. Only the gateway knows, and only while the entries exist.
The intermittent failures are the second symptom of the same event. Something is opening a very large number of conversations, the translation table is filling, and when it is full there is no entry available for a new conversation so it fails. Entries age out, space frees, and it works again. That is exactly the pattern users described.
So the immediate task is to find the inside machine, and the gateway’s table is where to do it. Listing the entries and counting by inside address finds the one generating thousands where everything else has tens, which takes about a minute if you know the table exists.
Two things for afterwards. Logging the translations, so that an abuse notice can be traced back to a machine and a time rather than to a building, which is the only way to answer this kind of notice at all. And a limit on how many entries one inside address may occupy, so that one machine can no longer take the office offline by exhausting a shared resource.
The thing worth saying in the report: the network was not attacked and nothing was broken. A shared finite resource was consumed by one participant, and the design had no per-participant limit.
Try it
Watch a translation happen. Run the Prove it command. Seeing two hosts arrive at a server as one address, with the gateway’s table holding both, makes the mechanism concrete.
Count your own. On a home router, find the active connection or NAT table. The number of entries an ordinary household generates is usually surprising, and it is the same table that fills in the scenario above.
Find your public address. Any site that reports the address it sees is showing you the outside of your own NAT. Compare it to the address on your machine, and if the one you see starts 100.64 then you are behind carrier grade NAT and topic 07’s panel applies to you.
Check yourself
Fifty machines share one public address. How does the gateway know which one a reply belongs to?
From a table it built when each conversation started.
For each outbound conversation the gateway records the inside address and port, and assigns an outside port of its own. A reply arriving for that outside port is looked up and rewritten back to the inside address and port.
Because a port is 16 bits, one address supports tens of thousands of simultaneous conversations. Rewriting the port as well as the address is what PAT means, and it is why one address serves fifty machines.
Why does outbound need no configuration and inbound need an explicit rule?
Because outbound creates the table entry as it goes and inbound has nothing to match.
An unsolicited packet arriving from outside has a destination of the gateway’s address and some port, and no entry says which of the inside machines it belongs to. There is nothing to look up, so it is dropped.
Port forwarding supplies that information in advance: a static rule saying traffic on this outside port goes to this inside address. It is destination translation rather than source translation.
Why is NAT not a firewall?
Because dropping traffic you cannot place is not the same as deciding to deny it.
NAT has no policy, so there is no way to express what should be allowed. It inspects nothing, so anything an inside machine connects out to can send whatever it likes back, which covers essentially all malware. A port forwarding rule opens a hole with no rules attached. And it does nothing at all between inside machines, which reach each other directly.
A firewall denies what it has been told to deny. NAT drops what it does not understand.
An SSH session left open overnight is dead in the morning, with no error at either end. Why?
The translation table entry aged out.
Entries are removed after a period without traffic, because the gateway cannot always tell that a conversation has finished, and an idle connection looks identical to an abandoned one.
Neither end is told. Both still believe the connection exists, and the next packet arrives at the gateway with nothing matching it and is dropped. Keepalives exist to prevent exactly this by making sure the conversation is never idle long enough.
New connections intermittently fail for everybody in an office, with no packet loss and no link fault. What would you check?
The number of entries in the gateway’s translation table against its maximum.
The table is finite. One machine opening very many conversations can fill it, and while it is full there is no entry available for anybody’s new conversation, so new connections fail while established ones continue. Entries age out, space frees, and it works again.
It presents as intermittent because it is, and nothing about it looks like a network fault, which is why the counter is the thing to look at.
What kind of protocol does NAT break, and what are the two fixes?
Anything that carries an address inside its payload rather than only in the header. NAT rewrites headers and has no general way to know that bytes further in are also an address. Active mode FTP and SIP are the standard examples.
The first fix is an application layer gateway, which is code in the NAT device that understands the protocol and rewrites the addresses inside it. It works and it is a layering violation that has produced subtle bugs for decades.
The second is the application discovering its own public address by asking a server outside, which is what STUN does and how most voice and video handle it now.
References
- RFC 3022, Traditional IP Network Address Translator - IETF, which defines NAT and the port translation variant. Accessed 2026-08-10.
- RFC 2663, NAT Terminology and Considerations - IETF, on what NAT breaks. Accessed 2026-08-10.
- RFC 6598, Shared Address Space - IETF, the carrier grade NAT range. Accessed 2026-08-10.
- nft(8) - netfilter project, the tool that configures the masquerade rule in the topology. Accessed 2026-08-10.
Where the output came from. The captured block was produced on
blog/scripts/topologies/nat-gateway.sh through blog/scripts/netlab.sh. The
gateway runs a real masquerade rule in nftables, so the rewriting is the kernel’s
and the identifiers the server sees are what it actually received. The outside
network uses 203.0.113.0/24, which RFC 5737 reserves for documentation.
The scenario in Work it through describes a table filling up, which is not captured. Exhausting a translation table would work and would take several minutes of generating junk to produce one number, so the page describes the counter to look at instead.
If you also work on Linux. Firewall concepts and netfilter on the Linux+ track covers the same nftables machinery from the administration side, including where the NAT hooks sit relative to the filtering ones.