Analyse Your Network Packets with LibPCAP

LibPCapData on a network flows in the form of packets, which are entities that carry your data across the network. These packets are routed from one router to another by intelligent nodes. LibPCAP is a library that lets you ‘capture’ these packets.

Packet capture lets you intercept and entirely grab any packet (with all its headers) that is seen by the network device. Packet capture is done regardless of which port the packet is sent to, or even regardless of the host. LibPCAP provides an implementation independent library that lets you access the underlying packet capture facility provided by your operating system. Hence, libPCAP is used to capture packets, directly off the network cards.

Pre-requisites
There are a few things you should know before you can implement the code provided in this article.
Here is a list of things you might want to review before proceeding to read this article.

  • Basics of C programming
  • The basics of networking and its terminology

 

Getting started
The only thing you need to set up, to run the codes provided in this article, is a working C/C++ compiler (I have used gcc in this article) and libPCAP. To install libPCAP on Ubuntu, you can simply run the following command in a terminal:

apt-get install libpcap-dev

In other distributions, you can download the source package for libPCAP from http://www.tcpdump.org/#latest-release and build it with the following commands:

./configure
make
make install

Now that we have set up everything we need, let us try and understand libPCAP better.

Understanding libPCAP
Let us have a look at the structure of the PCAP packet sniffer that we are trying to design. With this, I’ll highlight the functions libPCAP will need to perform.

First, find out the device you want to sniff on. In Linux, this can be eth0 (ethernet), or wlan0 (wireless). This may vary based on your network connection. LibPCAP can automatically find which device you want to sniff on. Next, set up PCAP and initialise it, to sniff on the device you found or set up in the previous step. You can then set up filters, to capture only the kind of traffic you are interested in. When you specify a filter, then you must compile it, for correctness, before you apply it. Finally, make libPCAP grab packets in a loop, until your needs are satisfied.

Opening the device for sniffing
It is remarkably simple to set up your device. If you know your device string (eth0, wlan0, etc), you can set it directly, or pass it as a parameter to your program. If you want libPCAP to find your default device, use pcap_lookupdev (char *errbuf). The parameter errbuf is buffer memory (array), which stores the description of the error in case the command fails. Most libPCAP commands let you pass this array as a parameter, and it universally performs the same function.
The code to find out the default device is given below:

#include<stdio.h>
#include<pcap.h>
int main()
{
char *dev,errbuf[PCAP_ERRBUF_SIZE];
dev=pcap_lookupdev(errbuf);
printf("Device: %s\n",dev);
return 0;
}

The size of the error buffer errbuf is defined to be PCAP_ERRBUF_SIZE. This is a value predefined in the pcap.h header file. Compile the above code with the following command:

gcc code1.c -lpcap

-lpcap is the linker directive to link the associated libPCAP shared library, and must be used every time you want to compile code that uses libPCAP. You may also need to run your compiled program with root privileges.
Alternatively, if you know the device you want to sniff on, you can simply call the function:

pcap_lookup with the dev = “* device id for the device you want to sniff on*” eg – dev=“wlan0”

To open the device for sniffing, use:

pcap_t * pcap_open_live (char *device,int snaplen,int promisc,int to_ms,char *errbuf)

The values of the parameters in the above defined function are as follows.
device: This argument stores the device specified above (the device to be sniffed on).
snaplen: This is an integer that defines the maximum number of bytes to be captured.
Promisc: This is an integer that sets the device to work in promiscuous mode. Promiscuous mode sniffs all traffic on the wire — this could be all the traffic on the network. The downside of using promiscuous mode is that it can be easily detected, and the host may be taxed for resources. In non-promiscuous mode, the sniffer only captures packets that are relevant to the host, which means that only traffic that is to, from or routed by the host will be captured.
to_ms: This is an integer that specifies the read time out in milliseconds (a value of 0 specifies no time out).
errbuf: This stores the error message in case the call fails.
Notice that the function returns a pointer value of type pcap_t. This function returns a value that is your session handler. The code to open the connection is given below:

