Scapy has a sniff function that is great for getting packets off the wire, but I recently discovered just how great this feature really is. There’s an argument 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 .nsummary display with a format of your choice.

In the ScapyDoc.pdf, 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



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 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
    # —— START SNIFFER¬†
    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 *