How to Configure Linux Advanced Policy Routing using iproute2

The Limits of Standard Routing

In a typical Linux environment, routing is incredibly simple. When a server wants to send an IP packet to the internet, the kernel looks at the packet’s destination address, consults the main routing table, finds the “Default Gateway,” and forwards the packet out of eth0.

However, what happens if your server has two completely different internet connections? Suppose eth0 is connected to a high-speed fiber line (for serving web traffic) and eth1 is connected to a slow, highly secure VPN tunnel (for database backups). The standard routing table can only have one active default gateway. If the web server attempts to reply to a client, the packet might accidentally route out of the VPN tunnel, causing the connection to fail.

To solve this, system administrators use Advanced Policy Routing via the iproute2 suite. Policy Routing allows you to make routing decisions based not just on the destination IP, but on the Source IP, the specific network interface, or even the TCP/UDP port. You can create multiple independent routing tables and define strict rules determining which table a packet must use.

Step 1: Creating Custom Routing Tables

Linux allows you to create up to 255 independent routing tables. They are defined in the /etc/iproute2/rt_tables file.

Open the file with root privileges:

sudo nano /etc/iproute2/rt_tables

Add two new tables at the bottom, assigning them arbitrary ID numbers (e.g., 10 and 20) and logical names:

10  fiber_route
20  vpn_route

Save and close the file.

Step 2: Populating the Routing Tables

Now, you must define the specific routes within those isolated tables.

Assume the fiber connection on eth0 has a gateway of 192.168.1.1 and a local IP of 192.168.1.100.

To add the default route specifically into the fiber_route table, execute:

sudo ip route add default via 192.168.1.1 dev eth0 table fiber_route

Assume the VPN connection on eth1 has a gateway of 10.0.0.1 and a local IP of 10.0.0.50.

To add the default route specifically into the vpn_route table, execute:

sudo ip route add default via 10.0.0.1 dev eth1 table vpn_route

Step 3: Creating the Routing Rules

The tables exist, but the kernel won’t use them until you define the Rules that dictate when a packet should be pushed into a specific table.

We want to ensure that if a packet originates from the fiber IP (192.168.1.100), it must leave through the fiber gateway. If a packet originates from the VPN IP (10.0.0.50), it must leave through the VPN gateway. This is known as Source-Based Routing.

Execute the following rules:

sudo ip rule add from 192.168.1.100 table fiber_route
sudo ip rule add from 10.0.0.50 table vpn_route

To verify that your rules are active, run:

ip rule show

Making the Configuration Persistent

The commands executed with ip route and ip rule are volatile. They reside entirely in RAM and will vanish the second the Linux server reboots.

To make them permanent on Ubuntu/Debian systems utilizing netplan, you must edit the YAML configuration file (typically located in /etc/netplan/).

network:
  version: 2
  ethernets:
    eth0:
      addresses:
        - 192.168.1.100/24
      routing-policy:
        - from: 192.168.1.100
          table: 10
      routes:
        - to: default
          via: 192.168.1.1
          table: 10
    eth1:
      addresses:
        - 10.0.0.50/24
      routing-policy:
        - from: 10.0.0.50
          table: 20
      routes:
        - to: default
          via: 10.0.0.1
          table: 20

Apply the netplan configuration:

sudo netplan apply

Your server now possesses a highly sophisticated routing intelligence, capable of effortlessly juggling multiple active WAN connections simultaneously without ever crossing the streams.

Get the best tech tips delivered straight to your inbox.

Join thousands of readers mastering Apple, Google, Microsoft, and Linux.