Search code examples
pythonnetwork-programmingdockerpcappacket-sniffers

Cannot trace error in python pcapy wrapper


I'm using python pcapy in a docker container using this piece of code:

from pcapy import open_live, findalldevs
import sys
import traceback

p = open_live("eth0", 1024, False, 100)
dumper = p.dump_open("test.pcap")

devices = findalldevs()
print dumper, devices
while True:
    try:
        print p.next()
    except Exception as e:
        print dir(e), e.message, e.args[0]
        traceback.print_exc(file=sys.stdout)
        break

When I run it I get the following exception:

Traceback (most recent call last):

File "test_pcap.py", line 12, in

print p.next()

PcapError

I've tried to play with the arguments by changing to different maximum packet sizes and setting promiscuous to True.

I've tried to get any message from the exception, but it seems the message is empty. I also skimmed through pcapy source code: since the exception in the PcapyError object is empty and the other PcapErrors in the next function are explicit strings, it implies we are falling into the condition in which buf is empty. It seems pcap_geterr returns an empty string because pp->pcap has been closed and the pointer to the pcap exception no longer exists (take a look into the doc).

When I run using the loop() method, everything works fine:

# Modified from: http://snipplr.com/view/3579/
import pcapy
from impacket.ImpactDecoder import *

# list all the network devices
pcapy.findalldevs()

max_bytes = 1024
promiscuous = False
read_timeout = 100 # in milliseconds
pc = pcapy.open_live("eth0", max_bytes,
    promiscuous, read_timeout)

# callback for received packets
def recv_pkts(hdr, data):
    packet = EthDecoder().decode(data)
    print packet

packet_limit = -1 # infinite
pc.loop(packet_limit, recv_pkts) # capture packets

I really don't know the source of the problem or what else to do for debugging it.

EDIT

I cannot find any error using strace. This is the grep for error in strace output:

strace python test_pcap.py 2>&1 1>/dev/null | grep -i error

read(6, "\0\0\0t\3\0\0\0intt\n\0\0\0ValueErrort\23\0\0\0_"..., 4096) = 995

getsockopt(3, SOL_SOCKET, SO_ERROR, [0], [4]) = 0

getsockopt(5, SOL_SOCKET, SO_ERROR, [0], [4]) = 0

getsockopt(5, SOL_SOCKET, SO_ERROR, [0], [4]) = 0

EDIT2

I also tested pcap.h by calling to pcap_next myself:

 // Modified from: http://www.tcpdump.org/pcap.html
 #include <pcap.h>
 #include <stdio.h>

 int main(int argc, char *argv[])
 {
        pcap_t *handle;                 /* Session handle */
        char *dev;                      /* The device to sniff on */
        char errbuf[PCAP_ERRBUF_SIZE];  /* Error string */
        bpf_u_int32 mask;               /* Our netmask */
        bpf_u_int32 net;                /* Our IP */
        struct pcap_pkthdr header;      /* The header that pcap gives us */
        const u_char *packet;           /* The actual packet */

        /* Define the device */
        dev = pcap_lookupdev(errbuf);
        if (dev == NULL) {
                fprintf(stderr, "Couldn't find default device: %s\n", errbuf);
                return(2);
        }
        /* Find the properties for the device */
        if (pcap_lookupnet(dev, &net, &mask, errbuf) == -1) {
                fprintf(stderr, "Couldn't get netmask for device %s: %s\n", dev, errbuf);
                net = 0;
                mask = 0;
        }
        /* Open the session in promiscuous mode */
        handle = pcap_open_live(dev, BUFSIZ, 1, 1000, errbuf);
        if (handle == NULL) {
                fprintf(stderr, "Couldn't open device %s: %s\n", "eth0", errbuf);
                return(2);
        }
        while (1) {
                /* Grab a packet */
                packet = pcap_next(handle, &header);
                /* Print its length */
                printf("Jacked a packet with length of [%d]\n", header.len);
                /* Print contents */
                printf("\tPacket: %s\n", packet);
                /* And close the session */
        }
        pcap_close(handle);
        return(0);
 }

To compile, write it to test_sniff.c and run:

gcc test_sniff.c -o test_sniff -lpcap

And I was able to capture packets successfully. So I don't really know where the problem is...

Other info to reproduce behaviour

  • Docker version: Docker version 1.5.0, build a8a31ef
  • Docker image is the Docker default Ubuntu
  • Python2.7

Solution

  • pcapy doesn't use Python socket module. It won't raise socket.timeout which is raised if timeout has been enabled by previous socket.settimeout call. socket.settimeout is used to set a socket into blocking, non-blocking or timeout state.

    In pcapy, the timeout argument of open_live is passed to poll syscall at least in Linux, should differ by OS where poll is not available.

    Reader.next call raises PcapError if there's no packet to return because it hasn't captured any packets yet. It's not an error, just an indication like StopIteration. It can be ignored and Reader.next has to be called again.

    Reader.loop won't return until it has at least one packet to return or an error occurs.

    The following code captures 10 packets and exits.

    from pcapy import open_live, findalldevs, PcapError
    
    p = open_live("eth0", 1024, False, 100)
    dumper = p.dump_open("test.pcap")
    
    devices = findalldevs()
    print dumper, devices
    count=0
    while True:
        try:
            packet = p.next()
        except PcapError:
            continue
        else:
            print packet
            count += 1
            if count == 10:
                break