/*
 * 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_traffic.c,v 5.16 2003/11/19 14:38:03 sdo Exp $";

#ifdef LOAD_MODULE_TRAFFIC

#include "mod_traffic.h"


/* info kept for each (active) port */
struct traffic_info {
    /* which port */
    u_short port;
    
    /* interval byte counters */
    PLINE line_nbytes;
    u_long nbytes;
    u_long ttlbytes;

    /* interval packet counters */
    PLINE line_npackets;
    u_long npackets;
    u_long ttlpackets;

    /* active connections */
    PLINE line_nactive;
    u_long nactive;
    u_long ttlactive;

    /* idle connections */
    PLINE line_nidle;
    u_long nidle;
    u_long ttlidle;

    /* open connections */
    PLINE line_nopen;
    u_long nopen;
    u_long ttlopen;

    /* instantaneous open connections */
    PLINE line_niopen;
    u_long n_i_open;
    u_long ttl_i_open;

    /* long-duration connections */
    PLINE line_nlong;
    u_long nlong;
    u_long ttllong;

    /* pureacks */
    PLINE line_pureacks;
    u_long npureacks;
    u_long ttlpureacks;

    /* which color is used for plotting */
    char *color;

    /* linked list of the one's we're using */
    struct traffic_info *next;
};
static struct traffic_info *traffichead = NULL;
#define NUM_PORTS 65536
static struct traffic_info **ports;  /* [NUM_PORTS] */
#define EXCLUDE_PORT ((void *)(-1))
#define INCLUDE_PORT (NULL)

/* name of the file that port data is dumped into */
#define PORT_FILENAME "traffic_byport.dat"
#define STATS_FILENAME "traffic_stats.dat"

/* additional info kept per connection */
struct conn_info {
    Bool wasactive;		/* was this connection active over the interval? */
    Bool wasopen;		/* was this this connection EVER open? */
    Bool isopen;		/* is this connection open now? */
    Bool islong;		/* is this a long-duration connection? */
    Bool halfopen;		/* for half open conns */
    struct traffic_info *pti1;	/* pointer to the port info for this one */
    struct traffic_info *pti2;	/* pointer to the port info for this one */
    struct conn_info *next;	/* next in the chain */

    u_int last_dupacks;		/* last value of dupacks I saw */
    u_int last_rexmits;		/* last value of rexmits I saw */
    u_int last_rtts;		/* last value of rtt counters I saw */
};
static struct conn_info *connhead = NULL;



/* plotter files that we keep open */
static PLOTTER plotter_bytes;
static PLOTTER plotter_packets;
static PLOTTER plotter_active;
static PLOTTER plotter_idle;
static PLOTTER plotter_open;
static PLOTTER plotter_openclose;
static PLOTTER plotter_i_open;
static PLOTTER plotter_loss;
static PLOTTER plotter_long;
static PLOTTER plotter_rtt;
static PLOTTER plotter_halfopen;
static PLOTTER plotter_pureacks;
static PLOTTER plotter_data;

#define  PLOTTER_BYTES_FILENAME		"traffic_bytes.xpl"
#define  PLOTTER_PACKETS_FILENAME	"traffic_packets.xpl"
#define  PLOTTER_ACTIVE_FILENAME	"traffic_active.xpl"
#define  PLOTTER_OPEN_FILENAME		"traffic_open.xpl"
#define  PLOTTER_OPENCLOSE_FILENAME	"traffic_openclose.xpl"
#define  PLOTTER_I_OPEN_FILENAME	"traffic_i_open.xpl"
#define  PLOTTER_LOSS_FILENAME		"traffic_loss.xpl"
#define  PLOTTER_LONG_FILENAME		"traffic_long.xpl"
#define  PLOTTER_RTT_FILENAME		"traffic_rtt.xpl"
#define  PLOTTER_HALFOPEN_FILENAME	"traffic_halfopen.xpl"
#define  PLOTTER_PUREACKS_FILENAME	"traffic_pureacks.xpl"
#define  PLOTTER_IDLE_FILENAME		"traffic_idle.xpl"
#define  PLOTTER_DATA_FILENAME		"traffic_data.xpl"

/* argument flags */
static float age_interval = 15.0;  /* 15 seconds by default */
static Bool doplot_bytes = FALSE;
static Bool doplot_packets = FALSE;
static Bool doplot_active = FALSE;
static Bool doplot_open = FALSE;
static Bool doplot_openclose = FALSE;
static Bool doplot_i_open = FALSE;
static Bool doplot_loss = FALSE;
static Bool doplot_long = FALSE;
static Bool doplot_rtt = FALSE;
static Bool doplot_halfopen = FALSE;
static Bool doplot_pureacks = FALSE;
static Bool doplot_idle = FALSE;
static Bool doplot_data = FALSE;
static int longconn_duration = 60;


