scapy with pypy [FIX]

While writing a sniffer plugin for my GambolPutty project, I tried using the infamous scapy library to do the sniffing.
As it is listed in the compatibility list for pypy (see here) I thought it to be a good choice.
Still, I stumbled over some problems…

Setting an interface for sniffing

sniff(prn=self.parsePacket, iface='eth0')

resulted in the following error (see here):

Exception in thread Thread-1:
Traceback (most recent call last):
  File "/usr/lib64/pypy-2.2.1/lib-python/2.7/threading.py", line 551, in __bootstrap_inner
    self.run()
  File "/opt/src/gambolputty/input/ScapyNetSniffer.py", line 345, in run
    sniff(prn=self.parsePacket, iface='eth2', filter="tcp and port 80")
  File "/usr/lib64/pypy-2.2.1/site-packages/scapy/sendrecv.py", line 561, in sniff
    s = L2socket(type=ETH_P_ALL, *arg, **karg)
  File "/usr/lib64/pypy-2.2.1/site-packages/scapy/arch/linux.py", line 455, in __init__
    self.ins.bind((iface, type))
  File "<string>", line 1, in bind
error: unknown address family

Setting a filter for sniffing

sniff(prn=self.parsePacket, filter='tcp and port 80')

resulted in the following error:

Exception in thread Thread-1:
Traceback (most recent call last):
  File "/usr/lib64/pypy-2.2.1/lib-python/2.7/threading.py", line 551, in __bootstrap_inner
    self.run()
  File "/opt/src/gambolputty/input/ScapyNetSniffer.py", line 345, in run
    sniff(prn=self.parsePacket, filter="tcp and port 80") # iface='eth2',
  File "/usr/lib64/pypy-2.2.1/site-packages/scapy/sendrecv.py", line 561, in sniff
    s = L2socket(type=ETH_P_ALL, *arg, **karg)
  File "/usr/lib64/pypy-2.2.1/site-packages/scapy/arch/linux.py", line 463, in __init__
    attach_filter(self.ins, filter)
  File "/usr/lib64/pypy-2.2.1/site-packages/scapy/arch/linux.py", line 135, in attach_filter
    s.setsockopt(SOL_SOCKET, SO_ATTACH_FILTER, bpfh)
  File "<string>", line 1, in setsockopt
error: [Errno 22] Invalid argument

These errors popped up on a vagrant 64bit machine with CentOS6.5 and pypy-2.2 and pypy-2.4.

After some testing I found a solution which at least worked for me.

Here is the patch:

--- linux.py	2014-10-09 15:03:46.401539030 +0000
+++ linux_patched.py	2014-10-09 14:51:42.639351837 +0000
@@ -18,8 +18,13 @@
 from scapy.supersocket import SuperSocket
 import scapy.arch
 from scapy.error import warning
+import ctypes
 
-
+try:
+    import __pypy__
+    is_pypy = True
+except ImportError:
+    is_pypy = False
 
 # From bits/ioctls.h
 SIOCGIFHWADDR  = 0x8927          # Get hardware address    
@@ -128,11 +133,9 @@
 
     # XXX. Argl! We need to give the kernel a pointer on the BPF,
     # python object header seems to be 20 bytes. 36 bytes for x86 64bits arch.
-    if scapy.arch.X86_64:
-        bpfh = struct.pack("HL", nb, id(bpf)+36)
-    else:
-        bpfh = struct.pack("HI", nb, id(bpf)+20)  
-    s.setsockopt(SOL_SOCKET, SO_ATTACH_FILTER, bpfh)
+    str_buffer = ctypes.create_string_buffer(bpf)
+    fprog_addr = struct.pack('HL', nb, ctypes.addressof(str_buffer))  
+    s.setsockopt(SOL_SOCKET, SO_ATTACH_FILTER, fprog_addr)
 
 def set_promisc(s,iff,val=1):
     mreq = struct.pack("IHH8s", get_if_index(iff), PACKET_MR_PROMISC, 0, "")
@@ -451,7 +454,7 @@
         self.ins = socket.socket(socket.AF_PACKET, socket.SOCK_RAW, socket.htons(type))
         self.ins.setsockopt(socket.SOL_SOCKET, socket.SO_RCVBUF, 0)
         _flush_fd(self.ins)
-        if iface is not None:
+        if iface is not None and not is_pypy:
             self.ins.bind((iface, type))
         if not nofilter:
             if conf.except_filter:

To apply it, save it as scapy_pypy.patch in the same dir as the linux.py file of scapy package (e.g. /usr/lib64/pypy-2.2.1/site-packages/scapy/arch/).
Then just execute:

patch < scapy_pypy.patch

Hope this is helpful for someone else ;)

Dieser Beitrag wurde unter /dev/administration veröffentlicht. Setze ein Lesezeichen auf den Permalink.

Schreibe einen Kommentar

Deine E-Mail-Adresse wird nicht veröffentlicht. Erforderliche Felder sind mit * markiert.