Sie sind auf Seite 1von 185

1

Python Network Programming Course Applications Guide by Mihai Cătălin Teodosiu

Python Network Programming

by Mihai Cătălin Teodosiu, CCNP, Udemy & GNS3 Academy Instructor

Teodosiu Python Network Programming by Mihai Cătălin Teodosiu, CCNP, Udemy & GNS3 Academy Instructor Page 1

Page 1 of 185

2

Python Network Programming Course Applications Guide by Mihai Cătălin Teodosiu

1. What’s this document all about?

Note: This e-book is a comprehensive guide containing all the applications developed throughout my Python Network Programming courses: “Python Network Programming - Part 1: Build 7 Python Apps”, “Python Network Programming - Part 2: Multivendor Environment” & “Python Network Programming - Part 3: Scapy & Security Tools”.

Note: This document is intended for students enrolled in all three courses and is distributed for personal use only. The distribution of this material to people not enrolled in the “Python Network Programming” course series is strictly prohibited and is subject to copyright infringement. The author of this document is entitled to invoke legal and technological measures to prevent and penalize copyright infringement. More information here: https://en.wikipedia.org/wiki/Copyright_infringement

IMPORTANT, BEFORE YOU CONTINUE!

All the code, scripts and applications are explained, turned into working applications and tested inside the course. For detailed explanations and testing, please see the course sections referenced by each application below. Pay attention to comments in the code!

Page 2 of 185

3

Python Network Programming Course Applications Guide by Mihai Cătălin Teodosiu

2. Python Network Programming - Part 1: Build 7 Python Apps

NOTE! Python programming knowledge is required in order to understand the network automation applications below. Learn Python from scratch inside the video course series!

2.1. Telnet with Python

(template only, reference: Section 11. Python Networking)

#Open telnet connection to devices def open_telnet_conn(ip):

#Change exception message try:

#Define telnet parameters username = 'teopy' password = 'python' TELNET_PORT = 23 TELNET_TIMEOUT = 5 READ_TIMEOUT = 5

#Logging into device connection = telnetlib.Telnet(ip, TELNET_PORT, TELNET_TIMEOUT)

output = connection.read_until("name:", READ_TIMEOUT) connection.write(username + "\n")

output = connection.read_until("word:", READ_TIMEOUT) connection.write(password + "\n")

time.sleep(1)

#Setting terminal length for entire output - no pagination connection.write("terminal length 0\n")

time.sleep(1)

#Entering global config mode connection.write("\n") connection.write("configure terminal\n")

time.sleep(1)

#Open user selected file for reading selected_cmd_file = open(cmd_file, 'r')

#Starting from the beginning of the file

selected_cmd_file.seek(0)

#Writing each line in the file to the device for each_line in selected_cmd_file.readlines():

connection.write(each_line + '\n')

time.sleep(1)

Page 3 of 185

4

Python Network Programming Course Applications Guide by Mihai Cătălin Teodosiu

#Closing the file selected_cmd_file.close()

#Test for reading command output #output = connection.read_very_eager() #print output

#Closing the connection connection.close()

except IOError:

print "Input parameter error! Please check username, password and file name."

#Calling the Telnet function open_telnet_conn(ip)

Page 4 of 185

5

Python Network Programming Course Applications Guide by Mihai Cătălin Teodosiu

2.2. SSH with Python

(template only, reference: Section 11. Python Networking)

#Open SSHv2 connection to devices def open_ssh_conn(ip):

#Change exception message try:

#Define SSH parameters selected_user_file = open(user_file, 'r')

#Starting from the beginning of the file

selected_user_file.seek(0)

username = selected_user_file.readlines()[0].split(',')[0]

#Starting from the beginning of the file

selected_user_file.seek(0)

password = selected_user_file.readlines()[0].split(',')[1]

#Logging into device session = paramiko.SSHClient()

session.set_missing_host_key_policy(

paramiko.AutoAddPolicy())

session.connect(ip, username = username, password = password)

connection = session.invoke_shell()

#Setting terminal length for entire output - no pagination connection.send("terminal length 0\n")

time.sleep(1)

#Entering global config mode connection.send("\n") connection.send("configure terminal\n")

time.sleep(1)

#Open user selected file for reading selected_cmd_file = open(cmd_file, 'r')

#Starting from the beginning of the file

selected_cmd_file.seek(0)

#Writing each line in the file to the device for each_line in selected_cmd_file.readlines():

connection.send(each_line + '\n')

time.sleep(2)

Page 5 of 185

6

Python Network Programming Course Applications Guide by Mihai Cătălin Teodosiu

#Closing the user file selected_user_file.close()

#Closing the command file selected_cmd_file.close()

#Checking command output for IOS syntax errors output = connection.recv(65535)

if re.search(r"% Invalid input detected at", output):

print "* There was at least one IOS syntax error on device %s"

% ip

else:

print "\nDONE for device %s" % ip

#Test for reading command output #print output + "\n"

#Closing the connection session.close()

except paramiko.AuthenticationException:

print "* Invalid username or password. \n* Please check the username/password file or the device configuration!"

print "* Closing program

\n"

#Calling the SSH function open_ssh_conn(ip)

Page 6 of 185

7

Python Network Programming Course Applications Guide by Mihai Cătălin Teodosiu

2.3. SNMP with Python

(template only, reference: Section 11. Python Networking)

#!/usr/bin/env python

from pysnmp.entity.rfc3413.oneliner import cmdgen

#SNMP function def snmp_get(ip):

#Creating command generator object cmdGen = cmdgen.CommandGenerator()

#Performing SNMP GETNEXT operations on the OSPF OIDs #The basic syntax of nextCmd: nextCmd(authData, transportTarget, *varNames) #The nextCmd method returns a tuple of (errorIndication, errorStatus, errorIndex, varBindTable)

errorIndication, errorStatus, errorIndex, varBindNbrTable = cmdGen.nextCmd(cmdgen.CommunityData(comm),

cmdgen.UdpTransportTarget((ip, 161)),

'1.3.6.1.2.1.14.10.1.3')

#print cmdGen.nextCmd(cmdgen.CommunityData(comm),cmdgen.UdpTransportTarget((ip,

161)),'1.3.6.1.2.1.14.10.1.3')

#print varBindNbrTable

errorIndication, errorStatus, errorIndex, varBindNbrIpTable = cmdGen.nextCmd(cmdgen.CommunityData(comm),

cmdgen.UdpTransportTarget((ip, 161)),

'1.3.6.1.2.1.14.10.1.1')

#print varBindNbrIpTable

errorIndication, errorStatus, errorIndex, varBindHostTable = cmdGen.nextCmd(cmdgen.CommunityData(comm),

cmdgen.UdpTransportTarget((ip, 161)),

'1.3.6.1.4.1.9.2.1.3')

#print varBindHostTable

Page 7 of 185

8

Python Network Programming Course Applications Guide by Mihai Cătălin Teodosiu

errorIndication, errorStatus, errorIndex, varBindHostIdTable = cmdGen.nextCmd(cmdgen.CommunityData(comm),

cmdgen.UdpTransportTarget((ip, 161)),

'1.3.6.1.2.1.14.1.1')

#print varBindHostIdTable

Page 8 of 185

9

Python Network Programming Course Applications Guide by Mihai Cătălin Teodosiu

2.4. Application #1 - Basic subnet calculator

