{ "cells": [ { "cell_type": "markdown", "metadata": {}, "source": [ "## Introduction to Route Computation and Analysis using Batfish" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Network engineers routinely need to validate routing and forwarding in the network. They often do that by connecting to multiple network devices and executing a series of `show route` commands. This distributed debugging is highly complex even in a moderately-sized network. Batfish makes this task extremely simple by providing an easy-to-query, centralized view of routing tables in the network. \n", "\n", "In this notebook, we will look at how you can extract routing information from Batfish.\n", "\n", "Check out a video demo of this notebook [here](https://www.youtube.com/watch?v=AutkFa0xUxg)." ] }, { "cell_type": "code", "execution_count": 1, "metadata": {}, "outputs": [], "source": [ "# Import packages\n", "%run startup.py\n", "bf = Session(host=\"localhost\")" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Initializing the Network and Snapshot" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "`SNAPSHOT_PATH` below can be updated to point to a custom snapshot directory, see the [Batfish instructions](https://github.com/batfish/batfish/wiki/Packaging-snapshots-for-analysis) for how to package data for analysis.
\n", "More example networks are available in the [networks](https://github.com/batfish/batfish/tree/master/networks) folder of the Batfish repository." ] }, { "cell_type": "code", "execution_count": 2, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "'example_snapshot'" ] }, "execution_count": 2, "metadata": {}, "output_type": "execute_result" } ], "source": [ "# Initialize a network and snapshot\n", "NETWORK_NAME = \"example_network\"\n", "SNAPSHOT_NAME = \"example_snapshot\"\n", "\n", "SNAPSHOT_PATH = \"networks/example\"\n", "\n", "bf.set_network(NETWORK_NAME)\n", "bf.init_snapshot(SNAPSHOT_PATH, name=SNAPSHOT_NAME, overwrite=True)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "The network snapshot that we initialized above is illustrated below. You can download/view devices' configuration files [here](https://github.com/batfish/pybatfish/tree/master/jupyter_notebooks/networks/example).\n", "\n", "![example-network](https://raw.githubusercontent.com/batfish/pybatfish/master/jupyter_notebooks/networks/example/example-network.png)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "All of the information we will show you in this notebook is dynamically computed by Batfish based on the configuration files for the network devices." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### View Routing Tables for ALL devices and ALL VRFs\n", "Batfish makes **all** routing tables in the network easily accessible. Let's take a look at how you can retrieve the specific information you want." ] }, { "cell_type": "code", "execution_count": 3, "metadata": {}, "outputs": [], "source": [ "# Get routing tables for all nodes and VRFs\n", "routes_all = bf.q.routes().answer().frame()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "We are not going to print this table as it has a large number of entries. " ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### View Routing Tables for default VRF on AS1 border routers" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "There are 2 ways that we can get the desired subset of data: " ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Option 1) Only request that information from Batfish by passing in parameters into the `routes()` question. This is useful to do when you need to reduce the amount of data being returned, but is limited to regex filtering based on VRF, Node, Protocol and Network." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Option 2) Filter the output of the `routes()` question using the Pandas [APIs](https://pandas.pydata.org/pandas-docs/stable/generated/pandas.DataFrame.filter.html)." ] }, { "cell_type": "code", "execution_count": 4, "metadata": {}, "outputs": [], "source": [ "?bf.q.routes" ] }, { "cell_type": "code", "execution_count": 5, "metadata": {}, "outputs": [], "source": [ "# Get the routing table for the 'default' VRF on border routers of as1\n", "# using BF parameters\n", "routes_as1border = bf.q.routes(nodes=\"/as1border/\", vrfs=\"default\").answer().frame()" ] }, { "cell_type": "code", "execution_count": 6, "metadata": {}, "outputs": [ { "data": { "text/html": [ "
\n", "\n", "\n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", "
NodeVRFNetworkNext_HopProtocolMetricAdmin_DistanceTag
0as1border1default1.0.1.0/24interface GigabitEthernet0/0connected00None
1as1border1default1.0.1.1/32interface GigabitEthernet0/0local00None
2as1border1default1.0.2.0/24interface GigabitEthernet0/0 ip 1.0.1.2ospf2110None
3as1border1default1.1.1.1/32interface Loopback0connected00None
4as1border1default1.2.2.2/32interface GigabitEthernet0/0 ip 1.0.1.2ospf3110None
5as1border1default1.10.1.1/32interface GigabitEthernet0/0 ip 1.0.1.2ospf2110None
6as1border1default2.128.0.0/16ip 10.12.11.2bgp5020None
7as1border1default3.0.1.0/24ip 10.13.22.3ibgp50200None
8as1border1default3.0.2.0/24ip 10.13.22.3ibgp50200None
9as1border1default10.12.11.0/24interface GigabitEthernet1/0connected00None
10as1border1default10.12.11.1/32interface GigabitEthernet1/0local00None
11as1border1default10.13.22.0/24interface GigabitEthernet0/0 ip 1.0.1.2ospfE220110None
12as1border1default10.14.22.0/24interface GigabitEthernet0/0 ip 1.0.1.2ospfE220110None
13as1border2default1.0.1.0/24interface GigabitEthernet1/0 ip 1.0.2.2ospf2110None
14as1border2default1.0.2.0/24interface GigabitEthernet1/0connected00None
15as1border2default1.0.2.1/32interface GigabitEthernet1/0local00None
16as1border2default1.1.1.1/32interface GigabitEthernet1/0 ip 1.0.2.2ospf3110None
17as1border2default1.2.2.2/32interface Loopback0connected00None
18as1border2default1.10.1.1/32interface GigabitEthernet1/0 ip 1.0.2.2ospf2110None
19as1border2default2.128.0.0/16ip 10.12.11.2ibgp50200None
20as1border2default3.0.1.0/24ip 10.13.22.3bgp5020None
21as1border2default3.0.2.0/24ip 10.13.22.3bgp5020None
22as1border2default10.12.11.0/24interface GigabitEthernet1/0 ip 1.0.2.2ospfE220110None
23as1border2default10.13.22.0/24interface GigabitEthernet0/0connected00None
24as1border2default10.13.22.1/32interface GigabitEthernet0/0local00None
25as1border2default10.14.22.0/24interface GigabitEthernet2/0connected00None
26as1border2default10.14.22.1/32interface GigabitEthernet2/0local00None
\n", "
" ], "text/plain": [ " Node VRF Network \\\n", "0 as1border1 default 1.0.1.0/24 \n", "1 as1border1 default 1.0.1.1/32 \n", "2 as1border1 default 1.0.2.0/24 \n", "3 as1border1 default 1.1.1.1/32 \n", "4 as1border1 default 1.2.2.2/32 \n", "5 as1border1 default 1.10.1.1/32 \n", "6 as1border1 default 2.128.0.0/16 \n", "7 as1border1 default 3.0.1.0/24 \n", "8 as1border1 default 3.0.2.0/24 \n", "9 as1border1 default 10.12.11.0/24 \n", "10 as1border1 default 10.12.11.1/32 \n", "11 as1border1 default 10.13.22.0/24 \n", "12 as1border1 default 10.14.22.0/24 \n", "13 as1border2 default 1.0.1.0/24 \n", "14 as1border2 default 1.0.2.0/24 \n", "15 as1border2 default 1.0.2.1/32 \n", "16 as1border2 default 1.1.1.1/32 \n", "17 as1border2 default 1.2.2.2/32 \n", "18 as1border2 default 1.10.1.1/32 \n", "19 as1border2 default 2.128.0.0/16 \n", "20 as1border2 default 3.0.1.0/24 \n", "21 as1border2 default 3.0.2.0/24 \n", "22 as1border2 default 10.12.11.0/24 \n", "23 as1border2 default 10.13.22.0/24 \n", "24 as1border2 default 10.13.22.1/32 \n", "25 as1border2 default 10.14.22.0/24 \n", "26 as1border2 default 10.14.22.1/32 \n", "\n", " Next_Hop Protocol Metric Admin_Distance \\\n", "0 interface GigabitEthernet0/0 connected 0 0 \n", "1 interface GigabitEthernet0/0 local 0 0 \n", "2 interface GigabitEthernet0/0 ip 1.0.1.2 ospf 2 110 \n", "3 interface Loopback0 connected 0 0 \n", "4 interface GigabitEthernet0/0 ip 1.0.1.2 ospf 3 110 \n", "5 interface GigabitEthernet0/0 ip 1.0.1.2 ospf 2 110 \n", "6 ip 10.12.11.2 bgp 50 20 \n", "7 ip 10.13.22.3 ibgp 50 200 \n", "8 ip 10.13.22.3 ibgp 50 200 \n", "9 interface GigabitEthernet1/0 connected 0 0 \n", "10 interface GigabitEthernet1/0 local 0 0 \n", "11 interface GigabitEthernet0/0 ip 1.0.1.2 ospfE2 20 110 \n", "12 interface GigabitEthernet0/0 ip 1.0.1.2 ospfE2 20 110 \n", "13 interface GigabitEthernet1/0 ip 1.0.2.2 ospf 2 110 \n", "14 interface GigabitEthernet1/0 connected 0 0 \n", "15 interface GigabitEthernet1/0 local 0 0 \n", "16 interface GigabitEthernet1/0 ip 1.0.2.2 ospf 3 110 \n", "17 interface Loopback0 connected 0 0 \n", "18 interface GigabitEthernet1/0 ip 1.0.2.2 ospf 2 110 \n", "19 ip 10.12.11.2 ibgp 50 200 \n", "20 ip 10.13.22.3 bgp 50 20 \n", "21 ip 10.13.22.3 bgp 50 20 \n", "22 interface GigabitEthernet1/0 ip 1.0.2.2 ospfE2 20 110 \n", "23 interface GigabitEthernet0/0 connected 0 0 \n", "24 interface GigabitEthernet0/0 local 0 0 \n", "25 interface GigabitEthernet2/0 connected 0 0 \n", "26 interface GigabitEthernet2/0 local 0 0 \n", "\n", " Tag \n", "0 None \n", "1 None \n", "2 None \n", "3 None \n", "4 None \n", "5 None \n", "6 None \n", "7 None \n", "8 None \n", "9 None \n", "10 None \n", "11 None \n", "12 None \n", "13 None \n", "14 None \n", "15 None \n", "16 None \n", "17 None \n", "18 None \n", "19 None \n", "20 None \n", "21 None \n", "22 None \n", "23 None \n", "24 None \n", "25 None \n", "26 None " ] }, "execution_count": 6, "metadata": {}, "output_type": "execute_result" } ], "source": [ "# Get the routing table for the 'default' VRF on border routers of as1\n", "# using Pandas filtering\n", "routes_as1border = routes_all[(routes_all['Node'].str.contains('as1border')) & (routes_all['VRF'] == 'default')]\n", "routes_as1border" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### View BGP learnt routes for default VRF on AS1 border routers" ] }, { "cell_type": "code", "execution_count": 7, "metadata": {}, "outputs": [], "source": [ "# Getting BGP routes in the routing table for the 'default' VRF on border routers of as1\n", "# using BF parameters\n", "routes_as1border_bgp = bf.q.routes(nodes=\"/as1border/\", vrfs=\"default\", protocols=\"bgp\").answer().frame()" ] }, { "cell_type": "code", "execution_count": 8, "metadata": {}, "outputs": [ { "data": { "text/html": [ "
\n", "\n", "\n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", "
NodeVRFNetworkNext_HopProtocolMetricAdmin_DistanceTag
6as1border1default2.128.0.0/16ip 10.12.11.2bgp5020None
20as1border2default3.0.1.0/24ip 10.13.22.3bgp5020None
21as1border2default3.0.2.0/24ip 10.13.22.3bgp5020None
\n", "
" ], "text/plain": [ " Node VRF Network Next_Hop Protocol Metric \\\n", "6 as1border1 default 2.128.0.0/16 ip 10.12.11.2 bgp 50 \n", "20 as1border2 default 3.0.1.0/24 ip 10.13.22.3 bgp 50 \n", "21 as1border2 default 3.0.2.0/24 ip 10.13.22.3 bgp 50 \n", "\n", " Admin_Distance Tag \n", "6 20 None \n", "20 20 None \n", "21 20 None " ] }, "execution_count": 8, "metadata": {}, "output_type": "execute_result" } ], "source": [ "# Geting BGP routes in the routing table for the 'default' VRF on border routers of as1\n", "# using Pandas filtering\n", "routes_as1border_bgp = routes_all[(routes_all['Node'].str.contains('as1border')) & (routes_all['VRF'] == 'default') & (routes_all['Protocol'] == 'bgp')]\n", "routes_as1border_bgp" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### View BGP learnt routes for ALL VRFs on ALL routers with Metric >=50\n", "We cannot pass in `metric` as a parameter to Batfish, so this task is best handled with the Pandas API." ] }, { "cell_type": "code", "execution_count": 9, "metadata": {}, "outputs": [ { "data": { "text/html": [ "
\n", "\n", "\n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", "
NodeVRFNetworkNext_HopProtocolMetricAdmin_DistanceTag
6as1border1default2.128.0.0/16ip 10.12.11.2bgp5020None
20as1border2default3.0.1.0/24ip 10.13.22.3bgp5020None
21as1border2default3.0.2.0/24ip 10.13.22.3bgp5020None
40as2border1default1.0.1.0/24ip 10.12.11.1bgp5020None
41as2border1default1.0.2.0/24ip 10.12.11.1bgp5020None
106as2border2default3.0.1.0/24ip 10.23.21.3bgp5020None
107as2border2default3.0.2.0/24ip 10.23.21.3bgp5020None
182as2dept1default1.0.1.0/24ip 2.34.101.3bgp5020None
183as2dept1default1.0.1.0/24ip 2.34.201.3bgp5020None
184as2dept1default1.0.2.0/24ip 2.34.101.3bgp5020None
185as2dept1default1.0.2.0/24ip 2.34.201.3bgp5020None
195as2dept1default3.0.1.0/24ip 2.34.101.3bgp5020None
196as2dept1default3.0.1.0/24ip 2.34.201.3bgp5020None
197as2dept1default3.0.2.0/24ip 2.34.101.3bgp5020None
198as2dept1default3.0.2.0/24ip 2.34.201.3bgp5020None
226as2dist1default2.128.0.0/24ip 2.34.101.4bgp5020None
227as2dist1default2.128.1.0/24ip 2.34.101.4bgp5020None
261as2dist2default2.128.0.0/24ip 2.34.201.4bgp5020None
262as2dist2default2.128.1.0/24ip 2.34.201.4bgp5020None
271as3border1default2.128.0.0/16ip 10.23.21.2bgp5020None
281as3border2default1.0.1.0/24ip 10.13.22.1bgp5020None
282as3border2default1.0.2.0/24ip 10.13.22.1bgp5020None
\n", "
" ], "text/plain": [ " Node VRF Network Next_Hop Protocol Metric \\\n", "6 as1border1 default 2.128.0.0/16 ip 10.12.11.2 bgp 50 \n", "20 as1border2 default 3.0.1.0/24 ip 10.13.22.3 bgp 50 \n", "21 as1border2 default 3.0.2.0/24 ip 10.13.22.3 bgp 50 \n", "40 as2border1 default 1.0.1.0/24 ip 10.12.11.1 bgp 50 \n", "41 as2border1 default 1.0.2.0/24 ip 10.12.11.1 bgp 50 \n", "106 as2border2 default 3.0.1.0/24 ip 10.23.21.3 bgp 50 \n", "107 as2border2 default 3.0.2.0/24 ip 10.23.21.3 bgp 50 \n", "182 as2dept1 default 1.0.1.0/24 ip 2.34.101.3 bgp 50 \n", "183 as2dept1 default 1.0.1.0/24 ip 2.34.201.3 bgp 50 \n", "184 as2dept1 default 1.0.2.0/24 ip 2.34.101.3 bgp 50 \n", "185 as2dept1 default 1.0.2.0/24 ip 2.34.201.3 bgp 50 \n", "195 as2dept1 default 3.0.1.0/24 ip 2.34.101.3 bgp 50 \n", "196 as2dept1 default 3.0.1.0/24 ip 2.34.201.3 bgp 50 \n", "197 as2dept1 default 3.0.2.0/24 ip 2.34.101.3 bgp 50 \n", "198 as2dept1 default 3.0.2.0/24 ip 2.34.201.3 bgp 50 \n", "226 as2dist1 default 2.128.0.0/24 ip 2.34.101.4 bgp 50 \n", "227 as2dist1 default 2.128.1.0/24 ip 2.34.101.4 bgp 50 \n", "261 as2dist2 default 2.128.0.0/24 ip 2.34.201.4 bgp 50 \n", "262 as2dist2 default 2.128.1.0/24 ip 2.34.201.4 bgp 50 \n", "271 as3border1 default 2.128.0.0/16 ip 10.23.21.2 bgp 50 \n", "281 as3border2 default 1.0.1.0/24 ip 10.13.22.1 bgp 50 \n", "282 as3border2 default 1.0.2.0/24 ip 10.13.22.1 bgp 50 \n", "\n", " Admin_Distance Tag \n", "6 20 None \n", "20 20 None \n", "21 20 None \n", "40 20 None \n", "41 20 None \n", "106 20 None \n", "107 20 None \n", "182 20 None \n", "183 20 None \n", "184 20 None \n", "185 20 None \n", "195 20 None \n", "196 20 None \n", "197 20 None \n", "198 20 None \n", "226 20 None \n", "227 20 None \n", "261 20 None \n", "262 20 None \n", "271 20 None \n", "281 20 None \n", "282 20 None " ] }, "execution_count": 9, "metadata": {}, "output_type": "execute_result" } ], "source": [ "routes_filtered = routes_all[(routes_all['Protocol'] == 'bgp') & (routes_all['Metric'] >= 50)]\n", "routes_filtered" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### View the routing entries for network 1.0.2.0/24 on ALL routers in ALL VRFs" ] }, { "cell_type": "code", "execution_count": 10, "metadata": {}, "outputs": [], "source": [ "# grab the route table entry for network 1.0.2.0/24 from all routers in all VRFs\n", "# using BF parameters\n", "routes_filtered = bf.q.routes(network=\"1.0.2.0/24\").answer().frame()" ] }, { "cell_type": "code", "execution_count": 11, "metadata": {}, "outputs": [ { "data": { "text/html": [ "
\n", "\n", "\n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", "
NodeVRFNetworkNext_HopProtocolMetricAdmin_DistanceTag
2as1border1default1.0.2.0/24interface GigabitEthernet0/0 ip 1.0.1.2ospf2110None
14as1border2default1.0.2.0/24interface GigabitEthernet1/0connected00None
29as1core1default1.0.2.0/24interface GigabitEthernet0/0connected00None
41as2border1default1.0.2.0/24ip 10.12.11.1bgp5020None
77as2border2default1.0.2.0/24ip 10.12.11.1ibgp50200None
113as2core1default1.0.2.0/24ip 10.12.11.1ibgp50200None
148as2core2default1.0.2.0/24ip 10.12.11.1ibgp50200None
184as2dept1default1.0.2.0/24ip 2.34.101.3bgp5020None
185as2dept1default1.0.2.0/24ip 2.34.201.3bgp5020None
200as2dist1default1.0.2.0/24ip 10.12.11.1ibgp50200None
235as2dist2default1.0.2.0/24ip 10.12.11.1ibgp50200None
270as3border1default1.0.2.0/24ip 10.13.22.1ibgp50200None
282as3border2default1.0.2.0/24ip 10.13.22.1bgp5020None
294as3core1default1.0.2.0/24ip 10.13.22.1ibgp50200None
\n", "
" ], "text/plain": [ " Node VRF Network Next_Hop \\\n", "2 as1border1 default 1.0.2.0/24 interface GigabitEthernet0/0 ip 1.0.1.2 \n", "14 as1border2 default 1.0.2.0/24 interface GigabitEthernet1/0 \n", "29 as1core1 default 1.0.2.0/24 interface GigabitEthernet0/0 \n", "41 as2border1 default 1.0.2.0/24 ip 10.12.11.1 \n", "77 as2border2 default 1.0.2.0/24 ip 10.12.11.1 \n", "113 as2core1 default 1.0.2.0/24 ip 10.12.11.1 \n", "148 as2core2 default 1.0.2.0/24 ip 10.12.11.1 \n", "184 as2dept1 default 1.0.2.0/24 ip 2.34.101.3 \n", "185 as2dept1 default 1.0.2.0/24 ip 2.34.201.3 \n", "200 as2dist1 default 1.0.2.0/24 ip 10.12.11.1 \n", "235 as2dist2 default 1.0.2.0/24 ip 10.12.11.1 \n", "270 as3border1 default 1.0.2.0/24 ip 10.13.22.1 \n", "282 as3border2 default 1.0.2.0/24 ip 10.13.22.1 \n", "294 as3core1 default 1.0.2.0/24 ip 10.13.22.1 \n", "\n", " Protocol Metric Admin_Distance Tag \n", "2 ospf 2 110 None \n", "14 connected 0 0 None \n", "29 connected 0 0 None \n", "41 bgp 50 20 None \n", "77 ibgp 50 200 None \n", "113 ibgp 50 200 None \n", "148 ibgp 50 200 None \n", "184 bgp 50 20 None \n", "185 bgp 50 20 None \n", "200 ibgp 50 200 None \n", "235 ibgp 50 200 None \n", "270 ibgp 50 200 None \n", "282 bgp 50 20 None \n", "294 ibgp 50 200 None " ] }, "execution_count": 11, "metadata": {}, "output_type": "execute_result" } ], "source": [ "# grab the route table entry for network 1.0.2.0/24 from all routers in all VRFs\n", "# using Pandas filtering\n", "routes_filtered = routes_all[routes_all['Network'] == \"1.0.2.0/24\"]\n", "routes_filtered" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Using Panda's filtering it is easy to retrieve the list of nodes which have the network in the routing table for at least 1 VRF. This type of processing should always be done using the Pandas APIs." ] }, { "cell_type": "code", "execution_count": 12, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "['as1border1', 'as1border2', 'as1core1', 'as2border1', 'as2border2', 'as2core1', 'as2core2', 'as2dept1', 'as2dist1', 'as2dist2', 'as3border1', 'as3border2', 'as3core1']\n" ] } ], "source": [ "# Get the list of nodes that have the network 1.0.2.0/24 in at least 1 VRF\n", "# the .unique function removes duplicate entries that would have been returned if the network was in multiple VRFs on a node or there were\n", "# multiple route entries for the network (ECMP)\n", "print(sorted(routes_filtered[\"Node\"].unique()))" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Now we will retrieve the list of nodes that do NOT have this prefix in their routing table. This is easy to do with the Pandas [groupby](https://pandas.pydata.org/pandas-docs/stable/generated/pandas.DataFrame.groupby.html) and [filter](https://pandas.pydata.org/pandas-docs/stable/generated/pandas.core.groupby.DataFrameGroupBy.filter.html) functions." ] }, { "cell_type": "code", "execution_count": 13, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "['host1', 'host2']\n" ] } ], "source": [ "# Group all routes by Node and filter for those that don't have '1.0.2.0/24'\n", "routes_filtered = routes_all.groupby('Node').filter(lambda x: all(x['Network'] != '1.0.2.0/24'))\n", "\n", "# Get the unique node names and sort the list\n", "print(sorted(routes_filtered[\"Node\"].unique()))" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "The only devices that do not have a route to **1.0.2.0/24** are the 2 hosts in the snapshot. This is expected, as they should just have a default route. Let's verify that." ] }, { "cell_type": "code", "execution_count": 14, "metadata": {}, "outputs": [ { "data": { "text/html": [ "
\n", "\n", "\n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", "
NodeVRFNetworkNext_HopProtocolMetricAdmin_DistanceTag
309host1default0.0.0.0/0interface eth0 ip 2.128.0.1static01None
310host1default2.128.0.0/24interface eth0connected00None
311host1default2.128.0.101/32interface eth0local00None
312host2default0.0.0.0/0interface eth0 ip 2.128.1.1static01None
313host2default2.128.1.0/24interface eth0connected00None
314host2default2.128.1.101/32interface eth0local00None
\n", "
" ], "text/plain": [ " Node VRF Network Next_Hop Protocol \\\n", "309 host1 default 0.0.0.0/0 interface eth0 ip 2.128.0.1 static \n", "310 host1 default 2.128.0.0/24 interface eth0 connected \n", "311 host1 default 2.128.0.101/32 interface eth0 local \n", "312 host2 default 0.0.0.0/0 interface eth0 ip 2.128.1.1 static \n", "313 host2 default 2.128.1.0/24 interface eth0 connected \n", "314 host2 default 2.128.1.101/32 interface eth0 local \n", "\n", " Metric Admin_Distance Tag \n", "309 0 1 None \n", "310 0 0 None \n", "311 0 0 None \n", "312 0 1 None \n", "313 0 0 None \n", "314 0 0 None " ] }, "execution_count": 14, "metadata": {}, "output_type": "execute_result" } ], "source": [ "routes_all[routes_all['Node'].str.contains('host')]" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "With Batfish and Pandas you can easily retrieve the exact information you are looking for from the routing tables on ANY or ALL network devices for ANY or ALL VRFs." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "This concludes the notebook. To recap, in this notebook we covered the foundational tasks for route analysis:\n", "\n", "1. How to get routes at all nodes in the network or only at a subset of them\n", "2. How to retrieve routing entries that match a specific protocol or metric\n", "3. How to find which nodes have an entry for a prefix or which ones do not\n" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "We hope you found this notebook useful and informative. Future notebooks will dive into more advanced topics like path analysis, debugging ACLs and firewall rules, validating routing policy, etc.. so stay tuned! " ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Want to know more? " ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Reach out to us through [Slack](https://join.slack.com/t/batfish-org/shared_invite/enQtMzA0Nzg2OTAzNzQ1LTcyYzY3M2Q0NWUyYTRhYjdlM2IzYzRhZGU1NWFlNGU2MzlhNDY3OTJmMDIyMjQzYmRlNjhkMTRjNWIwNTUwNTQ`) or [GitHub](https://github.com/batfish/batfish) to learn more, or send feedback." ] } ], "metadata": { "hide_input": false, "kernelspec": { "display_name": "Python 3", "language": "python", "name": "python3" }, "language_info": { "codemirror_mode": { "name": "ipython", "version": 3 }, "file_extension": ".py", "mimetype": "text/x-python", "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", "version": "3.14.4" } }, "nbformat": 4, "nbformat_minor": 2 }