/*
 * traftop:  libpcap interface
 *
 * kolya@mit.edu, Oct 2001
 */

#include <stdio.h>
#include <stdlib.h>
#include <inttypes.h>
#include <sys/types.h>
#include <sys/time.h>
#include <netinet/in.h>
#include <netinet/in_systm.h>
#include <netinet/ip.h>
#include <pcap.h>

#include "traftop.h"

static void
print_packet(int length, char *msg, void *v_addr, int addr_len)
{
    u_char *addr = (char *)v_addr;
    int i;

    printf("BYTES %d %s ", length, msg);
    for (i=0; i<addr_len; i++) {
	if (i>0) printf(":");
	printf("%d", addr[i]);
    }
    printf("\n");
}

static void
ether_cb(u_char *user, const struct pcap_pkthdr *h, const u_char *p)
{
    u_int caplen = h->caplen;
    u_int length = h->len;
    u_int plen = caplen;
    struct ether_header *ep;
    u_short ether_type;

    if (plen < ETHER_HDRLEN) {
	fprintf(stderr, "Warning: packet too short, %d bytes.\n", caplen);
	return;
    }

    ep = (struct ether_header *)p;
    p += ETHER_HDRLEN;
    plen -= ETHER_HDRLEN;
    ether_type = ntohs(ep->ether_type);

    print_packet(length, "ETH-SRC", ep->ether_shost, ETHER_ADDR_LEN);
    print_packet(length, "ETH-DST", ep->ether_dhost, ETHER_ADDR_LEN);

    if (ether_type == 0x0800) {
	struct ip *ip;

	if (plen < IP_HDRLEN) {
	    fprintf(stderr, "Warning: IP packet caplen %d too short\n", caplen);
	    return;
	}

	ip = (struct ip *)p;
	p += 4 * ip->ip_hl;
	print_packet(length, "IP-SRC", &ip->ip_src.s_addr, IP_ADDR_LEN);
	print_packet(length, "IP-DST", &ip->ip_dst.s_addr, IP_ADDR_LEN);
    }
}

int
main(int argc, char *argv[])
{
    pcap_t *pd;
    char *device;
    char ebuf[PCAP_ERRBUF_SIZE];
    int snaplen = 60, cnt;
    pcap_handler callback;
    struct timeval start_time, end_time;
    int dt_usec;
    struct pcap_stat stat;

    if (argc < 2) {
	cnt = 10000;
    } else {
	cnt = atoi(argv[1]);
    }

    *ebuf = '\0';

    device = pcap_lookupdev(ebuf);
    if (!device) {
	fprintf(stderr, "Error looking up device: %s\n", ebuf);
	exit(-1);
    }

    pd = pcap_open_live(device, snaplen, 1 /* PROMISC */, 1000, ebuf);
    if (!pd) {
	fprintf(stderr, "Error opening device: %s\n", ebuf);
	exit(-1);
    }
    if (*ebuf) {
	fprintf(stderr, "Warning: %s\n", ebuf);
    }

    gettimeofday(&start_time, NULL);
    callback = ether_cb;
    if (pcap_loop(pd, cnt, callback, 0) < 0) {
	fprintf(stderr, "Error from pcap_loop: %s\n", pcap_geterr(pd));
	exit(-1);
    }
    gettimeofday(&end_time, NULL);

    dt_usec = end_time.tv_sec - start_time.tv_sec;
    dt_usec *= 1000 * 1000;
    dt_usec += end_time.tv_usec - start_time.tv_usec;

    if (pcap_stats(pd, &stat) < 0) {
	fprintf(stderr, "pcap_stats: %s\n", pcap_geterr(pd));
    } else {
	if (stat.ps_recv != cnt)
	    fprintf(stderr, "pcap_stats: expecting %d pkts, got %d\n",
		    cnt, stat.ps_recv);
	if (stat.ps_drop)
	    fprintf(stderr, "pcap_stats: dropped %d pkts\n",
		    stat.ps_drop);
    }

    pcap_close(pd);
    printf("TIMEDIFF %d\n", dt_usec);
    exit(0);
}
