Saturday, October 26, 2013

Creating a simple and fast packet sniffer in C++

I have seen several articles on the web about writing packet sniffers using C and C++ which suggest using raw sockets and dealing with protocol headers and endianness yourself. This is not only very tedious but also there are libraries which already deal with that for you and make it extremely easy to work with network packets.

One of them is libtins, a library I have been actively developing for the past few years. It has support for several protocols, including Ethernet, IP, IPv6, TCP, UDP, DHCP, DNS and IEEE 802.11, and it works on GNU/Linux, Windows, OSX and FreeBSD. It even works on different architectures such as ARM and MIPS, so you could go ahead and develop some application which could be executed inside routers and other devices.

Let's see how you would sniff some TCP packets and print their source and destination port and addresses:
#include <iostream>
#include <tins/tins.h>
using namespace std;
using namespace Tins;
bool callback(const PDU &pdu) {
    const IP &ip = pdu.rfind_pdu<IP>();
    const TCP &tcp = pdu.rfind_pdu<TCP>();
    cout << ip.src_addr() << ':' << tcp.sport() << " -> " 
         << ip.dst_addr() << ':' << tcp.dport() << endl;
    return true;
}

int main() {
    // Sniff on interface eth0
    Sniffer sniffer("eth0");
    sniffer.sniff_loop(callback);
}
This is the output I get when executing it:


As you can see, it's fairly simple. Let's go through the snippet and see what it's doing:
  • The callback function is the one that libtins will call for us each time a new packet is sniffed. It returns a boolean, whch indicates whether sniffing should go on or not, and takes a parameter of type PDU, which will hold the sniffed packet. This library represents packets as a series of Protocol Data Units(PDU) stacked over each other. So in this case, every packet would contain an EthernetII, IP and TCP PDUs.
  • Inside callback's body, you can see that we're calling PDU::rfind_pdu. This is a member function template which looks for the provided PDU type inside the packet, and returns a reference to it. So in the first two lines we're retrieving the IP and TCP layers, and then we're simply printing the addresses and ports.
  • Finally, in main an object of type Sniffer is constructed. When constructing it, we indicate that we want to sniff on interface eth0. After that, Sniffer::sniff_loop is called, which will start sniffing packets and calling our callback for each of them.
Note that this example will run successfully on any of the supported operating systems(as long as you use the right interface name, of course). The endianness of each of the printed fields is handled internally by the library, so you don't even have to worry about making your code work in Big Endian architectures.

Now, you may be wondering whether using libtins will make your code significantly slower. If that is your concern, then you should not worry about it at all! This library was designed keeping efficiency in mind at all times. As a consequence it's the fastest packet sniffing and interpretation library I've tried out (note that I tried several, such as scapy, dpkt, impacket and libcrafter). Go ahead and have a look at these benchmarks to see how fast it actually works.

libtins allows you to implement fast packet sniffers in very few lines of code. It also supports additional features like reassembling and following TCP streams, decrypting WPA2 traffic (both TKIP and AES) and defragmenting IP datagrams.

If you want to learn more about libtins, please visit this tutorial, which covers everything you should know before starting to develop your network sniffing application!