(full code, reference: Section 13. Application #1 - Basic subnet calculator)

Logical flow diagram

calculator (full code, reference: Section 13. Application #1 - Basic subnet calculator) Logical flow diagram Page

Page 9 of 185

10

Python Network Programming Course Applications Guide by Mihai Cătălin Teodosiu

############# Application #1 - Part #1 #############

import random import sys

def subnet_calc():

try:

print "\n"

#Checking IP address validity while True:

ip_address = raw_input("Enter an IP address: ")

#Checking octets

a = ip_address.split('.')

if (len(a) == 4) and (1 <= int(a[0]) <= 223) and (int(a[0]) != 127) and (int(a[0]) != 169 or int(a[1]) != 254) and (0 <= int(a[1]) <= 255 and 0 <= int(a[2]) <= 255 and 0 <= int(a[3]) <= 255):

break

else:

print "\nThe IP address is INVALID! Please retry!\n"

continue

masks = [255, 254, 252, 248, 240, 224, 192, 128, 0]

#Checking Subnet Mask validity while True:

subnet_mask = raw_input("Enter a subnet mask: ")

#Checking octets

b = subnet_mask.split('.')

if (len(b) == 4) and (int(b[0]) == 255) and (int(b[1]) in masks) and (int(b[2]) in masks) and (int(b[3]) in masks) and (int(b[0]) >= int(b[1]) >= int(b[2]) >= int(b[3])):

break

else:

print "\nThe subnet mask is INVALID! Please retry!\n"

continue

############# Application #1 - Part #2 #############

#Algorithm for subnet identification, based on IP and Subnet Mask

#Convert mask to binary string mask_octets_padded = [] mask_octets_decimal = subnet_mask.split(".") #print mask_octets_decimal

for octet_index in range(0, len(mask_octets_decimal)):

Page 10 of 185

11

Python Network Programming Course Applications Guide by Mihai Cătălin Teodosiu

#print bin(int(mask_octets_decimal[octet_index]))

binary_octet =

bin(int(mask_octets_decimal[octet_index])).split("b")[1]

#print binary_octet

if len(binary_octet) == 8:

mask_octets_padded.append(binary_octet)

elif len(binary_octet) < 8:

binary_octet_padded = binary_octet.zfill(8) mask_octets_padded.append(binary_octet_padded)

#print mask_octets_padded

decimal_mask = "".join(mask_octets_padded)

#print decimal_mask

#Example: for 255.255.255.0 =>

11111111111111111111111100000000

#Counting host bits in the mask and calculating number of hosts/subnet no_of_zeros = decimal_mask.count("0") no_of_ones = 32 - no_of_zeros no_of_hosts = abs(2 ** no_of_zeros - 2) #return positive value for

mask /32

#print no_of_zeros #print no_of_ones #print no_of_hosts

#Obtaining wildcard mask wildcard_octets = [] for w_octet in mask_octets_decimal:

wild_octet = 255 - int(w_octet) wildcard_octets.append(str(wild_octet))

#print wildcard_octets

wildcard_mask = ".".join(wildcard_octets) #print wildcard_mask

############# Application #1 - Part #3 #############

#Convert IP to binary string ip_octets_padded = [] ip_octets_decimal = ip_address.split(".")

for octet_index in range(0, len(ip_octets_decimal)):

binary_octet =

bin(int(ip_octets_decimal[octet_index])).split("b")[1]

if len(binary_octet) < 8:

binary_octet_padded = binary_octet.zfill(8)

Page 11 of 185

12

Python Network Programming Course Applications Guide by Mihai Cătălin Teodosiu

ip_octets_padded.append(binary_octet_padded)

else:

ip_octets_padded.append(binary_octet)

#print ip_octets_padded

binary_ip = "".join(ip_octets_padded)

#print binary_ip

11000000101010000000001001100100

#Example: for 192.168.2.100 =>

#Obtain the network address and broadcast address from the binary strings obtained above

network_address_binary = binary_ip[:(no_of_ones)] + "0" * no_of_zeros #print network_address_binary

broadcast_address_binary = binary_ip[:(no_of_ones)] + "1" * no_of_zeros #print broadcast_address_binary

net_ip_octets = [] for octet in range(0, len(network_address_binary), 8):

net_ip_octet = network_address_binary[octet:octet+8] net_ip_octets.append(net_ip_octet)

#print net_ip_octets

net_ip_address = [] for each_octet in net_ip_octets:

net_ip_address.append(str(int(each_octet, 2)))

#print net_ip_address

network_address = ".".join(net_ip_address) #print network_address

bst_ip_octets = [] for octet in range(0, len(broadcast_address_binary), 8):

bst_ip_octet = broadcast_address_binary[octet:octet+8] bst_ip_octets.append(bst_ip_octet)

#print bst_ip_octets

bst_ip_address = [] for each_octet in bst_ip_octets:

bst_ip_address.append(str(int(each_octet, 2)))

#print bst_ip_address

broadcast_address = ".".join(bst_ip_address) #print broadcast_address

Page 12 of 185

13

Python Network Programming Course Applications Guide by Mihai Cătălin Teodosiu

#Results for selected IP/mask print "\n" print "Network address is: %s" % network_address print "Broadcast address is: %s" % broadcast_address print "Number of valid hosts per subnet: %s" % no_of_hosts print "Wildcard mask: %s" % wildcard_mask print "Mask bits: %s" % no_of_ones print "\n"

############# Application #1 - Part #4 #############

#Generation of random IP in subnet while True:

generate = raw_input("Generate random ip address from subnet?

(y/n)")

if generate == "y":

generated_ip = []

#Obtain available IP address in range, based on the difference between octets in broadcast address and network address for indexb, oct_bst in enumerate(bst_ip_address):

#print indexb, oct_bst for indexn, oct_net in enumerate(net_ip_address):

#print indexn, oct_net if indexb == indexn:

if oct_bst == oct_net:

#Add identical octets to the generated_ip

list

generated_ip.append(oct_bst)

else:

#Generate random number(s) from within octet intervals and append to the list

generated_ip.append(str(random.randint(int(oct_net), int(oct_bst))))

#IP address generated from the subnet pool #print generated_ip y_iaddr = ".".join(generated_ip) #print y_iaddr

print "Random IP address is: %s" % y_iaddr print "\n" continue

else:

print "Ok, bye!\n" break

except KeyboardInterrupt:

print "\n\nProgram aborted by user. Exiting sys.exit()

Page 13 of 185

\n"

14

Python Network Programming Course Applications Guide by Mihai Cătălin Teodosiu

#Calling the function subnet_calc()

Page 14 of 185

15

Python Network Programming Course Applications Guide by Mihai Cătălin Teodosiu

2.5. Application #2 SSH/Telnet network configuration

(full

configuration)

code,

reference:

Section

Logical flow diagram

14.

Application

#2

SSH/Telnet

network

configuration) code, reference: Section Logical flow diagram 14. Application #2 – SSH/Telnet network Page 15 of

Page 15 of 185

16

Python Network Programming Course Applications Guide by Mihai Cătălin Teodosiu

2.5.1. SSH network configuration

############# Application #2 - Part #1 #############

import paramiko import threading import os.path import subprocess import time import sys import re

#Checking IP address file and content validity def ip_is_valid():

check = False global ip_list

while True:

#Prompting user for input

 

print "\n# #

# #

#

#

# #

#

#

# #

#

#

#

#

#

#

# #

#

# #

#

#

# #

#\n"

 

ip_file = raw_input("# Enter IP file name and extension: ")

 

print "\n# #

# #

#

#

# #

#

#

# #

#

#

#

#

#

#

# #

#

# #

#

#

# # #"

#Changing exception message try:

#Open user selected file for reading (IP addresses file) selected_ip_file = open(ip_file, 'r')

#Starting from the beginning of the file

selected_ip_file.seek(0)

#Reading each line (IP address) in the file ip_list = selected_ip_file.readlines()

#Closing the file selected_ip_file.close()

except IOError:

print "\n* File %s does not exist! Please check and try again!\n" % ip_file

#Checking octets for ip in ip_list:

a = ip.split('.')

if (len(a) == 4) and (1 <= int(a[0]) <= 223) and (int(a[0]) != 127) and (int(a[0]) != 169 or int(a[1]) != 254) and (0 <= int(a[1]) <= 255 and 0 <= int(a[2]) <= 255 and 0 <= int(a[3]) <= 255):

check = True

Page 16 of 185

17

Python Network Programming Course Applications Guide by Mihai Cătălin Teodosiu

break

else:

print '\n* There was an INVALID IP address! Please check and try again!\n' check = False continue

#Evaluating the 'check' flag if check == False:

continue

elif check == True:

break

############# Application #2 - Part #2 #############

#Checking IP reachability print "\n* Checking IP reachability. Please wait

check2 = False

while True:

for ip in ip_list:

\n"

ping_reply = subprocess.call(['ping', '-c', '2', '-w', '2', '- q', '-n', ip])

if ping_reply == 0:

check2 = True continue

elif ping_reply == 2:

print "\n* No response from device %s." % ip check2 = False break

else:

print "\n* Ping to the following device has FAILED:", ip check2 = False break

#Evaluating the 'check' flag if check2 == False:

print "* Please re-check IP address list or device.\n" ip_is_valid()

elif check2 == True:

print '\n* All devices are reachable. Waiting for

username/password file break

\n'

#Checking user file validity def user_is_valid():

global user_file

Page 17 of 185

18

Python Network Programming Course Applications Guide by Mihai Cătălin Teodosiu

")

while True:

print "# # #

user_file = raw_input("# Enter user/pass file name and extension:

# #

#

#

#

#

#

#

# #

#

#

# #

#

#

#

# #

# # # # # #\n"

print "\n# #

# #

#

#

# #

#

#

# #

#

#

#

#

#

#

# #

#

# #

#

#

# # #"

#Changing output messages if os.path.isfile(user_file) == True:

print "\n* Username/password file has been validated. Waiting

for command file break

else:

\n"

print "\n* File %s does not exist! Please check and try again!\n" % user_file continue

#Checking command file validity def cmd_is_valid():

global cmd_file

#\n"

while True:

print "\n\n#

# #

#

# #

#

# #

# #

#

#

#

#

#

#

#

# #

# #

#

#

#

# #

cmd_file = raw_input("# Enter command file name and extension: ")

print "\n# #

# #

#

#

# #

#

#

# #

#

#

#

#

#

#

# #

#

# #

#

#

# # #"

#Changing output messages if os.path.isfile(cmd_file) == True:

print "\n* Sending command(s) to device(s) break

\n"

else:

print "\n* File %s does not exist! Please check and try again!\n" % cmd_file continue

#Change exception message try:

#Calling IP validity function ip_is_valid()

except KeyboardInterrupt:

print "\n\n* Program aborted by user. Exiting sys.exit()

\n"

#Change exception message try:

#Calling user file validity function user_is_valid()

except KeyboardInterrupt:

print "\n\n* Program aborted by user. Exiting

\n"

Page 18 of 185

19

Python Network Programming Course Applications Guide by Mihai Cătălin Teodosiu

sys.exit()

#Change exception message try:

#Calling command file validity function cmd_is_valid()

except KeyboardInterrupt:

print "\n\n* Program aborted by user. Exiting sys.exit()

\n"

############# Application #2 - Part #3 #############

#Open SSHv2 connection to devices def open_ssh_conn(ip):

#Change exception message try:

#Define SSH parameters selected_user_file = open(user_file, 'r')

#Starting from the beginning of the file

selected_user_file.seek(0)

#Reading the username from the file username = selected_user_file.readlines()[0].split(',')[0]

#Starting from the beginning of the file

selected_user_file.seek(0)

#Reading the password from the file password =

selected_user_file.readlines()[0].split(',')[1].rstrip("\n")

#Logging into device session = paramiko.SSHClient()

#For testing purposes, this allows auto-accepting unknown host

keys

#Do not use in production! The default would be RejectPolicy session.set_missing_host_key_policy(paramiko.AutoAddPolicy())

#Connect to the device using username and password session.connect(ip, username = username, password = password)

#Start an interactive shell session on the router connection = session.invoke_shell()

#Setting terminal length for entire output - disable pagination connection.send("terminal length 0\n")

time.sleep(1)

#Entering global config mode connection.send("\n") connection.send("configure terminal\n")

Page 19 of 185

20

Python Network Programming Course Applications Guide by Mihai Cătălin Teodosiu

time.sleep(1)

#Open user selected file for reading selected_cmd_file = open(cmd_file, 'r')

#Starting from the beginning of the file

selected_cmd_file.seek(0)

#Writing each line in the file to the device for each_line in selected_cmd_file.readlines():

connection.send(each_line + '\n')

time.sleep(2)

#Closing the user file selected_user_file.close()

#Closing the command file selected_cmd_file.close()

#Checking command output for IOS syntax errors router_output = connection.recv(65535)

if re.search(r"% Invalid input detected at", router_output):

print "* There was at least one IOS syntax error on device %s"

% ip

else:

print "\nDONE for device %s" % ip

#Test for reading command output #print router_output + "\n"

#Closing the connection session.close()

except paramiko.AuthenticationException:

print "* Invalid username or password. \n* Please check the username/password file or the device configuration!"

print "* Closing program

\n"

############# Application #2 - Part #4 #############

#Creating threads def create_threads():

threads = [] for ip in ip_list:

th = threading.Thread(target = open_ssh_conn, args = (ip,)) #args is a tuple with a single element th.start() threads.append(th)

for th in threads:

th.join()

Page 20 of 185

21

Python Network Programming Course Applications Guide by Mihai Cătălin Teodosiu

#Calling threads creation function create_threads()

#End of program

Page 21 of 185

22

Python Network Programming Course Applications Guide by Mihai Cătălin Teodosiu

2.5.2. Telnet network configuration

#!/usr/bin/env python

import telnetlib import threading import os.path import subprocess import time import sys

#Checking IP address validity def ip_is_valid():

check = False global ip_list

while True:

#Prompting user for input ip_file = raw_input("Enter IP file name and extension: ")

#Changing exception message try:

#Open user selected file for reading (IP addresses file) selected_ip_file = open(ip_file, 'r')

#Starting from the beginning of the file

selected_ip_file.seek(0)

#Reading each line (IP address) in the file ip_list = selected_ip_file.readlines()

#Closing the file selected_ip_file.close()

except IOError:

print "\nFile %s does not exist! Please check and try again!\n" % ip_file

#Checking octets for ip in ip_list:

a = ip.split('.')

if (len(a) == 4) and (1 <= int(a[0]) <= 223) and (int(a[0]) != 127) and (int(a[0]) != 169 or int(a[1]) != 254) and (0 <= int(a[1]) <= 255 and 0 <= int(a[2]) <= 255 and 0 <= int(a[3]) <= 255):

check = True break

else:

Page 22 of 185

23

Python Network Programming Course Applications Guide by Mihai Cătălin Teodosiu

print '\n* There was an INVALID IP address! Please check and try again!\n' check = False continue

#Evaluating the 'check' flag if check == False:

continue

elif check == True:

break

#Checking IP reachability print "\nChecking IP reachability

check2 = False

while True:

for ip in ip_list:

\n"

ping_reply = subprocess.call(['ping', '-c', '3', '-w', '3', '- q', '-n', ip])

if ping_reply == 0:

check2 = True continue

elif ping_reply == 2:

print "\nNo response from device %s." % ip check2 = False break

else:

print "\nPing to the following device has FAILED:", ip check2 = False break

#Evaluating the 'check' flag if check2 == False:

print "Please re-check IP address list or device.\n" ip_is_valid()

elif check2 == True:

print '\nAll devices are reachable. Waiting for command

file

\n'

break

#Checking command file validity def cmd_is_valid():

global cmd_file

while True:

cmd_file = raw_input("Enter command file name and extension: ")

#Changing exception message

Page 23 of 185

24

Python Network Programming Course Applications Guide by Mihai Cătălin Teodosiu

if os.path.isfile(cmd_file) == True:

print "\nSending command(s) to device(s) break

else:

\n"

print "\nFile %s does not exist! Please check and try again!\n" % cmd_file continue

#Change exception message try:

#Calling IP validity function ip_is_valid()

except KeyboardInterrupt:

print "\n\nProgram aborted by user. Exiting sys.exit()

\n"

#Change exception message try:

#Calling command file validity function cmd_is_valid()

except KeyboardInterrupt:

print "\n\nProgram aborted by user. Exiting sys.exit()

\n"

#Open telnet connection to devices def open_telnet_conn(ip):

#Change exception message try:

#Define telnet parameters username = 'teopy' password = 'python'

#Specify the Telnet port (default is 23, anyway) port = 23

#Specify the connection timeout in seconds for blocking operations, like the connection attempt connection_timeout = 5

#Specify a timeout in seconds. Read until the string is found or until the timout has passed reading_timeout = 5

#Logging into device connection = telnetlib.Telnet(ip, port, connection_timeout)

#Waiting to be asked for an username router_output = connection.read_until("Username:", reading_timeout) #Enter the username when asked and a "\n" for Enter

Page 24 of 185

25

Python Network Programming Course Applications Guide by Mihai Cătălin Teodosiu

connection.write(username + "\n")

#Waiting to be asked for a password router_output = connection.read_until("Password:", reading_timeout) #Enter the password when asked and a "\n" for Enter connection.write(password + "\n")

time.sleep(1)

#Setting terminal length for the entire output - disabling pagination connection.write("terminal length 0\n")

time.sleep(1)

#Entering global config mode connection.write("\n") connection.write("configure terminal\n")

time.sleep(1)

#Open user selected file for reading selected_cmd_file = open(cmd_file, 'r')

#Starting from the beginning of the file

selected_cmd_file.seek(0)

#Writing each line in the file to the device for each_line in selected_cmd_file.readlines():

connection.write(each_line + '\n')

time.sleep(1)

#Closing the file selected_cmd_file.close()

#Test for reading command output #router_output = connection.read_very_eager() #print router_output

#Closing the connection connection.close()

except IOError:

print "Input parameter error! Please check username, password and file name."

#Creating threads def create_threads():

threads = [] for ip in ip_list:

th = threading.Thread(target = open_telnet_conn, args = (ip,)) #args is a tuple with a single element th.start() threads.append(th)

Page 25 of 185

26

Python Network Programming Course Applications Guide by Mihai Cătălin Teodosiu

for th in threads:

th.join()

#Calling threads creation function create_threads()

#End of program

Page 26 of 185

27

Python Network Programming Course Applications Guide by Mihai Cătălin Teodosiu

2.6. Application #3 - DHCP client simulator

(full code, reference: Section 15. Application #3 - DHCP client simulator)

Logical flow diagram

simulator (full code, reference: Section 15. Application #3 - DHCP client simulator) Logical flow diagram Page

Page 27 of 185

28

Python Network Programming Course Applications Guide by Mihai Cătălin Teodosiu

############# Application #3 - Part #1 #############

#DHCP client simulator

#In scapy interactive mode - DHCP packets:

''' 'Ether / IP / UDP 0.0.0.0:bootpc > 255.255.255.255:bootps / BOOTP / DHCP'

DHCP DISCOVER:

'Ether(src=\'08:00:27:f9:51:87\', dst=\'ff:ff:ff:ff:ff:ff\', type=2048)/IP(frag=0L, src=\'0.0.0.0\', proto=17, tos=16, dst=\'255.255.255.255\', chksum=14742, len=328, options=[], version=4L, flags=0L, ihl=5L, ttl=128, id=0)/UDP(dport=67, sport=68, len=308,

chksum=47898)/BOOTP(hlen=6,

sname=\'\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\

\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00

\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x0

0\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x

00\\x00\\x00\\x00\\x00\\x00\\x00\', xid=398202904, ciaddr=\'0.0.0.0\', hops=0, giaddr=\'0.0.0.0\',

chaddr="\\x08\\x00\'\\xf9Q\\x87\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x

00\\x00", yiaddr=\'0.0.0.0\', secs=0, flags=0L, htype=1,

file=\'\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\

x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\

\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00

\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x0

0\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x

00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\

x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\

\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00

\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\',

siaddr=\'0.0.0.0\', options=\'c\\x82Sc\', op=1)/DHCP(options=[(\'message- type\', 1), (\'hostname\', \'kali-teo\'), (\'param_req_list\', \'\\x01\\x1c\\x02\\x03\\x0f\\x06w\\x0c,/\\x1ay*\'), \'end\', \'pad\', \'pad\', \'pad\', \'pad\', \'pad\', \'pad\', \'pad\', \'pad\', \'pad\', \'pad\', \'pad\', \'pad\', \'pad\', \'pad\', \'pad\', \'pad\', \'pad\', \'pad\', \'pad\', \'pad\', \'pad\', \'pad\', \'pad\', \'pad\', \'pad\', \'pad\', \'pad\', \'pad\', \'pad\', \'pad\', \'pad\'])'

DHCP OFFER:

'Ether(src=\'c0:04:1a:5c:00:01\', dst=\'08:00:27:f9:51:87\', type=2048)/IP(frag=0L, src=\'192.168.2.111\', proto=17, tos=0, dst=\'192.168.2.1\', chksum=13540, len=328, options=[], version=4L, flags=0L, ihl=5L, ttl=255, id=0)/UDP(dport=68, sport=67, len=308,

chksum=19350)/BOOTP(hlen=6,

sname=\'\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\

\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00

\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x0

0\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x

00\\x00\\x00\\x00\\x00\\x00\\x00\', xid=398202904, ciaddr=\'0.0.0.0\', hops=0, giaddr=\'0.0.0.0\',

chaddr="\\x08\\x00\'\\xf9Q\\x87\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x

00\\x00", yiaddr=\'192.168.2.1\', secs=0, flags=0L, htype=1,

file=\'\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\

Page 28 of 185

29

Python Network Programming Course Applications Guide by Mihai Cătălin Teodosiu

x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\

\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00

\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x0

0\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x

00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\

x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\

\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00

\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\',

siaddr=\'0.0.0.0\', options=\'c\\x82Sc\', op=2)/DHCP(options=[(\'message- type\', 2), (\'server_id\', \'192.168.2.111\'), (\'lease_time\', 86400), (\'renewal_time\', 43200), (\'rebinding_time\', 75600), (\'subnet_mask\', \'255.255.255.0\'), \'end\', \'pad\', \'pad\', \'pad\', \'pad\', \'pad\', \'pad\', \'pad\', \'pad\', \'pad\', \'pad\', \'pad\', \'pad\', \'pad\', \'pad\', \'pad\', \'pad\', \'pad\', \'pad\', \'pad\', \'pad\', \'pad\', \'pad\', \'pad\', \'pad\', \'pad\', \'pad\'])'

DHCP OFFER (more options):

'Ether(src='ca:04:15:ec:00:08', dst='00:00:5e:4a:a3:fe', type=2048)/IP(frag=0L, src='192.168.2.111', proto=17, tos=0, dst='192.168.2.236', chksum=9573, len=328, options=[], version=4L, flags=0L, ihl=5L, ttl=255, id=3732)/UDP(dport=68, sport=67, len=308,

chksum=3558)/BOOTP(hlen=6,

sname='\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\

x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\

\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00

\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x0

0\\x00\\x00\\x00\\x00\\x00\\x00', xid=868370, ciaddr='0.0.0.0', hops=0,

giaddr='0.0.0.0',

chaddr='\\x00\\x00^J\\xa3\\xfe\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x0

0\\x00', yiaddr='192.168.2.236', secs=0, flags=0L, htype=1,

file='\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x

00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\

x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\

\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00

\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x0

0\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x

00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\

x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\

\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00', siaddr='0.0.0.0', options='c\\x82Sc', op=2)/DHCP(options=[('message-type', 2), ('server_id', '192.168.2.111'), ('lease_time', 86400), ('renewal_time', 43200), ('rebinding_time', 75600), ('subnet_mask', '255.255.255.0'), ('router', '192.168.2.254'), 'end', 'pad', 'pad', 'pad', 'pad', 'pad', 'pad', 'pad', 'pad', 'pad', 'pad', 'pad', 'pad', 'pad', 'pad', 'pad', 'pad', 'pad', 'pad', 'pad', 'pad'])'

DHCP REQUEST:

'Ether(src=\'08:00:27:f9:51:87\', dst=\'ff:ff:ff:ff:ff:ff\', type=2048)/IP(frag=0L, src=\'0.0.0.0\', proto=17, tos=16, dst=\'255.255.255.255\', chksum=14742, len=328, options=[], version=4L, flags=0L, ihl=5L, ttl=128, id=0)/UDP(dport=67, sport=68, len=308,

chksum=61228)/BOOTP(hlen=6,

sname=\'\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\

\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00

Page 29 of 185

30

Python Network Programming Course Applications Guide by Mihai Cătălin Teodosiu

\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x0

0\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x

00\\x00\\x00\\x00\\x00\\x00\\x00\', xid=398202904, ciaddr=\'0.0.0.0\', hops=0, giaddr=\'0.0.0.0\',

chaddr="\\x08\\x00\'\\xf9Q\\x87\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x

00\\x00", yiaddr=\'0.0.0.0\', secs=0, flags=0L, htype=1,

file=\'\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\

x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\

\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00

\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x0

0\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x

00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\

x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\

\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00

\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\',

siaddr=\'0.0.0.0\', options=\'c\\x82Sc\', op=1)/DHCP(options=[(\'message- type\', 3), (\'server_id\', \'192.168.2.111\'), (\'requested_addr\', \'192.168.2.1\'), (\'hostname\', \'kali-teo\'), (\'param_req_list\', \'\\x01\\x1c\\x02\\x03\\x0f\\x06w\\x0c,/\\x1ay*\'), \'end\', \'pad\', \'pad\', \'pad\', \'pad\', \'pad\', \'pad\', \'pad\', \'pad\', \'pad\', \'pad\', \'pad\', \'pad\', \'pad\', \'pad\', \'pad\', \'pad\', \'pad\', \'pad\', \'pad\'])'

DHCP ACK:

'Ether(src=\'c0:04:1a:5c:00:01\', dst=\'08:00:27:f9:51:87\', type=2048)/IP(frag=0L, src=\'192.168.2.111\', proto=17, tos=0, dst=\'192.168.2.1\', chksum=13539, len=328, options=[], version=4L, flags=0L, ihl=5L, ttl=255, id=1)/UDP(dport=68, sport=67, len=308,

chksum=18582)/BOOTP(hlen=6,

sname=\'\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\

\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00

\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x0

0\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x

00\\x00\\x00\\x00\\x00\\x00\\x00\', xid=398202904, ciaddr=\'0.0.0.0\', hops=0, giaddr=\'0.0.0.0\',

chaddr="\\x08\\x00\'\\xf9Q\\x87\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x

00\\x00", yiaddr=\'192.168.2.1\', secs=0, flags=0L, htype=1,

file=\'\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\

x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\

\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00

\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x0

0\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x

00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\

x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\

\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00

\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\',

siaddr=\'0.0.0.0\', options=\'c\\x82Sc\', op=2)/DHCP(options=[(\'message- type\', 5), (\'server_id\', \'192.168.2.111\'), (\'lease_time\', 86400), (\'renewal_time\', 43200), (\'rebinding_time\', 75600), (\'subnet_mask\', \'255.255.255.0\'), \'end\', \'pad\', \'pad\', \'pad\', \'pad\', \'pad\', \'pad\', \'pad\', \'pad\', \'pad\', \'pad\', \'pad\', \'pad\', \'pad\', \'pad\', \'pad\', \'pad\', \'pad\', \'pad\', \'pad\', \'pad\', \'pad\', \'pad\', \'pad\', \'pad\', \'pad\', \'pad\'])' '''

Page 30 of 185

31

Python Network Programming Course Applications Guide by Mihai Cătălin Teodosiu

import subprocess import logging import random import sys

#This will suppress all messages that have a lower level of seriousness than error messages, while running or loading Scapy logging.getLogger("scapy.runtime").setLevel(logging.ERROR) logging.getLogger("scapy.interactive").setLevel(logging.ERROR) logging.getLogger("scapy.loading").setLevel(logging.ERROR)

try:

from scapy.all import *

except ImportError:

print "Scapy package for Python is not installed on your system." print "Get it from https://pypi.python.org/pypi/scapy and try again." sys.exit()

#To see a list of what commands Scapy has available, run the lsc() function. #Run the ls() command to see ALL the supported protocols. #Run the ls(protocol) command to see the fields and default values for any protocol. #See packet layers with the .summary() function. #See packet contents with the .show() function. #Dig into a specific packet layer using a list index:

pkts[3][2].summary()

the #

index chooses the layer for that specific packet. #Using the .command() packet method will return a string of the command

necessary to recreate that sniffed packet.

first index chooses the packet out of the pkts list, the second

print "\n! Make sure to run this program as ROOT !\n"

#Setting network interface in promiscuous mode net_iface = raw_input("Enter the interface to the target network: ")

subprocess.call(["ifconfig", net_iface, "promisc"], stdout=None, stderr=None, shell=False)

print "\nInterface %s was set to PROMISC mode." % net_iface

Page 31 of 185

32

Python Network Programming Course Applications Guide by Mihai Cătălin Teodosiu

#Scapy normally makes sure that replies come from the same IP address the stimulus was sent to. #But our DHCP packet is sent to the IP broadcast address (255.255.255.255) and any answer packet will have the IP address of the replying DHCP server as its source IP address (e.g. 192.168.2.101). #Because these IP addresses don't match, we have to disable Scapy's check with conf.checkIPaddr = False before sending the stimulus. #Source:

https://bitbucket.org/pbi/test/wiki/doc/IdentifyingRogueDHCPServers conf.checkIPaddr = False

############# Application #3 - Part #2 #############

################## DHCP SEQUENCE ################# all_given_leases = [] server_id = [] client_mac = []

#Generate entire DHCP sequence def generate_dhcp_seq():

global all_given_leases

#Defining some DHCP parameters x_id = random.randrange(1, 1000000) hw = "00:00:5e" + str(RandMAC())[8:] hw_str = mac2str(hw) #print hw

#Assigning the .command() output of a captured DHCP DISCOVER packet to a variable dhcp_dis_pkt = Ether(dst="ff:ff:ff:ff:ff:ff", src=hw)/IP(src="0.0.0.0",dst="255.255.255.255") / UDP(sport=68,dport=67)/BOOTP(op=1, xid=x_id, chaddr=hw_str)/DHCP(options=[("message-type","discover"),("end")])

#Sending the DISCOVER packet and catching the OFFER reply #Generates two lists (answ and unansw). answd is a list containg a tuple: the first element is the DISCOVER packet, the second is the OFFER packet answd, unanswd = srp(dhcp_dis_pkt, iface=pkt_inf, timeout = 2.5,

verbose=0)

#print answd #print unanswd #print answd.summary() #print unanswd.summary() #print answd[0][1][BOOTP].yiaddr

#The IP offered by the DHCP server to the client is extracted from the received answer offered_ip = answd[0][1][BOOTP].yiaddr #print offered_ip

Page 32 of 185

33

Python Network Programming Course Applications Guide by Mihai Cătălin Teodosiu

#Assigning the .command() output of a captured DHCP REQUEST packet to a variable dhcp_req_pkt = Ether(dst="ff:ff:ff:ff:ff:ff", src=hw)/IP(src="0.0.0.0",dst="255.255.255.255") / UDP(sport=68,dport=67)/BOOTP(op=1, xid=x_id, chaddr=hw_str)/DHCP(options=[("message-type","request"),("requested_addr", offered_ip),("end")])

#Sending the REQUEST for the offered IP address #Capturing the ACK from the server answr, unanswr = srp(dhcp_req_pkt, iface=pkt_inf, timeout = 2.5,

verbose=0)

#print answr #print unanswr #print answr[0][1][IP].src #print answr[0][1][BOOTP].yiaddr

#The IP offered by the DHCP server to the client is extracted from the received answer offered_ip_ack = answr[0][1][BOOTP].yiaddr

#DHCP Server IP/ID server_ip = answr[0][1][IP].src #print server_ip

#Adding each leased IP to the list of leases all_given_leases.append(offered_ip_ack)

#Adding the server IP to a list server_id.append(server_ip)

client_mac.append(hw)

return all_given_leases, server_id, client_mac

############# Application #3 - Part #3 #############

################## DHCP RELEASE ################# def generate_dhcp_release(ip, hw, server):

#Defining DHCP Transaction ID x_id = random.randrange(1, 1000000) hw_str = mac2str(hw)

#Creating the RELEASE packet dhcp_rls_pkt = IP(src=ip,dst=server) / UDP(sport=68,dport=67)/BOOTP(chaddr=hw_str, ciaddr=ip, xid=x_id)/DHCP(options=[("message-type","release"),("server_id", server),("end")])

Page 33 of 185

34

Python Network Programming Course Applications Guide by Mihai Cătălin Teodosiu

#Sending the RELEASE packet send(dhcp_rls_pkt, verbose=0)

############# Application #3 - Part #4 #############

################## USER MENU ################# try:

#Enter option for the first screen while True:

print "\nUse this tool to:\ns - Simulate DHCP Clients\nr - Simulate DHCP Release\ne - Exit program\n"

user_option_sim = raw_input("Enter your choice: ")

if user_option_sim == "s":

print "\nObtained leases will be exported to 'DHCP_Leases.txt'!"

pkt_no = raw_input("\nNumber of DHCP clients to simulate: ")

pkt_inf = raw_input("Interface on which to send packets: ")

print "\nWaiting for clients to obtain IP addresses

try:

\n"

#Calling the function for the required number of times

(pkt_no)

for iterate in range(0, int(pkt_no)):

all_leased_ips = generate_dhcp_seq()[0]

#print all_leased_ips

except IndexError:

print "No DHCP Server detected or connection is broken." print "Check your network settings and try again.\n" sys.exit()

#List of all leased IPs dhcp_leases = open("DHCP_Leases.txt", "w")

#print all_leased_ips #print server_id #print client_mac

#Print each leased IP to the file for index, each_ip in enumerate(all_leased_ips):

print >>dhcp_leases, each_ip + "," + server_id[index] + "," + client_mac[index]

dhcp_leases.close()

Page 34 of 185

35

Python Network Programming Course Applications Guide by Mihai Cătălin Teodosiu

continue

elif user_option_sim == "r":

while True:

print "\ns - Release a single address\na - Release all addresses\ne - Exit to the previous screen\n"

user_option_release = raw_input("Enter your choice: ")

if user_option_release == "s":

print "\n"

user_option_address = raw_input("Enter IP address to

release: ")

#print all_leased_ips #print server_id #print client_mac

try:

#Check if required IP is in the list and run the

release function for it

if user_option_address in all_leased_ips:

index = all_leased_ips.index(user_option_address)

generate_dhcp_release(user_option_address, client_mac[index], server_id[index])

print "\nSending RELEASE packet

else:

\n"

print "IP Address not in list.\n" continue

except (NameError, IndexError):

print "\nSimulating DHCP RELEASES cannot be done separately, without prior DHCP Client simulation." print "Restart the program and simulate DHCP Clients and RELEASES in the same program session.\n" sys.exit()

elif user_option_release == "a":

#print all_leased_ips #print server_id #print client_mac

try:

#Check if required IP is in the list and run the

release function for it

for user_option_address in all_leased_ips:

Page 35 of 185

36

Python Network Programming Course Applications Guide by Mihai Cătălin Teodosiu

index = all_leased_ips.index(user_option_address)

generate_dhcp_release(user_option_address, client_mac[index], server_id[index])

except (NameError, IndexError):

print "\nSimulating DHCP RELEASES cannot be done separately, without prior DHCP Client simulation." print "Restart the program and simulate DHCP Clients and RELEASES in the same program session.\n" sys.exit()

print "\nThe RELEASE packets have been sent.\n"

#Erasing all leases from the file open("DHCP_Leases.txt", "w").close()

print "File 'DHCP_Leases.txt' has been cleared."

else:

continue

else:

break

print "Exiting sys.exit()

See ya

\n\n"

except KeyboardInterrupt:

print "\n\nProgram aborted by user. Exiting sys.exit()

#End of program

Page 36 of 185

\n"

37

Python Network Programming Course Applications Guide by Mihai Cătălin Teodosiu

2.7. Application #4 - Network parameters extraction

(full code, reference: Section 16. Application #4 - Network parameters extraction)

Logical flow diagram

(full code, reference: Section 16. Application #4 - Network parameters extraction) Logical flow diagram Page 37

Page 37 of 185

38

Python Network Programming Course Applications Guide by Mihai Cătălin Teodosiu

############# Application #4 - Part #1 #############

#Configure the permissions on the script first! 'chmod 755 script.py"

#Make sure to have SSHv2 enabled and RSA 1024 bit key generated on every device!

import MySQLdb as mdb import paramiko import threading import os.path import subprocess import datetime import time import sys import re

#Module for output coloring from colorama import init, deinit, Fore, Style

# Procedure for configuring Linux scheduler:

# root@kali:/# crontab -l

# root@kali:/# crontab -e

# Add the following line to run the script every 5 minutes, every hour, every day, every month:

# */5 * * * * /path_to_file/NetMon_SQL_v1.py /path_to_file/NETWORK_IP /path_to_file/SSH_USERPASS.txt /path_to_file/SQL_CONN.txt

# For more info about configuring scheduler:

http://kvz.io/blog/2007/07/29/schedule-tasks-on-linux-using-crontab/

# Before scheduling this task, run the script in the console to check for errors:

# Go to the folder containing the script and all files, using cd /netmon_folder_path

# Enter this command: python NetMon_SQL_v1.py NETWORK_IP.txt

SSH_USERPASS.txt SQL_CONN.txt

# Check the console output and SQL_Error_Log.txt file for any errors.

# Running the script is recommended at intervals of at least 5 minutes.

view scheduled tasks edit scheduler

#Initialize colorama init()

#Checking number of arguments passed into the script if len(sys.argv) == 4:

ip_file = sys.argv[1] user_file = sys.argv[2] sql_file = sys.argv[3]

print Fore.BLUE + Style.BRIGHT + "\n\n* The script will be executed using files:\n" print Fore.BLUE + "Cisco network IP file is: " + Fore.YELLOW + "%s" % ip_file

Page 38 of 185

39

Python Network Programming Course Applications Guide by Mihai Cătălin Teodosiu

print Fore.BLUE + "SSHv2 connection file is: " + Fore.YELLOW + "%s" % user_file print Fore.BLUE + "MySQL connection file is: " + Fore.YELLOW + "%s" % sql_file print Fore.BLUE + Style.BRIGHT + "\n"

else:

print Fore.RED + Style.BRIGHT + "\nIncorrect number of arguments (files) passed into the script." print Fore.RED + "Please try again.\n" sys.exit()

#Checking IP address file and content validity def ip_is_valid():

check = False global ip_list

while True:

#Changing exception message try:

#Open user selected file for reading (IP addresses file) selected_ip_file = open(ip_file, 'r')

#Starting from the beginning of the file

selected_ip_file.seek(0)

#Reading each line (IP address) in the file ip_list = selected_ip_file.readlines()

#Closing the file selected_ip_file.close()

except IOError:

print Fore.RED + "\n* File %s does not exist! Please check and try again!\n" % ip_file sys.exit()

#Checking octets for ip in ip_list:

a = ip.split('.')

if (len(a) == 4) and (1 <= int(a[0]) <= 223) and (int(a[0]) != 127) and (int(a[0]) != 169 or int(a[1]) != 254) and (0 <= int(a[1]) <= 255 and 0 <= int(a[2]) <= 255 and 0 <= int(a[3]) <= 255):

check = True break

else:

print '\n* There was an INVALID IP address! Please check and try again!\n' check = False continue

Page 39 of 185

40

Python Network Programming Course Applications Guide by Mihai Cătălin Teodosiu

#Evaluating the 'check' flag if check == False:

sys.exit()

elif check == True:

break

#Checking IP reachability print "* Checking IP reachability

check2 = False

while True:

for ip in ip_list:

Please wait

\n"

ping_reply = subprocess.call(['ping', '-c', '3', '-w', '3', '- q', '-n', ip], stdout = subprocess.PIPE)

if ping_reply == 0:

check2 = True continue

elif ping_reply == 2:

print Fore.RED + "\n* No response from device %s." % ip check2 = False break

else:

print Fore.RED + "\n* Ping to the following device has

FAILED:", ip

check2 = False break

#Evaluating the 'check' flag if check2 == False:

print Fore.RED + "* Please re-check IP address list or

device.\n"

sys.exit()

elif check2 == True:

print '\n* All devices are reachable. Checking SSHv2

connection file break

\n'

#Checking user file validity def user_is_valid():

global user_file

while True:

#Changing output messages if os.path.isfile(user_file) == True:

print "\n* SSHv2 connection file has been validated. Checking

MySQL connection file break

\n"

Page 40 of 185

41

Python Network Programming Course Applications Guide by Mihai Cătălin Teodosiu

else:

print Fore.RED + "\n* File %s does not exist! Please check and try again!\n" % user_file sys.exit()

#Checking SQL connection command file validity def sql_is_valid():

global sql_file

while True:

#Changing output messages if os.path.isfile(sql_file) == True:

print "\n* MySQL connection file has been validated

\n"

print "\n* Any MySQL errors will be logged to: " + Fore.YELLOW + "SQL_Error_Log.txt\n" + Fore.BLUE

print "\n* Reading network data and writing to MySQL break

else:

\n"

print Fore.RED + "\n* File %s does not exist! Please check and try again!\n" % sql_file sys.exit()

#Change exception message try:

#Calling IP validity function ip_is_valid()

except KeyboardInterrupt:

print Fore.RED + "\n\n* Program aborted by user. Exiting sys.exit()

\n"

#Change exception message try:

#Calling user file validity function user_is_valid()

except KeyboardInterrupt:

print Fore.RED + "\n\n* Program aborted by user. Exiting sys.exit()

\n"

#Change exception message try:

#Calling MySQL file validity function sql_is_valid()

except KeyboardInterrupt:

print Fore.RED + "\n\n* Program aborted by user. Exiting sys.exit()

\n"

############# Application #4 - Part #2 #############

check_sql = True def sql_connection(command, values):

Page 41 of 185

42

Python Network Programming Course Applications Guide by Mihai Cătălin Teodosiu

global check_sql

#Define SQL connection parameters selected_sql_file = open(sql_file, 'r')

#Starting from the beginning of the file

selected_sql_file.seek(0)

sql_host = selected_sql_file.readlines()[0].split(',')[0]

#Starting from the beginning of the file

selected_sql_file.seek(0)

sql_username = selected_sql_file.readlines()[0].split(',')[1]

#Starting from the beginning of the file

selected_sql_file.seek(0)

sql_password = selected_sql_file.readlines()[0].split(',')[2]

#Starting from the beginning of the file

selected_sql_file.seek(0)

sql_database =

selected_sql_file.readlines()[0].split(',')[3].rstrip("\n")

#Connecting and writing to database try:

sql_conn = mdb.connect(sql_host, sql_username, sql_password, sql_database)

cursor = sql_conn.cursor()

cursor.execute("USE NetMon")

cursor.execute(command, values)

#Commit changes sql_conn.commit()

except mdb.Error, e:

sql_log_file = open("SQL_Error_Log.txt", "a")

#Print any SQL errors to the error log file print >>sql_log_file, str(datetime.datetime.now()) + ": Error %d:

%s" % (e.args[0],e.args[1])

#Closing sql log file:

sql_log_file.close()

#Setting check_sql flag to False if any sql error occurs check_sql = False

#Closing the sql file

Page 42 of 185

43

Python Network Programming Course Applications Guide by Mihai Cătălin Teodosiu

selected_sql_file.close()

#Initialize the necessary lists and dictionaries cpu_values = [] io_mem_values = [] proc_mem_values = [] upint_values = []

top3_cpu = {} top3_io_mem = {} top3_proc_mem = {} top3_upint = {}

#Open SSHv2 connection to devices def open_ssh_conn(ip):

global check_sql

#Change exception message try:

#Define SSH parameters selected_user_file = open(user_file, 'r')

#Starting from the beginning of the file

selected_user_file.seek(0)

#Reading the username from the file username = selected_user_file.readlines()[0].split(',')[0]

#Starting from the beginning of the file

selected_user_file.seek(0)

#Reading the password from the file password =

selected_user_file.readlines()[0].split(',')[1].rstrip("\n")

#Logging into device session = paramiko.SSHClient()

#For testing purposes, this allows auto-accepting unknown host

keys

#Do not use in production! The default would be RejectPolicy session.set_missing_host_key_policy(paramiko.AutoAddPolicy())

#Connect to the device using username and password session.connect(ip, username = username, password = password)

#Start an interactive shell session on the router connection = session.invoke_shell()

#Setting terminal length for entire output - disable pagination connection.send("terminal length 0\n")

time.sleep(1)

Page 43 of 185

44

Python Network Programming Course Applications Guide by Mihai Cătălin Teodosiu

#Entering global config mode #connection.send("\n") #connection.send("configure terminal\n")

#time.sleep(1)

#Reading commands from within the script #Using the "\" line continuation character for better readability of the commands to be sent selected_cisco_commands = '''show version | include (, Version|uptime is|bytes of memory|Hz)&\ show inventory&\ show interfaces | include bia&\ show processes cpu | include CPU

utilization&\

(Ethernet|Serial)&\

Device ID&\

Protocol'''

show memory statistics&\ show ip int brief | include

show cdp neighbors detail | include

show ip protocols | include Routing

#Splitting commands by the "&" character command_list = selected_cisco_commands.split("&")

#Writing each line in the command string to the device for each_line in command_list:

connection.send(each_line + '\n')

time.sleep(3)

#Closing the user file selected_user_file.close()

#Checking command output for IOS syntax errors output = connection.recv(65535)

if re.search(r"% Invalid input detected at", output):

print Fore.RED + "* There was at least one IOS syntax error on device %s" % ip

else:

print Fore.GREEN + "* All parameters were extracted from device %s" % ip,

#Test for reading command output #print output + "\n"

############# Application #4 - Part #3 #############

MySQL

#Extracting device parameters

starting #

with the ones destined to the NetworkDevices table in

dev_hostname = re.search(r"(.+) uptime is", output)

Page 44 of 185

45

Python Network Programming Course Applications Guide by Mihai Cătălin Teodosiu

hostname = dev_hostname.group(1) #print hostname

dev_mac = re.findall(r"\(bia (.+?)\)", output) #print dev_mac mac = dev_mac[0] #print mac

dev_vendor = re.search(r"(.+?) (.+) bytes of memory", output) vendor = dev_vendor.group(1) #print vendor

dev_model = re.search(r"(.+?) (.+?) (.+) bytes of memory", output) model = dev_model.group(2) #print model

dev_image_name = re.search(r" \((.+)\), Version", output) image_name = dev_image_name.group(1) #print image_name

dev_os = re.search(r"\), Version (.+),", output) os = dev_os.group(1) #print os

serial_no = "" if len(re.findall(r"(.+), SN: (.+?)\r\n", output)) == 0:

serial_no = "unknown" else:

serial_no = re.findall(r"(.+), SN: (.+?)\r\n",

output)[0][1].strip()

#print serial_no

dev_uptime = re.search(r" uptime is (.+)\n", output) uptime = dev_uptime.group(1) uptime_value_list = uptime.split(', ')

#Getting the device uptime in seconds y_sec = 0 w_sec = 0 d_sec = 0 h_sec = 0 m_sec = 0

for j in uptime_value_list:

if 'year' in j:

y_sec = int(j.split(' ')[0]) * 31449600

elif 'week' in j:

w_sec = int(j.split(' ')[0]) * 604800

elif 'day' in j:

d_sec = int(j.split(' ')[0]) * 86400

Page 45 of 185

46

Python Network Programming Course Applications Guide by Mihai Cătălin Teodosiu

elif 'hour' in j:

h_sec = int(j.split(' ')[0]) * 3600

elif 'minute' in j:

m_sec = int(j.split(' ')[0]) * 60

total_uptime_sec = y_sec + w_sec + d_sec + h_sec + m_sec #print total_uptime_sec

cpu_model = "" if re.search(r".isco (.+?) \((.+)\) processor(.+)\n", output) ==

None:

cpu_model = "unknown" else:

cpu_model = re.search(r".isco (.+?) \((.+)\) processor(.+)\n",

output).group(2)

#print cpu_model

cpu_speed = "" if re.search(r"(.+?)at (.+?)MHz(.+)\n", output) == None:

cpu_speed = "unknown" else:

cpu_speed = re.search(r"(.+?)at (.+?)MHz(.+)\n",

output).group(2)

#print cpu_speed

serial_int = "" if re.findall(r"Serial([0-9]*)/([0-9]*) (.+)\n", output) == None:

serial_int = "no serial" else:

serial_int = len(re.findall(r"Serial([0-9]*)/([0-9]*) (.+)\n",

output))

 

#print serial_int

dev_cdp_neighbors = re.findall(r"Device ID: (.+)\r\n", output) all_cdp_neighbors = ','.join(dev_cdp_neighbors) #print all_cdp_neighbors

dev_routing_pro = re.findall(r"Routing Protocol is \"(.+)\"\r\n",

output)

#print dev_routing_pro is_internal = [] is_external = [] for protocol in dev_routing_pro:

if 'bgp' in protocol:

is_external.append(protocol)

else:

is_internal.append(protocol)

internal_pro = ','.join(is_internal) external_pro = ','.join(is_external)

#print internal_pro #print external_pro

Page 46 of 185

47

Python Network Programming Course Applications Guide by Mihai Cătălin Teodosiu

############# Application #4 - Part #4 #############

### CPU ###

dev_cpu_util_per5min = re.search(r"CPU utilization for five seconds: (.+) five minutes: (.+?)%", output) cpu_util_per5min = dev_cpu_util_per5min.group(2) #print cpu_util_per5min

#Append CPU value for each device to the cpu_values list

cpu_values.append(int(cpu_util_per5min))

#Get top 3 CPU devices top3_cpu[hostname] = cpu_util_per5min

### Processor Memory ###

dev_used_proc_mem = re.search(r"Processor(.+)\n ", output) dev_used_proc_mem = dev_used_proc_mem.group(1) #print dev_used_proc_mem

total_proc_mem = dev_used_proc_mem.split(' used_proc_mem = dev_used_proc_mem.split(' #print total_proc_mem #print used_proc_mem

')[2].strip()

')[3].strip()

#Get percentage of used proc mem proc_mem_percent = format(int(used_proc_mem) * 100 / float(total_proc_mem), ".2f") #print proc_mem_percent

#Append used proc memory values for each device to the mem_values

list

proc_mem_values.append(float(proc_mem_percent))

#Get top 3 proc memory devices top3_proc_mem[hostname] = proc_mem_percent

### I/O Memory ###

dev_used_io_mem = re.search(r"

dev_used_io_mem = dev_used_io_mem.group(1)

#print dev_used_io_mem

I/O(.+)\n", output)

total_io_mem = dev_used_io_mem.split(' used_io_mem = dev_used_io_mem.split(' #print total_io_mem #print used_io_mem

')[2].strip()

')[3].strip()

#Get percentage of used proc mem io_mem_percent = format(int(used_io_mem) * 100 / float(total_io_mem), ".2f") #print io_mem_percent

Page 47 of 185

48

Python Network Programming Course Applications Guide by Mihai Cătălin Teodosiu

#Append used I/O memory values for each device to the mem_values

list

io_mem_values.append(float(io_mem_percent))

#Get top 3 I/O memory devices top3_io_mem[hostname] = io_mem_percent

### UP Interfaces ###

dev_total_int = re.findall(r"([A-Za-z]*)Ethernet([0- 9]*)(.+)YES(.+)\n", output) total_int = len(dev_total_int) #print total_int

dev_total_up_int = re.findall(r"(.+)Ethernet([0-9]*)/([0- 9]*)[\s]*(.+)up[\s]*up", output) total_up_int = len(dev_total_up_int) #print total_up_int

#Get percentage of Eth UP interfaces out of the total number of Eth interfaces intf_percent = format(total_up_int * 100 / float(total_int),

".2f")

#print intf_percent

#Append percentage of UP interfaces for each device to the upint_values list upint_values.append(float(intf_percent))

#Get top 3 UP Eth interfaces density devices top3_upint[hostname] = intf_percent

#Insert/Update if exists all network devices data into the MySQL database table NetworkDevices. Calling sql_connection function sql_connection("REPLACE INTO NetworkDevices(Hostname,MACAddr,Vendor,Model,Image,IOSVersion,SerialNo,Upt ime,CPUModel,CPUSpeed,SerialIntfNo,CiscoNeighbors,IntRoutingPro,ExtRouting Pro) VALUES(%s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s)", (hostname, mac, vendor, model, image_name, os, serial_no, total_uptime_sec, cpu_model, cpu_speed, serial_int, all_cdp_neighbors, internal_pro, external_pro))

#Closing the SSH connection session.close()

except paramiko.AuthenticationException:

print Fore.RED + "* Invalid SSH username or password. \n* Please check the username/password file or the device configuration!\n" check_sql = False

#Creating threads def create_threads():

threads = []

Page 48 of 185

49

Python Network Programming Course Applications Guide by Mihai Cătălin Teodosiu

for ip in ip_list:

th = threading.Thread(target = open_ssh_conn, args = (ip,)) #args is a tuple with a single element th.start() threads.append(th)

for th in threads:

th.join()

#Calling threads creation function create_threads()

############# Application #4 - Part #5 #############

#Poll date and time are based on the system clock

poll_timestamp = datetime.datetime.now() #print poll_timestamp

###Testing code### #print cpu_values #print proc_mem_values #print io_mem_values #print upint_values

#print top3_cpu #print top3_proc_mem #print top3_io_mem #print top3_upint ###

#Defining a function to get top 3 devices in CPU/mem/intf usage def top3(each_dict):

global top3_list top3 = []

for host, usage in sorted(each_dict.items(), key = lambda x: x[1], reverse = True)[:3]:

top3.append(host)

top3_list = ",".join(top3) #print top3_list

#CPU average function def cpu_average():

try:

cpu = sum(cpu_values) / float(len(cpu_values))

#Calling the top3 function for the CPU dictionary

top3(top3_cpu)

#Write values to the MySQL database CPUUtilization table sql_connection("INSERT INTO

CPUUtilization(NetworkCPUUtilizationPercent,Top3CPUDevices,PollTimestamp)

VALUES(%s, %s, %s)", (cpu, top3_list, poll_timestamp))

Page 49 of 185

50

Python Network Programming Course Applications Guide by Mihai Cătălin Teodosiu

except ZeroDivisionError:

print "* There was an error while computing a network parameter. No record has been added to MySQL. Please retry."

cpu_average()

#Used proc memory average function def mem_proc_average():

try:

mem_proc = sum(proc_mem_values) / float(len(proc_mem_values))

#Calling the top3 function for the mem proc dictionary

top3(top3_proc_mem)

#Write values to the MySQL database ProcMemUtilization table sql_connection("INSERT INTO

ProcMemUtilization(NetworkProcMemUtilizationPercent,Top3ProcMemDevices,Pol

lTimestamp) VALUES(%s, %s, %s)", (mem_proc, top3_list, poll_timestamp))

except ZeroDivisionError:

print "* There was an error while computing a network parameter. No record has been added to MySQL. Please retry."

mem_proc_average()

#Used I/O memory average function def mem_io_average():

try:

mem_io = sum(io_mem_values) / float(len(io_mem_values))

#Calling the top3 function for the mem I/O dictionary

top3(top3_io_mem)

#Write values to the MySQL database IOMemUtilization table sql_connection("INSERT INTO

IOMemUtilization(NetworkIOMemUtilizationPercent,Top3IOMemDevices,PollTimes

tamp) VALUES(%s, %s, %s)", (mem_io, top3_list, poll_timestamp))

except ZeroDivisionError:

print "* There was an error while computing a network parameter. No record has been added to MySQL. Please retry."

mem_io_average()

#Total UP Eth interfaces function def upint_total():

try:

upint = sum(upint_values) / float(len(upint_values))

#Calling the top3 function for the UP intf dictionary

top3(top3_upint)

#Write values to the MySQL database UPEthInterfaces table

Page 50 of 185

51

Python Network Programming Course Applications Guide by Mihai Cătălin Teodosiu

sql_connection("INSERT INTO

UPEthInterfaces(NetworkUPEthIntfPercent,Top3UPEthIntf,PollTimestamp)

VALUES(%s, %s, %s)", (upint, top3_list, poll_timestamp))

except ZeroDivisionError:

print "* There was an error while computing a network parameter. No record has been added to MySQL. Please retry."

upint_total()

#print check_sql

if check_sql == True:

print "\n* All parameters were successfully exported to MySQL."

else:

print Fore.RED + "\n* There was a problem exporting data to MySQL.\n* Check the files, database and SQL_Error_Log.txt.\n"

#De-initialize colorama deinit()

#End of program

Page 51 of 185

52

Python Network Programming Course Applications Guide by Mihai Cătălin Teodosiu

2.8. Application #5 - OSPF network discovery via SNMP

(full code, reference: Section 17. Application #5 - OSPF network discovery via SNMP)

Logical flow diagram

(full code, reference: Section 17. Application #5 - OSPF network discovery via SNMP) Logical flow diagram

Page 52 of 185

53

Python Network Programming Course Applications Guide by Mihai Cătălin Teodosiu

############# Application #5 - Part #1 #############

''' Make the following configuration on each router in the network:

configure terminal snmp-server community public RO '''

# Open a regular Linux terminal

# Go to the folder containing the script, using cd /folder_path

# Enter "sudo python OSPF_SNMP.py" and the password for the account

# You may also need to configure the permissions on the script first! "chmod 755 script.py"

# Check the console output for any errors

#Necessary Python packages (they are already installed on the Debian VM) #https://pypi.python.org/pypi/setuptools #https://pypi.python.org/pypi/networkx #https://pypi.python.org/pypi/matplotlib #https://pypi.python.org/pypi/pysnmp #https://pypi.python.org/pypi/colorama

import pprint import subprocess import binascii import sys

try:

import matplotlib.pyplot as matp

except ImportError:

print Fore.RED + Style.BRIGHT + "\n* Module matplotlib needs to be installed on your system." print "* Download it from: https://pypi.python.org/pypi/matplotlib\n" + Fore.WHITE + Style.BRIGHT sys.exit()

try:

import networkx as nx

except ImportError:

print Fore.RED + Style.BRIGHT + "\n* Module networkx needs to be installed on your system." print "* Download it from: https://pypi.python.org/pypi/networkx" print "* You should also install decorator:

https://pypi.python.org/pypi/decorator\n" + Fore.WHITE + Style.BRIGHT sys.exit()

try:

#Module for output coloring from colorama import init, deinit, Fore, Style

except ImportError:

Page 53 of 185

54

Python Network Programming Course Applications Guide by Mihai Cătălin Teodosiu

print Fore.RED + Style.BRIGHT + "\n* Module colorama needs to be installed on your system." print "* Download it from: https://pypi.python.org/pypi/colorama\n" + Fore.WHITE + Style.BRIGHT sys.exit()

try:

#Module for SNMP from pysnmp.entity.rfc3413.oneliner import cmdgen

except ImportError:

print Fore.RED + Style.BRIGHT + "\n* Module pysnmp needs to be installed on your system." print "* Download it from: https://pypi.python.org/pypi/pysnmp\n" + Fore.WHITE + Style.BRIGHT sys.exit()

#Initialize colorama init()

#Prompting user for input try:

print Style.BRIGHT + "\n######################## OSPF DISCOVERY TOOL ########################" print "Make sure to connect to a device already running OSPF in the network!" print "SNMP community string should be the same on all devices running OSPF!\n" ip = raw_input(Fore.BLUE + Style.BRIGHT + "\n* Please enter root device IP: ") comm = raw_input("\n* Please enter community string: ")

except KeyboardInterrupt:

print Fore.RED + Style.BRIGHT + "\n\n* Program aborted by user.

Exiting

\n"

sys.exit()

############# Application #5 - Part #2 #############

#Checking IP address validity def ip_is_valid():

while True:

#Checking octets a = ip.split('.')

if (len(a) == 4) and (1 <= int(a[0]) <= 223) and (int(a[0]) != 127) and (int(a[0]) != 169 or int(a[1]) != 254) and (0 <= int(a[1]) <= 255 and 0 <= int(a[2]) <= 255 and 0 <= int(a[3]) <= 255):

break

Page 54 of 185

55

Python Network Programming Course Applications Guide by Mihai Cătălin Teodosiu

else:

print '\n* There was an INVALID IP address! Please check and try again!\n' sys.exit()

#Checking IP reachability

print Fore.GREEN + Style.BRIGHT + "\n* Valid IP address. Checking IP

reachability

\n"

while True:

ping_reply = subprocess.call(['ping', '-c', '3', '-w', '3', '-q', '-n', ip], stdout = subprocess.PIPE)

if ping_reply == 0:

print Fore.GREEN + Style.BRIGHT + "* Device is reachable.

Performing SNMP extraction

\n"

moments

print Fore.GREEN + Style.BRIGHT + "* This may take a few \n" break

elif ping_reply == 2:

print Fore.RED + Style.BRIGHT + "\n* No response from device

%s." % ip

sys.exit()

else:

print Fore.RED + Style.BRIGHT + "\n* Ping to the following device has FAILED:", ip print "\n" sys.exit()

#Change exception message try:

#Calling IP validity function ip_is_valid()

except KeyboardInterrupt:

print Fore.RED + Style.BRIGHT + "\n\n* Program aborted by user.

Exiting

\n"

sys.exit()

ospf = []

#SNMP function def snmp_get(ip):

nbridlist = [] nbriplist = [] ospf_devices = {}

#Creating command generator object cmdGen = cmdgen.CommandGenerator()

Page 55 of 185

56

Python Network Programming Course Applications Guide by Mihai Cătălin Teodosiu

#Performing SNMP GETNEXT operations on the OSPF OIDs #The basic syntax of nextCmd: nextCmd(authData, transportTarget, *varNames) #The nextCmd method returns a tuple of (errorIndication, errorStatus, errorIndex, varBindTable)

errorIndication, errorStatus, errorIndex, varBindNbrTable = cmdGen.nextCmd(cmdgen.CommunityData(comm),

cmdgen.UdpTransportTarget((ip, 161)),

'1.3.6.1.2.1.14.10.1.3')

#print cmdGen.nextCmd(cmdgen.CommunityData(comm),cmdgen.UdpTransportTarget((ip,

161)),'1.3.6.1.2.1.14.10.1.3')

#print varBindNbrTable

errorIndication, errorStatus, errorIndex, varBindNbrIpTable = cmdGen.nextCmd(cmdgen.CommunityData(comm),

cmdgen.UdpTransportTarget((ip, 161)),

'1.3.6.1.2.1.14.10.1.1')

#print varBindNbrIpTable

errorIndication, errorStatus, errorIndex, varBindHostTable = cmdGen.nextCmd(cmdgen.CommunityData(comm),

cmdgen.UdpTransportTarget((ip, 161)),

'1.3.6.1.4.1.9.2.1.3')

#print varBindHostTable

errorIndication, errorStatus, errorIndex, varBindHostIdTable = cmdGen.nextCmd(cmdgen.CommunityData(comm),

cmdgen.UdpTransportTarget((ip, 161)),

'1.3.6.1.2.1.14.1.1')

#print varBindHostIdTable

#Extract and print out the results for varBindNbrTableRow in varBindNbrTable:

for oid, nbrid in varBindNbrTableRow:

hex_string = binascii.hexlify(str(nbrid)) #print hex_string octets = [hex_string[i:i+2] for i in range(0, len(hex_string),

2)]

#print octets ip = [int(i, 16) for i in octets]

Page 56 of 185

57

Python Network Programming Course Applications Guide by Mihai Cătălin Teodosiu

#print ip nbr_r_id = '.'.join(str(i) for i in ip) #print nbr_r_id nbridlist.append(nbr_r_id) #print('%s = %s' % (oid, nbr_r_id))

for varBindNbrIpTableRow in varBindNbrIpTable:

for oid, nbrip in varBindNbrIpTableRow:

hex_string = binascii.hexlify(str(nbrip)) octets = [hex_string[i:i+2] for i in range(0, len(hex_string),

2)]

 

ip = [int(i, 16) for i in octets] nbr_ip = '.'.join(str(i) for i in ip) nbriplist.append(nbr_ip) #print('%s = %s' % (oid, nbr_ip))

for varBindHostTableRow in varBindHostTable:

for oid, host in varBindHostTableRow:

ospf_host = str(host) #print('%s = %s' % (oid, host))

for varBindHostIdTableRow in varBindHostIdTable:

for oid, hostid in varBindHostIdTableRow:

hex_string = binascii.hexlify(str(hostid)) octets = [hex_string[i:i+2] for i in range(0, len(hex_string),

2)]

ip = [int(i, 16) for i in octets] ospf_host_id = '.'.join(str(i) for i in ip) #print('%s = %s' % (oid, hostid))

#Adding OSPF data by device in the ospf_device dictionary ospf_devices["Host"] = ospf_host ospf_devices["HostId"] = ospf_host_id ospf_devices["NbrRtrId"] = nbridlist ospf_devices["NbrRtrIp"] = nbriplist

ospf.append(ospf_devices)

return ospf

#Calling the function for the user specified IP address ospf = snmp_get(ip) #pprint.pprint(ospf)

############# Application #5 - Part #3 #############

def find_unqueried_neighbors():

#Host OSPF Router IDs all_host_ids = []

for n in range(0, len(ospf)):

hid = ospf[n]["HostId"] all_host_ids.append(hid)

Page 57 of 185

58

Python Network Programming Course Applications Guide by Mihai Cătălin Teodosiu

#print "HID" #print all_host_ids #print "\n"

#Neighbor OSPF Router IDs all_nbr_ids = []

for n in range(0, len(ospf)):

for each_nid in ospf[n]["NbrRtrId"]:

if each_nid == "0.0.0.0":

pass

else:

all_nbr_ids.append(each_nid)

#print "NBR" #print all_nbr_ids #print list(set(all_nbr_ids)) #print "\n"

#Determining which neighbors were not queried and adding them to a

list

all_outsiders = []

for p in all_nbr_ids:

if p not in all_host_ids:

all_outsiders.append(p)

#print "OUT" #print all_outsiders #print "\n"

#Running the snmp_get() function for each unqueried neighbor for q in all_outsiders:

for r in range(0, len(ospf)):

for index, s in enumerate(ospf[r]["NbrRtrId"]):

#print index, s

if q == s:

new_ip = ospf[r]["NbrRtrIp"][index] snmp_get(new_ip)

else:

pass

return all_host_ids, all_nbr_ids, ospf

############# Application #5 - Part #4 #############

#Calling the function above while True:

Page 58 of 185

59

Python Network Programming Course Applications Guide by Mihai Cătălin Teodosiu

if (len(list(set(find_unqueried_neighbors()[0]))) ==

len(list(set(find_unqueried_neighbors()[1])))):

break

final_devices_list = find_unqueried_neighbors()[2]

#pprint.pprint(final_devices_list)

#Creating list of neighborships neighborship_dict = {}

for each_dictionary in final_devices_list:

for index, each_neighbor in enumerate(each_dictionary["NbrRtrId"]):

each_tuple = (each_dictionary["HostId"], each_neighbor) neighborship_dict[each_tuple] = each_dictionary["NbrRtrIp"][index]

#pprint.pprint(neighborship_dict)

############# Application #5 - Part #5 #############

while True:

try:

#User defined actions print Fore.BLUE + Style.BRIGHT + "* Please choose an action:\n\n1 - Display OSPF devices on the screen\n2 - Export OSPF devices to CSV file\n3 - Generate OSPF network topology\ne - Exit" user_choice = raw_input("\n* Enter your choice: ") print "\n"

#Defining actions if user_choice == "1":

for each_dict in final_devices_list:

print "Hostname: " + Fore.YELLOW + Style.BRIGHT + "%s" % each_dict["Host"] + Fore.BLUE + Style.BRIGHT print "OSFP RID: " + Fore.YELLOW + Style.BRIGHT + "%s" % each_dict["HostId"] + Fore.BLUE + Style.BRIGHT print "OSPF Neighbors by ID: " + Fore.YELLOW + Style.BRIGHT + "%s" % ', '.join(each_dict["NbrRtrId"]) + Fore.BLUE + Style.BRIGHT

print "OSPF Neighbors by IP: " + Fore.YELLOW + Style.BRIGHT + "%s" % ', '.join(each_dict["NbrRtrIp"]) + Fore.BLUE + Style.BRIGHT

print "\n"

continue

#Printing devices to CSV file elif user_choice == "2":

print Fore.CYAN + Style.BRIGHT + "* Generating " + Fore.YELLOW

+ Style.BRIGHT + "OSPF_DEVICES" + Fore.CYAN + Style.BRIGHT + " file

Page 59 of 185

\n"

60

Python Network Programming Course Applications Guide by Mihai Cătălin Teodosiu

print Fore.CYAN + Style.BRIGHT + "* Check the script folder. Import the file into Excel for a better view of the devices.\n"

csv_file = open("OSPF_DEVICES.txt", "w")

print >>csv_file, "Hostname" + ";" + "OSPFRouterID" + ";" + "OSPFNeighborRouterID" + ";" + "OSPFNeighborIP"

for each_dict in final_devices_list:

print >>csv_file, each_dict["Host"] + ";" + each_dict["HostId"] + ";" + ', '.join(each_dict["NbrRtrId"]) + ";" + ', '.join(each_dict["NbrRtrIp"])

csv_file.close()

continue

############# Application #5 - Part #6 #############

#Generating OSPF network topology elif user_choice == "3":

print Fore.CYAN + Style.BRIGHT + "* Generating OSPF network

topology

\n"

+ Fore.BLUE + Style.BRIGHT

#Drawing the topology using the list of neighborships G = nx.Graph() G.add_edges_from(neighborship_dict.keys()) pos = nx.spring_layout(G, k = 0.1, iterations = 70) nx.draw_networkx_labels(G, pos, font_size = 9, font_family = "sans-serif", font_weight = "bold") nx.draw_networkx_edges(G, pos, width = 4, alpha = 0.4, edge_color = 'black') nx.draw_networkx_edge_labels(G, pos, neighborship_dict, label_pos = 0.3, font_size = 6) nx.draw(G, pos, node_size = 700, with_labels = False) matp.show()

continue

elif user_choice == "e":

print Fore.RED + Style.BRIGHT + "* Exiting sys.exit()

else:

Bye!\n"

print Fore.RED + Style.BRIGHT + "* Invalid option. Please

retry.\n"

continue

except KeyboardInterrupt:

print Fore.RED + Style.BRIGHT + "\n\n* Program aborted by user.

Exiting

\n"

sys.exit()

#De-initialize colorama

Page 60 of 185

61

Python Network Programming Course Applications Guide by Mihai Cătălin Teodosiu

deinit()

#End of program

Page 61 of 185

62

Python Network Programming Course Applications Guide by Mihai Cătălin Teodosiu

2.9. Application #6 - Basic network sniffer

(full code, reference: Section 18. Application #6 - Basic network sniffer)

This application is a basic network sniffer, which captures some predefined protocols and saves info about each network packet in an external file.

As with the other applications in this course, the full code is available for download.

Based on what you have learned so far in the course, it’s your job now to study, understand and test the code against a network device, as you’ve seen me doing with the previous applications.

Feel free to alter the code in any way you want, add new protocols to be captured, more data to be exported in the external file and so on. New functionality of any kind is welcome. Just make sure to adapt your code to the contents of the packet in Scapy.

Also, please read the first 33 lines in the code carefully, as they are a good introduction to the code that follows.

As you’ve probably guessed, I used Scapy to build this sniffer, because this tool allows packet handling, decoding and analysis in a very intuitive way.

Also, pay special attention to the recommendations and settings that I made before starting to build the user menu and so on. I am referring to these lines and the ones above them:

net_iface = raw_input("* Enter the interface on which to run the sniffer (like 'eth1'):

") subprocess.call(["ifconfig", net_iface, "promisc"], stdout=None, stderr=None, shell=False)

Further more, please read the comments before every code block, as they are good guidelines to what functionality is covered by that piece of code.

As you can see at line 72, the program asks the user what network interface is the capture process going to be executed on. A good example is entering “eth1”.

net_iface = raw_input("* Enter the interface on which to run the sniffer (like 'eth1'):

")

Then, at line 80, the user is asked to enter the number of packets he wishes to be captured by the sniffer:

Page 62 of 185

63

Python Network Programming Course Applications Guide by Mihai Cătălin Teodosiu

pkt_to_sniff = raw_input("* Enter the number of packets to capture (0 is infinity): ")

At line 92, the program requires the number of seconds to run the capture:

time_to_sniff = raw_input("* Enter the number of seconds to run the capture: ")

At line 103, the program asks the user for the protocol to filter the packets by:

proto_sniff = raw_input("* Enter the protocol to filter by (arp|bootp|icmp|0 is all): ")

Lines 115 and 116 are dedicated to choosing the file name and creating the file, by opening it for writing (“w”):

file_name = raw_input("* Please give a name to the log file: ") sniffer_log = open(file_name, "w")

At line 124, you can find the function that takes care of the parameter extraction from each packet and logging the packet info to the file: def packet_log(pkt)

The program implements a counter for each packet, then records the source MAC address and destination MAC address to the file, on a single row.

Finally, the sniffing process is initialized by the sniff() function in Scapy, at line 138, passing the values collected from the user as arguments to this function.

pkt

prn=packet_log)

=

sniff(iface=net_iface,

count=int(pkt_to_sniff),

timeout=int(time_to_sniff),

Now, to test the program, first you should have direct connectivity from the Debian VM to the router in GNS3 (R1 - 192.168.2.101 was my test device):

root@debian:/home/debian/workingdir# ping 192.168.2.101 PING 192.168.2.101 (192.168.2.101) 56(84) bytes of data. 64 bytes from 192.168.2.101: icmp_req=1 ttl=255 time=429 ms

Let’s choose ICMP packets for capturing purposes and after the capture is started, I am going to ping the VM (192.168.2.100) from R1.

Please see the following way to use the program menu as an example:

root@debian:/home/debian/workingdir# python Sniffer.py

! Make sure to run this program as ROOT !

* Enter the interface on which to run the sniffer (like 'eth1'): eth1

Page 63 of 185

64

Python Network Programming Course Applications Guide by Mihai Cătălin Teodosiu

Interface eth1 was set to PROMISC mode.

Enter the number of packets to capture (0 is infinity): 0

The program will capture packets until the timeout expires.

* Enter the number of seconds to run the capture: 10

The program will capture packets for 10 seconds.

* Enter the protocol to filter by (arp|bootp|icmp|0 is all): icmp

The program will capture only ICMP packets.

* Please give a name to the log file: udemy.txt

* Starting the capture

Waiting for 10 seconds

At this point, the program listens for all the ICMP packets it receives in the next 10 seconds on eth1 (ping from R1 now!). The results will be exported to the udemy.txt file.

And these are the results in this case:

root@debian:/home/debian/workingdir# cat udemy.txt Packet 1: SMAC: c0:01:24:c0:00:00 DMAC: 08:00:27:f2:9b:7c Packet 2: SMAC: 08:00:27:f2:9b:7c DMAC: c0:01:24:c0:00:00 Packet 3: SMAC: c0:01:24:c0:00:00 DMAC: 08:00:27:f2:9b:7c Packet 4: SMAC: 08:00:27:f2:9b:7c DMAC: c0:01:24:c0:00:00 Packet 5: SMAC: c0:01:24:c0:00:00 DMAC: 08:00:27:f2:9b:7c Packet 6: SMAC: 08:00:27:f2:9b:7c DMAC: c0:01:24:c0:00:00 Packet 7: SMAC: c0:01:24:c0:00:00 DMAC: 08:00:27:f2:9b:7c Packet 8: SMAC: 08:00:27:f2:9b:7c DMAC: c0:01:24:c0:00:00 Packet 9: SMAC: c0:01:24:c0:00:00 DMAC: 08:00:27:f2:9b:7c Packet 10: SMAC: 08:00:27:f2:9b:7c DMAC: c0:01:24:c0:00:00 Packet 11: SMAC: c0:01:24:c0:00:00 DMAC: 08:00:27:f2:9b:7c Packet 12: SMAC: 08:00:27:f2:9b:7c DMAC: c0:01:24:c0:00:00 Packet 13: SMAC: c0:01:24:c0:00:00 DMAC: 08:00:27:f2:9b:7c Packet 14: SMAC: 08:00:27:f2:9b:7c DMAC: c0:01:24:c0:00:00 Packet 15: SMAC: c0:01:24:c0:00:00 DMAC: 08:00:27:f2:9b:7c Packet 16: SMAC: 08:00:27:f2:9b:7c DMAC: c0:01:24:c0:00:00 Packet 17: SMAC: c0:01:24:c0:00:00 DMAC: 08:00:27:f2:9b:7c Packet 18: SMAC: 08:00:27:f2:9b:7c DMAC: c0:01:24:c0:00:00 Packet 19: SMAC: c0:01:24:c0:00:00 DMAC: 08:00:27:f2:9b:7c

Packet 20: SMAC: 08:00:27:f2:9b:7c DMAC: c0:01:24:c0:00:00

Page 64 of 185

65

Python Network Programming Course Applications Guide by Mihai Cătălin Teodosiu

Logical flow diagram

Guide by Mihai Cătălin Teodosiu Logical flow diagram ############# Application #6 - Basic Network Sniffer

############# Application #6 - Basic Network Sniffer #############

#In Scapy, we will use the sniff() function to capture network packets. #To see a list of what commands Scapy has available, run the lsc() function. #Run the ls() command to see ALL the supported protocols. #Run the ls(protocol) command to see the fields and default values for any protocol. #See packet layers with the .summary() function. #See packet contents with the .show() function. #Dig into a specific packet layer using a list index:

pkts[3][2].summary()

Page 65 of 185

66

Python Network Programming Course Applications Guide by Mihai Cătălin Teodosiu

the #

index chooses the layer for that specific packet. #Using the .command() packet method will return a string of the command necessary to recreate that sniffed packet.

first index chooses the packet out of the pkts list, the second

#To see the list of optional arguments for the sniff() function:

'''

>>> print sniff

Sniff packets sniff([count=0,] [prn=None,] [store=1,] [offline=None,] [lfilter=None,] + L2ListenSocket args) -> list of packets

doc

count: number of packets to capture. 0 means infinity store: wether to store sniffed packets or discard them prn: function to apply to each packet. If something is returned, it is displayed. Ex:

ex: prn = lambda x: x.summary() lfilter: python function applied to each packet to determine if further action may be done ex: lfilter = lambda x: x.haslayer(Padding) offline: pcap file to read packets from, instead of sniffing them timeout: stop sniffing after a given time (default: None) L2socket: use the provided L2socket opened_socket: provide an object ready to use .recv() on stop_filter: python function applied to each packet to determine if we have to stop the capture after this packet ex: stop_filter = lambda x: x.haslayer(TCP)

'''

#Importing the necessary modules

import logging import subprocess

#This will suppress all messages that have a lower level of seriousness than error messages, while running or loading Scapy logging.getLogger("scapy.runtime").setLevel(logging.ERROR) logging.getLogger("scapy.interactive").setLevel(logging.ERROR) logging.getLogger("scapy.loading").setLevel(logging.ERROR)

try:

from scapy.all import *

except ImportError:

print "Scapy package for Python is not installed on your system." print "Get it from https://pypi.python.org/pypi/scapy and try again." sys.exit()

Page 66 of 185

67

Python Network Programming Course Applications Guide by Mihai Cătălin Teodosiu

#Asking the user for some parameters: interface on which to sniff, the number of packets to sniff, the time interval to sniff, the protocol #Making the necessary configurations

print "\n! Make sure to run this program as ROOT !\n"

#Setting network interface in promiscuous mode

#Wikipedia: In computer networking, promiscuous mode or "promisc mode"[1] is a mode for a wired network interface controller (NIC) or wireless network interface controller (WNIC)

that #

central processing unit (CPU) rather than passing only the frames that the controller is intended to receive.

#This mode is normally used for packet sniffing that takes place on a router or on a computer connected to a hub.

causes the controller to pass all traffic it receives to the

#Also, when using our setup (VirtualBox-to-GNS3), you should go to the Settings section for the virtual machine you are using

select #

Promiscuous Mode: Allow All

the adapter that connects to the GNS3 network and set

net_iface = raw_input("* Enter the interface on which to run the sniffer (like 'eth1'): ")

subprocess.call(["ifconfig", net_iface, "promisc"], stdout=None, stderr=None, shell=False)

print "\nInterface %s was set to PROMISC mode." % net_iface print

#Asking the user for the number of packets to sniff (the "count" parameter) pkt_to_sniff = raw_input("Enter the number of packets to capture (0 is infinity): ")

#Considering the case when the user enters 0 (infinity) if int(pkt_to_sniff) != 0:

print "\nThe program will capture %d packets." % int(pkt_to_sniff) print elif int(pkt_to_sniff) == 0:

print "\nThe program will capture packets until the timeout expires." print

#Asking the user for the time interval to sniff (the "timeout" parameter) time_to_sniff = raw_input("* Enter the number of seconds to run the capture: ")

#Handling the value entered by the user if int(time_to_sniff) != 0:

print "\nThe program will capture packets for %d seconds." % int(time_to_sniff) print

Page 67 of 185

68

Python Network Programming Course Applications Guide by Mihai Cătălin Teodosiu

#Asking the user for any protocol filter he might want to apply to the sniffing process #For this example I chose three protocols: ARP, BOOTP, ICMP #You can customize this to add your own desired protocols proto_sniff = raw_input("* Enter the protocol to filter by (arp|bootp|icmp|0 is all): ")

#Considering the case when the user enters 0 (all) if (proto_sniff == "arp") or (proto_sniff == "bootp") or (proto_sniff == "icmp"):

print "\nThe program will capture only %s packets." % proto_sniff.upper() print elif int(proto_sniff) == 0:

print "\nThe program will capture all protocols." print

#Creating an external file for packet logging file_name = raw_input("* Please give a name to the log file: ") sniffer_log = open(file_name, "w")

#Initializing the packet counter packet_no = 0

#This is the function that will be applied to each captured packet #The function will extract some parameters from the packet and then log each packet to an external file def packet_log(pkt):

#The packet index global packet_no

#Filtering the packets based on the protocol. Using the lower() method to ignore the case when searching for the protocol in the packet. if proto_sniff.lower() in pkt[0][1].summary().lower():

packet_no = packet_no + 1 #Writing the data for each packet to the external file print >>sniffer_log, "Packet " + str(packet_no) + ": " + "SMAC: " + pkt[0].src + " DMAC: " + pkt[0].dst

print "\n* Starting the capture time_to_sniff

Waiting for %s seconds

"

%

#Running the sniffing process pkt = sniff(iface=net_iface, count=int(pkt_to_sniff), timeout=int(time_to_sniff), prn=packet_log)

#print pkt.show()

#Printing the closing message print "\n* The timeout of %s seconds has passed." % time_to_sniff

Page 68 of 185

69

Python Network Programming Course Applications Guide by Mihai Cătălin Teodosiu

print "* Please check the %s file to see the captured packets.\n" % file_name

#Closing the log file sniffer_log.close()

#End of program. Feel free to modify it, test it, add new protocols to sniff and improve de code whenever you feel the need to.

Page 69 of 185

70

Python Network Programming Course Applications Guide by Mihai Cătălin Teodosiu

2.10. Application #7 - Configuration file comparator

(full code, reference: Section 19. Application #7 - Configuration file comparator)

This application connects to a router in the network via Telnet, extracts the output of “show running-config” and “show startup-config”, filters the irrelevant lines and finally compares the configurations. Now, I know this can be accomplished using the “show archive config differences”command in Cisco CLI, but I wanted you to know how can this task be accomplished using Python.

As with the other applications in this course, the full code is available for download.

Based on what you have learned so far in the course, it’s your job now to study, understand and test the code against a network device, as you’ve seen me doing with the previous applications.

Feel free to alter the code in any way you want. New functionality of any kind is welcome, enhancements as well. Just make sure to adapt your code to the command output format.

Also, please read the first 13 lines in the code carefully, as they are a good introduction to the code that follows. As you can see, the first thing you should do is configure Telnet access on the router and the username and password:

username teopy privilege 15 password 0 python line vty 0 4 privilege level 15 login local transport input telnet ssh

At line 27, I have defined the ip_validity() function, which takes care of checking whether the IP address of the router, which the user enters at the prompt, is valid or not. You have already seen this kind of validity check in action in the previous applications, so there is nothing new here. The same comment is valid for the file_validity() function (line 46). Both functions are defined at this point and will be called later in the code.

At line 61, the telnet() function is defined, which takes a single parameter:command. The value of this parameter will be passed to theconnection.write() method at line 96.

Starting with line 108, I defined the user menu, which will accept 3 options, except e - Exit program:

1 - Compare running-config with startup-config

2 - Compare running-config with local file

3 - Compare startup-config with local file

I had treated only the first option, comparing the running-config with the startup-config -

Page 70 of 185

71

Python Network Programming Course Applications Guide by Mihai Cătălin Teodosiu

lines 115-196, leaving you with the job of coding and testing the other two options, having my code from option 1 as a guideline.

Now, let’s look at option 1 for a bit. First, I called the ip_validity() function to get this out of the way. Next, a very important step, I called the telnet()function for each of the two commands I am interested in, and saved the returned output to a separate variable: output_run for the running-config and output_start for the startup-config.

Then, I have created (opened for writing) two files, each of them storing the output of the corresponding command. The file names are intuitively chosen. Don’t forget to close the files after writing the contents of those variables, to save the information.

Next, I opened the files for reading and used the readlines() method on each file object to store the lines in each file as elements of a list. Of course, then I closed the files.

Then, using a for loop, I have filtered the lines in each file which were of no interest to our goal. We are only interested in the lines starting with the one defining the IOS version: “version 12.4” for example. That is actually the first relevant line in each file.

Now, after “cleaning” the files, we are left with only the pure router configurations. It’s time to create a new file (file_diff.txt), in which all the config differences are going to be stored. Actually, we are going to compare the two lists obtained with the readlines() method.

Finally, using list comprehensions, we are going to find the lines in the running-config which are not present in the startup-config and vice versa. In case there are multiple differences, we use a for loop to iterate over the lists and then print those differences directly into the file_diff.txt file., one per line As stated in the code, the rule is:

A "+" sign means the line is present in the RUNNING-CONFIG but not in the STARTUP-CONFIG A "-" sign means the line is present in the STARTUP-CONFIG but not in the RUNNING-CONFIG

Now, let’s make a quick test. If you have just started the router and made no config yet, then the startup-config and running-config are the same. No surprise here. But, to make the test more relevant, let’s configure a few things before starting the comparison, without saving the changes to the startup-config. So, let’s go to router R1:

R1(config)#username udemy1 password udemy R1(config)#username udemy2 password udemy R1(config)#username udemy3 password udemy

Now, these three configurations are the differences between the startup-config and the running-config. We should see them after running our program, saved in the file_diff.txt file. Let’s test this:

Page 71 of 185

72

Python Network Programming Course Applications Guide by Mihai Cătălin Teodosiu

root@debian:/home/debian/workingdir# python ConfigFileComp.py

Use this tool to:

1

- Compare running-config with startup-config

2

- Compare running-config with local file

3

- Compare startup-config with local file

e

- Exit program

Enter your choice: 1 Enter an IP address: 192.168.2.101

Please wait while the config file is being analyzed

Use this tool to:

1

- Compare running-config with startup-config

2

- Compare running-config with local file

3

- Compare startup-config with local file

e

- Exit program

Enter your choice: e

Exiting

See ya

Now let’s check the results. We should see all three commands with a “+”sign, right?

root@debian:/home/debian/workingdir# cat file_diff.txt

+username udemy1 password 0 udemy

+username udemy2 password 0 udemy

+username udemy3 password 0 udemy

root@debian:/home/debian/workingdir#

and

success! As expected, the three commands are marked as differences, in the file.

Page 72 of 185

73

Python Network Programming Course Applications Guide by Mihai Cătălin Teodosiu

Logical flow diagram

Network Programming – Course Applications Guide by Mihai Cătălin Teodosiu Logical flow diagram Page 73 of

Page 73 of 185

74

Python Network Programming Course Applications Guide by Mihai Cătălin Teodosiu

############# Application #7 - Config File Comparator #############

#This program will:

# * Connect to a router via Telnet and it will compare the running-config

file to the startup-config file on that device (this can be usually done

with: show archive config differences)

# * Compare a locally stored config file (.txt) with the running-config

file or startup-config file running on a router. #Please see the "Python File Operations" section in the course for a recap of the necessary concepts. #Don't forget to configure Telnet access on the router!

#username teopy privilege 15 password 0 python #line vty 0 4

# privilege level 15

# login local

# transport input telnet ssh

#The first part of the program is very similar to Application #2 in the course.

import telnetlib import os.path import subprocess import time import sys

def ip_validity():

global ip_address

#Checking IP validity while True:

ip_address = raw_input("Enter an IP address: ")

#Checking octets a = ip_address.split('.')

if (len(a) == 4) and (1 <= int(a[0]) <= 223) and (int(a[0]) != 127) and (int(a[0]) != 169 or int(a[1]) != 254) and (0 <= int(a[1]) <= 255 and 0 <= int(a[2]) <= 255 and 0 <= int(a[3]) <= 255):

break

else:

print "\nThe IP address is INVALID! Please retry!\n" continue

def file_validity():

while True:

Page 74 of 185

75

Python Network Programming Course Applications Guide by Mihai Cătălin Teodosiu

cfg_file = raw_input("Enter config file name and extension: ")

#Changing exception message

if os.path.isfile(cfg_file) == True:

print "\nFile was found break

else:

\n"

print "\nFile %s does not exist! Please check and try again!\n" % cfg_file continue

def telnet(command):

#Connecting to router via Telnet #Define telnet parameters username = 'teopy' password = 'python'

#Specify the Telnet port (default is 23, anyway) port = 23

#Specify the connection timeout in seconds for blocking operations, like the connection attempt connection_timeout = 5

#Specify a timeout in seconds. Read until the string is found or until the timout has passed reading_timeout = 5

#Logging into device connection = telnetlib.Telnet(ip_address, port, connection_timeout)

#Waiting to be asked for an username router_output = connection.read_until("Username:", reading_timeout) #Enter the username when asked and a "\n" for Enter connection.write(username + "\n")

#Waiting to be asked for a password router_output = connection.read_until("Password:", reading_timeout) #Enter the password when asked and a "\n" for Enter connection.write(password + "\n")

time.sleep(1)

#Setting terminal length for the entire output - disabling pagination connection.write("terminal length 0\n")

time.sleep(1)

#Entering global config mode connection.write("\n") connection.write(command + "\n")

time.sleep(5)

Page 75 of 185

76

Python Network Programming Course Applications Guide by Mihai Cătălin Teodosiu

router_output = connection.read_very_eager() #print router_output

#Closing the connection connection.close()

return router_output

################## USER MENU ################# try:

#Entering user option while True:

print "\nUse this tool to:\n1 - Compare running-config with startup-config\n2 - Compare running-config with local file\n3 - Compare startup-config with local file\ne - Exit program\n"

user_option = raw_input("Enter your choice: ")

analyzed

if user_option == "1":

###Checking IP validity first### ip_validity()

print "\nPlease wait while the config file is being \n"

output_run = telnet("show running-config") output_start = telnet("show startup-config")

#print output_run #print output_start

###Creating and writing the command output to files### file_run = open("file_run.txt", "w")

print >>file_run, output_run

file_start = open("file_start.txt", "w")

print >>file_start, output_start

#Closing both files after writing file_run.close() file_start.close()

###Comparing the contents of the files and saving the differences to a new file### ###First, reading the lines in each file and storing them as elements of a list###

file_run = open("file_run.txt", "r")

file_start = open("file_start.txt", "r")

list_run = file_run.readlines()

Page 76 of 185

77

Python Network Programming Course Applications Guide by Mihai Cătălin Teodosiu

#print list_run

list_start = file_start.readlines() #print list_start

#Closing both files after reading file_run.close() file_start.close()

###Secondly, filtering the elements at the beginning of each file/list, because we are interested in only the lines starting from "version 12.4" up to the end### #This is done by finding out the index of the element that contains "version" and deleting all the elements at indexes lower than this index

#The deletion process is done by slicing the first elements in the list, up to the index of the element containing "version" and replacing that slice with, basically, nothing

for index, element in enumerate(list_run):

if "version " in element and "!\r\n" == list_run[list_run.index(element) - 1]:

list_run[0:list_run.index(element)] = []

#print list_run

for index, element in enumerate(list_start):

if "version " in element and "!\r\n" == list_start[list_start.index(element) - 1]:

list_start[0:list_start.index(element)] = []

#print list_start

###Finally, comparing the elements in both lists and exporting the differences to a new file### #Inside the file, the following rules apply:

#A "+" sign means the line is present in the RUNNING-CONFIG but not in the STARTUP-CONFIG #A "-" sign means the line is present in the STARTUP-CONFIG but not in the RUNNING-CONFIG

file_diff = open("file_diff.txt", "w")

#Finding lines in the running-config which are not present in the startup-config run_diff = [x for x in list_run if x not in list_start] #print run_diff

#Printing the lines to the file_diff.txt file for line in run_diff:

print >>file_diff, "+" + line

#Finding lines in the startup-config which are not present in the running-config

Page 77 of 185

78

Python Network Programming Course Applications Guide by Mihai Cătălin Teodosiu

start_diff = [x for x in list_start if x not in list_run] #print start_diff

#Printing the lines to the file_diff.txt file for line in start_diff:

print >>file_diff, "-" + line

file_diff.close()

elif user_option == "2":

#Having the code from option "1" as a guideline, you should be able to compare the running-config with a local config file #Write the code and test it pass

elif user_option == "3":

#Having the code from option "1" as a guideline, you should be able to compare the startup-config with a local config file #Write the code and test it pass

else:

print "Exiting sys.exit()

See ya

\n\n"

except KeyboardInterrupt:

print "\n\nProgram aborted by user. Exiting sys.exit()

#End of program

Page 78 of 185

\n"

79

Python Network Programming Course Applications Guide by Mihai Cătălin Teodosiu

2.11. Sockets Server & Client

(full code, reference: Section 21. 100 Exercises, Network Programming Project and Updates)

2.11.1. Socket Server

import socket

srv = socket.socket(socket.AF_INET, socket.SOCK_STREAM)

srv_ip = socket.gethostbyname(socket.gethostname())

srv_port = 11111

srv.bind((srv_ip, srv_port))

srv.listen(2)

client, ip = srv.accept()

client.send("Hi! Welcome to this server!")

client.close()

2.11.2. Socket Client

import socket

cli = socket.socket(socket.AF_INET, socket.SOCK_STREAM)

ip = socket.gethostbyname(socket.gethostname())

port = 11111

cli.connect((ip, port))

server_reply = cli.recv(65535)

print server_reply

cli.close()

Page 79 of 185

80

Python Network Programming Course Applications Guide by Mihai Cătălin Teodosiu

3.

Environment

NOTE! Python programming knowledge is required in order to understand the network automation applications below. Learn Python from scratch inside the video course series!

Python

Network

Programming

Part

2:

Multivendor

-

3.1.1. Configuring IP, Remote Access & SNMP - Cisco IOS

(full code, reference: Section 7. Cisco Network Programming (IOS))

To allow remote access to your Cisco IOS device, you must enter the configuration below.

Please note that some versions of Cisco IOS do not support SSH, so you should make sure you have a SSH-compatible IOS version. According to cisco.com: "The Cisco IOS image used must be a k9(crypto) image in order to support SSH. For example c3750e- universalk9-tar.122-35.SE5.tar is a k9 (crypto) image."

Here is the configuration you have to make in order to allow remote access. Please enter Global Configuration mode (#configure terminal) before pasting this configuration.

So, to configure an IP address, SSHv2 and SNMP use this configuration: