How to Perform Packet Sniffing Using Libpcap with C Example Code

by Himanshu Arora on October 25, 2012

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.


Linux Sysadmin Course Linux provides several powerful administrative tools and utilities which will help you to manage your systems effectively. If you don’t know what these tools are and how to use them, you could be spending lot of time trying to perform even the basic administrative tasks. The focus of this course is to help you understand system administration tools, which will help you to become an effective Linux system administrator.
Get the Linux Sysadmin Course Now!

If you enjoyed this article, you might also like..

  1. 50 Linux Sysadmin Tutorials
  2. 50 Most Frequently Used Linux Commands (With Examples)
  3. Top 25 Best Linux Performance Monitoring and Debugging Tools
  4. Mommy, I found it! – 15 Practical Linux Find Command Examples
  5. Linux 101 Hacks 2nd Edition eBook Linux 101 Hacks Book

Bash 101 Hacks Book Sed and Awk 101 Hacks Book Nagios Core 3 Book Vim 101 Hacks Book

{ 16 comments… read them below or add one }

1 Bob October 25, 2012 at 11:41 am

Wow, great article!!! I always wondered how this was done and now I know. Keep up the great work!!!

2 Yuvaraj A October 25, 2012 at 8:06 pm

Really a nice article.
I have used wireshark and tcpdump. But i have not thought about how those tools works.

Good work!!!

3 alex December 14, 2012 at 1:08 am

HELP!! why I failed when I run out on the terminal>> sniffer.c: 66:36: error: empty character constant

4 dimple shah January 21, 2013 at 1:57 am

pcap_open_live() fails for eth1 which is actually the active interface in my machine. Error is- the device does not exist. Can you please help me to fix this problem?

5 Mooj February 26, 2013 at 1:33 am

To avoid the empty character constant problem, write
dev_buff[strlen(dev_buff)-1] =”;

Select the root for compiling file testing.c
gcc testing.c -lpcap -o testing

For Execution write
./testing tcp 10

6 Mooj February 27, 2013 at 4:51 am

Missing zero digit for initilizing in ….dev_buff[strlen(dev_buff)-1] =’0′;
The dev_buff[strlen(dev_buff)-1] should be initialized with zero (=’0′) to eliminate or clear off the trailing newline that fgets sets, like dev_buff[strlen(dev_buff)-1] =’0′

7 Jidifi August 27, 2013 at 2:15 am

Instead of:
dev_buff[strlen(dev_buff)-1] =’0′;
Try:
dev_buff[strlen(dev_buff)-1] =”;

It works for me.

8 Jidifi August 27, 2013 at 2:17 am

Correction:
========
Instead of:
dev_buff[strlen(dev_buff)-1] =’0′;
Try:
dev_buff[strlen(dev_buff)-1] =’\';

It works for me.

9 Jidifi August 27, 2013 at 2:20 am

Typo correction 2:
==============
Instead of:
dev_buff[strlen(dev_buff)-1] =’0′;
Try:
dev_buff[strlen(dev_buff)-1] =’(anti-slash character)0′;

It works for me.

10 Sasha December 2, 2013 at 7:11 am

Remember this: ’0′ != 0
So the solution is here:
dev_buff[strlen(dev_buff)-1] = 0;
Otherwise, the device will be affected by the assigned char.

11 Chris December 2, 2013 at 11:14 pm

Hi all,

Help —> Have tried all the given solutions by the people above me and none of it works..
^^ Sasha- Have u got urs working .. I tried your solution and dint work as well..

12 Sasha December 3, 2013 at 2:37 am

Yes, it works for me. Con you compile the code with this command?
gcc testing.c -lpcap -o testing

13 Chris December 4, 2013 at 12:08 am

Thanks a lot Sasha.. got compiled .. but i still get this error when i run it
sudo ./testing tcp 10

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. any (Pseudo-device that captures on all interfaces)
4. lo (Sorry, No description available for this device)

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

—You opted for device [3] to capture [10] packets—

Starting capture…pcap_open_live() failed due to [3: No such device exists (SIOCGIFHWADDR: No such device)]

It is the case for all 4 interfaces that i tried… It will be great if you could help out
Thanks

14 Chris December 4, 2013 at 12:12 am

silly of me to do that … should have entered interface name.. even then i get a pcap_compile failed error..

15 Chris December 4, 2013 at 9:43 pm

Works perfectly fine in an another system.. Thanks a lot Sasha

16 Asad February 16, 2014 at 9:05 pm

Hello, Thanks for great information.
I want to use libpcap to capture the MAC addresses of all station which are connected or not connected to a AP. Could you please help me? Should I put my wlan0 on monitor mode? What should I add to this code to do this.
I want to compile the code on a router linux operation system to detect MAC addresses of the devices with time stamp.

Leave a Comment

Previous post:

Next post: