How to Perform Packet Sniffing Using Libpcap with C Example Code

7 min read Original article ↗

Network tools like wireshark, tcpdump, etc, are fairly popular for packet sniffing. This article provides a basic overview of the libpcap library which forms the base of packet sniffing for many network monitoring tools including wireshark, tcpdump, snort, etc.

What is Packet Sniffing and How it Works?

Packet sniffing is a technique through which the network data to and from your computer can be monitored easily. The data travels on network in form of packets and a packet sniffing tool can easily capture these packets. Mostly packet sniffers are used by network administrators and developers working on network tools. But, overall packet sniffers are handy for debugging network related problems and can be used by anyone who has the required privileges.

Packet sniffers work by sniffing on an interface device like eth0 etc. A list of interfaces can be obtained by the command ifconfig. Once the interface is selected, there can be some options through which one can filter out the packets based on protocol, source port, destination port etc. Choosing a filter option is not necessary. Thereon the packet capture is started.

To understand packet capture and display filters, refer to our tutorial on wireshark. For command line tool, refer to tcpdump, which also does packet sniffing but produces output on the command line.

The libpcap library

Libpcap is the underlying library used for packet sniffing by many of the popular network monitoring tools. To understand the use of this library, one requires basic understanding of C programming language.

Here is how libpcap works :

  • Choose the network interface device on which the packet sniffing is to be done. For example ‘eth0’ , ‘wlan0’  etc on Linux.
  • Once the device is chosen, initialize the pcap library with this device.
  • Next, we can apply filter options for cases like if we want to sniff only TCP/IP packets or if we want to specify that sniff packets only from a particular source or destination port etc. This filter is compiled and then applied using a set of libpcap library functions.
  • Next the pcap library enters into its packet capturing loop where it captures number of packets as set by the program.
  • Once a packet is captures, a callback function is called in which whole of the packet is available to print its details or use it an any other way

The above mentioned four steps are the basic steps to start a packet capture through libpcap.

An example

The code below makes use of the libpcap functions to achieve a basic packet capture. After capturing the packets, inside the callback function, the length of each packet is printed on stdout.

#include <pcap.h>
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <netinet/if_ether.h>
#include <netinet/tcp.h>
#include <netinet/ip.h>
#include <string.h>

void callback(u_char *useless,const struct pcap_pkthdr* pkthdr,const u_char*
        packet)
{
  static int count = 1;

  printf("\nPacket number [%d], length of this packet is: %d\n", count++, pkthdr->len);
}

int main(int argc,char **argv)
{
    char *dev;
    char errbuf[PCAP_ERRBUF_SIZE];
    pcap_t* descr;
    struct bpf_program fp;        /* to hold compiled program */
    bpf_u_int32 pMask;            /* subnet mask */
    bpf_u_int32 pNet;             /* ip address*/
    pcap_if_t *alldevs, *d;
    char dev_buff[64] = {0};
    int i =0;

    // Check if sufficient arguments were supplied
    if(argc != 3)
    {
        printf("\nUsage: %s [protocol][number-of-packets]\n",argv[0]);
        return 0;
    }

    // Prepare a list of all the devices
    if (pcap_findalldevs(&alldevs, errbuf) == -1)
    {
        fprintf(stderr,"Error in pcap_findalldevs: %s\n", errbuf);
        exit(1);
    }

    // Print the list to user
    // so that a choice can be
    // made
    printf("\nHere is a list of available devices on your system:\n\n");
    for(d=alldevs; d; d=d->next)
    {
        printf("%d. %s", ++i, d->name);
        if (d->description)
            printf(" (%s)\n", d->description);
        else
            printf(" (Sorry, No description available for this device)\n");
    }

    // Ask user to provide the interface name
    printf("\nEnter the interface name on which you want to run the packet sniffer : ");
    fgets(dev_buff, sizeof(dev_buff)-1, stdin);

    // Clear off the trailing newline that
    // fgets sets
    dev_buff[strlen(dev_buff)-1] = '';

    // Check if something was provided
    // by user
    if(strlen(dev_buff))
    {
        dev = dev_buff;
        printf("\n ---You opted for device [%s] to capture [%d] packets---\n\n Starting capture...",dev, (atoi)(argv[2]));
    }     

    // If something was not provided
    // return error.
    if(dev == NULL)
    {
        printf("\n[%s]\n", errbuf);
        return -1;
    }

    // fetch the network address and network mask
    pcap_lookupnet(dev, &pNet, &pMask, errbuf);

    // Now, open device for sniffing
    descr = pcap_open_live(dev, BUFSIZ, 0,-1, errbuf);
    if(descr == NULL)
    {
        printf("pcap_open_live() failed due to [%s]\n", errbuf);
        return -1;
    }

    // Compile the filter expression
    if(pcap_compile(descr, &fp, argv[1], 0, pNet) == -1)
    {
        printf("\npcap_compile() failed\n");
        return -1;
    }

    // Set the filter compiled above
    if(pcap_setfilter(descr, &fp) == -1)
    {
        printf("\npcap_setfilter() failed\n");
        exit(1);
    }

    // For every packet received, call the callback function
    // For now, maximum limit on number of packets is specified
    // by user.
    pcap_loop(descr,atoi(argv[2]), callback, NULL);

    printf("\nDone with packet sniffing!\n");
    return 0;
}

In the code above :

  • The function pcap_findalldevs() is used to fetch a list of all available interface devices. This list can be shown to the user so that the intended interface can be selected to sniff packets on. Please note that these is exists a function pcap_lookupdev() that also returns an interface device but the problem with this function is that it returns the first available non loop-back device. So in case I am using wireless network connection and the interface device for my connection is ‘wlan0’ but pcap_lookupdev() function would still return ‘eth0’ as it encounters this interface first. So using pcap_findalldevs() is a better option as it  produces a list of interface devices to choose from.
  • The list returned by the function pcap_findalldevs() is given to user and the user’s input is taken from stdin.
  • Then the function pcap_lookupnet() is used to fetch the ip address and network mask.
  • Through the function pcap_open_live() the pcap library is initialized with the interface device selected.
  • Through pcap_compile() function , we can compile any filter on protocol etc set by the user.
  • Through pcap_setfilter(), this filter is applied.
  • Finally through function pcap_loop() the library starts packet capture on the selected device with the filter applied and with every relevant packet captured, the callback function is called.

Here is the output of above program :

$ sudo ./pcap tcp 10
[sudo] password for himanshu:

Here is a list of available devices on your system:

1. eth0 (Sorry, No description available for this device)
2. wlan0 (Sorry, No description available for this device)
3. usbmon1 (USB bus number 1)
4. usbmon2 (USB bus number 2)
5. usbmon3 (USB bus number 3)
6. usbmon4 (USB bus number 4)
7. usbmon5 (USB bus number 5)
8. usbmon6 (USB bus number 6)
9. usbmon7 (USB bus number 7)
10. any (Pseudo-device that captures on all interfaces)
11. lo (Sorry, No description available for this device)

Enter the interface name on which you want to run the packet sniffer : wlan0

 ---You opted for device [wlan0] to capture [10] packets---

 Starting capture...
Packet number [1], length of this packet is: 496

Packet number [2], length of this packet is: 66

Packet number [3], length of this packet is: 357

Packet number [4], length of this packet is: 66

Packet number [5], length of this packet is: 238

Packet number [6], length of this packet is: 66

Packet number [7], length of this packet is: 403

Packet number [8], length of this packet is: 66

Packet number [9], length of this packet is: 121

Packet number [10], length of this packet is: 66

Done with packet sniffing!

If you are not executing the above program as root, you should use sudo to run the program as the actions done by libpcap library requires super user privileges.