Skip to main content
Version: v24.09

Feature Overview

Packet capture

Packet capture (A.K.A packet sniffing or network tapping) is the process of intercepting and logging traffic that passes over a digital network or part of a network (taken from Wikipedia). It is one of the most important and popular features of PcapPlusPlus and it is what PcapPlusPlus is basically all about.

There are multiple packet capture engines out there, the most popular are libpcap, WinPcap (which is libpcap for Windows), Npcap (WinPcap's successor), Intel DPDK, eBPF AF_XDP, ntop's PF_RING and raw sockets. Each engine has different strengths, purposes and features, works on different platforms and obviously has different APIs and setup process. Most of them are written in C (to achieve the best performance) and don't expose a C++ interface.

PcapPlusPlus aims to create a consolidated and easy-to-use C++ API for all of these engines which simplifies their complexity and provides a common infrastructure for capturing, processing, analyzing and forging of network packets.

Here is a list of of the packet capture engines currently supported:

The main features provided for each one are:

  • Browse all interfaces available on the machine
  • Capture network traffic on a specific interface
  • Send packets to the network
  • Filter packets

What's next?

You can find out more information in the API documentation, tutorial pages and browse through the code of the example apps.

Packet parsing and crafting

PcapPlusPlus provides advanced capabilities for packet analysis which include:

  • Packet parsing, which is a detailed analysis of the protocols and layers of a packet
  • Packet crafting which is generation and editing of network packets

A large variety of network protocols are supported, see the full list here.

Packets can be analyzed from any source including those captured from the network, packets stored in PCAP/PCAPNG files, and more. The design of PcapPlusPlus allows similar analysis capabilities for each packet, regardless of where it came from. For example, you can write the same code for parsing packets that are captured using DPDK, libpcap, WinPcap/Npcap, raw sockets or read from a PCAP/PCAPNG file. Same goes for packet crafting.

Consider this simple code snippet that shows how to read a packet from a PCAP file, parse it, identify if it's an IPv4 packet and print the source and dest IP addresses:

#include <iostream>
#include <IPv4Layer.h>
#include <Packet.h>
#include <PcapFileDevice.h>

int main(int argc, char* argv[])
{
// open a pcap file for reading
pcpp::PcapFileReaderDevice reader("1_packet.pcap");
if (!reader.open())
{
std::cerr << "Error opening the pcap file" << std::endl;
return 1;
}

// read the first (and only) packet from the file
pcpp::RawPacket rawPacket;
if (!reader.getNextPacket(rawPacket))
{
std::cerr << "Couldn't read the first packet in the file" << std::endl;
return 1;
}

// parse the raw packet into a parsed packet
pcpp::Packet parsedPacket(&rawPacket);

// verify the packet is IPv4
if (parsedPacket.isPacketOfType(pcpp::IPv4))
{
// extract source and dest IPs
pcpp::IPv4Address srcIP = parsedPacket.getLayerOfType<pcpp::IPv4Layer>()->getSrcIPv4Address();
pcpp::IPv4Address destIP = parsedPacket.getLayerOfType<pcpp::IPv4Layer>()->getDstIPv4Address();

// print source and dest IPs
std::cout
<< "Source IP is '" << srcIP << "'; "
<< "Dest IP is '" << destIP << "'"
<< std::endl;
}

// close the file
reader.close();

return 0;
}

Read and write packets from/to files

Network packets can be stored in files, usually for offline analysis. PCAP and PCAPNG are the two most popular file formats and both are supported in PcapPlusPlus. In addition the SNOOP file format is supported for read-only.

The main features include:

  • Read packets from PCAP/PCAPNG/SNOOP files
  • Create new PCAP/PCAPNG files and write packets to them
  • Use the packet filtering mechanism to read or write only packets that match the filter
  • Append packets to existing PCAP/PCAPNG files
  • Write compressed PCAPNG files using Zstd compression in real time (OPTIONAL and requires building with Zstd)

Consider this simple code snippet that shows how to open a PCAP file for reading and another PCAPNG file for writing, and then read all packets from the PCAP file and write them to the PCAPNG file:

// create a pcap file reader
pcpp::PcapFileReaderDevice pcapReader("input.pcap");
pcapReader.open();

// create a pcapng file writer
pcpp::PcapNgFileWriterDevice pcapNgWriter("output.pcapng");
pcapNgWriter.open();

// raw packet object
pcpp::RawPacket rawPacket;

// read packets from pcap reader and write pcapng writer
while (pcapReader->getNextPacket(rawPacket)) {
pcapNgWriter.writePacket(rawPacket);
}

pcapNgReader.close(); //These will close when going out of scope
pcapNgWriter.close(); //or you may close them manually

For more information please refer to the Read/Write Pcap Files tutorial, look at the API documentation or browse through the code of the example apps.

Reading/Writing PCAPNG files with compression

Zstd streaming compression is only supported when working with pcapng files. To enable this feature you must build PcapPlusPlus with Zstd support. For more guidance on building PcapPlusPlus see the build instructions per platform.

Once you have a working build modifying your code to start enabling compression is fast and easy!

When writing PCAPNG files, to enable streaming compression all you need to do is add a second integer argument when constructing your writer. The integer should be between 0-10 and it specifies the compression level. Values outside this range will be clamped. A value of zero, which is also the default, indicates to skip compression. A value of 10 would indicate use maximum compression. For most scenarios a value of 5 or less should be adequate.

For reading compressed PCAPNG files the only requirement is that the file name extension must terminate in .zstd. If a compressed file is supplied to the reader without the .zstd extension the file will fail to load. Currently, APPENDING to a compressed file is NOT supported!

If you write code enabling compression, by adding a compression level to your writer constructor, but use a build of PcapPlusPlus without compression support, everything will work just fine and the compression will be skipped/ignored and normal PCAPNG files will be generated/read.

There is a tradeoff between compression speed and compression efficiency. A compression value of 10 will yield the most compression but be slower, while a value of 1 will yield the least compression but be fastest. Depending upon your capture rates and data size you can tune this number to fit your needs.

Since Zstd is designed to support fast and efficient streaming compression most users should not see any noticeable performance impact when enabling compression. Exact savings from compression will always vary based upon the input data, however; in one test case an uncompressed PCAPNG file of 140MB was duplicated with a compression level of 5 to yield a compressed PCAPNG file of only 40MB giving about 4x space savings! Note that the compression is performed while the file is written so you will not notice any delay when closing the file or a long processing time like you work normally experience when compressing an existing file.

Some example compression code:

// create a pcapng file reader
pcpp::PcapNgFileReaderDevice reader("input.pcap.zstd"); //Notice the Zstd extension
reader.open(); //This is required for proper loading

// create a pcapng file writer
pcpp::PcapNgFileWriterDevice writer("output.pcapng.zstd", 5); //The second integer argument 5
pcapNgWriter.open(); //is the compression level to use

// read packets from pcapng reader and write pcapng writer
while (reader->getNextPacket(rawPacket)) {
writer.writePacket(rawPacket);
}

DPDK support

The Data Plane Development Kit (DPDK) is a set of data plane libraries and network interface controller drivers for fast packet processing, currently managed as an open-source project under the Linux Foundation. The DPDK provides a programming framework for x86, ARM, and PowerPC processors and enables faster development of high speed data packet networking applications (taken from Wikipedia).

DPDK provides packet processing in line rate using kernel bypass for a large range of network interface cards. Notice that not every NIC supports DPDK as the NIC needs to support the kernel bypass feature. You can read more about DPDK in DPDK's web-site and get a list of supported NICs here.

As DPDK API is written in C, PcapPlusPlus wraps its main functionality in easy-to-use C++ wrappers which have minimum impact on performance and packet processing rate. In addition it brings DPDK to the PcapPlusPlus framework and APIs so you can use DPDK together with other PcapPlusPlus features such as packet analysis, etc.

You can find more information about setting up DPDK and the API in DPDK support page and also in DPDK tutorial.

AF_XDP support

AF_XDP represents an address family tailored for optimizing high-performance packet processing. It functions as a socket, specifically referred to as XSK sockets, introduced in Linux Kernel 4.18. Unlike complete kernel bypass solutions like DPDK, AF_XDP leverages kernel functionality to create an efficient framework akin to DPDK or AF_Packet.

AF_XDP facilitates the direct transfer of packets to userspace, minimizing latency by circumventing the conventional kernel network stack. While it doesn't entirely bypass the kernel like DPDK, AF_XDP establishes an in-kernel fast path for accelerated packet processing.

AF_XDP is built on the foundation of eBPF (extended Berkeley Packet Filter) and XDP (eXpress Data Path). eBPF provides means to run custom code in the kernel in response to various events. XDP is one of these hooks that gives access to nework packets as soon as they arrive at the network driver. The eBPF program can then decide what to do with these packets, like modifying them, dropping them, passing them to the network stack, or in the case of AF_XDP scokets - redirect them to userspace.

Here are a few good sources to learn more about AF_XDP:

PcapPlusPlus currently provides fairly basic support for AF_XDP sockets which includes:

  • Creating an AF_XDP socket with either default or custom configuration
  • Receiving and sending packets through the socket
  • Collecting statistics on packets going through the socket

While the interface is simple, it wraps most of the complexity working with AF_XDP sockets. Behind the scenes, it leverages libbpf to create and configure the socket, then utilize various rings for receiving packets from the kernel and transmitting packets to the kernel.

AF_XDP is only available on Linux. In order to build PcapPlusplus with AF_XDP please make sure you have libbpf installed on your device and use the -DPCAPPP_USE_XDP=ON CMake option.

To learn more about the API please take a look at the AF_XDP example.

PF_RING support

PF_RING™ is a new type of network socket that dramatically improves the packet capture speed. It's providing high-speed packet capture, filtering and analysis (taken from ntop's website).

