30 September 2026
Who is around me I know well, for I am a carp and what would that entail? My hearing is sharp and that alone might do, but what does the Address Resolution Protocol do? It helps out too!
There is a labyrinth in every carp, go read the wikipedia article it is very interesting. Anyhow, back to informatics. Reading pcap files is great because we have free and open-source packet analyzers such as Wireshark. But I wanted to create my own and that only for the sole purpose of reading ARP requests.
Recap on ARP. But essentially we're expecting messages such as:
Ethernet protocol source: fc:22:f4:a4:7d:20 destination: ff:ff:ff:ff:ff:ff ARP protocol: who has 10.0.0.14? Tell 10.0.0.138
Our pcap file can include many different protocols, but we only care about ARP, so let's first filter it out. ARP has a standardized hextype of 0x0806.
Then, when we are certain that it in fact is ARP, we can let ourselves be guided by the structure of the packet. (can be seen on the linked wikipedia article).
The most single important piece is the Operation located at the 32+16 bit within the ARP packet. Notice that I am doing packet[21]. It is 21 because
(32+16)/8 is 6. Before the 32+16 bits there is 14 bytes containing ethernet data, meaning when you add to that you get 20. The operation is two bytes wide, meaning 20 and 21, but since it is big-endian, packet[20] will always be 00 and values regarding request/reply will sit in packet[21]. When looking at the packet[n], you really only care about n>14, since lesser ones are taken from the ethernet header.
#include <pcap.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
void process_packet(u_char *arg,
const struct pcap_pkthdr *pkthdr,
const u_char *packet) {
// ETHERTYPE_ARP 0x0806
if (packet[12] != 0x08 || packet[13] != 0x06) {
return;
}
printf("Ethernet protocol "
"source: %02x:%02x:%02x:%02x:%02x:%02x "
"destination: %02x:%02x:%02x:%02x:%02x:%02x "
"ARP protocol: ",
packet[6], packet[7], packet[8],
packet[9], packet[10], packet[11],
packet[0], packet[1], packet[2],
packet[3], packet[4], packet[5]);
if (packet[21] == 1) {
// req
printf("who has %d.%d.%d.%d? Tell %d.%d.%d.%d\n",
packet[38], packet[39], packet[40], packet[41],
packet[28], packet[29], packet[30], packet[31]);
}
else {
// reply
printf("%d.%d.%d.%d is at %02x:%02x:%02x:%02x:%02x:%02x\n",
packet[28], packet[29], packet[30], packet[31],
packet[22], packet[23], packet[24],
packet[25], packet[26], packet[27]);
}
}
int main(int argc, char *argv[]) {
if (argc != 2) {
fprintf(stderr, "Expected format: %s <path_to_pcapfile>\n",
*argv);
return EXIT_FAILURE;
}
const char *file = argv[1];
pcap_t *pcap = NULL;
char error_buffer[PCAP_ERRBUF_SIZE];
memset(error_buffer, 0, PCAP_ERRBUF_SIZE);
// Open saved pcap file.
pcap = pcap_open_offline(file, error_buffer);
// Check if we were able to open the file.
if (pcap == NULL) {
fprintf(stderr, "ERROR: %s\n", error_buffer);
return EXIT_FAILURE;
}
// Loop over all packets and call our handler.
if (pcap_loop(pcap, -1, process_packet, NULL) == -1) {
fprintf(stderr, "ERROR: %s\n", pcap_geterr(pcap));
return EXIT_FAILURE;
}
pcap_close(pcap);
return EXIT_SUCCESS;
}wirecARP!