/* nstat - network statistics collector
 *
 * nstat [-i interface][-t time period (sec)]
 */

#ifdef SOLARIS
main()
{
     printf("Not ported to Solaris yet...\n");
}
#endif

#ifdef SUNOS4
#include	<stdio.h>
#include	<signal.h>
#include  	<fcntl.h>
#include	<malloc.h>
#include	<errno.h>
#include	<time.h>

#include	<sys/file.h>
#include	<sys/ioctl.h>
#include	<sys/types.h>
#include	<sys/time.h>
#include	<sys/socket.h>
#include  	<sys/stropts.h>
#include  	<net/if.h>
#include	<net/nit.h>
#include  	<net/nit_if.h>
#include  	<net/nit_buf.h>
#include  	<netinet/in.h>
#include	<netinet/if_ether.h>

#define	MAX_CHUNK	1024*100	/* maximum chunk size returned */
#define	TIMEOUT		2	/* chunk timeout in seconds */
#define	MAX_DATA_LEN	60
#define	PKT_TIMESTAMPS	0x1  /* packet headers contain timestamps */
#define	PKT_HEADERS	0x2  /* packets contain network-layer headers */
#define DEBUG 		0

struct nit_packet {       
    struct nit_bufhdr        header;
    u_long                   nit_if_drops,
                             nit_if_origlen;
    struct pkt {	/* simplified - ignores option fields */
    	/* ethernet */
    	u_char          edst[6];
    	u_char          esrc[6];
   	u_short         etype;
    	/* IP */
    	u_char          vl;
    	u_char          tos;
    	u_short         tlen;
    	u_short         ident;
    	u_short         foff;
    	u_char          time;
    	u_char          proto;
    	u_short         ipcksum;
    	u_char          isrc[4];/* can't use long, for alignment - sigh */
    	u_char          idst[4];/* can't use long, for alignment - sigh */
    	/* tcp */
    	u_short         tsrc;
    	u_short         tdst;
    	u_char          seq[4]; /* can't use long, for alignment - sigh */
    	u_char          ack[4];	/* can't use long, for alignment - sigh */
    	u_char          ofr;
    	u_char          code;
    	u_short         window;
    	u_short         tcpcksum;
    	u_short         urg;
    	u_char            data[1];
    } pkt;
};

u_char		*buffer;
u_long		*etypep, *udpportp, *tcpportp;
u_long		*etypeb, *udpportb, *tcpportb;
u_long  	*ipprotop, *ipprotob;
u_long		ipoptions = 0;
u_long		total = 0;
u_long		totalb = 0;
u_long		n802 = 0;
u_long		runt = 0;
u_long		dropped = 0;
u_long		cur_drop = 0;
int		nit;
int		promiscuous = 1;
char 		interface[10] = "le0";
int  		set_time = 3600;
struct timeval	starttimer, stoptimer;
int		debug = DEBUG;

void	init();
void	cleanup ();
void    report();
void	collect ();
void	setoptions ();
void	usage ();
void 	dump_pkt();

/*  Main routine setup everything, then call collect  */
main (argc,argv) 
int argc;
char **argv;
{
	setoptions(argc,argv);
	init();
	nit = insert_tap(interface);
	signal(SIGINT,cleanup);
	collect();     /* does not return */
}

void	setoptions (argc,argv)
int argc;
char **argv;
{
	if(argc==2)
		usage("argc = 2");
	while(argc>2) {
		if(argv[argc-2][0]!='-')
			usage("argv missing -");
		switch(argv[argc-2][1]){
			case 'i': {strcpy(interface,argv[argc-1]);
				   break;}
			case 't': {set_time = atoi(argv[argc-1]);
				   break;}
			default : {usage("bad switch");}
		}
		argc -= 2;
	}
}

void	usage (e)
char *e;
{
  fprintf(stderr,"%s\n",e);
  fputs("Usage: nstat \n",stderr);
  fputs("             [-i interface]\t(default \"le0\")\n",stderr);
  fputs("             [-t set_secs]\t(default 3600)\n",stderr);
  exit (1);
}

void init()
{
	buffer = (u_char *)calloc((unsigned)(MAX_CHUNK), sizeof(char));
	etypep = (u_long *)calloc((unsigned)65536,sizeof(u_long));	
	etypeb = (u_long *)calloc((unsigned)65536,sizeof(u_long));	
	tcpportp = (u_long *)calloc((unsigned)65536,sizeof(u_long));	
	tcpportb = (u_long *)calloc((unsigned)65536,sizeof(u_long));	
	udpportp = (u_long *)calloc((unsigned)65536,sizeof(u_long));	
	udpportb = (u_long *)calloc((unsigned)65536,sizeof(u_long));
	ipprotop = (u_long *)calloc((unsigned)256,sizeof(u_long));	
	ipprotob = (u_long *)calloc((unsigned)256,sizeof(u_long));	
}

