Skip to content

Pushing a packet back and forth between Linux subsystems

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 <gu48cs$rul$1@news1.tnib.de> 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.

1: ext0:  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:

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
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.

The PREROUTING chain of the mangle table has these rules:

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
and the POSTROUTING chain of the nat table has these:
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?

Trackbacks

No Trackbacks

Comments

Display comments as Linear | Threaded

Anonymous on :

Dare I ask why you want HTTP{,S} traffic on a separate interface?

Marc 'Zugschlus' Haber on :

This is basically only a simple example for a class of problems. In real life, one frequently funnels web traffic through a cheaper connection (which may be consumer grade, with dynamic ip yadda yadda) while the critical applications are routed though the low-latency, high-SLA leased line.

XANI on :

There was a ROUTE target patch to kernel and iptables but i couldn't find it for new kernel. Using that you could direct certain packets to certain GW/iface.

And btw at example in article. Instead of 2xSNAT you could just do -o MASQUERADE and dont worry about IP ;]

Marc 'Zugschlus' Haber on :

MASQUERADE is documented as "should only be used with dynamically assigned IP connections". The IP addresses in question are static, and I would like to keep the possibility to NAT to some other IP than the router's primary IP on that network.

flawed on :

Now, what would be the rationale behinde the dynamic IP restriction for MASQUERADE?

Add Comment

Markdown format allowed
Enclosing asterisks marks text as bold (*word*), underscore are made via _word_.
Standard emoticons like :-) and ;-) are converted to images.
E-Mail addresses will not be displayed and will only be used for E-Mail notifications.
Form options