/* local routines */
static struct traffic_info *MakeTrafficRec(u_short port);
static void MakeTrafficLines(struct traffic_info *pti);
static struct conn_info *MakeConnRec(void);
static void AgeTraffic(void);
static struct traffic_info *FindPort(u_short port);
static void IncludePorts(unsigned firstport, unsigned lastport);
static void ExcludePorts(unsigned firstport, unsigned lastport);
static void CheckPortNum(unsigned portnum);
static char *PortName(int port);
static void ParseArgs(char *argstring);
static void DoplotIOpen(int port, Bool fopen);


/* info for opens and closes graphs */
static PLINE line_num_closes;
static PLINE line_num_opens;
static PLINE line_open_conns;
static PLINE line_num_halfopens;
static int num_closes = 0;
static int num_opens = 0;
static int ttl_num_opens = 0;
static int ttl_num_closes = 0;
static int open_conns = 0;
static int num_halfopens = 0;

/* info for total byte counters */
static PLINE line_data_nonrexmit;
static PLINE line_data_all;
static u_llong data_nbytes_nonrexmit;
static u_llong data_nbytes_all;

/* info for the loss events graph */
static PLINE line_dupacks;
static PLINE line_rexmits;
static u_long dupacks;
static u_long ttl_dupacks;
static u_long rexmits;
static u_long ttl_rexmits;

/* info for the RTT graph */
static PLINE line_rtt_avg;
static PLINE line_rtt_min;
static PLINE line_rtt_max;
static float rtt_ttl;		/* in msecs */
static float ttl_rtt_ttl;	/* in msecs */
static int rtt_min = -1;	/* in msecs */
static int rtt_max = -1;	/* in msecs */
static int rtt_samples;
static int ttl_rtt_samples;
static u_int rtt_minvalid = 0;	/* minimum RTT to consider (ms) */
static u_int rtt_maxvalid = 0xffffffff; /* maximum RTT to consider (ms) */


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


static void
CheckPortNum(
    unsigned portnum)
{
    if (portnum >= NUM_PORTS) {
	fprintf(stderr,"mod_traffic: Invalid port number '%d'\n", portnum);
	traffic_usage();
	exit(-1);
    }
}


static void
ExcludePorts(
    unsigned firstport,
    unsigned lastport)
{
    CheckPortNum(firstport);
    CheckPortNum(lastport);

    if (ldebug)
	printf("mod_traffic: excluding ports [%d-%d]\n", firstport, lastport);

    while (firstport <= lastport)
	ports[firstport++] = EXCLUDE_PORT;
}


static void
IncludePorts(
    unsigned firstport,
    unsigned lastport)
{
    CheckPortNum(firstport);
    CheckPortNum(lastport);

    if (ldebug)
	printf("mod_traffic: including ports [%d-%d]\n", firstport, lastport);

    while (firstport <= lastport)
	ports[firstport++] = INCLUDE_PORT;
}



/* Mostly as a module example, here's a plug in that records TRAFFIC info */
int
traffic_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,"traffic",sizeof("traffic")-1) == 0) {
		/* I want to be called */
		args = argv[i]+(sizeof("-xtraffic")-1);
		enable = 1;
		printf("mod_traffic: characterizing traffic\n");
		argv[i] = NULL;
	    }
	}
    }

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

    /* init the data storage structure */
    ports = MallocZ(NUM_PORTS*sizeof(struct traffic_info *));

    ports[0] = MakeTrafficRec(0);

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

    /* open the output files */
    if (doplot_packets)
	plotter_packets =
	    new_plotter(NULL,
			PLOTTER_PACKETS_FILENAME,
			"packets per second over time by port",
			"time","packets/second",
			NULL);
    if (doplot_bytes)
	plotter_bytes =
	    new_plotter(NULL,
			PLOTTER_BYTES_FILENAME,
			"bytes per second over time by port",
			"time","bytes/second",
			NULL);
    if (doplot_active)
	plotter_active =
	    new_plotter(NULL,
			PLOTTER_ACTIVE_FILENAME,
			"active connections over time by port",
			"time","active connections",
			NULL);
    if (doplot_idle)
	plotter_idle =
	    new_plotter(NULL,
			PLOTTER_IDLE_FILENAME,
			"idle connections over time by port",
			"time","idle connections",
			NULL);
    if (doplot_open)
	plotter_open =
	    new_plotter(NULL,
			PLOTTER_OPEN_FILENAME,
			"open connections over time by port",
			"time","open connections",
			NULL);
    if (doplot_i_open)
	plotter_i_open =
	    new_plotter(NULL,
			PLOTTER_I_OPEN_FILENAME,
			"open connections over time by port - instantaneous",
			"time","number of connections",
			NULL);
    if (doplot_openclose) {
	plotter_openclose =
	    new_plotter(NULL,
			PLOTTER_OPENCLOSE_FILENAME,
			"connections opened and closed over time",
			"time","number of connections",
			NULL);
	line_num_opens = new_line(plotter_openclose, "Number Opens", "green");
	line_num_closes = new_line(plotter_openclose, "Number Closes", "red");
	line_open_conns = new_line(plotter_openclose, "Total Open", "blue");
    }
    if (doplot_halfopen) {
	plotter_halfopen =
	    new_plotter(NULL,
			PLOTTER_HALFOPEN_FILENAME,
			"half open connections over time",
			"time","number of half open connections",
			NULL);
	line_num_halfopens = new_line(plotter_halfopen,
				      "Halfopen Conns", "green");
    }
    if (doplot_pureacks) {
	plotter_pureacks =
	    new_plotter(NULL,
			PLOTTER_PUREACKS_FILENAME,
			"pure acks (no data) per second over time",
			"time","pureacks/second",
			NULL);
    }

    if (doplot_loss) {
	plotter_loss =
	    new_plotter(NULL,
			PLOTTER_LOSS_FILENAME,
			"packet loss per second over time",
			"time","events/second",
			NULL);
	line_dupacks = new_line(plotter_loss, "Triple Dupacks", "yellow");
	line_rexmits = new_line(plotter_loss, "Retransmits", "blue");
    }

    if (doplot_rtt) {
	plotter_rtt =
	    new_plotter(NULL,
			PLOTTER_RTT_FILENAME,
			"RTT over time",
			"time","RTT (msecs)",
			NULL);
	line_rtt_min = new_line(plotter_rtt, "Min RTT", "green");
	line_rtt_max = new_line(plotter_rtt, "Max RTT", "red");
	line_rtt_avg = new_line(plotter_rtt, "Average RTT", "blue");
    }

    if (doplot_data) {
	plotter_data =
	    new_plotter(NULL,
			PLOTTER_DATA_FILENAME,
			"Total Data Sent Over Time",
			"time","total bytes",
			NULL);
	line_data_all = new_line(plotter_data, "All Data", "blue");
	line_data_nonrexmit = new_line(plotter_data, "Non-Rexmitted Data", "red");
    }

    if (doplot_long) {
	char title[100];
	snprintf(title,sizeof(title),"connections still open after %d seconds\n",
		longconn_duration);
	plotter_long =
	    new_plotter(NULL,
			PLOTTER_LONG_FILENAME,
			title,
			"time","number of connections",
			NULL);
    }

    /* we don't want the normal output */
    printsuppress = TRUE;

    /* create any lines that I want to draw */
    MakeTrafficLines(ports[0]);

    /* init the graphs and etc... */
    AgeTraffic();

    return(1);	/* TRUE means call traffic_read and traffic_done later */
}


