At the end of the day NAT really just boils down to a lookup table on the NAT device containing the mappings for what connections go where. These table entries however are created on a per-session basis.
Ex. If I have a host at 10.10.10.10, wanting to communicate with a server on the internet at 8.8.8.8, through my internet gateway whose public IP is 1.1.1.1, when that communication happens my internet gateway might create a pair of entries looking something like this:
Source |
Sport |
Destination |
Dport |
Actions |
10.10.10.10 |
20000 |
8.8.8.8 |
80 |
SNAT 10.10.10.10:20000 --> 1.1.1.1:40000 |
8.8.8.8 |
80 |
1.1.1.1 |
40000 |
DNAT 1.1.1.1:40000 --> 10.10.10.10:20000 |
While it may appear that 1.1.1.1:40000 is now occupied for this session, now leaving ~63999 ports, this is not exactly true.
Since these entries are on a per-session basis, it would be perfectly reasonable for subsequent communications to occur and build out the table like so:
Source |
Sport |
Destination |
Dport |
Actions |
10.10.10.10 |
20000 |
8.8.8.8 |
80 |
SNAT 10.10.10.10:20000 --> 1.1.1.1:40000 |
8.8.8.8 |
80 |
1.1.1.1 |
40000 |
DNAT 1.1.1.1:40000 --> 10.10.10.10:20000 |
10.10.10.10 |
20001 |
8.8.8.8 |
81 |
SNAT 10.10.10.10:20001 --> 1.1.1.1:40000 |
8.8.8.8 |
81 |
1.1.1.1 |
40000 |
DNAT 1.1.1.1:40000 --> 10.10.10.10:20001 |
10.10.10.10 |
20002 |
8.8.4.4 |
80 |
SNAT 10.10.10.10:20002 --> 1.1.1.1:40000 |
8.8.4.4 |
80 |
1.1.1.1 |
40000 |
DNAT 1.1.1.1:40000 --> 10.10.10.10:20002 |
10.10.10.10 |
20003 |
8.8.4.4 |
81 |
SNAT 10.10.10.10:20003 --> 1.1.1.1:40000 |
8.8.4.4 |
81 |
1.1.1.1 |
40000 |
DNAT 1.1.1.1:40000 --> 10.10.10.10:20003 |
So on the public interface (which is typically where you would be concerned about NAT exhaustion), we can see that there are effectively 4 sessions:
1.1.1.1:40000 --> 8.8.8.8:80
1.1.1.1:40000 --> 8.8.8.8:81
1.1.1.1:40000 --> 8.8.4.4:80
1.1.1.1:40000 --> 8.8.4.4:81
but these 4 sessions are not totally consuming 4 ports, and the NAT device knows exactly where traffic belonging to any of these 4 sessions should be routed.
So while it may seem like there is a 64k port limit, it's really more like 64k per destination socket (with some caveats). Not inexhaustible, but certainly more scalable than a flat 64k.