//////////////////////////////////////////////////////////////////////
// PPPTraf 1.0. (C) DiSKiLLeR, 9/2/2000. (diskiller@cnbinc.com)     //
//                                                                  //
// This program is distributed under the terms of the GNU License.  //
// Please refer to the file README and COPYING.                     //
//////////////////////////////////////////////////////////////////////

#include <stdio.h>
#include <fcntl.h>
#include <errno.h>
#include <sys/types.h>
#include <sys/time.h>
#include <sys/ioctl.h>
#include <net/bpf.h>
#include <sys/socket.h>
#include <net/if.h>
#include <netinet/in.h>
#include <pthread.h>

#include "bpf.h"
#include "data.h"


static int bpf_fd;
static int bpf_buffsize;
static int bpf_initted=0;	// should be mutexed
static int bpf_downstate=0;	// needs to be mutexed
static pthread_mutex_t mut;


struct ip_hdr	// My IP Packet header
{
	short crap_a;
	short length;
	short ident;
	short crap_b;
	short crap_c;
	short checksum;
	long src_ip;
	long dest_ip;
};


struct packet	// First 44 interesting bytes
{
	struct bpf_hdr bpf_hdr;		// BPF header
	long ppp_hdr;			// PPP header
	struct ip_hdr ip_hdr;		// IP header
					// TCP header ...
};


// Helper functions
int bpf_open();
void bpf_process(struct packet);


#define bhp ((struct bpf_hdr *)bp)	// from TrafShow 2.0
					// Used by trafshow's algorithm.


int bpf_init(char *interface)
{
	struct ifreq ifreq;
	int r;
	unsigned int dummy;

	pthread_mutex_lock(&mut);

	if(bpf_initted != 0)
	{
		pthread_mutex_unlock(&mut);
		return -1;
	}

	/** Get a bpf **/
	bpf_fd = bpf_open();


	/** Set kernel buffer size for 44 bytes. **/
	//dummy = 44;

	/* Try and set the kernel buffer to something big.
	 * It'll end up being 32k (seems to be the max it allows)
	 * but its worth a try anyway ;)
	 */
	dummy = 1024*40;
	r = ioctl(bpf_fd, BIOCSBLEN, &dummy);
	if(r == -1)
		printf("[BIOCSBLEN] ret = %d\n", r);


	/** Select interface to listen to (tun0) **/
	strcpy(ifreq.ifr_name, interface);
	r = ioctl(bpf_fd, BIOCSETIF, &ifreq);
	if(r == -1)
		printf("[BIOCSETIF] ret = %d\n", r);


	/* This isn't needed, because we're using threads at the moment.
	 * If this is ever changed, we may need quicker response time (for
	 * the ncurses display).
	 */
	/** Set immediate mode **/
	//dummy = 1;
	//r = ioctl(bpf_fd, BIOCIMMEDIATE, &dummy);
	//if(r == -1)
	//	printf("[BIOCIMMEDIATE] ret = %d\n", r);


	/* This is interesting, because the FBSD kernel lets us set it for
	 * tun0 (and probably ppp) devices. But it doesn't make 'much' sense.
	 * It wouldn't make much sense on ethernet devices either. (think
	 * about what this program does/is for ...)
	 */
	/** Set promiscuous mode **/
	//r = ioctl(bpf_fd, BIOCPROMISC);
	//if(r == -1)
	//	printf("[BIOCPROMISC] ret = %d\n", r);



	/** Get buffer size **/
	r = ioctl(bpf_fd, BIOCGBLEN, &bpf_buffsize);
	if(r == -1)
		printf("[BIOCGBLEN] ret = %d\n", r);

	bpf_initted = 1;

	pthread_mutex_unlock(&mut);
	return 0;
}


