Intra-Domain Routing with OSPF

Imagine an internet service provider with 5,000 routers spanning a country. All belong to one administrative organization. Every router must learn a consistent, loop-free view of the network. This ensures packets from Boston to Los Angeles take a sensible path. That must hold even as links fail and come back at 3 a.m. Running Link-State Routing and Dijkstra's Algorithm naively across all 5,000 routers works in principle. But every router floods link-state updates to every other router. And every router recomputes shortest paths over a 5,000-node graph. This becomes a serious computational and bandwidth burden as the network grows. Open Shortest Path First (OSPF) is the industry-standard solution to this problem. It works inside a single administrative network, called an Autonomous System (AS). OSPF takes the clean mathematical idea of link-state routing. Each router floods its local connectivity. Then it runs Dijkstra's algorithm on the resulting global map. OSPF wraps this idea in a hierarchical, robust, and highly configurable protocol. It suits real, messy networks with thousands of routers, multiple vendors, and strict operational requirements. The central engineering move in OSPF is area partitioning. The AS is split into a backbone area and a collection of non-backbone areas. Most routers only need the topology of their own area, not the whole AS. This bounds the cost of flooding and the cost of Dijkstra's algorithm to a manageable size. The price is extra administrative structure (area border routers, area design rules). Routing also becomes slightly less precise at scale. OSPF also refines the raw link-state model with a rich system. It uses Link-State Advertisement (LSA) types, a Designated Router mechanism to tame flooding on broadcast media. It also adds metrics, authentication, and equal-cost multipath support. These features make it deployable in production. This article gives the precise mechanics of OSPF. It covers how routers discover neighbors and synchronize databases. It explains what each LSA type carries. It shows how areas interact through Area Border Routers. It describes how the final routing table is computed. We derive the time and space costs honestly. We trace a worked example with actual LSA exchanges and a Dijkstra run. We catalog the classic operational pitfalls. We close by placing OSPF next to Inter-Domain Routing with BGP. That is the protocol that takes over once packets must cross organizational boundaries.

Key takeaways
  • OSPF is a link-state protocol for a single Autonomous System (intra-domain routing), using Dijkstra's algorithm on a synchronized topology database.
  • Routers flood Link-State Advertisements (LSAs) reliably; every router in an area ends up with an identical Link-State Database (LSDB).
  • Areas create a two-level hierarchy: Area 0 (the backbone) plus non-backbone areas, connected via Area Border Routers (ABRs), bounding flood and computation cost.
  • A Designated Router (DR) and Backup DR reduce flooding overhead on broadcast/multi-access networks from O(n²) adjacencies to O(n).
  • Correctness rests on the synchronized-database invariant: if every router in an area computes shortest paths over the identical graph, per-node correctness of Dijkstra gives loop-free, consistent forwarding.
  • OSPF trades some routing precision (summarized inter-area routes, no full topology visibility) for scalability, faster convergence, and administrative control — a hierarchy pattern reused throughout distributed systems.
The core insight

OSPF scales link-state routing to thousands of routers by synchronizing an identical topology database within bounded areas, then running Dijkstra locally in each router.

The idea

OSPF is a link-state, intra-domain (interior gateway) routing protocol. "Intra-domain" means it routes traffic within a single Autonomous System (AS) — a network under one administrative authority, such as a university campus or an ISP's core — as opposed to inter-domain protocols like BGP that route between autonomous systems. The defining property of link-state protocols, inherited from Link-State Routing and Dijkstra's Algorithm, is that every router obtains a complete, identical map of the network's topology (the Link-State Database, LSDB) and independently computes shortest paths using Dijkstra's algorithm, rather than trusting neighbors' distance summaries as in distance-vector protocols. OSPF's key structural refinement over "plain" link-state routing is hierarchical area design. The AS is divided into areas, each identified by a 32-bit Area ID. One area, Area 0, is designated the backbone area and must be contiguous; every other (non-backbone) area must connect to the backbone, directly or via a virtual link. Routers with all interfaces in one area are internal routers; routers with interfaces in multiple areas are Area Border Routers (ABRs); routers connecting the AS to the outside world (e.g., to BGP) are Autonomous System Boundary Routers (ASBRs). Flooding of detailed topology information is confined to within an area — a router only needs the full LSDB for its own area, plus summarized reachability information for other areas, relayed by ABRs. This is the mechanism that keeps Dijkstra's per-router cost bounded even as the AS-wide router count grows into the thousands. OSPF represents topology as Link-State Advertisements (LSAs), each describing a piece of the graph: a router's links and their costs, a network's attached routers, or a summarized route to another area. LSAs are flooded reliably (with acknowledgments, sequence numbers, and aging) so that every router in the relevant area converges on an identical LSDB — this is the synchronized-database invariant that underlies OSPF's correctness. On broadcast-capable links (like Ethernet segments) with many attached routers, OSPF elects a Designated Router (DR) and Backup Designated Router (BDR) to avoid a full mesh of adjacencies, cutting flooding traffic from quadratic to linear in the number of attached routers.

