Every packet a Linux system sends to a destination outside its immediate local network must be routed — sent to the correct next hop toward its final destination. The IP routing table is the internal map that tells the kernel exactly how to do this. Understanding how to read and interpret this table is essential for diagnosing “can’t reach the internet” or “can’t reach this specific network” problems. This article explains routing tables from first principles, how to inspect them on Linux, and how to troubleshoot common routing issues.
What Is a Routing Table, Conceptually?
Think of the routing table as a decision list the kernel consults every time it needs to send a packet: “given this destination IP address, which network interface and which next-hop router should I use?” Entries are checked from most-specific to least-specific (a concept called longest prefix match), with a “default route” acting as the catch-all for anything not matched by a more specific entry.
flowchart TD
A[Packet to Send] --> B{Destination Matches a Specific Route?}
B -->|Yes - e.g. local subnet| C[Send Directly on That Interface]
B -->|No specific match| D{Default Route Exists?}
D -->|Yes| E[Send via Default Gateway]
D -->|No| F[Destination Unreachable]
Viewing the Routing Table
Modern Method: ip route
ip route show
Example output:
default via 192.168.1.1 dev eth0 proto dhcp metric 100
192.168.1.0/24 dev eth0 proto kernel scope link src 192.168.1.50 metric 100
10.0.0.0/24 via 192.168.1.254 dev eth0
Legacy Method: route
route -n
Example output:
Destination Gateway Genmask Flags Metric Ref Use Iface
0.0.0.0 192.168.1.1 0.0.0.0 UG 100 0 0 eth0
192.168.1.0 0.0.0.0 255.255.255.0 U 100 0 0 eth0
10.0.0.0 192.168.1.254 255.255.255.0 UG 0 0 0 eth0
The route command is considered legacy (part of the older net-tools package) — ip route from iproute2 is the modern standard on virtually all current distributions.
Understanding Each Field
| Field | Meaning |
|---|---|
default / 0.0.0.0/0 | The catch-all route used when no more specific route matches |
via 192.168.1.1 | The next-hop gateway/router IP |
dev eth0 | The network interface used for this route |
proto dhcp/kernel/static | How this route was added (DHCP, automatically by kernel, manually configured) |
scope link | This route reaches directly-connected hosts, no gateway needed |
metric | A priority value — lower metric routes are preferred when multiple routes could match |
src | The source IP address used when sending via this route |
What “Longest Prefix Match” Means
Given multiple matching routes, the kernel always chooses the most specific one (the one with the longest subnet mask/prefix). For example, given both:
0.0.0.0/0 via 192.168.1.1
10.0.0.0/24 via 192.168.1.254
A packet destined for 10.0.0.5 matches both routes technically, but 10.0.0.0/24 is more specific (a /24 is narrower than a /0), so it wins, and the packet is sent via 192.168.1.254 instead of the default gateway.
Viewing the Route to a Specific Destination
Rather than reading the whole table, you can ask the kernel exactly which route it would use for a particular destination:
ip route get 8.8.8.8
Example output:
8.8.8.8 via 192.168.1.1 dev eth0 src 192.168.1.50 uid 1000
This is often faster and less error-prone than manually reading through the whole table and figuring out which entry applies.
Adding and Removing Routes Manually (Runtime, Non-Persistent)
Add a route to a specific network via a specific gateway:
sudo ip route add 172.16.0.0/24 via 192.168.1.254 dev eth0
Add a default route:
sudo ip route add default via 192.168.1.1
Delete a route:
sudo ip route del 172.16.0.0/24
Important: these commands are not persistent — they take effect immediately but will be lost on reboot unless also added to your persistent network configuration (Netplan, nmcli, /etc/network/interfaces, etc., as covered in our article on boot-time networking).
Multiple Routing Tables (Policy Routing)
Linux supports more than one routing table simultaneously, useful for advanced scenarios like routing different traffic through different gateways (e.g., VPN split-tunneling):
ip route show table all
ip rule show
ip rule defines which routing table is consulted for which type of traffic — a topic sometimes called policy-based routing, useful when a server has multiple network interfaces (e.g., one for a management network, one for public internet).
A Practical Example: Diagnosing “Can Reach Local Network, Not the Internet”
Step 1: Check for a default route
ip route show | grep default
If nothing is returned, there’s no default route configured — this alone explains why only the local subnet is reachable.
Step 2: Add the missing default route (temporarily, to test)
sudo ip route add default via 192.168.1.1
Step 3: Test connectivity
ping -c 4 8.8.8.8
Step 4: If it works, make it permanent via your distribution’s persistent network configuration (Netplan, nmcli, etc.).
Comparison Table: route vs. ip route
| Aspect | route (net-tools, legacy) | ip route (iproute2, modern) |
|---|---|---|
| Maintenance status | Deprecated/legacy | Actively maintained, standard |
| Output format | Fixed columns | Flexible, more information available |
| IPv6 support | Limited | Full support |
Policy routing (ip rule) | Not supported | Supported |
| Available by default | Not always (must install net-tools) | Yes, on virtually all modern distros |
Real-World Example: Server With Two Network Interfaces
A server has eth0 (public internet, 203.0.113.5) and eth1 (internal management network, 10.10.10.5). Without careful routing configuration, outbound traffic might unexpectedly go out the wrong interface.
ip route show
default via 203.0.113.1 dev eth0
10.10.10.0/24 dev eth1 proto kernel scope link src 10.10.10.5
This confirms that traffic to the 10.10.10.0/24 management network correctly uses eth1, while everything else (including general internet traffic) correctly uses the eth0 default route — exactly the intended behavior for this dual-homed setup.
Best Practices
- Always verify a default route exists when troubleshooting “can’t reach the internet” issues — it’s one of the most common and simplest causes.
- Use
ip route get <destination>for quick, precise answers rather than manually parsing the whole table. - Make routing changes persistent through your distribution’s proper configuration system, not just runtime
ip route addcommands, or they’ll silently disappear on reboot. - Be careful with multi-homed servers (multiple NICs) — unintended routes can send traffic out the wrong interface, sometimes creating security exposure (e.g., internal traffic leaking onto a public interface).
- Document static routes added for VPNs, site-to-site links, or specific internal networks, since they’re easy to forget and hard to rediscover later.
- Use
ip ruleand multiple tables thoughtfully — policy routing is powerful but can become very confusing without clear documentation.
Troubleshooting
Problem: Can reach local network but not the internet
Check for a missing default route:
ip route show | grep default
Problem: Traffic to a specific network goes to the wrong gateway
Check for a missing or overly broad route, and verify with:
ip route get <destination-ip>
Problem: Added a route but it disappeared after reboot
Runtime ip route add commands aren’t persistent. Add the route to your distribution’s network configuration system (Netplan, nmcli, /etc/network/interfaces) instead.
Problem: Two default routes exist, causing unpredictable behavior
ip route show | grep default
If more than one default route appears, check their metric values — the lowest metric wins. Remove or adjust metrics on the unwanted one.
Problem: ip route add fails with “File exists”
A route to that destination already exists. Use ip route change instead to modify it, or delete the old one first:
sudo ip route change default via 192.168.1.1
Conclusion
The IP routing table is the kernel’s roadmap for every packet leaving a Linux system, and understanding how to read it — default routes, specific network routes, metrics, and longest prefix matching — is fundamental to diagnosing and fixing connectivity problems. With ip route show, ip route get, and a clear understanding of persistence, you can confidently inspect, modify, and troubleshoot routing behavior on any Linux system, from a simple single-homed server to a complex multi-interface, policy-routed setup.