Code a network Packet Sniffer in Python for Linux

By | July 29, 2020

Packet Sniffer

Sniffers are programs that can capture/sniff/detect network traffic packet by packet and analyse them for various reasons. Commonly used in the field of network security.

Wireshark is a very common packet sniffer/protocol analyzer. Packet sniffers can be written in python too.

In this article we are going to write a few very simple sniffers in python for the linux platform. Linux because, although python is a portable, the programs wont run or give similar results on windows for example.

This is due to difference in the implementation of the socket api.

Sniffers shown here don't use any extra libraries like libpcap.
They just use raw sockets. So lets start coding them

Basic Sniffer Code

The most basic form of a sniffer would be

#Packet sniffer in python
#For Linux

import socket

#create an INET, raw socket
s = socket.socket(socket.AF_INET, socket.SOCK_RAW, socket.IPPROTO_TCP)

# receive a packet
while True:
  print s.recvfrom(65565)

Run this with root privileges or sudo on ubuntu :

$ sudo python sniffer.py

The above sniffer works on the principle that a raw socket is capable of receiving all (of its type , like AF_INET) incoming traffic in Linux.

The output could look like this :

$ sudo python raw_socket.py 
("E \x00x\xcc\xfc\x00\x000\x06j%J}G\x13\xc0\xa8\x01\x06\x01\xbb\xa3\xdc\x0b\xbeI\xbf\x1aF[\x83P\x18\xff\xff\x88\xf6\x00\x00\x17\x03\x01\x00\x1c\xbbT\xb3\x07}\xb0\xedqE\x1e\xe7;-\x03\x9bU\xb7\xb1r\xd2\x9e]\xa1\xb8\xac\xa4V\x9a\x17\x03\x01\x00*\xed\x1f\xda\xa4##Qe\x9a\xe9\xd6\xadN\xf4\x9b\xc4\xf0C'\x01\xc4\x82\xdb\xb2\x8d(\xa5\xd0\x06\x95\x13WO\x0f\x8e\x1c\xa6f\x1d\xdf\xe1x", ('74.125.71.19', 0))
('E \x00I\xcc\xfd\x00\x000\x06jSJ}G\x13\xc0\xa8\x01\x06\x01\xbb\xa3\xdc\x0b\xbeJ\x0f\x1aF[\x83P\x18\xff\xff:\x11\x00\x00\x17\x03\x01\x00\x1c\xaa];\t\x81yi\xbbC\xb5\x11\x14(Ct\x13\x10wt\xe0\xbam\xa9\x88/\xf8O{', ('74.125.71.19', 0))
('E \x00(\xcc\xfe\x00\x000\x06jsJ}G\x13\xc0\xa8\x01\x06\x01\xbb\xa3\xdc\x0b\xbeJ0\x1aFa\x19P\x10\xff\xff\xe5\xb0\x00\x00', ('74.125.71.19', 0))
('E \x00(\xcc\xff\x00\x000\x06jrJ}G\x13\xc0\xa8\x01\x06\x01\xbb\xa3\xdc\x0b\xbeJ0\x1aFbtP\x10\xff\xff\xe4U\x00\x00', ('74.125.71.19', 0))

The above is a dump of the network packets in hex. They can be parsed using the unpack function.

Parsing the sniffed packet

Here is the code sniff and parse a TCP packet

#Packet sniffer in python for Linux
#Sniffs only incoming TCP packet

import socket, sys
from struct import *

#create an INET, STREAMing socket
try:
	s = socket.socket(socket.AF_INET, socket.SOCK_RAW, socket.IPPROTO_TCP)
except socket.error , msg:
	print 'Socket could not be created. Error Code : ' + str(msg[0]) + ' Message ' + msg[1]
	sys.exit()