/* return the record for traffic on this port */
static struct traffic_info *
FindPort(
    u_short port)
{
    struct traffic_info *pti;

    /* port "0" means "all", but we don't need to treat it as a special case */

    /* see what's there now */
    pti = ports[port];

    /* see if it's "excluded" */
    if ((port != 0) && (pti == EXCLUDE_PORT))
	return(NULL);

    /* make a new one if there's a NULL there */
    if (!pti) {
	pti = MakeTrafficRec(port);
	/* create any lines that I want to draw */
	MakeTrafficLines(pti);
    }

    return(pti);
}





static struct traffic_info *
MakeTrafficRec(
    u_short port)
{
    struct traffic_info *pti;

    pti = MallocZ(sizeof(struct traffic_info));

    if (ldebug>10)
	printf("MakeTrafficRec(%d) called\n", (int)port);

    /* init */
    pti->port = port;

    /* chain it in (at head of list) */
    pti->next = traffichead;
    traffichead = pti;

    /* add to lookup array */
    ports[port] = pti;

    return(pti);
}


static void
MakeTrafficLines(
    struct traffic_info *pti)
{
    char *portname;
    static int nextcolor = 0;

    /* map port number to name for printing */
    portname = (pti->port==0)?"total":strdup(PortName(pti->port));

    /* pick color */
    pti->color = ColorNames[nextcolor % NCOLORS];
    ++nextcolor;

    /* create the lines that we sometimes use */
    if (doplot_bytes)
	pti->line_nbytes = new_line(plotter_bytes, portname, pti->color);
    if (doplot_packets) 
	pti->line_npackets = new_line(plotter_packets, portname, pti->color);
    if (doplot_active)
	pti->line_nactive = new_line(plotter_active, portname, pti->color);
    if (doplot_idle)
	pti->line_nidle = new_line(plotter_idle, portname, pti->color);
    if (doplot_open)
	pti->line_nopen = new_line(plotter_open, portname, pti->color);
    if (doplot_long)
	pti->line_nlong = new_line(plotter_long, portname, pti->color);
    if (doplot_i_open)
	pti->line_niopen = new_line(plotter_i_open, portname, pti->color);
    if (doplot_pureacks)
	pti->line_pureacks = new_line(plotter_pureacks, portname, pti->color);
}



static struct conn_info *
MakeConnRec(void)
{
    struct conn_info *pci;

    pci = MallocZ(sizeof(struct conn_info));

    /* chain it in (at head of list) */
    pci->next = connhead;
    connhead = pci;

    return(pci);
}


