For a small tool that I am building for OSX, I want to capture the lengths of packets send and received from a certain ethernet controller.
When I fetch the ethernet cards I also get extra information like maximum packet sizes, link speeds etc.
When I start the (what I call) 'trafficMonitor' I launch it like this:
static void initializeTrafficMonitor(const char* interfaceName, int packetSize) {
char errbuf[PCAP_ERRBUF_SIZE];
pcap_t* sessionHandle = pcap_open_live(interfaceName, packetSize, 1, 100, errbuf);
if (sessionHandle == NULL)
{
printf("Error opening session for device %s: %s\n", interfaceName, errbuf);
return;
}
pcap_loop(sessionHandle, -1, packetReceived, NULL);
}
The supplied interfaceName
is the BSD name of the interface, for example en0
. The packetSize
variable is an integer where I supply the maximum packetsize for that ethernet adapter (that seemed logical at that time). For example the packetsize for my WiFi adapter is 1538
.
My callback method is called packetReceived
and looks like this:
void packetReceived(u_char* args, const struct pcap_pkthdr* header, const u_char* packet) {
struct pcap_work_item* item = malloc(sizeof(struct pcap_pkthdr) + header->caplen);
item->header = *header;
memcpy(item->data, packet, header->caplen);
threadpool_add(threadPool, handlePacket, item, 0);
}
I stuff all the properties for my packet in a new struct and launch a worker thread to analyze the packet and process the results. This is to not keep pcap waiting and is an attempt to fix this problem which already existed before adding this worker thread method.
The handlePacket
method is like this:
void handlePacket(void* args) {
const struct pcap_work_item* workItem = args;
const struct sniff_ethernet* ethernet = (struct sniff_ethernet*)(workItem->data);
u_int size_ip;
const struct sniff_ip* ip = (struct sniff_ip*)(workItem->data + SIZE_ETHERNET);
size_ip = IP_HL(ip) * 4;
if (size_ip < 20) {
return;
}
const u_int16_t type = ether_packet(&workItem->header, workItem->data);
switch (ntohs(type)) {
case ETHERTYPE_IP: {
char sourceIP[INET_ADDRSTRLEN];
char destIP[INET_ADDRSTRLEN];
inet_ntop(AF_INET, &ip->ip_src, sourceIP, sizeof(sourceIP));
inet_ntop(AF_INET, &ip->ip_dst, destIP, sizeof(destIP));
[refToSelf registerPacketTransferFromSource:sourceIP destinationIP:destIP packetLength:workItem->header.caplen packetType:ethernet->ether_type];
break;
}
case ETHERTYPE_IPV6: {
// handle v6
char sourceIP[INET6_ADDRSTRLEN];
char destIP[INET6_ADDRSTRLEN];
inet_ntop(AF_INET6, &ip->ip_src, sourceIP, sizeof(sourceIP));
inet_ntop(AF_INET6, &ip->ip_dst, destIP, sizeof(destIP));
[refToSelf registerPacketTransferFromSource:sourceIP destinationIP:destIP packetLength:workItem->header.caplen packetType:ethernet->ether_type];
break;
}
}
}
Based on the type of ethernet packet I try to figure out if it is an packet send using an IPv4 or IPv6 address. After that is determined I send some details to an objectiveC method (Source IP address, Destination IP address and packet length).
I cast the packet to the structs explained on the website of tcpdump (http://www.tcpdump.org/pcap.html).
The problem is that pcap either does not seem to keep up with the packets received/send. Either I am not sniffing all the packets or the packet lengths are wrong.
Does anyone have any pointers where I need to adjust my code to make pcap catch them all or where I have some sort of problem.
These methods are called from my objectiveC application and the refToSelf
is a reference to a objC class.
Edit: I am calling the initializeTrafficMonitor in a background thread, because the pcap_loop is blocking.