#include<stdio.h>
#include<pcap.h>
int main()
{
char *dev,errbuf[PCAP_ERRBUF_SIZE];
dev=pcap_lookupdev(errbuf);
pcap_t *handle;
handle=pcap_open_live(dev,BUFSIZ,1,0,errbuf);
return 0;
}

The value of BUFSIZ is defined in pcap.h. In the above code, you are passing the value of the device you want to sniff on, which was found by the pcap_lookupdev function, while you are setting the maximum number of bytes to be captured to be BUFSIZ, the packet sniffing mode to be promiscuous, and no time out on read time.

Filtering traffic
At times, you will be interested in only certain types of traffic, rather than in capturing all packets. Here is where filters come in. They let you only capture certain kinds of traffic. For example, you may only want packets from a specific protocol like UDP or TCP. Now, one may wonder why you shouldn’t use if/else statements? Well, you could, but filters are easier to use, and are more efficient. Before you apply your filter, you must compile it. The expression for the filter is a string.  Compile it with the following function:

int pcap_compile(pcap_t *handler,struct bpf_program *filter,char * filt,int opt, bpf_u_int32 netmask)

The parameters passed to pcap_compile are as follows:

  • handler: This is the session handler obtained earlier.
  • filter: –  This is the location where you store the compiled filter.
  • filt: – This is the string that represents your filter. The tcpdump man pages document the syntax of these filters well.
  • opt: – Specifies whether the compiled filter should be optimised or not.
  • netmask: – Specifies the network mask that your filter applies to.

Once the filter has been compiled, you must now apply it. Use the following function to do so:

int set_filter(pcap_t *handler, struct bpf_program *filter)

…where handler is the session handler, and filter is the location where your compiled filter resides. This is straightforward.
The following code documents how filters must be used:

#include<stdio.h>
#include<pcap.h>
int main()
{
char *dev,errbuf[PCAP_ERRBUF_SIZE];
dev=pcap_lookupdev(errbuf);
pcap_t *handle;
handle=pcap_open_live(dev,BUFSIZ,1,0,errbuf);
struct bpf_program filt;
char filter_app[]="port 80";
bpf_u_int32 mask,net;
pcap_lookupnet(dev,&net,&mask,errbuf);
pcap_compile(handle,&filt,filter_app,0,net);
pcap_setfilter(handle,&filt);
return 0;
}

In the above code, the function pcap_lookupnet is used to identify the network details, like the IP and network mask, given the device. Notice that the variable filter_app stores the string “port 80”, which is your filter string, which specifies that only traffic on port 80 should be captured. Port 80 is the TCP port. The syntax for other filter commands is documented in the tcpdump man pages.

Actual sniffing
Now that you have your filters, session and device set up, you need to actually start capturing packets, which you can do using the following function:

u_char * pcap_next(pcap_t *handler,struct pcap_pkthdr *h)

…where ‘handler’ is the session handler mentioned earlier and ‘h’ is a structure that will store a pointer to information about the packet to be captured. This includes the time it was sniffed, the packet length, and the length of the specific portion of the packet captured (in case it is fragmented). The function returns a pointer of type u_char to a packet that is described by ‘h’.
The code to use the pcap_next is given below:

#include<stdio.h>
#include<pcap.h>
int main()
{
char dev[]="wlan0",errbuf[PCAP_ERRBUF_SIZE];
pcap_t *handle;
handle=pcap_open_live(dev,BUFSIZ,1,0,errbuf);
struct bpf_program filt;
char filter_app[]="port 80";
bpf_u_int32 mask,net;
pcap_lookupnet(dev,&net,&mask,errbuf);
pcap_compile(handle,&filt,filter_app,0,net);
pcap_setfilter(handle,&filt);
struct pcap_pkthdr header;
const u_char *packet;
packet=pcap_next(handle,&header);
printf("%d \n",header.len);
pcap_close(handle);
return 0;
}