/*  Insert tap - setup NIT socket */
int     insert_tap (if_name)
char    *if_name;
{
    int                 if_fd;
    u_long              tempval;
    u_int		temp2;
    struct strioctl     si;
    struct ifreq        ifr;
    struct timeval      timeout;

    if ((if_fd = open("/dev/nit", O_RDONLY)) < 0) {
        perror ("opening nit stream");
        exit (1);
    }
    /* Arrange to get discrete messages from the stream */
    if (ioctl( if_fd, I_SRDOPT, (char *)RMSGD) != 0) {
         perror("I_SRDOPT");
         exit (1);
    }
    /* Push and configure NIT buffering module */
    if (ioctl(if_fd, I_PUSH, "nbuf") != 0) {
         perror("I_PUSH nbuf");
         exit (1);
    }

    si.ic_timout = INFTIM;
    timeout.tv_sec   = (long)(TIMEOUT);
    timeout.tv_usec  = 0;
    si.ic_cmd = NIOCSTIME;            
    si.ic_len = sizeof timeout;
    si.ic_dp  = (char *)&timeout;
    if (ioctl(if_fd, I_STR, (char *)&si) != 0) {
         perror("NIOCSTIME");
         exit (1);
    }
    si.ic_cmd = NIOCSCHUNK;
    temp2   = (u_int)(MAX_CHUNK);
    si.ic_len = sizeof temp2;
    si.ic_dp  = (char *)&temp2;
    if (ioctl(if_fd, I_STR, (char *)&si) != 0) {
         perror("NIOCSCHUNK");
         exit (1);
    } 
    /* Configure the NIT device, binding it to the proper interface */
    strncpy (ifr.ifr_name, if_name, sizeof (ifr.ifr_name));
    ifr.ifr_name[sizeof ifr.ifr_name -1] = ' ';
    si.ic_cmd = NIOCBIND;
    si.ic_len = sizeof ifr;
    si.ic_dp  = (char *)&ifr;
    if (ioctl(if_fd, I_STR, (char *)&si) != 0) {
         perror(": NIOCSBIND");
         exit (1);
    }

    /* Set snapshot length */
    /*  currently commented out, as it causes the machine to hang!!!
     *    tempval = (u_long) (MAX_DATA_LEN);
     *   si.ic_cmd = NIOCSSNAP;
     *   si.ic_len = sizeof tempval;
     *   si.ic_dp  = (char *)&tempval;
     *   if (ioctl(if_fd, I_STR, (char *)&si) != 0) {
     *        perror("NIOCSSNAP");
     *        exit (1);
     *   }
     */

    tempval = NI_DROPS | NI_LEN;
    if (promiscuous)
       tempval |= NI_PROMISC;
    si.ic_cmd = NIOCSFLAGS;
    si.ic_len = sizeof tempval;
    si.ic_dp  = (char *)&tempval;
    if (ioctl(if_fd, I_STR, (char *)&si) !=0) { 
         perror("NIOCSFLAGS");
         exit(1);
    }
    ioctl(if_fd, I_FLUSH, (char *)FLUSHR);
    return if_fd;
}

void    collect ()
{
    register unsigned char      *bp;
    register unsigned char      *bufend;
    register struct nit_packet  *hdrp;
    int                         tail;
    int				plen,nlen;

    gettimeofday (&starttimer, (struct timezone *)NULL);
    while (1) {
	while ((tail = read(nit,buffer, MAX_CHUNK)) <= 0);
        bp     = buffer;
        bufend = bp + tail;
        while (bp < bufend) {
            hdrp  = (struct nit_packet *)bp;
	    plen = hdrp->header.nhb_msglen;
	    nlen = hdrp->header.nhb_totlen;
            bp += nlen;
	    if(plen >= MAX_DATA_LEN+8)
	        countpacket(hdrp);
	    else
		runt++;
        }
    	gettimeofday (&stoptimer, (struct timezone *)NULL);
        if ( (stoptimer.tv_sec - starttimer.tv_sec) > set_time) {
	    report();
	    gettimeofday(&starttimer, (struct timezone *) NULL);
	}
		  
    }
}
							 
