TTR Networks

Intermediate · FRR on Debian: a BGP and OSPF router · section 5 of 10

Into the kernel, and OSPF inside

✓ Lab-tested 4 November 2026 on Debian 13.7, FRR 10.3 (Debian package), FRR 10.2.1 (upstreams), nftables 1.1.3, systemd 257

FRR's routing decisions only matter once they're in the kernel, because the kernel forwards the packets. Zebra installs every selected route, and marks each with where it came from: ip route shows proto bgp for the default and the web server's network, proto ospf for the server LAN and the core's loopback, proto static for the blackhole, and proto kernel for the directly connected links. ip route get answers the everyday question "where would a packet to this address go?" with the kernel's actual answer.

So ordinary Linux tools work on an FRR router: ip route, ping, traceroute and tcpdump see exactly what the routing protocols set up.

OSPF towards the core

Inside the office, edge and core are OSPF neighbours on a point-to-point link (no designated router election, so it reaches Full quickly). The core announces its server LAN; edge announces a default route because it has one from BGP. On core, that default shows as an OSPF route via edge.

The result, end to end: the server's traceroute to the web server goes through the core, edge, and ISP 1, and the web server's log shows the request arriving from the server's own address, 203.0.113.10. No NAT anywhere: the office's addresses are its own, and the internet routes them back by BGP.

The kernel's routing table on edge (real output)
root@edge:~# ip route
default nhid 13 via 192.0.2.0 dev eth0 proto bgp metric 20 
10.0.0.0/30 dev eth2 proto kernel scope link src 10.0.0.1 
10.255.0.2 nhid 15 via 10.0.0.2 dev eth2 proto ospf metric 20 
192.0.2.0/31 dev eth0 proto kernel scope link src 192.0.2.1 
192.0.2.2/31 dev eth1 proto kernel scope link src 192.0.2.3 
198.51.100.0/24 nhid 13 via 192.0.2.0 dev eth0 proto bgp metric 20 
203.0.113.0/26 nhid 15 via 10.0.0.2 dev eth2 proto ospf metric 20 
blackhole 203.0.113.0/24 proto static metric 20 

root@edge:~# ip route show proto bgp; ip route get 198.51.100.80
default nhid 13 via 192.0.2.0 dev eth0 metric 20 
198.51.100.0/24 nhid 13 via 192.0.2.0 dev eth0 metric 20 
198.51.100.80 via 192.0.2.0 dev eth0 src 192.0.2.1 uid 0 
    cache 
  • Line 2: The default route from BGP, via ISP 1.
  • Line 4: The core's loopback, from OSPF.
  • Line 8: The server LAN, from OSPF.
  • Line 9: The blackhole for the office /24, from staticd.
  • Lines 11–15: Only the BGP routes, and the kernel's answer for one address.
OSPF and the path to the internet (real output)
root@edge:~# vtysh -c "show ip ospf neighbor"

Neighbor ID     Pri State           Up Time         Dead Time Address         Interface                        RXmtL RqstL DBsmL
10.255.0.2        1 Full/-          6.406s            33.592s 10.0.0.2        eth2:10.0.0.1                        0     0     0


root@core:~# vtysh -c "show ip route ospf"
Codes: K - kernel route, C - connected, L - local, S - static,
       R - RIP, O - OSPF, I - IS-IS, B - BGP, E - EIGRP, N - NHRP,
       T - Table, v - VNC, V - VNC-Direct, A - Babel, F - PBR,
       f - OpenFabric, t - Table-Direct,
       > - selected route, * - FIB route, q - queued, r - rejected, b - backup
       t - trapped, o - offload failure

O>* 0.0.0.0/0 [110/10] via 10.0.0.1, eth0, weight 1, 00:00:01
O   10.0.0.0/30 [110/10] is directly connected, eth0, weight 1, 00:00:37
O   10.255.0.2/32 [110/0] is directly connected, lo, weight 1, 00:00:37
O   203.0.113.0/26 [110/10] is directly connected, eth1, weight 1, 00:00:37

root@server:~# traceroute -n -q 1 -w 1 198.51.100.80; curl -s -o /dev/null -w "%{http_code}\n" http://198.51.100.80/
traceroute to 198.51.100.80 (198.51.100.80), 30 hops max, 60 byte packets
 1  203.0.113.1  0.088 ms
 2  10.0.0.1  0.032 ms
 3  192.0.2.0  0.025 ms
 4  198.51.100.80  0.051 ms
200

root@web:~# tail -1 /tmp/web.log
203.0.113.10 - - [04/Nov/2026 22:02:38] "GET / HTTP/1.1" 200 -
  • Line 4: The core is a full neighbour on the point-to-point link.
  • Line 15: On core: a default route via edge, an external OSPF route.
  • Lines 20–26: From the server: core, edge, ISP 1, the web server. The page loads.
  • Lines 28–29: The web server sees the server's own address: no NAT.

Check your understanding

ip route on edge shows proto bgp on the default route. What put it there?

  • Zebra, FRR's daemon that installs FRR's chosen routes in the kernel.
  • systemd-networkd, from the .network files.
  • bgpd writes directly into the kernel.
  • The ISP pushed it with DHCP.