Introduction to Route-Leak Validation
Overview of Route-Leak Issues
A route leak occurs when BGP prefixes learned from one peer are advertised to another peer where they are not intended, violating the intended routing policy. Common causes include misconfigured import/export maps, missing route-filters, or incorrect AS-PATH prepending. In a production fabric, even a single leaked prefix can attract transit traffic, cause sub-optimal routing, or trigger black-holing if the leaked route is later filtered downstream. The impact is amplified in multi-tenant data-center fabrics or Internet Exchange points where leakage can affect unrelated customers.
Importance of Validation Workflows
Validating that a configuration change does not introduce route leaks before it is merged protects service reliability and reduces mean-time-to-repair. A repeatable validation workflow lets operators:
- Detect leaks early in the development cycle.
- Provide deterministic evidence that the intended policy (e.g., “no transit between AS 65001 and AS 65002”) holds.
- Reduce reliance on manual peer-review, which is error-prone and does not scale with topology complexity.
Current Workflow Limitations
Brittleness in Existing Validation Processes
Many teams rely on ad-hoc scripts that:
- Pull the latest configuration from a Git repo.
- Render Jinja2 templates on a static lab box.
- Run a FRR or Cisco-like daemon in a single VM and grep the BGP table for unexpected prefixes. These scripts break when:
- The underlying OS image changes (different FRR version, different kernel routing behavior).
- The lab box lacks the required dependencies (e.g.,
iproute2,frrpackages). - The script assumes a fixed interface naming scheme that does not survive container recreation.
Inconsistencies in Reproducibility
Because the validation environment is often a mutable VM or bare-metal host, successive runs can yield different results:
- Leftover routes or ARP entries from previous runs affect BGP convergence.
- Timing-dependent race conditions cause false positives/negatives.
- No version-controlled snapshot of the lab topology exists, making it impossible to rerun the exact same test after a code change.
Challenges in Scaling and Maintenance
- Adding a new device type (e.g., Juniper vMX) requires rewriting the script’s device-specific CLI parsing.
- Parallel execution is limited by the single VM’s CPU and memory.
- Maintaining a growing library of one-off validation scripts becomes a source of technical debt; each script must be updated when the network model evolves.
Containerlab Overview
Introduction to Containerlab
Containerlab is an open-source tool that defines and launches container-based network labs using a simple YAML topology file. It leverages Docker (or Podman) as the runtime and provides built-in support for a variety of network OS images via the vrnetlab project (FRR, Cisco-XR, Juniper-vJunos, Arista-cEOS, etc.).
Containerlab Architecture and Components
- Clab YAML – declarative description of nodes, links, and node-specific settings (image, environment variables, exposed ports, etc.).
- Runtime Engine – talks to the Docker daemon to create containers, veth pairs, and bridge networks that emulate point-to-point links.
- CLI –
containerlab deploy,containerlab inspect,containerlab destroy,containerlab exec, andcontainerlab savefor snapshotting. - Extension Points – users can attach custom configuration scripts via the
configblock or mount volumes to inject startup scripts.
Benefits of Using Containerlab for Networking Testing
- Immutability – each
deploycreates a fresh set of containers; destroying the lab removes all state. - Reproducibility – the topology file is version-controlled; the same file yields identical container names, IP addressing, and link layout.
- Isolation – each lab runs in its own Docker network, preventing interference with host networking or other labs.
- Scalability – limited only by the host’s Docker resources; multiple labs can run in parallel on a CI worker.
- Vendor-agnostic – the same topology file can describe FRR, Cisco, Juniper, or Arista nodes, enabling multi-vendor validation without rewriting test harnesses.
Batfish Overview
Introduction to Batfish
Batfish is a network configuration analysis platform that builds a formal model of the network from vendor-agnostic configuration files and answers questions about behavior (reachability, routing loops, filter effects, etc.). It supports data-plane verification, intent verification, and regression testing.
Batfish Architecture and Components
- Parser – converts vendor-specific configuration (e.g., FRR
frr.conf, Cisco IOS, Juniper JUNOS) into a vendor-neutral representation. - Fact Database – stores derived facts such as interface IP addresses, BGP peer groups, route-maps, and access-lists.
- Question Engine – evaluates predefined or custom questions (implemented in Java/Scala) against the fact database.
- Assertion Framework – allows users to assert that a question’s result satisfies a predicate; failures are reported as test failures.
- CLI / API – the
bfcommand-line tool and a REST API enable automation from CI pipelines.
Benefits of Using Batfish for Network Validation
- Exhaustive Analysis – Batfish examines all possible paths in the control plane, not just a single observed state.
- Policy-Centric – questions can be phrased in terms of intent (e.g., “no route leaks from AS 65001 to AS 65002”).
- Regression-Ready – the same set of questions can be run against successive configuration snapshots to detect drift.
- Language-Agnostic – configurations can be supplied as plain text files; no need to run actual devices.
- Integrates with CI –
bfreturns non-zero exit codes on assertion failures, making it a natural gate in a CI job.
Refactoring the Validation Workflow
Designing a Reproducible Test Environment
- Topology as Code – store a Containerlab YAML file (
clab_route_leak.yml) that defines:- Two FRR nodes representing AS 65001 and AS 65002.
- A third FRR node acting as a transit provider (AS 65003) that should not receive routes from AS 65001 via AS 65002.
- Point-to-point links using
kind: vethwith explicit IPv4/IPv6 addressing.
- Configuration Generation – use a lightweight templating tool (e.g.,
jinja2orgomplate) to render FRR configuration files from a values file (values.yaml). The rendered configs are mounted into each container at/etc/frr/frr.conf. - Batfish Snapshot – after the lab is up, execute
containerlab exec <node> -- vtysh -c "show running-config"to capture the running config, or directly copy the mounted config files into a Batfish snapshot directory. - Assertion – run a Batfish question that checks for route leaks and assert the result is empty.
Integrating Containerlab and Batfish
The integration consists of three stages:
- Deploy –
containerlab deploy -t clab_route_leak.yml. - Collect – extract configurations from each FRR container (via
docker cporcontainerlab exec). - Validate – upload the configs to Batfish, run the
bgpProcessquestion, and apply the built-inassertNoRouteLeaksassertion. All stages are scripted in a Makefile or a CI job definition, ensuring that a single command (make test-route-leak) performs the full cycle.
Example Code for Containerlab and Batfish Integration
clab_route_leak.yml
name: clab_route_leak
topology:
nodes:
as1:
kind: frr
image: frrouting/frr:9.0
config: ./configs/as1.frr
as2:
kind: frr
image: frrouting/frr:9.0
config: ./configs/as2.frr
transit:
kind: frr
image: frrouting/frr:9.0
config: ./configs/transit.frr
links:
- endpoints: ["as1:eth1", "as2:eth1"]
- endpoints: ["as2:eth2", "transit:eth1"]
- endpoints: ["as1:eth2", "transit:eth2"]
Makefile excerpt
.PHONY: test-route-leak
test-route-leak:
@echo "Deploying lab..."
containerlab deploy -t clab_route_leak.yml
@echo "Collecting configs..."
mkdir -p snapshot
docker cp $(shell docker ps -qf "name=clab_route_leak-as1"):/etc/frr/frr.conf snapshot/as1.frr
docker cp $(shell docker ps -qf "name=clab_route_leak-as2"):/etc/frr/frr.conf snapshot/as2.frr
docker cp $(shell docker ps -qf "name=clab_route_leak-transit"):/etc/frr/frr.conf snapshot/transit.frr
@echo "Uploading to Batfish..."
bf upload -n route_leak_test -r snapshot
@echo "Running Batfish assertion..."
bf assert -n route_leak_test -t assertNoRouteLeaks
@echo "Tearing down lab..."
containerlab destroy -t clab_route_leak.yml
The bf assert command returns a non-zero exit code if any route leak is detected, causing the Make target to fail.
Implementing the Regression Harness
Writing Test Cases for Route-Leak Validation
A Batfish test case is essentially a directory containing:
conf/– the configuration files.testrig.yaml– optional, defines the reference library and external data.questions/– custom questions (if needed).assertions/– YAML files that reference built-in assertions. assertions/route_leak_assert.yaml
- name: no_route_leaks
type: assertNoRouteLeaks
params:
# Optional: restrict to a specific VRF or address family
# vrf: default
# addressFamily: ipv4
When Batfish evaluates this assertion, it examines the BGP RIBs learned by each node and verifies that no prefix originated from a peer in a different AS is advertised to a peer where the export policy would prohibit it.
Using CLI for Test Execution and Automation
The bf CLI provides the following core commands for a regression harness:
bf init– creates a new Batfish workspace.bf upload -n <name> -r <path>– uploads a snapshot.bf question -n <name> -q <question>– runs a question and prints JSON.bf assert -n <name> -t <assertion>– runs an assertion; exits with code 0 on success, non-zero on failure.bf delete -n <name>– removes a snapshot to free resources. A typical CI step might look like:
bf upload -n pr_${{ github.sha }} -r ./snapshot
bf assert -n pr_${{ github.sha }} -t assertNoRouteLeaks || (echo "Route leak detected!" && exit 1)
bf delete -n pr_${{ github.sha }}
Example CLI Commands for Test Execution
# 1. Start the lab
containerlab deploy -t clab_route_leak.yml
# 2. Grab configs (alternative to docker cp: use containerlab exec)
containerlab exec as1 -- vtysh -c "show running-config" > snapshot/as1.frr
containerlab exec as2 -- vtysh -c "show running-config" > snapshot/as2.frr
containerlab exec transit -- vtysh -c "show running-config" > snapshot/transit.frr
# 3. Upload to Batfish
bf upload -n route_leak_snap -r snapshot
# 4. Run the built-in assertion
bf assert -n route_leak_snap -t assertNoRouteLeaks
# Echo result
echo "Batfish assertion exit code: $?"
# 5. Cleanup
containerlab destroy -t clab_route_leak.yml
bf delete -n route_leak_snap
Troubleshooting Common Issues
Debugging Containerlab and Batfish Integration
- Container startup failures – check
containerlab inspect -t clab_route_leak.ymlto see if any node is in afailedstate