How Linux Routing Tables Work
Introduction
A long time ago when I had to redo all the infrastructure of a crypto company I had to use Linux Routing Tables on the gateway. Routing tables are a core kernel mechanism that determines how packets leave the system. Every packet theat goes through uses a routing decision based on destination, policy rules, and routing tables. Understanding this is non-negotiable if you work with servers, containers, Kubernetes, VPNs, or cloud networking.
This article explains how Linux routing tables actually work, and how to use iproute2 to interact with them.
Routing Tables
A routing table is not just a list of networks and gateways. In Linux, it is a forwarding information base (FIB) stored in kernel memory. Each entry defines:
- Destination prefix (CIDR)
- Next hop (gateway or interface)
- Metric (priority)
- Scope (link, global, host)
- Source address (optional)
- Flags (proto, type)
You can inspect the main table with:
ip route show
Example:
default via 192.168.1.1 dev eth0 metric 100
192.168.1.0/24 dev eth0 proto kernel scope link src 192.168.1.200
Route Selection
Linux uses Longest Prefix Match (LPM):
/32beats/24/24beats/16- Specific routes always override generic ones
Example:
ip route add 10.0.0.0/8 via 192.168.1.1
ip route add 10.1.2.0/24 via 192.168.1.254
Traffic to 10.1.2.10 will always use the /24 route, regardless of metrics.
Metrics are only considered when prefix length is equal.
Common Routing Tables
Linux supports multiple routing tables, not just main.
Common tables:
| Table Name | ID |
|---|---|
| local | 255 |
| main | 254 |
| default | 253 |
These tables are defined in: /etc/iproute2/rt_tables
List all tables:
ip route show table all
This design enables policy-based routing, which is critical for:
- Multi-homed hosts
- VPN split tunneling
- Kubernetes CNI routing
- Cloud NAT and egress control
Policy Routing: ip rule
Routing decisions are two-step:
ip ruleselects a routing tableip routeselects a route inside that table
ip rule show
This command inspect rules, and shows this output as example:
200: from 10.0.0.0/24 lookup vpn
Which basically means that if the source IP is 10.0.0.0/24, use the vpn routing table. This is used a lot by the wireguard VPN and it happens before any route lookup in main.
The local Table
The local table handles:
- Loopback traffic
- Local IP addresses
- Broadcast addresses
ip route show table local
If this table is broken, the system cannot talk to itself. This is why tools like Docker, Kubernetes, and VPNs must respect it.
Common Debugging Commands
ip route get 8.8.8.8
Shows the exact route chosen.
ip rule
ip route show table main
ip route show table <custom>
For live traffic validation:
tcpdump -i eth0
Or inspect kernel decisions:
ss -tup
Example of two NICs using Policy Routing
Imagine a scenario where a host with two interfaces, each connected to a different network. The traffic must exit on the same interface it entered. How does that will work?
- eth0: 10.0.0.20 10.0.0.0/24
- eth1: 172.16.0.20 172.16.0.0/24
echo "100 net10" >> /etc/iproute2/rt_tables
echo "200 net172" >> /etc/iproute2/rt_tables
ip rule add from 10.0.0.0/24 table net10
ip rule add from 172.16.0.0/24 table net172
ip route add default via 10.0.0.1 dev eth0 table net10
ip route add default via 172.16.0.1 dev eth1 table net172
This will create two policy routes: net10 and net172 and guarantee that all the traffic goes to the correct policy and the correct gateway.
Debugging order
- Initiate where the packet is generated or received (
tcpdump) ip ruleselects routing table- Longest prefix match selects route
- Metric resolves ties
- Packet exits via interface or gateway
If you debug routing without following this order, you will waste hours.