LinuxUnix

Linux multihome host with rule based routing

Posted

http://jensd.be/468/linux/two-network-cards-rp_filter

rp_filter

Since RHEL 6 (and CentOS 6), asymmetric routing doesn’t work anymore out of the box. The cause of the issue is a change in the default value for kernel parameter rp_filter.

Rp_filter stands for reverse path filtering. The reverse path filter will check if the source of a packet that was received on a certain interface is reachable trough the same interface it was received. The purpose is to prevent spoofed packets, with a changed source address, not being processed/routed further. In a router it could also prevent routing packets that have a private IP as source to the internet as they obviously will never find their way back.

Since RHEL 6 and its derivative CentOS 6, rp_filter, which can be controlled by kernel parameters, is set on a default value of 1. This means that the rp_filter is operational in strict mode and does exactly what it is designed for.

Possible value are:

  • 0: No source validation
  • 1: Strict mode (failed packets are discarded), described in RFC3704
  • 2: Loose mode, only discards the packet when it isn’t routable over any of the interfaces on the host.
$ sysctl -a|grep rp_filter
...
net.ipv4.conf.all.arp_filter = 0
net.ipv4.conf.all.rp_filter = 0
net.ipv4.conf.default.arp_filter = 0
net.ipv4.conf.default.rp_filter = 1
net.ipv4.conf.ens192.arp_filter = 0
net.ipv4.conf.ens192.rp_filter = 1
net.ipv4.conf.ens224.arp_filter = 0
net.ipv4.conf.ens224.rp_filter = 1
net.ipv4.conf.lo.arp_filter = 0
net.ipv4.conf.lo.rp_filter = 0
$ sudo sysctl net.ipv4.conf.all.rp_filter=2
net.ipv4.conf.all.rp_filter = 2
$ echo “net.ipv4.conf.all.rp_filter = 2″|sudo tee /etc/sysctl.d/99-rp_filter.conf

The best solution

Besides the quick solution, there is also a better solution. While changing the value for rp_filter is getting both interfaces and IP-addresses to respond from other networks, the setup is still asymmetric. The best solution is to get rid of the asymmetric routing and let each interface route it’s own packets to the default gateway.

A normal routing table can only have one default gateway. This is quite logical since it’s the place where to send packets that do not match anything else in the rest of the table. To be able to have two default gateways, one for each interface, you need to setup policy based routing.

Policy based routing allows you to have multiple routing tables. Which table is used, depends on a set of rules.

To setup policy based routing for our example case, we will use two policy based tables. While it is possible to give a nice name to the tables (in /etc/iproute2/rt_tables), it’s not really when you only plan to have a few. Without a name, the tables are automatically created when you’re adding something to them.

Let’s start with adding a route for the network itself (link) and one for the default gateway for each interface. ens192 (192.168.0.10) will use table 1, ens224 (192.168.1.10) will use table 2.

To define when table 1 or 2 will be used, we’ll add a rule, based on the source of the packet to the policy and refresh the policy based routing:

To check if we did everything correctly, let’s list the tables and the rules:

As you can see in the output from ip rule show, our policy based tables have a higher priority than the main table, which can be viewed with ip route. Nevertheless it’s import to still have a default route in the main table since packets leaving the machine itself can have a source IP of 0.0.0.0 and would not match any of the rules in our policy.

Make the changes permanent

Up to now, the changes would get lost after a reboot or restart of the networking. To make the changes permanent, create a route and rule file for every interface. For the above example, the contents would look like this:

Now your configuration should be persistent.