void
traffic_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 */
{
    struct tcphdr *ptcp = (struct tcphdr *) ((char *)pip + 4*IP_HL(pip));
    struct traffic_info *pti1 = FindPort(ntohs(ptcp->th_sport));
    struct traffic_info *pti2 = FindPort(ntohs(ptcp->th_dport));
    u_long bytes = ntohs(pip->ip_len);
    static timeval last_time = {0,0};
    struct conn_info *pci = mod_data;
    int was_rexmit = 0;

    /* if neither port is interesting, then ignore this one */
    if (!pti1 && !pti2) {
	return;
    }

    /* OK, this connection is now active */
    pci->wasactive = 1;

    /* check to see if it's really "open" (traffic in both directions) */
    if (!pci->wasopen) {
	if ((ptp->a2b.packets > 0) && (ptp->b2a.packets > 0)) {
	    /* bidirectional: OK, we'll call it open */
	    pci->wasopen = 1;
	    pci->isopen = 1;
	    ++num_opens;
	    ++ttl_num_opens;
	    ++open_conns;

	    /* instantaneous opens and closes */
	    if (doplot_i_open) {
		DoplotIOpen(ntohs(ptcp->th_dport), TRUE);
		DoplotIOpen(ntohs(ptcp->th_sport), TRUE);
		DoplotIOpen(0, TRUE);
	    }
	}
    }

    /* add to port-specific counters */
    if (pti1) {
	pti1->nbytes += bytes;
	pti1->npackets += 1;
    }
    if (pti2) {
	pti2->nbytes += bytes;
	pti2->npackets += 1;
    }

    /* add to GLOBAL counters */
    ports[0]->nbytes += bytes;
    ports[0]->npackets += 1;
    ports[0]->npureacks += 1;

    /* see if we're closing it */
    if (RESET_SET(ptcp) ||
	(FIN_SET(ptcp) &&	/* find in BOTH directions */
	 ((ptp->a2b.fin_count>0) && (ptp->b2a.fin_count>0)))) {
	if (pci->isopen) {
	    pci->isopen = 0;
	    ++num_closes;
	    --open_conns;

	    /* instantaneous opens and closes */
	    if (doplot_i_open) {
		DoplotIOpen(ntohs(ptcp->th_dport), FALSE);
		DoplotIOpen(ntohs(ptcp->th_sport), FALSE);
		DoplotIOpen(0, FALSE);
	    }
	}
    }

    /* half open conns */
    if (FIN_SET(ptcp)) {
	if ((ptp->a2b.fin_count>0) && (ptp->b2a.fin_count>0)) {
	    if (pci->halfopen) {
		/* fully closed now */
		--num_halfopens;
		pci->halfopen = 0;
	    }
	} else if (!pci->halfopen) {
		/* half open now */
		++num_halfopens;
		pci->halfopen = 1;
	}
    }

    /* check losses */
    if (pci->last_dupacks != ptp->a2b.rtt_triple_dupack+
	ptp->b2a.rtt_triple_dupack) {
	pci->last_dupacks = ptp->a2b.rtt_triple_dupack+
	    ptp->b2a.rtt_triple_dupack;
	++dupacks;
	++ttl_dupacks;
    }
    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;
	++rexmits;
	++ttl_rexmits;
    }


    /* add to total data counters */
    data_nbytes_all += bytes;
    if (!was_rexmit)
	data_nbytes_nonrexmit += bytes;
    

    /* RTT stats */
    if (ACK_SET(ptcp)) {
	tcb *ptcb;
	int rtt;

	/* see which of the 2 TCB's this goes with */
	if (ptp->addr_pair.a_port == ntohs(ptcp->th_dport))
	    ptcb = &ptp->a2b;
	else
	    ptcb = &ptp->b2a;

	/* check the rtt counter of the last sample */
	rtt = ptcb->rtt_last / 1000.0;
 
	if ((pci->last_rtts != ptcb->rtt_count + ptcb->rtt_amback) &&
	    (ptcb->rtt_last != 0.0) &&
	    (rtt > rtt_minvalid) && (rtt <= rtt_maxvalid)) {

	    /* sample is only valid when one of these counters is higher */
	    pci->last_rtts = ptcb->rtt_count + ptcb->rtt_amback;

	    /* keep stats */
	    rtt_ttl += rtt;
	    ttl_rtt_ttl += rtt;
	    ++rtt_samples;
	    ++ttl_rtt_samples;

	    /* also, remember min and max */
	    if ((rtt_max == -1) || (rtt_max < rtt))
		rtt_max = rtt;
	    if ((rtt_min == -1) || (rtt_min > rtt))
		rtt_min = rtt;

	    if (ldebug > 9)
		printf("Rtt: %d,  min:%d,  max:%d\n",
		       rtt, rtt_min, rtt_max);
	}
    }


    /* see if this is now "long duration" */
    if (!pci->islong) {
	int etime_msecs = elapsed(ptp->first_time,current_time);
	if (etime_msecs/1000000 > longconn_duration) {
	    pci->islong = 1;
	}
    }

    /* count "pure acks" (no data) */
    if (ACK_SET(ptcp)) {
	int tcp_length, tcp_data_length;
	tcp_length = getpayloadlength(pip, plast);
	tcp_data_length = tcp_length - (4 * TH_OFF(ptcp));
	if (tcp_data_length == 0) {
	    if (pti1) {
		++pti1->npureacks;
	    }
	    if (pti2) {
		++pti2->npureacks;
	    }
	}
    }


    /* determine elapsed time and age the samples */
    if (elapsed(last_time,current_time)/1000000.0 > age_interval) {
	AgeTraffic();
	last_time = current_time;
    }
}