In the above code, you captured the packet and printed the length of the packet captured. Also note that, in the above code, the device string has been set to use the wlan0 connection to listen to packets on the wireless device. To see the list of available devices on your Linux system, you can use the ifconfig command at the terminal, and set your device accordingly.

In practice, very few sniffers use pcap_next directly. They usually use pcap_loop or pcap_dispatch to capture multiple packets. They use callback functions. If you have worked with Javascript, you might be familiar with a callback function. It can be understood as a function that is called, every time an event occurs. The functions pcap_loop and pcap_dispatch call the callback, every time a packet is captured. The signature for pcap_loop is given below:

int pcap_loop(pcap_t *handler,int cnt,pcap_handler callback,u_char *user)

The parameters for pcap_loop are defined below:

  • handler – This is the session handler.
  • cnt – This is the number of packets which should be captured by pcap_loop
  • callback – This is the callback function specified only by identifier, without the parentheses.
  • user – These are some additional arguments that we as users might want to pass to the callback function.
  • pcap_dispatch is identical in usage. The only difference between pcap_loop and pcap_dispatch is that pcap_dispatch will only process the first batch of packets it receives from the system, while pcap_loop will keep processing packets until the count runs out.

The callback function should have the signature, which should be as follows:

void callback_function(u_char *arg, const struct pcap_pkthdr *pkthdr,const u_char *packet)

The arguments to the callback function are as follows:
arg - This is the list of user parameters you passed to pcap_loop
pkthdr—-This parameter contains information about the packet captured. The structure of the pcap_pkthdr is given below:

struct pcap_pkthdr{
struct timeval ts;
bpf_u_int32 caplen;
bpf_u_int32 len;
}

packet – This is a pointer to the first byte of data containing the entire packet. As you might imagine, the packet isn’t a string, but rather a collection of structures.
Now that we have established the basics, we can proceed to build a small packet sniffer using libPCAP.
The code for the packet sniffer is given below:

#include <pcap.h>
#include <stdio.h>
#include <stdlib.h>
void callback_function(u_char *arg, const struct pcap_pkthdr* pkthdr,const u_char* packet)
{
int i=0;
static int count=0;
printf("Count: %d\n", ++count);
printf("Size: %d\n", pkthdr->len);
printf("Payload:\n");
for(i=0;i<pkthdr->len;i++) {
if(isprint(packet[i]))
printf("%c ",packet[i]);
else
printf(" . ",packet[i]);
if((i%16==0 && i!=0) || i==pkthdr->len-1)
printf("\n");
}
}
int main(int argc,char **argv)
{
int i;
char dev[]="wlan0";
char errbuf[PCAP_ERRBUF_SIZE];
pcap_t* descr;
const u_char *packet;
struct pcap_pkthdr hdr;
struct ether_header *eptr;    /* net/ethernet.h */
struct bpf_program fp;        /* hold compiled program */
bpf_u_int32 maskp,netp;            /* subnet mask */
pcap_lookupnet(dev, &netp, &maskp, errbuf);
descr = pcap_open_live(dev, BUFSIZ, 1,-1, errbuf);
pcap_compile(descr, &fp, "port 80", 0, netp);
pcap_setfilter(descr, &fp);
pcap_loop(descr, -1, callback_function, NULL);
return 0;
}

In the callback_function, we are only displaying the packet number, size of the packet and the packet payload. The output of running the above code is given in Figure 1.

Fig 1With this, I conclude the introduction to programming with libPCAP. You have learned some of the basic concepts behind network sniffing. Do test and experiment with libPCAP, to understand its full potential. There are also other sniffers you could experiment with, namely tcpdump and wireshark.

All published articles are released under Creative Commons Attribution-NonCommercial 3.0 Unported License, unless otherwise noted.
Open Source For You is powered by WordPress, which gladly sits on top of a CentOS-based LEMP stack.

Creative Commons License.