/****************************************************************************/
/*                                                                          */
/*      NNstat -- Internet Statistics Collection Package                    */
/*      April 1991                                                          */
/*                                                                          */
/*            Written by: Bob Braden & Annette DeSchon                      */
/*            USC Information Sciences Institute                            */
/*            Marina del Rey, California                                    */
/*                                                                          */
/*      Copyright (c) 1991 University of Southern California.               */
/*      All rights reserved.                                                */
/*                                                                          */
/*      Redistribution and use in source and binary forms are permitted     */
/*      provided that the above copyright notice and this paragraph are     */
/*      duplicated in all such forms and that any documentation,            */
/*      advertising materials, and other materials related to such          */
/*      distribution and use acknowledge that the software was              */
/*      developed by the University of Southern California, Information     */
/*      Sciences Institute.  The name of the University may not be used     */
/*      to endorse or promote products derived from this software           */
/*      without specific prior written permission.                          */
/*      THIS SOFTWARE IS PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR        */
/*      IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED      */
/*      WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR          */
/*      PURPOSE.                                                            */
/*                                                                          */
/****************************************************************************/

/*  Promiscuous Ethernet interface for statspy -- using
 *
 *     Lawrence Berkeley Labs Packet Capture Library (libpcap).
 *
 *  Changes:
 *  --------
 *  26Nov94 - Denis DeLaRoca <delaroca@oac.ucla.edu>
 *
 *
 */

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

#include <pcap.h>
#include "stat.h"
#include "packet.h"

#ifndef notdef
#define DATANEEDED 68	/* suffices for IP, ICMP, TCP and UDP analysis */
#else
#define DATANEEDED (64+sizeof(union ip_data)+sizeof(struct ether_header))
#endif
#define TIMEOUT 1000	/* 1.0 seconds */
#define PROMISC 1	/* set promiscuous mode */

static char errbuf[PCAP_ERRBUF_SIZE];
static pcap_t *p = NULL;
static struct bpf_program pgm;
static u_long net, netmask;
extern char *EtherIface;
int if_fd = -1;
u_int snaplen = DATANEEDED;
extern void parse(u_char *, int, int, struct timeval);


/*
 *  Open packet filter and initialize it.
 *
 *  The parameter passed selects a particular ethernet type field.
 *
 */

initdevice(EtherType)
int EtherType;
{
    /* XXX What about initdevice(0)? Such a call is made by */
    /* statspy when processing the -f <tracefile> option.   */

    /* Establish Packet Capture Filter */
    if (NULL == (p = pcap_open_live(EtherIface, snaplen,
					PROMISC, TIMEOUT, errbuf))) {
    	fprintf(stderr, "pcap_open_live: %s\n", errbuf);
    	exit(-1);
    }
	
   /* XXX Check Device Type (maybe not necessary) */
   if (pcap_datalink(p) != DLT_EN10MB) {
	fprintf(stderr, "NOT ETHERNET.\n");
	deinitdevice();
	exit(-1);
   }
	
   /* Get Net and Netmask */
   if (pcap_lookupnet(EtherIface, &net, &netmask, errbuf) < 0) {
	fprintf(stderr, "pcap_lookupnet: %s\n", errbuf);
	exit(-1);
   }

   /* Set Filter */
   errbuf[0] = '\0';
   if (EtherType > 0)
	sprintf(errbuf, "ether[12:2] = %d", EtherType);
   pcap_compile(p, &pgm, errbuf, 1, netmask);
   pcap_setfilter(p, &pgm);

   /* Get pcap's fileno */
   if_fd = pcap_fileno(p);

} /* initdevice */


deinitdevice()
{
    /* XXX Pcap gets upset when passed a null (pcap_t *) */
    if (NULL != p) {
	pcap_close(p);
    	p = NULL;
    	if_fd = -1;
    }

} /* deinitdevice */


void
process(user, p, d)
    u_char *user;
    struct pcap_pkthdr *p;
    u_char *d;
{
    parse(d, p->caplen, p->len, p->ts);
}


/*
 *       readether()
 *
 *  Read one block of packets from the filter (or none, if the read times out)
 *       and call parse() to process all of them.
 *
 */
readether()
{
    if (pcap_dispatch(p, -1, process, NULL) < 0) {
	fprintf(stderr, "pcap_dispatch: %s\n", pcap_geterr(p));
	fprintf(stderr, "closing pcap!\n");
	deinitdevice();
    }
} /* readether() */


/*
 * returns non-zero and catenates message onto *outp
 *       IFF something has been dropped or missed
 *  Doesn't reference "outp" if it returns 0.
 */
boolean if_stats(outp)
char *outp;
    {
    struct pcap_stat ps;

    if (NULL == p) {
	fprintf(stderr, "Network tap not yet initialized: do attach!\n");
	return(FALSE);
    }

    if (pcap_stats(p, &ps) < 0) {
	fprintf(stderr, "pcap_stats: %s\n", pcap_geterr(p));
	return(FALSE);
    }

    if (ps.ps_drop) {
        (void)sprintf(outp, "%d packets", ps.ps_recv);
        outp += strlen(outp);
        (void)sprintf(outp, " + %d discarded by kernel\n", ps.ps_drop);
        return(TRUE);
    }
    return(FALSE);

} /* if_stats() */
