VyOSII · Routing FundamentalsRouting primitives
Routing tables — what an entry actually is
What you'll learn
- Describe the fields of a routing-table entry as the kernel sees them
- Explain the difference between the kernel FIB and the FRRouting RIB
- Read `ip route` output and identify the source, metric, and next-hop of every entry
- Recognise the routing-table failure modes that surface as routing incidents
Prerequisites
Verified against VyOS 1.5.x LTS (circinus) · VyOS 1.4.x (sagitta) — legacy · FRRouting 10.x (VyOS 1.5) · Linux kernel 6.6 LTS (VyOS 1.5 base) · strongSwan 5.9.x (IPsec) · WireGuard 1.0.x (kernel module + userspace tooling) · 2026-08-15
The routing table is the kernel’s answer to “where does this packet go?”. Every forwarding decision the kernel makes reads one entry from this table and follows the next-hop. The routing engineer who does not understand the table — every field, every state, every interaction with the dynamic routing daemon — cannot debug routing incidents because every debugging command they run returns a row of the table and they cannot read it.
This lesson is the operator’s foundation in the routing table: the fields, the sources, the difference between the kernel FIB and the FRRouting RIB, and the failure modes that surface as routing incidents.
What is in a routing-table entry
A routing-table entry in the Linux kernel has seven fields. The operator who knows all seven by heart can read any output and know exactly what the kernel will do with a packet.
| Field | Meaning | Example |
|---|---|---|
| Destination | The prefix this entry matches | 10.0.0.0/24 |
| Source | Where this entry came from | connected, static, ospf, bgp |
| Scope | The administrative scope of the entry | link, global |
| Protocol | The routing protocol that contributed this entry | kernel, boot, static, ospf, bgp |
| Type | Unicast / local / broadcast / throw / unreachable / prohibit / blackhole | unicast |
| Next hop | The IP or interface to forward to | 192.0.2.1 dev eth0 |
| Metric | The protocol’s distance value | 20 (BGP) or 110 (OSPF) |
| Flags | Per-entry flags | onlink |
The ip route show output uses a column-aligned format. A
real-world example:
default via 192.0.2.1 dev eth0 proto static metric 100
192.0.2.0/24 dev eth0 proto kernel scope link src 192.0.2.50
10.0.0.0/24 via 192.0.2.10 dev eth0 proto bgp metric 20
blackhole 10.10.0.0/16
unreachable 192.0.2.128/25
The four lines show four different entry types:
- A default route via
192.0.2.1, statically configured, with metric 100. - A connected route for
192.0.2.0/24(added by the kernel when the interface came up). - A BGP-learned route to
10.0.0.0/24via192.0.2.10. - A blackhole route to
10.10.0.0/16(discarded silently). - An unreachable route to
192.0.2.128/25(rejected with ICMP unreachable).
The kernel FIB vs the FRRouting RIB
VyOS has two routing tables, in two different places:
- The kernel FIB (
ip route show) — what the kernel forwards packets based on. Plain text, easy to read, but cannot be modified directly by routing protocols (they install via netlink). - The FRRouting RIB (
vtysh show ip route) — what FRRouting knows about. More information, including the source protocol, the metric, the best-path selection criteria, and the time the route was last updated.
The two are related but not identical. FRRouting learns routes from BGP, OSPF, IS-IS, RIP, and the kernel; it runs best-path selection on each protocol’s candidates and installs the “best” route into the kernel FIB via netlink. The kernel FIB is a strict subset of the FRRouting RIB plus the kernel’s own entries (connected, static).
flowchart LR
subgraph "FRRouting RIB"
B1["BGP table"]
O1["OSPF LSDB"]
S1["Static config"]
Z["Zebra daemon"]
end
subgraph "Kernel FIB"
K1["Connected routes\n(admin distance 0)"]
K2["Static routes\n(admin distance 1)"]
K3["Best routes from each protocol"]
K4["Blackhole / unreachable"]
end
B1 --> Z
O1 --> Z
S1 --> Z
Z -->|"netlink"| K3
K1 --> K2
K2 --> K3
K3 --> K4
A route that exists in FRRouting but not in the kernel means FRRouting chose not to install it. Most commonly, the route is in FRRouting’s BGP table but FRRouting’s best-path algorithm rejected it (or the BGP inbound filter rejected it before best-path ran).
A route that exists in the kernel but not in FRRouting is the opposite direction: a connected, static, or kernel-level entry that the routing engine knows about but did not originate.
Administrative distance and protocol preference
The Linux kernel assigns each route an administrative distance (AD) based on its source. The lower AD wins when two routes cover the same prefix.
| Source | AD | Notes |
|---|---|---|
| Connected | 0 | Cannot be overridden |
| Kernel | 0 | Auto-installed |
| Static | 1 | Manual configuration |
| DHCP | 1 | Routes installed by DHCP |
| OSPF | 110 | Link-state IGP |
| IS-IS | 115 | Link-state IGP |
| RIP | 120 | Distance-vector IGP |
| eBGP | 20 | External BGP |
| iBGP | 200 | Internal BGP |
The AD is the kernel’s answer to “which source do I trust more for this prefix?”. A static route (AD 1) always beats an OSPF route (AD 110) for the same prefix. iBGP (AD 200) always loses to eBGP (AD 20) for the same prefix.
The operator who sees “the route I configured is not being
used” is almost always losing to AD. The fix is either removing
the competing entry, changing the AD (rare, with the metric
keyword), or accepting that the AD is correct and the dynamic
route is the right answer.
Reading the routing table in production
The diagnostic commands:
# Full kernel FIB
ip route show
# Specific prefix
ip route show 10.0.0.0/24
# What the kernel would do with a packet
ip route get 8.8.8.8
# All routing tables
ip route show table all
# A specific table
ip route show table main
ip route show table local
# Detailed
ip -d route show
# Counters (on Linux 4.4+)
ip -s route show
ip route get 8.8.8.8 is the most useful single command. It
performs the lookup the kernel would do for a packet and
returns the next-hop, the egress interface, and the route that
won. If the answer is RTNETLINK answers: Network is unreachable, the kernel has no route to that destination at
all — the routing table is the problem. If the answer is a
route with a next-hop, the kernel has a route and the issue is
elsewhere (Layer 2, firewall, NAT).
flowchart TB
P["Packet to 8.8.8.8"] --> L1{"Lookup in\nlocal table"}
L1 -->|match| L1a["Return local\n(deliver locally)"]
L1 -->|no match| L2{"Lookup in\nmain table"}
L2 -->|match| L2a["Return next-hop + dev"]
L2 -->|no match| L3["Network is unreachable"]
The lookup walks the tables in a defined order. On a VyOS
router the default tables are local (kernel-installed routes
for the router’s own IPs) and main (everything else).
Failure modes
Routes that disappear
A route that was in the FIB and now is not:
- The interface went down (
ip link show dev eth0to verify). - The dynamic protocol withdrew the route (
vtysh show ip bgp ...orvtysh show ip ospf neighborto verify). - The static route was removed by an administrator or by an automation script.
- A routing-protocol session reset (
show logto verify).
Routes that conflict
Two routes to the same prefix with different next-hops:
- The lower-AD route wins. The other route is installed in FRRouting’s RIB but not in the kernel FIB.
- Use
ip route show <prefix>to see which routes exist for the prefix. The lowest AD wins.
Routes that point to the wrong place
A route that points to a next-hop that no longer exists or is unreachable:
ip route get <dst>shows the route’s next-hop.ip neigh show <next-hop>shows the kernel’s neighbour state for that next-hop.- If the neighbour is
FAILED, the route is installed but the kernel cannot forward — the issue is Layer 2, not routing.
Routes that nobody installed
A prefix the operator expected to be in the routing table that is not there:
- The dynamic protocol did not advertise it.
- The inbound filter rejected it.
- The best-path algorithm chose a different route.
- The redistribution filter removed it.
- Check
vtysh show ip bgp <prefix>andvtysh show ip ospf database.
Validation
The validation sequence for “the route is wrong”:
- The route exists:
ip route show <prefix>returns the expected entry. - The route is the best:
vtysh show ip bgp <prefix>(if BGP) shows the route with*>(best path). - The next-hop is correct:
ip route get <dst>returns the expected next-hop and egress interface. - The next-hop is reachable:
ip neigh show <next-hop>returnsREACHABLEorSTALE. - The interface is up:
ip link show dev <ifname>returnsstate UP. - The path is healthy:
traceroute <dst>ortracepath <dst>confirms the path works end-to-end. - The firewall allows it:
conntrack -L -s <src> -d <dst>shows the connection being tracked. - The application works: actual end-to-end test from a host.
If every check passes, the routing table is fine. If traffic still does not flow, the issue is not routing.
Cross-course references
- The Linux course’s
V-Linux-NetConfigcovers the kernel routing table from the host perspective. - The lesson on administrative distance in this part covers the AD ordering in depth.
- The lessons on BGP, OSPF, and redistribution in later parts cover how dynamic routes enter and exit the table.
Quiz
Knowledge check · 4 questions
Q1. The operator configures a static default route via 192.0.2.1. The kernel shows the default route via 198.51.100.1 instead. What is the most likely cause?
The operator runs `set protocols static route 0.0.0.0/0 next-hop 192.0.2.1`. After `commit`, `ip route show 0.0.0.0/0` shows `default via 198.51.100.1 dev eth1`. The operator expected 192.0.2.1.
Q2. A route is in FRRouting's BGP table but not in the kernel FIB. What is the most likely cause?
Q3. A connected route wins over a static route to the same prefix, whatever distance the static route is given.
Q4. A route to 10.0.0.0/24 was in the kernel FIB this morning but is now missing. What are the most likely causes?
10.0.0.0/24 is learned via OSPF from a neighbour router. The OSPF session was up this morning. The route is no longer in `ip route show` at 14:00.
Passing score: 75%. Answers are checked in this browser.
Production discipline
The routing table is the routing engineer’s primary instrument. Every debugging session begins with reading the table. Every configuration change ends with reading the table to confirm the change is effective. Every incident report asks “is the route there?” before asking “is the route correct?”.
Plan the routing table once. Document the AD expectations. Validate the AD conflicts. Then routing either works or fails predictably.