OSPF for DevOps Engineers: Link-State Routing Without the CCNA
OSPF (Open Shortest Path First) is the most widely-deployed interior routing protocol in existence. It runs inside datacenters, campuses, ISPs, and — increasingly — Linux-based homelab routers using FRRouting (FRR). If you’ve ever wanted to run multiple routers in your lab and have them figure out routes automatically, or needed to understand what a network engineer is talking about when they mention “Area 0” and “Type 5 LSAs”, this post is for you.
This is OSPF from a DevOps-first perspective: enough theory to operate confidently, without the CCNA-depth memorization, plus a worked FRR example you can actually deploy in a homelab.
Why you’d run OSPF at all
Static routes work fine for simple networks. Add a few more routers or a few more links and static routes become:
- Fragile — a single typo on one router breaks reachability.
- Manual — every topology change needs coordinated edits.
- Stupid — they don’t route around failures.
Dynamic routing protocols fix all three. Every router advertises what it knows, learns what others know, and computes a forwarding table. When a link fails, the table updates automatically.
The two main interior options are:
- OSPF: link-state, fast convergence, well-understood, works across vendors.
- IS-IS: link-state, used heavily in ISPs, smaller market share outside of them, similar feel.
For homelab multi-router setups, multi-site VPN meshes, and small-to-medium enterprise, OSPF is the default. BGP is the other option — eBGP between leaf/spine is a datacenter pattern — but BGP is a policy protocol, not a discovery protocol, and for most “figure out reachability automatically” use cases OSPF is less configuration.
Link-state in one paragraph
In a distance-vector protocol (RIP is the classic), each router knows only what its neighbors tell it about distances to destinations. Slow convergence, loop-prone, deprecated. In a link-state protocol (OSPF, IS-IS), every router learns the entire topology — every link, every router — and independently computes shortest paths. Every router has the same topology map, so decisions are consistent and loops are structural impossibilities within an area.
The cost is that every topology change causes flooding of LSAs (Link State Advertisements) and every router re-runs SPF (Dijkstra’s Shortest Path First algorithm) to rebuild its map. For a small network this is fast; for huge networks, you partition into areas to contain flooding.
The vocabulary you need
- Router ID (RID): a 32-bit number that uniquely identifies each router. Conventionally set to the router’s loopback IP. Format looks like an IP but isn’t one — it’s a label.
- Area: a collection of routers that share a complete topology map. Area IDs also look like IPs but aren’t routable themselves.
- Backbone (Area 0): all non-backbone areas must connect to Area 0 either directly or via virtual links. Everything transits through Area 0.
- ABR (Area Border Router): a router with interfaces in multiple areas. Summarizes routes between them.
- ASBR (Autonomous System Boundary Router): a router that injects external routes (from BGP, static, connected) into OSPF.
- Adjacency: two routers synchronized on the same link. Routers form adjacencies, then exchange full topology, then stay in sync with incremental updates.
- Cost: the “distance” metric. Lower is preferred. OSPF derives cost from link bandwidth by default: cost = reference_bandwidth / link_bandwidth. Since reference defaults to 100 Mbit, every modern link maxes at cost 1 unless you raise the reference.
- Hello/Dead intervals: keepalive timing. Hello every 10 seconds, declare dead after 40 seconds by default on broadcast links.
Area design: the one decision that matters
For a homelab, put everything in Area 0. Multi-area OSPF exists for networks with enough routers that the flooding becomes painful — hundreds of routers, thousands of links. Under 50 routers, single-area is cleaner, easier to debug, and loses you nothing.
When you do need multiple areas:
- Stub area: no external routes (no Type 5 LSAs), just a default route. Good for spokes with limited RAM.
- Totally stubby: no external routes and no inter-area routes. Even smaller tables. Cisco-specific terminology but FRR supports it.
- NSSA (Not-So-Stubby Area): stub area that can itself inject external routes. Unusual but useful in specific topologies.
Don’t worry about these unless you actually need them. Single-area OSPF handles everything in a typical homelab or small office.
LSA types (skip on first read)
If you ever read OSPF output and wonder about the cryptic “Type N” references:
- Type 1 (Router LSA): advertises the router’s own links within its area.
- Type 2 (Network LSA): generated by the Designated Router on broadcast/NBMA networks, represents the multi-access segment.
- Type 3 (Summary LSA): inter-area prefix injected by an ABR.
- Type 4 (ASBR Summary): locates an ASBR from other areas.
- Type 5 (External LSA): injected by an ASBR from outside OSPF.
- Type 7 (NSSA External): external routes in NSSA areas, translated to Type 5 at the ABR.
You will see Type 1 and Type 5 most often. Don’t memorize the rest.
DR and BDR on multi-access links
On a broadcast medium (Ethernet segment with 3+ routers attached), flooding every change to every router is inefficient. OSPF elects a Designated Router (DR) and a Backup DR (BDR). Every router forms adjacency with only the DR/BDR, not with each other. Changes go to the DR, which floods them to everyone.
For point-to-point links (most modern setups, especially VPN tunnels and router-to-router crossover), there’s no DR election. Advertise the link as ip ospf network point-to-point and you skip the entire DR dance.
For broadcast segments with multiple routers (a shared LAN), accept the DR election. The router with the highest OSPF priority wins; ties break by highest Router ID. Most homelabs don’t have this scenario — each VLAN is usually terminated on a single router, so the VLAN is broadcast to hosts but has only one OSPF speaker.
FRRouting: OSPF on Linux
FRR is a fork of Quagga, itself descended from the Zebra routing suite. It implements OSPF, BGP, IS-IS, RIP, BFD, and more, running as a userspace daemon that programs Linux’s FIB via netlink. It replaces much of what Cisco/Juniper hardware does, on commodity Linux.
Install:
|
|
Enable OSPF in /etc/frr/daemons:
ospfd=yes
zebra=yes # always required
Start:
|
|
Then configure either by editing /etc/frr/frr.conf (Cisco-style config file) or by entering vtysh, FRR’s integrated shell that looks and feels like Cisco IOS.
A minimal homelab topology
Say you have three routers in a triangle, each with a /30 on two links and a /24 on a LAN:
r1
/ \
.0.0.0/30 .0.0.4/30
/ \
r2 ---- r3
.0.0.8/30
- r1 loopback: 10.99.99.1
- r2 loopback: 10.99.99.2
- r3 loopback: 10.99.99.3
- r1 LAN: 10.1.1.0/24
- r2 LAN: 10.2.2.0/24
- r3 LAN: 10.3.3.0/24
FRR config on r1:
! /etc/frr/frr.conf
frr version 10.1
frr defaults traditional
hostname r1
service integrated-vtysh-config
!
interface lo
ip address 10.99.99.1/32
!
interface eth0
ip address 10.1.1.1/24
ip ospf area 0
!
interface eth1
description link-to-r2
ip address 10.0.0.1/30
ip ospf area 0
ip ospf network point-to-point
ip ospf hello-interval 2
ip ospf dead-interval 8
!
interface eth2
description link-to-r3
ip address 10.0.0.5/30
ip ospf area 0
ip ospf network point-to-point
ip ospf hello-interval 2
ip ospf dead-interval 8
!
router ospf
ospf router-id 10.99.99.1
auto-cost reference-bandwidth 100000
passive-interface eth0
redistribute connected
!
Points worth explaining:
ip ospf area 0on interfaces is the modern FRR way to include an interface in OSPF. The older method wasnetwork 10.0.0.0/30 area 0insiderouter ospf; both work, the interface method is clearer.ip ospf network point-to-pointavoids DR election on router-to-router links. Always use this on point-to-point links.- Fast hellos (
hello-interval 2,dead-interval 8) converge in ~8 seconds instead of the default 40. Pair with BFD for sub-second. passive-interface eth0means “advertise this network into OSPF, but don’t send OSPF Hellos out of it”. Apply to LAN interfaces so you don’t try to form adjacencies with end hosts.auto-cost reference-bandwidth 100000sets the reference to 100 Gbps so that 10 GbE has cost 10 and 1 GbE has cost 100 — actual differentiation. Default of 100 Mbps makes every modern link cost 1, erasing bandwidth differentiation.redistribute connectedadvertises local directly-connected networks into OSPF. Useful if you didn’t explicitlyip ospf area 0every interface. For the LAN interfaces here, area 0 is already configured, so this is redundant but harmless.
Repeat with the obvious modifications on r2 and r3. Within a minute or two, every router will know every LAN prefix.
Verifying the cluster
In vtysh:
r1# show ip ospf neighbor
Neighbor ID Pri State Up Time Dead Time Address Interface
10.99.99.2 1 Full/- 00:02:14 7.128s 10.0.0.2 eth1:10.0.0.1
10.99.99.3 1 Full/- 00:02:14 7.451s 10.0.0.6 eth2:10.0.0.5
Full/- is the happy state: fully adjacent, no DR role (because point-to-point). Full/DR, Full/BDR, or Full/DROther on broadcast links also mean adjacency is healthy. States like Init, 2-Way, or ExStart during troubleshooting indicate where synchronization is stuck.
r1# show ip ospf database
OSPF Router with ID (10.99.99.1)
Router Link States (Area 0.0.0.0)
Link ID ADV Router Age Seq# CkSum Link count
10.99.99.1 10.99.99.1 123 0x80000005 0xa1b2 4
10.99.99.2 10.99.99.2 121 0x80000004 0xb3c4 4
10.99.99.3 10.99.99.3 122 0x80000003 0xc4d5 4
The LSA database. Every router has the same database in the same area — that’s the definition of link-state.
r1# show ip route ospf
O>* 10.2.2.0/24 [110/20] via 10.0.0.2, eth1, 00:01:45
O>* 10.3.3.0/24 [110/20] via 10.0.0.6, eth2, 00:01:45
O>* 10.99.99.2/32 [110/10] via 10.0.0.2, eth1, 00:02:03
O>* 10.99.99.3/32 [110/10] via 10.0.0.6, eth2, 00:02:03
The [110/X] is [administrative distance / metric]. 110 is the default OSPF AD; 20 is the summed cost to reach that destination.
Convergence and BFD
Default OSPF with default timers detects a link failure in 40 seconds. Unacceptable for anything interactive. Two ways to tighten:
Faster OSPF timers
interface eth1
ip ospf hello-interval 1
ip ospf dead-interval 4
Sub-4-second convergence. Works on most links but adds control-plane chatter.
BFD (Bidirectional Forwarding Detection)
A separate micro-protocol dedicated to failure detection. OSPF registers its adjacencies with BFD; BFD exchanges high-rate lightweight Hellos and tells OSPF within ~150 ms when a link drops.
In FRR:
interface eth1
ip ospf bfd
!
bfd
peer 10.0.0.2
detect-multiplier 3
receive-interval 50
transmit-interval 50
exit
!
This gives you ~150 ms failure detection without running OSPF itself at sub-second timers. The standard pattern for high-availability OSPF deployments.
Authentication
OSPF supports MD5 and (on newer FRR) HMAC-SHA-256 authentication on a per-interface basis. Set on each end of an adjacency:
interface eth1
ip ospf authentication message-digest
ip ospf message-digest-key 1 md5 my-shared-secret
Enable per-interface rather than per-area — makes mismatches easier to debug. If one end has authentication and the other doesn’t, the adjacency silently fails. Check with debug ospf packet all in vtysh temporarily.
For modern deployments, use HMAC-SHA-256 if both sides support it. MD5 is acceptable for homelab use but no longer considered strong authentication.
Redistribution: getting non-OSPF routes into OSPF
What if your router also has a BGP session (say, to a Cloudflare Tunnel or a VPS provider) or static routes? To share those routes with the rest of OSPF:
router ospf
redistribute static metric 20 metric-type 2
redistribute bgp metric 50
default-information originate always
metric-type 2(the default): external cost is not added to internal cost. Good for “this is an external route, its advertised cost is final”.metric-type 1: external cost adds to internal cost. Good when you actually want routers close to the ASBR to prefer it.default-information originate always: inject a default route (0.0.0.0/0) into OSPF as long as this router is running, whether or not it has a default route itself. Often what you want at the WAN edge.
Redistribution is where OSPF mistakes compound. Be precise. Redistribute by route-map filter rather than redistributing everything:
ip prefix-list MY-STATICS seq 5 permit 192.168.0.0/16 le 24
!
route-map STATIC-TO-OSPF permit 10
match ip address prefix-list MY-STATICS
!
router ospf
redistribute static route-map STATIC-TO-OSPF
Otherwise, a single ip route 0.0.0.0/0 null0 added later can inadvertently announce a default route to your entire OSPF cloud.
Common mistakes
- Mismatched area numbers. OSPF won’t form adjacency if both sides disagree on area. Error shows as neighbor stuck in
InitorExStart. - Mismatched hello/dead intervals. Same failure mode.
- Broadcast network with multiple OSPF speakers and no DR priority tuning. The DR election picks whichever router has the highest RID — often not the one you want. Set
ip ospf priorityexplicitly on routers that should be DR. - Forgetting
passive-interfaceon LAN-facing interfaces. OSPF hellos leak onto user VLANs, looking weird in packet captures, occasionally triggering security monitoring. - Default reference bandwidth (100 Mbps). Every link costs 1. No bandwidth differentiation. Set to at least 100 Gbps reference on modern networks.
- Redistributing everything. You’ll inject a loopback summary, a management static route, and an IPMI prefix you never intended to advertise. Use route-maps.
- Not setting a Router ID explicitly. FRR picks one from interface IPs. If those change, the RID can change, and the router appears as a “new” router to neighbors — causing re-convergence and log noise.
- Running without BFD. Default timers are too slow for modern applications. Either tune OSPF timers or deploy BFD.
- Multi-area without need. Single-area is simpler until you’re clearly at scale.
- Unauthenticated OSPF on untrusted links. Any host on the segment can inject bogus LSAs. Use authentication on anything crossing a network you don’t fully trust.
Debugging checklist
When an OSPF adjacency won’t come up, in order:
show ip ospf interface eth1— is OSPF even running on this interface?ping 10.0.0.2from r1 — basic L3 reachability.tcpdump -ni eth1 proto 89— OSPF protocol number is 89. Are Hellos flowing?- Compare hello/dead intervals on both ends.
- Compare area IDs.
- Compare authentication settings.
show ip ospf neighbor detail— if stuck in a state, the state tells you what’s wrong.
If you’re seeing adjacency but not routes:
show ip ospf database— does the database list the expected LSAs?show ip route ospf— are routes installed but not used? Check administrative distance conflicts with static/connected.
When OSPF is the right answer
- Multiple routers in a homelab you want to converge automatically.
- Multi-site VPN mesh (WireGuard tunnels between sites, OSPF over the tunnels).
- Small business / campus with a handful of buildings.
- Datacenter leaf/spine — though this is increasingly BGP-only territory.
When to pick something else
- Single router: no routing protocol needed.
- Two routers with a single link: static routes are fine.
- Public-internet-facing ASes: BGP.
- Huge datacenters with 1000+ switches: BGP EVPN or IS-IS is more typical.
- You just want reachability across a VPN mesh with minimal config: consider Tailscale or a similar overlay, which handles routing internally.
A minimal recipe you can memorize
router ospf
ospf router-id <loopback-IP>
auto-cost reference-bandwidth 100000
!
interface <WAN-link>
ip ospf area 0
ip ospf network point-to-point
ip ospf bfd
!
interface <LAN>
ip ospf area 0
ip ospf passive
Plus loopback on lo, plus BFD block, plus authentication on untrusted links. That’s a functional OSPF router. Everything else is tuning.
OSPF has been around since 1989, the core protocol is stable, the FRR implementation is mature, and the mental model — “every router has the same map” — is genuinely simple once it clicks. For a homelab engineer adding more routers than static routes can sanely handle, or a DevOps engineer building multi-site infrastructure over VPN tunnels, a few hours learning OSPF returns value for years.
Comments