PcapPlusPlus provides a clean and simple C++ wrapper API for Vanilla PF_RING. Currently only Vanilla PF_RING is supported which provides significant performance improvement in comparison to libpcap or Linux kernel, but PF_RING Zero Copy (which allows kernel bypass and zero-copy of packets from NIC to user-space) is not yet supported.

In order to compile PcapPlusPlus with PF_RING you need to:

  • Download PF_RING from ntop's web-site
  • Once PF_RING is compiled successfully, you need to run PcapPlusPlus build using the -DPCAPPP_USE_PF_RING=ON option
  • Before you activate any PcapPlusPlus application that uses PF_RING, don't forget to enable PF_RING kernel module. If you forget to do that, PcapPlusPlus will output an - appropriate error on startup which will remind you:
sudo insmod <PF_RING_LOCATION>/kernel/pf_ring.ko

Packet reassembly

Network protocols often need to transport large chunks of data which are complete in themselves, e.g. when transferring a file. The underlying protocol might not be able to handle that chunk size (e.g. limitation of the network packet size), or is stream-based like TCP, which doesn’t know data chunks at all.

In that case the network protocol has to handle the chunk boundaries itself and (if required) spread the data over multiple packets. It obviously also needs a mechanism to determine the chunk boundaries on the receiving side.

