// field notes · bgp
Why BGP sometimes chooses the wrong path
BGP is rarely random. When traffic takes an unexpected exit, trace the route through policy and the selection algorithm, one attribute at a time.
BGP is rarely doing something random. When a prefix leaves via the “wrong” upstream, the useful question isn’t “why did BGP break?” but “which decision did we ask it to make?”
It’s the same as a Defender pulling to one side under braking. You don’t start by replacing the brakes. You check tyre pressures, then the calipers, then the swivels, in order. BGP has an order too.
Start with the route as received
Before policy touches anything, look at what the neighbour actually sent. On Junos:
user@edge1> show route receive-protocol bgp 192.0.2.1 203.0.113.0/24 detail
If the route you expected isn’t here, no amount of local-preference tuning will help. The fault is upstream: a filter on their side, a missing announcement, or a max-prefix tear-down.
Then look at it after import policy
user@edge1> show route 203.0.113.0/24 detail | match "Local Preference|AS path|Communities|Inactive reason"
The Inactive reason line is the single most useful thing Junos tells you here. It names the exact tie-breaker that lost the route its place.
Walk the selection order
Simplified, Junos compares candidates in this order:
| Step | Attribute | Prefer |
|---|---|---|
| 1 | Local preference | Higher |
| 2 | AS path length | Shorter |
| 3 | Origin | IGP over EGP over incomplete |
| 4 | MED | Lower (same neighbour AS by default) |
| 5 | eBGP over iBGP | eBGP |
| 6 | IGP cost to next hop | Lower |
| 7 | Router ID / peer address | Lower |
Most “wrong path” tickets I see land on one of three causes.
1. Local preference set wider than intended
A term meant to prefer a transit provider for a handful of customer prefixes ends up matching everything because the from clause references an empty or stale prefix list:
policy-statement PREFER-TRANSIT-A {
term customers {
from prefix-list CUSTOMER-PREFIXES; # empty list matches nothing...
then local-preference 200;
}
term default {
then local-preference 200; # ...but this one matches everything
}
}
2. MED compared when you assumed it wasn’t
By default MED is only compared between routes from the same neighbouring AS. If someone enabled path-selection always-compare-med to fix a problem years ago, every multi-homed prefix now feels it.
3. IGP cost to the next hop
Once the BGP attributes tie, the decision drops down into your IGP. A metric change in IS-IS or OSPF for maintenance can silently shift egress for thousands of prefixes. If you can’t explain a BGP change from the BGP side, check the IGP.
The method
- Confirm what was received.
- Confirm what policy did to it.
- Read the inactive reason.
- Walk the selection order until the tie breaks.
Trace, diagnose, fix, in that order. The path BGP chose was almost always the one it was told to choose.