

/****************************************************************************/
/*                                                                          */
/*      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.                                                            */
/*                                                                          */
/****************************************************************************/

/*
 * Copyright (c) 1993, 1994
 *	The Regents of the University of California.  All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that: (1) source code distributions
 * retain the above copyright notice and this paragraph in its entirety, (2)
 * distributions including binary code include the above copyright notice and
 * this paragraph in its entirety in the documentation or other materials
 * provided with the distribution, and (3) all advertising materials mentioning
 * features or use of this software display the following acknowledgement:
 * ``This product includes software developed by the University of California,
 * Lawrence Berkeley Laboratory and its contributors.'' Neither the name of
 * the University nor the names of its contributors may 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
 *
 *     the BSD Packet Filter (BPF)
 *                  of
 *     Steve McCanne and Van Jacobsen
 *
 *  Changes:
 *  --------
 *  21Nov94 - Denis DeLaRoca <delaroca@oac.ucla.edu>
 *
 *	Rewrite for BPF v1.1 using code from the libpcap
 *      distribution. LBL's preferred interface for BPF
 *      (as well as other network tap interfaces) is thru
 *      pcap. At some point it might be desirable to migra-
 *	te NNStat to use the libpcap interface.
 *
 *	For systems that don't support kernel malloc, such as SunOS,
 *	BPF allocates its read buffers no bigger than the system's
 *	cluster mbufs (typically 1024 bytes). If this buffering is
 *	not sufficient, it is possible to install BPF with -DBPFPROF
 *	to make it use 16KB buffers (or other size you care to define).
 *
 *	I set DATANEEDED to 68, that should suffice for analyzing IP,
 *	ICMP, TCP and UDP. Previously it was being defined at 104.
 */

#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 <net/bpf.h>
#include "stat.h"
#include "packet.h"

/*
#define DATANEEDED (64+sizeof(union ip_data)+sizeof(struct ether_header))
*/
#define DATANEEDED 68
u_int snaplen = DATANEEDED;  /* alignment handled by packet filter driver */
struct timeval timeout = {1, 0};
u_int chunksize;

u_char *buf;

extern int errno;
int if_fd = -1;
extern char *EtherIface;

static  u_long  TotPkts = 0;        /* can't oflow for 79 hrs on ether */
static  u_long  TotDrops = 0;       /* count of dropped packets */
static  long    TotMissed = 0;      /* missed by i/f during this run */
static  long    OrigMissed = -1;    /* missed by i/f before this run */

/*
 * Not all systems have strerror()
 */
static char *
pcap_strerror(errnum)
int errnum;
{
    extern int sys_nerr;
    extern char *sys_errlist[];
    static char ebuf[20];

    if ((unsigned int)errnum < sys_nerr)
	return(sys_errlist[errnum]);

    (void)sprintf(ebuf, "Unknown error: %d", errnum);
    return(ebuf);
}

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

