OSPF Basics
OSPF Basics
OSPF is a link-state IGP: routers flood topology information within an area, run SPF, and install routes. This chapter builds a single-area triangle on FRR in Containerlab—the highest-ROI dynamic routing lab in the book.
Learning goals
By the end of this chapter you can:
- Contrast link-state vs distance-vector at a model level
- Bring up OSPF adjacencies on FRR and read neighbor states
- Advertise loopbacks and LANs into OSPF
- Predict reconvergence when a link costs out or dies
- Verify RIB/FIB and traceroute paths
Concepts
Distance-vector vs link-state
| Distance-vector (idea) | Link-state (OSPF) | |
|---|---|---|
| What is shared | Vectors to prefixes | Topology (LSAs) |
| Loop risk | Needs careful rules | SPF on common map |
| Visibility | Partial | Area-wide topology view |
| Example | Classic RIP | OSPF, IS-IS |
OSPF adjacency state machine (short)
Down → Init → 2-Way → ExStart → Exchange → Loading → Full
For learning: Full with expected neighbors on each transit link. Stuck states often mean MTU mismatch, authentication, layer-2, or network type mismatch.
Areas
Single-area (0) is enough here. Multi-area adds ABR summarization later in your journey; do not rush.
Cost
OSPF picks lowest cost path. Cost often derives from reference bandwidth / interface bandwidth. In FRR you can set ip ospf cost on interfaces for drills.
Network types (awareness)
Point-to-point vs broadcast affects adjacency count and DR/BDR. P2P links in labs are simple; Ethernet multipoint may elect DR.
Addressing plan
| Node | lo | eth1 (to peer) | eth2 | LAN eth3 |
|---|---|---|---|---|
| r1 | 1.1.1.1/32 | 10.0.12.1/24 (r2) | 10.0.13.1/24 (r3) | 192.168.1.1/24 |
| r2 | 2.2.2.2/32 | 10.0.12.2/24 | 10.0.23.1/24 | 192.168.2.1/24 |
| r3 | 3.3.3.3/32 | 10.0.23.2/24 | 10.0.13.2/24 | optional |
Hosts: 192.168.1.10, 192.168.2.10 with defaults to local r.
Method A: The Fast Netlab Workflow (Recommended)
Using Netlab (with provider: clab), you declare only the protocol intent and node interconnections. Netlab automatically plans IP subnets, allocates loopbacks, writes the FRR configurations, and stands up the Containerlab environment:
# topology.yml
---
provider: clab
defaults.device: frr
module: [ ospf ]
nodes:
r1:
r2:
r3:
h1:
device: linux
h2:
device: linux
links:
- r1:
r2:
- r2:
r3:
- r3:
r1:
- h1:
r1:
- h2:
r2:
validate:
ospf_neighbors:
description: "Verify all routers reach Full OSPF adjacency with 2 neighbors"
nodes: [ r1, r2, r3 ]
plugin: ospf_neighbor(count=2, state='Full')
end_to_end_ping:
description: "Verify end-to-end connectivity between endpoints h1 and h2"
nodes: [ h1 ]
exec:
ping_h2: ping -c 3 10.0.0.5Deploy and Validate with Netlab
# Compile configurations and bring up the Containerlab environment
netlab up
# Run automated OSPF adjacency and ping assertions
netlab validate
# Connect to any router CLI directly without memorizing container IDs
netlab connect r1Method B: Raw Containerlab & Manual FRR Configurations
If you are inspecting low-level container plumbing or deploying on a system without Python/Netlab, you can author the Containerlab topology and FRR configurations by hand.
Topology YAML (ospf-tri.clab.yml)
name: ospf-tri
topology:
nodes:
r1:
kind: linux
image: quay.io/frrouting/frr:10.2.1
binds:
- ./config/r1:/etc/frr
r2:
kind: linux
image: quay.io/frrouting/frr:10.2.1
binds:
- ./config/r2:/etc/frr
r3:
kind: linux
image: quay.io/frrouting/frr:10.2.1
binds:
- ./config/r3:/etc/frr
h1:
kind: linux
image: alpine:3.20
exec:
- apk add --no-cache iproute2 iputils
- ip addr add 192.168.1.10/24 dev eth1 && ip link set eth1 up
- ip route add default via 192.168.1.1
h2:
kind: linux
image: alpine:3.20
exec:
- apk add --no-cache iproute2 iputils
- ip addr add 192.168.2.10/24 dev eth1 && ip link set eth1 up
- ip route add default via 192.168.2.1
links:
- endpoints: ["r1:eth1", "r2:eth1"]
- endpoints: ["r2:eth2", "r3:eth1"]
- endpoints: ["r3:eth2", "r1:eth2"]
- endpoints: ["h1:eth1", "r1:eth3"]
- endpoints: ["h2:eth1", "r2:eth3"]FRR Daemons File (config/r1/daemons)
daemons file must enable:
zebra=yes
ospfd=yes
FRR OSPF Config (config/r1/frr.conf)
frr version 10.2.1
frr defaults traditional
hostname r1
!
interface lo
ip address 1.1.1.1/32
ip ospf area 0
!
interface eth1
ip address 10.0.12.1/24
ip ospf area 0
!
interface eth2
ip address 10.0.13.1/24
ip ospf area 0
!
interface eth3
ip address 192.168.1.1/24
ip ospf area 0
!
router ospf
ospf router-id 1.1.1.1
!
line vty
Advertise by putting interfaces in area 0 (common FRR style). Equivalent network statements exist; be consistent across nodes.
r2/r3: unique router-ids 2.2.2.2, 3.3.3.3; matching interface IPs; LANs in OSPF so remote LANs appear in RIB.
Deploy and Verify Manually
sudo containerlab deploy -t ospf-tri.clab.yml
docker exec clab-ospf-tri-r1 vtysh -c 'show ip ospf neighbor'
docker exec clab-ospf-tri-r1 vtysh -c 'show ip ospf route'
docker exec clab-ospf-tri-r1 vtysh -c 'show ip route'
docker exec clab-ospf-tri-r1 vtysh -c 'show ip ospf database'
docker exec clab-ospf-tri-h1 ping -c 3 192.168.2.10
docker exec clab-ospf-tri-h1 traceroute -n 192.168.2.10Predict
- Each router: 2 neighbors Full (triangle)
- RIB contains remote LAN and loopbacks via OSPF
- Path prefers lower cost sides
Neighbor not Full?
Check:
ip link
ip addr
# MTU match
vtysh -c 'show ip ospf interface'
# timers, area mismatch, passive interfaces accidentalCost change drill
# on r1 eth1 increase cost so traffic prefers other way
docker exec -it clab-ospf-tri-r1 vtysh
configure terminal
interface eth1
ip ospf cost 1000
end
write
# promote back to frr.conf in git after experimentdocker exec clab-ospf-tri-r1 vtysh -c 'show ip route 192.168.2.0/24'
docker exec clab-ospf-tri-h1 traceroute -n 192.168.2.10Predict: path shifts if alternate exists with lower total cost.
Link failure drill
docker exec clab-ospf-tri-r1 ip link set eth1 down
sleep 5
docker exec clab-ospf-tri-r1 vtysh -c 'show ip ospf neighbor'
docker exec clab-ospf-tri-r1 vtysh -c 'show ip route 192.168.2.0'
docker exec clab-ospf-tri-h1 ping -c 20 192.168.2.10
docker exec clab-ospf-tri-r1 ip link set eth1 upJournal reconvergence behavior and whether ping loss matches expectations.
Passive interfaces / LAN notes
Sometimes you put a LAN in OSPF for advertisement but want no adjacency on user ports:
router ospf
passive-interface eth3
Loopbacks are often passive. Transit links remain non-passive.
Verification checklist
| Check | Command |
|---|---|
| Neighbors | show ip ospf neighbor |
| Database | show ip ospf database |
| OSPF RIB | show ip ospf route |
| Kernel/FIB | show ip route / ip route |
| Data plane | ping / traceroute |
verify.sh
#!/usr/bin/env bash
set -euo pipefail
fail() { echo "FAIL: $*" >&2; exit 1; }
docker exec clab-ospf-tri-r1 vtysh -c 'show ip ospf neighbor' | grep -q Full \
|| fail "no Full neighbor on r1"
docker exec clab-ospf-tri-h1 ping -c 2 -W 1 192.168.2.10 || fail "h1->h2"
docker exec clab-ospf-tri-h2 ping -c 2 -W 1 192.168.1.10 || fail "h2->h1"
echo OKCommon mistakes
| Mistake | Symptom |
|---|---|
| ospfd not enabled | no neighbors |
| Mismatched areas | stuck |
| Different IP subnets on link | no adjacency |
| MTU mismatch | ExStart/Exchange issues |
| router-id collisions | confusing DB |
| Forgetting LAN in OSPF | hosts missing remote routes |
Beyond this chapter
- Multi-area + summarization at ABR
- Stub/NSSA ideas
- OSPFv3 for IPv6
- Authentication
Master single-area Full adjacencies and failover first.
Summary
- OSPF floods link state, computes SPF, installs routes
- FRR triangle lab is the default IGP workout
- Always verify neighbors Full and FIB and ping
- Cost and link-down drills teach reconvergence
- Keep configs in binds; promote experimental vtysh changes to git
Next: BGP basics—sessions, path selection process, and a dual-router lab without vendor exam noise.