Scapy has a sniff function that is great for getting packets off the wire, but there’s much more to show off how great this function really is! sniff has  an argument prn that allows you to pass a function that executes with each packet sniffed. The intended purpose of this function is to control how the packet prints out in the console allowing you to replace the default packet printing display with a format of your choice.

The prn argument is defined as:

prn: function to apply to each packet. If something is returned, it is displayed. For instance you can use prn = lambda x: x.summary().

In order for your program/script to format and return the packet info as you wish, the sniff function passes the packet object as the one and only argument into the function you specify in the sniff’s prn argument. This gives us the option to do some fun stuff (not just formatting) with each packet sniffed 🙂

For example, we can now perform custom actions with each sniffed packet. This can be anything from incrementing a packet count somewhere in the program, to doing some advanced packet parsing or manipulation, or even shipping that packet off into some sort of storage (.pcap appending or API POSTing anyone??).

Here’s a simple example for keeping track of the number of packets sniffed

This script keeps a Counter with an A/Z pair of IP addresses, displays the total packet count with each packet print(), and then prints out the conversation counts at the end.


Custom Formatted ARP Monitor

Here I use the same prn function and some conditional statements to very clearly tell me what ARP traffic my computer is seeing.


An important thing to keep in mind when using the prn argument

In the case of the example above, you are passing the custom_action function into the sniff function. If you used sniff(prn=custom_action()) instead, you would be passing the function’s returned value to the sniff function. This will generate the returned text before the function has a packet to parse and will not give you the results you want.

If you want to pass parameters into the custom_action function for additional control or the ability to modularize out the customAction function, you will have to use a nested function. I cover how to do that in the next article.

This post was originally published on

This article has 9 comments

  1. Pingback: Scapy p.05 – Sending our First Packet; ARP Response | thePacketGeek

  2. Pingback: Importing packets from trace files | thePacketGeek

  3. LittleHann

    tks for your paper
    i want to know how to capture and extract dns query info, i wrote some code like below
    from scapy.all import *
    from datetime import datetime
    import time
    import datetime
    import sys

    ############# MODIFY THIS PART IF NECESSARY ###############
    interface = ‘docker0’
    filter_bpf = ‘port 53’

    def select_DNS(pkt): 
    pkt_time = pkt.sprintf(‘%sent.time%’) 
    if DNSQR in pkt and pkt.dport == 53:  
    print “Request: ” + pkt[IP].proto + “” +  pkt[DNS][15].qtype + “” +  pkt[DNS][15].qname   
    elif DNSRR in pkt and == 53:
    # responses
    print ‘[**] Detected DNS Request Response Message at: ‘ + pkt_time
    sniff(iface=interface, filter=filter_bpf, store=0,  prn=select_DNS)

    but din’t work
    may i miss something?

    1. Mat

      Yeah, the prn argument takes a function (callable) and then calls that function with every packet received. It’s intended to control how the packet info is printed but we can have that callback do other things to, like the article goes over.

Leave a Reply

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