bpf_loop()
{
	int r;
	struct packet pkt;
	char *buff=0;
	char *bp=0;
	int hdrlen, caplen, bplen=0;


	/** We haven't been init'd. **/
	if(bpf_initted == 0)
	{
		return;
	}

	/** malloc some storage for our buffer **/
	buff = (char *)malloc(bpf_buffsize+4);

	if(buff == NULL)
	{
		/** oops **/
		fprintf(stderr, "bpf_loop: malloc() failed to allocate %d bytes.\n", bpf_buffsize+4);
		exit(1);
	}


	/** Main work loop **/
	for(;;)
	{
		/** Check to see if we're asked to die. **/
		if(bpf_downstate == 1)
		{
			pthread_mutex_lock(&mut);

			/** Do shutdown stuff **/
			close(bpf_fd);
			free(buff);
			bpf_downstate = 2;
			pthread_mutex_unlock(&mut);
			pthread_exit(0);	//die
		}

		// clear out packet	// i don't think we really need this
		//memset(buff, '\0', bpf_buffsize);

		/** read in data from kernel (fetch a packet to process) **/
		r = read(bpf_fd, buff, bpf_buffsize);

		/** Read no data? Lets switch context. Next time we're **/
		/** scheduled, we'll try to read() again.              **/
		if(r == 0)
		{
			sleep(1);
			//usleep(1000000);
			continue;
		}

		/* This algorithm is responsible for the occasional coredump.
		 * Its extremely rare, and i don't know why it happens. If
		 * you find the cause, let me know.
		 */
		bplen = 0;
		/** Copied from trafshow. Nice algorithm :) **/
		do
		{
			bp = buff + bplen;
			hdrlen = (int)bhp->bh_hdrlen;
			caplen = (int)bhp->bh_caplen;
			bplen += BPF_WORDALIGN(caplen + hdrlen);

			if(bplen>r)	// processed!
				break;

			/** inefficient, i know, i know **/
			memcpy(&pkt, bp, sizeof(pkt));
			bpf_process(pkt);
		}
		while(bplen < r);
	}
}


int bpf_dropped()
{
	struct bpf_stat stat;

	if(bpf_initted == 0)
		return -1;

	ioctl(bpf_fd, BIOCGSTATS, &stat);

	return stat.bs_drop;
}


int bpf_shutdown()
{
	if(bpf_initted == 0)
		return 0;

	/** set downstate to 1. This 'asks' the bpf_loop thread to exit. **/
	pthread_mutex_lock(&mut);
	bpf_downstate = 1;
	pthread_mutex_unlock(&mut);

	/** Wait for confirmation that bpf_loop thread is dead. **/
	while(1)
	{
		pthread_mutex_lock(&mut);
		if(bpf_downstate == 2)
			break;
		pthread_mutex_unlock(&mut);

		usleep(1000);
	}

	pthread_mutex_unlock(&mut);

	return 0;
}


int bpf_open()
{
	int fd;
	int n;
	char device[20];

	for(n=0 ; n<16 ; n++)
	{
		/** Open /dev/bpf[0..15] **/
		sprintf(device, "/dev/bpf%d", n);

		printf("Trying %s .... ", device);

		fd = open(device, O_RDONLY);

		/** Serious Error (not EBUSY) **/
		if(fd == -1 && errno != EBUSY)
		{
			printf("fatal error.\n");
			break;
		}

		/** Successful open **/
		if(fd != -1)
		{
			printf("ok.\n");
			break;
		}

		/** Its busy. Next bpf **/
		printf("busy.\n");
	}

	return( fd );
}


void bpf_process(struct packet pkt)
{
	struct in_addr in_addr;
	char src[20];
	char dest[20];
	short length;

	/** read out source IP **/
	in_addr.s_addr = pkt.ip_hdr.src_ip;
	strcpy(src, (char *)inet_ntoa(in_addr.s_addr));

	/** read out destination IP **/
	in_addr.s_addr = pkt.ip_hdr.dest_ip;
	strcpy(dest, (char *)inet_ntoa(in_addr.s_addr));

	/** read out the packet length **/
	length = ntohs(pkt.ip_hdr.length);

	/** Store the data **/
	data_store(src, dest, length);
}


syntax highlighted by Code2HTML, v. 0.9.1