pyats-routing
Routing Protocol Analysis
Deep routing protocol expertise for OSPF, BGP, EIGRP, IS-IS, and the routing table. This skill provides CCIE-level analysis workflows.
Routing Table Analysis
Always start here. The routing table is the source of truth for forwarding decisions.
Full Routing Table
PYATS_TESTBED_PATH=$PYATS_TESTBED_PATH python3 $MCP_CALL "python3 -u $PYATS_MCP_SCRIPT" pyats_run_show_command '{"device_name":"R1","command":"show ip route"}'
Per-Protocol Routes
# OSPF routes only
PYATS_TESTBED_PATH=$PYATS_TESTBED_PATH python3 $MCP_CALL "python3 -u $PYATS_MCP_SCRIPT" pyats_run_show_command '{"device_name":"R1","command":"show ip route ospf"}'
# BGP routes only
PYATS_TESTBED_PATH=$PYATS_TESTBED_PATH python3 $MCP_CALL "python3 -u $PYATS_MCP_SCRIPT" pyats_run_show_command '{"device_name":"R1","command":"show ip route bgp"}'
# Connected and static
PYATS_TESTBED_PATH=$PYATS_TESTBED_PATH python3 $MCP_CALL "python3 -u $PYATS_MCP_SCRIPT" pyats_run_show_command '{"device_name":"R1","command":"show ip route connected"}'
PYATS_TESTBED_PATH=$PYATS_TESTBED_PATH python3 $MCP_CALL "python3 -u $PYATS_MCP_SCRIPT" pyats_run_show_command '{"device_name":"R1","command":"show ip route static"}'
VRF Routes
PYATS_TESTBED_PATH=$PYATS_TESTBED_PATH python3 $MCP_CALL "python3 -u $PYATS_MCP_SCRIPT" pyats_run_show_command '{"device_name":"R1","command":"show ip route vrf MGMT"}'
Analysis checklist:
- Route source codes: C (connected), S (static), O (OSPF), O IA (OSPF inter-area), O E1/E2 (OSPF external), B (BGP), D (EIGRP), D EX (EIGRP external)
- Administrative distance and metric for each route
- Next-hop reachability — is the next-hop IP actually reachable?
- Recursive lookups — routes pointing to next-hops resolved through other routes
- Equal-cost multipath (ECMP) — multiple next-hops for the same prefix
- Default route presence and source
- Unexpected route absence — if a prefix should be there but is not
OSPF Deep Dive
OSPF Process Overview
PYATS_TESTBED_PATH=$PYATS_TESTBED_PATH python3 $MCP_CALL "python3 -u $PYATS_MCP_SCRIPT" pyats_run_show_command '{"device_name":"R1","command":"show ip ospf"}'
Check: Router ID, areas configured, SPF run count (high = instability), reference bandwidth, stub/NSSA config, authentication.
OSPF Neighbors
PYATS_TESTBED_PATH=$PYATS_TESTBED_PATH python3 $MCP_CALL "python3 -u $PYATS_MCP_SCRIPT" pyats_run_show_command '{"device_name":"R1","command":"show ip ospf neighbor"}'
Neighbor state analysis:
- FULL — Healthy, full adjacency
- FULL/DR or FULL/BDR — Normal on broadcast/NBMA networks
- FULL/DROTHER — Normal on broadcast/NBMA (non-DR/BDR)
- 2WAY/DROTHER — Normal, two DROTHERs don't form full adjacency
- INIT — One-way communication only → check MTU, ACLs, authentication
- EXSTART/EXCHANGE — Stuck in database exchange → MTU mismatch (most common), OSPF area mismatch, authentication
- LOADING — Stuck loading LSAs → database corruption, memory issues
- DOWN — No hello packets → interface down, ACL blocking, hello/dead timer mismatch
Common OSPF problems and their symptoms:
| Symptom | Likely Cause |
|---|---|
| Stuck in EXSTART | MTU mismatch between neighbors |
| Stuck in INIT | Hello reaching neighbor but not returning (ACL, asymmetric routing) |
| Neighbor flapping | Unstable link, hello/dead timer too aggressive, CPU too high to process hellos |
| Missing routes | Area type mismatch (stub vs non-stub), missing redistribute or default-information originate |
| Suboptimal routing | Cost misconfiguration, missing auto-cost reference-bandwidth |
OSPF Interfaces
PYATS_TESTBED_PATH=$PYATS_TESTBED_PATH python3 $MCP_CALL "python3 -u $PYATS_MCP_SCRIPT" pyats_run_show_command '{"device_name":"R1","command":"show ip ospf interface"}'
Check per interface: Area assignment, network type (broadcast/point-to-point/NBMA), cost, hello/dead timers, DR/BDR election, authentication type, passive status.
OSPF Database (LSDB)
PYATS_TESTBED_PATH=$PYATS_TESTBED_PATH python3 $MCP_CALL "python3 -u $PYATS_MCP_SCRIPT" pyats_run_show_command '{"device_name":"R1","command":"show ip ospf database"}'
LSA types to understand:
- Type 1 (Router LSA) — Every router generates one per area
- Type 2 (Network LSA) — DR generates on broadcast/NBMA segments
- Type 3 (Summary LSA) — ABR advertises between areas
- Type 4 (ASBR Summary) — ABR advertises ASBR reachability
- Type 5 (External LSA) — ASBR redistributes external routes
- Type 7 (NSSA External) — External routes in NSSA areas (converted to Type 5 at ABR)
Red flags in LSDB:
- Rapidly incrementing sequence numbers → LSA flooding loop
- Type 5 LSAs in stub area → misconfiguration
- Missing Type 3 LSAs for expected inter-area prefixes → ABR filtering or area mismatch
- Duplicate Router IDs → same RID on two routers
BGP Deep Dive
BGP Summary
PYATS_TESTBED_PATH=$PYATS_TESTBED_PATH python3 $MCP_CALL "python3 -u $PYATS_MCP_SCRIPT" pyats_run_show_command '{"device_name":"R1","command":"show ip bgp summary"}'
Check: Local AS, router ID, table version, total paths/best paths, per-neighbor state.
Neighbor state column meaning:
- Number (e.g., 5) → Established, number = received prefixes
Idle→ Not configured correctly, or administratively shutIdle (Admin)→neighbor shutdownconfiguredActive→ TCP connection failing → check reachability, TTL, ACLsConnect→ TCP SYN sent, no responseOpenSent→ TCP connected, waiting for OPEN replyOpenConfirm→ OPEN received, waiting for KEEPALIVEEstablished→ Healthy
BGP Neighbors Detail
PYATS_TESTBED_PATH=$PYATS_TESTBED_PATH python3 $MCP_CALL "python3 -u $PYATS_MCP_SCRIPT" pyats_run_show_command '{"device_name":"R1","command":"show ip bgp neighbors"}'
Deep analysis per neighbor:
- BGP state, uptime, last reset reason
- Messages sent/received (OPEN, UPDATE, KEEPALIVE, NOTIFICATION)
- Hold time and keepalive interval
- Address families negotiated
- Route-map/filter-list/prefix-list applied (inbound/outbound)
- Next-hop-self, soft-reconfiguration, route-reflector-client status
- Notification messages — decode the error code/subcode
Common BGP problems:
| Symptom | Likely Cause |
|---|---|
| Stuck in Active | TCP connection failing — check ACL, reachability, update-source, ebgp-multihop |
| Neighbor flapping | Unstable link, route-map causing route churn, max-prefix exceeded |
| 0 prefixes received | No network or redistribute on remote side, outbound filter on remote, address-family not activated |
| Routes not in RIB | Next-hop unreachable (next-hop-self missing for iBGP), route filtered by policy |
| Suboptimal path | Weight/local-pref/AS-path/MED not set correctly |
BGP Table
PYATS_TESTBED_PATH=$PYATS_TESTBED_PATH python3 $MCP_CALL "python3 -u $PYATS_MCP_SCRIPT" pyats_run_show_command '{"device_name":"R1","command":"show ip bgp"}'
BGP best path selection order (memorize this):
- Highest Weight (Cisco proprietary, local to router)
- Highest Local Preference (iBGP, default 100)
- Locally originated (network/redistribute/aggregate)
- Shortest AS-path
- Lowest Origin (IGP < EGP < Incomplete)
- Lowest MED (from same neighbor AS only)
- eBGP over iBGP
- Lowest IGP metric to next-hop
- Oldest route (for eBGP)
- Lowest router ID
- Lowest neighbor IP
EIGRP Analysis
EIGRP Neighbors
PYATS_TESTBED_PATH=$PYATS_TESTBED_PATH python3 $MCP_CALL "python3 -u $PYATS_MCP_SCRIPT" pyats_run_show_command '{"device_name":"R1","command":"show ip eigrp neighbors"}'
Check: Hold timer (resetting to max means hellos received), uptime, SRTT, RTO, Q count (should be 0 — non-zero means retransmission queue backed up).
EIGRP Topology
PYATS_TESTBED_PATH=$PYATS_TESTBED_PATH python3 $MCP_CALL "python3 -u $PYATS_MCP_SCRIPT" pyats_run_show_command '{"device_name":"R1","command":"show ip eigrp topology"}'
DUAL states:
- Passive (P) → Route is stable, normal
- Active (A) → Route in active query state → DUAL is searching for a feasible successor → if stuck, SIA (Stuck In Active) will kill the neighbor
EIGRP metric components (classic): Bandwidth, Delay, Reliability, Load, MTU (only BW and delay used by default).
EIGRP metric components (wide/named mode): Throughput, Latency, Reliability, Load, MTU, Extended attributes.
Redistribution Audit
When multiple routing protocols are in use, check redistribution points:
PYATS_TESTBED_PATH=$PYATS_TESTBED_PATH python3 $MCP_CALL "python3 -u $PYATS_MCP_SCRIPT" pyats_run_show_command '{"device_name":"R1","command":"show ip protocols"}'
Redistribution checklist:
- Is redistribution mutual (two-way)? Risk of routing loops
- Are route-maps/prefix-lists applied to redistribution? They should be
- Are metrics set correctly on redistribution? (seed metric for EIGRP, metric-type for OSPF)
- Is there a risk of suboptimal routing through the redistribution boundary?
- Administrative distance tuning — are AD values set to prefer the right protocol?
Route Filtering Verification
PYATS_TESTBED_PATH=$PYATS_TESTBED_PATH python3 $MCP_CALL "python3 -u $PYATS_MCP_SCRIPT" pyats_run_show_command '{"device_name":"R1","command":"show route-map"}'
PYATS_TESTBED_PATH=$PYATS_TESTBED_PATH python3 $MCP_CALL "python3 -u $PYATS_MCP_SCRIPT" pyats_run_show_command '{"device_name":"R1","command":"show ip prefix-list"}'
Convergence Validation
After any routing change, verify convergence:
- Check all expected neighbor adjacencies are FULL/Established
- Verify the routing table has all expected prefixes
- Ping across the network from the device to validate data plane
- Check logs for any protocol events during the change
- Compare route counts before and after
# Verify route count
PYATS_TESTBED_PATH=$PYATS_TESTBED_PATH python3 $MCP_CALL "python3 -u $PYATS_MCP_SCRIPT" pyats_run_show_command '{"device_name":"R1","command":"show ip route summary"}'