Raw sockets API is an interesting interface available on Unix-like operating systems to put our hands on network packets in their 'raw form' that includes the headers. Usually TCP and UDP sockets which we often use are a only giving us access to the payload of a transport layer datagram. But, there are times where we need to see the IP packet or may be the whole ethernet frame without loosing any content. Raw socket are the solution for this.
In the following example I'm presenting a simple ping-like program written in python that utilizes a raw socket to create the content of an IP packet with an ICMP payload.
Perhaps I would write a more comprehensive blog post about how to use raw sockets. Anyway, everything depends on whether I can spend an enough time on such an exercise. Anyway, let's see. Until then, that's all folks!
In the following example I'm presenting a simple ping-like program written in python that utilizes a raw socket to create the content of an IP packet with an ICMP payload.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 | import socket import sys import time from struct import pack, unpack # checksum function def checksum(msg): s = 0 # loop taking 2 characters at a time for i in range(0, len(msg), 2): w = (ord(msg[i]) << 8) + (ord(msg[i+1]) ) s = s + w s = (s>>16) + (s & 0xffff); #s = s + (s >> 16); #complement and mask to 4 byte short s = ~s & 0xffff return s #create a raw socket try: # this socket is to send custom made raw IP packets s = socket.socket(socket.AF_INET, socket.SOCK_RAW, socket.IPPROTO_RAW) # this socket is to receive ICMP packets s2 = socket.socket(socket.AF_INET, socket.SOCK_RAW, socket.IPPROTO_ICMP) print 'Sockets created' except socket.error, msg: print 'Socket failed. Error Code : ' + str(msg[0]) + ' Message ' + msg[1] sys.exit() #create a raw socket try: # binding to the IP address configured to the network interface in my computer # through which I need to send and receive packets. s.bind(('10.22.220.239',0)) s2.bind(('10.22.220.239',0)) print 'Binding sockets was successful' except socket.error, msg: print 'Bind failed. Error Code : ' + str(msg[0]) + ' Message ' + msg[1] sys.exit() num_rounds = 1 while num_rounds: # creating a packet to send packet = '' source_ip = '10.22.220.239' dest_ip = '8.8.8.8' # or socket.gethostbyname('www.google.com') # ip header fields ip_ihl =5 ip_ver = 4 ip_tos = 0 ip_tot_len = 0 #kernel will fill the correct total length ip_id = 54321 #Id of this packet ip_frag_off = 0 ip_ttl = 255 #ip_proto = socket.IPPROTO_TCP ip_proto = socket.IPPROTO_ICMP ip_check = 0 # kernel will fill the correct checksum ip_saddr = socket.inet_aton (source_ip) #Spoof the source ip address if you want to ip_daddr = socket.inet_aton (dest_ip) ip_ihl_ver = (ip_ver << 4) + ip_ihl # the ! in the pack format string means network order ip_header = pack('!BBHHHBBH4s4s', ip_ihl_ver, ip_tos, ip_tot_len, ip_id, ip_frag_off, ip_ttl, ip_proto, ip_check, ip_saddr, ip_daddr) ICMP_ECHO_REQUEST = 8 # Header is type (8), code (8), checksum (16), id (16), sequence (16) #icmp_header = struct.pack('bbHHh', ICMP_ECHO_REQUEST, 0, 0, id, 1) icmp_header = pack('bbHHh', ICMP_ECHO_REQUEST, 0, 0, 1, 1) icmp_data = 192 * 'Q' # Calculate the checksum on the data and the dummy header. my_checksum = checksum(icmp_header + icmp_data) # Now that we have the right checksum, we put that in. # It's just easier # to make up a new header than to stuff it into the # dummy. #icmp_header = struct.pack('bbHHh', ICMP_ECHO_REQUEST, 0, socket.htons(my_checksum), id, 1) icmp_header = pack('bbHHh', ICMP_ECHO_REQUEST, 0, socket.htons(my_checksum), 1, 1) # final full packet packet = ip_header + icmp_header + icmp_data #Send the packet finally - the port specified has no effect s.sendto(packet, (dest_ip , 0 )) # put this in a loop print 'sent a packet' #--------------------------------------------------------------------------- # receive a packet packet = s2.recvfrom(65565) #packet string from tuple packet = packet[0] #take first 20 characters for the ip header ip_header = packet[0:20] #now unpack them :) iph = unpack('!BBHHHBBH4s4s', ip_header) version_ihl = iph[0] version = version_ihl >> 4 ihl = version_ihl & 0xF iph_length = ihl * 4 ttl = iph[5] protocol = iph[6] s_addr = socket.inet_ntoa(iph[8]) d_addr = socket.inet_ntoa(iph[9]) print 'received a packet' print 'Version : ' + str(version) + ' IP Header Length : ' + str(iph_length) + ' TTL : ' + str(ttl) + ' Protocol : ' + str(protocol) + ' Source Address : ' + str(s_addr) + ' Destination Address : ' + str(d_addr) time.sleep(1) #num_rounds = 0 |
Perhaps I would write a more comprehensive blog post about how to use raw sockets. Anyway, everything depends on whether I can spend an enough time on such an exercise. Anyway, let's see. Until then, that's all folks!
No comments:
Post a Comment