Building a Packet Sniffer with Raw Sockets in C
23 Jan 2025Introduction
Network packet sniffing is an essential skill in the toolbox of any systems programmer or network engineer. It enables us to inspect network traffic, debug communication issues, and even learn how various networking protocols function under the hood.
In this article, we will walk through the process of building a simple network packet sniffer in C using raw sockets.
Before we begin, it might help to run through a quick networking primer.
OSI and Networking Layers
Before diving into the code, let’s briefly revisit the OSI model—a conceptual framework that standardizes network communication into seven distinct layers:
- Physical Layer: Deals with the physical connection and transmission of raw data bits.
- Data Link Layer: Responsible for framing and MAC addressing. Ethernet operates at this layer.
- Network Layer: Handles logical addressing (IP addresses) and routing. This layer is where IP packets are structured.
- Transport Layer: Ensures reliable data transfer with protocols like TCP and UDP.
- Session Layer: Manages sessions between applications.
- Presentation Layer: Transforms data formats (e.g., encryption, compression).
- Application Layer: Interfaces directly with the user (e.g., HTTP, FTP).
Our packet sniffer focuses on Layers 2 through 4. By analyzing Ethernet, IP, TCP, UDP, and ICMP headers, we gain insights into packet structure and how data travels across a network.
The Code
In this section, we’ll run through the functions that are needed to implement our packet sniffer. The layers that we’ll focus on are:
- Layer 2 (Data Link): Capturing raw Ethernet frames and extracting MAC addresses.
- Layer 3 (Network): Parsing IP headers for source and destination IPs.
- Layer 4 (Transport): Inspecting TCP, UDP, and ICMP protocols to understand port-level communication and message types.
Layer 2 (Data Link)
The Data Link Layer is responsible for the physical addressing of devices on a network. It includes the Ethernet header, which contains the source and destination MAC addresses. In this section, we analyze and print the Ethernet header.
Layer 3 (Network)
The Network Layer handles logical addressing and routing. In our code, this corresponds to the IP header, where we extract source and destination IP addresses.
Here, we use the iphdr
structure to parse the IP header. The inet_ntoa
function converts the source and destination
IP addresses from binary format to a human-readable string.
Layer 4 (Transport)
The Transport Layer ensures reliable data transfer and includes protocols like TCP, UDP, and ICMP. We have specific functions to parse and display these packets:
The TCP version of this function has a source and destination for the packet, but also has a sequence and acknowledgement which are key features for this protocol.
The UDP counterpart doesn’t have the sequencing or acknowledgement as it’s a general broadcast protocol.
ICMP’s type
, code
, and checksum
are used in the verification process of this protocol.
Tying it all together
The architecture of this code is fairly simple. The main
function sets up a loop which will continually receive raw
information from the socket. From there, a determination is made about what level the information is at. Using this
information we’ll call/dispatch to a function that specialises in that layer.
The recvfrom
receives the raw bytes in from the socket.
The process_packet
function is responsible for the dispatch of the information. This is really a switch
statement
focused on the incoming protocol:
This then ties all of our functions in together.
Running
Because of the nature of the information that this application will pull from your system, you will need to run this as root. You need that low-level access to your networking stack.
Conclusion
Building a network packet sniffer using raw sockets in C offers valuable insight into how data flows through the network stack and how different protocols interact. By breaking down packets layer by layer—from the Data Link Layer (Ethernet) to the Transport Layer (TCP, UDP, ICMP)—we gain a deeper understanding of networking concepts and system-level programming.
This project demonstrates key topics such as:
- Capturing raw packets using sockets.
- Parsing headers to extract meaningful information.
- Mapping functionality to specific OSI layers.
Packet sniffers like this are not only useful for learning but also serve as foundational tools for network diagnostics, debugging, and security monitoring. However, it’s essential to use such tools ethically and responsibly, adhering to legal and organizational guidelines.
In the future, we could extend this sniffer by writing packet payloads to a file, adding packet filtering (e.g., only capturing HTTP or DNS traffic), or even integrating with libraries like libpcap for more advanced use cases.
A full gist of this code is available to check out.