static char *
PortName(
    int port)
{
    static char buf[20];

    if (port == 0)
	return("total");

    snprintf(buf,sizeof(buf),"%d",port);
    return(buf);
}


static void
AgeTraffic(void)
{
    struct traffic_info *pti;
    struct conn_info *pci;
    static timeval last_time = {0,0};
    float etime;
    int ups;			/* units per second */

    /* first time doesn't count */
    if (ZERO_TIME(&last_time)) {
	last_time = current_time;
	return;
    }

    /* check elapsed time */
    etime = elapsed(last_time, current_time);
    if (ldebug>1)
	printf("AgeTraffic called, elapsed time is %.3f seconds\n", etime/1000000);
    if (etime == 0.0)
	return;

    /* roll the open/active/long connections into the port records */
    for (pci=connhead; pci; pci=pci->next) {
	if (pci->wasactive) {
	    if (pci->pti1)
		++pci->pti1->nactive;
	    if (pci->pti2)
		++pci->pti2->nactive;
	    pci->wasactive = 0;
	    ++ports[0]->nactive;
	}
	if (pci->isopen) {
	    if (pci->pti1)
		++pci->pti1->nopen;
	    if (pci->pti2)
		++pci->pti2->nopen;
	    ++ports[0]->nopen;
	    if (pci->islong) {
		if (pci->pti1)
		    ++pci->pti1->nlong;
		if (pci->pti2)
		    ++pci->pti2->nlong;
		++ports[0]->nlong;
	    }

	    if (!pci->wasactive) {
		/* open and !active ==> IDLE */
		if (pci->pti1)
		    ++pci->pti1->nidle;
		if (pci->pti2)
		    ++pci->pti2->nidle;
		++ports[0]->nidle;
	    }
	}
    }
    

    /* ============================================================ */
    /* plot halfopen conns */
    if (doplot_halfopen) {
	/* draw lines */
	extend_line(line_num_halfopens,current_time, num_halfopens);
    }


    /* ============================================================ */
    /* plot connection activity */
    /* opens */
    if (doplot_openclose) {
	/* draw lines */
	extend_line(line_num_opens,current_time, num_opens);
	extend_line(line_num_closes,current_time, num_closes);
	extend_line(line_open_conns,current_time, open_conns);

	/* reset interval counters */
	 
// Counting ttl_num_opens instantaneously as and when num_opens is incremented,
// so that ttl_num_opens is printed properly in traffic_stats.dat even 
// when the -C(openclose) option is not given.
// Hence commenting off the following line. - Mani, 4 Aug 2003.
//	ttl_num_opens += num_opens;
	ttl_num_closes += num_closes;
	num_opens = 0;
	num_closes = 0;
    }


    /* ============================================================ */
    /* report of loss events */
    if (doplot_loss) {
	/* convert to events/second */
	/* sdo bugfix - Wed May 12, 1999 - round UP!! */
	dupacks = (dupacks+age_interval-1)/age_interval;
	rexmits = (rexmits+age_interval-1)/age_interval;

	/* draw lines */
	extend_line(line_dupacks,current_time, dupacks);
	extend_line(line_rexmits,current_time, rexmits);

	/* reset interval counters */
	dupacks = 0;
	rexmits = 0;
    }


    /* ============================================================ */
    /* report of RTT */
    if (doplot_rtt && (rtt_samples > 0)) {
	int rtt_avg;

	/* convert to average rtt */
	rtt_avg = (int)((rtt_ttl/(float)rtt_samples));

	/* draw lines */
	extend_line(line_rtt_avg, current_time, rtt_avg);
	if (rtt_min != -1)
	    extend_line(line_rtt_min, current_time, rtt_min);
	if (rtt_max != -1)
	    extend_line(line_rtt_max, current_time, rtt_max);

	/* reset interval counters */
	rtt_ttl = 0;
	rtt_samples = 0;
	rtt_min = -1;
	rtt_max = -1;
    }


    /* ============================================================ */
    /* report of total data */
    if (doplot_data) {
	extend_line(line_data_all, current_time, data_nbytes_all);
	extend_line(line_data_nonrexmit, current_time, data_nbytes_nonrexmit);
    }

    /* ============================================================ */
    /* print them out */
    for (pti=traffichead; pti; pti=pti->next) {
	if (ldebug>1)
	    printf("  Aging Port %u   bytes: %lu  packets: %lu\n",
		   pti->port, pti->nbytes, pti->npackets);

	/* plot bytes */
	if (doplot_bytes) {
	    /* convert to units per second */
	    ups = (int)((float)pti->nbytes * 1000000.0 / etime);

	    /* plot it */
	    extend_line(pti->line_nbytes,current_time, ups);
	}

	/* plot packets */
	if (doplot_packets) {
	    /* convert to units per second */
	    ups = (int)((float)pti->npackets * 1000000.0 / etime);

	    /* plot it */
	    extend_line(pti->line_npackets,current_time, ups);
	}



	/* plot active connections */
	if (doplot_active) {
	    /* plot it */
	    extend_line(pti->line_nactive,current_time, pti->nactive);
	}

	/* plot idle connections */
	if (doplot_idle) {
	    /* plot it */
	    extend_line(pti->line_nidle,current_time, pti->nidle);
	}


	/* plot open connections */
	if (doplot_open) {
	    /* plot it */
	    extend_line(pti->line_nopen,current_time, pti->nopen);
	}

	/* plot long-duration */
	if (doplot_long) {
	    extend_line(pti->line_nlong,current_time, pti->nlong);
	}

	/* plot pureacks */
	if (doplot_pureacks) {
	    /* convert to units per second */
	    ups = (int)((float)pti->npureacks * 1000000.0 / etime);

	    extend_line(pti->line_pureacks, current_time, ups);
	}
    }

    /* zero them out */
    for (pti=traffichead; pti; pti=pti->next) {
	pti->ttlbytes += pti->nbytes;
	pti->ttlpackets += pti->npackets;
	pti->ttlpureacks += pti->npureacks;

	pti->nbytes = 0;
	pti->nlong = 0;
	pti->npackets = 0;
	pti->nactive = 0;
	pti->nidle = 0;
	pti->nopen = 0;
	pti->npureacks = 0;
    }

    last_time = current_time;
}


