Port forward with iptables
Solution 1
When you are trying to reach localhost, your source address is 127.0.0.1, as is your destination address. So, packet looks something like this:
| SRC | DST |
| 127.0.0.1 | 127.0.0.1 |
Since packets that are locally generated first traverse OUTPUT chain, you modify the packet with DNAT rule:
iptables -t nat -A OUTPUT \
-d 127.0.0.1 \
-p tcp --dport 80 \
-j DNAT \
--to 172.17.42.1:80
After OUTPUT chain, packet looks like this:
| SRC | DST |
| 127.0.0.1 | 172.17.42.1 |
So, the first error you see is that even if this packet gets routed outside the source device, destination will not know how to correctly return it. Thus, you also need to add additional SNAT rule:
iptables -t nat -A POSTROUTING \
-d 172.17.42.1 \
-p tcp --dport 80 \
-j SNAT \
--to-source <your_ip_addr>
Now, packet will look pretty much correct to exit on the network (source address will be the public address of this device and not localhost address).
But, this won't solve your pain just yet.
Routing decisions for packets generated on a local machine are taken in two places.
- before the OUTPUT chain
- after the OUTPUT chain and before POSTROUTING
Decision for this packet will be made in the second stage, before the source IP is rewritten in POSTROUTING chain...
So, kernel security mechanism will drop the packet because by default kernel refuses to route packets with src of 127.0.0.1. That means that even mangle coupled with fwmark wouldn't help us here. What's needed for this to work is to enable route_localnet. That's the per-iface variable that enables the use of 127/8 for local routing purposes (default is 0 - aka disabled).
sysctl -w net.ipv4.conf.all.route_localnet=1
You can safely change 'all' for the name of the outgoing interface in your case.
Now, packets will get routed by the previous rules in the table, and since we had the POSTROUTING SNAT, we will fix the source ip of 127.0.0.1 and rewrite it.
Hope this helps.
PS. sorry before 2-3 edits, it's 5AM here and I'm still awake :)
Solution 2
Before one jumps straight into firewall rules, one should also perform a simple forwarding check. Rather like when one checks the power cord is plugged in before taking apart the hardware.
Run:
cat /proc/sys/net/ipv4/ip_forward
If you get a zero, IPv4 will not forward. You'll need to turn this on.
To turn it on immediately and ephemerally to verify behavior:
echo 1 > /proc/sys/net/ipv4/ip_forward
The above turns it on for the machine but is simply modifying a kernel setting on the fly and will not be "saved".
Edit the sysctl.conf file to make the proper permanent change and ensure the following setting:
net.ipv4.ip_forward = 1
Related videos on Youtube
svobol13
Updated on September 18, 2022Comments
-
svobol13 almost 2 years
I got
lo
(127.0.0.1
) andeth0
(172.17.0.8
). I want to redirect packets that land on127.0.0.1:80
to172.17.42.1:80
(route frometh0
).I tried
iptables -t nat -A OUTPUT -p tcp --dport 80 -d 127.0.0.1 -j DNAT --to 172.17.42.1:80
But when I do
curl localhost:80
I get no response, when I docurl 172.17.42.1:80
it just works.-
Clément Perroud over 9 yearsNo, because packets that are generated locally don't go through a PREROUTING at all. First routing decision is taken before the packet enters OUTPUT chain, and second (final) routing decision is taken after the packet exits OUTPUT and before it enters POSTROUTING chain.
-
Magellan over 9 yearsYou turned on ipv4 forwarding in your sysctl.conf (and then reloaded) or in the appropriate location in /proc, yes?
-
-
Clément Perroud over 9 yearsACtually you don't need ip_forward for locally generated packets. ip_forward only allows incoming packets to be forwarded to other machines, it doesn't do anything for packets originating from localhost.
-
rjh about 7 yearsThis is fantastic thanks. I really appreciated how you explained each part of the routing and why it won't work without an extra step.