Introduction to Next-Hop Recursion Failure
A next-hop recursion failure occurs when a router’s control plane installs a route whose next‑hop address is unresolved because the next‑hop depends on another route that is absent, inactive, or resolves to an unreachable interface. The control plane may appear healthy—RIB entries are present, BGP/OSPF/IS‑IS adjacencies are up—but the FIB cannot program the forwarding entry because the recursive resolution chain terminates in a null or black‑hole next‑hop.
Common causes
- Static or configured next‑hops pointing to a down or unnumbered interface.
- BGP routes with a self‑referencing next‑hop that is filtered or suppressed before installation.
- OSPF/IS‑IS routes where the next‑hop is a loopback not advertised into the IGP (missing network statement or passive‑interface).
- Route‑policy or import‑map rewriting the next‑hop to an address absent from the RIB (e.g., discarded aggregate).
- MPLS VPN/EVPN where the BGP next‑hop is a PE address unreachable via the underlying IGP (missing label‑binding or VRF import).
Symptoms
- Forwarding black‑hole: traffic dropped despite the route appearing in
show ip route. - Ping/traceroute to the destination fails while pinging the next‑hop succeeds.
- Increased CPU/memory usage from repeated next‑hop resolution attempts.
- Log messages such as
%BGP-3-NOTIFICATION: Invalid NEXT_HOP attributeor%OSPF-4-ERROR: Next hop unreachable.
Initial troubleshooting
- Verify the route entry (
show ip route <prefix>). - Check next‑hop reachability (
ping <next-hop>ortraceroute <next-hop>). - Confirm IGP/BGP adjacency state (
show ip ospf neighbor,show bgp summary). - Inspect applied route‑policy (
show route-policy <name>).
If the next‑hop is reachable but the route remains inactive, the problem lies in the recursion chain, requiring a deeper look at the forwarding dependency graph.
Direct CLI Tracing
Overview
Direct CLI tracing uses the router’s built‑in diagnostic commands to inspect the RIB, FIB, and protocol tables. Typical commands include:
- RIB inspection –
show ip route,show route,show ipv6 route. - FIB / hardware forwarding –
show cef ipv4 prefix <prefix> detail,show route receive,show mpls forwarding-table. - Protocol‑specific next‑hop attributes –
show bgp <prefix>,show ospf database router,show isis database detail. - Recursion debugging –
show ip route recursion <prefix>,show route next-hop <address> detail. - Log and trace buffers –
show logging,show monitor event-trace bgp all.
The operator must correlate the output manually to locate where the recursion breaks.
Step‑by‑Step Procedure
- Identify the problematic prefix from alerts or user reports.
- Display the RIB entry and note the next‑hop(s).
show ip route 10.0.0.0/24 - Check each next‑hop for reachability in the RIB/FIB.
show ip route <next-hop> show cef ipv4 <next-hop> detail - If the next‑hop is missing, trace why:
- Look for IGP advertisement (
show ospf database,show isis database). - Check BGP attributes (
show bgp <next-hop>). - Verify any route‑policy that may have filtered or altered the next‑hop.
- Look for IGP advertisement (
- Repeat steps 2‑4 for each discovered next‑hop until a reachable next‑hop is found or a dead‑end is reached.
- Confirm FIB programming for the prefix once a valid next‑hop is resolved.
show cef ipv4 10.0.0.0/24 detail - Document the failure point (e.g., “Next‑hop 192.168.1.10 missing from RIB because OSPF LSA Type‑1 not advertised due to passive‑interface on Gig0/1”).
Example CLI Commands
| Vendor | Command | Purpose |
|---|---|---|
| Cisco IOS‑XR | show route 10.0.0.0/24 detail | RIB entry with next‑hop and source protocol |
| Cisco IOS‑XR | show cef ipv4 10.0.0.0/24 detail | FIB entry and hardware programming status |
| Cisco IOS‑XR | show bgp 10.0.0.0/24 | BGP path attributes, next‑hop, and any filtering |
| Cisco IOS‑XR | show ospf interface GigabitEthernet0/0/0/1 | Verify interface state for OSPF adjacency |
| Juniper Junos | show route 10.0.0.0/24 extensive | RIB, next‑hop, and forwarding‑table details |
| Juniper Junos | show route receive-protocol bgp 10.0.0.0/24 | BGP‑received route before import policy |
| Juniper Junos | show ospf neighbor | OSPF adjacency state |
| Arista EOS | show ip route 10.0.0.0/24 vrf default | RIB entry |
| Arista EOS | show ip bgp 10.0.0.0/24 | BGP path details |
| Arista EOS | show ip route <next-hop> | Next‑hop reachability |
Limitations
- Manual and time‑intensive – each hop requires a separate command set; large graphs become unwieldy.
- Human error prone – subtle policy matches may be missed or vendor output misinterpreted.
- Limited historical view – CLI shows current state; intermittent failures that self‑heal are hard to capture without continuous logging.
- Scalability – iterating CLI for hundreds of prefixes is impractical.
- Tool‑dependency – some platforms lack a dedicated “recursion” command, forcing manual graph assembly.
Scripted Table Collection
Introduction
Scripted collection automates retrieval of RIB, FIB, and protocol tables from multiple devices, normalizes the output (e.g., JSON or CSV), and enables offline graph construction and analysis. Typical approaches:
- Bash/Python scripts using SSH or NETCONF/RESTCONF to run
showcommands. - Ansible playbooks with
iosxr_command,juniper_junos_command, orarista_eos_commandmodules. - Telemetry pipelines (gNMI, gRPC) streaming routing tables to a time‑series database for later querying.
The scripted approach decouples data gathering from analysis, allowing reuse of the same dataset for multiple investigations.
Script Examples
Python + Netmiko (Cisco IOS‑XR)
#!/usr/bin/env python3
import json
from netmiko import ConnectHandler
devices = [
{"host": "r1.example.com", "username": "admin", "password": "****", "device_type": "cisco_xr"},
{"host": "r2.example.com", "username": "admin", "password": "****", "device_type": "cisco_xr"},
]
def collect_rib_bgp(dev):
try:
conn = ConnectHandler(**dev)
conn.enable()
rib_out = conn.send_command("show route 10.0.0.0/24 detail", use_textfsm=True)
bgp_out = conn.send_command("show bgp 10.0.0.0/24", use_textfsm=True)
conn.disconnect()
return {
"host": dev["host"],
"rib": rib_out,
"bgp": bgp_out,
}
except Exception as e:
return {"host": dev["host"], "error": str(e)}
results = [collect_rib_bgp(d) for d in devices]
with open("routing_snapshot.json", "w") as f:
json.dump(results, f, indent=2)
Ansible (Juniper Junos)
- name: Gather routing tables for next‑hop analysis
hosts: juniper_routers
gather_facts: false
tasks:
- name: Run show route extensive
juniper_junos_command:
commands:
- show route 10.0.0.0/24 extensive
register: route_out
- name: Run show bgp path
juniper_junos_command:
commands:
- show bgp 10.0.0.0/24
register: bgp_out
- name: Save to file
copy:
content: |
{{ inventory_hostname }}
ROUTE: {{ route_out.stdout[0] }}
BGP: {{ bgp_out.stdout[0] }}
dest: "/tmp/routing_{{ inventory_hostname }}.txt"
Analyzing Collected Data
Once normalized, a simple graph algorithm can detect missing next‑hops:
import json, sys
def load_snapshot(path):
with open(path) as f:
return json.load(f)
def build_rib_map(snapshot):
rib = {}
for entry in snapshot:
host = entry["host"]
rib[host] = {}
for route in entry.get("rib", []):
prefix = route["prefix"]
nexthops = route.get("nexthop", [])
rib[host][prefix] = nexthops
return rib
def find_missing_next_hops(rib_map):
missing = []
for host, prefixes in rib_map.items():
for prefix, nexthops in prefixes.items():
for nh in nexthops:
found = any(nh in v for v in rib_map.values())
if not found:
missing.append((host, prefix, nh))
return missing
if __name__ == "__main__":
snap = load_snapshot("routing_snapshot.json")
rib = build_rib_map(snap)
failures = find_missing_next_hops(rib)
for host, prefix, nh in failures:
print(f"[{host}] Prefix {prefix} depends on unresolved next‑hop {nh}")
The script prints each prefix whose next‑hop does not appear as a destination in any collected RIB, pinpointing the recursion break.
Scaling Limitations
- Collection window – pulling full routing tables from hundreds of routers can consume significant bandwidth and CPU; incremental or filtered collection (e.g., only prefixes of interest) mitigates this.
- Storage – raw snapshots grow linearly with device count and prefix volume; compression or time‑series roll‑up is needed for long‑term retention.
- Parsing variability – different OS versions may change CLI text output, breaking TextFSM or TTP templates; version‑specific parsers increase maintenance overhead.
- Concurrency limits – SSH‑based scripts are limited by simultaneous sessions; scaling to thousands of devices often requires a proxy or orchestration framework (e.g., Napalm, Nornir).
- Stale data – if the failure is transient, a snapshot taken after recovery will not show the problem; continuous streaming telemetry is required for truly intermittent issues.
AI‑Assisted Tool Orchestration
Overview
AI‑assisted orchestration platforms ingest telemetry (SNMP, syslog, gNMI, streaming telemetry), logs, and occasional on‑demand CLI outputs, then apply machine‑learning models or rule‑based inference engines to surface probable root causes. Examples:
- Cisco Crosswork Network Insights – anomaly detection on BGP/OSPF state.
- Juniper Mist AI™ – correlates routing‑protocol events with forwarding drops.
- Arista CloudVision with AI‑driven Insights – predicts forwarding‑path black‑holes.
- Vendor‑agnostic solutions – OpenNMS with ML plugins or custom pipelines using TensorFlow/PyTorch that classify routing‑state vectors.
These tools typically output a probability score or a ranked list of hypotheses (e.g., “Next‑hop recursion failure due to missing OSPF LSA for 192.168.10.0/24 – 87% confidence”).
Configuring AI‑Assisted Tools for Recursion Failure Detection
- Enable telemetry streams for RIB/FIB and protocol state.
- Cisco IOS‑XR:
telemetry model-drivensubscription toCisco-IOS-XR-ipv4-rib-operandCisco-IOS-XR-bgp-oper. - Juniper Junos:
set system services extension-service request-response grpcand subscribe tojunos-routing/rib.
- Cisco IOS‑XR:
- Forward logs (syslog or structured logging) to the AI platform’s ingest endpoint.
- Define a detection model (often pre‑built) that looks for:
RIB entry presentANDNext‑hop address not present in any RIB/FIBANDNo active BGP/OSPF/IS‑IS withdrawal for that next‑hop.
- Set alert thresholds (e.g., trigger when the condition persists > 30 seconds).
- Integrate with ticketing or automation (e.g., create a ServiceNow incident or run a remediation playbook).
Example Use Cases and CLI/Code Snippets
-
Telemetry subscription (IOS‑XR)
telemetry model-driven subscription NEXT_HOP_CHECK sensor-group-id 10 sensor-path Cisco-IOS-XR-ipv4-rib-oper sensor-path Cisco-IOS-XR-bgp-oper destination-id 1 address 10.10.10.5 port 57500 protocol gRPC encoding self-describing-gpb sample-interval 30000 -
Log forwarding (Junos)
set system syslog host 10.10.10.5 any any set system services extension-service request-response grpc -
Simple Python inference (pseudo‑code)
import pandas as pd # rib_df: columns [device, prefix, next_hop, present] # nh_rib_df: columns [device, prefix, present] merged = rib_df.merge( nh_rib_df.rename(columns={'prefix':'next_hop','present':'nh_present'}), on=['device','next_hop'], how='left' ) merged['recursion_fail'] = merged['present'] & (~merged['nh_present'].fillna(False)) alerts = merged[merged['recursion_fail']][['device','prefix','next_hop']]
These snippets illustrate how telemetry, logs, and lightweight analytics can be combined to automatically surface next‑hop recursion failures without manual CLI traversal.