Hop-by-Hop Truth
Your path tool draws a corridor. Routers forward hop by hop. Here is the 4,032-pair measurement of the gap.
Michel Wijnberg
Almost every network path tool I have used (including, until recently, mine) works like this:
- Take the source router.
- Run Dijkstra over the topology.
- Draw the resulting shortest path.
That is a source-rooted corridor, and it is a subtly wrong model of IP forwarding. Real forwarding has no corridor. Every router independently matches the destination address against its own routing table and picks a next hop. The two answers agree only as long as every intermediate router’s decision rule and input set match the source’s.
In OSPF, they routinely do not.
One packet that never arrives
Here is a case from my lab, measured on every router involved with
show ip route, not modelled but read off the boxes:
| Router | Says about the destination | Forwards to |
|---|---|---|
dfw1-gw2 | metric 20031, type inter area | dfw1-cr1 |
dfw1-cr1 | metric 30001, type intra area | ord1-cr1 ← not dfw2-cr1 |
ord1-cr1 | metric 20001, type intra area | jfk1-cr1 |
jfk1-cr1 | metric 10001, type intra area | lhr1-cr1 |
lhr1-cr1 | metric 1, connected | delivered |
The source-rooted corridor drew this:
dfw1-gw2 → dfw1-cr1 → dfw2-cr1 → iad2-cr1 → ams1-cr1 → lhr1-cr1
The packet never reaches dfw2-cr1. The corridor is a cheaper path. It is
just not a path any router will build. Two independent RFC 2328 rules make
dfw1-cr1 refuse it:
§16 step (3) / §16.2 ¶1: the backbone gate. A router attached to multiple
areas examines only backbone summary-LSAs. dfw2-cr1’s cheaper cost-20011
summary genuinely is in dfw1-cr1’s area-4.6.23.0 database (I confirmed it
with show ip ospf database summary), but it is not a candidate, because
dfw1-cr1 is an ABR and that summary did not arrive via area 0. RFC 3509
§2.1/§2.2 (the Cisco ABR behaviour actually running on these boxes) conditions
this on having an active backbone connection, which dfw1-cr1 has.
§16.2 step (6): intra beats inter, full stop. “If the paths present in the
table are intra-area paths, do nothing with the LSA (intra-area paths are always
preferred).” dfw1-cr1 already holds an intra-area path at metric 30001. The
20011 summary loses to it. Metric is irrelevant: this is a rule about path
type, and a cost comparison never happens.
Either rule alone kills the corridor. A tool that runs one SPF from the source
cannot see either, because it never asks dfw1-cr1 what dfw1-cr1 thinks.
How often does this actually matter?
This is the part I care about, because “your model is theoretically imprecise” is a much weaker claim than a number.
So I read the full OSPF routing table off all 64 routers in AS 200 and compared every one of the 4,032 ordered source/destination pairs:
| Measurement | Result |
|---|---|
| Inter-area pairs where the corridor drew ≥1 hop no router would take | 360 of 2,839 (12.68%) |
| Intra-area pairs with the same defect | 0 of 1,193 |
| Overall | 360 of 4,032 (8.93%) |
Zero for intra-area is the sanity check. Within a single area every router runs SPF over an identical LSDB, and a shortest path stays shortest from every point along it, so the two models have every reason to coincide. The point is that they were measured coinciding rather than assumed to: 1,193 pairs, no disagreement. The moment an ABR and a summary-LSA enter the picture, one path in eight is fiction.
Not “slightly suboptimal”. Fiction, containing a router the packet provably never visits. If you are using that path to plan a maintenance window or to explain an incident, you are reasoning about a route that does not exist.
The fix: ask every router
Osprey now walks the chain. At each hop it evaluates that router’s own installed route toward the destination address, using that router’s own LSDB, its own area memberships, its own ABR status and its own administrative distance mix, then follows the next hop it actually selects, and repeats.
Validated against the same 4,032 pairs:
| Check | Result |
|---|---|
| Router’s own metric reproduced exactly | 4,032 / 4,032 |
| Path type (intra / inter / external) exact | 4,032 / 4,032 |
| Full installed ECMP next-hop set identical | 4,032 / 4,032 |
Chain terminates delivered at the requested destination | 4,032 / 4,032 |
Not “close”. Identical, on every pair, including the complete equal-cost next-hop set rather than one arbitrarily chosen member.
Here is what that looks like on the canvas: forward path in orange, reverse in blue, everything not on the path dimmed to a corridor:
Note the 4 equal-cost paths selector and the per-flow hash label. Osprey
enumerates the complete ECMP set rather than picking one member and presenting
it as the path, because your traffic is hashed across all four, and a
troubleshooting session that examines only one of them will find nothing wrong
three times out of four.
And here is the same path opened up:
Every hop carries the router that made the decision, the area it made it in,
the physical ingress and egress interfaces with their addresses, and the cost.
ABR badges are derived from the LSDBs, not from hostnames. The area ribbon
underneath shows the path crossing 12.1.24.0 → 0.0.0.0 → 6.18.1.0, which
is the backbone transit RFC 2328 requires for inter-area traffic.
And the total cost shown, 30061, is the source’s own installed metric.
It is literally the number show ip route prints on lax1-gw1, stub cost
included, not a sum Osprey computed by adding up links it liked.
The explanation is generated, not decorative:
- Route type: O IA (inter-area): Source 172.16.2.33 and destination 172.16.5.23 have no shared area, routing via backbone (area 0.0.0.0)
- Path traverses: area 12.1.24.0 (cost 10) → area 0.0.0.0 (cost 30040) → area 6.18.1.0 (cost 10)
- Total cost: 30061 via 10 hops
- ECMP: 4 equal-cost paths available
Across the AS boundary
Inside one IGP the chain is computable because the LSDB is complete. Across an AS boundary, nothing is. There is no shared database, no shared metric space, and no shared administrative-distance policy.
The honest answer is not to give up, and it is definitely not to pretend the metrics compose. It is to stitch evidence.
Fifteen hops from lax1-gw1 in AS 200 (tenant Harrier-Broadband, OSPF) to
i-sin1-gw1 in AS 300 (tenant Merlin-Carrier, IS-IS). Read what it does:
- Hop 8
jfk1-gw1carries aBGPbadge: this is where the IGP stops and the BGP decision takes over. - Hop 9
i-jfk1-gw1carries anIS-ISbadge: a completely different protocol, different address plan, different tenant. - The footer says “Per-segment costs (never summed) | 15 hops”. An OSPF cost
of 20051 and an IS-IS cost of 1320 are numbers in unrelated metric spaces.
Adding them would produce
21371, which is not a quantity that exists. Osprey refuses to print it.
The generated explanation names its evidence at every step:
- BGP border selection:
jfk1-gw1holds 10.66.0.0/15 for 10.66.15.3 (AS path: 300)- AS 200: IGP segment:
lax1-gw1→jfk1-gw1, cost 20051- eBGP transition 200 → 300:
jfk1-gw1 Et1/1 → i-jfk1-gw1 Et1/2(bmp-peer+l2; ip-bound)- AS 300: IGP segment:
i-jfk1-gw1→i-sin1-gw1, cost 1320
Step 3 is my favourite line in the product. It does not say “these two routers are probably connected somehow”. It names physical ports on both ends, and then it shows its work in that parenthesis:
bmp-peer: an established eBGP session between them, seen via BMP+l2: the far side was matched through the borders’ LLDP/CDP adjacencyip-bound: both session addresses resolved to those specific ports through the devices’ IP-MIB bindings
Three independent pieces of evidence agreeing. When they do not agree, the product says so instead, which is the entire subject of the next post.
Why I care about this more than about features
A path drawing is a claim about what your network will do with a packet. If the claim is wrong one time in eight and nothing on screen distinguishes the wrong ones from the right ones, the drawing is worse than useless. It is confidently useless, and it will be believed in exactly the moment when being wrong is expensive.
Getting from “plausible” to “reproduces all 64 routing tables exactly, 4,032 out of 4,032” took a validation harness, a lab I can read ground truth from, and a willingness to find out that my own tool had been drawing fiction 360 times.
That is what the lab is for.
Next, the other half of the same coin: the places where Osprey cannot compute the answer, and says so: What Osprey Refuses to Guess.