Traditional IP routing in Linux selects the next-hop gateway based solely on the packet’s destination IP address using the main kernel routing table. However, multi-homed servers, dual-WAN gateways, and VPN tunnel endpoints often require granular traffic control based on source IP address, network interface, or firewall packet marks. Linux implements this capability through Policy-Based Routing (PBR) and the Routing Policy Database (RPDB), managed via the ip rule and ip route commands from the iproute2 suite.
Understanding the Routing Policy Database (RPDB)
When the Linux kernel prepares to route an outbound or forwarded packet, it consults the RPDB in ascending numerical priority (from priority 0 up to 32767). Each rule specifies matching criteria and designates which routing table evaluates the packet. By default, Linux includes three core rules:
- Priority 0 (local table): Handles local loopback and broadcast addresses. This rule cannot be deleted.
- Priority 32766 (main table): Evaluates standard static and DHCP routes configured on the machine.
- Priority 32767 (default table): Evaluates fallback post-processing routes; typically empty unless specifically populated.
Policy-based routing allows administrators to insert custom rules between priority 1 and 32765 that direct packets to independent, isolated routing tables before the main table is evaluated.
Inspecting Active Routing Rules
To inspect your server’s current RPDB configuration:
- Open your Linux terminal session.
- Run the rule listing command:
ip rule show - The terminal displays the priority number, selector criteria (such as
from allor specific subnets), and the target table name or integer ID.
Defining Custom Routing Table Names
While routing tables can be referenced by numeric IDs (1 to 252), naming your tables prevents configuration errors and simplifies auditing:
- Open the routing table definition file in a text editor with root privileges:
sudo nano /etc/iproute2/rt_tables - Scroll to the bottom of the file and add your custom table ID and identifier name (for example, table ID
100for a secondary ISP or VPN):100 custom_isp - Save and close the file. The Linux kernel and
iproute2utilities will now recognisecustom_ispas a valid table identifier.
Populating Routes in the Custom Table
Before adding policy rules that point traffic to the custom table, configure the appropriate gateway routes inside that table:
- Add the network subnet route for the secondary interface (e.g.,
eth1):sudo ip route add 192.168.2.0/24 dev eth1 src 192.168.2.50 table custom_isp - Add the dedicated default gateway for the secondary uplink:
sudo ip route add default via 192.168.2.1 dev eth1 table custom_isp - Verify the contents of the custom table:
ip route show table custom_isp
Adding Policy Rules Using ip rule
Once the custom routing table contains valid routes, attach policy rules in the RPDB to route matching traffic through it:
1. Route by Source IP Address
To force all traffic originating from a secondary IP address (such as 192.168.2.50) to exit through its corresponding gateway:
sudo ip rule add from 192.168.2.50/32 table custom_isp priority 1000
2. Route by Incoming Network Interface
To route traffic received on a specific interface through the custom table:
sudo ip rule add iif eth1 table custom_isp priority 1010
3. Route by Firewall Mark (fwmark)
For advanced traffic shaping, combine iptables or nftables packet marking with policy routing. If firewall rules mark specific packets with hex value 0x1:
sudo ip rule add fwmark 1 table custom_isp priority 1020
Testing and Deleting Policy Rules
To test how the Linux kernel resolves a simulated route for a specific source and destination IP:
ip route get 8.8.8.8 from 192.168.2.50
The output indicates the chosen gateway, outbound interface, and matching routing table (e.g., table custom_isp).
If you need to remove a rule:
sudo ip rule del from 192.168.2.50/32 table custom_isp
Alternatively, delete the rule directly by priority:
sudo ip rule del priority 1000