/* * 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 * * Original Author: Eric Helvey * School of Electrical Engineering and Computer Science * Ohio University * Athens, OH * ehelvey@cs.ohiou.edu * http://www.tcptrace.org/ * Extensively Modified: Shawn Ostermann */ #include "tcptrace.h" static char const GCC_UNUSED rcsid[] = "$Header: /usr/local/cvs/tcptrace/mod_tcplib.c,v 5.32 2003/11/19 14:38:03 sdo Exp $"; #ifdef LOAD_MODULE_TCPLIB /**************************************************************************** * * Module Title: Mod_TCPLib * * Author: Eric Helvey * * Purpose: To generate data files needed by TCPLib and TrafGen. * ****************************************************************************/ #include "mod_tcplib.h" #include "dyncounter.h" /* reading old files is problematic and I never use it anyway!!! it probably doesn't work anymore. sdo - Thu Aug 5, 1999 */ #undef READ_OLD_FILES /* we're no longer interested in the old phone/conv columns */ #undef INCLUDE_PHONE_CONV /* Local global variables */ /* different types of "directions" */ #define NUM_DIRECTION_TYPES 4 enum t_dtype {LOCAL = 0, INCOMING = 1, OUTGOING = 2, REMOTE = 3}; static char *dtype_names[NUM_DIRECTION_TYPES] = {"local","incoming","outgoing", "remote"}; /* structure to keep track of "inside" */ struct insidenode { ipaddr min; ipaddr max; struct insidenode *next; } *inside_head = NULL; #define LOCAL_ONLY (inside_head == NULL) /* for the parallelism hack */ #define BURST_KEY_MAGIC 0x49524720 /* 'I' 'R' 'G' '' */ struct burstkey { unsigned long magic; /* MUST be BURST_KEY_MAGIC */ unsigned long nbytes; /* bytes in burst (INCLUDING this struct) */ unsigned char key; /* one character key to return */ unsigned char unused[3]; /* (explicit padding) */ unsigned long groupnum; /* for keeping track of parallel HTTP */ }; #ifdef BROKEN char *BREAKDOWN_APPS_NAMES[] = { "app 1", "app 2", "app 3", "app 4", "app 5", "app 6", "app 7", "app 8" }; #endif /* BROKEN */ /* for VM efficiency, we pull the info that we want out of the tcptrace structures into THIS structure (or large files thrash) */ typedef struct module_conninfo_tcb { /* cached connection type (incoming, remote, etc) */ enum t_dtype dtype; /* cached data bytes */ u_llong data_bytes; /* * FTP: number of data connections against this control conn * HTTP: * NNTP: number of bursts * HTTP: number of bursts */ u_long numitems; /* burst info */ u_long burst_bytes; /* size of the current burst */ struct burstdata *pburst; /* was the last segment PUSHed? */ Bool last_seg_pushed; /* Thu Aug 26, 1999 - not used */ /* last time new data was sent */ timeval last_data_time; /* link back to REAL information */ tcb *ptcb; /* previous connection of same type */ struct module_conninfo *prev_dtype_all;/* for ALL app types */ struct module_conninfo *prev_dtype_byapp; /* just for THIS app type */ } module_conninfo_tcb; /* structure that this module keeps for each connection */ #define TCB_CACHE_A2B 0 #define TCB_CACHE_B2A 1 #define LOOP_OVER_BOTH_TCBS(var) var=TCB_CACHE_A2B; var<=TCB_CACHE_B2A; ++var typedef struct module_conninfo { /* cached info */ struct module_conninfo_tcb tcb_cache[2]; /* this connection should be ignored for breakdown/convarrival */ Bool ignore_conn; /* breakdown type */ short btype; /* cached copy of address pair */ tcp_pair_addrblock addr_pair; /* link back to the tcb's */ tcp_pair *ptp; /* time of connection start */ timeval first_time; timeval last_time; /* previous connection in linked list of all connections */ struct module_conninfo *prev; /* for parallel http sessions */ struct parallelism *pparallelism; /* unidirectional conns ignored totally */ Bool unidirectional_http; /* for determining bursts */ tcb *tcb_lastdata; /* to determine parallelism in conns, trafgen encodes a group number in the data */ u_long http_groupnum; /* next connection in linked list by endpoint pairs */ struct module_conninfo *next_pair; } module_conninfo; module_conninfo *module_conninfo_tail = NULL; /* data structure to store endpoint pairs */ typedef struct endpoint_pair { /* endpoint identification */ tcp_pair_addrblock addr_pair; /* linked list of connections using that pair */ module_conninfo *pmchead; /* next address pair */ struct endpoint_pair *pepnext; } endpoint_pair; #define ENDPOINT_PAIR_HASHSIZE 1023 /* for tracking burst data */ struct burstdata { dyn_counter nitems; /* total items (bursts) in connection */ dyn_counter size; /* size of the items */ dyn_counter idletime; /* idle time between bursts */ }; /* for tracking number of connections */ struct parallelism { Bool counted[NUM_DIRECTION_TYPES]; /* have we already accumulated this? */ /* (in each of the 4 directions) */ Bool persistant[2]; /* is this persistant (for each TCB) */ u_short maxparallel; /* maximum degree of parallelism */ u_long ttlitems[2]; /* across entire group (each dir) */ }; static struct tcplibstats { /* telnet packet sizes */ dyn_counter telnet_pktsize; /* telnet interarrival times */ dyn_counter telnet_interarrival; /* conversation interarrival times */ dyn_counter conv_interarrival_all; /* protocol-specific interarrival times */ dyn_counter conv_interarrival_byapp[NUM_APPS]; /* conversation duration */ dyn_counter conv_duration; /* for the interval breakdowns */ int interval_count; timeval last_interval; int tcplib_breakdown_interval[NUM_APPS]; /* histogram files */ MFILE *hist_file; /* for NNTP, we track: */ /* # items per connection */ /* idletime between items */ /* burst size */ struct burstdata nntp_bursts; /* for HTTP1.0, we track: */ /* # items per connection */ /* # connections */ /* idletime between items */ /* burst size */ struct burstdata http_P_bursts; dyn_counter http_P_maxconns; /* max degree of concurrency */ dyn_counter http_P_ttlitems; /* ttl items across whole parallel group */ dyn_counter http_P_persistant; /* which parallel groups are persistant */ /* for HTTP1.1, we track: */ /* # items per connection */ /* idletime between items */ /* burst size */ struct burstdata http_S_bursts; /* telnet packet sizes */ dyn_counter throughput; int throughput_bytes; } *global_pstats[NUM_DIRECTION_TYPES] = {NULL}; /* local debugging flag */ static int ldebug = 0; /* parallelism for our TRAFGEN files */ static Bool trafgen_generated = FALSE; /* offset for all ports */ static int ipport_offset = 0; /* the name of the directory (prefix) for the output */ static char *output_dir = DEFAULT_TCPLIB_DATADIR; /* the name of the current tcptrace input file */ static char *current_file = NULL; /* characters to print in interval breakdown file */ static const char breakdown_hash_char[] = { 'S', 'N', 'T', 'F', 'H', 'f'}; /* FTP endpoints hash table */ endpoint_pair *ftp_endpoints[ENDPOINT_PAIR_HASHSIZE]; /* HTTP endpoints hash table */ endpoint_pair *http_endpoints[ENDPOINT_PAIR_HASHSIZE]; /* internal types */ typedef Bool (*f_testinside) (module_conninfo *pmc, module_conninfo_tcb *ptcbc); /* various statistics and counters */ static u_long debug_newconn_counter; /* total conns */ static u_long debug_newconn_badport; /* a port we don't want */ static u_long debug_newconn_goodport; /* we want the port */ static u_long debug_newconn_ftp_data_heuristic; /* merely ASSUMED to be ftp data */ static u_llong debug_total_bytes; /* total "bytes" accepted */ /* parallel http counters */ static u_long debug_http_total; /* all HTTP conns */ static u_long debug_http_parallel; /* parallel HTTP, not counted in breakdown/conv */ static u_long debug_http_single; static u_long debug_http_groups; static u_long debug_http_slaves; static u_long debug_http_uni_conns; /* data in at most one direction, ignored */ static u_llong debug_http_uni_bytes; /* data in at most one direction, ignored */ static u_long debug_http_persistant; static u_long debug_http_nonpersistant; /* conns by type */ static u_long conntype_counter[NUM_DIRECTION_TYPES]; /* both flows have data */ static u_long conntype_duplex_counter[NUM_DIRECTION_TYPES]; /* this flow has data, twin is empty */ static u_long conntype_uni_counter[NUM_DIRECTION_TYPES]; /* this flow has NO data, twin is NOT empty */ static u_long conntype_nodata_counter[NUM_DIRECTION_TYPES]; /* neither this flow OR its twin has data */ static u_long conntype_noplex_counter[NUM_DIRECTION_TYPES]; /* Function Prototypes */ static void ParseArgs(char *argstring); static int breakdown_type(tcp_pair *ptp); static void do_final_breakdown(char* filename, f_testinside p_tester, struct tcplibstats *pstats); static void do_all_final_breakdowns(void); static void do_all_conv_arrivals(void); static void do_tcplib_final_converse(char *filename, char *protocol, dyn_counter psizes); static void do_tcplib_next_converse(module_conninfo_tcb *ptcbc, module_conninfo *pmc); static void do_tcplib_conv_duration(char *filename, dyn_counter psizes); static void do_tcplib_next_duration(module_conninfo_tcb *ptcbc, module_conninfo *pmc); static void tcplib_cleanup_bursts(void); static void tcplib_save_bursts(void); static Bool is_parallel_http(module_conninfo *pmc_new); static void tcplib_filter_http_uni(void); /* prototypes for connection-type determination */ static Bool is_ftp_ctrl_port(portnum port); static Bool is_ftp_data_port(portnum port); static Bool is_http_port(portnum port); static Bool is_nntp_port(portnum port); static Bool is_smtp_port(portnum port); static Bool is_telnet_port(portnum port); /* shorthand */ #define is_ftp_ctrl_conn(pmc) (pmc->btype == TCPLIBPORT_FTPCTRL) #define is_ftp_data_conn(pmc) (pmc->btype == TCPLIBPORT_FTPDATA) #define is_http_conn(pmc) (pmc->btype == TCPLIBPORT_HTTP) #define is_nntp_conn(pmc) (pmc->btype == TCPLIBPORT_NNTP) #define is_smtp_conn(pmc) (pmc->btype == TCPLIBPORT_SMTP) #define is_telnet_conn(pmc) (pmc->btype == TCPLIBPORT_TELNET) static char* namedfile(char *localsuffix, char * file); static void setup_breakdown(void); static void tcplib_add_telnet_interarrival(tcp_pair *ptp, module_conninfo *pmc, dyn_counter *psizes); static void tcplib_add_telnet_packetsize(struct tcplibstats *pstats, int length); static void tcplib_do_ftp_control_size(char *filename, f_testinside p_tester); static void tcplib_do_ftp_itemsize(char *filename, f_testinside p_tester); static void tcplib_do_ftp_numitems(char *filename, f_testinside p_tester); static void tcplib_do_smtp_itemsize(char *filename, f_testinside p_tester); static void tcplib_do_telnet_duration(char *filename, f_testinside p_tester); static void tcplib_do_telnet_interarrival(char *filename, f_testinside p_tester); static void tcplib_do_telnet_packetsize(char *filename, f_testinside p_tester); static void tcplib_init_setup(void); static void update_breakdown(tcp_pair *ptp, struct tcplibstats *pstats); module_conninfo *FindPrevConnection(module_conninfo *pmc, enum t_dtype dtype, int app_type); static char *FormatBrief(tcp_pair *ptp,tcb *ptcb); static char *FormatAddrBrief(tcp_pair_addrblock *addr_pair); static void ModuleConnFillcache(void); /* prototypes for determining "insideness" */ static void DefineInside(char *iplist); static Bool IsInside(ipaddr *pipaddr); static Bool TestOutgoing(module_conninfo*, module_conninfo_tcb *ptcbc); static Bool TestIncoming(module_conninfo*, module_conninfo_tcb *ptcbc); static Bool TestLocal(module_conninfo*, module_conninfo_tcb *ptcbc); static Bool TestRemote(module_conninfo*, module_conninfo_tcb *ptcbc); static int InsideBytes(module_conninfo*, f_testinside); static enum t_dtype traffic_type(module_conninfo *pmc, module_conninfo_tcb *ptcbc); /* prototypes for endpoint pairs */ static void TrackEndpoints(module_conninfo *pmc); static hash EndpointHash(tcp_pair_addrblock *addr_pair); static hash IPHash(ipaddr *paddr); static Bool SameEndpoints(tcp_pair_addrblock *paddr_pair1, tcp_pair_addrblock *paddr_pair2); static struct module_conninfo_tcb *MostRecentFtpControl(endpoint_pair *pep); static Bool CouldBeFtpData(tcp_pair *ptp); static void AddEndpointPair(endpoint_pair *hashtable[], module_conninfo *pmc); static endpoint_pair *FindEndpointPair(endpoint_pair *hashtable[], tcp_pair_addrblock *paddr_pair); static Bool IsNewBurst(module_conninfo *pmc, tcb *ptcb, module_conninfo_tcb *ptcbc, struct tcphdr *tcp); /* various helper routines used by many others -- sdo */ static Bool ActiveConn(module_conninfo *pmc); static Bool RecentlyActiveConn(module_conninfo *pmc); static void tcplib_do_GENERIC_itemsize( char *filename, int btype, f_testinside p_tester, int bucketsize); static void tcplib_do_GENERIC_burstsize( char *filename, dyn_counter counter); static void tcplib_do_GENERIC_P_maxconns( char *filename, dyn_counter counter); static void tcplib_do_GENERIC_nitems( char *filename, dyn_counter counter); static void tcplib_do_GENERIC_idletime( char *filename, dyn_counter counter); static void StoreCounters(char *filename, char *header1, char *header2, int bucketsize, dyn_counter psizes); #ifdef READ_OLD_FILES static dyn_counter ReadOldFile(char *filename, int bucketsize, int maxlegal, dyn_counter psizes); #endif /* READ_OLD_FILES */ /* First section is comprised of functions that TCPTrace will call * for all modules. */ /*************************************************************************** * * Function Name: tcplib_init * * Returns: TRUE/FALSE whether or not the tcplib module for tcptrace * has been requested on the command line. * * Purpose: To parse the command line arguments for the tcplib module's * command line flags, return whether or not to run the module, * and to set up the local global variables needed to generate * the tcplib data files. * * Called by: LoadModules() in tcptrace.c * * ****************************************************************************/ int tcplib_init( int argc, /* Number of command line arguments */ char *argv[] /* Command line arguments */ ) { int i; /* Runner for command line arguments */ int enable = 0; /* Do we turn on this module, or not? */ char *args = NULL; for(i = 0; i < argc; i++) { if (!argv[i]) continue; /* See if they want to use us */ if ((argv[i] != NULL) && (strncmp(argv[i], "-xtcplib", 8) == 0)) { /* Calling the Tcplib part */ enable = 1; args = argv[i]+(sizeof("-xtcplib")-1); printf("Capturing TCPLib traffic\n"); /* We free this argument so that no other modules * or the main program mis-interprets this flag. */ argv[i] = NULL; continue; } } /* If enable is not true, then all tcplib functions will * be ignored during this run of the program. */ if (!enable) return(0); /* don't call me again */ /* parse the encoded args */ ParseArgs(args); /* init internal data */ tcplib_init_setup(); /* don't care for detailed output! */ printsuppress = TRUE; return TRUE; } /* wants strings of the form IP1-IP2 */ static struct insidenode * DefineInsideRange( char *ip_pair) { char *pdash; struct insidenode *pnode; ipaddr *paddr; char *paddr1; char *paddr2; if (ldebug>2) printf("DefineInsideRange('%s') called\n", ip_pair); pdash = strchr(ip_pair,'-'); if (pdash == NULL) { /* just one address, treat it as a range */ paddr1 = ip_pair; paddr2 = ip_pair; } else { /* a pair */ *pdash = '\00'; paddr1 = ip_pair; paddr2 = pdash+1; } pnode = MallocZ(sizeof(struct insidenode)); paddr = str2ipaddr(paddr1); if (paddr == NULL) { fprintf(stderr,"invalid IP address: '%s'\n", paddr1); exit(-1); } pnode->min = *paddr; paddr = str2ipaddr(paddr2); if (paddr == NULL) { fprintf(stderr,"invalid IP address: '%s'\n", paddr2); exit(-1); } pnode->max = *paddr; return(pnode); } static struct insidenode * DefineInsideRecurse( char *iplist) { char *pcomma; struct insidenode *left; /* find commas and recurse */ pcomma = strchr(iplist,','); if (pcomma) { *pcomma = '\00'; left = DefineInsideRecurse(iplist); left->next = DefineInsideRecurse(pcomma+1); return(left); } else { /* just one term left */ return(DefineInsideRange(iplist)); } } static void DefineInside( char *iplist) { if (ldebug>2) printf("DefineInside(%s) called\n", iplist); inside_head = DefineInsideRecurse(iplist); if (ldebug) { struct insidenode *phead; printf("DefineInside: result:\n "); for (phead=inside_head; phead; phead=phead->next) { printf("(%s <= addr", HostAddr(phead->min)); printf(" <= %s)", HostAddr(phead->max)); if (phead->next) printf(" OR "); } printf("\n"); } } static Bool IsInside( ipaddr *paddr) { struct insidenode *phead; /* if use didn't specify "inside", then EVERYTHING is "inside" */ if (LOCAL_ONLY) return(TRUE); for (phead = inside_head; phead; phead=phead->next) { int cmp1 = IPcmp(&phead->min, paddr); int cmp2 = IPcmp(&phead->max, paddr); if ((cmp1 == -2) || (cmp2 == -2)) { /* not all the same address type, fail */ return(FALSE); } if ((cmp1 <= 0) && /* min <= addr */ (cmp2 >= 0)) /* max >= addr */ return(TRUE); } return(FALSE); } static Bool TestOutgoing( module_conninfo *pmc, module_conninfo_tcb *ptcbc) { if (ptcbc == &pmc->tcb_cache[TCB_CACHE_A2B]) return( IsInside(&pmc->addr_pair.a_address) && !IsInside(&pmc->addr_pair.b_address)); else return( IsInside(&pmc->addr_pair.b_address) && !IsInside(&pmc->addr_pair.a_address)); } static Bool TestIncoming( module_conninfo *pmc, module_conninfo_tcb *ptcbc) { if (ptcbc == &pmc->tcb_cache[TCB_CACHE_A2B]) return(!IsInside(&pmc->addr_pair.a_address) && IsInside(&pmc->addr_pair.b_address)); else return(!IsInside(&pmc->addr_pair.b_address) && IsInside(&pmc->addr_pair.a_address)); } static Bool TestLocal( module_conninfo *pmc, module_conninfo_tcb *ptcbc) { return(IsInside(&pmc->addr_pair.a_address) && IsInside(&pmc->addr_pair.b_address)); } static Bool TestRemote( module_conninfo *pmc, module_conninfo_tcb *ptcbc) { return(!IsInside(&pmc->addr_pair.a_address) && !IsInside(&pmc->addr_pair.b_address)); } static int InsideBytes( module_conninfo *pmc, f_testinside p_tester) /* function to test "insideness" */ { int temp = 0; int dir; for (LOOP_OVER_BOTH_TCBS(dir)) { /* if "p_tester" likes this side of the connection, count the bytes */ if ((*p_tester)(pmc, &pmc->tcb_cache[dir])) temp += pmc->tcb_cache[dir].data_bytes; } return(temp); } 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) { /* The "-o####" flag sets the offset that we're going * to consider for tcplib data files. The reason is that * for verification purposes, when trafgen creates traffic * it sends it to non-standard ports. So, in order to get * a data set from generated traffic, we'd have to remove * the offset. The -o allows us to do that. */ if (argv[i] && !strncmp(argv[i], "-o", 2)) { ipport_offset = atoi(argv[i]+2); if (!ipport_offset) { fprintf(stderr, "\ Invalid argument to flag \"-o\".\n\ Must be integer value greater than 0.\n"); exit(1); } printf("TCPLib port offset - %d\n", ipport_offset); } /* The "-iIPs" gives the definition of "inside". When it's used, * we divide the the data into four sets: * data.incoming: * for all data flowing from "inside" to "outside" * data.outgoing: * for all data flowing from "outside" to "inside" * data.local: * for all data flowing from "inside" to "inside" * data.remote: * for all data flowing from "outside" to "outside" * (probably an error) */ else if (argv[i] && !strncmp(argv[i], "-i", 2)) { if (!isdigit((int)*(argv[i]+2))) { fprintf(stderr,"-i requires IP address list\n"); tcplib_usage(); exit(-1); } DefineInside(argv[i]+2); } /* parallelism hack */ else if (argv[i] && !strncmp(argv[i], "-H", 2)) { trafgen_generated = TRUE; } /* local debugging flag */ else if (argv[i] && !strncmp(argv[i], "-d", 2)) { ++ldebug; } /* We will probably need to add another flag here to * specify the directory in which to place the data * files. And here it is. */ else if (argv[i] && !strncmp(argv[i], "-D", 2)) { char *pdir = argv[i]+2; if (!pdir) { fprintf(stderr,"argument -DDIR requires directory name\n"); exit(-1); } output_dir = strdup(pdir); printf("TCPLib output directory - %sdata\n", output_dir); } /* ... else invalid */ else { fprintf(stderr,"tcplib module: bad argument '%s'\n", argv[i]); exit(-1); } } } static void tcplib_save_bursts() { int dtype; module_conninfo *pmc; int non_parallel = 0; char *filename; tcplib_cleanup_bursts(); /* accumulate parallelism stats */ for (dtype=0; dtype < NUM_DIRECTION_TYPES; ++dtype) { non_parallel = 0; for (pmc = module_conninfo_tail; pmc; pmc = pmc->prev) { struct parallelism *pp = pmc->pparallelism; int dir; /* make sure it's http */ if (!is_http_conn(pmc)) continue; /* ignore unidirectional */ if (pmc->unidirectional_http) continue; /* check each TCB */ for (LOOP_OVER_BOTH_TCBS(dir)) { module_conninfo_tcb *ptcbc = &pmc->tcb_cache[dir]; /* make sure it's -- the right direction -- and parallel -- not already counted */ if (ptcbc->dtype != dtype) continue; if (pp == NULL) { ++non_parallel; continue; } if (pp->counted[dtype]) continue; /* count the max connections */ AddToCounter(&global_pstats[dtype]->http_P_maxconns, pp->maxparallel, 1, 1); /* count the ttl items in the parallel group */ AddToCounter(&global_pstats[dtype]->http_P_ttlitems, pp->ttlitems[dir], 1, GRAN_NUMITEMS); /* binary counter, one sample of either: */ /* 1: NOT persistant */ /* 2: persistant */ AddToCounter(&global_pstats[dtype]->http_P_persistant, pp->persistant[dir]?2:1, 1, 1); /* debugging */ if (pp->persistant[dir]) ++debug_http_persistant; else ++debug_http_nonpersistant; /* don't count it again! */ pmc->pparallelism->counted[dtype] = TRUE; } } /* add the NON-parallel HTTP to the counter */ AddToCounter(&global_pstats[dtype]->http_P_maxconns, 1, non_parallel, 1); } /* write all the counters */ for (dtype=0; dtype < NUM_DIRECTION_TYPES; ++dtype) { if (ldebug>1) printf("tcplib: running burstsizes (%s)\n", dtype_names[dtype]); /* ---------------------*/ /* Burstsize */ /* ---------------------*/ /* HTTP 1.0 */ filename = namedfile(dtype_names[dtype],TCPLIB_HTTP_P_BURSTSIZE_FILE); tcplib_do_GENERIC_burstsize(filename, global_pstats[dtype]->http_P_bursts.size); /* HTTP 1.1 */ filename = namedfile(dtype_names[dtype],TCPLIB_HTTP_S_BURSTSIZE_FILE); tcplib_do_GENERIC_burstsize(filename, global_pstats[dtype]->http_S_bursts.size); /* NNTP */ filename = namedfile(dtype_names[dtype],TCPLIB_NNTP_BURSTSIZE_FILE); tcplib_do_GENERIC_burstsize(filename, global_pstats[dtype]->nntp_bursts.size); /* ---------------------*/ /* Total parallel items */ /* ---------------------*/ /* HTTP 1.0 */ filename = namedfile(dtype_names[dtype],TCPLIB_HTTP_P_TTLITEMS_FILE); tcplib_do_GENERIC_nitems(filename, global_pstats[dtype]->http_P_ttlitems); /* ---------------------*/ /* Num Items in Burst */ /* ---------------------*/ /* HTTP 1.1 */ filename = namedfile(dtype_names[dtype],TCPLIB_HTTP_S_NITEMS_FILE); tcplib_do_GENERIC_nitems(filename, global_pstats[dtype]->http_S_bursts.nitems); /* NNTP */ filename = namedfile(dtype_names[dtype],TCPLIB_NNTP_NITEMS_FILE); tcplib_do_GENERIC_nitems(filename, global_pstats[dtype]->nntp_bursts.nitems); /* ---------------------*/ /* Idletime */ /* ---------------------*/ /* HTTP 1.0 */ filename = namedfile(dtype_names[dtype],TCPLIB_HTTP_P_IDLETIME_FILE); tcplib_do_GENERIC_idletime(filename, global_pstats[dtype]->http_P_bursts.idletime); /* HTTP 1.1 */ filename = namedfile(dtype_names[dtype],TCPLIB_HTTP_S_IDLETIME_FILE); tcplib_do_GENERIC_idletime(filename, global_pstats[dtype]->http_S_bursts.idletime); /* NNTP */ filename = namedfile(dtype_names[dtype],TCPLIB_NNTP_IDLETIME_FILE); tcplib_do_GENERIC_idletime(filename, global_pstats[dtype]->nntp_bursts.idletime); /* store the counters */ filename = namedfile(dtype_names[dtype],TCPLIB_HTTP_P_MAXCONNS_FILE); tcplib_do_GENERIC_P_maxconns(filename, global_pstats[dtype]->http_P_maxconns); /* store the persistance */ filename = namedfile(dtype_names[dtype],TCPLIB_HTTP_P_PERSIST_FILE); tcplib_do_GENERIC_nitems(filename, global_pstats[dtype]->http_P_persistant); if (LOCAL_ONLY) break; } } /*************************************************************************** * * Function Name: tcplib_done * * Returns: Nothing * * Purpose: This function runs after all the packets have been read in * and filed. The functions that tcplib_done calls are the ones * that generate the data files. * * Called by: FinishModules() in tcptrace.c * * ****************************************************************************/ static void RunAllFour( void (*f_runme) (char *,f_testinside), char *thefile) { char *filename; filename = namedfile("local",thefile); (*f_runme)(filename,TestLocal); if (LOCAL_ONLY) return; /* none of the rest will match anyway */ filename = namedfile("incoming",thefile); (*f_runme)(filename,TestIncoming); filename = namedfile("outgoing",thefile); (*f_runme)(filename,TestOutgoing); filename = namedfile("remote",thefile); (*f_runme)(filename,TestRemote); } void tcplib_done() { char *filename; int i; /* fill the info cache */ if (ldebug) printf("tcplib: completing data structure\n"); ModuleConnFillcache(); /* do TELNET */ if (ldebug) printf("tcplib: running telnet\n"); RunAllFour(tcplib_do_telnet_packetsize,TCPLIB_TELNET_PACKETSIZE_FILE); RunAllFour(tcplib_do_telnet_interarrival,TCPLIB_TELNET_INTERARRIVAL_FILE); RunAllFour(tcplib_do_telnet_duration,TCPLIB_TELNET_DURATION_FILE); /* do FTP */ if (ldebug) printf("tcplib: running ftp\n"); RunAllFour(tcplib_do_ftp_control_size,TCPLIB_FTP_CTRLSIZE_FILE); RunAllFour(tcplib_do_ftp_itemsize,TCPLIB_FTP_ITEMSIZE_FILE); RunAllFour(tcplib_do_ftp_numitems,TCPLIB_FTP_NITEMS_FILE); /* do SMTP */ if (ldebug) printf("tcplib: running smtp\n"); RunAllFour(tcplib_do_smtp_itemsize,TCPLIB_SMTP_ITEMSIZE_FILE); /* do NNTP */ if (ldebug) printf("tcplib: running nntp\n"); /* do HTTP */ if (ldebug) printf("tcplib: running http\n"); /* filter out the unidirectional HTTP (server pushes) */ tcplib_filter_http_uni(); /* for efficiency, do all burst size stuff together */ if (ldebug) printf("tcplib: running burstsizes\n"); tcplib_save_bursts(); /* do the breakdown stuff */ if (ldebug) printf("tcplib: running breakdowns\n"); do_all_final_breakdowns(); /* do the conversation interrival time */ if (ldebug) printf("tcplib: running conversation interarrival times\n"); do_all_conv_arrivals(); for (i=0; i < NUM_DIRECTION_TYPES; ++i) { if (ldebug>1) printf("tcplib: running conversation arrivals (%s)\n", dtype_names[i]); filename = namedfile(dtype_names[i],TCPLIB_NEXT_CONVERSE_FILE); do_tcplib_final_converse(filename, "total", global_pstats[i]->conv_interarrival_all); #ifdef BROKEN /* do the application-specific tables for Mark */ for (j=0; j < NUM_APPS; ++j) { char new_filename[128]; char *app_name = BREAKDOWN_APPS_NAMES[j]; snprintf(new_filename,sizeof(new_filename),"%s_%s", filename, app_name); do_tcplib_final_converse(new_filename, app_name, global_pstats[i]->conv_interarrival_byapp[j]); } #endif /* BROKEN */ /* do conversation durations */ filename = namedfile(dtype_names[i],TCPLIB_CONV_DURATION_FILE); do_tcplib_conv_duration(filename, global_pstats[i]->conv_duration); if (LOCAL_ONLY) break; } /* print stats */ debug_http_single = debug_http_total - debug_http_parallel; printf("tcplib: total connections seen: %lu (%lu accepted, %lu bad port)\n", debug_newconn_counter, debug_newconn_goodport, debug_newconn_badport); printf("tcplib: total bytes seen: %" FS_ULL "\n", debug_total_bytes); printf("tcplib: %lu random connections accepted under FTP data heuristic\n", debug_newconn_ftp_data_heuristic); printf("tcplib: %lu HTTP conns (%lu parallel, %lu single)\n", debug_http_total, debug_http_parallel, debug_http_single); printf("tcplib: %lu HTTP conns (%lu single, %lu leaders, %lu slaves)\n", debug_http_total, debug_http_single, debug_http_groups, debug_http_slaves); printf("tcplib: %lu groups (%lu persistant ||, %lu nonpersistant ||)\n", debug_http_groups, debug_http_persistant, debug_http_nonpersistant); printf("tcplib: %lu (%.2f%%) unidir. HTTP conns (%" FS_ULL " bytes, %.2f%%) ignored\n", debug_http_uni_conns, 100.0 * ((float)debug_http_uni_conns / (float)(debug_newconn_counter + debug_http_uni_conns)), debug_http_uni_bytes, 100.0 * ((float)debug_http_uni_bytes / (float)(debug_total_bytes + debug_http_uni_bytes))); for (i=0; i < NUM_DIRECTION_TYPES; ++i) { printf(" Flows of type %-8s %5lu (%lu duplex, %lu noplex, %lu unidir, %lu nodata)\n", dtype_names[i], conntype_counter[i], conntype_duplex_counter[i], conntype_noplex_counter[i], conntype_uni_counter[i], conntype_nodata_counter[i]); } /* dump HTTP groups for debugging */ { module_conninfo *pmc; printf("Group Numbers for HTTP conns\n"); for (pmc = module_conninfo_tail; pmc; pmc = pmc->prev) { tcb *ptcb = pmc->tcb_cache[TCB_CACHE_A2B].ptcb; if (pmc->btype != TCPLIBPORT_HTTP) continue; if (pmc->unidirectional_http) continue; printf("%s: %30s\tGROUPNUM %5lu\tdata %" FS_ULL ":%" FS_ULL "\n", ts2ascii(&ptcb->ptp->first_time), FormatBrief(ptcb->ptp, ptcb), pmc->http_groupnum, pmc->tcb_cache[0].data_bytes, pmc->tcb_cache[1].data_bytes); } printf("Unidirectional HTTP conns (ignored)\n"); for (pmc = module_conninfo_tail; pmc; pmc = pmc->prev) { tcb *ptcb = pmc->tcb_cache[TCB_CACHE_A2B].ptcb; if (pmc->btype != TCPLIBPORT_HTTP) continue; if (!pmc->unidirectional_http) continue; printf("%s: %30s\tGROUPNUM %5lu\tdata %" FS_ULL ":%" FS_ULL "\n", ts2ascii(&ptcb->ptp->first_time), FormatBrief(ptcb->ptp, ptcb), pmc->http_groupnum, pmc->tcb_cache[0].data_bytes, pmc->tcb_cache[1].data_bytes); } } return; } /*************************************************************************** * * Function Name: tcplib_read * * Returns: Nothing * * Purpose: This function is called each time a packet is read in by * tcptrace. tcplib_read examines the packet, and keeps track * of certain information about the packet based on the packet's * source and/or destination ports. * * Called by: ModulesPerPacket() in tcptrace.c * * ****************************************************************************/ void tcplib_read( struct ip *pip, /* The packet */ tcp_pair *ptp, /* The pair of hosts - basically the conversation */ void *plast, /* Unused here */ void *pmodstruct /* Nebulous structure used to hold data that the module * feels is important. */ ) { struct tcphdr *tcp; /* TCP header information */ int data_len = 0; /* Length of the data cargo in the packet, and * the period of time between the last two packets * in a conversation */ tcb *ptcb; module_conninfo_tcb *ptcbc; struct tcplibstats *pstats; module_conninfo *pmc = pmodstruct; enum t_dtype dtype; int dir; /* first, discard any connections that we aren't interested in. */ /* That means that pmodstruct is NULL */ if (pmc == NULL) { return; } /* Setting a pointer to the beginning of the TCP header */ tcp = (struct tcphdr *) ((char *)pip + (4 * IP_HL(pip))); /* calculate the amount of user data */ data_len = pip->ip_len - /* size of entire IP packet (and IP header) */ (4 * IP_HL(pip)) - /* less the IP header */ (4 * TH_OFF(tcp)); /* less the TCP header */ /* stats */ debug_total_bytes += data_len; /* see which of the 2 TCB's this goes with */ if (ptp->addr_pair.a_port == ntohs(tcp->th_sport)) { ptcb = &ptp->a2b; dir = TCB_CACHE_A2B; } else { ptcb = &ptp->b2a; dir = TCB_CACHE_B2A; } ptcbc = &pmc->tcb_cache[dir]; /* see where to keep the stats */ dtype = traffic_type(pmc,ptcbc); pstats = global_pstats[dtype]; /* Let's do the telnet packet sizes. Telnet packets are the only * ones where we actually care about the sizes of individual packets. * All the other connection types are a "send as fast as possible" * kind of setup where the packet sizes are always optimal. Because * of this, we need the size of each and every telnet packet that * comes our way. */ if (is_telnet_conn(pmc)) { if (data_len > 0) { if (ldebug>2) printf("read: adding %d byte telnet packet to %s\n", data_len, dtype_names[dtype]); tcplib_add_telnet_packetsize(pstats,data_len); } } /* Here's where we'd need to do telnet interarrival times. The * same basic scenario applies with telnet packet interarrival * times. Because telnet type traffic is "stop and go", we need * to be able to model how long the "stops" are. So we measure * the time in between successive packets in a single telnet * conversation. */ if (is_telnet_conn(pmc)) { tcplib_add_telnet_interarrival( ptp, pmc, &pstats->telnet_interarrival); } /* keep track of bytes/second too */ if (data_len > 0) { static timeval last_time = {0,0}; unsigned etime; /* accumulate total bytes */ pstats->throughput_bytes += data_len; /* elapsed time in milliseconds */ etime = (int)(elapsed(last_time, current_time)/1000.0); /* every 15 seconds, gather throughput stats */ if (etime > 15000) { AddToCounter(&pstats->throughput, pstats->throughput_bytes, etime, 1024); pstats->throughput_bytes = 0; last_time = current_time; } } /* create data for traffic breakdown over time file */ /* (sdo - only count packets with DATA) */ if (data_len > 0) { int a2b_btype = pmc->btype; if (a2b_btype != TCPLIBPORT_NONE) { pstats->tcplib_breakdown_interval[a2b_btype] += ptp->a2b.data_bytes; } } /* if it's http and we don't already know that it's parallel, and this is the first data, and we're looking at trafgen data, check the group number encoded in the data stream */ if (is_http_conn(pmc) && trafgen_generated && (ptcb->data_bytes == data_len) && data_len >= sizeof(struct burstkey)) { u_char *pdata = (u_char *)tcp + TH_OFF(tcp)*4; int available = (char *)plast - (char *)pdata + 1; struct burstkey *pburst; if (0) printf("Looking for burst key (in %d bytes of data, %d bytes available)\n", data_len, available); /* see where the burst key should be */ pburst = (void *) pdata; if (pburst->magic == BURST_KEY_MAGIC) { pmc->http_groupnum = ntohl(pburst->groupnum); if (ldebug>1) printf("FOUND BURST KEY in %s, group num is %lu!\n", FormatBrief(ptcb->ptp, ptcb), pmc->http_groupnum); /* check for parallelism */ if (is_parallel_http(pmc)) { pmc->ignore_conn = TRUE; } } } /* DATA Burst checking (NNTP and HTTP only) */ if ((data_len > 0) && (is_nntp_conn(pmc) || is_http_conn(pmc))) { /* see if it's a new burst */ if (IsNewBurst(pmc, ptcb, ptcbc, tcp)) { int etime; if (ldebug > 1) printf("New burst starts at time %s for %s\n", ts2ascii(¤t_time), FormatBrief(pmc->ptp,ptcb)); /* count the PREVIOUS burst item */ /* NB: the last is counted in tcplib_cleanup_bursts() */ ++ptcbc->numitems; /* special burst handling for HTTP */ if (is_http_conn(pmc) && pmc->pparallelism) { struct parallelism *pp = pmc->pparallelism; if (ptcbc->numitems > 1) { pp->persistant[dir] = TRUE; } /* add to total bursts in parallel group */ ++pp->ttlitems[dir]; } /* accumulate burst size stats */ if (ldebug>1) printf("Adding burst size %ld to %s\n", ptcbc->burst_bytes, FormatBrief(pmc->ptp,ptcb)); AddToCounter(&ptcbc->pburst->size, ptcbc->burst_bytes, 1, GRAN_BURSTSIZE); /* reset counter for next burst */ ptcbc->burst_bytes = 0; /* determine idle time (elapsed time in milliseconds) */ etime = (int)(elapsed(ptcbc->last_data_time, current_time)/1000.0); /* accumulate idletime stats */ /* version 2.0 - Thu Aug 26, 1999, subtract the RTT */ /* use rtt_last, RTT of last "good ack" */ if (ptcb->rtt_last > 0.0) { int last_good_rtt = ptcb->rtt_last / 1000.0; #ifdef OLD printf("last_good: %d (%f), etime_0: %d etime_1: %d\n", last_good_rtt, ptcb->rtt_last, etime, etime-last_good_rtt); #endif /* OLD */ etime -= last_good_rtt; if (etime >= 0) AddToCounter(&ptcbc->pburst->idletime, etime, 1, GRAN_BURSTIDLETIME); } } /* accumulate size of current burst */ ptcbc->burst_bytes += data_len; /* remember when the last data was sent (for idletime) */ ptcbc->last_data_time = current_time; } /* This is just a sanity check to make sure that we've got at least * one time, and that our breakdown section is working on the same * file that we are. */ data_len = (current_time.tv_sec - pstats->last_interval.tv_sec); if (data_len >= TIMER_VAL) { update_breakdown(ptp, pstats); } /* analysis done, remember the last packet and PUSH status */ pmc->last_time = current_time; ptcbc->last_seg_pushed = PUSH_SET(tcp); return; } /****************************************************************** * * fill the tcb cache, make the 'previous' linked lists * ******************************************************************/ static void ModuleConnFillcache( void) { module_conninfo *pmc; enum t_dtype dtype; int dir; /* fill the cache */ for (pmc = module_conninfo_tail; pmc ; pmc=pmc->prev) { tcp_pair *ptp = pmc->ptp; /* shorthand */ int a2b_bytes = ptp->a2b.data_bytes; int b2a_bytes = ptp->b2a.data_bytes; /* both sides byte counters */ pmc->tcb_cache[TCB_CACHE_A2B].data_bytes = a2b_bytes; pmc->tcb_cache[TCB_CACHE_B2A].data_bytes = b2a_bytes; /* debugging stats */ if ((a2b_bytes == 0) && (b2a_bytes == 0)) { /* no bytes at all */ ++conntype_noplex_counter[pmc->tcb_cache[TCB_CACHE_A2B].dtype]; ++conntype_noplex_counter[pmc->tcb_cache[TCB_CACHE_B2A].dtype]; } else if ((a2b_bytes != 0) && (b2a_bytes == 0)) { /* only A2B has bytes */ ++conntype_uni_counter[pmc->tcb_cache[TCB_CACHE_A2B].dtype]; ++conntype_nodata_counter[pmc->tcb_cache[TCB_CACHE_B2A].dtype]; } else if ((a2b_bytes == 0) && (b2a_bytes != 0)) { /* only B2A has bytes */ ++conntype_nodata_counter[pmc->tcb_cache[TCB_CACHE_A2B].dtype]; ++conntype_uni_counter[pmc->tcb_cache[TCB_CACHE_B2A].dtype]; } else { /* both sides have bytes */ ++conntype_duplex_counter[pmc->tcb_cache[TCB_CACHE_A2B].dtype]; ++conntype_duplex_counter[pmc->tcb_cache[TCB_CACHE_B2A].dtype]; } /* globals */ pmc->last_time = ptp->last_time; } for (dtype = LOCAL; dtype <= REMOTE; ++dtype) { /* do the A sides, then the B sides */ for (LOOP_OVER_BOTH_TCBS(dir)) { int app; if (ldebug>1) printf(" Making previous for %s, side %s\n", dtype_names[dir], (dir==TCB_CACHE_A2B)?"A":"B"); /* do conversation interravial calculations by app */ for (app=-1; app <= NUM_APPS; ++app) { /* note: app==-1 means ALL apps */ for (pmc = module_conninfo_tail; pmc ; ) { module_conninfo_tcb *ptcbc = &pmc->tcb_cache[dir]; /* if app != -1, we just want SOME of them */ if ((app != -1) && (pmc->btype != app)) { /* don't want this one, try the next one */ pmc = pmc->prev; continue; } if (ptcbc->dtype == dtype) { module_conninfo *prev = FindPrevConnection(pmc,dtype,app); if (app == -1) ptcbc->prev_dtype_all = prev; else ptcbc->prev_dtype_byapp = prev; pmc = prev; } else { pmc = pmc->prev; } } } } } } /****************************************************************** * * to improve efficiency, we try to keep all of these on the * same virual pages * ******************************************************************/ static module_conninfo * NewModuleConn() { #define CACHE_SIZE 128 static module_conninfo *pcache[CACHE_SIZE]; static int num_cached = 0; module_conninfo *p; if (num_cached == 0) { int i; char *ptmp; ptmp = MallocZ(CACHE_SIZE*sizeof(module_conninfo)); for (i=0; i < CACHE_SIZE; ++i) { pcache[i] = (module_conninfo *)ptmp; ptmp += sizeof(module_conninfo); } num_cached = CACHE_SIZE; } /* grab one from the cache and return it */ p = pcache[--num_cached]; return(p); } /*************************************************************************** * * Function Name: tcplib_newconn * * Returns: The time of this connection. This becomes the pmodstruct that * is returned with each call to tcplib_read. * * Purpose: To setup and handle new connections. * * Called by: ModulesPerConn() in tcptrace.c * * ****************************************************************************/ void * tcplib_newconn( tcp_pair *ptp) /* This conversation */ { int btype; /* breakdown type */ module_conninfo *pmc; /* Pointer to a timeval structure. The * timeval structure becomes the time of * the last connection. The pmc * is tcptrace's way of allowing modules * to keep track of information about * connections */ /* trafgen only uses a few ports... */ if (trafgen_generated) { u_short server_port = ptp->addr_pair.b_port; if ((server_port < ipport_offset+IPPORT_FTP_DATA) || (server_port > ipport_offset+IPPORT_NNTP)) { ++debug_newconn_badport; return(NULL); } } /* verify that it's a connection we're interested in! */ ++debug_newconn_counter; btype = breakdown_type(ptp); if (btype == TCPLIBPORT_NONE) { ++debug_newconn_badport; return(NULL); /* so we won't get it back in tcplib_read() */ } else { /* else, it's acceptable, count it */ ++debug_newconn_goodport; } /* create the connection-specific data structure */ pmc = NewModuleConn(); pmc->first_time = current_time; pmc->ptp = ptp; pmc->tcb_cache[TCB_CACHE_A2B].ptcb = &ptp->a2b; pmc->tcb_cache[TCB_CACHE_B2A].ptcb = &ptp->b2a; /* cache the address info */ pmc->addr_pair = ptp->addr_pair; /* determine its "insideness" */ pmc->tcb_cache[TCB_CACHE_A2B].dtype = traffic_type(pmc, &pmc->tcb_cache[TCB_CACHE_A2B]); pmc->tcb_cache[TCB_CACHE_B2A].dtype = traffic_type(pmc, &pmc->tcb_cache[TCB_CACHE_B2A]); ++conntype_counter[pmc->tcb_cache[TCB_CACHE_A2B].dtype]; ++conntype_counter[pmc->tcb_cache[TCB_CACHE_B2A].dtype]; /* determine the breakdown type */ pmc->btype = btype; /* chain it in */ pmc->prev = module_conninfo_tail; module_conninfo_tail = pmc; /* setup the burst counter shorthand */ if ((btype == TCPLIBPORT_NNTP) || (btype == TCPLIBPORT_HTTP)) { module_conninfo_tcb *ptcbc; struct tcplibstats *pstats; int dir; /* printf("NewConn, saw btype %d for %s\n", btype, */ /* FormatBrief(ptp)); */ for (LOOP_OVER_BOTH_TCBS(dir)) { ptcbc = &pmc->tcb_cache[dir]; pstats = global_pstats[ptcbc->dtype]; if (btype == TCPLIBPORT_NNTP) { ptcbc->pburst = &pstats->nntp_bursts; } else if (btype == TCPLIBPORT_HTTP) { /* assume 1.1 unless we see parallelism later */ ptcbc->pburst = &pstats->http_S_bursts; } ptcbc->last_data_time = current_time; } } /* debugging counter */ if (btype == TCPLIBPORT_HTTP) ++debug_http_total; /* add to list of endpoints we track */ TrackEndpoints(pmc); /* if it's NOT trafgen generated and it's HTTP, check if it's parallel */ if (is_http_conn(pmc) && !trafgen_generated) { if (is_parallel_http(pmc)) { pmc->ignore_conn = TRUE; } } return (pmc); } /*************************************************************************** * * Function Name: tcplib_newfile * * Returns: Nothing * * Purpose: This function is called by tcptrace every time that a new * trace file is opened. tcplib_newfile basically sets up a new * line in the breakdown file, so that we can get a picture of * the traffic distribution for a single trace. * * Called by: ModulesPerFile() in tcptrace.c * * ****************************************************************************/ void tcplib_newfile( char *filename, /* Name of the file just opened. */ u_long filesize, Bool fcompressed ) { static int first_file = TRUE; /* If this isn't the first file that we've seen this run, then * we want to run do_final_breakdown on the file we ran BEFORE * this one. */ if (!first_file) { do_all_final_breakdowns(); free(current_file); } else { /* If this is the first file we've seen, then we just want to * record the name of this file, and do nothing until the file * is done. */ printf("%s", filename); first_file = FALSE; } /* remember the current file name */ current_file = (char *) strdup(filename); setup_breakdown(); return; } /*************************************************************************** * * Function Name: tcplib_usage * * Returns: Nothing * * Purpose: To print out usage instructions for this module. * * Called by: ListModules() in tcptrace.c * * ****************************************************************************/ void tcplib_usage() { printf("\ \t-xtcplib\"[ARGS]\"\tgenerate tcplib-format data files from trace\n"); printf("\ \t -oN set port offset to N, default is 0\n\ \t for example, we normally find telnet at 23, but\n\ \t if it's at 9023, then use \"-o9000\"\n\ \t -iIPLIST\n\ \t define the IP addresses which are \"inside\". Format allows\n\ \t ranges and commas, as in:\n\ \t -i128.1.0.0-128.2.255.255\n\ \t -i128.1.0.0-128.2.255.255,192.10.1.0-192.10.2.240\n\ \t -H use hacks to find data from trafgen-generated files\n\ \t -DDIR store the results in directory DIR, default is \"data\"\n\ "); } /* End of the tcptrace standard function section */ /*************************************************************************** * * Function Name: tcplib_init_setup * * Returns: Nothing * * Purpose: To setup and initialize the tcplib module's set of * global variables. * * Called by: tcplib_init() in mod_tcplib.c * * ****************************************************************************/ static void tcplib_init_setup(void) { int i; /* Loop Counter */ enum t_dtype ix; struct tcplibstats *pstats; /* We need to save the contents in order to piece together the answers * later on * * sdo - so why does Eric turn it OFF? */ save_tcp_data = FALSE; for (ix = LOCAL; ix <= REMOTE; ++ix) { /* create the big data structure */ global_pstats[ix] = pstats = MallocZ(sizeof(struct tcplibstats)); for(i = 0; i < NUM_APPS; i++) { pstats->tcplib_breakdown_interval[i] = 0; } } setup_breakdown(); return; } /*************************************************************************** * * Function Name: setup_breakdown * * Returns: Nothing * * Purpose: To open the traffic breakdown graph file, and to set the * interval count. * * Called by: tcplib_init_setup() in mod_tcplib.c * tcplib_newfile() in mod_tcplib.c * * ****************************************************************************/ static void setup_breakdown(void) { int ix; for (ix = LOCAL; ix <= REMOTE; ++ix) { struct tcplibstats *pstats = global_pstats[ix]; char *prefix = dtype_names[ix]; char *filename = namedfile(prefix,TCPLIB_BREAKDOWN_GRAPH_FILE); if (!(pstats->hist_file = Mfopen(filename, "w"))) { perror(filename); exit(1); } pstats->interval_count = 0; } } /*************************************************************************** * * Function Name: update_breakdown * * Returns: Nothing * * Purpose: To create a file containing a kind of histogram of traffic * seen in this file. The histogram would contain one row per * a set # of seconds, and would display one characteristic * character per a specified number of bytes. * * Called by: tcplib_read() in mod_tcplib.c * * ****************************************************************************/ static void update_breakdown( tcp_pair *ptp, /* This conversation */ struct tcplibstats *pstats) { int i; /* Looping variable */ int count; /* Displays the interval number. A new histogram line is displayed * at TIMER_VALUE seconds. */ Mfprintf(pstats->hist_file, "%d\t", pstats->interval_count); /* Display some characters for each type of traffic */ for(i = 0; i < NUM_APPS; i++) { struct tcplibstats *pstats = global_pstats[LOCAL]; /* We'll be displaying one character per BREAKDOWN_HASH number of bytes */ count = (pstats->tcplib_breakdown_interval[i] / BREAKDOWN_HASH) + 1; /* If there was actually NO traffic of that type, then we don't * want to display any characters. But if there was a little bit * of traffic, even much less than BREAKDOWN_HASH, we want to * acknowledge it. */ if (!pstats->tcplib_breakdown_interval[i]) count--; /* Print one hash char per count. */ while(count > 0) { Mfprintf(pstats->hist_file, "%c", breakdown_hash_char[i]); count--; } } /* After we've done all the applications, end the line */ Mfprintf(pstats->hist_file, "\n"); /* Zero out the counters */ for(i = 0; i < NUM_APPS; i++) { pstats->tcplib_breakdown_interval[i] = 0; } /* Update the breakdown interval */ pstats->interval_count++; /* Update the time that the last breakdown interval occurred. */ pstats->last_interval = current_time; } /*************************************************************************** * * Function Name: namedfile * * Returns: Relative path name attached to output file name. * * Purpose: The namedfile uses the -D command line argument to take a data * directory and puts it together with its default file name to * come up with the file name needed for output. * * Called by: do_final_breakdown() in mod_tcplib.c * do_tcplib_final_converse() in mod_tcplib.c * tcplib_do_telnet_duration() in mod_tcplib.c * tcplib_do_telnet_interarrival() in mod_tcplib.c * tcplib_do_telnet_pktsize() in mod_tcplib.c * tcplib_do_ftp_itemsize() in mod_tcplib.c * tcplib_do_ftp_control_size() in mod_tcplib.c * tcplib_do_smtp_itemsize() in mod_tcplib.c * tcplib_do_nntp_itemsize() in mod_tcplib.c * tcplib_do_http_itemsize() in mod_tcplib.c * ****************************************************************************/ static char * namedfile( char * localsuffix, char * real) /* Default file name for the output file */ { char directory[256]; static char buffer[256]; /* Buffer to store the full file name */ if (!LOCAL_ONLY) snprintf(directory,sizeof(directory),"%s_%s", output_dir, localsuffix); else snprintf(directory,sizeof(directory),"%s", output_dir); /* try to CREATE the directory if it doesn't exist */ if (access(directory,F_OK) != 0) { if (mkdir(directory,0755) != 0) { perror(directory); exit(-1); } if (ldebug>1) printf("Created directory '%s'\n", directory); } snprintf(buffer,sizeof(buffer),"%s/%s", directory, real); return buffer; } /*************************************************************************** * * Function Name: do_final_breakdown * * Returns: Nothing * * Purpose: To generate the final breakdown file. More specifically, to * generate the one line in the breakdown file associated with * the input file that is currently being traced. * * Called by: tcplib_done() in mod_tcplib.c * tcplib_newfile() in mod_tcplib.c * * ****************************************************************************/ static void do_final_breakdown( char *filename, f_testinside p_tester, struct tcplibstats *pstats) { module_conninfo *pmc; MFILE* fil; /* File descriptor for the traffic breakdown file */ long file_pos; /* Offset within the traffic breakdown file */ u_long num_parallel_http = 0; /* This is the header for the traffic breakdown file. It follows the * basic format of the original TCPLib breakdown file, but has been * modified to accomodate the additions that were made to TCPLib */ #ifdef INCLUDE_PHONE_CONV char *header = "stub\tsmtp\tnntp\ttelnet\tftp\thttp\tphone\tconv\n"; #else /* INCLUDE_PHONE_CONV */ char *header = "stub smtp\tnntp\ttelnet\tftp\thttp\n"; #endif /* INCLUDE_PHONE_CONV */ if (!(fil = Mfopen(filename, "a"))) { perror("Opening Breakdown File"); exit(1); } Mfseek(fil, 0, SEEK_END); file_pos = Mftell(fil); /* Basically, we're checking to see if this file has already been * used. We have the capability to both start a new set of data * based on a trace file, or we have the ability to incorporate one * trace file's data into the data from another trace. This would * have the effect of creating a hybrid traffic pattern, that matches * neither of the sources, but shares characteristics of both. */ if (file_pos < strlen(header)) { Mfprintf(fil, "%s", header); } /* We only do this next part if we actually have a file name. In * earlier revisions, sending a NULL filename signified the end of * all trace files. At this point, a NULL file name has no useful * purpose, so we ignore it completely. */ if (current_file) { int bad_port = 0; int no_data = 0; int bad_dir = 0; /* for protocol breakdowns */ int breakdown_protocol[NUM_APPS] = {0}; /* The breakdown file line associated with each trace file is * prefaced with the trace file's name. This was part of the * original TCPLib format. */ Mfprintf(fil, "%-16s ", current_file); /* count the connections of each protocol type */ for (pmc = module_conninfo_tail; pmc ; pmc = pmc->prev) { int protocol_type; module_conninfo_tcb *ptcbc; /* check the protocol type */ protocol_type = pmc->btype; if (protocol_type == TCPLIBPORT_NONE) { ++bad_port; continue; /* not interested, loop to next conn */ } /* count the parallel HTTP separately */ if (pmc->ignore_conn) { if (protocol_type == TCPLIBPORT_HTTP) { ++num_parallel_http; continue; } } /* see if we want A->B */ ptcbc = &pmc->tcb_cache[TCB_CACHE_A2B]; if ((*p_tester)(pmc, ptcbc)) { /* count it if there's data */ if (ptcbc->data_bytes > 0) { if (pmc->ignore_conn && is_http_conn(pmc)) ++num_parallel_http; else ++breakdown_protocol[protocol_type]; } else { ++no_data; } } else { /* see if we want B->A */ ptcbc = &pmc->tcb_cache[TCB_CACHE_B2A]; if ((*p_tester)(pmc, ptcbc)) { /* count it if there's data */ if (ptcbc->data_bytes > 0) { if (pmc->ignore_conn && is_http_conn(pmc)) ++num_parallel_http; else ++breakdown_protocol[protocol_type]; } else { ++no_data; } } else { ++bad_dir; } } } /* Print out each of the columns we like */ /* SMTP */ Mfprintf(fil, "%.4f\t", ((float)breakdown_protocol[TCPLIBPORT_SMTP])/ num_tcp_pairs); /* NNTP */ Mfprintf(fil, "%.4f\t", ((float)breakdown_protocol[TCPLIBPORT_NNTP])/ num_tcp_pairs); /* TELNET */ Mfprintf(fil, "%.4f\t", ((float)breakdown_protocol[TCPLIBPORT_TELNET])/ num_tcp_pairs); /* FTP */ Mfprintf(fil, "%.4f\t", ((float)breakdown_protocol[TCPLIBPORT_FTPCTRL])/ num_tcp_pairs); /* HTTP */ Mfprintf(fil, "%.4f\t", ((float)breakdown_protocol[TCPLIBPORT_HTTP])/ num_tcp_pairs); #ifdef UNDEF /* FTP Data */ Mfprintf(fil, "%.4f\t", ((float)breakdown_protocol[TCPLIBPORT_FTPDATA])/ num_tcp_pairs); /* Parallel HTTP */ Mfprintf(fil, "%.4f\t", ((float)num_parallel_http)/ num_tcp_pairs); #endif /* UNDEF */ #ifdef INCLUDE_PHONE_CONV /* Place holders for phone and converstation intervals. The * phone type was never fully developed in the original TCPLib * implementation. At the current time, we don't consider * phone type conversations. The placeholder for conversation * intervals allows us to use TCPLib's existing setup for * aquiring statistics. Without a placeholder in the * breakdown file, TCPLib won't recognize this particular * item, and in the generation of statistically equivalent * traffic patterns, the interval between converstaions is of * utmost importance, especially as far as the scalability of * traffic is concerned. */ Mfprintf(fil, "%.4f\t%.4f", (float)0, (float)0); #endif /* INCLUDE_PHONE_CONV */ Mfprintf(fil, "\n"); } Mfclose(fil); Mfclose(pstats->hist_file); } static void do_all_final_breakdowns(void) { char *filename; filename = namedfile("local",TCPLIB_BREAKDOWN_FILE); do_final_breakdown(filename, TestLocal, global_pstats[LOCAL]); if (LOCAL_ONLY) return; /* none of the rest will match anyway */ filename = namedfile("incoming",TCPLIB_BREAKDOWN_FILE); do_final_breakdown(filename, TestIncoming, global_pstats[INCOMING]); filename = namedfile("outgoing",TCPLIB_BREAKDOWN_FILE); do_final_breakdown(filename, TestOutgoing, global_pstats[OUTGOING]); filename = namedfile("remote",TCPLIB_BREAKDOWN_FILE); do_final_breakdown(filename, TestRemote, global_pstats[REMOTE]); } /*************************************************************************** * * Function Name: breakdown_type * * Returns: The generic type of connection associated with "port" * * Purpose: To convert the port given to the function to the appropriate * TCPLib type port. As we come across other ports that have the * same basic characteristics as TCPLib type, we can just add * them here. * * Called by: tcplib_read() in mod_tcplib.c * do_final_breakdown() in mod_tcplib.c * * ****************************************************************************/ static int breakdown_type( tcp_pair *ptp) { /* shorthand */ portnum porta = ptp->addr_pair.a_port; portnum portb = ptp->addr_pair.b_port; if (is_telnet_port(porta) || is_telnet_port(portb)) return(TCPLIBPORT_TELNET); if (is_ftp_ctrl_port(porta) || is_ftp_ctrl_port(portb)) return(TCPLIBPORT_FTPCTRL); if (is_smtp_port(porta) || is_smtp_port(portb)) return(TCPLIBPORT_SMTP); if (is_nntp_port(porta) || is_nntp_port(portb)) return(TCPLIBPORT_NNTP); if (is_http_port(porta) || is_http_port(portb)) return(TCPLIBPORT_HTTP); if (is_ftp_data_port(porta) || is_ftp_data_port(portb) || CouldBeFtpData(ptp)) return(TCPLIBPORT_FTPDATA); return TCPLIBPORT_NONE; } /* End Breakdown Stuff */ /* Begin Next Conversation Stuff */ /*************************************************************************** * * Function Name: do_tcplib_next_converse * * Returns: Nothing * * Purpose: This function takes a new conversation and deals with the time * between successive conversations. If an entry in the breakdown * table already exists with that particular time, then the counter * is simply incremented. If not, then a new table is made with a * space for the new table item. We're using arrays, but a change * might be made to use a linked list before too long. * * Called by: tcplib_newconn() in mod_tcplib.c * * ****************************************************************************/ static void do_tcplib_next_converse( module_conninfo_tcb *ptcbc, module_conninfo *pmc) { struct tcplibstats *pstats; module_conninfo *pmc_previous; enum t_dtype dtype; int etime; /* Time difference between the first packet in this * conversation and the first packet in the previous * conversation. Basically, this is the time between * new conversations. */ /* see where to keep the stats */ dtype = traffic_type(pmc, ptcbc); pstats = global_pstats[dtype]; if (ldebug>2) { printf("do_tcplib_next_converse: %s, %s\n", FormatBrief(pmc->ptp, ptcbc->ptcb), dtype_names[dtype]); } /* sdo - Wed Jun 16, 1999 */ /* new method, search backward to find the previous connection that had */ /* data flowing in the same "direction" and then use the difference */ /* between the starting times of those two connections as the conn */ /* interrival time */ /* sdo - Fri Jul 9, 1999 (information already computed in Fillcache) */ /* FIRST, do conversation interarrivals for ALL conns */ if (ptcbc->prev_dtype_all != NULL) { pmc_previous = ptcbc->prev_dtype_all; /* elapsed time since that previous connection started */ etime = (int)(elapsed(pmc_previous->first_time, pmc->first_time)/1000.0); /* convert us to ms */ /* keep stats */ AddToCounter(&pstats->conv_interarrival_all, etime, 1, 1); } /* THEN, do conversation interarrivals by APP type */ if (ptcbc->prev_dtype_byapp != NULL) { pmc_previous = ptcbc->prev_dtype_byapp; /* elapsed time since that previous connection started */ etime = (int)(elapsed(pmc_previous->first_time, pmc->first_time)/1000.0); /* convert us to ms */ /* keep stats */ AddToCounter(&pstats->conv_interarrival_byapp[pmc->btype], etime, 1, 1); } return; } /* End of the breakdown section */ /* return the previous connection that passes data in the direction */ /* given in "dtype" and has app type apptype (or -1 for any) */ module_conninfo * FindPrevConnection( module_conninfo *pmc, enum t_dtype dtype, int app_type) { module_conninfo_tcb *ptcbc; int count = 0; /* loop back further in time */ for (pmc = pmc->prev; pmc; pmc = pmc->prev) { int dir; /* ignore FTP Data and parallel HTTP */ if (pmc->ignore_conn) continue; for (LOOP_OVER_BOTH_TCBS(dir)) { ptcbc = &pmc->tcb_cache[dir]; if ((app_type != -1) && (app_type != pmc->btype)) { /* skip it, wrong app */ } if (ptcbc->dtype == dtype) { if (ptcbc->data_bytes != 0) return(pmc); } if (ldebug) ++count; } } if (ldebug > 1) printf("FindPrevConnection %s returned NULL, took %d searches\n", dtype_names[dtype], count); return(NULL); } /*************************************************************************** * * Function Name: do_tcplib_final_converse * * Returns: Nothing * * Purpose: To generate a new line in the breakdown file which shows the * conversation percentages viewed in the file that is currently * open, but has just been ended. * * Called by: tcplib_done() in mod_tcplib.c * * ****************************************************************************/ static void do_tcplib_final_converse( char *filename, char *protocol, dyn_counter psizes) { const int bucketsize = GRAN_CONVARRIVAL; char title[80]; #ifdef READ_OLD_FILES /* sdo - OK, pstats->conv_interarrival already has the counts we */ /* made. First, include anything from an existing file. */ psizes = ReadOldFile(filename, bucketsize, 0, psizes); #endif /* READ_OLD_FILES */ /* generate the graph title of the table */ snprintf(title,sizeof(title),"Conversation Interval Time (ms) - %s", protocol); /* Now, dump out the combined data */ StoreCounters(filename,title, "% Interarrivals", bucketsize, psizes); return; } static void do_tcplib_conv_duration( char *filename, dyn_counter psizes) { const int bucketsize = GRAN_CONVDURATION; #ifdef READ_OLD_FILES psizes = ReadOldFile(filename, bucketsize, 0, psizes); #endif /* READ_OLD_FILES */ /* Now, dump out the combined data */ StoreCounters(filename,"Conversation Duration (ms)", "% Conversations", bucketsize, psizes); return; } static void do_tcplib_next_duration( module_conninfo_tcb *ptcbc, module_conninfo *pmc) { struct tcplibstats *pstats; enum t_dtype dtype; int etime; /* Time difference between the first packet in this * conversation and the last packet */ /* see where to keep the stats */ dtype = traffic_type(pmc, ptcbc); pstats = global_pstats[dtype]; if (ldebug>2) { printf("do_tcplib_next_duration: %s, %s\n", FormatBrief(pmc->ptp, ptcbc->ptcb), dtype_names[dtype]); } /* elapsed time since that previous connection started */ etime = (int)(elapsed(pmc->first_time, pmc->last_time)/1000.0); /* convert us to ms */ /* keep stats */ AddToCounter(&pstats->conv_duration, etime, 1, GRAN_CONVDURATION); return; } /* End Next Conversation Stuff */ /* Begin Telnet stuff */ /*************************************************************************** * * Function Name: is_telnet_port * * Returns: TRUE/FALSE whether a given port is a telnet/login type port. * * Purpose: To accept a port number and determine whenter or not the port * would exhibit the characteristics of a telnet/login port. * * Called by: tcplib_read() in mod_tcplib.c * tcplib_do_telnet_duration() in mod_tcplib.c * tcplib_add_telnet_interval() in mod_tcplib.c * ****************************************************************************/ Bool is_telnet_port( portnum port) /* The port we're looking at */ { port -= ipport_offset; switch(port) { case IPPORT_LOGIN: case IPPORT_KLOGIN: case IPPORT_OLDLOGIN: case IPPORT_FLN_SPX: case IPPORT_UUCP_LOGIN: case IPPORT_KLOGIN2: case IPPORT_NLOGIN: case IPPORT_TELNET: /* case IPPORT_SSH: */ /* not considered safe to assume -- sdo */ return TRUE; break; default: return FALSE; } } /*************************************************************************** * * Function Name: tcplib_do_telnet_duration * * Returns: Nothing * * Purpose: To collect information about the duration of a telnet * conversation, and merge this information with data from * previous runs of this module, if such data exists. * * Called by: tcplib_do_telnet() in mod_tcplib.c * * ****************************************************************************/ void tcplib_do_telnet_duration( char *filename, /* where to store the output */ f_testinside p_tester) /* functions to test "insideness" */ { dyn_counter psizes = NULL; const int bucketsize = GRAN_TELNET_DURATION; module_conninfo *pmc; #ifdef READ_OLD_FILES /* This section reads in the data from the existing telnet duration * file in preparation for merging with the current data. */ psizes = ReadOldFile(filename, bucketsize, 0, psizes); #endif /* READ_OLD_FILES */ /* Fill the array with the current data */ for (pmc = module_conninfo_tail; pmc; pmc=pmc->prev) { /* if there wasn't data flowing in this direction, skip it */ if (InsideBytes(pmc,p_tester) == 0) continue; /* Only work this for telnet connections */ if (is_telnet_conn(pmc)) { /* convert the time difference to ms */ int temp = (int)( elapsed(pmc->first_time, pmc->last_time)/1000.0); /* convert us to ms */ /* increment the number of instances at this time. */ AddToCounter(&psizes, temp, 1, bucketsize); } } /* Output data to the file */ StoreCounters(filename,"Duration (ms)", "% Conversations", bucketsize,psizes); /* free the dynamic memory */ DestroyCounters(&psizes); } /*************************************************************************** * * Function Name: do_all_conv_arrivals * * Returns: Nothing * * Purpose: collect all the conversation interarrival times * * Called by: tcplib_done mod_tcplib.c * * ****************************************************************************/ void do_all_conv_arrivals() { module_conninfo *pmc; if (ldebug>1) printf("do_all_conv_arrivals: there are %d tcp pairs\n", num_tcp_pairs); /* process each connection */ for (pmc = module_conninfo_tail; pmc; pmc=pmc->prev) { int dir; if (ldebug>2) { static int count = 0; printf("do_all_conv_arrivals: processing pmc %d: %p\n", ++count, pmc); } /* ignore unidirectional HTTP */ if (is_http_conn(pmc) && pmc->unidirectional_http) continue; for (LOOP_OVER_BOTH_TCBS(dir)) { if (pmc->tcb_cache[dir].data_bytes != 0) { do_tcplib_next_converse(&pmc->tcb_cache[dir], pmc); do_tcplib_next_duration(&pmc->tcb_cache[dir], pmc); } } } } /*************************************************************************** * * Function Name: tcplib_add_telnet_interarrival * * Returns: Nothing * * Purpose: This function takes the current packet and computes the time * between the current packet and the previous packet. This value * is then added to the list of telnet interarrivals. These values * will be used at a later time. * * Called by: tcplib_read() in mod_tcplib.c * * ****************************************************************************/ void tcplib_add_telnet_interarrival( tcp_pair *ptp, /* This conversation */ module_conninfo *pmc, dyn_counter *psizes) { int temp = 0; /* time differential between packets */ /* Basically, I need the current time AND the time of the previous * packet BOTH right now. As far as I can see, this function * won't get called until the previous packet time has already * been overwritten by the current time. This makes obtaining * interarrival times more difficult. */ /* Answer - changed the original program. We added the pmstruct thing * to the original TCPTrace which allows a module to store information * about a connection. Quite handy. Thanks, Dr. Ostermann */ /* First packet has no interarrival time */ if (tv_same(ptp->last_time,ptp->first_time)) { /* If this is the first packet we've seen, nothing to do. * We'll be able to get some data the next * time. */ return; } /* Determining the time difference in ms */ temp = (int)(elapsed(pmc->last_time, current_time)/1000.0); /* us to ms */ /* We're going to set an artificial maximum for telnet interarrivals * for the case when someone (like me) would open a telnet session * and just leave it open and not do anything on it for minutes or * hours, or in some cases days. Keeping track of the exact time * for a connection like that is not worth the effort, so we just * set a ceiling and if it's over the ceiling, we make it the * ceiling. */ if (temp > MAX_TEL_INTER_COUNT - 1) temp = MAX_TEL_INTER_COUNT - 1; /* In this case, we know for a fact that we don't have a value of * temp that larger than the array, so we just increment the count */ (void) AddToCounter(psizes, temp, 1, 1); return; } /*************************************************************************** * * Function Name: tcplib_do_telnet_interarrival * * Returns: Nothing * * Purpose: To model integrate the old data for telnet interarrival times * with the data gathered during this execution of the program. * * Called by: tcplib_do_telnet() in mod_tcplib.c * * ****************************************************************************/ void tcplib_do_telnet_interarrival( char *filename, /* where to store the output */ f_testinside p_tester) /* stuck with the interface :-( */ { const int bucketsize = GRAN_TELNET_ARRIVAL; dyn_counter psizes = NULL; /* ugly interface conversion :-( */ if (p_tester == TestIncoming) psizes = global_pstats[INCOMING]->telnet_interarrival; else if (p_tester == TestOutgoing) psizes = global_pstats[OUTGOING]->telnet_interarrival; else if (p_tester == TestLocal) psizes = global_pstats[LOCAL]->telnet_interarrival; else if (p_tester == TestRemote) psizes = global_pstats[REMOTE]->telnet_interarrival; else { fprintf(stderr, "tcplib_do_telnet_interarrival: internal inconsistancy!\n"); exit(-1); } #ifdef READ_OLD_FILES /* add in the data from the old run (if it exists) */ psizes = ReadOldFile(filename, bucketsize, MAX_TEL_INTER_COUNT, psizes); #endif /* READ_OLD_FILES */ /* Dumping the data out to the data file */ StoreCounters(filename, "Interarrival Time (ms)", "% Interarrivals", bucketsize,psizes); } /*************************************************************************** * * Function Name: tcplib_do_telnet_packetsize * * Returns: Nothing * * Purpose: To take the data on telnet packet sizes measured during this * run of the program, merge them with any existing data, and * drop a data file. * * Called by: tcplib_do_telnet() in mod_tcplib.c * * ****************************************************************************/ void tcplib_do_telnet_packetsize( char *filename, /* where to store the output */ f_testinside p_tester) /* stuck with the interface :-( */ { const int bucketsize = GRAN_TELNET_PACKETSIZE; dyn_counter psizes = NULL; /* ugly interface conversion :-( */ if (p_tester == TestIncoming) psizes = global_pstats[INCOMING]->telnet_pktsize; else if (p_tester == TestOutgoing) psizes = global_pstats[OUTGOING]->telnet_pktsize; else if (p_tester == TestLocal) psizes = global_pstats[LOCAL]->telnet_pktsize; else if (p_tester == TestRemote) psizes = global_pstats[REMOTE]->telnet_pktsize; else { fprintf(stderr, "tcplib_do_telnet_packetsize: internal inconsistancy!\n"); exit(-1); } #ifdef READ_OLD_FILES /* In this section, we're reading in from the previous data file, * applying the data contained there to the data set that we've * acquired during this run, and then dumping the merged data set * back out to the data file */ psizes = ReadOldFile(filename, bucketsize, 0, psizes); #endif /* READ_OLD_FILES */ /* Dumping the data out to the data file */ StoreCounters(filename, "Packet Size (bytes)", "% Packets", bucketsize,psizes); } /*************************************************************************** * * Function Name: tcplib_add_telnet_packetsize * * Returns: Nothing * * Purpose: Takes a length as acquired from a telnet packet data size and * increments the count of the telnet packet size table by one for * entry which corresponds to that length. If the packet size is * larger than the allocated table allows, we truncate the packet * size. * * Called by: tcplib_read() in mod_tcplib.c * * ****************************************************************************/ void tcplib_add_telnet_packetsize( struct tcplibstats *pstats, int length) /* The length of the packet to be added to the table */ { /* Incrementing the table */ AddToCounter(&pstats->telnet_pktsize, length, 1, 1); } /* End Telnet Stuff */ /* Begin FTP Stuff */ /*************************************************************************** * * Function Name: is_ftp_ctrl_conn * * Returns: Boolean value * * Purpose: To determine if the connection is an FTP control port. * * Called by: tcplib_do_ftp_control_size() in mod_tcplib.c * ****************************************************************************/ Bool is_ftp_ctrl_port( portnum port) { port -= ipport_offset; return (port == IPPORT_FTP_CONTROL); } Bool is_ftp_data_port( portnum port) { port -= ipport_offset; return (port == IPPORT_FTP_DATA); } /*************************************************************************** * * Function Name: tcplib_do_ftp_itemsize * * Returns: Nothing * * Purpose: To generate the ftp.itemsize data file from the information * collected on ftp transfer sizes. This function also integrates * new data with old data, if any old data exists. * * Called by: tcplib_do_ftp() in mod_tcplib.c * ****************************************************************************/ void tcplib_do_ftp_itemsize( char *filename, /* where to store the output */ f_testinside p_tester) /* functions to test "insideness" */ { const int bucketsize = GRAN_FTP_ITEMSIZE; tcplib_do_GENERIC_itemsize(filename, TCPLIBPORT_FTPDATA, p_tester, bucketsize); } void tcplib_do_ftp_numitems( char *filename, /* where to store the output */ f_testinside p_tester) /* functions to test "insideness" */ { int bucketsize = GRAN_NUMITEMS; module_conninfo *pmc; dyn_counter psizes = NULL; #ifdef READ_OLD_FILES /* If an old data file exists, open it, read in its contents * and store them until they are integrated with the current * data */ psizes = ReadOldFile(filename, bucketsize, 0, psizes); #endif /* READ_OLD_FILES */ /* fill out the array with data from the current connections */ for (pmc = module_conninfo_tail; pmc; pmc = pmc->prev) { /* We only need the stats if it's the right port */ if (is_ftp_ctrl_conn(pmc)) { if ((*p_tester)(pmc, &pmc->tcb_cache[TCB_CACHE_A2B])) { if (ldebug && (pmc->tcb_cache[TCB_CACHE_A2B].numitems == 0)) printf("numitems: control %s has NONE\n", FormatBrief(pmc->ptp, NULL)); AddToCounter(&psizes, pmc->tcb_cache[TCB_CACHE_A2B].numitems, 1, 1); } } } /* store all the data (old and new) into the file */ StoreCounters(filename,"Total Articles", "% Conversation", bucketsize,psizes); /* free the dynamic memory */ DestroyCounters(&psizes); } static void tcplib_do_ftp_control_size( char *filename, /* where to store the output */ f_testinside p_tester) /* functions to test "insideness" */ { const int bucketsize = GRAN_FTP_CTRLSIZE; tcplib_do_GENERIC_itemsize(filename, TCPLIBPORT_FTPCTRL, p_tester, bucketsize); } /* End of FTP Stuff */ /* Begin SMTP Stuff */ static Bool is_smtp_port( portnum port) { port -= ipport_offset; return (port == IPPORT_SMTP); } static void tcplib_do_smtp_itemsize( char *filename, /* where to store the output */ f_testinside p_tester) /* functions to test "insideness" */ { const int bucketsize = GRAN_SMTP_ITEMSIZE; tcplib_do_GENERIC_itemsize(filename, TCPLIBPORT_SMTP, p_tester, bucketsize); } /* Done SMTP Stuff */ /* Begin NNTP Stuff */ static Bool is_nntp_port( portnum port) { port -= ipport_offset; return (port == IPPORT_NNTP); } /* Done NNTP Stuff */ /* Begin HTTP Stuff */ static Bool is_http_port( portnum port) { port -= ipport_offset; return ((port == IPPORT_HTTP) || (port == IPPORT_HTTPS)); } /*************************************************************************** ** ** Support Routines ** ***************************************************************************/ /*************************************************************************** * * StoreCounters -- store a dyncounter structure into a file * this get's a little interesting because of the way tcplib works * for example, given the simple table * 1 0.3333 * 2 0.6667 * 3 1.0000 * tcplib will generate integer samples (from a random test): * 1 66756 0.6676 0.6676 * 2 33244 0.3324 1.0000 * * as another example, given the simple table (same as before except * for the first line) * 0 0.0000 * 1 0.3333 * 2 0.6667 * 3 1.0000 * tcplib will generate integer samples (from a random test): * 0 33609 0.3361 0.3361 * 1 33044 0.3304 0.6665 * 2 33347 0.3335 1.0000 * SO... if we really want 1/3 1's, 2's, and 3's, we need the table: * 1 0.0000 * 2 0.3333 * 3 0.6667 * 4 1.0000 * and a random test gives us * 1 33609 0.3361 0.3361 * 2 33044 0.3304 0.6665 * 3 33347 0.3335 1.0000 * which is exactly what we wanted. * * ... therefore, for each counter, we store counter+GRANULARITY * in the table, and also store a 0.0000 value for the FIRST entry * **************************************************************************/ static void StoreCounters( char *filename, char *header1, char *header2, int bucketsize, dyn_counter psizes) { MFILE *fil; int running_total = 0; int lines = 0; if (ldebug>1) printf("Saving data for file '%s'\n", filename); /* verify bucketsize, but not needed anymore */ if (bucketsize != GetGran(psizes)) { /* probably because the counter was never used */ if (GetTotalCounter(psizes) != 0) { fprintf(stderr,"StoreCounters: bad bucketsize (%s)\n", filename); exit(-1); } } if (!(fil = Mfopen(filename, "w"))) { perror(filename); exit(1); } Mfprintf(fil, "%s\t%s\tRunning Sum\tCounts\n", header1, header2); if (psizes == NULL) { if (ldebug>1) printf(" (No data for file '%s')\n", filename); } else { int cookie = 0; int first = TRUE; while (1) { u_long ix; int value; u_long count; u_long total_counter = GetTotalCounter(psizes); u_long gran = GetGran(psizes); if (NextCounter(&psizes, &cookie, &ix, &count) == 0) break; value = ix; running_total += count; if (count) { if (first) { /* see comments above! */ Mfprintf(fil, "%.3f\t%.4f\t%d\t%d\n", (float)(value), 0.0, 0, 0); first = FALSE; } Mfprintf(fil, "%.3f\t%.4f\t%d\t%lu\n", (float)(value+gran), (float)running_total/(float)total_counter, running_total, count); ++lines; } } } Mfclose(fil); if (ldebug>1) printf(" Stored %d values into %d lines of '%s'\n", running_total, lines, filename); } #ifdef READ_OLD_FILES static dyn_counter ReadOldFile( char *filename, int bucketsize, int maxlegal, /* upper limit on array IX (or 0) */ dyn_counter psizes) { FILE* old; /* File pointer for old data file */ float bytes; int count; int linesread = 0; /* If the an old data file exists, open it, read in its contents * and store them until they are integrated with the current * data */ if ((old = fopen(filename, "r"))) { char buffer[256]; /* read and discard the first line */ fgets(buffer, sizeof(buffer)-1, old); /* Read in each line in the file and pick out the pieces of */ /* the file. Store each important piece is psizes */ /* format is: Total Bytes % Conversations Running Sum Counts 5.000 0.0278 1 1 170.000 0.1111 4 3 */ /* (we only need the 1st and 4th fields) */ while (fscanf(old, "%f\t%*f\t%*d\t%d\n", &bytes, &count) == 4) { ++linesread; if ((maxlegal != 0) && (bytes > maxlegal)) bytes = maxlegal; AddToCounter(&psizes, (((int)bytes)/bucketsize), count); } if (ldebug>2) { if (psizes && (linesread > 0)) printf("Read data from old file '%s' (%lu values)\n", filename, TotalCounter(psizes)); else printf("Old data file '%s' had no data\n", filename); } fclose(old); } return(psizes); } #endif /* READ_OLD_FILES */ /* all of the itemsize routines look like this */ static void tcplib_do_GENERIC_itemsize( char *filename, /* where to store the output */ int btype, f_testinside p_tester, /* functions to test "insideness" */ int bucketsize) /* how much data to group together */ { module_conninfo *pmc; dyn_counter psizes = NULL; #ifdef READ_OLD_FILES /* If an old data file exists, open it, read in its contents * and store them until they are integrated with the current * data */ psizes = ReadOldFile(filename, bucketsize, 0, psizes); #endif /* READ_OLD_FILES */ /* fill out the array with data from the current connections */ for (pmc = module_conninfo_tail; pmc; pmc = pmc->prev) { /* We only need the stats if it's the right breakdown type */ if (pmc->btype == btype) { int nbytes = InsideBytes(pmc,p_tester); /* if there's no DATA, don't count it! (sdo change!) */ if (nbytes != 0) AddToCounter(&psizes, nbytes, 1, bucketsize); } } /* store all the data (old and new) into the file */ StoreCounters(filename,"Article Size (bytes)", "% Articles", bucketsize,psizes); /* free the dynamic memory */ DestroyCounters(&psizes); } /* cleanup all the burstsize counters */ /* called for HTTP_P, HTTP_S, and NNTP */ static void tcplib_cleanup_bursts() { module_conninfo *pmc; /* all but the last burst was ALREADY recorded, so we just clean up any burst that might be left */ for (pmc = module_conninfo_tail; pmc; pmc = pmc->prev) { int dir; for (LOOP_OVER_BOTH_TCBS(dir)) { module_conninfo_tcb *ptcbc = &pmc->tcb_cache[dir]; /* check for burst data */ if (ptcbc->pburst == NULL) continue; /* count the LAST burst */ if (ptcbc->burst_bytes != 0) { ++ptcbc->numitems; } /* add the last burst into the ttl for the parallel stream */ if (ptcbc->burst_bytes != 0) { struct parallelism *pp = pmc->pparallelism; if (pp) { ++pp->ttlitems[dir]; } } if (ptcbc->burst_bytes != 0) { AddToCounter(&ptcbc->pburst->size, ptcbc->burst_bytes, 1, GRAN_BURSTSIZE); } if (ptcbc->numitems != 0) { AddToCounter(&ptcbc->pburst->nitems, ptcbc->numitems, 1,GRAN_NUMITEMS); } } } } /* both ftp and nntp look the same */ static void tcplib_do_GENERIC_burstsize( char *filename, /* where to store the output */ dyn_counter counter) { int bucketsize = GRAN_BURSTSIZE; /* store all the data (old and new) into the file */ StoreCounters(filename,"Burst Size", "% Bursts", bucketsize, counter); } static void tcplib_do_GENERIC_P_maxconns( char *filename, /* where to store the output */ dyn_counter counter) { int bucketsize = GRAN_MAXCONNS; /* store all the data (old and new) into the file */ StoreCounters(filename,"Max Conns", "% Streams", bucketsize, counter); } static void tcplib_do_GENERIC_nitems( char *filename, /* where to store the output */ dyn_counter counter) { int bucketsize = GRAN_NUMITEMS; /* store all the data (old and new) into the file */ StoreCounters(filename,"Num Items", "% Conns", bucketsize, counter); } /* both ftp and nntp look the same */ static void tcplib_do_GENERIC_idletime( char *filename, /* where to store the output */ dyn_counter counter) { int bucketsize = GRAN_BURSTIDLETIME; /* store all the data (old and new) into the file */ StoreCounters(filename,"Idle Time", "% Bursts", bucketsize, counter); } static enum t_dtype traffic_type( module_conninfo *pmc, module_conninfo_tcb *ptcbc) { if (TestLocal(pmc,ptcbc)) return(LOCAL); if (TestIncoming(pmc,ptcbc)) return(INCOMING); if (TestOutgoing(pmc,ptcbc)) return(OUTGOING); if (TestRemote(pmc,ptcbc)) return(REMOTE); fprintf(stderr,"Internal error in traffic_type\n"); exit(-1); } static char * FormatBrief( tcp_pair *ptp, tcb *ptcb) { tcb *pab = &ptp->a2b; tcb *pba = &ptp->b2a; static char infobuf[100]; if (ptcb == pba) snprintf(infobuf,sizeof(infobuf),"%s - %s (%s2%s)", ptp->b_endpoint, ptp->a_endpoint, pba->host_letter, pab->host_letter); else snprintf(infobuf,sizeof(infobuf),"%s - %s (%s2%s)", ptp->a_endpoint, ptp->b_endpoint, pab->host_letter, pba->host_letter); return(infobuf); } static char * FormatAddrBrief( tcp_pair_addrblock *paddr_pair) { static char infobuf[100]; char infobuf1[100]; char infobuf2[100]; snprintf(infobuf1,sizeof(infobuf1),"%s", HostName(paddr_pair->a_address)); snprintf(infobuf2,sizeof(infobuf2),"%s", HostName(paddr_pair->b_address)); snprintf(infobuf,sizeof(infobuf),"%s - %s", infobuf1, infobuf2); return(infobuf); } /* find a connection pair */ static endpoint_pair * FindEndpointPair( endpoint_pair *hashtable[], tcp_pair_addrblock *paddr_pair) { endpoint_pair **ppep_head; endpoint_pair *pep_search; /* find the correct hash bucket */ ppep_head = &hashtable[EndpointHash(paddr_pair)]; /* search the bucket for the correct pair */ for (pep_search = *ppep_head; pep_search; pep_search = pep_search->pepnext) { /* see if it's the same connection */ if (SameEndpoints(&pep_search->addr_pair, paddr_pair)) return(pep_search); } /* after loop, pep_search is NON-NULL if we found it */ return(NULL); } /* add a connection to the list of all connections on that address pair */ static void AddEndpointPair( endpoint_pair *hashtable[], module_conninfo *pmc) { endpoint_pair **ppep_head; endpoint_pair *pep_search; /* search the bucket for the correct pair */ ppep_head = &hashtable[EndpointHash(&pmc->addr_pair)]; pep_search = FindEndpointPair(hashtable,&pmc->addr_pair); if (pep_search == NULL) { /* not found, create it */ pep_search = MallocZ(sizeof(endpoint_pair)); /* fill in the address info */ pep_search->addr_pair = pmc->ptp->addr_pair; /* put at the front of the bucket */ pep_search->pepnext = *ppep_head; *ppep_head = pep_search; } /* put the new connection at the front of the list for this endpoint pair */ pmc->next_pair = pep_search->pmchead; pep_search->pmchead = pmc; if (ldebug>1) { printf("\nEndpoint pair bucket\n"); /* for each thing on the bucket list */ for (pep_search = *ppep_head; pep_search; pep_search = pep_search->pepnext) { module_conninfo *pmc; printf(" %s:\n", FormatAddrBrief(&pep_search->addr_pair)); /* for each connection on that pair */ for (pmc = pep_search->pmchead; pmc; pmc = pmc->next_pair) { printf(" %u <-> %u\n", pmc->addr_pair.a_port, pmc->addr_pair.b_port); } } } } /* remove unidirectional conns from consideration */ static void tcplib_filter_http_uni() { module_conninfo *pmc; for (pmc = module_conninfo_tail; pmc; pmc = pmc->prev) { if ((pmc->tcb_cache[0].data_bytes == 0) || (pmc->tcb_cache[1].data_bytes == 0)) { /* unidirectional (or no data at all) */ pmc->unidirectional_http = TRUE; /* update counters */ ++debug_http_uni_conns; debug_http_uni_bytes += pmc->tcb_cache[0].data_bytes + pmc->tcb_cache[1].data_bytes; /* UNDO other counters */ --debug_http_total; if (pmc->pparallelism && (pmc->pparallelism->maxparallel > 1)) { --pmc->pparallelism->maxparallel; --debug_http_parallel; /* if this is NOT the last one, decrement slave count */ if (pmc->pparallelism->maxparallel > 0) { --debug_http_slaves; } else { /* nobody left, empty group */ --debug_http_groups; } } /* persistance is OK, as those are already ignored in tcplib_save_bursts */ } } } static Bool is_parallel_http( module_conninfo *pmc_new) { endpoint_pair *pep; module_conninfo *pmc; struct parallelism *pp = NULL; int dir; int parallel_conns = 0; u_long parent_groupnum = 0; /* see if there are any other connections on these endpoints */ pep = FindEndpointPair(http_endpoints, &pmc_new->addr_pair); /* if none, we're done */ if (pep == NULL) return(FALSE); /* search that pep chain for PARALLEL conns */ for (pmc = pep->pmchead; pmc; pmc = pmc->next_pair) { /* for efficiency, as we search we remove old, inactive entries */ /* NOTE that this is the NEXT entry, not current */ if (pmc->next_pair) { if (!RecentlyActiveConn(pmc->next_pair)) { /* remove it (by linking around it) */ pmc->next_pair = pmc->next_pair->next_pair; } } /* if it's ME or it's not ACTIVE, skip it */ if ((pmc_new == pmc) || !RecentlyActiveConn(pmc)) continue; if (trafgen_generated) { /* burst key group nums must also be the same */ if (pmc_new->http_groupnum != pmc->http_groupnum) { continue; /* skip it */ } } /* OK, it's ACTIVE */ /* mark it as parallel if not already done */ if (pmc->pparallelism == NULL) { pmc->pparallelism = MallocZ(sizeof(struct parallelism)); /* if HE isn't marked, then he must be the "parent", and this must be a new group */ ++debug_http_groups; ++debug_http_parallel; /* if this isn't trafgen-generated, give it a group number */ /* (mostly for debugging) */ if (!trafgen_generated) { static u_long groupnum = 0; parent_groupnum = ++groupnum; pmc->http_groupnum = parent_groupnum; } /* switch its stats to parallel */ for (LOOP_OVER_BOTH_TCBS(dir)) { module_conninfo_tcb *ptcbc = &pmc->tcb_cache[dir]; struct tcplibstats *pstats = global_pstats[ptcbc->dtype]; ptcbc->pburst = &pstats->http_P_bursts; } } else { /* it's already known to be parallel, so he's my brother */ if (!trafgen_generated) { parent_groupnum = pmc->http_groupnum; } } /* remember the parallel struct for these connections */ pp = pmc->pparallelism; /* sanity check, if we've already found one, it better be */ /* the same as this one!! */ if ((pp != NULL) && (pp != pmc->pparallelism)) { fprintf(stderr,"FindParallelHttp: bad data structure!!\n"); exit(-1); } /* found one more */ ++parallel_conns; } /* if we didn't find any, we're done */ if (parallel_conns == 0) return(FALSE); /* mark ME as parallel too */ ++debug_http_slaves; ++debug_http_parallel; for (LOOP_OVER_BOTH_TCBS(dir)) { module_conninfo_tcb *ptcbc = &pmc_new->tcb_cache[dir]; struct tcplibstats *pstats = global_pstats[ptcbc->dtype]; ptcbc->pburst = &pstats->http_P_bursts; } /* printf("FindParallel, marking as parallel %s\n", */ /* FormatBrief(pmc_new->ptp)); */ /* if this isn't trafgen generated, take the group number */ pmc_new->http_groupnum = parent_groupnum; /* update stats on this parallel system */ pmc_new->pparallelism = pp; ++parallel_conns; /* include ME */ /* update maximum parallelism, if required */ if (parallel_conns > pmc_new->pparallelism->maxparallel) pmc_new->pparallelism->maxparallel = parallel_conns; /* this _IS_ parallel */ return(TRUE); } static void TrackEndpoints( module_conninfo *pmc) { /* remember the endpoints (ftp and HTTP) */ if (is_ftp_ctrl_conn(pmc)) { AddEndpointPair(ftp_endpoints,pmc); } else if (is_http_conn(pmc)) { AddEndpointPair(http_endpoints,pmc); } /* if it's an FTP data connection, find the control conn */ if (is_ftp_data_conn(pmc)) { endpoint_pair *pep; /* for FTP Data, we ignore this one */ pmc->ignore_conn = TRUE; pep = FindEndpointPair(ftp_endpoints, &pmc->addr_pair); if (pep) { /* "charge" this new DATA connection to the most recently-active ftp control connection */ struct module_conninfo_tcb *tcbc_control; tcbc_control = MostRecentFtpControl(pep); ++tcbc_control->numitems; if (ldebug>1) { printf("Charging ftp data to %s, count %lu\n", FormatBrief(tcbc_control->ptcb->ptp, tcbc_control->ptcb), tcbc_control->numitems); } } else { if (ldebug>1) fprintf(stderr,"WARNING: no FTP control conn for %s???\n", FormatBrief(pmc->ptp, NULL)); } } } /* could this connection be an FTP data connection that's NOT on port 21? */ static Bool CouldBeFtpData( tcp_pair *ptp) { endpoint_pair *pep; struct module_conninfo_tcb *ptcbc; /* make sure NEITHER port is reserved */ if (ptp->addr_pair.a_port < 1024 || ptp->addr_pair.b_port < 1024) return(FALSE); /* see if there's any active FTP control connection on these endpoints... */ pep = FindEndpointPair(ftp_endpoints,&ptp->addr_pair); if (pep == NULL) return(FALSE); /* find the most recent FTP control connection */ ptcbc = MostRecentFtpControl(pep); if (pep == NULL) return(FALSE); /* OK, I guess it COULD be... */ ++debug_newconn_ftp_data_heuristic; return(TRUE); } /* find the TCB (client side) for the most recently-active control connection on this pair of endpoints */ static module_conninfo_tcb * MostRecentFtpControl( endpoint_pair *pep) { struct module_conninfo *pmc; static module_conninfo_tcb *tcbc_newest = NULL; tcb *tcb_newest; timeval time_newest; if (pep->pmchead == NULL) { /* None at all, that's odd... */ fprintf(stderr,"MostRecentFtpControl: unexpected empty list \n"); exit(-1); } /* search the rest looking for something newer */ for (pmc = pep->pmchead; pmc; pmc = pmc->next_pair) { tcb *ptcb_client = &pmc->ptp->a2b; /* if it's not "active", we're not interested */ if (!ActiveConn(pmc)) continue; /* have we found anyone yet? */ if (tcbc_newest == NULL) { tcb_newest = &pmc->ptp->a2b; tcbc_newest = &pmc->tcb_cache[TCB_CACHE_A2B]; time_newest = tcb_newest->last_data_time; } else if (tv_gt(ptcb_client->last_data_time, time_newest)) { /* this is "most recent" */ tcb_newest = ptcb_client; tcbc_newest = &pmc->tcb_cache[TCB_CACHE_A2B]; time_newest = ptcb_client->last_data_time; } } return(tcbc_newest); } static hash IPHash( ipaddr *paddr) { hash hval = 0; int i; if (ADDR_ISV4(paddr)) { /* V4 */ hval = paddr->un.ip4.s_addr; } else if (ADDR_ISV6(paddr)) { /* V6 */ for (i=0; i < 16; ++i) hval += paddr->un.ip6.s6_addr[i]; } else { /* address type unknown */ fprintf(stderr,"Unknown IP address type %d encountered\n", ADDR_VERSION(paddr)); exit(-1); } return(hval); } static hash EndpointHash( tcp_pair_addrblock *addr_pair) { hash hval; hval = IPHash(&addr_pair->a_address) + IPHash(&addr_pair->b_address); return(hval % ENDPOINT_PAIR_HASHSIZE); } static Bool SameEndpoints( tcp_pair_addrblock *pap1, tcp_pair_addrblock *pap2) { if (IPcmp(&pap1->a_address,&pap2->a_address) == 0) { if (IPcmp(&pap1->b_address,&pap2->b_address) == 0) { return(TRUE); } } else if (IPcmp(&pap1->a_address,&pap2->b_address) == 0) { if (IPcmp(&pap1->b_address,&pap2->a_address) == 0) { return(TRUE); } } return(FALSE); } /* Data is considered a NEW burst if: * 1) All previous data was ACKed * 2) There was intervening data in the other direction * 3) idletime > RTT */ static Bool IsNewBurst( module_conninfo *pmc, tcb *ptcb, module_conninfo_tcb *ptcbc, struct tcphdr *tcp) { seqnum seq = ntohl(tcp->th_seq); tcb *orig_lastdata; tcb *ptcb_otherdir = ptcb->ptwin; /* remember the last direction the data flowed */ orig_lastdata = pmc->tcb_lastdata; pmc->tcb_lastdata = ptcb; if (graph_tsg) { plotter_perm_color(ptcb->tsg_plotter, "green"); plotter_text(ptcb->tsg_plotter, current_time, seq, "a", "?"); } /* it's only a NEW burst if there was a PREVIOUS burst */ if (ptcbc->burst_bytes == 0) { if (graph_tsg) plotter_text(ptcb->tsg_plotter, current_time, seq, "b", "==0"); return(FALSE); } /* check for old data ACKed */ if (SEQ_LESSTHAN(ptcb_otherdir->ack,seq)) { /* not ACKed */ if (graph_tsg) plotter_text(ptcb->tsg_plotter, current_time, seq, "b", "noack"); return(FALSE); } /* check for idletime > RTT */ { u_long etime_usecs = elapsed(ptcbc->last_data_time, current_time); u_long last_rtt_usecs = ptcb->rtt_last; if ((last_rtt_usecs != 0) && (etime_usecs < last_rtt_usecs)) { if (graph_tsg) { char buf[100]; snprintf(buf,sizeof(buf),"short (%ld < %ld)", etime_usecs, last_rtt_usecs); plotter_text(ptcb->tsg_plotter, current_time, seq, "b", buf); } return(FALSE); } } /* check for intervening data */ if (ptcb == orig_lastdata) { /* no intervening data */ if (graph_tsg) plotter_text(ptcb->tsg_plotter, current_time, seq, "b", "!data"); return(FALSE); } /* ... else, it's a new burst */ if (graph_tsg) { plotter_perm_color(ptcb->tsg_plotter, "magenta"); plotter_text(ptcb->tsg_plotter, current_time, seq, "r", "YES!!"); } return(TRUE); } /* is this connection "active" */ /* 1: not reset */ /* 2: either 0 or 1 fins */ static Bool ActiveConn( module_conninfo *pmc) { if (FinCount(pmc->ptp) > 1) return(FALSE); if (ConnReset(pmc->ptp)) return(FALSE); return(TRUE); } /* is this connection "parallel" */ /* 1: ActiveConn() */ /* 2: last packets sent "recently" (defined as within 10 seconds) */ static Bool RecentlyActiveConn( module_conninfo *pmc) { int dir; if (ActiveConn(pmc)) return(TRUE); for (LOOP_OVER_BOTH_TCBS(dir)) { timeval last_packet = pmc->tcb_cache[dir].ptcb->last_time; /* elapsed time from last packet (in MICROseconds) */ if (elapsed(last_packet,current_time) < 10*US_PER_SEC) { /* 10 seconds for now, hope it works! */ return(TRUE); } } return(FALSE); } #endif /* LOAD_MODULE_TCPLIB */