/* (C) Ionut Spirlea - ionuts@rdscv.ro */

#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include <sys/time.h>
#include <time.h>
#include <signal.h>
#include <stdarg.h>
#include <pcap.h>

#include "global.h"


char 			*pcap_filter;
time_t 			time_aaa;
T_PCAP_COUNTERS		counters;
T_PCAP_SPEEDS		speeds;
char			dev[D_MAX_DEV_LEN];
int			pspeed_promisc;
pcap_t			*handle;

void sigalrm_h(int);
void sigint_h(int);
int pspeed_get_params(int argc, char **argv);

void error(const char *fmt, ...)
{
	va_list ap;

	(void)fprintf(stderr, "p_speed: ");
	va_start(ap, fmt);
	(void)vfprintf(stderr, fmt, ap);
	va_end(ap);
	if (*fmt) {
		fmt += strlen(fmt);
		if (fmt[-1] != '\n')
			(void)fputc('\n', stderr);
	}
	exit(1);   
	/* NOTREACHED */
}

void update_speeds()
{
	static unsigned k = 0;
	static int last_bytes = 0;
	static int last_pkts = 0;
	
	speeds.pkts[k] = (float)(counters.pkts - last_pkts)   / 0.3333333;
	speeds.kbps[k] = (float)(counters.bytes - last_bytes) / 0.3333333;
	speeds.kbps[k] /= 128.0;
	
	k = (k +1 ) % 3;	
	last_bytes = counters.bytes;
	last_pkts = counters.pkts;
	 
}

void show_speed()
{
	struct tm *tm;
	time_t current_time;
	float pkts;
	float kbps;
	
	pkts = (speeds.pkts[0] + speeds.pkts[1] + speeds.pkts[2]) / 2.99999999999;
	kbps = (speeds.kbps[0] + speeds.kbps[1] + speeds.kbps[2]) / 2.99999999999;
	
	current_time=time(NULL);
	tm = localtime(&current_time);
	printf("%02d/%02d/%02d-%02d:%02d:%02d",
		tm->tm_year+1900, tm->tm_mon+1, tm->tm_mday,
		tm->tm_hour,tm->tm_min, tm->tm_sec);
	printf("\t %7.2f kbps \t @%6.0f pkts/sec\n", kbps, pkts);
}

int main(int argc, char *argv[])
{
	struct itimerval	v;
	char errbuf[PCAP_ERRBUF_SIZE];
	struct bpf_program filter;
	bpf_u_int32 mask;
	bpf_u_int32 net;
	struct pcap_pkthdr header;
	const u_char *packet;
	int i, len;

	if (argc < 2)
		error("bad argv\n Usage:\np_speed [-p] [-i interface] [pcap filter]\n\t(c) ionuts@rdscv.ro\n");
		
	pspeed_get_params(argc, argv);
		
	for(i = 1, len = 0; i<argc; len += strlen(argv[i]) + 1, i++);
	
	if((pcap_filter = (char *) malloc(len + 16)) == NULL)
		error("malloc error");

	memset(pcap_filter, 0, len);
	for(i = 1; i < argc; i++) 
	{
		if(strlen(argv[i]) != 0)
		{
			strcat(pcap_filter, argv[i]);
			strcat(pcap_filter, " ");
		}
	}
	
	signal(SIGINT, sigint_h);
	signal(SIGALRM, sigalrm_h);

	v.it_interval.tv_sec  = 0;
	v.it_interval.tv_usec = 333333;         /* 3 @ 3 */
	v.it_value.tv_sec     = 0;
	v.it_value.tv_usec    = 333333;
	setitimer(ITIMER_REAL, &v, NULL);
                                                
	time_aaa = time(NULL);
	
	memset(errbuf, 0, PCAP_ERRBUF_SIZE);
		
	pcap_lookupnet(dev, &net, &mask, errbuf);
	handle = pcap_open_live(dev, BUFSIZ, pspeed_promisc, 0, errbuf);
	if(handle == NULL)
		error("%s", errbuf);
	else if (*errbuf)
		fprintf(stderr, "%s", errbuf);
	
	if (pcap_compile(handle, &filter, pcap_filter, 0, net) < 0)
		error("%s", pcap_geterr(handle));

        if (pcap_setfilter(handle, &filter) < 0)
                        error("%s", pcap_geterr(handle));
                        	
	printf("Start logging for \"%s\"\n", pcap_filter);
	for( ; ;) {
		packet = pcap_next(handle, &header);
		counters.pkts++;
		counters.bytes += header.len;
	}
	
    pcap_close(handle);

    return(0);
}

void sigalrm_h(int sig)
{
	static int show = 1;
	
	update_speeds();
	if (show == 0)
		show_speed();

	show = (show + 1) % 3;
}

void sigint_h(int sig)
{
	time_t time_now, delta_t;
	                                
	time_now = time(NULL);
	delta_t = time_now - time_aaa;
	
	printf("\n--- %straffic statistics ---\n", pcap_filter);
	printf("%u packets, %u bytes, speed %.2f kbps @ %.0f packets/sec\n",
		counters.pkts, counters.bytes,
		(float)(counters.bytes) / (float) delta_t / 128.0,
		(float)(counters.pkts) / (float) delta_t);
	
	free(pcap_filter);
	
	pcap_close(handle);
	
	exit(0);
}

/**
 * Get device 
 */
int pspeed_get_params(int argc, char **argv)
{
	int i;
	
	pspeed_promisc = D_DEFAULT_PROMISC;
	strncpy(dev, D_DEFAULT_DEVICE, D_MAX_DEV_LEN);
	
	for(i = 0; i < argc; i++) {
		if((strncasecmp(argv[i], "-p", 2) == 0)) {
			pspeed_promisc = 1;
			strcpy(argv[i], "");
		}
		if(strncasecmp(argv[i], "-i", 2) == 0) { /* get device parameter */
			if(strcasecmp(argv[i], "-i") == 0) {
				strcpy(argv[i], "");
				/* -i dev */
				if(i == (argc - 1))
					return 0; 
				strncpy(dev, argv[i+1], D_MAX_DEV_LEN);
				strcpy(argv[i+1], "");
			} else { /* -idev */
				strncpy(dev, argv[i] + 2, D_MAX_DEV_LEN);
				strcpy(argv[i], "");
			}
		}
	}
	
	return 1;
	
} /* pspeed_get_params() */

