DNAT and IP source routing woes

I recently came across a nasty problem.
I have a Linux box (let’s call it LinuxFW) with three ethernet ports:

  • LAN: 192.168.0.1 connected to eth0
  • ADSL1: $IP1 connected to eth1
  • ADSL2: $IP2 connected to eth2

$IP1 and $IP2 are my public IP addresses.
Their respective default gateways are $GW1 and $GW2.

I use IP source routing to accept incoming connections from $IP1 and $IP2.
This is the easy part: there is a lot of documentation (see LARTC website for the details) on how to do this, I’ll just sum it up here for completeness.


ip route add default via $GW1 table adsl1
ip rule add from $IP1 table adsl1
ip route add default via $GW2 table adsl2
ip rule add from $IP2 table adsl2
ip route add default via $GW1


echo 200 adsl1 >>/etc/iproute2/rt_tables
echo 201 adsl2 >>/etc/iproute2/rt_tables

If somebody connects to $IP1 on port 22, LinuxFW receives packets on eth1 and outgoing packets are sent through eth1, thanks to the iproute2 rules mentioned above.
Similarly, if somebody connects to $IP2 on port 22, packets enter from eth2 and exit from eth2.

So far so good. Now comes the hard part:

I added a new Linux box behind the old one, let’s call it LinuxMail. The new box has IP address 192.168.0.2 and is running Postfix. It’s connected to the same LAN switch that LinuxFW has on “eth0”.
Obviously I could install a smtp proxy on LinuxFW and redirect all emails from LinuxFW to LinuxMail, but this poses some tread-offs, i.e.:

  • LinuxMail won’t be able to see the IP addresses of incoming connections directly
  • LinuxMail won’t be able to do connection throttling
  • LinuxMail won’t be able to reject messages for non-existant accounts

All these problems can be solved to some degree by introducing more logic on the smtp proxy of course, but let’s assume that we don’t want to do that, either because LinuxFW doesn’t have enough resources or because we want to keep things simple.
There are three solutions to this problem.

1 – CTSTATE DNAT

All incoming packets with destination port 25 coming from either eth1 or eth2 will be redirected to 192.168.0.2 and marked respectively with “0x6” and “0x7”. These iproute2 rules will take care of routing the reply packets to the appropriate interface:

2 – CONNMARK

Here is a simple way to open port 25 on both $IP1 and $IP2 and redirect incoming connections to LinuxMail using CONNMARK:

Of course you will still need the usual:

3 – Double IP addresses

Another DIY solution is to assign a second private IP address to LinuxMail. In this scenario LinuxMail has the following IP addresses:

192.168.0.2

192.168.0.3

and LinuxFW redirects incoming connections from $IP1 to 192.168.0.2 and from $IP2 to 192.168.0.3:

Of course you will still need the usual:

Credits:

LARTC: http://lartc.org/howto/lartc.rpdb.html#LARTC.RPDB.SIMPLE
Pascal Hambourg: http://www.gossamer-threads.com/lists/iptables/user/68743#68743
Rodrigo Campos: http://www.mail-archive.com/lartc@mailman.ds9a.nl/msg16307.html

Share

3 thoughts on “DNAT and IP source routing woes

  1. Nice. I’ve managed to get option 2 working.

    I’m not so sure that option 1 is correct however. And while I don’t need it as option 2 works, it annoys me that I cannot figure out how to get it to work.

    First of all, all targets are terminating, except when stated otherwise. If either line 1 or 2 match, iptables goes to the DNAT target and returns afterwards. That means that rule 3 and 4 will never be reached for packets that have been DNAT’ed. If rules 1-2 and 3-4 are switched, it won’t work either, both because MARK is terminating, and because –ctstate DNAT is false.

    So the limitations, considering the schema at http://www.iptables.info/en/structure-of-iptables.html are: –ctstate DNAT requires the rule to run after the -j DNAT rule which is in nat->PREROUTING. It must run before the final routing decision as after that the -i cannot be used anymore. And the filter table is not able to set markers. That leaves only mangle->FORWARD, but I cannot seem to get it to work over there either.

    Are you sure option 1 is supposed to work like that?

  2. Okay, I figured it out. The conntrack rules in option #1 are not supposed to mark traffic that is being redirected to the target server, but traffic that is coming back from the target server and is to be routed back to the client. So, it is:

    iptables -t nat -I PREROUTING -i eth1 -p tcp –dport 25 -j DNAT –to 192.168.0.2:25
    iptables -t nat -I PREROUTING -i eth2 -p tcp –dport 25 -j DNAT –to 192.168.0.2:25
    iptables -t nat -A PREROUTING -i eth0 -m conntrack –ctstate DNAT –ctorigdst $IP1 -j MARK –set-mark 0x6
    iptables -t nat -A PREROUTING -i eth0 -m conntrack –ctstate DNAT –ctorigdst $IP2 -j MARK –set-mark 0x7

    (eth0 instead of eth1+eth2)

  3. Sorry, still wrong. Should be:

    iptables -t nat -I PREROUTING -i eth1 -p tcp –dport 25 -j DNAT –to 192.168.0.2:25
    iptables -t nat -I PREROUTING -i eth2 -p tcp –dport 25 -j DNAT –to 192.168.0.2:25
    iptables -t mangle -A PREROUTING -i eth0 -m conntrack –ctstate DNAT –ctorigdst $IP1 -j MARK –set-mark 0x6
    iptables -t mangle -A PREROUTING -i eth0 -m conntrack –ctstate DNAT –ctorigdst $IP2 -j MARK –set-mark 0x7

    (mangle instead of nat on rule #3 and #4)

Leave a Reply