Skip to content
LinkState
Go back

Route policy intent versus forwarding outcome

Introduction to Routing Policy and Configuration

Overview of RIB and FIB

The Routing Information Base (RIB) is the router’s logical table of all routes learned from routing protocols, static configuration, or policy injection. Each entry contains the destination prefix, next‑hop, administrative distance, metric, and associated attributes (e.g., BGP communities, OSPF tags).

The Forwarding Information Base (FIB) is a derived, optimized data structure used by the forwarding plane to make per‑packet lookup decisions. In most modern routers the FIB is a compressed trie or hash table built from the RIB; any discrepancy between the two means packets may be forwarded along a path that the control plane does not consider valid.

Declared Policy and Rendered Configuration

Declared policy is the intent expressed in the router’s configuration language (e.g., route‑map, policy‑statement, filter‑list). Rendered configuration is the actual set of configuration lines that the router has parsed, validated, and installed into its active config after a commit or write‑memory operation. Ideally, declared policy equals rendered configuration; however, syntax errors, conditional inheritance, or vendor‑specific defaults can cause a mismatch that is not immediately visible in the output of show running-config.


Understanding RIB and FIB Divergence

Causes of Divergence After Policy Changes

  1. Partial Commit or Failed Validation – A policy edit may be accepted syntactically but rejected during semantic validation (e.g., referencing an undefined prefix‑list). The router rolls back the offending lines, leaving the previously active policy in the rendered config while the RIB still reflects the old routes.
  2. Asynchronous RIB‑FIB Refresh – Some platforms update the RIB immediately after a policy re‑apply but defer FIB reconstruction to a background process or to the next scheduled poll. During this window the FIB may still contain stale entries.
  3. Route‑Redistribution Loops or Dampening – A policy change that alters redistribution metrics can cause a route to be withdrawn from the RIB, but the FIB may retain a forwarding entry if the route is still present in a local RIB copy used by the forwarding ASIC.
  4. Hardware‑Specific FIB Limits – TCAM exhaustion or hash‑collision handling can cause the FIB to drop a prefix silently while the RIB keeps it, especially after a large batch of policy‑driven prefix additions.
  5. Control‑Plane vs. Data‑Plane Software Versions – In routers that separate the control plane (e.g., a Linux‑based FRR instance) from the forwarding ASIC, a mismatch in the version of the routing daemon versus the firmware that programs the FIB can lead to divergence.

Impact of Divergence on Network Routing


Declared Policy and Rendered Configuration Matching

Policy Configuration Examples

Cisco IOS‑XR

route-policy BGP-IN
  if destination in PFX-ALLOWED then
    set local-preference 200
    set community 65000:100 additive
  else
    drop
  endif
end-policy

Juniper Junos

policy-options {
    policy-statement BGP-IN {
        term ALLOW {
            from {
                prefix-list PFX-ALLOWED;
            }
            then {
                local-preference 200;
                community add 65000:100;
                accept;
            }
        }
        term DENY {
            then reject;
        }
    }
}

FRR (Linux)

route-map BGP-IN permit 10
 match ip address prefix-list PFX-ALLOWED
 set local-preference 200
 set community 65000:100 additive
!
route-map BGP-IN deny 20

Rendered Configuration Examples

After committing the above, the rendered config (as seen with show running-config | include route-policy|policy-options|route-map) should contain the exact same lines, assuming no syntax errors. Example output from IOS‑XR:

RP/0/RP0/CPU0:router# show running-config | include route-policy
route-policy BGP-IN
  if destination in PFX-ALLOWED then
    set local-preference 200
    set community 65000:100 additive
  else
    drop
  endif
end-policy

If the prefix‑list PFX-ALLOWED is missing, the commit will fail and the rendered config will not contain the new route-policy; the old policy remains active.

CLI Commands for Policy and Configuration Verification