# receive a packet
while True:
	packet = s.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 'Version : ' + str(version) + ' IP Header Length : ' + str(ihl) + ' TTL : ' + str(ttl) + ' Protocol : ' + str(protocol) + ' Source Address : ' + str(s_addr) + ' Destination Address : ' + str(d_addr)
	
	tcp_header = packet[iph_length:iph_length+20]
	
	#now unpack them :)
	tcph = unpack('!HHLLBBHHH' , tcp_header)
	
	source_port = tcph[0]
	dest_port = tcph[1]
	sequence = tcph[2]
	acknowledgement = tcph[3]
	doff_reserved = tcph[4]
	tcph_length = doff_reserved >> 4
	
	print 'Source Port : ' + str(source_port) + ' Dest Port : ' + str(dest_port) + ' Sequence Number : ' + str(sequence) + ' Acknowledgement : ' + str(acknowledgement) + ' TCP header length : ' + str(tcph_length)
	
	h_size = iph_length + tcph_length * 4
	data_size = len(packet) - h_size
	
	#get data from the packet
	data = packet[h_size:]
	
	print 'Data : ' + data
	print

The above code breaks down the packet into IP Header + TCP Header + Data.

The unpack function is used to break down the packet. Documentation

The output of the code should look like this :

$ sudo python tcp_sniffer.py 
Version : 4 IP Header Length : 5 TTL : 56 Protocol : 6 Source Address : 74.125.236.85 Destination Address : 192.168.1.101
Source Port : 443 Dest Port : 38461 Sequence Number : 2809673723 Acknowledgement : 3312567259 TCP header length : 5
Data : 2X???@???0? 
                   ???k?/&???=?5Hz??>5QBp0?O???Z???$??

Version : 4 IP Header Length : 5 TTL : 56 Protocol : 6 Source Address : 74.125.236.85 Destination Address : 192.168.1.101
Source Port : 443 Dest Port : 38461 Sequence Number : 2809673778 Acknowledgement : 3312567259 TCP header length : 5
Data : ?
??j?!I??*??*??Z???;?L?]Y-

Version : 4 IP Header Length : 5 TTL : 52 Protocol : 6 Source Address : 173.192.42.183 Destination Address : 192.168.1.101
Source Port : 80 Dest Port : 52813 Sequence Number : 1202422309 Acknowledgement : 3492657980 TCP header length : 5
Data : HTTP/1.1 502 Bad Gateway
Server: nginx
Date: Tue, 11 Sep 2012 08:56:00 GMT
Content-Type: text/html
Content-Length: 568
Connection: close

<html>
<head><title>502 Bad Gateway</title></head>
<body bgcolor="white">
<center><h1>502 Bad Gateway</h1></center>
<hr><center>nginx</center>
</body>
</html>
<!-- a padding to disable MSIE and Chrome friendly error page -->
<!-- a padding to disable MSIE and Chrome friendly error page -->
<!-- a padding to disable MSIE and Chrome friendly error page -->
<!-- a padding to disable MSIE and Chrome friendly error page -->
<!-- a padding to disable MSIE and Chrome friendly error page -->
<!-- a padding to disable MSIE and Chrome friendly error page -->


Version : 4 IP Header Length : 5 TTL : 56 Protocol : 6 Source Address : 74.125.236.85 Destination Address : 192.168.1.101
Source Port : 443 Dest Port : 38461 Sequence Number : 2809673811 Acknowledgement : 3312568679 TCP header length : 5
Data :

According to RFC 791 an IP header looks like this :

0                   1                   2                   3
0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|Version|  IHL  |Type of Service|          Total Length         |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|         Identification        |Flags|      Fragment Offset    |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|  Time to Live |    Protocol   |         Header Checksum       |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|                       Source Address                          |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|                    Destination Address                        |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|                    Options                    |    Padding    |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+

If the IHL is 5 then total size is 20 bytes hence options+padding is absent.
For TCP packets the protocol is 6. Source address is the source IPv4 address in long format.

Next comes the TCP header :

0                   1                   2                   3
0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|          Source Port          |       Destination Port        |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|                        Sequence Number                        |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|                    Acknowledgment Number                      |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|  Data |           |U|A|P|R|S|F|                               |
| Offset| Reserved  |R|C|S|S|Y|I|            Window             |
|       |           |G|K|H|T|N|N|                               |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|           Checksum            |         Urgent Pointer        |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|                    Options                    |    Padding    |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|                             data                              |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+

