BGP and routing table
Let's take an example in which we need to configure BGP, validate if a session is up, and report the details for the same. In our example, we would take two routers (as a prerequisite, both routers are able to ping each other) as follows:
As we see R2 and testrouter are able to ping each other using an IP address of the FastEthernet0/0 interface of each other.
The next step is a very basic configuration of BGP (in our case, we use the Autonomous System (AS) number 200). The code is as follows:
from netmiko import ConnectHandler
import time
def pushbgpconfig(routerip,remoteip,localas,remoteas,newconfig="false"):
uname="cisco"
passwd="cisco"
device = ConnectHandler(device_type='cisco_ios', ip=routerip, username=uname, password=passwd)
cmds=""
cmds="router bgp "+localas
cmds=cmds+"\n neighbor "+remoteip+" remote-as "+remoteas
xcheck=device.send_config_set(cmds)
print (xcheck)
outputx=device.send_command("wr mem")
print (outputx)
device.disconnect()
def validatebgp(routerip,remoteip):
uname="cisco"
passwd="cisco"
device = ConnectHandler(device_type='cisco_ios', ip=routerip, username=uname, password=passwd)
cmds="show ip bgp neighbors "+remoteip+" | include BGP state"
outputx=device.send_command(cmds)
if ("Established" in outputx):
print ("Remote IP "+remoteip+" on local router "+routerip+" is in ESTABLISHED state")
else:
print ("Remote IP "+remoteip+" on local router "+routerip+" is NOT IN ESTABLISHED state")
device.disconnect()
pushbgpconfig("192.168.255.249","192.168.255.248","200","200")
### we give some time for bgp to establish
print ("Now sleeping for 5 seconds....")
time.sleep(5) # 5 seconds
validatebgp("192.168.255.249","192.168.255.248")
The output is as follows:
As we see, we push the neighbor config (BGP config) to the router. Once the config is pushed, the script waits for 5 seconds and validates the state of BGP if it is in the ESTABLISHED state. This validation confirms that the config that we pushed has all the sessions that are newly configured as established.
Let's push an incorrect config as follows:
from netmiko import ConnectHandler
import time
def pushbgpconfig(routerip,remoteip,localas,remoteas,newconfig="false"):
uname="cisco"
passwd="cisco"
device = ConnectHandler(device_type='cisco_ios', ip=routerip, username=uname, password=passwd)
cmds=""
cmds="router bgp "+localas
cmds=cmds+"\n neighbor "+remoteip+" remote-as "+remoteas
xcheck=device.send_config_set(cmds)
print (xcheck)
outputx=device.send_command("wr mem")
print (outputx)
device.disconnect()
def validatebgp(routerip,remoteip):
uname="cisco"
passwd="cisco"
device = ConnectHandler(device_type='cisco_ios', ip=routerip, username=uname, password=passwd)
cmds="show ip bgp neighbors "+remoteip+" | include BGP state"
outputx=device.send_command(cmds)
if ("Established" in outputx):
print ("Remote IP "+remoteip+" on local router "+routerip+" is in ESTABLISHED state")
else:
print ("Remote IP "+remoteip+" on local router "+routerip+" is NOT IN ESTABLISHED state")
device.disconnect()
pushbgpconfig("192.168.255.249","192.168.255.248","200","400")
### we give some time for bgp to establish
print ("Now sleeping for 5 seconds....")
time.sleep(5) # 5 seconds
validatebgp("192.168.255.249","192.168.255.248")
The output of the preceding code is as follows:
As we see in the preceding output, now we are pushing the config with an incorrect remote (400 in this case). Of course, since the config is not correct, we get a non-established message, which confirms that the config that we pushed was not correct. In a similar way, we can push the bulk of the configs by calling the methods as many times as we want for each of the remote neighbors to be configured. Additionally, sometimes we need to get specific information under certain config sections from a running config.
As an example, the following code will give out a list for each section of the running config:
from netmiko import ConnectHandler
import itertools
class linecheck:
def __init__(self):
self.state = 0
def __call__(self, line):
if line and not line[0].isspace():
self.state += 1
return self.state
def getbgpipaddress(routerip):
uname="cisco"
passwd="cisco"
device = ConnectHandler(device_type='cisco_ios', ip=routerip, username=uname, password=passwd)
cmds="show running-config"
outputx=device.send_command(cmds)
device.disconnect()
for _, group in itertools.groupby(outputx.splitlines(), key=linecheck()):
templist=list(group)
if (len(templist) == 1):
if "!" in str(templist):
continue
print(templist)
getbgpipaddress("192.168.255.249")
The output is as follows:
As we see in the preceding output, we got all the sections of the running config, except the exclamation mark ! that we see in a running config (executing the router command show running-config on router). The focus of this output is that we have a config that is now parsed for each section in running config grouped in a single list, or in other words, a specific set of configs meant for a specific section (such as an interface or BGP) is grouped in a single list.
Lets enhance this code. As an example, we only want to see what BGP remote IPs are configured in our router:
from netmiko import ConnectHandler
import itertools
import re
class linecheck:
def __init__(self):
self.state = 0
def __call__(self, line):
if line and not line[0].isspace():
self.state += 1
return self.state
def getbgpipaddress(routerip):
uname="cisco"
passwd="cisco"
device = ConnectHandler(device_type='cisco_ios', ip=routerip, username=uname, password=passwd)
cmds="show running-config"
outputx=device.send_command(cmds)
device.disconnect()
for _, group in itertools.groupby(outputx.splitlines(), key=linecheck()):
templist=list(group)
if (len(templist) == 1):
if "!" in str(templist):
continue
if "router bgp" in str(templist):
for line in templist:
if ("neighbor " in line):
remoteip=re.search("\d+.\d+.\d+.\d+",line)
print ("Remote ip: "+remoteip.group(0))
getbgpipaddress("192.168.255.249")
The output is as follows:
In this case, first we parse the running config and focus on the section which has the router bgp config. Once we get to that particular list, we parse the list and fetch the remote IP using the regex on the specific command that contains the string neighbor. The result values would be the remote IPs under the BGP section.
As we are working with BGP, the AS numbers, being an integral part of BGP, need to be parsed or validated. Using the preceding strategies, we can get the AS numbers for BGP routes/prefixes, but in addition to that, there is a Python library pyasn that can easily find out AS number information for a given public IP address.
Again, as mentioned earlier, we need to install the following library before we can call it in the code, by using:
pip install cymruwhois
The code is as follows:
import socket
def getfromhostname(hostname):
print ("AS info for hostname :"+hostname)
ip = socket.gethostbyname(hostname)
from cymruwhois import Client
c=Client()
r=c.lookup(ip)
print (r.asn)
print (r.owner)
def getfromip(ip):
print ("AS info for IP : "+ip)
from cymruwhois import Client
c=Client()
r=c.lookup(ip)
print (r.asn)
print (r.owner)
getfromhostname("google.com")
getfromip("107.155.8.0")
The output is as follows:
As we see, the first method getfromhostname is used to fetch information for a given hostname. The other method getfromip is used to fetch the same information by using an IP address instead of any hostname.