countpacket(p) 
struct nit_packet *p;
{
    int ipoptlen;
    cur_drop = p -> nit_if_drops - dropped;
    total++;
    totalb += p->nit_if_origlen;
    if(p->pkt.etype <1515) {
	n802++;
	return;
    }
    if(debug)
        dump_pkt((char *)&(p->pkt));
    etypep[p->pkt.etype]++;
    etypeb[p->pkt.etype] += p->nit_if_origlen;
    if(p->pkt.etype == ETHERTYPE_IP) {
        ipprotop[(unsigned int)p->pkt.proto]++;
        ipprotob[(unsigned int)p->pkt.proto] += p->nit_if_origlen;
        ipoptlen = (((p->pkt.vl)&0x0f)-5)*2;
        if(ipoptlen != 0) {
	    ipoptions++;
	    printf("# ipoptlen %d\n",ipoptlen);
        }

        if (p->pkt.proto == IPPROTO_TCP) {
	    tcpportp[*(&(p->pkt.tsrc)+ipoptlen)]++;
            tcpportp[*(&(p->pkt.tdst)+ipoptlen)]++;
	    tcpportb[*(&(p->pkt.tsrc)+ipoptlen)] += p->nit_if_origlen;
	    tcpportb[*(&(p->pkt.tdst)+ipoptlen)] += p->nit_if_origlen;
        } 
        else if (p->pkt.proto == IPPROTO_UDP){
	    udpportp[*(&(p->pkt.tsrc)+ipoptlen)]++;
	    udpportp[*(&(p->pkt.tdst)+ipoptlen)]++;
	    udpportb[*(&(p->pkt.tsrc)+ipoptlen)] += p->nit_if_origlen;
	    udpportb[*(&(p->pkt.tdst)+ipoptlen)] += p->nit_if_origlen;
        }
    }
}

void report()
{
    int i;
    char  t1[40],t2[40];

    strcpy(t1,ctime(&starttimer.tv_sec));
    strcpy(t2,ctime(&stoptimer.tv_sec));
    printf ("#Start %s#Stop  %s",t1,t2);
    printf("#%lu packets, %lu bytes, %lu 802.3, %lu runt, %lu missed, %lu ipopt.\n",
	    total, totalb, n802, runt, cur_drop,ipoptions);
    total = 0;
    totalb = 0;
    n802 = 0;
    runt = 0;
    ipoptions = 0;
    dropped += cur_drop;

    for(i=0;i<65536;i++) {
	if(etypep[i]>0){
	    printf("e %x\t# %lu\tb %lu\n",i,
		etypep[i],etypeb[i]);
	    etypep[i] = 0;
	    etypeb[i] = 0;
	}
    } 
    for(i=0;i<256;i++) {
	if(ipprotop[i] >0) {
	    printf("i %d\t# %lu\tb %lu\n",i,
		ipprotop[i],ipprotob[i]);
	    ipprotop[i] = 0;
	    ipprotob[i] = 0;
	}
    }
    for(i=0;i<65536;i++) {
	if(tcpportp[i]>0) {
	    printf("t %d\t# %lu\tb %lu\n",i,
		tcpportp[i],tcpportb[i]);
	    tcpportp[i] = 0;
	    tcpportb[i] = 0;
	}
    } 
    for(i=0;i<65536;i++) {
	if(udpportp[i]>0) {
	    printf("u %d\t# %lu\tb %lu\n",i,
		udpportp[i],udpportb[i]);
	    udpportp[i] = 0;
	    udpportb[i] = 0;
	}
    } 
    fflush(stdout);
}

									
void	cleanup ()
{
    close (nit);
    report();
    exit (0);
}

void dump_pkt(b)
char *b;
{
	char s[32],d[32];
	int i1,i2;
	struct pkt *p;
	extern char *ether_ntoa();
	extern char *inet_ntoa();

	p = (struct pkt *) b;

	strcpy(s, ether_ntoa((struct ether_addr *) p->esrc));
	strcpy(d, ether_ntoa((struct ether_addr *) p->edst));
	printf("Ethernet src %s dst %s type %d\n",s,d,p->etype);

	strcpy(s, inet_ntoa((struct inet_addr *) p->isrc));
	strcpy(d, inet_ntoa((struct inet_addr *) p->idst));
	printf("IP src %s dst %s length %d ident %d\n",
		s,d, (int) p->tlen, (int) p->ident);

	bcopy(p->seq,&i1,4);
	bcopy(p->ack,&i2,4);
	printf("TCP src port %d dst port %d seq %d ack %d\n",
		(int)p->tsrc, (int)p->tdst, i1, i2);
	printf("Code %d Data 0x%x\n",(int)p->code,(int)p->data[0]);
}
#endif
