#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;
}