This mechanism is called reassembly, although a specific protocol specification might use a different term for this (e.g. desegmentation, defragmentation, etc).

(this description is taken from Wireshark documentation).

PcapPlusPlus currently supports two types of packets reassembly:

  • IPv4 and IPv6 defragmentation which is a Layer 3 (Network layer) packet reassembly. You can read more about IP fragmentation here. To get more information about how it works and the API to use it please refer to the API documentation and browse through the code of the IPDefragUtil and IPFragUtil example apps
  • TCP reassembly which is a Layer 4 (Transport layer) packet reassembly. To get more information on how it works and the API to use it please refer to the API documentation and browse through the code of the TcpReassembly example app

Packet filtering

Most packet capture engines contain packet filtering capabilities. In order to set the filters there should be a known syntax user can use. The most popular syntax is Berkeley Packet Filter (BPF). Detailed explanation of the syntax can be found here.

The challenge with BPF is that it is too complicated and poorly documented. When compiling BPF filters you often get syntax errors that are hard to understand and debug. Our experience with BPF was not good, so we decided to include in PcapPlusPlus a filter mechanism which is more structured, easier to understand and less error-prone by creating classes that represent filters. Each possible filter phrase is represented by a class. The filter, in the end, is that class.

Consider the following code snippet for creating the filter src ip 1.1.1.1 and dst port 80 and setting it up on a packet capture device:

// create a filter instance to capture only traffic on port 80
pcpp::PortFilter portFilter(80, pcpp::DST);

// create a filter instance to capture only TCP traffic
pcpp::IPFilter ipFilter("1.1.1.1", pcpp::SRC);

// create an AND filter to combine both filters - capture only TCP traffic on port 80
pcpp::AndFilter andFilter;
andFilter.addFilter(&portFilter);
andFilter.addFilter(&ipFilter);

// set the filter on the device
dev->setFilter(andFilter);

You can read more in the PcapFilter.h API documentation and in the capture packets tutorial.

TLS Fingerprinting

