1

I am porting my sniffer from C to Python using libpcap via ctypes. Here is the python code:

import ctypes, sys
from ctypes.util import find_library

if sys.platform == "darwin":
    _pcap = ctypes.cdll.LoadLibrary(find_library("libpcap"))
elif sys.platform == "linux2":
    _pcap = ctypes.cdll.LoadLibrary("libpcap.so")

errbuf = ctypes.create_string_buffer(256)

pcap_lookupdev = _pcap.pcap_lookupdev
pcap_lookupdev.restype = ctypes.c_char_p
dev = pcap_lookupdev(errbuf)
print dev

# create handler
pcap_create = _pcap.pcap_create
handle = pcap_create(dev, errbuf)
print handle
if not handle:
    print "failed creating handler:",errbuf
    exit()

# monitor mode
pcap_can_set_rfmon = _pcap.pcap_can_set_rfmon
print "can rfmon:",pcap_can_set_rfmon(handle)

On Linux it works perfectly fine, but on Mac OS X it runs into a segmentation fault when I use handle. The value of handle even is sometimes negative, sometimes positive. I already tried to change the return type of pcap_create to unsigned int, but that didn't help, but I think it returns a wrong type under OS X...

I did a printf("size of pcap_t: %zu\n", sizeof(pcap_t *)); on both systems in C to get the size of pcap_t handler type. On Linux it says 4 and on OS X 8. But I I don't know how to go on from this point...

Or am I on the wrong path? Does anyone have an idea?

3
  • is your python install 32 or 64 bit? Seems like libpcap is 64 bit in Mac and 32 bit in linux. Maybe the ints aren't mapping correctly? Commented Nov 7, 2012 at 18:09
  • Yes, I checked with sys.maxsize > 2**32, 64 Bit in Mac and 32 in Linux. But how to handle that. How to make that generic? I will need to run that on both, 32 and 64 bit systems. Commented Nov 7, 2012 at 19:50
  • libpcap is not 64-bit in OS X, it's 32-bit and 64-bit; OS X supports "fat binaries", and the libpcap shared library is a fat binary. At least on Mountain Lion, /usr/bin/python is also fat, so it's also 32-bit and 64-bit. user1758827 is on a 64-bit machine, so the 64-bit versions of Python and libpcap should both be used. Commented Nov 9, 2012 at 18:34

2 Answers 2

1

Data types matter.

You need to tell ctypes that the return value of pcap_create() is a pointer, and you need to tell it that the argument to pcap_can_set_rfmon() is a pointer.

You do this by doing

# create handler
pcap_create = _pcap.pcap_create
pcap_create.restype = ctypes.c_void_p
handle = pcap_create(dev, errbuf)
print handle
if not handle:
    print "failed creating handler:",errbuf
    exit()

# monitor mode
pcap_can_set_rfmon = _pcap.pcap_can_set_rfmon
pcap_can_set_rfmon.argtypes = [ctypes.c_void_p]
print "can rfmon:",pcap_can_set_rfmon(handle)

The

pcap_create.restype = ctypes.c_void_p

and

pcap_can_set_rfmon.argtypes = [ctypes.c_void_p]

