Linux Policy Routing with iprule and iproute2: Managing Rules and Routing Tables
Linux policy routing is implemented with iproute2. Use ip to inspect, create, and delete rules and routes; legacy tools like ifconfig/route cannot express policy decisions.
Verify iproute2
# ip -V
ip utility, iproute2-5.15.0
Policy Routing Rule Database (RPDB)
Viewing rules
# ip rule list
0: from all lookup local
32766: from all lookup main
32767: from all lookup default
Adding rules
Rules select a routing table based on match conditions. Common selectors include from (source), to (destination), tos, fwmark, and iif (incoming inetrface).
- Match by source address
# ip rule add from 10.20.30.40 table 100
# ip rule add from 10.20.31.0/24 table 200
- Match by destination address
# ip rule add to 203.0.113.5 table 100
# ip rule add to 203.0.113.0/24 table 200
- Match by firewall mark (with Netfilter)
Mark traffic in mangle and route by the mark value. Example: send DNS via table 101, HTTPS via table 102, and everything else via table 103.
# iptables -t mangle -A PREROUTING -i lan0 -p udp --dport 53 -j MARK --set-mark 0x1
# iptables -t mangle -A PREROUTING -i lan0 -p tcp --dport 443 -j MARK --set-mark 0x2
# iptables -t mangle -A PREROUTING -i lan0 -j MARK --set-mark 0x3
# ip rule add fwmark 0x1 table 101
# ip rule add fwmark 0x2 table 102
# ip rule add fwmark 0x3 table 103
- Match by input interface
# ip rule add iif lan1 table 201
# ip rule add iif lan2 table 202
Rule priorities
Rules are processed by ascending priority (pref). Lower numbers win. The kernel installs three defaults:
# ip rule list
0: from all lookup local
32766: from all lookup main
32767: from all lookup default
When you add rules with out an explicit priority, they are assigned below 32766 (32765, 32764, ...). Set an explicit priority with pref:
# ip rule add from 10.20.31.0/24 table 200 pref 20
# ip rule add from 10.20.30.0/24 table 100 pref 10
# ip rule list
0: from all lookup local
10: from 10.20.30.0/24 lookup 100
20: from 10.20.31.0/24 lookup 200
32766: from all lookup main
32767: from all lookup default
Routing table identifiers
Tables are addressed by numeric ID or by name via /etc/iproute2/rt_tables. Add readable names when useful:
# printf "101 corpnet\n102 guestnet\n" >> /etc/iproute2/rt_tables
Deleting rules
Any unique selector can be used to delete a rule:
# ip rule del pref 10
# ip rule del from 10.20.31.0/24
# ip rule del table 200
# ip rule del from 10.20.30.0/24 table 100 pref 10
# ip rule list
0: from all lookup local
20: from 10.20.31.0/24 lookup 200
32766: from all lookup main
32767: from all lookup default
Routing Table Management
Viewing a routing table
Inspect which tables are referenced by rules, then show table contents. Omitting the table defaults to main.
# ip rule list
0: from all lookup local
32766: from all lookup main
32767: from all lookup default
# ip route show table main
192.0.2.0/24 dev enp0s3 proto kernel scope link src 192.0.2.10
10.0.5.0/24 dev enp0s8 proto kernel scope link src 10.0.5.20
default via 192.0.2.1 dev enp0s3
Built-in tables:
- local: host and broadcast routes for local addresses; auto-populated.
- main: the primary routing table used by default; typically populated from interface configuration.
- default: empty unless explicitly used.
Adding routes to main
# ip route show table main
192.0.2.0/24 dev enp0s3 proto kernel scope link src 192.0.2.10
10.0.5.0/24 dev enp0s8 proto kernel scope link src 10.0.5.20
default via 192.0.2.1 dev enp0s3
# ip route add 172.16.10.0/24 via 192.0.2.254 dev enp0s3 table main
# ip route show table main
192.0.2.0/24 dev enp0s3 proto kernel scope link src 192.0.2.10
10.0.5.0/24 dev enp0s8 proto kernel scope link src 10.0.5.20
172.16.10.0/24 via 192.0.2.254 dev enp0s3
default via 192.0.2.1 dev enp0s3
Creating and populating a new table
A route in a non-main table is only used when a rule selects that table.
# echo "201 corpnet" >> /etc/iproute2/rt_tables
# ip rule list
0: from all lookup local
32766: from all lookup main
32767: from all lookup default
# ip rule add from 10.0.5.0/24 table corpnet pref 50
# ip route show table corpnet
# (empty)
# ip route add table corpnet 10.0.5.0/24 dev enp0s8 scope link
# ip route add table corpnet default via 10.0.5.1 dev enp0s8
# ip route show table corpnet
10.0.5.0/24 dev enp0s8 scope link
default via 10.0.5.1 dev enp0s8
Deleting routes from a table
# ip route show table corpnet
10.0.5.0/24 dev enp0s8 scope link
default via 10.0.5.1 dev enp0s8
# ip route del default table corpnet
# ip route show table corpnet
10.0.5.0/24 dev enp0s8 scope link
# ip route del 10.0.5.0/24 table corpnet
# ip route show table corpnet
# (empty)