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