void	
traffic_done(void)
{
    struct traffic_info *pti;
    struct conn_info *pci;
    double etime = elapsed(first_packet,last_packet);
    int etime_secs = etime / 1000000.0;
    MFILE *pmf;
    int i;

    /* roll the active connections into the port records */
    for (pci=connhead; pci; pci=pci->next) {
	if (pci->pti1)
	    ++pci->pti1->ttlactive;
	if (pci->pti2)
	    ++pci->pti2->ttlactive;
	++ports[0]->ttlactive;
    }

    AgeTraffic();

    pmf = Mfopen(PORT_FILENAME,"w");
    printf("Dumping port statistics into file %s\n", PORT_FILENAME);

    /* dump out the data */
    Mfprintf(pmf,"Overall totals by port\n");
    for (i=0; i < NUM_PORTS; ++i) {
	pti = ports[i];
	if ((pti != EXCLUDE_PORT) && (pti != INCLUDE_PORT)) {
	    if (i == 0)
		Mfprintf(pmf,"TOTAL        ");
	    else
		Mfprintf(pmf,"Port %5u   ", pti->port);
	    Mfprintf(pmf,"\
bytes: %12lu  pkts: %10lu  conns: %8lu  tput: %8lu B/s\n",
		     pti->ttlbytes,
		     pti->ttlpackets,
		     pti->ttlactive,
		     pti->ttlbytes / etime_secs);
	}
    }
    Mfclose(pmf);

    /* dump specific stats */
    pmf = Mfopen(STATS_FILENAME,"w");
    printf("Dumping overall statistics into file %s\n", STATS_FILENAME);

    pti = ports[0];

    Mfprintf(pmf, "\n\nOverall Statistics over %d seconds (%s):\n",
	     etime_secs, elapsed2str(etime));

    /* ttl bytes */
    Mfprintf(pmf, "%" FS_ULL " ttl bytes sent, %.3f bytes/second\n",
	     data_nbytes_all,
	     (float)data_nbytes_all / ((float)etime_secs));

    /* ttl bytes (nonrexmit)*/
    Mfprintf(pmf, "%" FS_ULL " ttl non-rexmit bytes sent, %.3f bytes/second\n",
	     data_nbytes_nonrexmit,
	     (float)data_nbytes_nonrexmit / ((float)etime_secs));

    /* ttl bytes (nonrexmit)*/
    Mfprintf(pmf, "%" FS_ULL " ttl rexmit bytes sent, %.3f bytes/second\n",
	     data_nbytes_all - data_nbytes_nonrexmit,
	     (float)(data_nbytes_all - data_nbytes_nonrexmit) /
	     ((float)etime_secs));

    /* ttl packets */
    Mfprintf(pmf, "%u packets sent, %.3f packets/second\n",
	     pti->ttlpackets,
	     (float)pti->ttlpackets / ((float)etime_secs));

    /* connections opened */
    Mfprintf(pmf, "%u connections opened, %.3f conns/second\n",
	     ttl_num_opens,
	     (float)ttl_num_opens / ((float)etime_secs));

    /* dupacks */
    Mfprintf(pmf, "%u dupacks sent, %.3f dupacks/second\n",
	     ttl_dupacks,
	     (float)ttl_dupacks / ((float)etime_secs));

    /* rexmits */
    Mfprintf(pmf, "%u rexmits sent, %.3f rexmits/second\n",
	     ttl_rexmits,
	     (float)ttl_rexmits / ((float)etime_secs));

    /* RTT */
    Mfprintf(pmf, "average RTT: %.3f msecs\n",
	     ttl_rtt_ttl / (float)ttl_rtt_samples);


    Mfclose(pmf);

    printf("Plotting performed at %.3f second intervals\n", age_interval);

}




