I'm trying to understand the limitations of using iptables DNAT with loopback addresses. Consider we have an application which can connect only to 127.0.0.1; the obvious solution to make server and client work on different nodes is to use NAT as following:
Make sure all outgoing packets have the main client host's ip address:
iptables -t nat -A POSTROUTING -o eth0 -j SNAT --to-source <CLIENT_IP>
Now let's try to fool the client and DNAT a connection to the outside world:
iptables -t nat -A OUTPUT -d 127.0.0.1/32 -p tcp -m tcp --dport <SERVICE_PORT> -j DNAT --to-destination <SERVER_IP>
Unfortunately, that just doesn't work, when trying to connect to 127.0.0.1:<SERVICE_PORT>
program just hangs on connect
syscall. The interesing thing is that I cannot see any SYN packets on any interfaces (tcpdump -qn -i any port <SERVICE_PORT>
), however I can see the packet counters increase while looking at iptables statistics (iptables -nvL -t nat
).
Googling for solution I've found a kernel build option CONFIG_IP_NF_NAT_LOCAL
, which was used in kernels 2.6.0-2.6.10 to solve the problem of using NAT with locally originated connections. Unfortunately, current git kernel tree holds information for versions 2.6.11 and later, so I hit dead end here.
Looking further I've run into this thread, concerning fixes of some DNAT on loopback issues in 2.6.11; one of the patches removes CONFIG_IP_NF_NAT_LOCAL
build option and enables the code unconditionally (here's the actual diff).
However, the current state of this problem is quite unclear to me. I'd like to find some explanations or references proving this is a kernel bug or some not-so-well-documented feature. I know that network 127.0.0.0/8 routing is handled by the kernel in a special way, but from the iptables documentation it is clear that nat table OUTPUT chain is the only place which makes sense for that kind of rules and states no exceptions or any kind of notes regarding 127/8 network.
Please refrain from advising some workarounds with third-party tools, I've already have some, I just want to have some explanation why this exact configuration does not work and why it should not work. Any examples of how this DNAT can be done with any other iptables rules are welcome.
The above configuration was tested on Debian squeeze and wheezy, with kernels 2.6.32, 3.1.0, 3.2.0.