and the balance after the TCP header is the data portion.

The C version of the code is here.
The Php version of the code is here.

Note :

1. The above sniffer picks up only TCP packets, because of the declaration :

s = socket.socket(socket.AF_INET, socket.SOCK_RAW, socket.IPPROTO_TCP)

For UDP and ICMP the declaration has to be :

s = socket.socket(socket.AF_INET, socket.SOCK_RAW, socket.IPPROTO_UDP)
s = socket.socket(socket.AF_INET, socket.SOCK_RAW, socket.IPPROTO_ICMP)

You might be tempted to think of doing:

s = socket.socket(socket.AF_INET, socket.SOCK_RAW, socket.IPPROTO_IP)

but this will not work , since IPPROTO_IP is a dummy protocol not a real one.

2. This sniffer picks up only incoming packets.

3. This sniffer delivers only IP frames , which means ethernet headers are not available.

Sniff all data with Ethernet header

Now let us see how we can overcome the above mentioned drawbacks.
The solutions is quite simple.

This line:

s = socket.socket(socket.AF_INET, socket.SOCK_RAW, socket.IPPROTO_TCP)

needs to be changed to:

s = socket.socket( socket.AF_PACKET , socket.SOCK_RAW , socket.ntohs(0x0003))

Now the same socket will receive :

1. All incoming and outgoing traffic.

2. All Ethernet frames , which means all kinds of IP packets(TCP , UDP , ICMP) and even other kinds of packets(like ARP) if there are any.

3. It will also provide the ethernet header as a part of the received packet.

Here is the source code :

# Packet sniffer in python
# For Linux - Sniffs all incoming and outgoing packets :)


import socket, sys
from struct import *

#Convert a string of 6 characters of ethernet address into a dash separated hex string
def eth_addr (a) :
  b = "%.2x:%.2x:%.2x:%.2x:%.2x:%.2x" % (ord(a[0]) , ord(a[1]) , ord(a[2]), ord(a[3]), ord(a[4]) , ord(a[5]))
  return b

#create a AF_PACKET type raw socket (thats basically packet level)
#define ETH_P_ALL    0x0003          /* Every packet (be careful!!!) */
try:
	s = socket.socket( socket.AF_PACKET , socket.SOCK_RAW , socket.ntohs(0x0003))
except socket.error , msg:
	print 'Socket could not be created. Error Code : ' + str(msg[0]) + ' Message ' + msg[1]
	sys.exit()

