

/****************************************************************************/
/*                                                                          */
/*      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 
 *
 *     the Berkeley Packet Filter (BPF), 
 *                  Van Jacobson, Proprietor.
 *
 */

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

#define DATANEEDED (64+sizeof(union ip_data)+sizeof(struct ether_header))
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 */

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

initdevice(EtherType)
int EtherType;
    {
    char *bpf_dev = "/dev/bpf0";
    struct ifreq setifp;
    struct bpf_devp {
        u_short bdev_type;
        u_short bdev_hdrlen;
    }  devp;
    struct bpf_program filter_prog;
    
        /*  Open the device. */  
    while (if_fd < 0) {
        if_fd = open(bpf_dev, O_RDONLY);   
        if (if_fd == -1) {
            if (errno == EBUSY) {
                /* That special device is busy; try another ...*/
                *bpf_dev++; /* quick and dirty... */
                continue;
            }
            perror("BPF open error");
            exit(-1);
        }
    }
    
        /* Bind to interface */
    strcpy( setifp.ifr_name, EtherIface);   
    if (ioctl(if_fd, BIOCSETIF, &setifp) == -1) {
        perror("BPF BIOSETIF ioctl error");
        exit(-1);
    }
    
        /*  Get device parameters. */
    
    if (ioctl(if_fd, BIOCDEVP, &devp) == -1) {
        perror("BPF BIOCDEVP");
        exit(-1);
    }
    if (devp.bdev_type != DLT_EN10MB) {
        printf("NOT ETHERNET.");
        exit(-1);
    }
    
/*******
        /* Set to truncate... return just headers */ 
/*******
    if (ioctl(if_fd, BIOCTRUNCATE, &snaplen) < 0) {
        perror("BPF BIOCTRUNCATE ioctl");
        exit(-1);
    }
********/
    
        /* Set timeouit */
    if (ioctl(if_fd, BIOCSRTIMEOUT, &timeout) < 0) {
        perror("BPF BIOCSRTIMEOUT ioctl");
        exit(-1);
    }
    
        /* Get Buffer Size and allocate buffer dynamically */
    if (ioctl(if_fd, BIOCGBLEN, &chunksize) < 0) {
        perror("BPF BIOCGBLEN ioctl");
        exit(-1);
    }
    if ((buf = (u_char *) malloc(chunksize)) == NULL) {
        printf("MEMORY SHORTAGE: packet buffer");
        exit(-1);
    }  
    
   /********  Set the filter.     
        filter.enf_Priority = 3;
        filter.enf_FilterLen = 0;
        if (EtherType != -1) {
            filter.enf_Filter[filter.enf_FilterLen++] =
            ENF_PUSHWORD + 6;
            filter.enf_Filter[filter.enf_FilterLen++] =
            ENF_PUSHLIT | ENF_EQ;
            filter.enf_Filter[filter.enf_FilterLen++] =
            htons(EtherType);
        }
        if (ioctl(if_fd, BIOCSETF, &filter) == -1) {
            perror("BPF BIOCSETF ioctl");
            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()
    {
    register u_char *bp, *bufstop;
    register int readlen;
    register int captured;    
    register struct bpf_hdr {  /* BPF header on each packet */
               u_short bh_hdrlen;
               u_short bh_datalen;
               struct timeval bh_tstamp;
           } *bhp;

    if (if_fd < 0) return;
    
    readlen = read(if_fd, (caddr_t) buf, chunksize);
    if (readlen < 0) {
        lseek(if_fd, 0, 0);
        readlen = read(if_fd, (caddr_t) buf, chunksize);
        if (readlen < 0)
            perror("Packet read");
    }

    bp = buf;
    bufstop = buf + readlen;
    while (bp < bufstop) {
        bhp = (struct bpf_hdr *) bp;
        captured = (snaplen < bhp->bh_datalen)? snaplen:bhp->bh_datalen;
        
            /* Call parse() */
        parse(bp + bhp->bh_hdrlen, captured, bhp->bh_datalen, bhp->bh_tstamp);
        
        bp = (u_char *) bp + BPF_WORDALIGN(bhp->bh_hdrlen + captured);
    }
} /* 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 {
        u_int bs_recv;
        u_int bs_drop;
    }  stats;

    if (ioctl(if_fd, BIOCGSTATS, &stats) < 0) {
        printf("BIOCGSTATS error");
        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() */