How it works

1. Neighbor discovery — the Hello protocol. Each OSPF router periodically (default every 10 seconds on broadcast links, 30 seconds on non-broadcast) multicasts a Hello packet out each interface to the reserved multicast address `224.0.0.5` (AllSPFRouters). A Hello lists the sender's Router ID, area ID, Hello/Dead intervals, and the Router IDs of neighbors it has already heard from. Two routers become neighbors once they see each other listed (bidirectional communication confirmed), provided their Hello/Dead intervals, area ID, and authentication match.

2. Adjacency formation and the Designated Router. On point-to-point links, neighbors always become fully adjacent (they exchange full databases). On broadcast/multi-access networks (e.g., a shared Ethernet segment with `n` routers), forming `n(n-1)/2` full adjacencies would mean each of the `n` routers floods every LSA update to `n-1` others — Θ(n²) messages per update. OSPF instead elects a DR and BDR via a priority-based election during the Hello exchange; every other router becomes fully adjacent only to the DR and BDR, and the DR relays updates to everyone else. This reduces the flooding fan-out to Θ(n) — a direct, and provable, complexity improvement over full-mesh adjacency.

3. Database synchronization (the Exchange/Loading states). Two newly-adjacent routers exchange condensed LSA headers (Database Description packets), compute the set-difference against their own LSDB, and then explicitly request (Link State Request) and receive (Link State Update) the missing or newer LSAs. This is a reliable, bounded synchronization: every LSA exchange is acknowledged (Link State Acknowledgment), and each LSA carries a 32-bit sequence number plus an age field, so stale or duplicate copies are detected and discarded.

4. Flooding and aging. After initial synchronization, any change (a link going up/down, a cost change) triggers the affected router to originate a fresh LSA, flood it to all neighbors (who re-flood it onward, except back to the sender), and each recipient updates its LSDB and re-triggers its local Dijkstra computation. LSAs also self-expire: each carries an age that increments over time (`MaxAge`, typically 3600 seconds), forcing periodic refresh even without topology change — this guards against a stale entry silently lingering forever due to a lost flood.

5. Shortest-path computation. Once a router's LSDB for its area is complete and consistent, it runs Dijkstra's algorithm with itself as the source, treating each router and transit network as a graph node and each LSA-advertised link cost as an edge weight. The result is a Shortest Path Tree (SPT) rooted at the router; the next-hop toward each destination is read off the SPT and installed in the forwarding table.

6. Inter-area routing. An ABR runs Dijkstra separately for each area it touches, and then summarizes reachable destinations from one area as Summary LSAs (Type 3) injected into adjacent areas (typically into the backbone, and from the backbone outward to other areas). This means a router in a non-backbone area does not see the internal topology of other areas — only aggregated distances — bounding both database size and the exposed topology. ``` // OSPF core loop, per router R in area A initialize LSDB[A] = { R's own Router-LSA } initialize neighbor table = {} on receive Hello from interface i: if Hello.areaID != A or auth fails: discard if Hello lists R.RouterID: mark neighbor bidirectional if link type is broadcast/multi-access: participate in DR/BDR election using Hello.priority else: add sender to neighbor table (one-way) on neighbor becomes FULL (databases synchronized): // triggered after Database Description + LSR/LSU/LSAck exchange mark adjacency FULL schedule SPF recomputation on topology change detected locally (interface up/down, cost change): origin_LSA = build_LSA(R, changed_links) origin_LSA.seq += 1 origin_LSA.age = 0 LSDB[A][R] = origin_LSA flood(origin_LSA, exclude = incoming_interface) schedule SPF recomputation on receive LSA L from neighbor N (via LSU): existing = LSDB[A][L.originator] if existing is None or L.seq > existing.seq or L.age == MaxAge: LSDB[A][L.originator] = L send LSAck to N flood(L, exclude = interface_from(N)) schedule SPF recomputation else: // stale or duplicate — do not re-flood send LSAck to N (if L.seq == existing.seq, no update needed) procedure schedule_SPF_recomputation(): // coalesced: run once after a short delay to absorb bursts G = build_graph(LSDB[A]) // nodes = routers/networks, edges = LSA links SPT, dist = Dijkstra(G, source = R) // standard link-state shortest-path tree forwarding_table = extract_next_hops(SPT) if R is ABR: for each other area A2 that R also serves: summarize(area A, dist) -> Type-3 Summary LSAs injected into A2 install(forwarding_table) ``` The following figure shows a small AS with two non-backbone areas joined by the backbone through two Area Border Routers.