# receive a packet
while True:
	packet = s.recvfrom(65565)
	
	#packet string from tuple
	packet = packet[0]
	
	#parse ethernet header
	eth_length = 14
	
	eth_header = packet[:eth_length]
	eth = unpack('!6s6sH' , eth_header)
	eth_protocol = socket.ntohs(eth[2])
	print 'Destination MAC : ' + eth_addr(packet[0:6]) + ' Source MAC : ' + eth_addr(packet[6:12]) + ' Protocol : ' + str(eth_protocol)

	#Parse IP packets, IP Protocol number = 8
	if eth_protocol == 8 :
		#Parse IP header
		#take first 20 characters for the ip header
		ip_header = packet[eth_length:20+eth_length]
		
		#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 'Version : ' + str(version) + ' IP Header Length : ' + str(ihl) + ' TTL : ' + str(ttl) + ' Protocol : ' + str(protocol) + ' Source Address : ' + str(s_addr) + ' Destination Address : ' + str(d_addr)

		#TCP protocol
		if protocol == 6 :
			t = iph_length + eth_length
			tcp_header = packet[t:t+20]

			#now unpack them :)
			tcph = unpack('!HHLLBBHHH' , tcp_header)
			
			source_port = tcph[0]
			dest_port = tcph[1]
			sequence = tcph[2]
			acknowledgement = tcph[3]
			doff_reserved = tcph[4]
			tcph_length = doff_reserved >> 4
			
			print 'Source Port : ' + str(source_port) + ' Dest Port : ' + str(dest_port) + ' Sequence Number : ' + str(sequence) + ' Acknowledgement : ' + str(acknowledgement) + ' TCP header length : ' + str(tcph_length)
			
			h_size = eth_length + iph_length + tcph_length * 4
			data_size = len(packet) - h_size
			
			#get data from the packet
			data = packet[h_size:]
			
			print 'Data : ' + data

		#ICMP Packets
		elif protocol == 1 :
			u = iph_length + eth_length
			icmph_length = 4
			icmp_header = packet[u:u+4]

			#now unpack them :)
			icmph = unpack('!BBH' , icmp_header)
			
			icmp_type = icmph[0]
			code = icmph[1]
			checksum = icmph[2]
			
			print 'Type : ' + str(icmp_type) + ' Code : ' + str(code) + ' Checksum : ' + str(checksum)
			
			h_size = eth_length + iph_length + icmph_length
			data_size = len(packet) - h_size
			
			#get data from the packet
			data = packet[h_size:]
			
			print 'Data : ' + data

		#UDP packets
		elif protocol == 17 :
			u = iph_length + eth_length
			udph_length = 8
			udp_header = packet[u:u+8]

			#now unpack them :)
			udph = unpack('!HHHH' , udp_header)
			
			source_port = udph[0]
			dest_port = udph[1]
			length = udph[2]
			checksum = udph[3]
			
			print 'Source Port : ' + str(source_port) + ' Dest Port : ' + str(dest_port) + ' Length : ' + str(length) + ' Checksum : ' + str(checksum)
			
			h_size = eth_length + iph_length + udph_length
			data_size = len(packet) - h_size
			
			#get data from the packet
			data = packet[h_size:]
			
			print 'Data : ' + data

		#some other IP packet like IGMP
		else :
			print 'Protocol other than TCP/UDP/ICMP'
			
		print

The above program must be run with root privileges, since creating raw sockets on linux requires root access.

$ sudo python sniffer.py

The output should be something like this :

Destination MAC : 00-1c-c0-f8-79-ee Source MAC : 00-25-5e-1a-3d-f1 Protocol : 8
Version : 4 IP Header Length : 5 TTL : 57 Protocol : 6 Source Address : 64.131.72.23 Destination Address : 192.168.1.6
Source Port : 80 Dest Port : 58928 Sequence Number : 1392138007 Acknowledgement : 2935013912 TCP header length : 6
Data : ??y?%^?=E ,@9?c@?H?P?0R?W????`?5t?

Destination MAC : 00-25-5e-1a-3d-f1 Source MAC : 00-1c-c0-f8-79-ee Protocol : 8
Version : 4 IP Header Length : 5 TTL : 64 Protocol : 6 Source Address : 192.168.1.6 Destination Address : 64.131.72.23
Source Port : 58928 Dest Port : 80 Sequence Number : 2935013912 Acknowledgement : 1392138008 TCP header length : 5
Data : %^?=???yE(mU@@?2?@?H?0P????R?W?PJc

Destination MAC : 00-1c-c0-f8-79-ee Source MAC : 00-25-5e-1a-3d-f1 Protocol : 8
Version : 4 IP Header Length : 5 TTL : 55 Protocol : 17 Source Address : 78.141.179.8 Destination Address : 192.168.1.6
Source Port : 34049 Dest Port : 56295 Length : 28 Checksum : 25749
Data : @7?YN?????d??????r'?y@?f?h`??

Destination MAC : 00-1c-c0-f8-79-ee Source MAC : 00-25-5e-1a-3d-f1 Protocol : 8
Version : 4 IP Header Length : 5 TTL : 118 Protocol : 17 Source Address : 173.181.21.51 Destination Address : 192.168.1.6
Source Port : 5999 Dest Port : 56295 Length : 26 Checksum : 22170
Data : s)vL??3?o???V?Z???cw?k??pIQ

It parses the Ethernet header and also the UDP and ICMP headers.

Ethernet header looks like this :

