I need to pickle a scapy
packet. Most of the time this works, but sometimes the pickler complains about a function object. As a rule of thumb: ARP packets pickle fine. Some UDP packets are problematic.

- 6,578
- 2
- 31
- 67
-
Background is usage in conjunction with the `multiprocessing` module. It requires objects to be pickleable if you want to transfer them through a `Queue`. – Helmut Grohne Nov 16 '10 at 12:22
-
Could someone answer this question, so I can award the bounty? – Helmut Grohne Nov 23 '10 at 12:37
6 Answers
My solution (as inspired by the scapy mailing list) is as follows:
class PicklablePacket:
"""A container for scapy packets that can be pickled (in contrast
to scapy packets themselves)."""
def __init__(self, pkt):
self.contents = bytes(pkt)
self.time = pkt.time
def __call__(self):
"""Get the original scapy packet."""
pkt = scapy.Ether(self.contents)
pkt.time = self.time
return pkt
Anywhere I wish to pass a scapy
Packet
through a Queue
I simply wrap it in a PicklablePacket
and __call__
it afterwards. I am not aware of data that is not retained this way. However this approach only works with Ethernet
packets. (All packets sniffed on a regular NIC (not WLAN) are Ethernet.) It could probably be extended to work for other types, too.

- 6,578
- 2
- 31
- 67
-
Thanks for posting back. While I didn't need your exact solution, it helped me find my own. – Mr. Shickadance Mar 24 '11 at 13:50
If by pickle you mean generically serialize you can always use the pcap import/export methods: rdpcap and wrpcap.
wrpcap("pkt.pcap",pkt)
pkt = rdpcap("pkt.pcap")
Or you could start up your process and grab the packets in another process. If there is some pattern you can match, say a known port or source IP tcpdump will work:
tcpdump -i eth0 -w FOO.pcap host 172.20.33.12 and \(udp or arp\)
You can then read the generated pcap in as above:
pkts = rdpcap('FOO.pcap')

- 26,632
- 7
- 60
- 80
-
-
2) This method actually looses data. For instance the timestamp is rounded. This is not necessarily a problem, but has to be taken into account. – Helmut Grohne Nov 16 '10 at 10:48
-
Can scapy even push out 500 packets per second? I've found that when sending packets it's much less than that. You could always run a tcpdump in another terminal. – Paul Rubel Nov 18 '10 at 20:46
-
-
You could pass a reference to a file in the queue. strings are pickleable. – Paul Rubel Nov 23 '10 at 22:31
-
Sorry, creating a file for each packet is a bad joke in terms of performance. If I wouldn't care about performance I could use `threading` instead of `multiprocessing` and wouldn't have to pickle at all. – Helmut Grohne Dec 07 '10 at 10:22
(This is more for reference, so no votes expected)
The Scapy list scapy.ml@secdev.org is well-monitored and tends to be very responsive. If you don't get answers here, try there as well.

- 20,690
- 15
- 67
- 81
-
Oh that's where communication goes on? The issue tracker seems pretty inactive. At least one of my bugs stayed there for half a year without any response. – Helmut Grohne Nov 16 '10 at 10:53
-
1as a side note, 500 packets per second might be more than Scapy handles as well. It's designed for flexibility not speed :) Ask on the list. I don't really use it much, so I am no expert. – Gregg Lind Nov 17 '10 at 13:49
-
Actually I can confirm, that scapy handles 500 packets per second very well. Granted I use more than one thread and (after this question) more than one process. – Helmut Grohne Nov 23 '10 at 11:00
As inspired by this question one can use the dill library (or others like sPickle etc - see pypi search pickle) to save scapy packets. E.g. Install dill using sudo easy_install dill
or sudo pip install dill
. Here's a basic usage scenario:
import dill as pickle
# E.g. Dump an array of packets stored in variable mypackets to a file
pickle.dump(mypackets, open('mypackets.dill-pickle', 'w'))
# Restore them from the file
mypackets = pickle.load(open('mypackets.dill-pickle', 'rb'))
Also one can of course just use scapy's native functions to dump the packets to a pcap file (readable by tcpdump/wireshark etc) - if one just has an array of packets:
wrpcap("packets_array.pcap",packets_array)
To get the PicklabePacket class to work with scapy 3.0.0 you can use this class definition:
class PicklablePacket:
"""A container for scapy packets that can be pickled (in contrast
to scapy packets themselves).
This works for python 3.5.1 and scapy 3.0.0 """
def __init__(self, pkt):
self.__contents = pkt.__bytes__()
self.__time = pkt.time
def __call__(self):
"""Get the original scapy packet."""
pkt = scapy.all.Ether(self.__contents)
pkt.time = self.__time
return pkt
You can monkeypatch the Packet
class and inject __getstate__
and __setstate__
methods that convert the function in the object from and to a picklable representation. See here for details.
def packet_getstate(self):
# todo
def packet_setstate(self, state):
# todo
from scapy.packet import Packet
Packet.__getstate__ = packet_getstate
Packet.__setstate__ = packet_setstate

- 13,378
- 5
- 34
- 38
-
1This is kind of obvious to me. The non-obvious part is the todo part. – Helmut Grohne Nov 23 '10 at 14:44
-
Interactively inspecting a `Packet` object that fails pickling (`pprint.pprint(packet.__dict__)`) would easily show what the function is, if it's a lambda, a function or an instance method. Do you already know what kind of function is? – piro Nov 24 '10 at 13:42
-
The full traceback is too long for a comment, but you can easily reproduce that yourself. The final line is: pickle.PicklingError: Can't pickle
at 0x8bb2bc4>: it's not found as scapy.layers.inet. – Helmut Grohne Nov 25 '10 at 11:07