/*
 * Copyright (c) 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001,
 *               2002, 2003, 2004
 *	Ohio University.
 *
 * ---
 * 
 * Starting with the release of tcptrace version 6 in 2001, tcptrace
 * is licensed under the GNU General Public License (GPL).  We believe
 * that, among the available licenses, the GPL will do the best job of
 * allowing tcptrace to continue to be a valuable, freely-available
 * and well-maintained tool for the networking community.
 *
 * Previous versions of tcptrace were released under a license that
 * was much less restrictive with respect to how tcptrace could be
 * used in commercial products.  Because of this, I am willing to
 * consider alternate license arrangements as allowed in Section 10 of
 * the GNU GPL.  Before I would consider licensing tcptrace under an
 * alternate agreement with a particular individual or company,
 * however, I would have to be convinced that such an alternative
 * would be to the greater benefit of the networking community.
 * 
 * ---
 *
 * This file is part of Tcptrace.
 *
 * Tcptrace was originally written and continues to be maintained by
 * Shawn Ostermann with the help of a group of devoted students and
 * users (see the file 'THANKS').  The work on tcptrace has been made
 * possible over the years through the generous support of NASA GRC,
 * the National Science Foundation, and Sun Microsystems.
 *
 * Tcptrace is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * Tcptrace is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with Tcptrace (in the file 'COPYING'); if not, write to the
 * Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston,
 * MA 02111-1307 USA
 * 
 * Author:	Shawn Ostermann
 * 		School of Electrical Engineering and Computer Science
 * 		Ohio University
 * 		Athens, OH
 *		ostermann@cs.ohiou.edu
 *		http://www.tcptrace.org/
 */
#include "tcptrace.h"
static char const GCC_UNUSED rcsid[] =
   "$Header: /usr/local/cvs/tcptrace/mod_slice.c,v 1.5 2003/11/19 14:38:03 sdo Exp $";

#ifdef LOAD_MODULE_SLICE

#include "mod_slice.h"

/* name of the file that slice data is dumped into */
#define SLICE_FILENAME "slice.dat"

/* time and date formats */
enum t_time_format {tf_long=1, tf_brief=2, tf_unix=3, tf_unix_long=4};

/* argument flags */
static float slice_interval = 15.0;  /* 15 seconds by default */
static timeval tv_slice_interval;
static enum t_time_format time_format = tf_brief;

/* local debugging flag */
static int ldebug = 0;




/* info that I keep about each connection */
struct conn_info {
    u_long last_rexmits;	/* for detecting rexmits */
    u_long last_active_slice;	/* for checking activity */
};


/* counters that we keep for each interval */
static struct slice_counters {
    /* active conns */
    u_long n_active;

    /* connection opens/closes */
    u_long n_opens;

    /* bytes/segments (including rexmits) */
    u_long n_bytes;
    u_long n_segs;

    /* bytes/segments that are rexmitted */
    u_long n_rexmit_bytes;
    u_long n_rexmit_segs;
} info;


/* local routines */
static void AgeSlice(timeval *);
static void ParseArgs(char *argstring);


/* globals */
static u_long slice_number = 1;	/* while slice interval are we in? */






/* Set things up */
int
slice_init(
    int argc,
    char *argv[])
{
    int i;
    int enable=0;
    char *args = NULL;

    for (i=1; i < argc; ++i) {
	if (!argv[i])
	    continue;  /* argument already taken by another module... */
	if (strncmp(argv[i],"-x",2) == 0) {
	    if (strncasecmp(argv[i]+2,"slice",sizeof("slice")-1) == 0) {
		/* I want to be called */
		args = argv[i]+(sizeof("-xslice")-1);
		enable = 1;
		argv[i] = NULL;
	    }
	}
    }

    if (!enable)
	return(0);	/* don't call me again */

    /* parse the encoded args */
    ParseArgs(args);

    /* turn the slice interval into a timeval */
    tv_slice_interval.tv_sec = (int)slice_interval;
    tv_slice_interval.tv_usec =
	1000000 * (slice_interval - tv_slice_interval.tv_sec);

    /* tell them what's happening */
    printf("mod_slice: generating data in %.3fsec slices to file %s\n",
	   slice_interval, SLICE_FILENAME);
    if (ldebug)
	printf("Slice interval tv: %u.%06u\n",
	       (unsigned)tv_slice_interval.tv_sec,
	       (unsigned)tv_slice_interval.tv_usec);

    /* init the graphs and etc... */
    AgeSlice(NULL);

    return(1);	/* TRUE means call slice_read and slice_done later */
}