+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|       Ethernet destination address (first 32 bits)            |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Ethernet dest (last 16 bits)  |Ethernet source (first 16 bits)|
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|       Ethernet source address (last 32 bits)                  |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|        Type code              |                               |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+

UDP Header according to RFC 768 :

0      7 8     15 16    23 24    31  
 +--------+--------+--------+--------+ 
 |     Source      |   Destination   | 
 |      Port       |      Port       | 
 +--------+--------+--------+--------+ 
 |                 |                 | 
 |     Length      |    Checksum     | 
 +--------+--------+--------+--------+ 
 |                                     
 |          data octets ...            
 +---------------- ...

ICMP Header according to RFC 792 :

0                   1                   2                   3
0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|     Type      |     Code      |          Checksum             |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|                             unused                            |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|      Internet Header + 64 bits of Original Data Datagram      |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+

This kind of a sniffer does not depend on any external libraries like libpcap.

The C version of this code is here.

Resources

1. http://docs.python.org/library/socket.html

About Silver Moon

A Tech Enthusiast, Blogger, Linux Fan and a Software Developer. Writes about Computer hardware, Linux and Open Source software and coding in Python, Php and Javascript. He can be reached at [email protected].

62 Comments

Code a network Packet Sniffer in Python for Linux
  1. annnn

    i am trying to insert this code in a module and pushing the data collected from this data to multiprocessing.queue struct and using another process to do some analysis for the same, but as soon as we start getting data from network interface , i am not able to switch to the second process to do packet anaysis, any pointers on how to resolve this

  2. Srini

    Hi,

    Thanks for the detailed explanation.

    Will this script able to capture only ipv4 packets or it will sniff both ipv4 and ipv6?

    Thanks in advance

  3. Arnie

    Payload I am getting as below: How to unpack the payload ?

    Data : E �4�@ ��i��i���H�9�’/w���Y�
    �%�J��X���\�?��W������’=L�$�@u�{�=�]ȇa��”�2E��Ŝ��XM��C��pe
    ��ף�JJ�f� q�p�7XQk^�/��J7�.¥�l
    HG��I$x�5:�{�h���)�L�_’Z�w�q�����v���&�Ñ�\��-���Ɠ��i��-|”k���_0� �bS��4=/h�е�������h�F!5��C)���V�\̘Ã�\^7�Үۥ&k���W�-p�%�
    ���]E��W8��hL>� ���쪻Nz�nPBg8!Mj��k4

  4. Larry

    Should s.recvfrom only ever return a single packet?

    I’m creating a raw socket using socket.socket(socket.AF_PACKET, socket.SOCK_RAW, socket.ntohs(0x0003)). I’m reading the socket with s.recvfrom(65536) but since this should be layer 2, I don’t ever expect more than 9k read (max jumbo frame) right? Is it possible I’m getting two packets in one request? Should this always return 1 packet only, and if so how can it ever be larger than a 9k jumbo frame? MTU on the interface is set to 9001

  5. Karim

    Hi sir … I’m a beginner on python … I can’t understand this part!!
    version_ihl = iph[0]
    version = version_ihl >> 4
    ihl = version_ihl & 0xF

    1. Meltus

      The unpack() function makes sure iph[0] got the first byte of the packet. Looking back in the diagram, you could see that the first byte (8 bit) consists of the first 4 bit which is the version of the ip protocol, and the latter 4 bit which is the internet header length.

      By >> or right-shifting the value by 4, it means that you push all the bits in the variable to the right by 4, in order to get the first 4 bit, which is the version section.
      Also, if you AND or & the value by 0xF which in binary means 00001111, you get the last 4 bit, which is the internet header length.

  6. Bonio

    I want to Trace only outgoing packets and I have no idea how to sort them out. I want to save them to text file as well, “on the fly” or after I break compiling. Can anyone help?

  7. dega

    Thanks for your code SilverMoon.
    I have a fix to propose:
    In the second example (‘Sniff all data with ethernet header’):
    – The Ethernet type is unpacked in line 32. Since it’s BigEndian in the packet, unpack does the appropriate job.
    – Line 33 is superfluous.
    – In lines 36 and 37, the constant should be 0x800 (https://en.wikipedia.org/wiki/EtherType)

  8. momo

    Can this code sniff SIP message ?
    I see it can sniff NBNS packet ( format like SIP msg different only payload )
    but can’t sniff SIP

    thx for your advie
    sorry for my poor english

  9. momo

    I want to sniff SIP. I already check in wireshark, sip is use udp only ( and port 5060 only ).
    But Program never sniff SIP.

    Sorry for my poor english
    Thx for your advice

  10. MannyH

    Hi – Awesome post – love it!

    Any recommendations on how I can mod it for parsing pcaps? I’d like to avoid using DPKT or PyShark!

    Thxs!!

  11. Veit

    Thanks for the great post. It is really usefull.
    There is a way to block (drop) some packets? like:

    if source_ip == ‘192.168.1.1’ :
    drop()

  12. John Just

    I’m trying very hard to find how to convert the line below to something in Windows that will capture the ethernet header. Do you have any ideas? I have looked at the Python docs and been searching forums but haven’t seen anything….
    s = socket.socket( socket.AF_PACKET , socket.SOCK_RAW , socket.ntohs(0x0003))

  13. matt

    So I tried to convert this code to python 3. But I am running into problems with the unpacking part, specifically the line “eth = unpack(‘!6s6sH’, eth_header)”. What is this line doing and why in python3 does it seem to do something else?

    Anyone tried converting the code to python3?

    1. Phosphenius

      unpack(..) is part of the pack module which provides functionality to pack binary data into strings objects. Since the Python3 socket functions no longer work with string objects but with bytes objects instead, you don’t need (un)packing anymore.

  14. aaron

    Thanks for the great post. It is really helpful. Could you please explain me what does ‘!HHLLBBHHH’ this means? Sorry for being naive.

    1. Random

      I have tried googling it.
      the “!” in unpack(“!HHLLBBHHH”) means the unpacked data should follow the format after this “!”,
      which is “H, H, L, L, B, B, H, H, H”, these will unpacked as a tuple.

  15. aaron

    It’s really difficult to find the detailed program you have provided in
    any other site. Thank you. If you also give the description for your
    programs, it would become the great website for learning socket
    programming.

  16. aaron

    Thanks for very useful info. I’m novice to socket programming. Please help me with this. Is it possible to receive both TCP and ICMP packets in a single code. Is it something like creating a socket with this type: s = socket.socket(socket.AF_INET, socket.SOCK_RAW, socket.IPPROTO_ICMP_TCP)

  17. Baktha

    Thanks for the very clear and detailed note/tip!!

    One question– despite putting eth0 in promiscuous mode, I seem to be able to sniff ONLY packets destined to the Pi. I am trying to connect the Pi to a Layer2 switch, and using port mirroring/spanning, trying to sniff
    packets going to/from another port (on the Layer 2 switch).

    Any hints on why the Pi does not seem to be “see” or sniff packets with destination addresses other than its own?

    Thanks much!

  18. p

    Thanks for the post. My question is how can you know that which format specifier is to be used to unpack a particular header? For an example in case of a ethernet header we use unpack(!6s!6s!2s). How can I know that I have to use strings and not any other format specifier to get ethernet addresses? Is there any documentation for it?

  19. Paradise

    Hi!

    I can’t understand how this could work since socket.recvfrom() seems to work only for IPPROTO_UDP sockets.

    When I try to do this:

    s = socket.socket(socket.AF_INET, socket.SOCK_RAW, socket.IPPROTO_TCP)
    while True:
    print s.recvfrom(65565)

    I have this error:
    ” print s.recvfrom(65565)
    socket.error: [Errno 10022] An invalid argument was supplied”

    This works fine with IPPROTO_UDP

  20. illyria

    Nice article. Where does socket.ntohs(0x0003) come from? I couldn’t find this constant in any of the BSD socket docs.

  21. j0lly

    Hello sir, i’m really enjoying your articles about python and sockets; I’m also translating it in my language and is a very educational exercise. The unpack header is a bit over my knowledge but, for what i can understand and read, you are using 20 bytes as standard tcp header; Am i right? if so, is this a typical simplified construct to not bother with the [options] flag in the tcp header? i’m a real beginner in programming and in networking, but i would like to understand if i’m parsing the right way your code. Thanks for the precious articles

    1. Silver Moon

      what is the type of socket you are creating ?

      the following socket will fetch only incoming udp packets.
      s = socket.socket(socket.AF_INET, socket.SOCK_RAW, socket.IPPROTO_UDP);

      the following socket will fetch all incoming and outgoing packets as raw ethernet frames.
      s = socket.socket( socket.AF_PACKET , socket.SOCK_RAW , socket.ntohs(0x0003));

  22. David Luu

    Helpful post. What would you recommend if we’d want to sniff outgoing packets too? Maybe need to run sniffer on a machine that’s set as a router/proxy where the endpoints of interest (to sniff) pass data through?

  23. kapil

    hehehhe…. man wats d need 2 write such a huge code… u cud hav implemented d same thing in hardly 20-30 lines of code using the SCAPY module in python…. :)

  24. jijokjose

    Thanks for the script. I run this sniffer script and it works perfectly but the data portion is not much formatted. Some unreadable chacters are displaying. Like the following

    Data : 6\FC\81\800000pagead2googlesyndicationcom000\C0 00000c0pagead46l doubleclicknet0\C0;00000f0J}\EB-

    Is there is any way to convert this to unreadable content into readable text ..??

    1. Silver Moon Post author

      unreadable characters mean they dont have any representable ascii character.
      so they just display as codes.

    1. Silver Moon Post author

      its not possible directly, instead the port of all tcp/udp packets need to be checked and the particular packet has to be picked out.

      1. Aaron88

        why can’t we bind a socket to particular port and sniff only on that port. Kindly reply, I really need information on this for my project. All of my questions asked here are ignored. Please guys.

    2. answerguy

      yep there is.
      Wrap the following code:

      s.recvfrom(2048)
      in a while loop like this:

      while True:

      while http_port is not 80:
      pack = s.recvfrom(2048)

      tcpHeader = pack[0][34:54]
      tcp_hdr = struct.unpack(“!HH16s”, tcpHeader)
      http_port = tcp_hdr[0]
      function(pack[0][54:]) #pass the data into a function

      it will check the port of every packet and will pass the data through to a function when a packet’s port is 80.

  25. ain

    hi sir..
    it this source code available when running on window,
    for your information, i want to create a packet sniffer program using python on windows platfrom..
    and the worse thing is, i’m the beginner on python.

    i’ve tried ur better sniffer code but it keep give me this error.

    Traceback (most recent call last):
    File “C:\Python26\New folder\sniff.py”, line 6, in
    s = socket.socket(socket.AF_PACKET, socket.SOCK_RAW, socket.ntohs(0x0003))
    AttributeError: ‘module’ object has no attribute ‘AF_PACKET’

    and i’m stuck. i don’t know what the solution..
    pls help me out :(

      1. xavier

        hello,I have try the “network sniffer with raw sockets on Windows”.
        And I only get outgoing packages,

        how can i get incoming packages on Windows?

  26. Jamie

    Thanks for this script. I was looking for something to use in black box testing. I am doing some work testing an embedded linux system which has python but we don’t have a compiler to build stuff like tcpdump that isn’t installed

    In the last, improved script there is a typo/slip at line 121, it should be

    data=packet[h_size:]

    Keep up the good work

        1. ayiis

          one more thing that, the length of TCP data should be equal to

          iph[2] – iph_length – tcph_length*4

          i think you made a mistake here :P

          1. bread

            Thank you SilverMoon for your work!
            And thank you, ayiis!

            I had a problem with empty TCP packets. This program said the TCP data size would be 6, while it was 0.
            But I can’t find the problem, since
            data_size = len(packet) – eth_length + iph_length + tcph_length * 4
            should be equal to
            data_size = iph[2] – iph_length – tcph_length * 4

Leave a Reply

Your email address will not be published. Required fields are marked *