void *
traffic_newconn(
    tcp_pair *ptp)
{
    struct conn_info *pci;

    pci = MakeConnRec();
    pci->pti1 = FindPort(ptp->addr_pair.a_port);
    pci->pti2 = FindPort(ptp->addr_pair.b_port);

    return(pci);
}


void
traffic_usage(void)
{
    printf("\t-xtraffic\"[ARGS]\"\tprint info about overall traffic\n");
    printf("\
\t   module argument format:\n\
\t       -iS          set statistics interval to S (float) seconds, default 15.0\n\
\t       -pP          include information on port P\n\
\t       -pP1-P2      include information on ports in the range [P1-P2]\n\
\t       -p-P         exclude information on port P\n\
\t       -p-P1-P2     exclude information on ports in the range [P1-P2]\n\
\t       -pSPEC,SPEC  commas chain together specs\n\
\t       -G           generate all graphs\n\
\t       -A           generate the 'active connections' graph\n\
\t       -B           generate the 'bytes per second' graph\n\
\t       -C           generate the 'opens and closes' graph\n\
\t       -H           generate the 'halfopen connections' graph\n\
\t       -K           generate the 'pure acKs/second' graph\n\
\t       -L           generate the 'losses per second' graph\n\
\t       -O           generate the 'open connections' graph\n\
\t       -I           generate the 'instantaneous open connections' graph\n\
\t       -P           generate the 'packets per second' graph\n\
\t       -Q           generate the 'idle (Quiet) connections' graph\n\
\t       -R[MIN[-MAX]]generate the 'round trip time' graph\n\
\t                    with args, ignore samples outside MIN to MAX (in ms)\n\
\t       -T           generate the 'total data' graph\n\
\t       -D[SECS]     generate the 'long duration connection' graph\n\
\t		      default definition of 'long' is 60 seconds\n\
\t       -d           enable local debugging in this module\n\
\t     Examples\n\
\t       -xtraffic\" -p23\"            only port 23\n\
\t       -xtraffic\" -p1-1023\"        only ports 1-1023\n\
\t       -xtraffic\"-p1-1023,-10-20 -L -O\"  only ports 1-1023, but exclude ports 10-20\n\
\t     With no ports specification, all ports are gathered.  With ANY\n\
\t     spec, all ports are initially EXCLUDED\n\
");
}

