How does an internet server respond to a request from a private IP


I'm learning about private/public IPs, port forwarding and NAT, and still fail to see the answer to a simple question:

Suppose two users on the same network are communicating with a single server outside their network (say, me & my wife send http requests to The server sees both users as a single public ip, and often – as in this case – communicating on the same port. How does the router route the packets according to the two different connections? Is there any routing info inside the packet beyond ip/port? Is there some 'session table' maintained somewhere? By whom and what exactly does it include?

Best Answer

Within a computer, between processes

First let's see how a single computer distinguishes between concurrent connections.

Most transport protocols such as TCP, UDP, SCTP use two ports, source and destination – i.e. one on either end of the connection. That is, packets do not simply walk "through" a port; instead they travel from port X to port Y.

The destination port is usually well-known (80 for HTTP, 53 for DNS…) but the source port is usually randomly chosen by the OS itself, which also ensures that the src/dst combination is unique.

So when your browser makes multiple connections "to Yahoo's port 80", all of them actually have different source ports, and the OS keeps a socket table such as:

PROCESS       PROTO  LOCAL                REMOTE            STATE
9894/firefox  tcp  google.server:80  established
9894/firefox  tcp  yahoo.server:80   established
17463/chrome  tcp  yahoo.server:80   established
9894/firefox  udp        --

So when the OS receives a TCP packet from yahoo.server:80 to local port 52909, it can map it to the specific connection made by Firefox.

Important to note that this has nothing to do with NAT yet, and happens the same way even if you're directly connected. (NAT will make use of it, though.)

(You can see this table using netstat -n, or various graphical tools on Windows. Often "local/remote" are labelled "source/destination", although that's not entirely accurate.)

Within a NATed network, between computers

The answer to your question about NAT is very similar, only everything is done at a larger scale.

The router performing NAT keeps a "state" table containing both the internal and external addresses & ports. For example, if your two HTTP requests used separate TCP connections, they might be tracked as:

PROTO   ORIG-SRC             ORIG-DST         REPLY-SRC        REPLY-DST
6/tcp   yahoo.server:80  yahoo.server:80  your.public.ip.addr:52909
6/tcp  yahoo.server:80  yahoo.server:80  your.public.ip.addr:39163
6/tcp  yahoo.server:80  yahoo.server:80  your.public.ip.addr:28330
17/udp       your.public.ip.addr:4984

When the router receives a packet from REPLY-SRC (Yahoo) addressed to REPLY-DST (your public IP address), it knows that the real destination must be taken from the ORIG-SRC column in order to undo the NAT.

(If there's no matching state, then manually configured port-forwarding rules are processed. If still none match, then the packet was really meant for the router itself.)

Note how the state table contains addresses and ports, allowing multiple connections to the same server to be distinguished by the port combination. In my example, two computers accidentally used the same port combination, so the 2nd connection had its ports translated as well.

(In fact, some NATs look only at the port and ignore the source address completely; this reduces the number of possible connections, but makes it a lot easier for peer-to-peer programs to perform "NAT hole-punching".)

Such state is kept even for connectionless protocols such as UDP or ICMP, so the entries expire after some interval of inactivity even though there's no explicit "close connection" packet. (The state table is actually part of the firewall, so even if NAT isn't done, the router might still use it to distinguish between "active" connections and stray packets.)

(If your router is Linux-based, conntrack -L or cat /proc/net/nf_conntrack would show this table. For OpenBSD or pfSense, try pfctl -s state.)

Related Question