OSPF Basics

Updated

September 4, 2026

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

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.

Topology YAML

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

daemons file must enable:

zebra=yes
ospfd=yes

FRR OSPF config (r1 example)

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

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.10

Predict

  • 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 accidental

Cost 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 experiment
docker 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.10

Predict: path shifts if alternate exists with lower total cost.

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 OK

Common 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.