Linux – Port forwarding with DNAT and SNAT without touching other packets

iptableslinux

I have a Linux gateway with iptables which does routing and port forwarding. I want the port forwarding to happen independent of the routing.

To port forward, I add this to the nat table:

iptables -t nat -A "$PRE" -p tcp -d $GW --dport $fromPort -j DNAT --to-destination $toHost:$toPort
iptables -t nat -A "$POST" -p tcp -d $toHost --dport $toPort -j SNAT --to $SRC

$PRE and POST are actually destination-specific chains that I jump to from the PREROUTING and POSTROUTING chains respectively so I can keep the iptables clean. $SRC is the IP address I'm SNATing to which is different from the gateway IP $GW.

The problem with this setup is that regular routed packets that were not DNATed but happen to go to the same $toHost:$toPort combo will also be SNATed.

I wish to avoid this. Any clever things I can do?

Best Answer

Keep it simple: you don't actually need to SNAT anything. By doing this, every packet will reach the target server appearing to come from your firewall, rather than from the originating client, which in most cases is not what you want.

With the SNAT rule out of the way, you don't have to worry about it matching other traffic.

Note: Your FORWARD rules will have to match against the real source IP and the internal destination IP, since the FORWARD chain is reached after the DNAT rules are applied.

EDIT

Given the requirement for SNAT: as long as you're happy with the way the DNAT rule is matching packets, then you can precede that with a mangle rule that marks those same packets:

iptables -t mangle -A <mangle chain> -p tcp -d $GW --dport $fromPort -j MARK --set-mark 0x1

Then all your SNAT rule has to do is match those marked packets:

iptables -t nat -A "$POST" -m mark --mark 0x1 -j SNAT --to $SRC

If you need to identify multiple, separate streams, you should be able to vary the mark in script along with $SRC.