1. Home
  2. Computer Networks
  3. NAT (Network Address Translation)

NAT (Network Address Translation)

A whole home network hides behind one public IP address. Follow a packet through a NAT router in 3D, watch the NAT table grow, see two PCs share one address by port, and see why unsolicited packets are dropped unless you forward a port.

Interactive 3DBeginner13 min readCNUpdated

Drag to rotate · Right-drag to pan · Click, then scroll to zoom · Space play · ←→ step

What's happening

Pseudocode

    Try this in the 3D model

    • Press Outbound and reply. Which fields does the router rewrite on the way out, and which on the way back?
    • What source address does the web server see? Could it send a packet straight to 192.168.1.10?
    • Press Two hosts share one IP. Both PCs use source port 51000. Which public ports do they get?
    • Press Inbound and port forwarding. Why is the first packet dropped, and what rule makes the second one get through?

    Why NAT exists

    IPv4 addresses are 32 bits, so there are only about 4.3 billion of them, far fewer than the devices that want to be online. The fix that kept the Internet running is to give every home or company one public address and let all its devices use private addresses inside. The private ranges (RFC 1918) may be reused in every network but are never routed on the Internet:

    Range Size
    10.0.0.0/8 16.7 million addresses
    172.16.0.0/12 (172.16.x.x to 172.31.x.x) 1 million
    192.168.0.0/16 65,536

    A NAT router sits on the border. It has a private address on the inside (here 192.168.1.1) and a public one on the outside (203.0.113.5), and it rewrites addresses in every packet that crosses it.

    Three kinds of NAT

    Kind Mapping Use
    Static NAT one private address ↔ one public address, fixed a server that must always be reachable
    Dynamic NAT private addresses get a free public address from a pool rare today
    PAT (port address translation), also NAPT or masquerading many private addresses ↔ one public address, told apart by port almost every home and office router

    The 3D model shows PAT.

    Following one connection

    Press Outbound and reply:

    Where Source Destination
    PC1 → router (inside) 192.168.1.10:51000 93.184.216.34:80
    router → server (outside) 203.0.113.5:40001 93.184.216.34:80
    server → router (outside) 93.184.216.34:80 203.0.113.5:40001
    router → PC1 (inside) 93.184.216.34:80 192.168.1.10:51000
    1. Outbound: the router has no entry for 192.168.1.10:51000, so it picks a free public port (40001), records 192.168.1.10:51000 ↔ 203.0.113.5:40001 in the NAT table and rewrites the source.
    2. The server sees a client called 203.0.113.5:40001. It never learns about 192.168.1.10.
    3. Inbound: the reply comes to 203.0.113.5:40001. The router looks up port 40001, finds 192.168.1.10:51000 and rewrites the destination.

    Changing an address also changes the checksums: the IP header checksum, and the TCP/UDP checksum, which covers the addresses too (through the “pseudo-header”). The router recomputes both.

    Many hosts, one address

    Press Two hosts share one IP. PC1 and PC2 both happen to choose source port 51000. The router gives them different public ports:

    Private side Public side
    192.168.1.10:51000 203.0.113.5:40001
    192.168.1.11:51000 203.0.113.5:40002

    A reply to port 40002 can only belong to PC2. The 16-bit port number allows about 65,000 simultaneous connections per public address (per protocol and remote endpoint), which is plenty for a home and enough for a small company. ISPs even put whole neighbourhoods behind one address with carrier-grade NAT (CGNAT).

    Packets nobody asked for

    Press Inbound and port forwarding. A packet arrives from the Internet for 203.0.113.5:8080. There is no table entry, because no inside host started this conversation, so the router has no idea which PC it is for. It drops the packet. As a side effect NAT blocks unsolicited inbound connections, which is often mistaken for a firewall. (It is not a real security feature: a firewall should still be configured.)

    To run a server behind NAT, the administrator adds a port-forwarding rule: “public port 8080 → 192.168.1.10:80”. Now the same packet is translated and delivered.

    The cost of NAT

    • It breaks end-to-end addressing. Hosts behind NAT cannot be contacted directly, which hurts peer-to-peer apps, games and VoIP. They work around it with STUN, TURN, UDP hole punching or UPnP (which lets a program open a port forward automatically).
    • Protocols that put IP addresses inside the payload (classic FTP, SIP) need an Application Level Gateway (ALG) in the router to rewrite those too.
    • The router keeps state for every connection, so it can run out of table space or ports.
    • IPv6 has 128-bit addresses, enough for every device to have a public one, so NAT is mostly unnecessary there.

    Code

    class Nat:
        """A NAT router with one public address that translates ports too (PAT, also called NAPT or masquerading)."""
        def __init__(self, public_ip, first_port=40001):
            self.public_ip, self.next_port = public_ip, first_port
            self.out = {}                       # (private ip, private port) -> public port
            self.back = {}                      # public port -> (private ip, private port)
            self.forward = {}                   # static rules: public port -> (private ip, private port)
    
        def outbound(self, pkt):
            """A packet leaves the LAN: rewrite the SOURCE address and port."""
            key = (pkt['src'], pkt['sport'])
            if key not in self.out:             # first packet of this connection: create a table entry
                self.out[key] = self.next_port
                self.back[self.next_port] = key
                self.next_port += 1
            return {**pkt, 'src': self.public_ip, 'sport': self.out[key]}
    
        def inbound(self, pkt):
            """A packet arrives from the Internet: rewrite the DESTINATION, or drop it if nobody asked for it."""
            if pkt['dst'] != self.public_ip:
                return None
            target = self.back.get(pkt['dport']) or self.forward.get(pkt['dport'])
            if target is None:
                return None                     # no table entry: drop
            return {**pkt, 'dst': target[0], 'dport': target[1]}
    
    def show(p):
        return f"{p['src']}:{p['sport']} -> {p['dst']}:{p['dport']}"
    
    nat = Nat('203.0.113.5')
    web = ('93.184.216.34', 80)
    
    p1 = {'src': '192.168.1.10', 'sport': 51000, 'dst': web[0], 'dport': web[1]}
    p2 = {'src': '192.168.1.11', 'sport': 51000, 'dst': web[0], 'dport': web[1]}   # the same source port!
    print(show(nat.outbound(p1)))
    # 203.0.113.5:40001 -> 93.184.216.34:80
    print(show(nat.outbound(p2)))
    # 203.0.113.5:40002 -> 93.184.216.34:80
    print(show(nat.outbound(p1)))                      # same connection again: same mapping
    # 203.0.113.5:40001 -> 93.184.216.34:80
    
    reply = {'src': web[0], 'sport': web[1], 'dst': '203.0.113.5', 'dport': 40002}
    print(show(nat.inbound(reply)))
    # 93.184.216.34:80 -> 192.168.1.11:51000
    
    probe = {'src': '198.51.100.9', 'sport': 4444, 'dst': '203.0.113.5', 'dport': 8080}
    print(nat.inbound(probe))                           # nobody inside asked for this
    # None
    nat.forward[8080] = ('192.168.1.10', 80)            # port forwarding rule
    print(show(nat.inbound(probe)))
    # 198.51.100.9:4444 -> 192.168.1.10:80
    print(nat.out)
    # {('192.168.1.10', 51000): 40001, ('192.168.1.11', 51000): 40002}

    Common mistakes

    • Saying NAT changes the destination of outgoing packets. Outbound, it changes the source; inbound, the destination.
    • Forgetting the port. With one public address, only the public port identifies the connection. That is the “P” in PAT.
    • Calling NAT a firewall. Dropping unknown inbound packets is a side effect, not a security policy.
    • Thinking the server sees the private address. It sees only the router’s public address and port.
    • Forgetting the checksums. Rewriting an address without recomputing the IP and TCP/UDP checksums would make the receiver discard the packet.

    Complexity at a glance

    Case / operationTimeWhy
    Translating one packetO(1)A hash-table lookup on (IP, port), plus updating the IP and TCP/UDP checksums.
    Connections per public IPabout 65,000Per protocol and remote endpoint, limited by the 16-bit port number.
    Table entry lifetimeuntil idle timeoutTypically minutes for UDP and longer for established TCP connections.
    Extra spaceOne table entry per active connection

    Quick check

    Test yourself — pick an answer to see if you got it.

    1. Which of these is a private IPv4 address that may not appear on the public Internet?

    2. When a packet goes from the LAN out to the Internet, what does a PAT router change?

    3. Two PCs behind the same NAT both use source port 51000 to the same server. How does the router tell the replies apart?

    4. A packet from the Internet arrives at the NAT router's public address on a port that has no table entry and no forwarding rule. What happens?

    5. Why is NAT much less needed with IPv6?

    Saved in this browser.
    Spotted a mistake or a bug in the 3D model?

    Report a mistake

    in NAT (Network Address Translation). Thank you — every report makes the lesson better for the next reader.

    We'll also include a link to the step of the 3D model you're on and your browser type, so we can reproduce it.