3

I'd like to set up a TCP DNAT from 127.0.0.1, port 4242 to 11.22.33.44, port 5353 on Linux 3.x (currently 3.2.52, but I can upgrade if needed).

It looks like the simple DNAT rule setup doesn't work, telnet 127.0.0.1 4242 hangs for a minute in Trying 127.0.0.1..., and then it times out. Maybe it's because the kernel is discarding the returning packets (e.g. SYN+ACK), because it considers them Martian. I don't need an explanation why the simple solution doesn't work, I need a solution, even if it's complicated (e.g. it involves creating may rules).

I could set up a usual DNAT from another local IP address, outside the 127.0.0.0/8 network, but now I need 127.0.0.1 as the destination address. I know that I can set up a user-level port forwarding process, but now I need a solution which can be set up using iptables and doesn't need helper processes.

I was googling for this for an hour. It was asked multiple times, but I couldn't find any working solutions.

Also there are many questions about DNAT to 127.0.0.1, but I don't need that, I need the opposite.

Please note that a single iptables -j REDIRECT rule wouldn't work, because I need to change the IP address of the outgoing packet from 127.0.0.1 to 11.22.33.44, and -j REDIRECT can't change the IP address.

pts
  • 425
  • 1
  • 5
  • 15

3 Answers3

6

This works for me, routing traffic from localhost:8081 to 172.17.0.1:80, where 172.17.0.1 is a veth behind the bridge interface named docker0.

sysctl -w net.ipv4.conf.docker0.route_localnet=1
iptables -t nat -A OUTPUT -o lo -p tcp -m tcp --dport 8081 -j DNAT --to-destination 172.17.0.1:80
iptables -t nat -A POSTROUTING -o docker0 -m addrtype --src-type LOCAL --dst-type UNICAST -j MASQUERADE

A key piece of the puzzle is the MASQUERADE rule.

Further inspiration:

pwaller
  • 161
  • 1
  • 3
  • Thank you for your answer! Unfortunately `sysctl -w net.ipv4.conf.eth0.route_localnet=1` didn't make any difference, it still doesn't work. – pts May 18 '18 at 07:31
5

You will have to run the following three commands to make it work:

iptables -t nat -A OUTPUT -p tcp -d 127.0.0.1 --dport 4242 -j DNAT --to 11.22.33.44:5353
sysctl -w net.ipv4.conf.eth0.route_localnet=1
iptables -t nat -A POSTROUTING -p tcp -s 127.0.0.1 -d 11.22.33.44 --dport 5353 -j SNAT --to $your-eth0-ip

Here is the detailed explanation.

The first command will do the DNAT as expected. However, if you try to capture packet with only this rule set, you will find you get nothing:

tcpdump -i any -n port 5353

This is because linux kernel drop this kind of packet by default, if the packet has 127.0.0.0/8 as one end, and an external ip address as the other end.

The second command changes the kernel parameter, to let this kind of packet pass (of course, you should change eth0 accordingly). After this, when you capture packet on eth0, you will see packets sent out, but with source address 127.0.0.1 and destination address 11.22.33.44. Whether this packet can reach the target server or not (the intermediate routers will drop this packet), there is no way for this packet to return. So you should add an SNAT rule, to change the source address to your eth0's. And now it should work.

stephenjy
  • 51
  • 1
  • 1
  • Thank you for your answer! Unfortunately `sysctl -w net.ipv4.conf.eth0.route_localnet=1` didn't make any difference, it still doesn't work. – pts May 18 '18 at 07:30
1

Is it something like this you need ? iptables port redirect not working for localhost

iptables -t nat -I OUTPUT -p tcp -d 127.0.0.1 --dport 443 -j REDIRECT --to-ports 8080

Adrien

Adrien M.
  • 226
  • 2
  • 5
  • Thank you for writing your answer. Unfortunately it doesn't solve my problem, because `-j REDIRECT` can't change the IP address, and I need to change it from 127.0.0.1 to 11.22.33.44. If I didn't need to change the IP address, it would work. – pts Nov 08 '13 at 07:21
  • iptables won't change the IP address. What are you trying to achieve ? Why do you want to change the IP address ? – Lawrence Nov 08 '13 at 07:48
  • Sorry, missed some lines. So, it's not a REDIRECT that you need, but rather something like : `iptables -t nat -A PREROUTING -d 127.0.0.1 -j DNAT --to 11.22.33.44` But according to what you're trying to do, if you only change the 127.0.0.1 packet, you'll also need to change the return packet (11.22.33.44 => 127.0.0.1). Have a look on [this Schema](http://upload.wikimedia.org/wikipedia/commons/8/8f/Diagrama_linux_netfilter_iptables.gif) to understand placement of tables : – Adrien M. Nov 08 '13 at 08:18
  • @Adrien M.: Yes, this (with `-j DNAT`) is the standard solution, but it doesn't work for `telnet 127.0.0.1 4242` (it works with all other local IP address), because Linux is dropping the response packets (see the details in the question). If you think you have a solution which solves the problem, please post it. – pts Nov 08 '13 at 13:19
  • @Lawrence: I'm trying to achieve that the connection initiated by `telnet 127.0.0.1 4242` gets forwarded to 11.22.33.44:5353 by the kernel, without using a user-level port forwarder process. Sorry if this was not clear from the question. – pts Nov 08 '13 at 13:21
  • Ah Righto then. When you said iptables won't change the IP address I got a bit confused. – Lawrence Nov 08 '13 at 13:48