Building a packet sniffer

Talal
4 min readAug 2, 2023

--

In this series we are going to build a packet sniffer in C by using libpcap.

What is a packet sniffer ?

A packet sniffer is a piece of hardware or software used to record and monitor traffic on a network. There are many packet sniffers out there one example is Wireshark.

Requirements :

  • C programming fundamentals, especially pointers.
  • Network fundamentals.

What is libpcap ?

libpcap is an open source library that allows us to capture network packets. It is used by wireshark, tcpdump, nmap, snort and many other programs.

Installation

$ wget -q https://www.tcpdump.org/release/libpcap-1.10.4.tar.gz
$ tar xzf libpcap-1.10.4.tar.gz
$ cd libpcap-1.10.4 && ./configure
$ make
$ sudo make install

Note: above is the latest version released as of this writing, to get the latest version visit https://www.tcpdump.org/ .

Getting started

To start capturing packets we need to first now what device are we going to be capturing the packets from, run the command ifconfig and select the device you want. Here is an example output from Ubuntu machine :

$ ifconfig
enp0s3: flags=4163<UP,BROADCAST,RUNNING,MULTICAST> mtu 1500
inet 192.168.8.4 netmask 255.255.255.0 broadcast 192.168.8.255
inet6 fe80::ab13:cbef:ac72:beab prefixlen 64 scopeid 0x20<link>
ether 08:00:27:4a:4e:cf txqueuelen 1000 (Ethernet)
RX packets 182031 bytes 232029230 (232.0 MB)
RX errors 0 dropped 0 overruns 0 frame 0
TX packets 98371 bytes 7377681 (7.3 MB)
TX errors 0 dropped 0 overruns 0 carrier 0 collisions 0
lo: flags=73<UP,LOOPBACK,RUNNING> mtu 65536
inet 127.0.0.1 netmask 255.0.0.0
inet6 ::1 prefixlen 128 scopeid 0x10<host>
loop txqueuelen 1000 (Local Loopback)
RX packets 3174 bytes 381220 (381.2 KB)
RX errors 0 dropped 0 overruns 0 frame 0
TX packets 3174 bytes 381220 (381.2 KB)
TX errors 0 dropped 0 overruns 0 carrier 0 collisions 0

Some header files are required, and these are :

#include <stdio.h>
#include <stdlib.h>
#include <pcap/pcap.h>

Then we need to open the device for reading the packets using pcap_open_live() function. This function takes five arguments :

  • The device to open for reading.
  • The snapshot length.
  • Enable/Disable promiscuous mode.
  • Packet buffer timeout in milliseconds.
  • A buffer to store any errors.

Currently we are only concerned with the first and last argument (device and error buffer)

pcap_open_live() returns a pointer of type pcap_t which will be passed as an argument to pcap_loop()

/*
* Declare the device name and error buffer size
* PCAP_ERRBUF_SIZE is defined in pcap.h
*/
char *device = “enp0s3”;
char error_buffer[PCAP_ERRBUF_SIZE];

/*
* BUFSIZ is defined in stdio.h, 0 to disable promiscuous mode and -1 to
* disable timeout.
*/
pcap_t *capdev = pcap_open_live(device, BUFSIZ, 0, -1, error_buffer);

/*
* if capdev is null that means something went wrong, so we print the
* error (which is stored in error_buffer) and exit the program.
*/
if (capdev == NULL) {
printf(“ERR: pcap_open_live() %s\n”, error_buffer);
exit(1);
}

The next thing we need to do is to call pcap_loop(), but what is it ? pcap_loop() will process a number of packets, if this number
is set to -1 or 0 it will process packets indefinitely or until another
condition stops it.

pcap_loop takes four arguments:

  • Device used to capture packets.
  • Number of packets to process.
  • Callback function, this is called each time a packet is captured.
  • The first argument to pass to the callback function.
// lets limit the capture to 5 packets.
int packets_count = 5;
/*
* pcap_loop returns 0 upon success and -1 if it fails,
* we listen to this return value and print an error if
* pcap_loop failed
*/
if (pcap_loop(capdev, packets_count, call_me, (u_char*)NULL)) {
printf(“ERR: pcap_loop() failed!\n”);
exit(1);
}

I know you are eager to compile and run 🙂 but we are not done yet, we have one more thing to do, and that is to define our callback function (call_me).

The callback function specifies a pcap_handle which expects three arguments :

  • The last argument passed to pcap_loop.
  • A pointer to a pcap_pkthdr struct that points to the packet timestamp and lengths.a
  • A pointer to the packet data.
void call_me(u_char *user, const struct pcap_pkthdr *pkthdr, 
const u_char *packetd_ptr) {
printf(“You just recieved a packet!\n”);
}

Now we are ready to compile and run, here is the full code :

/*
* nsniff.c
*/

#include <pcap/pcap.h>
#include <stdio.h>
#include <stdlib.h>

void call_me(u_char *user, const struct pcap_pkthdr *pkthdr,
const u_char *packetd_ptr) {
printf("You just recieved a packet!\n");
}

int main(int argc, char const *argv[]) {
char *device = "enp0s3"; // remember to replace this with your device name
char error_buffer[PCAP_ERRBUF_SIZE];
int packets_count = 5;

pcap_t *capdev = pcap_open_live(device, BUFSIZ, 0, -1, error_buffer);

if (capdev == NULL) {
printf("ERR: pcap_open_live() %s\n", error_buffer);
exit(1);
}

if (pcap_loop(capdev, packets_count, call_me, (u_char *)NULL)) {
printf("ERR: pcap_loop() failed!\n");
exit(1);
}

return 0;
}
$ cc -o nsniff nsiff.c -lpcap
$ sudo ./nsniff
You just recieved a packet!
You just recieved a packet!
You just recieved a packet!
You just recieved a packet!
You just recieved a packet!

Now we have a very basic packet sniffer. In the next parts we are going to add features to this program in order to make it useful.

--

--