/*
* 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' '<space>' */
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 */
syntax highlighted by Code2HTML, v. 0.9.1