/*
 * $Id: pkthist.c,v 1.1 2000/08/24 04:50:18 wessels Exp $
 *
 * outputs the mean and median size of packets sampled over
 * some time period.
 */

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <strings.h>
#include <pcap.h>
#include <assert.h>
#include <errno.h>
#include <fcntl.h>
#include <sys/signal.h>
#include <sys/wait.h>

#include <sys/socket.h>

#include <net/if.h>
#include <netinet/in.h>
#include <netinet/if_ether.h>
#include <netinet/in_systm.h>
#include <netinet/ip.h>
#include <netinet/ip_var.h>
#include <netinet/udp.h>
#include <netinet/udp_var.h>
#include <netinet/tcp.h>
#include <netinet/tcpip.h>

#include <arpa/inet.h>


#define SNAPLEN 68
#define MAX_PKTSZ 5120

static unsigned int pktcnt[MAX_PKTSZ];
static unsigned int sum_size = 0;
static unsigned int npkts = 0;
static time_t now = 0;

void
packet_handle(unsigned char *user, const struct pcap_pkthdr * h, const unsigned char *p)
{
    now = h->ts.tv_sec;
    if (h->len >= MAX_PKTSZ) {
	fprintf(stderr, "big packet: %d octets\n", (int) h->len);
	return;
    }
    pktcnt[h->len]++;
    sum_size += h->len;
    npkts++;
}

static unsigned int
histogram_nth(unsigned int n)
{
    unsigned int i;
    unsigned int sum = 0;
    for (i = 0; i < MAX_PKTSZ; i++) {
	if (sum + pktcnt[i] >= n)
	    break;
	sum += pktcnt[i];
    }
    return i;
}

static double
median_size(void)
{
    unsigned int half = npkts >> 1;
    if (1 == npkts % 2)
	/* even */
	return (double) histogram_nth(half + 1);
    else
	return (double) ((histogram_nth(half) + histogram_nth(half + 1)) / 2);
}

int
main(int argc, char *argv[])
{
    struct bpf_program bpf;
    pcap_t *pc;
    char errmsg[PCAP_ERRBUF_SIZE];
    char *dev;
    unsigned int netmask;
    unsigned int localnet;
    struct timeval start;
    time_t sampletime;
    if (3 != argc) {
	fprintf(stderr, "usage: %s device sampletime\n", argv[0]);
	return 1;
    }
    dev = strdup(argv[1]);
    sampletime = (time_t) atoi(argv[2]);
    memset(&pktcnt, '\0', sizeof(pktcnt));
    pc = pcap_open_live(dev, SNAPLEN, 0, 1000, errmsg);
    if (NULL == pc) {
	fprintf(stderr, "%s: %s\n", dev, errmsg);
	return 1;
    }
    if (pcap_lookupnet(dev, &localnet, &netmask, errmsg) < 0) {
	localnet = 0;
	netmask = 0;
	fprintf(stderr, "%s\n", errmsg);
    }
    setuid(getuid());
    pcap_compile(pc, &bpf, 0, 1, netmask);
    if (pcap_setfilter(pc, &bpf) < 0) {
	fprintf(stderr, "%s\n", pcap_geterr(pc));
	return 1;
    }
    gettimeofday(&start, NULL);
    now = start.tv_sec;
    while (1) {
	if (pcap_dispatch(pc, 0, packet_handle, NULL) < 0) {
	    fprintf(stderr, "pcap_dispatch: %s\n", pcap_geterr(pc));
	    break;
	}
	if (now - start.tv_sec > sampletime)
	    break;
    }
    pcap_close(pc);
    printf("packets-sampled: %d\n", npkts);
    printf("mean-size: %f\n", (double) sum_size / (double) npkts);
    printf("median-size: %f\n", median_size());
#if 0
    /* cheat use netmask for array index */
    for (netmask = 0; netmask < MAX_PKTSZ; netmask++) {
	if (0 == pktcnt[netmask])
	    continue;
	printf("%d %d\n", netmask, pktcnt[netmask]);
    }
#endif
    return 0;
}

