Packet sniffer and parser in C
This post will cover a concise implementation of how to open live pcap sessions on any network device and reading the incoming packets on that interface. In the end, the post will display how to parse the packets appropriately to get the required information.
We use libpcap in the implementation to listen to the packets on the network device. The same can also be used to directly read from a pcap file instead of live sessions. In the implementation, we have turned the promiscuous mode to true so that we are able to listen to packets also not destined for the machine on which the session is being run on.
In the above gist, we use bpf (Berkeley Packet Filters) along with libpcap to compile the required filters. BPF or eBPF can be used to run secure sandboxed code directly in the kernel. We can use the bpf filters to listen for the kind of traffic that we are interested in. In the above gist, we use this line to listen on port 22 which is mainly used for SSH.
char filter_exp[] = “port 22”;
Once the filters are compiled, we set the filter on the libpcap session to filter the packets appropriately.
We are using pcap_next here to get the next packet. Ideally, we should use pcap_loop so that can read the packets from the session in a loop.
To compile and run the code, try the following
gcc -o /tmp/pcapper packet_capture.c -lpcap
Now that we have received the packet, we move on to the techniques used to parse the packet to retrieve the information required.
In order to understand the above gist, it is important to understand pointer arithmetics and how information is laid across the memory.
We can see that the packet variable is a pointer of u_char which points to the starting byte of the data pointed by packet.
const u_char *packet;
In order to read the ethernet header, we need to typecast the packet to the ethernet variable of type sniff_ethernet
ethernet = (struct sniff_ethernet*)(packet);
The ethernet headers are of fixed length always. To parse the next layer, we do the following.
ip = (struct sniff_ip*)(packet + SIZE_ETHERNET);
The IP header starts from (packet + SIZE_ETHERNET). The TCP headers can be derived from the same format and so on.
You can also implement custom tunneling protocols via this format and parse the packet accordingly.
The entire post has been heavily derived from https://www.tcpdump.org/pcap.html.
Please let me know if you have any queries regarding the article. Happy reading!!
References