Figure
OSPF area hierarchy: Area 0 backbone joins Area 1 and Area 2 via ABRs
Area 0 (Backbone) B1 B2 cost 10 Area 1 R1 R2 ABR1 Area 2 R3 R4 ABR2 ABR1, ABR2 flood Type-3 Summary LSAs into Area 0 and back into their own areas

Complexity & correctness

Per-router computational cost. Let a single area contain `N` router/network nodes and `E` link-state edges. Each router runs Dijkstra's algorithm on this graph whenever the area's LSDB changes materially. Using a binary heap, Dijkstra runs in ``` O((N + E) log N) ``` time per invocation — the standard bound: each of `N` extract-min operations costs `O(log N)`, and each of `E` edge relaxations costs `O(log N)` for a possible heap decrease-key. Because OSPF confines detailed topology to an area, `N` and `E` are the size of one area, not the whole AS. This is precisely the payoff of area partitioning: without it, every router in a 5,000-router AS would run Dijkstra over a 5,000-node graph on every change; with areas of, say, 200 routers each, the per-event cost drops roughly by the ratio of `(N_total log N_total)` to `(N_area log N_area)` — a substantial constant-factor (often 10–50×) reduction in practice, though asymptotically the exponent is unchanged.

Flooding cost. In the worst case, flooding one new LSA to all `n` routers in an area over a spanning-tree-like flooding pattern costs `O(n)` messages (each router forwards to its neighbors once, and duplicate suppression via sequence numbers prevents re-flooding). Without the DR/BDR mechanism, a broadcast segment with `k` attached routers would need `O(k²)` adjacency-level exchanges (each pair separately synchronizing and each flooding to every other). With a DR and BDR, only `2k` adjacencies are needed (each router to DR, each router to BDR), and the DR performs the fan-out — an asymptotic improvement from quadratic to linear in `k`.

Space. Each router stores the full LSDB of its own area(s): `O(N + E)` entries, plus summarized Type-3/Type-4/Type-5 LSAs for external and inter-area reachability, typically `O(D)` where `D` is the number of distinct destination prefixes summarized. This is far smaller than storing the entire AS topology when area sizes are bounded.

