dnat from 2 inet ports to 1 internal host?

Wes Hodges whodges@fabgear.com
Tue, 29 May 2001 22:09:57 -0400

> I think that it simply doesn't work the way they said. I just had an
> email from Sascha explaining how it works but the explanation did not
> convince me. He was talking about two internal networks and one ISP
> connection which is a totally different picture.

That truly is a different setup and there should be no problem with DNATing
that arrangement.  The default route from either internal host would send the
response packet out the ISP interface.

This is how I believe the packets travel through the kernel, route and iptable
code.  I have not looked into the code to verify this.  It has mainly been my
experience by trial and error.

Incoming packet from interface from ISP.
1. The incoming packet goes to the nat table PREROUTING chain and gets
    DNATed. The destination IP address gets rewritten to an internal host.
2. The routing decision is made.  Internal destination, so it goes out the internal
3. The packet goes through the filter table FORWARD chain. Here iptables lets
   the packet go through to the internal interface. Sends the packet on its way.

Response from the internal host
1.  The internal host sends the response back to the firewall.  Most likely by its
   default route.
2. The routing decision is made.  By default the routing decision is made on the
  destination IP address.  In this case it most likely chooses the default route.
  At this point the packet still has an internal source IP address.
3. The packet is SNATed.  The source IP address is changed back to the address
  the packet came in one.  This, I believe is handled automatically by the DNAT rule.
  Now the packet has an external source IP address.
4.  The packet is sent out the interface chosen during the routing decision.

Now this means that the packet will be sent out the interface that is the default route.
This is not necessarily the interface the request came in on.  Asynchronies routing would
occur and most likely your other ISP would drop the packet since its source IP address
is not from its network.

To get the response to go back out the interface it came in own I had to do two things.
Well actually three.

1. Add another address to the internal host(s).
2. Add a policy routing rule that says if the source address is the extra internal
  the route via a specific ISP (interface).
3. Make sure whatever services are listening on the internal host, listen on both internal
  addresses.  Most things will listen on all interfaces the service finds at startup.
  times you may have to do this explicitly.

Doing this I can make sure any request coming in from one external interface the response
goes back out the same interface.

Little diagram: (ISP1, eth1) (ISP2, eth2)
            default route via eth1
           (internal network, eth0)
        (,, eth0)
                internal host
            default route via eth0

Semi-real life example from the above diagram:
1. client: request from ->
2. firewall: request comes in interface eth2.
3. firewall: send packet through net table PREROUTING chain.
    rewrite destination IP to internal address.
4. Make routing decision based on destination IP address.  Since the
    destination address is, send out interface eth0.
5. firewall: send packet to filter table FORWARD chain.  Get approval
    to send packet out eth0.
6. internal host: Get a request to from
7. internal host: Send response back to from
8. internal host: Route via default gateway.
9. firewall: Receive request from to
10. firewall: Packet does not go through may iptable because it is
     an ip_conntrack(ed) connection.  OK to send out because it was
    previously DNATed.
11. firewall: Route response based on -> addresses.
12. firewall: A policy routing rule is triggered based on source
      and route is made via (eth2).
13. firewall: The packet is now unDNATed (SNATed) and the source address
     is changed from to
14. client: Receives response from to

>From experience this is what seems to happen.  If this info is indeed correct, I think it
would benefit
to be placed in the FAQ.  It took quite a bit on experimentation to get all this to work.

I hope this helps anyone with two or more external interfaces.