Understanding low-level network traffic flow requires bypassing standard OS socket abstractions (SOCK_STREAM, SOCK_DGRAM) and intercepting raw layer-2 Ethernet frames directly from wireless interface drivers.
In Linux, opening a raw socket with socket(AF_PACKET, SOCK_RAW, htons(ETH_P_ALL)) allows software to capture every packet received or transmitted over a network card—including wlan0. In this article, we write a complete C packet sniffer to decode Ethernet headers, IPv4 headers, and TCP/UDP ports in real time.
Quick Command Sequence
Enable promiscuous mode on your wireless interface, compile the C sniffer, and execute with root privileges:
# 1. Enable promiscuous mode on wlan0 interface
sudo ip link set wlan0 promisc on
# 2. Compile C packet sniffer
gcc -O2 wifi_sniffer.c -o wifi_sniffer
# 3. Run packet sniffer (requires root privileges for raw sockets)
sudo ./wifi_sniffer wlan0
# 4. Disable promiscuous mode when finished
sudo ip link set wlan0 promisc offHow Linux Raw Sockets (AF_PACKET) Work
Domain (`AF_PACKET`): Instructs the kernel to bypass higher-level TCP/IP stack processing and expose raw device driver frame buffers.
Type (`SOCK_RAW`): Delivers full raw Ethernet frames including link-layer headers rather than stripped transport payloads.
Protocol (`ETH_P_ALL`): Captures all network protocols (IP, ARP, RARP, IPv6) hitting the network interface.
Interface Binding: Using
SO_BINDTODEVICEorbind()withstruct sockaddr_lllocks the sniffer specifically towlan0.
Complete C Wi-Fi Packet Sniffer (wifi_sniffer.c)
This C program creates an AF_PACKET raw socket, binds to wlan0, reads incoming frame buffers, and decodes MAC, IP, and port headers:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <arpa/inet.h>
#include <sys/socket.h>
#include <net/ethernet.h>
#include <netinet/ip.h>
#include <netinet/tcp.h>
#include <netinet/udp.h>
#include <net/if.h>
#include <sys/ioctl.h>
#define BUFFER_SIZE 65536
void process_packet(unsigned char *buffer, int size) {
struct ethhdr *eth = (struct ethhdr *)buffer;
// Check if packet contains IPv4 header (ETH_P_IP = 0x0800)
if (ntohs(eth->h_proto) == ETH_P_IP) {
struct iphdr *ip = (struct iphdr *)(buffer + sizeof(struct ethhdr));
struct in_addr src_ip, dst_ip;
src_ip.s_addr = ip->saddr;
dst_ip.s_addr = ip->daddr;
printf("
================ [ IP PACKET ] ================
");
printf("Source MAC : %02X:%02X:%02X:%02X:%02X:%02X
",
eth->h_source[0], eth->h_source[1], eth->h_source[2],
eth->h_source[3], eth->h_source[4], eth->h_source[5]);
printf("Destination MAC : %02X:%02X:%02X:%02X:%02X:%02X
",
eth->h_dest[0], eth->h_dest[1], eth->h_dest[2],
eth->h_dest[3], eth->h_dest[4], eth->h_dest[5]);
printf("Source IP : %s
", inet_ntoa(src_ip));
printf("Destination IP : %s
", inet_ntoa(dst_ip));
printf("Protocol : ");
if (ip->protocol == IPPROTO_TCP) {
struct tcphdr *tcp = (struct tcphdr *)(buffer + sizeof(struct ethhdr) + (ip->ihl * 4));
printf("TCP (Src Port: %d, Dst Port: %d)
", ntohs(tcp->source), ntohs(tcp->dest));
} else if (ip->protocol == IPPROTO_UDP) {
struct udphdr *udp = (struct udphdr *)(buffer + sizeof(struct ethhdr) + (ip->ihl * 4));
printf("UDP (Src Port: %d, Dst Port: %d)
", ntohs(udp->source), ntohs(udp->dest));
} else if (ip->protocol == IPPROTO_ICMP) {
printf("ICMP
");
} else {
printf("Other (%d)
", ip->protocol);
}
}
}
int main(int argc, char *argv[]) {
const char *iface = (argc > 1) ? argv[1] : "wlan0";
// 1. Create AF_PACKET raw socket
int raw_socket = socket(AF_PACKET, SOCK_RAW, htons(ETH_P_ALL));
if (raw_socket < 0) {
perror("Socket creation failed (Root privileges required)");
return 1;
}
// 2. Bind socket to target interface (e.g. wlan0)
if (setsockopt(raw_socket, SOL_SOCKET, SO_BINDTODEVICE, iface, strlen(iface)) < 0) {
perror("Failed to bind socket to interface");
close(raw_socket);
return 1;
}
printf("Started Wi-Fi Raw Packet Sniffer on interface '%s'...
", iface);
unsigned char buffer[BUFFER_SIZE];
while (1) {
int data_size = recvfrom(raw_socket, buffer, BUFFER_SIZE, 0, NULL, NULL);
if (data_size < 0) {
perror("Recvfrom error");
break;
}
process_packet(buffer, data_size);
}
close(raw_socket);
return 0;
}Byte Ordering and Header Pointer Arithmetic
Network Byte Order (`ntohs` / `ntohl`): Network headers store multi-byte integers in Big-Endian format. Always wrap 16-bit ports and protocols in
ntohs()for correct x86/ARM Little-Endian rendering.Variable IP Header Length (`ip->ihl`): Never assume IPv4 headers are 20 bytes. Calculate transport header offsets dynamically using
sizeof(struct ethhdr) + (ip->ihl * 4).
Comments and corrections