You should install the conntrack
command usually packaged as conntrack or conntrack-tools, from http://conntrack-tools.netfilter.org/ . It will mostly display the same content as /proc/net/nf_conntrack
but can do more.
Run conntrack in event mode on the NAT gateway: conntrack -E
(or you can choose conntrack -E --proto tcp --orig-port-dst 443
to limit to HTTPS). Now your previous example with an HTTPS request would give something similar to this:
[NEW] tcp 6 120 SYN_SENT src=192.168.2.55 dst=217.254.1.76 sport=50798 dport=443 [UNREPLIED] src=217.254.1.76 dst=193.157.56.3 sport=443 dport=50798
[UPDATE] tcp 6 60 SYN_RECV src=192.168.2.55 dst=217.254.1.76 sport=50798 dport=443 src=217.254.1.76 dst=193.157.56.3 sport=443 dport=50798
[UPDATE] tcp 6 432000 ESTABLISHED src=192.168.2.55 dst=217.254.1.76 sport=50798 dport=443 src=217.254.1.76 dst=193.157.56.3 sport=443 dport=50798 [ASSURED]
[UPDATE] tcp 6 120 FIN_WAIT src=192.168.2.55 dst=217.254.1.76 sport=50798 dport=443 src=217.254.1.76 dst=193.157.56.3 sport=443 dport=50798 [ASSURED]
[UPDATE] tcp 6 60 CLOSE_WAIT src=192.168.2.55 dst=217.254.1.76 sport=50798 dport=443 src=217.254.1.76 dst=193.157.56.3 sport=443 dport=50798 [ASSURED]
[UPDATE] tcp 6 30 LAST_ACK src=192.168.2.55 dst=217.254.1.76 sport=50798 dport=443 src=217.254.1.76 dst=193.157.56.3 sport=443 dport=50798 [ASSURED]
[UPDATE] tcp 6 120 TIME_WAIT src=192.168.2.55 dst=217.254.1.76 sport=50798 dport=443 src=217.254.1.76 dst=193.157.56.3 sport=443 dport=50798 [ASSURED]
[DESTROY] tcp 6 src=192.168.2.55 dst=217.254.1.76 sport=50798 dport=443 src=217.254.1.76 dst=193.157.56.3 sport=443 dport=50798 [ASSURED]
The nat table is special, in that it is used only once per flow*, when the [NEW]
state is created. Everything else is short-circuited by the conntrack entry found. The SNAT rule in POSTROUTING didn't "silently create a DNAT rule" for the reply. It altered the conntrack entry to have reply dst=193.157.56.3, and told netfilter "I changed something", that's most of it. Everything else (including the source ip alteration) is handled by conntrack (modules nf_conntrack, nf_conntrack_ipv4) and nat (modules nf_nat, nf_nat_ipv4 and maybe a few more here), not by iptables. Consider the entries are a connections state database.
When a reply packet is received, the packet, having no association stored, is looked through the conntrack entries (actually looking an association both ways, either in the original part, or in the reply part, this doesn't matter), and a match is found because the entry was created before. Then the packed information is updated with this connection association. There's no need to look up this packet though the entries anymore later, even if some of its properties (source or destination...) are changed the information is directly available. Some of the macro/inline functions handling this are defined in skbuff.h . Look for _nfct
and nfct
. The packet (aka the skbuff), after the lookup, receives this value in skb->_nfct
.
So the few things you might have missed:
- iptables is not netfilter. It's a user of netfilter. nftables is an other user of netfilter. conntrack and nat (eg module nf_nat) are part of netfilter.
- the POSTROUTING hook will never see the reply packet because the connection is not NEW: the nat table is not called anymore for this flow and the packets identified as part of this flow.
- most of the handling of conntrack and nat is done... by conntrack and nat, not by iptables. iptables can use the ressources of conntrack (eg:
-m conntrack --ctstate ESTABLISHED
) or nat (anything in the nat table has to). For the example above, The conntrack entry alone has the information to "de-SNAT" the packet, and indeed it looks similar to a DNAT with a connection originally inbound.
- There's usually no need to look up the conntrack entries more than once for a packet part of an existing connection, the "index" is attached to the packet after the lookup.
You can be convinced that the nat table doesn't see other packets after the first by running a few times iptables-save -c
and looking at how the counters increment for the rules in the nat table: only for the first packet.
*look at the NAT part:
This table is slightly different from the `filter' table, in that only
the first packet of a new connection will traverse the table: the
result of this traversal is then applied to all future packets in the
same connection.