Correctness argument. OSPF's correctness reduces to two guarantees layered on top of Dijkstra's known-correct shortest path computation: 1. Database synchronization invariant. The reliable flooding protocol (sequence numbers + acknowledgments + retransmission on timeout + MaxAge expiry) guarantees that, absent partition, every router in an area eventually holds a set of LSAs that is identical and reflects the most recent originator for each entry. This is an eventual-consistency property: after a bounded number of flooding rounds following the last topology change (bounded by the area's diameter), all routers converge to the same LSDB. 2. Local Dijkstra correctness. Dijkstra's algorithm is provably correct for graphs with non-negative edge weights (OSPF costs are configured as positive integers): the standard greedy-exchange argument shows that the first time a node is popped from the priority queue, its tentative distance equals its true shortest-path distance, because any alternative path would have to pass through a node not yet finalized, whose distance is already ≥ the current minimum. Combining these: if every router in the area computes Dijkstra over the same graph, each router's shortest-path tree is mutually consistent — i.e., if router A's SPT says the next hop toward router C is B, then B's own SPT (over the same graph) will continue the path correctly, so no forwarding loops arise as long as databases remain synchronized. This is the crucial invariant that link-state protocols provide and distance-vector protocols do not automatically guarantee (count-to-infinity is precisely a failure of exactly this invariant).

Honest tradeoffs. Area partitioning buys scalability at the cost of routing optimality: because Summary LSAs only carry an aggregate distance (not full topology), a router may pick a suboptimal inter-area path when multiple ABRs offer routes into another area — OSPF cannot always find the true AS-wide shortest path once summarization hides topology (this is the classic "no optimal inter-area routing" limitation, sometimes visualized as OSPF's version of the "two ABR problem"). Additionally, convergence after a failure is not instantaneous: it requires Hello-based failure detection (bounded by the Dead interval, default 40 seconds, though sub-second Bidirectional Forwarding Detection is commonly layered on top), flooding propagation delay, and SPF recomputation delay (often deliberately throttled/coalesced to avoid computational storms during periods of instability — a real operational tension between fast convergence and CPU protection).

Worked example

Consider a small Area 1 with four routers: R1, R2, R3, R4, connected as below, with link costs shown (costs are typically inversely related to bandwidth in real deployments; here we use illustrative integers).

LinkCost
R1–R25
R1–R32
R3–R21
R3–R44
R2–R48

Step 1 — Router-LSA origination. Each router builds a Router-LSA listing its interfaces and costs. R1's LSA: `{link to R2: cost 5, link to R3: cost 2}`. R3's LSA: `{link to R1: cost 2, link to R2: cost 1, link to R4: cost 4}`, and so on symmetrically.

Step 2 — Flooding. Suppose R1 comes up last. It floods its Router-LSA to R2 and R3, both of which install it, send LSAck, and re-flood to their other neighbors (R2 re-floods to R4; R3 re-floods to R4). R4, receiving the same LSA twice (once via R2, once via R3), installs it on the first copy and discards the duplicate (same Router-LSA, same sequence number) after sending an LSAck — this duplicate suppression is exactly what keeps flooding at `O(n)` rather than exploding combinatorially.

Step 3 — Synchronized LSDB. After flooding settles, all four routers hold an identical set of four Router-LSAs, describing the weighted graph above.

Step 4 — Dijkstra from R1's perspective. R1 runs Dijkstra with itself as source.

StepSettled setFrontier distancesChosen node
0{}R1:0, R2:∞, R3:∞, R4:∞—
1{R1}R2:5, R3:2, R4:∞R3 (dist 2)
2{R1,R3}R2: min(5, 2+1=3)=3, R4: 2+4=6R2 (dist 3)
3{R1,R3,R2}R4: min(6, 3+8=11)=6R4 (dist 6)
4{R1,R3,R2,R4}done—

Final shortest-path tree at R1: R1→R3 (cost 2), R1→R3→R2 (cost 3), R1→R3→R4 (cost 6). Notice the path R1→R2 directly would cost 5, worse than the 3 achieved via R3 — this is exactly the kind of non-obvious shortest path that link-state computation, not naive distance-vector hop-counting, correctly discovers. R1 installs next-hop R3 for both destinations R2 and R4, since both lie on the tree branch through R3.

Step 5 — An ABR summarizes. Suppose R4 is actually an ABR also serving Area 0. R4 does not flood the raw Router-LSA graph of Area 1 into Area 0; instead it originates a Type-3 Summary LSA such as `{destination: R1's subnet, cost: 6}` (its own computed distance), hiding the internal topology (R1–R3–R4 chain) from routers in other areas — they see only "distance 6 to reach that subnet via R4," not the internal graph.

Step 6 — A link failure. If link R3–R2 (cost 1) fails, R3 and R2 each detect it (via missed Hellos, Dead interval expiry, or an immediate link-down interrupt) and originate fresh Router-LSAs with higher sequence numbers omitting that link. These flood through the area; every router — including R1 — recomputes Dijkstra. R1's new tree: R1→R3 (2), R1→R2 (5, direct, since the R3–R2 shortcut is gone), R1→R3→R4 (6, unaffected since it never used the failed link). The forwarding table updates only where necessary (R2's next hop switches from R3 to direct), demonstrating that OSPF convergence is local to the affected destinations, not a full table rebuild from scratch conceptually — though implementations do rerun Dijkstra wholesale for simplicity.

Common pitfalls & traps

PitfallWhy it happensConsequence
Discontiguous backbone (Area 0 split into pieces)Network grows organically without a coherent Area 0 designSome areas lose reachability to others; requires virtual links as a stopgap
Mismatched Hello/Dead intervals or area IDs between neighborsManual misconfiguration on one routerAdjacency never forms (stuck in Init/2-Way); silent partial connectivity
Treating all areas as "free" to add without planningUnderestimating that every area needs an ABR and summarization designSuboptimal inter-area paths, unexpected routing asymmetry
Ignoring MaxAge / LSA refresh under a flaky linkAssuming flooding alone keeps state fresh foreverA stale LSA lingers past its true validity if refresh floods are lost, until MaxAge forces expiry
SPF recomputation storms during instabilityNo throttling on repeated LSA changesRouter CPU saturates recomputing Dijkstra on every micro-flap, delaying convergence further
Confusing OSPF's shortest-cost path with shortest hop-countAssuming "shortest path" always means fewest routersA path with more hops but lower total configured cost is correctly preferred, surprising operators expecting hop-count behavior

Pioneers & history

OSPF emerged from the Internet Engineering Task Force's effort to replace the earlier distance-vector RIP protocol, which suffered from slow convergence and count-to-infinity problems in larger networks. The protocol's design drew directly on link-state ideas already validated in ARPANET's internal routing redesign and in the IS-IS protocol developed contemporaneously for OSI networks — OSPF and IS-IS remain the two dominant link-state IGPs in production networks today, often compared feature-for-feature.

Origins · 1 minJohn Moy and how this was discovered
JM
John Moy
1953–present · Designer of OSPF, IETF working group chair through its standardization in RFC 1131 (1989) and RFC 2328 (1998)

John Moy led the OSPF working group at the IETF, translating the theoretical link-state model into a protocol robust enough for real, imperfect networks: broadcast media with many attached routers, unreliable links, and administrative domains needing hierarchy. His book OSPF: Anatomy of an Internet Routing Protocol remains a canonical reference for the protocol's internal state machines.

History
1979
— ARPANET migrates its core routing from an early distance-vector design to a link-state algorithm after severe scaling and looping problems.
1988
— The IETF forms a working group to design an open (non-proprietary) link-state IGP for TCP/IP networks.
1989
— RFC 1131 publishes OSPF version 1.
1991
— OSPF version 2 introduces areas, the Designated Router mechanism, and authentication, becoming the version used ever since (refined in RFC 2328, 1998).
1999–2008
— OSPFv3 (RFC 2740, later RFC 5340) extends OSPF to IPv6, generalizing addressing while preserving the area and LSA architecture.

Where this matters

OSPF is one of the two dominant Interior Gateway Protocols running inside enterprise networks, university campuses, and ISP core networks worldwide (the other being IS-IS, structurally similar but historically favored by some large carriers). Data center fabrics, campus backbones, and multi-building enterprise networks configure OSPF areas to mirror physical or organizational boundaries — a headquarters building might be Area 0, each branch office its own non-backbone area. Cloud providers' internal backbone networks and large-scale ISPs use OSPF (or IS-IS) as the IGP that computes reachability to every internal router and link, information which is then fed as input to Inter-Domain Routing with BGP — BGP relies on the IGP to resolve next-hop reachability for routes learned from other autonomous systems, making OSPF (or IS-IS) a foundational layer beneath every inter-domain routing decision. Traffic engineering extensions (OSPF-TE) let OSPF distribute additional link attributes (bandwidth, delay) used by MPLS traffic engineering to compute constrained paths, extending the same link-state database mechanism far beyond simple shortest-path forwarding.

Ask the tutor: How would OSPF's area design change if a network operator wanted to minimize convergence time after frequent link flaps, versus minimizing steady-state routing table size?

How is OSPF different from RIP, and why does that matter?

RIP is a distance-vector protocol: routers exchange only their own distance estimates to destinations, not full topology, and each router computes distances via the Bellman-Ford-style relaxation using neighbor-reported distances. This makes RIP vulnerable to the count-to-infinity problem, where stale, indirect information causes routers to slowly and incorrectly increase advertised distances after a failure, sometimes taking many exchange rounds to converge. OSPF instead floods complete link-state information, so every router computes shortest paths directly from a known-correct, synchronized topology graph using Link-State Routing and Dijkstra's Algorithm. This gives OSPF materially faster, more reliable convergence and immunity to count-to-infinity, at the cost of higher memory (storing the full topology) and more complex protocol machinery (reliable flooding, areas, DR election).

Why must Area 0 be contiguous, and what happens if it isn't?

OSPF's inter-area routing model assumes all non-backbone areas connect to a single, contiguous backbone, and that inter-area traffic always transits through Area 0. If Area 0 becomes split — say, due to a WAN link failure separating two backbone segments — routers in different backbone fragments can no longer exchange Summary LSAs consistently, and inter-area reachability breaks even though each fragment is internally healthy. The standard remedy is a virtual link, a logically configured tunnel across a non-backbone area that stitches the backbone fragments back into one logical Area 0. Virtual links are considered a workaround, not a design goal — well-planned OSPF deployments avoid needing them by ensuring physical backbone contiguity from the start.

What exactly is stored in a Router-LSA versus a Summary LSA?

A Router-LSA (Type 1) is originated by every router and lists that router's own interfaces: attached networks, neighboring routers, and the configured cost of each link — this is the raw material from which the intra-area topology graph is built. A Summary LSA (Type 3), originated only by Area Border Routers, instead advertises a single aggregated distance to a destination network in another area, with no internal topology detail. This distinction is exactly what enables area partitioning: internal routers assemble Router-LSAs into a detailed graph for Dijkstra within their own area, while treating Summary LSAs as simple weighted "shortcut" edges to destinations outside the area, without needing to know how that distance was computed.

How does OSPF handle equal-cost paths, and why is that useful?

When Dijkstra's computation finds multiple next hops with identical minimum cost to the same destination, OSPF can install all of them as Equal-Cost Multi-Path (ECMP) routes rather than arbitrarily picking one. Traffic is then load-balanced across these paths, typically using a hash of packet header fields to keep individual flows on a single path (avoiding reordering) while spreading aggregate load. This is a direct, practical benefit of the underlying graph-shortest-path formulation: because Dijkstra naturally exposes ties in the distance array, OSPF implementations can surface them to the forwarding plane rather than discarding the information, improving both throughput and resilience to a single path's failure.

Does OSPF guarantee no routing loops, ever?

OSPF guarantees loop-freedom within a fully synchronized area under Dijkstra's standard correctness argument: since every router computes shortest paths over the identical graph, next-hop chains cannot cycle. However, this guarantee is conditional on the synchronized-database invariant actually holding. Transient loops can occur during convergence — the brief window after a topology change where some routers have processed the new LSA and recomputed while others have not yet — though these are self-correcting once flooding completes. Loops can also arise from operational errors that break synchronization (asymmetric filtering, software bugs, mismatched area configuration), which is why the invariant, not the algorithm alone, is the real correctness guarantee to monitor.

How does OSPF interact with BGP in a real ISP network?

OSPF (or IS-IS) runs as the Interior Gateway Protocol, giving every router inside the AS a synchronized view of internal topology and computing shortest paths to internal destinations, including the loopback addresses of routers that speak BGP. BGP itself does not care about physical topology; it exchanges reachability information about IP prefixes between autonomous systems, expressed as sequences of AS numbers. When a router installs a BGP route, its next hop is often another router's loopback address reachable only via the IGP — so BGP effectively depends on OSPF to resolve "how do I actually reach that next-hop router," making OSPF the substrate beneath Inter-Domain Routing with BGP rather than a competitor to it.

Why does OSPF need explicit acknowledgments (LSAck) if IP itself can be unreliable?

IP provides best-effort delivery with no guarantee that a flooded LSA update actually arrives at every neighbor, and OSPF's correctness depends entirely on databases staying synchronized. Without acknowledgments, a dropped Link State Update could leave one router permanently behind, silently computing shortest paths over a stale graph — exactly the failure mode the warning above describes. By requiring an explicit LSAck for every LSA, retransmitting on timeout, and using sequence numbers to detect which copy is authoritative, OSPF turns an unreliable IP substrate into a reliable flooding protocol, analogous to how Link-State Routing and Dijkstra's Algorithm assumes a correct global view is achievable and OSPF must engineer that assumption into reality.

Before this

Where this leads