Skip to content

QPcap - A Qt-Style Wrapper Around libpcap

Saturday, 19 March 2011  |  rich

I've been working on a new library for the last couple of weeks that provides a Qt style API for libpcap. For those who aren't familiar with it, libpcap is the library used for capturing raw packets used by tools like tcpdump and wireshark. The pcap library has been around for years, and is built in C rather than C++. The API is pretty simple, but it's not one that can be trivially integrated into a gui application. The aim of the library I've been working on is to make using packet capture in Qt programs a simple matter of using a QObject and connecting to its slots.

Developing a wrapper for a C api is a fairly common activity for C++ developers, though not one that is done every day. My aim here is to provide an API that feel familiar to developers using Qt, so I've tried to provide something that uses the Qt style rather than simply wrapping the pcap methods into a C++ class. A side effect of this is that it will probably seem a little bit unfamiliar to people who know libpcap but, I feel that the benefit of making something that is easy to use by Qt developers outweighs this.

I'm only going to cover two aspects of the pcap wrapper in my post today: how the packet processing is triggered, and how the callback used by pcap is converted into a Qt-style signal. I'll do another post later about how the wrappers around the packet data work, but for now that's still in flux so writing about it seems a bit pointless.

A pcap capture sits there grabbing data from the wire, but doesn't do anything until you call pcap_dispatch. One approach we could take would be to repeatedly call this function, but this would lead to high cpu load as we'd often find there was no work to do. Fortunately on unix platforms pcap provides a function that gives us a file descriptor we can use for the select() system call - pcap_get_selectable_fd(). Qt, like other modern gui toolkits provides a way for us to integrate file descriptors into the select call of their event loop. In Qt, this comes in the form of the QSocketNotifier class.

In qpcap, I've used QSocketNotifier to know when I should call pcap_dispatch. This means we can process the packets and won't be burning CPU time pointlessly. The code that sets this up is in QPcap::start() as follows:

    int fd = pcap_get_selectable_fd(d->handle);
    d->notifier = new QSocketNotifier( fd, QSocketNotifier::Read, this );
    connect( d->notifier, SIGNAL(activated(int)), this, SLOT(dataAvailable()) );
    d->notifier->setEnabled(true);

The dataAvailable() slot this code calls is at the heart of QPcap, but is trivial. It just calls the pcap_dispatch method we referred to earlier. All the work is done by the callback function passed to it which is called once for each packet received.

void QPcap::dataAvailable()
{
    pcap_dispatch( d->handle, -1 /* all packets*/, (pcap_handler)&QPcap::packet_callback, (uchar *)this );
}

The callback function used by pcap_dispatch is a static method of QPcap. This is required to avoid issues with the difference between the calling conventions of C++ methods (which have the implicit 'this' argument) and C functions. We use the user data argument of the pcap_dispatch function to let us know which instance of QPcap should be notified. After setting the header and packet members of our data object, we trigger the packetReady() signals so that objects connected to our class will be informed.

void QPcap::packet_callback( uchar *self, const pcap_pkthdr *header, const uchar *packet )
{
    QPcap *qpcap = reinterpret_cast<QPcap *>(self);
    qpcap->d->header.header = header;
    qpcap->d->packet = packet;

    qpcap->packetReady();
    qpcap->packetReady( qpcap->d->packet );
}

This post only covers a couple of functions of qpcap, if you want to look at it in more detail, then the code is available on gitorious at http://gitorious.org/qpcap/qpcap/. I'll be writing at least one more post covering the wrapper classes offered by the library for accessing packets, but I'll leave that until the classes are a bit more complete.