Any rule in the nat
table is executed for every first packet in a connection.
When a new packet arrives, system searches a connection tracking table if it belongs to a known connection. If that's the case, it is assigned to that connection and translation is applied which was recorded for that connection in the table. The table stores enough information to identify forward and reply packets of a connection and translate them. A connection is a bidirectional thing, two flows. The nat
table isn't traversed for packets that were identified by the connection tracker (the conntrack
).
However, if it wasn't found, a new record in the conntrack table is created, and then packet is processed according to the nat
table. If a translation is requested, packet is translated and information is recorded into the conntrack table for subsequent forward and reply packets to be translated the same way.
A connection is deleted if timeout occurs, or if TCP connection is FINalized or RESet. What is considered a "connection" for connectionless protocols is specific to each case. For example, for UDP only addresses, ports and timeouts are considered.
You can see current contents of this table via the pseudo-file /proc/net/nf_conntrack
. Note you can see what exactly is translated to what. Also you may use helper utilites like conntrack-tools
or iptstate
.
Please, consider this diagram.
All your packets are being translated. They are translated, because the rule instructs doing so. Outgoing packets get translated as you expected, their source address gets changed to 1.2.3.4 and then they being sent out to original destination.
Incoming packets get their source address translated to 1.2.3.4, then they're routed to original destination. You have set up a source translation prior to routing (SNAT
rule in the PREROUTING
chain), remember? That rule applies to all packets.
The destination in this case is localhost, I believe. After a translation, the system sees a packet from 1.2.3.4 incoming to its own address.
If you install, say, a HTTP server there and read its access log, you'll find that all requests were made from 1.2.3.4. That's because it sees only packets after source address was translated. The real source is hidden by the source nat.
Then, if it bothers to reply, it forms a packet from its own address to 1.2.3.4. Connection tracker (see "output path" in the diagram) identifies that as reply to a known connection and translates it back according to the table entry it found (notice, however, now it changes a destination address from 1.2.3.4 to what it found in the table, because it's a reply). Then reply packet is rerouted and sent out to original sender.
Source address of a reply packet must not be translated — there was no instruction to do so.
To avoid such confusing effects, you always add a outgoing interface match to SNAT rules. For instance, iptables -t nat -A POSTROUTING -o eth0 -j SNAT --to-source 1.2.3.4
.
You can achieve interesting effects if you translate incoming packets, for example, you can make packets from remote host to appear they were sent from the LAN, but such rule will have quite tight match, at least a source IP which you want to appear local and a address in the LAN to use as its alias. That address will likely be different than the address you use in the general SNAT rule.