lines are required here. This code will work with both 32-bit and 64-bit pointers, so you can use it on 32-bit and 64-bit Linux and on 32-bit and 64-bit OS X (and 32-bit and 64-bit Solaris and 32-bit and 64-bit FreeBSD and..., with whatever changes are needed to the code to load the library - on most Un*xes shared libraries have names ending with ".so", so if you don't want to use find_library on other Un*xes, the Linux code may suffice).

Sign up to request clarification or add additional context in comments.

2 Comments

Thanks, very helpful answer and comment (above). I had the return type of pcap_create set to c_void_p once, but because I didn't set the argtypes, it still didn't work. But now it does. Thanks again.
Yeah, I first tried fixing just the return type, but it still crashed, so I then tried fixing the argtypes, which worked. I don't know whether any of the Python wrappers for libpcap/WinPcap support the new pcap_create()/pcap_activate()-based APIs - the documentation for them wasn't all that good - but, if they do, that's probably the best way to use libpcap/WinPcap in Python.
0

I really wanted to come here and post since I was having this issue on OS X as well. The issue I found with your code is:

handle = pcap_create(dev, errbuf)

You need to set the restype, here is my example as this may help a few of you down the road: https://github.com/killswitch-GUI/NIX-Sniffer-Examples

    import ctypes
    import threading
    import sys
    import os
    import errno


    OSX_PCAP_DYLIB = '/usr/lib/libpcap.A.dylib'
    OSX_LIBC_DYLIB = '/usr/lib/libSystem.B.dylib'
    PCAP_ERRBUF_SIZE = 256
    packet_count_limit = ctypes.c_int(1)
    timeout_limit = ctypes.c_int(1000) # In milliseconds 
    err_buf = ctypes.create_string_buffer(PCAP_ERRBUF_SIZE)

    class bpf_program(ctypes.Structure):
        _fields_ = [("bf_len", ctypes.c_int),("bf_insns", ctypes.c_void_p)]

    class pcap_pkthdr(ctypes.Structure):
        _fields_ = [("tv_sec", ctypes.c_long), ("tv_usec", ctypes.c_long), ("caplen", ctypes.c_uint), ("len", ctypes.c_uint)]

    class pcap_stat(ctypes.Structure):
        _fields_ = [("ps_recv",ctypes.c_uint), ("ps_drop",ctypes.c_uint), ("ps_ifdrop", ctypes.c_int)]

    def pkthandler(pkthdr,packet):
        print("In callback:")
        print("pkthdr[0:7]:",pkthdr.contents.len)
        print(pkthdr.contents.tv_sec,pkthdr.contents.caplen,pkthdr.contents.len)
        print(packet.contents[:10])
        print()

    print "-------------------------------------------"
    libc = ctypes.CDLL(OSX_LIBC_DYLIB, use_errno=True)
    if not libc:
        print "Error loading C libary: %s" % errno.errorcode[ctypes.get_errno()]
    print "* C runtime libary loaded: %s" % OSX_LIBC_DYLIB
    pcap = ctypes.CDLL(OSX_PCAP_DYLIB, use_errno=True)
    if not pcap:
        print "Error loading C libary: %s" % errno.errorcode[ctypes.get_errno()]
    print "* C runtime libary loaded: %s" % OSX_PCAP_DYLIB
    print "* C runtime handle at: %s" % pcap

    print "-------------------------------------------"
    pcap_lookupdev = pcap.pcap_lookupdev
    pcap_lookupdev.restype = ctypes.c_char_p
    dev = pcap.pcap_lookupdev()
    print "* Device handle at: %s" % dev

    net = ctypes.c_uint()
    mask = ctypes.c_uint()
    pcap.pcap_lookupnet(dev,ctypes.byref(net),ctypes.byref(mask),err_buf)
    print "* Device IP to bind: %s" % net
    print "* Device net mask: %s" % mask

    #pcap_t *pcap_open_live(const char *device, int snaplen,int promisc, int to_ms, char *errbuf)
    pcap_open_live = pcap.pcap_open_live
    pcap_open_live.restype = ctypes.POINTER(ctypes.c_void_p)
    pcap_create = pcap.pcap_create
    pcap_create.restype = ctypes.c_void_p
    #pcap_handle = pcap.pcap_create(dev, err_buf)
    pcap_handle = pcap.pcap_open_live(dev, 1024, packet_count_limit, timeout_limit, err_buf)
    print "* Live capture device handle at: %s" % pcap_handle 

    pcap_can_set_rfmon = pcap.pcap_can_set_rfmon
    pcap_can_set_rfmon.argtypes = [ctypes.c_void_p]
    if (pcap_can_set_rfmon(pcap_handle) == 1):
        print "* Can set interface in monitor mode"

    pcap_pkthdr_p = ctypes.POINTER(pcap_pkthdr)()
    packetdata = ctypes.POINTER(ctypes.c_ubyte*65536)()
    #print pcap.pcap_next(pcap_handle,ctypes.byref(pcap_pkthdr_p))

    if (pcap.pcap_next_ex(pcap_handle, ctypes.byref(pcap_pkthdr_p), ctypes.byref(packetdata)) == 1):
        print "* Packet captured!"
        pkthandler(pcap_pkthdr_p,packetdata)

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.