How does iptables filtering in the FORWARD or INPUT chain interact with NAT?

This page seems to suggest that, if there is a rule in the PREROUTING chain of the nat table that translates destination 1.2.3.4:80 to 10.1.1.1:8080, then the rules in the INPUT and FORWARD chain should match on 10.1.1.1:8080, not 1.2.3.4:80.

What if one wants to implement an order of operation more like what's outlined here? That is, how do you perform filtering on characteristics of packets before NAT?

One possibility seems to be using the PREROUTING chain of the raw table. The issue is that conntrack isn't available in raw/PREROUTING (see note 1), and e.g. a non-initial UDP fragment will not be related to the initial fragment and will produce unexpected matches.

Please let me know if I've described the dilemma clearly, and whether there's any workaround.

Note 1: Correct me if I'm wrong, but when I tried to use conntrack in raw/PREROUTING, all initial SYN packets were marked "INVALID" rather than "NEW". It also seems like, in the Netfilter flowchart, "conntrack" is after raw/PREROUTING.


Solution 1:

You don't need any raw/PREROUTING rules. You can use the conntrack match to filter the packets by original (before translation) destination/source address/port number. The conntrack is a successor of the older state match. It can check various additional metadata, related with conntrack entry (and NAT).

Couple of examples:

# allow any port-forwarded packets
iptables -t filter -A FORWARD -m conntrack --ctstate DNAT -j ACCEPT

# check the original destination address of DNATed packets
iptables -t filter -A FORWARD -p tcp --dport 8080 -m conntrack --ctstate DNAT --ctorigdstport 80 --ctorigdst X.X.X.X --ctdir ORIGINAL -j ACCEPT

For more details check the output of iptables -m conntrack --help and man iptables-extensions.

Solution 2:

The Packet flow in Netfilter and General Networking describes the relations between the different hooks seeing a packet. Here's a smaller part of it:

Packet flow in Netfilter and General Networking

So if you want to interact statefully with packets before nat, the logical choice is mangle/PREROUTING: the conntrack hook already tracked the packet: it won't get an INVALID state, but nat still didn't happen.

Just remember that iptables' nat sees only the first packet and then everything else is handled directly by conntrack, still happening at the same place: between mangle/PREROUTING and the routing decision.

The other method is described in Anton Danilov's answer: by querying conntrack to check the former address from its lookup table.