initdevice(EtherType)
int EtherType;
{
   int n = 0;
   char device[sizeof "/dev/bpf0"];
   struct ifreq ifr;
   struct bpf_version bv;
   int l;
   struct bpf_program pgm;
   static struct bpf_insn insns[] = {
	BPF_STMT(BPF_RET+BPF_K, DATANEEDED),
   };

   /* Open Packet Filter Device */
   do {
	sprintf(device, "/dev/bpf%d", n++);
	if_fd = open(device, O_RDONLY);
   } while(if_fd < 0 && errno == EBUSY);

   if (if_fd < 0)
	fprintf(stderr, "open %s: %s\n", device, pcap_strerror(errno));

   /* Get Packet Filter Version */
   if (ioctl(if_fd, BIOCVERSION, (caddr_t)&bv) < 0) {
	fprintf(stderr, "ioctl BIOCVERSION: %s\n",
	                                    pcap_strerror(errno));
	exit(-1);
   }

   /* Bind to Interface */
   strcpy(ifr.ifr_name, EtherIface);
   if (ioctl(if_fd, BIOCSETIF, (caddr_t)&ifr) < 0) {
	fprintf(stderr, "ioctl BIOCSETIF: %s\n",
					    pcap_strerror(errno));
	exit(-1);
   }

   /* Get the data Link Layer Type */
   if (ioctl(if_fd, BIOCGDLT, (caddr_t)&l) < 0) {
	fprintf(stderr, "ioctl BIOCGDLT: %s\n", pcap_strerror(errno));
	exit(-1);
   }
   if (l != DLT_EN10MB) {
	fprintf(stderr, "Data Link Layer Type not Ethernet\n");
	exit(-1);
   }

   /* Set Timeout */
   if (ioctl(if_fd, BIOCSRTIMEOUT, (caddr_t)&timeout) < 0) {

	fprintf(stderr, "ioctl BIOCSRTIMEOUT: %s\n",
						pcap_strerror(errno));
	exit(-1);
   }

   /* Set Promiscuous Mode */
   if (ioctl(if_fd, BIOCPROMISC, NULL) < 0) {
	fprintf(stderr, "ioctl BIOCPROMISC: %s\n",
						pcap_strerror(errno));
	exit(-1);
   }

   /* Get Buffer Size */
   if (ioctl(if_fd, BIOCGBLEN, (caddr_t)&chunksize) < 0) {
	fprintf(stderr, "ioctl BIOCGBLEN: %s\n", pcap_strerror(errno));
	exit(-1);
   }

   /* Set Packet Filter */
   pgm.bf_len = sizeof(insns) / sizeof(struct bpf_insn);
   pgm.bf_insns = insns;
   if (ioctl(if_fd, BIOCSETF, (caddr_t)&pgm) < 0) {
	fprintf(stderr, "ioctl BIOCSETF: %s\n", pcap_strerror(errno));
	exit(-1);
   }

   /* Allocate Buffer */
   if (NULL == (buf = (u_char *)malloc(chunksize))) {
	fprintf(stderr, "malloc: %s\n", pcap_strerror(errno));
	exit(-1);
   }

} /* initdevice() */


deinitdevice()
{
    close(if_fd);
    if_fd = -1;
}

/*
 *       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()
{
	int cc;
	register u_char *bp, *ep;

 again:
	cc = read(if_fd, (char *)buf, chunksize);
	if (cc < 0) {
		/* Don't choke when we get ptraced */
		switch (errno) {

		case EINTR:
			goto again;

#if defined(sun) && !defined(BSD)
		/*
		 * Due to a SunOS bug, after 2^31 bytes, the kernel
		 * file offset overflows and read fails with EINVAL.
		 * The lseek() to 0 will fix things.
		 */
		case EINVAL:
			if ((long)(tell(if_fd) + chunksize) < 0) {
				(void)lseek(if_fd, 0, 0);
				goto again;
			}
			/* fall through */
#endif
		}
		fprintf(stderr, "read: %s\n", pcap_strerror(errno));
		exit (-1);
	}
		bp = buf;

	/*
	 * Loop through each packet.
	 */
#define bhp ((struct bpf_hdr *)bp)
	ep = bp + cc;
	while (bp < ep) {
		register int caplen, hdrlen;
		caplen = bhp->bh_caplen;
		hdrlen = bhp->bh_hdrlen;
		
		parse(bp + hdrlen, caplen, bhp->bh_datalen,
							bhp->bh_tstamp);
		bp += BPF_WORDALIGN(caplen + hdrlen);
	}
#undef bhp
} /* 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 bpf_stat stats;

    if (if_fd < 0) {
	fprintf(stderr, "Network tap not yet initialized: \
attach command-file!\n");
	return(FALSE);
    }

    if (ioctl(if_fd, BIOCGSTATS, (caddr_t)&stats) < 0) {
        fprintf(stderr, "ioctl BIOCGSTATS: %s\n",
						pcap_strerror(errno));
        return(FALSE);
    };

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

} /* if_stats() */
