Monday, July 4, 2016

A simple libpcap example for live packet captures

libpcap is an extremely useful library for, among other things, capturing network packets in realtime. The library has a lot of options, so I put together a simple example of how to do a packet capture. Depending on your platform, libpcap may return packets in "chunks". In other words, the kernel may wait until a certain number of packets have been received, or a certain amount of time has elapsed, before notifying your application that data is available. In this example, since I want to be notified as soon as a packet is available, I attempt to turn off those behaviors using the pcap_set_timeout() and pcap_set_immediate_mode() APIs. These APIs may behave differently across various platforms.

#include <stdio.h>
#include <stdlib.h>
#include <pcap/pcap.h>
#include <net/ethernet.h>
pcap_t * CreatePcapForInterface( const char * interfaceName )
{
char errbuf[PCAP_ERRBUF_SIZE];
int success = 0;
// Create a packet capture handle for the specified interface
pcap_t * pcap = pcap_create( interfaceName, errbuf );
if( pcap == NULL )
{
fprintf( stderr, "Unable to create pcap for interface %s (%s).\n", interfaceName, errbuf );
goto exit;
}
// Deliver packets as soon as they arrive. See the pcap man page for more info.
if( pcap_set_timeout( pcap, 1 ) != 0 )
{
fprintf( stderr, "Unable to configure timeout.\n" );
goto exit;
}
// When immediate mode is enabled, reads return immediately upon packet reception.
// Otherwise, a read will block until either the kernel buffer becomes full or a timeout occurs.
if( pcap_set_immediate_mode( pcap, 1 ) != 0 )
{
fprintf( stderr, "Unable to configure immediate mode.\n" );
goto exit;
}
// Activate packet capture handle to look at packets on the network
int activateStatus = pcap_activate( pcap );
if( activateStatus < 0 )
{
pcap_perror( pcap, "Activate failed" );
goto exit;
}
// Set ethernet link-layer header type
if( pcap_set_datalink( pcap, DLT_EN10MB ) )
{
pcap_perror( pcap, "Set datalink failed" );
goto exit;
}
success = 1;
exit:
if( success == 0 )
{
if( pcap )
{
pcap_close( pcap );
pcap = NULL;
}
}
return pcap;
}
void MonitorPcap( pcap_t * pcap )
{
int pcapFD = pcap_get_selectable_fd( pcap );
if( pcapFD < 0 )
return;
fd_set allFileDescriptorSet;
FD_ZERO( &allFileDescriptorSet );
FD_SET( pcapFD, &allFileDescriptorSet );
for( ;; )
{
fd_set readFileDescriptorSet = allFileDescriptorSet;
int readyCount = select( pcapFD + 1, &readFileDescriptorSet, NULL, NULL, NULL );
if( readyCount < 0 )
break;
if( FD_ISSET( pcapFD, &readFileDescriptorSet ) )
{
struct pcap_pkthdr * pcapHeader;
const u_char * packetPtr;
int packetCount = pcap_next_ex( pcap, &pcapHeader, &packetPtr );
if( packetCount < 0 )
break;
printf( "Got %u byte packet:\n", pcapHeader->caplen );
if( pcapHeader->caplen >= sizeof( struct ether_header ) )
{
struct ether_header * eh = (struct ether_header *)packetPtr;
printf( "\tdst=%02x:%02x:%02x:%02x,%02x:%02x src=%02x:%02x:%02x:%02x,%02x:%02x, type=0x%04x\n",
eh->ether_dhost[0], eh->ether_dhost[1], eh->ether_dhost[2],
eh->ether_dhost[3], eh->ether_dhost[4], eh->ether_dhost[5],
eh->ether_shost[0], eh->ether_shost[1], eh->ether_shost[2],
eh->ether_shost[3], eh->ether_shost[4], eh->ether_shost[5],
ntohs( eh->ether_type ) );
}
}
}
}
int main()
{
pcap_t * pcap = CreatePcapForInterface( "en0" );
if( !pcap )
exit( EXIT_FAILURE );
// This could be done on a separate thread, or as a part
// of general file descriptor monitoring.
MonitorPcap( pcap );
return 0;
}

No comments:

Post a Comment