Network Mapping Tool for Cisco IOS devices
Yet another free, not so sophisticated program to plot the network map of cisco switches and routers that are interconnected in a local environment. The logic of the program is pretty simple. when user enters details of the first node (IP address, username, password), the program establishes SSH connection to first node and run cdp command to learn the neighbor nodes. from the command output, it filters out the node name and the IP addresses.
For testing purpose, I created a network using GNS3 and cisco L3 router images.
This Python script is a network mapping tool using the networkx
library for representing and analyzing the structure of a network, matplotlib
for visualization, and netmiko
for network device communication. Let’s break down the code:
Libraries
import networkx as nx
import matplotlib.pyplot as plt
import netmiko
import re
from getpass import getpass
from queue import Queue
import signal
signal.signal(signal.SIGFPE,signal.SIG_DFL)
signal.signal(signal.SIGINT,signal.SIG_DFL)
- The script imports several libraries:
networkx
: A Python package for the creation, manipulation, and study of the structure, dynamics, and functions of complex networks.matplotlib.pyplot
: A plotting library for creating static, animated, and interactive visualizations in Python.netmiko
: A multi-vendor library to simplify Paramiko SSH connections to network devices.re
: Regular expression operations for pattern matching.getpass
: Used to securely input a password (though not used in this script).Queue
: A basic FIFO queue for storing IP addresses during network traversal.signal
: Used for signal handling, specifically setting default behavior for certain signals.
Function Definitions
def add_node(name, device_name, ip_address):
'''
Passes input arguments into structured data for creating nodes
'''
node_list.append((name, {"device_name": device_name, "IP": ip_address}))
G.add_nodes_from(node_list)
def add_edge(nodeA, nodeB, link_type):
'''
Passes input arguments into structured data for creating node edges and links
'''
edge_list.append((nodeA, nodeB, {"type": link_type}))
edge_labels[(nodeA, nodeB)] = link_type
G.add_edges_from(edge_list)
- Two functions are defined:
add_node
: Takes arguments (name, device_name, ip_address) and appends the node information tonode_list
, then adds nodes to the graph (G
).add_edge
: Takes arguments (nodeA, nodeB, link_type) and appends the edge information toedge_list
, adds edges to the graph (G
), and updates edge labels.
Variable Declarations
G = nx.Graph()
node_list = []
edge_list = []
edge_labels = {}
nodpos_attr = {}
queue = Queue()
known_neighbors = {}
known_ip = []
device = {
"ip" :"192.168.34.2",
"device_type" : "cisco_ios",
"username" : "cisco",
"password" : "cisco"
} # For testing purposes, the values are given in the code as dictionary variables.
- The script initializes several variables and data structures:
G
: Anetworkx
graph.node_list
,edge_list
, andedge_labels
: Lists to store node information, edge information, and edge labels.nodpos_attr
: Dictionary for storing node positions for visualization.queue
: A queue for storing IP addresses during network traversal.known_neighbors
: A dictionary to store information about known neighbors.known_ip
: A list to store known IP addresses.device
: A dictionary with connection details for a network device.
Main Code
queue.put(device["ip"]) # First IP address passed to queue
while not queue.empty():
device["ip"] = queue.get()
try:
print(f'Connecting to device {device["ip"]}')
connection = netmiko.ConnectHandler(**device) # Establishing SSH connection
node_name = connection.send_command('sh run | include host')
cdp_output = connection.send_command('show cdp neighbors detail')
add_node(node_name[9:], node_name[9:], device["ip"]) # Adding current node to the map
neighbors = re.findall("Device ID: .+\n.*:.*\n.*:.*\n", str(cdp_output))
for neighbor in neighbors:
op_nbr = re.match("Device ID: (.*)\..*\..*\n.*\n.*: (.*)", str(neighbor))
known_neighbors[op_nbr.group(1)] = op_nbr.group(2)
if op_nbr.group(2) not in known_ip:
known_ip.append(op_nbr.group(2))
queue.put(op_nbr.group(2)) # Adding IP addresses of neighbor nodes for the next iteration
add_edge(node_name[9:], op_nbr.group(1), "copper") # Adding neighbor nodes as edges to the map
connection.disconnect()
except (netmiko.exceptions.NetmikoAuthenticationException,
netmiko.exceptions.NetmikoTimeoutException) as error:
error_message = re.match(".*", str(error)).group(0)
print(f'Cannot connect to {device["ip"]} due to {error_message}')
print('============================================================')
print("\nKnown neighbors are:")
for kn in known_neighbors:
print(kn+' - '+known_neighbors[kn])
- The main code performs the following steps:
- Initializes the queue with the first IP address.
- Iterates over the queue until it’s empty, connecting to devices and retrieving CDP neighbor information.
- Adds nodes and edges to the graph based on the gathered information.
- Prints known neighbors.
Code for Plotting the Network Map
nodpos = nx.spring_layout(G)
for n in G.nodes():
nodpos_attr[n] = nodpos[n]+[0, -0.3]
edge_labels = nx.get_edge_attributes(G, 'type')
node_labels = nx.get_node_attributes(G, 'device_name')
nx.draw_networkx(G, pos=nodpos, with_labels=True, node_color="blue", node_size=1500, font_color="white", font_weight="bold")
nx.draw_networkx_edge_labels(G, pos=nodpos, edge_labels=edge_labels, label_pos=0.5, font_color='red')
nx.draw_networkx_labels(G, pos=nodpos_attr, labels=node_labels)
plt.show()
- This part of the code uses
matplotlib
andnetworkx
to visualize the network graph. It sets the positions of nodes, draws nodes, edges, and labels, and finally shows the plot.
Overall, the script connects to a network device, retrieves CDP neighbor information, and creates a network graph using networkx
and visualizes it with matplotlib
. The visualization helps in understanding the network topology and the relationships between different devices.
Following is the flowchart for the program
Here is the generated output