PlatformVerify Declared IntentVerify Rendered Config
Cisco IOS‑XRshow running-config route-policy BGP-IN`show running-config
Juniper Junosshow configuration policy-options policy-statement BGP-IN`show configuration
FRRshow route-map BGP-IN`show running-config
Linux (iproute2)nft list table ip filter (if using nftables)nft list ruleset

A match between the declared intent block and the rendered config lines confirms that the policy has been successfully loaded into the active configuration.


Comparison Chain for Detecting Divergence

Step‑by‑Step Comparison Process

  1. Declare → Rendered – Verify that the policy text in the committed config matches the source of truth (e.g., Git repo, CI‑generated snippet). Any mismatch indicates a commit failure or partial apply.
  2. Rendered → RIB – Extract the effective policy from the rendered config (via parser or native show command) and simulate its effect on the RIB (or directly compare the RIB to the expected routes derived from the policy). Tools: show ip route, show route, show ip bgp.
  3. RIB → FIB – Compare each RIB entry’s next‑hop and outgoing interface to the corresponding FIB entry. Discrepancies indicate a failure in the RIB‑to‑FIB sync process.
  4. FIB → Data‑Plane (optional) – Validate hardware counters or packet‑traces to confirm that the FIB entry is actually being used for forwarding.

If steps 1 and 2 pass but step 3 fails, the root cause lies in the RIB‑FIB synchronization layer, not in the policy itself.

Example Comparison Chain Scenarios

Scenario A – Policy Accepted, RIB Updated, FIB Stale

  1. Declared policy: set local-preference 200 for PFX-ALLOWED.
  2. Rendered config matches declared policy (verified via show running-config).
  3. RIB shows the prefix with local‑preference 200 and next‑hop 10.0.0.2.
  4. FIB still shows the prefix with local‑preference 100 and next‑hop 10.0.0.1 (old policy).
    → Divergence detected at step 3.

Scenario B – Policy Rejected, Rendered Config Unchanged

  1. Declared policy references missing prefix‑list PFX-ALLOWED.
  2. Commit fails; rendered config retains old policy (no new route-policy lines).
  3. RIB still reflects old policy (no change).
  4. FIB matches RIB (both old).
    → Divergence detected at step 1 (declared ≠ rendered).

Scenario C – Hardware TCAM Exhaustion

  1. Declared and rendered policies both add 100k new prefixes.
  2. RIB receives all 100k prefixes (verified via show ip route | count).
  3. FIB shows only 90k entries; the missing 10k are silently dropped.
    → Divergence detected at step 3 despite correct policy and RIB.

Code Examples for Automating Comparison

Below is a Python snippet using Napalm to fetch rendered config, RIB, and FIB, then compare them. The example assumes Cisco IOS‑XR but can be adapted.

#!/usr/bin/env python3
import json
from napalm import get_network_driver

def fetch_data(device):
    driver = get_network_driver('iosxr')
    with driver(hostname=device['host'],
                username=device['user'],
                password=device['pwd']) as dev:
        # 1. Rendered config (as structured object)
        rendered = dev.get_config()['running']
        # 2. RIB (routing table)
        rib = dev.get_route_to('0.0.0.0/0')  # returns dict of all routes
        # 3. FIB (CEF table on IOS‑XR)
        fib = dev.get_fib()  # vendor‑specific; returns list of FIB entries
    return rendered, rib, fib

def compare_policy(rendered_cfg, expected_policy):
    # Simple string containment; in production use a proper parser (e.g., CiscoConfParse)
    return expected_policy in rendered_cfg

def compare_rib_fib(rib, fib):
    # Build a set of (prefix, next-hop) from RIB
    rib_set = {(entry['prefix'], entry['next_hop']) for entry in rib.values()}
    # Build same set from FIB (adjust keys per vendor)
    fib_set = {(f['prefix'], f['next_hop']) for f in fib}
    missing_in_fib = rib_set - fib_set
    extra_in_fib = fib_set - rib_set
    return missing_in_fib, extra_in_fib

if __name__ == '__main__':
    device = dict(host='router1.example.com', user='admin', pwd='secret')
    expected_policy = "route-policy BGP-IN"  # from source of truth
    rendered, rib, fib = fetch_data(device)

    # Step 1: Declared vs Rendered
    if not compare_policy(rendered, expected_policy):
        print("❌ Declared policy does NOT match rendered config")
    else:
        print("✅ Declared policy matches rendered config")

    # Step 3: RIB vs FIB
    missing, extra = compare_rib_fib(rib, fib)
    if missing:
        print(f"❌ RIB→FIB divergence: {len(missing)} prefixes missing in FIB")
        for p, nh in list(missing)[:5]:
            print(f"   {p} via {nh}")
    else:
        print("✅ RIB and FIB are consistent")

    if extra:
        print(f"⚠️  FIB contains {len(extra)} entries not present in RIB (possible stale hardware entries)")

The script prints a clear pass/fail for each link in the chain, enabling automated alerting when the RIB‑FIB link breaks while the policy‑config link stays intact.


Troubleshooting RIB and FIB Divergence

Identifying Divergence Using CLI Commands

PlatformCheck RIBCheck FIBTypical Divergence Symptom
Cisco IOS‑XRshow route ipv4 unicast
show bgp ipv4 unicast
show cef ipv4 prefix <x.x.x.x/len> detail
show cef ipv4 drops
show route shows a prefix, but show cef returns “%CEF: No entry found” or shows an older next‑hop.
Juniper Junosshow route
show bgp neighbor <ip> received-routes
show route forwarding-table destination <x.x.x.x/len>
show route forwarding-table match <x.x.x.x/len>
Forwarding table lacks the entry or shows a different next‑hop.
FRRshow ip route
show bgp ipv4 unicast
show ip fib (if FRR compiled with FIB support)
dpctl dump flows (OVS)
show ip route lists a prefix; show ip fib returns “No such entry”.
Linux (kernel)ip route show table mainip -d route show table <tid> or cat /proc/net/fib_trieKernel FIB missing routes that appear in ip route.

Procedure

  1. Identify the suspect prefix (e.g., from user complaint or monitoring alert).
  2. Run the RIB command to confirm the prefix exists and note its attributes (next‑hop, metric, local‑pref).
  3. Run the FIB command for the same prefix; compare next‑hop, outgoing interface, and any encapsulation labels.
  4. If they differ, check the FIB sync logs: show logging | include fib (IOS‑XR), show system processes detail | match fib (Junos), or dmesg | grep -i fib (Linux).

Analyzing Logs and System Messages

Look for timestamps that align with the policy change commit. A gap between the commit timestamp and the first FIB‑error log often points to asynchronous FIB reconstruction.

Common Issues and Solutions

IssueLikely CauseRemedy
FIB missing prefixes after large policy pushTCAM exhaustion or hash collisionIncrease TCAM allocation, summarize routes, or enable FIB compression if supported.
Stale next‑hop in FIB after route‑map changeAsynchronous FIB refreshForce a FIB rebuild (clear cef ipv4 on IOS‑XR, restart routing on Junos) or wait for the background refresh interval.
Policy commit fails silentlyUndefined referenced object (prefix‑list, community‑set)Validate all referenced objects before commit; use commit check or equivalent.
FIB shows extra entries not in RIBHardware aging or stale ASIC cacheClear FIB (clear ip fib on FRR, clear route forwarding-table on Junos) and monitor for re‑population.
Divergence only on specific line cardsLine‑card firmware mismatchUpgrade line‑card firmware to match control‑plane version; verify compatibility matrix.

By following the comparison chain and using the CLI/log‑based checks above, operators can quickly isolate whether a policy‑configuration mismatch or a RIB‑FIB synchronization problem is responsible for observed forwarding anomalies.


Share this post on:

Previous Post
Deliberately isolating a route reflector to calibrate budgets
Next Post
Reusable Harnesses Without Topology-Coupled Assertions