void
slice_read(
    struct ip *pip,		/* the packet */
    tcp_pair *ptp,		/* info I have about this connection */
    void *plast,		/* past byte in the packet */
    void *mod_data)		/* connection info for this one */
{
    u_long bytes = ntohs(pip->ip_len);
    static timeval next_time = {0,0};
    struct conn_info *pci = mod_data;
    int was_rexmit = 0;

    /* if this is the first packet, determine the END of the slice */
    if (ZERO_TIME(&next_time)) {
	next_time = current_time;
	tv_add(&next_time, tv_slice_interval);
    }

    /* if we've gone over our interval, print out data so far */
    while (tv_ge(current_time,next_time)) {
	/* output a line */
	AgeSlice(&next_time);

	/* when does the next interval start? */
	tv_add(&next_time, tv_slice_interval);
    }

    /* see if it was a retransmission */
    if (pci->last_rexmits != ptp->a2b.rexmit_pkts+ptp->b2a.rexmit_pkts) {
	pci->last_rexmits = ptp->a2b.rexmit_pkts+ptp->b2a.rexmit_pkts;
	was_rexmit = 1;
    }

    /* add to total data counters */
    ++info.n_segs;
    info.n_bytes += bytes;
    if (was_rexmit) {
	info.n_rexmit_segs += 1;
	info.n_rexmit_bytes += bytes;
    }

    /* if it hasn't already been active in this slice interval, count it*/
    /* (avoids having to zero lots of counters!) */
    if (pci->last_active_slice != slice_number) {
	pci->last_active_slice = slice_number;
	++info.n_active;
    }
    

}



static void
AgeSlice(
    timeval *pnow)
{
    char *pch_now;
    static MFILE *pmf = NULL;

    

    /* first time doesn't count */
    if (pnow == NULL) {
	/* open the output file */
	pmf = Mfopen(SLICE_FILENAME,"w");

	/* print the headers */
	Mfprintf(pmf,"\
%s     segs    bytes  rexsegs rexbytes      new   active\n\
%s -------- -------- -------- -------- -------- --------\n",
		 (time_format == tf_long)?	"date                           ":
		 (time_format == tf_brief)?	"date           ":
		 (time_format == tf_unix)?	"date     ":
		 (time_format == tf_unix_long)?	"date            ":"UNKNOWN",

		 (time_format == tf_long)?	"-------------------------------":
		 (time_format == tf_brief)?	"---------------":
		 (time_format == tf_unix)?	"---------":
		 (time_format == tf_unix_long)?	"----------------":"UNKNOWN"
		 );
	return;
    }

    /* format the current time */
    pch_now = ts2ascii(pnow);
    if (time_format == tf_brief) {
	/* remove the year */
	pch_now[26] = '\00';
	/* remove the month and stuff */
	pch_now += 11;  /* strlen("Fri Jan 12 ") */
    }

    /* print the stats collected */
    switch(time_format) {
      case tf_long:
      case tf_brief:
	Mfprintf(pmf, "%s", pch_now); break;
      case tf_unix:
	Mfprintf(pmf, "%8lu", pnow->tv_sec); break;
      case tf_unix_long:
	Mfprintf(pmf, "%8lu.%06u", pnow->tv_sec, pnow->tv_usec); break;
    }
    Mfprintf(pmf, " %8lu %8lu %8lu %8lu %8lu %8lu\n",
	     info.n_segs,
	     info.n_bytes,
	     info.n_rexmit_segs,
	     info.n_rexmit_bytes,
	     info.n_opens,
	     info.n_active);


    /* zero out the counters */
    memset(&info, 0, sizeof(info));
    

    /* new slice interval */
    ++slice_number;
}


void	
slice_done(void)
{
    /* print the last few packets */
    AgeSlice(&current_time);
}




void *
slice_newconn(
    tcp_pair *ptp)
{
    struct conn_info *pci;
    
    ++info.n_opens;

    pci = MallocZ(sizeof(struct conn_info));

    return(pci);
}


void
slice_usage(void)
{
    printf("\
\t-xslice\"[ARGS]\"\tprint data info in slices\n\
\t   module argument format:\n\
\t       -iS   set slice interval to S (float) seconds, default 15.0\n\
\t       -d    enable local debugging in this module\n\
\t       -tb   specify time and date 'briefly'\n\
\t       -tl   specify time and date in long, 'Unix Format'\n\
\t       -tu   specify time and date as a Unix timestamp (secs)\n\
\t       -tU   specify time and date as a Unix timestamp (secs.usecs)\n\
");
}


static void
ParseArgs(char *argstring)
{
    int argc;
    char **argv;
    int i;
    
    /* make sure there ARE arguments */
    if (!(argstring && *argstring))
	return;

    /* break the string into normal arguments */
    StringToArgv(argstring,&argc,&argv);

    /* check the module args */
    for (i=1; i < argc; ++i) {
	float interval;
	if (ldebug > 1)
	    printf("Checking argv[%d]: '%s'\n", i, argv[i]);
	if (strcmp(argv[i],"-d") == 0) {
	    ++ldebug;
	} else if (strncmp(argv[i],"-t",2) == 0) {
	    switch (argv[i][2]) {
	      case 'u': time_format = tf_unix; break;
	      case 'U': time_format = tf_unix_long; break;
	      case 'l': time_format = tf_long; break;
	      case 'b': time_format = tf_brief; break;
	      default:
		fprintf(stderr,"Bad -t option ('%s') for slice module\n", argv[i]);
		slice_usage();
		exit(-1);
	    }
	} else if (sscanf(argv[i],"-i%f", &interval) == 1) {
	    slice_interval = interval;
	    if (ldebug)
		printf("mod_slice: setting slice interval to %.3f seconds\n",
		       slice_interval);
	} else {
	    fprintf(stderr,"Slice module: bad argument '%s'\n",
		    argv[i]);
	    exit(-1);
	}
    }
}


#endif /* LOAD_MODULE_SLICE */
