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):

  • /32 beats /24
  • /24 beats /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:

  1. ip rule selects a routing table
  2. ip route selects 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

  1. Initiate where the packet is generated or received (tcpdump)
  2. ip rule selects routing table
  3. Longest prefix match selects route
  4. Metric resolves ties
  5. Packet exits via interface or gateway

If you debug routing without following this order, you will waste hours.