Linux policy routing is still incredibly painful if one wants to have more sophisticated routing than just "take source and destination IP address for the routing decision". The mechanisms that have been in use seven years ago still work though, and I didn't find any possibility to do it any easier. In this article, I'll try to explain the "old" mechanisms and hope that somebody from lazyweb will comment and say "it can be done so much easier".
This is a translation of the Usenet article <email@example.com> in de.comp.os.unix.networking.misc in the hope that the english-speaking blogosphere can give additional insights.
Given a Linux-based router with one internal network (int0), one perimeter network (per0) and two Internet connections (ext0, ext1) with one IP address each. We need to do source NAT to deliver Internet to the internal and perimeter networks. The internet connection on ext0 will be used for http and https, while all other traffic needs to go out on ext1. The perimeter network is reachable from the outside via destination NAT.
mtu 1500 qdisc noqueue inet 10.81.221.145/29 brd 10.1.221.151 scope global ext0 2: ext1: mtu 1500 qdisc noqueue inet 10.82.83.225/29 brd 10.82.83.231 scope global ext1 3: per0: mtu 1500 qdisc noqueue inet 172.16.1.254/24 brd 172.16.1.255 scope global per0 4: int0: mtu 1500 qdisc noqueue inet 192.168.8.254/24 brd 192.168.8.255 scope global int0
We have the following routing rules:
Table main contains all rules for the directly connected networks, but no default route. Both to_ tables have one default route each, pointing to the respective ISP, and the table defaultroute has once more a default gateway pointing to ext1's gateway.0: from all lookup 255 10: from all lookup main 32: from all fwmark 0x12e lookup to_ext0 32: from all fwmark 0x12f lookup to_ext1 32000: from all lookup defaultroute 32000: from all lookup defaultroute 32766: from all lookup main 32767: from all lookup default
The PREROUTING chain of the mangle table has these rules:
and the POSTROUTING chain of the nat table has these:iptables --protocol tcp --dport 80 --in-int int+ --set-mark 0x12e iptables --protocol tcp --dport 443 --in-int int+ --set-mark 0x12e iptables --in-int int+ --set-mark 0x12f
iptables --match mark --mark 0x12e --out-int ext0 --jump SNAT --to-source 10.81.221.145 iptables --match mark --mark 0x12f --out-int ext1 --jump SNAT --to-source 10.82.83.225
If now somebody from the internal network accesses a web server on the Internet. The outgoig packet will first be marked in the PREROUTING chain, then the routing rules will use the firewall mark to consult the correct routing table and pass the packet back to the packet filter, where the POSTROUTING chain will use the outgoing interface and the firewall mark to SNAT the packet to the correct source IP so that the answer will actually reach us.
Has a less ugly way available way to do this become available in the last five years? Or is it still necessary to have the different parts of the networking subsystems play ping-pong with the packet? I particularly dislike that someone not familiar with policy routing will not see any default route in the routing table printed by "route" or "ip route", which will probably be very confusing.
Is there any possibility to interact with the routing code without firewall marks, maybe by directly setting a gateway from a firewall rule?