static void
ParseArgs(char *argstring)
{
    int argc;
    char **argv;
    static int excluded = 0;
    int i;
    char *pch;
    
    /* 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 (sscanf(argv[i],"-i%f", &interval) == 1) {
	    age_interval = interval;
	    if (ldebug)
		printf("mod_traffic: setting age interval to %.3f seconds\n",
		       age_interval);
	} else if (strcmp(argv[i],"-G") == 0) {
	    doplot_active = TRUE;
	    doplot_idle = TRUE;
	    doplot_data = TRUE;
	    doplot_bytes = TRUE;
	    doplot_loss = TRUE;
	    doplot_long = TRUE;
	    doplot_open = TRUE;
	    doplot_halfopen = TRUE;
	    doplot_openclose = TRUE;
	    doplot_i_open = TRUE;
	    doplot_packets = TRUE;
	    doplot_pureacks = TRUE;
	    if (ldebug)
		fprintf(stderr,
			"mod_traffic: generating all graphs\n");
	} else if (strcmp(argv[i],"-A") == 0) {
	    doplot_active = TRUE;
	    if (ldebug)
		fprintf(stderr,
			"mod_traffic: generating 'active' graph into '%s'\n",
			PLOTTER_ACTIVE_FILENAME);
	} else if (strcmp(argv[i],"-B") == 0) {
	    doplot_bytes = TRUE;
	    if (ldebug)
		fprintf(stderr,
			"mod_traffic: generating 'bytes' graph into '%s'\n",
			PLOTTER_BYTES_FILENAME);
	} else if (strcmp(argv[i],"-H") == 0) {
	    doplot_halfopen = TRUE;
	    if (ldebug)
		fprintf(stderr,
			"mod_traffic: generating 'halfopen' graph into '%s'\n",
			PLOTTER_HALFOPEN_FILENAME);
	} else if (strcmp(argv[i],"-Q") == 0) {
	    doplot_idle = TRUE;
	    if (ldebug)
		fprintf(stderr,
			"mod_traffic: generating 'idle' graph into '%s'\n",
			PLOTTER_IDLE_FILENAME);
	} else if (strcmp(argv[i],"-K") == 0) {
	    doplot_pureacks = TRUE;
	    if (ldebug)
		fprintf(stderr,
			"mod_traffic: generating 'pureacks' graph into '%s'\n",
			PLOTTER_PUREACKS_FILENAME);
	} else if (strcmp(argv[i],"-L") == 0) {
	    doplot_loss = TRUE;
	    if (ldebug)
		fprintf(stderr,
			"mod_traffic: generating 'loss' graph into '%s'\n",
			PLOTTER_LOSS_FILENAME);
	} else if (strcmp(argv[i],"-T") == 0) {
	    doplot_data = TRUE;
	    if (ldebug)
		fprintf(stderr,
			"mod_traffic: generating 'total data' graph into '%s'\n",
			PLOTTER_DATA_FILENAME);
	} else if (strncmp(argv[i],"-D",2) == 0) {
	    doplot_long = TRUE;
	    if (strlen(argv[i]) > 2) {
		/* grab the number */
		longconn_duration = atoi(argv[i]+2);
		if (longconn_duration <= 0) {
		    fprintf(stderr,"bad time value for -LN '%s'\n",
			    argv[i]);
		    exit(-1);
		}

	    }
	    if (ldebug)
		fprintf(stderr,
			"mod_traffic: generating 'long duration' graph (%d secs) into '%s'\n",
			longconn_duration,
			PLOTTER_LONG_FILENAME);
	} else if (strcmp(argv[i],"-O") == 0) {
	    doplot_open = TRUE;
	    if (ldebug)
		fprintf(stderr,
			"mod_traffic: generating 'open' graph into '%s'\n",
			PLOTTER_OPEN_FILENAME);
	} else if (strcmp(argv[i],"-C") == 0) {
	    doplot_openclose = TRUE;
	    if (ldebug)
		fprintf(stderr,
			"mod_traffic: generating 'openclose' graph into '%s'\n",
			PLOTTER_OPENCLOSE_FILENAME);
	} else if (strcmp(argv[i],"-I") == 0) {
	    doplot_i_open = TRUE;
	    if (ldebug)
		fprintf(stderr,
			"mod_traffic: generating 'instantaneous openclose' graph into '%s'\n",
			PLOTTER_I_OPEN_FILENAME);
	} else if (strcmp(argv[i],"-P") == 0) {
	    doplot_packets = TRUE;
	    if (ldebug)
		fprintf(stderr,
			"mod_traffic: generating 'packets' graph into '%s'\n",
			PLOTTER_PACKETS_FILENAME);
	} else if (strncmp(argv[i],"-R",2) == 0) {
	    int nargs;
	    doplot_rtt = TRUE;
	    if (ldebug)
		fprintf(stderr,
			"mod_traffic: generating 'rtt' graph into '%s'\n",
			PLOTTER_RTT_FILENAME);
	    /* check for valid RTT range args */
	    nargs = sscanf(argv[i],"-R%d-%d", &rtt_minvalid,
			   &rtt_maxvalid);
	    switch (nargs) {
	      case 2: {		/* 2 args is min and max */
		  /* sanity check */
		  if (rtt_maxvalid <= rtt_minvalid) {
		      fprintf(stderr,
			      "mod_traffic: Out of order min-max range for -R '%s'\n",
			      argv[i]);
		      traffic_usage();
		      exit(-1);
		  }
		  break;
	      }
	      case 1: {		/* 1 args in min rtt */
		  /* sanity check */
		  if (rtt_maxvalid <= rtt_minvalid) {
		      fprintf(stderr,
			      "mod_traffic: Out of order min-max range for -R '%s'\n",
			      argv[i]);
		      traffic_usage();
		      exit(-1);
		  }
		  break;
	      }
	      case 0: 		/* no args, that's OK */
	      case -1: 		/* (means the same as 0) */
		break;
	      default:		/* illegal args  */
		fprintf(stderr,
			"mod_traffic: Invalid min-max range for -R '%s'\n",
			argv[i]);
		traffic_usage();
		exit(-1);
		break;
	    }
	} else if (strncmp(argv[i],"-p",2) == 0) {
	    pch = argv[i]+2;
	    while (pch && *pch) {
		char *pch_next;
		unsigned port1, port2;

		if ((pch_next = strchr(pch,',')) != NULL) {
		    *pch_next = '\00';
		    ++pch_next;
		}

		if (!excluded) {
		    ExcludePorts(1,NUM_PORTS-1);
		    excluded = 1;
		}

		if (sscanf(pch,"-%u-%u", &port1, &port2) == 2) {
		    ExcludePorts(port1,port2);
		} else if (sscanf(pch,"%u-%u", &port1, &port2) == 2) {
		    IncludePorts(port1,port2);
		} else if (sscanf(pch,"-%u", &port1) == 1) {
		    ExcludePorts(port1,port1);
		} else if (sscanf(pch,"%u", &port1) == 1) {
		    IncludePorts(port1,port1);
		} else {
		    fprintf(stderr,"mod_traffic: Invalid port specification string '%s'\n", pch);
		    traffic_usage();
		    exit(-1);
		}

		pch = pch_next;
	    }
	} else {
	    fprintf(stderr,"Traffic module: bad argument '%s'\n",
		    argv[i]);
	    exit(-1);
	}
    }
}

static void
DoplotIOpen(int port, Bool fopen)
{
    struct traffic_info *pti;

    /* just for this port */
    if ((pti = FindPort(port)) == NULL)
	return;

    if (fopen)
	++pti->n_i_open;
    else
	--pti->n_i_open;

    extend_line(pti->line_niopen, current_time, pti->n_i_open);
}

#endif /* LOAD_MODULE_TRAFFIC */