TLS Fingerprinting is a technique that extracts specific parameters from TLS handshake messages such as ClientHello and ServerHello. Most applications that need network access (such as browsers, apps, etc.) have a unique combination(s) of these parameters while making network connections, so this technique can be used to fingerprint those applications. By using a database of curated fingerprints it's possible to detect network traffic anomalies which can point to malware, security vulnerabilities or other undesired behavior. It can also be used for network visibility, for example identify popular applications, detect applications that use weak encryption, etc. This method of fingerprinting is very effective because TLS handshake is often done in lower levels of the application hence it's difficult to forge or tamper with it.

The primary concept for TLS fingerprinting came from Lee Brotherston’s 2015 research (GitHub repo). Following his work more articles and implementations came out, one of them was Salesforce JA3 (GitHub repo) which implements TLS fingerprinting in Python and Zeek. This project introduced two types of TLS fingerprinting: JA3 which is the "traditional" ClientHello fingerprinting (which is the more common and well-known method), and JA3S which is ServerHello fingerprinting.

PcapPlusPlus contains an implementation of JA3 and JA3S in C++. There are not a lot of C++ implementations for TLS fingerprinting and we thought this can be a good feature for PcapPlusPlus as it already analyzes TLS network traffic.

Using TLS fingerprinting in PcapPlusPlus is very easy, here is a quick example:

// Get the handshake layer and extract the ClientHello message
pcpp::SSLHandshakeLayer* sslHandshakeLayer = parsedPacket.getLayerOfType<pcpp::SSLHandshakeLayer>();
pcpp::SSLClientHelloMessage* clientHelloMessage =
sslHandshakeLayer->getHandshakeMessageOfType<pcpp::SSLClientHelloMessage>();

// Extract the TLS fingerprint from the ClientHello message
pcpp::SSLClientHelloMessage::ClientHelloTLSFingerprint tlsFingerprint =
clientHelloMessage->generateTLSFingerprint();
std::pair<std::string, std::string> tlsFingerprintStringAndMD5 = tlsFingerprint.toStringAndMD5();

// Print the full fingerprint and an MD5 version of it
std::cout
<< "ClientHello (JA3) TLS fingerprint: "
<< tlsFingerprintStringAndMD5.first
<< "; MD5: "
<< tlsFingerprintStringAndMD5.second
<< std::endl;

ServerHello TLS fingerprinting (JA3S) is almost similar but for pcpp::SSLServerHelloMessage messages.

To learn more please take a look at the TLS fingerprinting example in PcapPlusPlus GitHub repo which demonstrates how to collect ClientHello and ServerHello fingerprints from live traffic or pcap files, write them to an output file and display various statistics.

Supported network protocols

PcapPlusPlus currently supports parsing, editing and generation of packets of the following protocols:

  1. Ethernet II
  2. IEEE 802.3 Ethernet
  3. LLC (Only BPDU supported)
  4. Null/Loopback
  5. Packet trailer (a.k.a footer or padding)
  6. PPPoE
  7. SLL (Linux cooked capture)
  8. SLL2 (Linux cooked capture v2)
  9. STP
  10. VLAN
  11. VXLAN
  12. Wake on LAN (WoL)
  13. NFLOG (Linux Netfilter NFLOG) - parsing only (no editing capabilities)

Network Layer (L3)

  1. ARP
  2. GRE
  3. ICMP
  4. ICMPv6
  5. IGMP (IGMPv1, IGMPv2 and IGMPv3 are supported)
  6. IPv4
  7. IPv6
  8. MPLS
  9. NDP
  10. Raw IP (IPv4 & IPv6)
  11. VRRP (IPv4 & IPv6)
  12. WireGuard

Transport Layer (L4)

  1. COTP
  2. GTP (v1)
  3. IPSec AH & ESP - parsing only (no editing capabilities)
  4. TCP
  5. TPKT
  6. UDP

Session Layer (L5)

  1. SDP
  2. SIP

Presentation Layer (L6)

  1. SSL/TLS - parsing only (no editing capabilities)

Application Layer (L7)

  1. ASN.1 decoder and encoder
  2. BGP (v4)
  3. DHCP
  4. DHCPv6
  5. DNS
  6. FTP
  7. HTTP headers (request & response)
  8. LDAP
  9. NTP (v3, v4)
  10. Radius
  11. S7 Communication (S7comm)
  12. SMTP
  13. SOME/IP
  14. SSH - parsing only (no editing capabilities)
  15. Telnet - parsing only (no editing capabilities)
  16. Generic payload

License

PcapPlusPlus is released under the Unlicense license.