Packet sniffer in Python with Scapy

The common method of making a packet sniffer in User-Space is to use low-level operations on RAW sockets. But, instead of that, you can just use a powerful Python library – Scapy (see documentation), which exposes a user-friendly high level API. So, it hides all unfortunately low-level abstraction which we don’t really like in Python. For those who need analyze some raw bytes – no worries, the raw data are also accessible. In short, that interesting tool makes a packet capturing really easy. You can create a custom packet sniffer in just few lines of code. What more, scapy provides a wide range of features that help you filter and decode packets. You can write your own code that will sniff exactly what you need. Below you can find a minimal example you can run on your computer.

Example packet sniffer in Python

This example script (sniffer.py) will print out a summary of each captured packet. Note that the name of network interface on your device may differ. If so, change the name in a code.

#!/usr/bin/env python3

from scapy.all import *


def handler(packet):
print(packet.summary())


if __name__ == "__main__":
sniff(iface="eth0", prn=handler, store=0)

Running a script will require a root (administrator) privileges because Scapy uses a privilege-restricted low-level calls to capture packets.

$ sudo python3 sniffer.py 

Ether / IPv6 / UDP 2a00:1450:401b:806::200a:443 > 2a02:a318:a03f:d400:1878:9a:d825:d7a0:34964 / Raw
Ether / IPv6 / UDP 2a00:1450:401b:806::200a:443 > 2a02:a318:a03f:d400:1878:9a:d825:d7a0:34964 / Raw
Ether / IPv6 / UDP 2a00:1450:401b:806::200a:443 > 2a02:a318:a03f:d400:1878:9a:d825:d7a0:34964 / Raw
Ether / IPv6 / UDP 2a00:1450:401b:806::200a:443 > 2a02:a318:a03f:d400:1878:9a:d825:d7a0:34964 / Raw
Ether / IPv6 / UDP 2a02:a318:a03f:d400:1878:9a:d825:d7a0:34964 > 2a00:1450:401b:806::200a:443 / Raw
Ether / IPv6 / UDP 2a02:a318:a03f:d400:1878:9a:d825:d7a0:34964 > 2a00:1450:401b:806::200a:443 / Raw
Ether / IPv6 / UDP 2a02:a318:a03f:d400:1878:9a:d825:d7a0:34964 > 2a00:1450:401b:806::200a:443 / Raw
Ether / IPv6 / UDP 2a02:a318:a03f:d400:1878:9a:d825:d7a0:42428 > 2a00:1450:401b:80d::200a:443 / Raw
Ether / IPv6 / UDP 2a00:1450:401b:806::200a:443 > 2a02:a318:a03f:d400:1878:9a:d825:d7a0:34964 / Raw
Ether / IPv6 / UDP 2a00:1450:401b:80d::200a:443 > 2a02:a318:a03f:d400:1878:9a:d825:d7a0:42428 / Raw
Ether / IPv6 / UDP 2a02:a318:a03f:d400:1878:9a:d825:d7a0:42428 > 2a00:1450:401b:80d::200a:443 / Raw
Ether / IPv6 / UDP 2a00:1450:401b:807::200e:443 > 2a02:a318:a03f:d400:1878:9a:d825:d7a0:34616 / Raw
Ether / IPv6 / UDP 2a02:a318:a03f:d400:1878:9a:d825:d7a0:34616 > 2a00:1450:401b:807::200e:443 / Raw
Ether / IPv6 / UDP 2a00:1450:401b:807::200e:443 > 2a02:a318:a03f:d400:1878:9a:d825:d7a0:34616 / Raw
Ether / IP / UDP / DNS Ans "b'iPad (\xc5\x81ukasz)._companion-link._tcp.local.'"
Ether / IPv6 / UDP / DNS Ans "b'iPad (\xc5\x81ukasz)._companion-link._tcp.local.'"
Ether / IP / UDP / DNS Ans "b'iPad-ukasz.local.'"

Another example packet sniffer in Python

By adding argparse to your code you can pass some basic settings and filter parameters, as in the script below:

#!/usr/bin/env python3

import argparse
from scapy.all import *


class Sniffer:
def __init__(self, args):
self.args = args

def __call__(self, packet):
if self.args.verbose:
packet.show()
else:
print(packet.summary())

def run_forever(self):
sniff(iface=self.args.interface, prn=self, store=0)


if __name__ == "__main__":
parser = argparse.ArgumentParser()
parser.add_argument('-v', '--verbose', default=False, action='store_true', help='be more talkative')
parser.add_argument('-i', '--interface', type=str, required=True, help='network interface name')
args = parser.parse_args()
sniffer = Sniffer(args)
sniffer.run_forever()

Example logs of running script:

$ sudo python3 sniffer.py -v -i eth0
###[ Ethernet ]### dst = 14:f6:d8:5d:e3:b4 src = 34:2c:c4:e8:23:f2 type = IPv6 ###[ IPv6 ]### version = 6 tc = 128 fl = 0 plen = 47 nh = UDP hlim = 59 src = 2a00:1450:401b:807::200e dst = 2a02:a318:a03f:d400:1878:9a:d825:d7a0 ###[ UDP ]### sport = 443 dport = 52714 len = 47 chksum = 0xb152 ###[ Raw ]### load = 'Iq\\xd7\\xe6\\x97oM\\x91\\xba\\xa5\\xb8q\\x81i\\xe6$\x01\\xbf\\xec*\\xe7\\xe1\\x93ɪ\\xd7a\\xe4\x1csJ\\x81\\xcdtX\x06\\xcf#\x1b' ###[ Ethernet ]### dst = 34:2c:c4:e8:23:f2 src = 14:f6:d8:5d:e3:b4 type = IPv6 ###[ IPv6 ]### version = 6 tc = 0 fl = 175942 plen = 42 nh = UDP hlim = 64 src = 2a02:a318:a03f:d400:1878:9a:d825:d7a0 dst = 2a00:1450:401b:807::200e ###[ UDP ]### sport = 52714 dport = 443 len = 42 chksum = 0xb0ef ###[ Raw ]### load = 'L\\xbe\\x8f\\xb5\\xb6\\xb7\\xad\\xf5h\\xdd\\xf2\\xe95\\xc1:\\x8alY)\\xc0t\\xf4\\xcc\x0e\\xb8zk\\x82%\\x86\x7ff\\xb4\x15'

 

Leave a Comment