Logo Search packages:      
Sourcecode: tcptrace version File versions  Download package

trace.c

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


#include "gcache.h"


/* locally global variables */
static int tcp_packet_count = 0;
static int search_count = 0;
static int active_conn_count = 0;
static int closed_conn_count = 0;
static Bool *ignore_pairs = NULL;/* which ones will we ignore */
static Bool bottom_letters = 0;     /* I don't use this anymore */
static Bool more_conns_ignored = FALSE;
static double sample_elapsed_time=0; /* to keep track of owin samples */
static double total_elapsed_time=0; /* to keep track of owin samples */ 
static int num_removed_tcp_pairs = 0;
static int tline_left  = 0; /* left and right time lines for the time line charts */
static int tline_right = 0;

/* provided globals  */
int num_tcp_pairs = -1; /* how many pairs we've allocated */
tcp_pair **ttp = NULL;  /* array of pointers to allocated pairs */
int max_tcp_pairs = 64; /* initial value, automatically increases */
u_long tcp_trace_count = 0;


/* local routine definitions */
static tcp_pair *NewTTP(struct ip *, struct tcphdr *);
static tcp_pair *FindTTP(struct ip *, struct tcphdr *, int *, ptp_ptr **);
static void MoreTcpPairs(int num_needed);
static void ExtractContents(u_long seq, u_long tcp_data_bytes,
                      u_long saved_data_bytes, void *pdata, tcb *ptcb);
static Bool check_hw_dups(u_short id, seqnum seq, tcb *ptcb);
static u_long SeqRep(tcb *ptcb, u_long seq);
static void UpdateConnLists(ptp_ptr *tcp_ptr, struct tcphdr *ptcp);
static void UpdateConnList(ptp_ptr *tcp_ptr, 
                     const Bool valid, 
                     ptp_ptr **conn_list_head, 
                     ptp_ptr **conn_list_tail);
static void RemoveOldConns(ptp_ptr **conn_list_head, 
                     ptp_ptr **conn_list_tail,
                     const unsigned expire_interval,
                     const Bool num_conn_check,
                     int *conn_count);
static void RemoveConn(const ptp_ptr *tcp_ptr);
static void RemoveTcpPair(const ptp_ptr *tcp_ptr);
static Bool MissingData(tcp_pair *ptp);

/* options */
Bool show_zero_window = TRUE;
Bool show_rexmit = TRUE;
Bool show_out_order = TRUE;
Bool show_sacks = TRUE;
Bool show_rtt_dongles = FALSE;
Bool show_triple_dupack = TRUE;
Bool show_zwnd_probes = TRUE;
Bool nonames = FALSE;
Bool use_short_names = FALSE;
Bool show_urg = TRUE;
int thru_interval = 10; /* in segments */


/* what colors to use */
/* choose from: "green" "red" "blue" "yellow" "purple" "orange"
   "magenta" "pink" */
char *window_color      = "yellow";
char *ack_color         = "green";
char *sack_color  = "purple";
char *data_color  = "white";
char *retrans_color     = "red";
char *hw_dup_color      = "blue";
char *out_order_color   = "pink";
char *text_color  = "magenta";
char *default_color     = "white";
char *synfin_color      = "orange";
char *push_color  = "white";  /* top arrow for PUSHed segments */
char *ecn_color         = "yellow";
char *urg_color         = "red";
char *probe_color       = "orange";
char *a2b_seg_color     = "green";     /* colors for segments on the time line chart */
char *b2a_seg_color     = "yellow"; 
                      

/* ack diamond dongle colors */
char *ackdongle_nosample_color      = "blue";
char *ackdongle_ambig_color   = "red";



/* 
 * ipcopyaddr: copy an IPv4 or IPv6 address  
 */
static inline void IP_COPYADDR (ipaddr *ptoaddr, ipaddr *pfromaddr)
{
    if (ADDR_ISV6(pfromaddr)) {
      memcpy(ptoaddr->un.ip6.s6_addr, pfromaddr->un.ip6.s6_addr, 16);
      ptoaddr->addr_vers = 6;
    } else {
      ptoaddr->un.ip4.s_addr = pfromaddr->un.ip4.s_addr;
      ptoaddr->addr_vers = 4;
    }
}



/*
 * ipsameaddr: test for equality of two IPv4 or IPv6 addresses
 */
static inline int IP_SAMEADDR (ipaddr *paddr1, ipaddr *paddr2)
{
    int ret = 0;
    if (ADDR_ISV4(paddr1)) {
      if (ADDR_ISV4(paddr2))
          ret = (paddr1->un.ip4.s_addr == paddr2->un.ip4.s_addr);
    } else {
      /* already know ADDR_ISV6(paddr1) */
      if (ADDR_ISV6(paddr2))
          ret = (memcmp(paddr1->un.ip6.s6_addr,
                    paddr2->un.ip6.s6_addr,16) == 0);
    }
    if (debug > 3)
      printf("SameAddr(%s(%d),%s(%d)) returns %d\n",
             HostName(*paddr1), ADDR_VERSION(paddr1),
             HostName(*paddr2), ADDR_VERSION(paddr2),
             ret);
    return ret;
}

/*  
 *  iplowaddr: test if one IPv4 or IPv6 address is lower than the second one
 */
static inline int IP_LOWADDR (ipaddr *paddr1, ipaddr *paddr2)
{
    int ret = 0;
    if (ADDR_ISV6(paddr1)) {
      if (ADDR_ISV6(paddr2))
          ret = (memcmp(paddr1->un.ip6.s6_addr,
                    paddr2->un.ip6.s6_addr,16) < 0);
    } else {
      /* already know ADDR_ISV4(paddr1) */
      if (ADDR_ISV4(paddr2))
          ret = (paddr1->un.ip4.s_addr < paddr2->un.ip4.s_addr);
    }
    if (debug > 3)
      printf("LowAddr(%s(%d),%s(%d)) returns %d\n",
             HostName(*paddr1), ADDR_VERSION(paddr1),
             HostName(*paddr2), ADDR_VERSION(paddr2),
             ret);
    return ret;
}


/* return elapsed time in microseconds */
/* (time2 - time1) */
double
elapsed(
    struct timeval time1,
    struct timeval time2)
{
    struct timeval etime;

    /*sanity check, some of the files have packets out of order */
    if (tv_lt(time2,time1)) {
      return(0.0);
    }

    if (0) {
      fprintf(stderr,"elapsed(%s,", ts2ascii(&time1));
      fprintf(stderr,"%s) is ", ts2ascii(&time2));
    }

    etime = time2;
    tv_sub(&etime, time1);

    if (0)
      fprintf(stderr,"\n\t%s \n", ts2ascii(&etime));

    return((double)etime.tv_sec * 1000000 + (double)etime.tv_usec);
}



/* subtract the rhs from the lhs, result in lhs */
void
tv_sub(struct timeval *plhs, struct timeval rhs)
{
    /* sanity check, lhs MUST BE more than rhs */
    if (tv_lt(*plhs,rhs)) {
      fprintf(stderr,"tvsub(%s,", ts2ascii(plhs));
      fprintf(stderr,"%s) bad timestamp order!\n", ts2ascii(&rhs));
/*    exit(-1); */
      plhs->tv_sec = plhs->tv_usec = 0;
      return;
    }
    
    if (plhs->tv_usec >= rhs.tv_usec) {
      plhs->tv_usec -= rhs.tv_usec;
    } else if (plhs->tv_usec < rhs.tv_usec) {
      plhs->tv_usec += US_PER_SEC - rhs.tv_usec;
      plhs->tv_sec -= 1;
    }
    plhs->tv_sec -= rhs.tv_sec;
}


/* add the RHS to the LHS, answer in *plhs */
void
tv_add(struct timeval *plhs, struct timeval rhs)
{
    plhs->tv_sec += rhs.tv_sec;
    plhs->tv_usec += rhs.tv_usec;

    if (plhs->tv_usec >= US_PER_SEC) {
      plhs->tv_usec -= US_PER_SEC;
      plhs->tv_sec += 1;
    }
}


/* are the 2 times the same? */
Bool
tv_same(struct timeval lhs, struct timeval rhs)
{
    return((lhs.tv_sec  == rhs.tv_sec) &&
         (lhs.tv_usec == rhs.tv_usec));
}


/*  1: lhs >  rhs */
/*  0: lhs == rhs */
/* -1: lhs <  rhs */
int
tv_cmp(struct timeval lhs, struct timeval rhs)
{
    if (lhs.tv_sec > rhs.tv_sec) {
      return(1);
    }

    if (lhs.tv_sec < rhs.tv_sec) {
      return(-1);
    }

    /* ... else, seconds are the same */
    if (lhs.tv_usec > rhs.tv_usec)
      return(1);
    else if (lhs.tv_usec == rhs.tv_usec)
      return(0);
    else
      return(-1);
}



/* copy the IP addresses and port numbers into an addrblock structure   */
/* in addition to copying the address, we also create a HASH value      */
/* which is based on BOTH IP addresses and port numbers.  It allows     */
/* faster comparisons most of the time                            */
void
CopyAddr(
    tcp_pair_addrblock *ptpa,
    struct ip *pip,
    portnum port1,
    portnum port2)
{
    ptpa->a_port = port1;
    ptpa->b_port = port2;

    if (PIP_ISV4(pip)) { /* V4 */
      IP_COPYADDR(&ptpa->a_address, IPV4ADDR2ADDR(&pip->ip_src));
      IP_COPYADDR(&ptpa->b_address, IPV4ADDR2ADDR(&pip->ip_dst));
      /* fill in the hashed address */
      ptpa->hash = ptpa->a_address.un.ip4.s_addr
          + ptpa->b_address.un.ip4.s_addr
          + ptpa->a_port + ptpa->b_port;
       
    } else { /* V6 */
      int i;
      struct ipv6 *pip6 = (struct ipv6 *)pip;
      IP_COPYADDR(&ptpa->a_address, IPV6ADDR2ADDR(&pip6->ip6_saddr));
      IP_COPYADDR(&ptpa->b_address, IPV6ADDR2ADDR(&pip6->ip6_daddr));
      /* fill in the hashed address */
      ptpa->hash = ptpa->a_port + ptpa->b_port;
      for (i=0; i < 16; ++i) {
          ptpa->hash += ptpa->a_address.un.ip6.s6_addr[i];
          ptpa->hash += ptpa->b_address.un.ip6.s6_addr[i];
      }
    }

    if (debug > 3)
      printf("Hash of (%s:%d,%s:%d) is %d\n",
             HostName(ptpa->a_address),
             ptpa->a_port,
             HostName(ptpa->b_address),
             ptpa->b_port,
             ptpa->hash);
}

/* 
 * This function tells us which way to go (Left or Right) in search for our 
 * matching 4-tuple {IP1:port1; IP2:port2} in the AVL tree hash-bucket.
 * 
 * It returns LT or RT depending on if we had to go left or right in the AVL Tree to
 * find our exact 4-tuple match, if it existed in the tree.
 * If the exact 4-tuple is found, it returns 0.
 */

int
AVL_WhichDir(
           tcp_pair_addrblock *ptpa1,
           tcp_pair_addrblock *ptpa2)
{

    /*
     * Here is our algorithm. If ptpa1={x1:p1; x2:p2} and ptpa2={y1:q1; y2:q2}
     * we choose X1=min(x1,x2) and X2=max(x1,x2); Similarly for Y1, Y2.
     * P1=port associated with X1, i.e. it is p1 if x1<x2 and it is p2 if not.
     * P2=port associated with X2. Similarly Q1, Q2 are calculated based on Y1,Y2.
     * 
     * Compare (X1, Y1)? ; X1<Y1 => LEFT; X1>Y1 => RIGHT; X1==Y1 => Continue down
     * 
     * Compare (X2, Y2)? ; X2<Y2 => LEFT; X2>Y2 => RIGHT; X2==Y2 => Continue down
     * 
     * Compare (P1, Q1)? ; P1<Q1 => LEFT; P1>Q1 => RIGHT; P1==Q1 => Continue down
     * 
     * Compare (P2, Q2)? ; P2<Q2 => LEFT; P2>Q2 => RIGHT;
     * 
     * If P2==Q2, then this connection should have matched the A2B or B2A catch 
     * from WhichDir()
     */
      
    ipaddr *X1, *X2, *Y1, *Y2;
    int P1, P2, Q1, Q2;
      
    if (IP_LOWADDR(&(ptpa1->a_address), &(ptpa1->b_address))) {         
        X1=&ptpa1->a_address;
      P1=ptpa1->a_port;     
      X2=&ptpa1->b_address;
      P2=ptpa1->b_port;
    } 
    else {
        X1=&ptpa1->b_address;
      P1=ptpa1->b_port;
      X2=&ptpa1->a_address;
      P2=ptpa1->a_port;
    }

    if (IP_LOWADDR(&(ptpa2->a_address), &(ptpa2->b_address))) {         
        Y1=&ptpa2->a_address;
      Q1=ptpa2->a_port;
      Y2=&ptpa2->b_address;
      Q2=ptpa2->b_port;
    } 
    else {
        Y1=&ptpa2->b_address;
      Q1=ptpa2->b_port;     
      Y2=&ptpa2->a_address;
      Q2=ptpa2->a_port;
    }

    // Optimization suggested by Dr.Ostermann. Check the ports first.
    if (P1<Q1) return LT;
    if (Q1<P1) return RT;
      
    if (P2<Q2) return LT;
    if (Q2<P2) return RT;


    if (IP_LOWADDR(X1,Y1)) return LT;
    if (IP_LOWADDR(Y1,X1)) return RT;
      
    if (IP_LOWADDR(X2,Y2)) return LT;
    if (IP_LOWADDR(Y2,X2)) return RT;
      
    return 0;
}

int
  WhichDir(
             tcp_pair_addrblock *ptpa1,
             tcp_pair_addrblock *ptpa2)
{
#ifdef BROKEN_COMPILER
   /* sorry for the ugly nested 'if', but a 4-way conjunction broke my*/
   /* Optimizer (under 'gcc version cygnus-2.0.2')*/
   
   /* same as first packet */
   if (IP_SAMEADDR(&(ptpa1->a_address), &(ptpa2->a_address)))
     if (IP_SAMEADDR(&(ptpa1->b_address), &(ptpa2->b_address)))
       if ((ptpa1->a_port == ptpa2->a_port))
       if ((ptpa1->b_port == ptpa2->b_port))
         return(A2B);
   
   /* reverse of first packet */
   if (IP_SAMEADDR(&(ptpa1->a_address), &(ptpa2->b_address)))
     if (IP_SAMEADDR(&(ptpa1->b_address), &(ptpa2->a_address)))
       if ((ptpa1->a_port == ptpa2->b_port))
       if ((ptpa1->b_port == ptpa2->a_port))
         return(B2A);
#else /* BROKEN_COMPILER */
   /* same as first packet */
   if (IP_SAMEADDR(&(ptpa1->a_address), &(ptpa2->a_address)) &&
       IP_SAMEADDR(&(ptpa1->b_address), &(ptpa2->b_address)) &&
       (ptpa1->a_port == ptpa2->a_port) &&
       (ptpa1->b_port == ptpa2->b_port))
     return(A2B);
   
   /* reverse of first packet */
   if (IP_SAMEADDR(&(ptpa1->a_address), &(ptpa2->b_address)) &&
       IP_SAMEADDR(&(ptpa1->b_address), &(ptpa2->a_address)) &&
       (ptpa1->a_port == ptpa2->b_port) &&
       (ptpa1->b_port == ptpa2->a_port))
     return(B2A);
#endif /* BROKEN_COMPILER */
   
   /* different connection */
   return(0);
}

int
SameConn(
       tcp_pair_addrblock *ptpa1,
       tcp_pair_addrblock *ptpa2,
       int      *pdir)
{
   
   /* if the hash values are different, they can't be the same */
   if (ptpa1->hash != ptpa2->hash)
     return(0);
   
   /* OK, they hash the same, are they REALLY the same function */
   *pdir = WhichDir(ptpa1,ptpa2);
   return(*pdir != 0);
}



static tcp_pair *
NewTTP(
    struct ip *pip,
    struct tcphdr *ptcp)
{
    char title[210];
    tcp_pair *ptp;

    if (0) {
      printf("trace.c:NewTTP() calling MakeTcpPair()\n");
    }
    ptp = MakeTcpPair();
    ++num_tcp_pairs;

    if (!run_continuously) {
      /* make a new one, if possible */
      if ((num_tcp_pairs+1) >= max_tcp_pairs) {
      MoreTcpPairs(num_tcp_pairs+1);
      }
      /* create a new TCP pair record and remember where you put it */
      ttp[num_tcp_pairs] = ptp;
      ptp->ignore_pair = ignore_pairs[num_tcp_pairs];
    }


    /* grab the address from this packet */
    CopyAddr(&ptp->addr_pair,
           pip, ntohs(ptcp->th_sport), ntohs(ptcp->th_dport));

    ptp->a2b.time.tv_sec = -1;
    ptp->b2a.time.tv_sec = -1;

    ptp->a2b.host_letter = strdup(NextHostLetter());
    ptp->b2a.host_letter = strdup(NextHostLetter());

    ptp->a2b.ptp = ptp;
    ptp->b2a.ptp = ptp;
    ptp->a2b.ptwin = &ptp->b2a;
    ptp->b2a.ptwin = &ptp->a2b;

    /* fill in connection name fields */
    ptp->a_hostname = strdup(HostName(ptp->addr_pair.a_address));
    ptp->a_portname = strdup(ServiceName(ptp->addr_pair.a_port));
    ptp->a_endpoint =
      strdup(EndpointName(ptp->addr_pair.a_address,
                      ptp->addr_pair.a_port));
    ptp->b_hostname = strdup(HostName(ptp->addr_pair.b_address));
    ptp->b_portname = strdup(ServiceName(ptp->addr_pair.b_port));
    ptp->b_endpoint = 
      strdup(EndpointName(ptp->addr_pair.b_address,
                      ptp->addr_pair.b_port));

    /* make the initial guess that each side is a reno tcp */
    /* this might actually be a poor thing to do in the sense that
       we could be looking at a Tahoe trace ... but the only side
       effect for the moment is that the LEAST estimate may be
       busted, although it very well may not be */
    ptp->a2b.tcp_strain = TCP_RENO;
    ptp->b2a.tcp_strain = TCP_RENO;

    ptp->a2b.LEAST = ptp->b2a.LEAST = 0;
    ptp->a2b.in_rto = ptp->b2a.in_rto = FALSE;

    /* init time sequence graphs */
    ptp->a2b.tsg_plotter = ptp->b2a.tsg_plotter = NO_PLOTTER;
    if (graph_tsg && !ptp->ignore_pair) {
      if (!ignore_non_comp || (SYN_SET(ptcp))) {
          snprintf(title,sizeof(title),"%s_==>_%s (time sequence graph)",
                ptp->a_endpoint, ptp->b_endpoint);
          ptp->a2b.tsg_plotter =
            new_plotter(&ptp->a2b,NULL,title,
                      graph_time_zero?"relative time":"time",
                      graph_seq_zero?"sequence offset":"sequence number",
                      PLOT_FILE_EXTENSION);
          snprintf(title,sizeof(title),"%s_==>_%s (time sequence graph)",
                ptp->b_endpoint, ptp->a_endpoint);
          ptp->b2a.tsg_plotter =
            new_plotter(&ptp->b2a,NULL,title,
                      graph_time_zero?"relative time":"time",
                      graph_seq_zero?"sequence offset":"sequence number",
                      PLOT_FILE_EXTENSION);
          if (graph_time_zero) {
            /* set graph zero points */
            plotter_nothing(ptp->a2b.tsg_plotter, current_time);
            plotter_nothing(ptp->b2a.tsg_plotter, current_time);
          }
      }
    }

    /* init owin graphs */
    ptp->a2b.owin_plotter = ptp->b2a.owin_plotter = NO_PLOTTER;
    if (graph_owin && !ptp->ignore_pair) {
      if (!ignore_non_comp || (SYN_SET(ptcp))) {
          snprintf(title,sizeof(title),"%s_==>_%s (outstanding data)",
                ptp->a_endpoint, ptp->b_endpoint);
          ptp->a2b.owin_plotter =
            new_plotter(&ptp->a2b,NULL,title,
                      graph_time_zero?"relative time":"time",
                      "Outstanding Data (bytes)",
                      OWIN_FILE_EXTENSION);
          snprintf(title,sizeof(title),"%s_==>_%s (outstanding data)",
                ptp->b_endpoint, ptp->a_endpoint);
          ptp->b2a.owin_plotter =
            new_plotter(&ptp->b2a,NULL,title,
                      graph_time_zero?"relative time":"time",
                      "Outstanding Data (bytes)",
                      OWIN_FILE_EXTENSION);
          if (graph_time_zero) {
            /* set graph zero points */
            plotter_nothing(ptp->a2b.owin_plotter, current_time);
            plotter_nothing(ptp->b2a.owin_plotter, current_time);
          }
          ptp->a2b.owin_line =
            new_line(ptp->a2b.owin_plotter, "owin", "red");
          ptp->b2a.owin_line =
            new_line(ptp->b2a.owin_plotter, "owin", "red");

          if (show_rwinline) {
            ptp->a2b.rwin_line =
              new_line(ptp->a2b.owin_plotter, "rwin", "yellow");
            ptp->b2a.rwin_line =
              new_line(ptp->b2a.owin_plotter, "rwin", "yellow");
          }
        
          ptp->a2b.owin_avg_line =
            new_line(ptp->a2b.owin_plotter, "avg owin", "blue");
          ptp->b2a.owin_avg_line =
            new_line(ptp->b2a.owin_plotter, "avg owin", "blue");
          ptp->a2b.owin_wavg_line =
            new_line(ptp->a2b.owin_plotter, "wavg owin", "green");
          ptp->b2a.owin_wavg_line =
            new_line(ptp->b2a.owin_plotter, "wavg owin", "green");
      }
    }

    /* init time line graphs (Avinash, 2 July 2002) */
    ptp->a2b.tline_plotter = ptp->b2a.tline_plotter = NO_PLOTTER;
    if (graph_tline && !ptp->ignore_pair) {
      if (!ignore_non_comp || (SYN_SET(ptcp))) {
          /* We don't want the standard a2b type name so we will specify
           * a filename of type a_b when we call new_plotter.
           */ 
          char filename[25];
          snprintf(filename,sizeof(filename),"%s_%s",
                 ptp->a2b.host_letter, ptp->a2b.ptwin->host_letter);

          snprintf(title,sizeof(title),"%s_==>_%s (time line graph)",
                ptp->a_endpoint, ptp->b_endpoint);
          /* We will keep both the plotters the same since we want all
           * segments going in either direction to be plotted on the same
           * graph
           */ 
          ptp->a2b.tline_plotter = ptp->b2a.tline_plotter =
            new_plotter(&ptp->a2b,filename,title,
                      "segments",
                      "relative time",
                      TLINE_FILE_EXTENSION);
             
          /* Switch the x & y axis types.
           * The default is x - timeval, y - unsigned,
           * we need x - unsigned, y - dtime.
           * Both the plotters are the same so we will
           * only call this function once.
           */
          plotter_switch_axis(ptp->a2b.tline_plotter, TRUE);
            
          /* set graph zero points */
          plotter_nothing(ptp->a2b.tline_plotter, current_time);
          plotter_nothing(ptp->b2a.tline_plotter, current_time);

          /* Some graph initializations 
           * Generating a drawing space between x=0-100.
           * The time lines will be at x=40 for source, x=60 for destination.
           * Rest of the area on either sides will be used to print segment
           * information.
           * 
           *  seg info |----->| 
           *           |<-----| seg info
           */
          tline_left  = 40;
          tline_right = 60;
          plotter_invisible(ptp->a2b.tline_plotter, current_time, 0);
          plotter_invisible(ptp->a2b.tline_plotter, current_time, 100);
      }
    }
   
   
    /* init segment size graphs */
    ptp->a2b.segsize_plotter = ptp->b2a.segsize_plotter = NO_PLOTTER;
    if (graph_segsize && !ptp->ignore_pair) {
      snprintf(title,sizeof(title),"%s_==>_%s (segment size graph)",
            ptp->a_endpoint, ptp->b_endpoint);
      ptp->a2b.segsize_plotter =
          new_plotter(&ptp->a2b,NULL,title,
                  graph_time_zero?"relative time":"time",
                  "segment size (bytes)",
                  SEGSIZE_FILE_EXTENSION);
      snprintf(title,sizeof(title),"%s_==>_%s (segment size graph)",
            ptp->b_endpoint, ptp->a_endpoint);
      ptp->b2a.segsize_plotter =
          new_plotter(&ptp->b2a,NULL,title,
                  graph_time_zero?"relative time":"time",
                  "segment size (bytes)",
                  SEGSIZE_FILE_EXTENSION);
      if (graph_time_zero) {
          /* set graph zero points */
          plotter_nothing(ptp->a2b.segsize_plotter, current_time);
          plotter_nothing(ptp->b2a.segsize_plotter, current_time);
      }
      ptp->a2b.segsize_line =
          new_line(ptp->a2b.segsize_plotter, "segsize", "red");
      ptp->b2a.segsize_line =
          new_line(ptp->b2a.segsize_plotter, "segsize", "red");
      ptp->a2b.segsize_avg_line =
          new_line(ptp->a2b.segsize_plotter, "avg segsize", "blue");
      ptp->b2a.segsize_avg_line =
          new_line(ptp->b2a.segsize_plotter, "avg segsize", "blue");
    }

    /* init RTT graphs */
    ptp->a2b.rtt_plotter = ptp->b2a.rtt_plotter = NO_PLOTTER;

    ptp->a2b.ss = MakeSeqspace();
    ptp->b2a.ss = MakeSeqspace();

    ptp->filename = cur_filename;

    return(ptp);
}



/* connection records are stored in a hash table.  Buckets are linked   */
/* lists sorted by most recent access.                            */
#ifdef SMALL_TABLE
#define HASH_TABLE_SIZE 1021  /* oughta be prime */
#else /* SMALL_TABLE */
#define HASH_TABLE_SIZE 4099  /* oughta be prime */
#endif /* SMALL_TABLE */
static ptp_snap *ptp_hashtable[HASH_TABLE_SIZE] = {NULL};


/* search efficiency data (optional) */
/* one entry per hash table bucket */
struct search_efficiency {
    unsigned num_connections;
    unsigned max_connections;
    unsigned max_depth;
    unsigned num_searches;
    unsigned num_comparisons;
};
static struct search_efficiency hashtable_efficiency[HASH_TABLE_SIZE];


/* double linked-lists of live and closed connections */
static ptp_ptr    *live_conn_list_head = NULL;
static ptp_ptr    *live_conn_list_tail = NULL;
static ptp_ptr    *closed_conn_list_head = NULL;
static ptp_ptr    *closed_conn_list_tail = NULL;
static timeval    last_update_time = {0, 0};

static tcp_pair *
FindTTP(
    struct ip *pip,
    struct tcphdr *ptcp,
    int *pdir,
    ptp_ptr **tcp_ptr)
{
    ptp_snap **pptph_head = NULL;
    ptp_snap *ptph;
    tcp_pair_addrblock  tp_in;
    struct search_efficiency *pse = NULL;
    unsigned depth = 0;
    int dir, conn_status;
    hash hval;
    *tcp_ptr = NULL;

    if (debug > 10) {
      printf("trace.c: FindTTP() called\n");
    }

    /* grab the address from this packet */
    CopyAddr(&tp_in, pip, ntohs(ptcp->th_sport), ntohs(ptcp->th_dport));

    /* grab the hash value (already computed by CopyAddr) */
    hval = tp_in.hash % HASH_TABLE_SIZE;

    pptph_head = &ptp_hashtable[hval];

    if (debug) {
      /* search efficiency checking */
      pse = &hashtable_efficiency[hval];
    }
   
    if (pse) {
      /* search efficiency instrumentation */
      depth = 0;
      ++pse->num_searches;
    }

    for (ptph = *pptph_head; ptph; ) {
      if (debug) {
          /* search efficiency instrumentation */
          ++search_count;
          if (pse) {
            ++depth;
            ++pse->num_comparisons;
          }
      }

      /* See if the current node in the AVL tree hash-bucket 
       * is the exact same connection as ourselves,
       * either in A2B or B2A directions.
       */
          
      dir = WhichDir(&tp_in, &ptph->addr_pair);
                
      if (dir == A2B || dir == B2A) {
          /* OK, this looks good, suck it into memory */
        
          tcb *thisdir;
          tcb *otherdir;
          tcp_pair *ptp;
          if (run_continuously) {
            ptp_ptr *ptr = (ptp_ptr *)ptph->ptp;
            ptp = ptr->ptp;
          }
          else {
            ptp = (tcp_pair *)ptph->ptp;
          }
        
          /* figure out which direction this packet is going */
          if (dir == A2B) {
            thisdir  = &ptp->a2b;
            otherdir = &ptp->b2a;
          } else {
            thisdir  = &ptp->b2a;
            otherdir = &ptp->a2b;
          }
        
          /* check for "inactive" */
          /* (this shouldn't happen anymore, they aren't on the list */
          if (ptp->inactive) {
           
            if (!run_continuously)
                continue;
            else {
                *tcp_ptr = (ptp_ptr *)ptph->ptp;
                return ((*tcp_ptr)->ptp);
            }
          }
        
        
          /* Fri Oct 16, 1998 */
          /* note: original heuristic was not sufficient.  Bugs */
          /* were pointed out by Brian Utterback and later by */
          /* myself and Mark Allman */
        
          if (!run_continuously) { 
            /* check for NEW connection on these same endpoints */
            /* 1) At least 4 minutes idle time */
            /*  OR */
            /* 2) heuristic (we might miss some) either: */
            /*    this packet has a SYN */
            /*    last conn saw both FINs and/or RSTs */
            /*    SYN sequence number outside last window (rfc 1122) */
            /*      (or less than initial Sequence, */
            /*       for wrap around trouble)  - Tue Nov  3, 1998*/
            /*  OR */
            /* 3) this is a SYN, last had a SYN, seq numbers differ */
            /* if so, mark it INACTIVE and skip from now on */
            if (0 && SYN_SET(ptcp)) {
                /* better keep this debugging around, it keeps breaking */
                printf("elapsed: %f sec\n",
                     elapsed(ptp->last_time,current_time)/1000000);
                printf("SYN_SET: %d\n", SYN_SET(ptcp));
                printf("a2b.fin_count: %d\n", ptp->a2b.fin_count);
                printf("b2a.fin_count: %d\n", ptp->b2a.fin_count);
                printf("a2b.reset_count: %d\n", ptp->a2b.reset_count);
                printf("b2a.reset_count: %d\n", ptp->b2a.reset_count);
                printf("dir: %d (%s)\n", dir, dir==A2B?"A2B":"B2A");
                printf("seq:    %lu \n", (u_long)ntohl(ptcp->th_seq));
                printf("winend: %lu \n", otherdir->windowend);
                printf("syn:    %lu \n", otherdir->syn);
                printf("SEQ_GREATERTHAN winend: %d\n", 
                     SEQ_GREATERTHAN(ntohl(ptcp->th_seq),otherdir->windowend));
                printf("SEQ_LESSTHAN init syn: %d\n", 
                     SEQ_LESSTHAN(ntohl(ptcp->th_seq),thisdir->syn));
            } 
           
            if (/* rule 1 */
                (elapsed(ptp->last_time,current_time)/1000000 > nonreal_live_conn_interval)//(4*60)) - Using nonreal_live_conn_interval instead of the 4 mins heuristic
                || /* rule 2 */
                ((SYN_SET(ptcp)) && 
                 (((thisdir->fin_count >= 1) ||
                   (otherdir->fin_count >= 1)) ||
                  ((thisdir->reset_count >= 1) ||
                   (otherdir->reset_count >= 1))) &&
                 (SEQ_GREATERTHAN(ntohl(ptcp->th_seq),otherdir->windowend) ||
                  SEQ_LESSTHAN(ntohl(ptcp->th_seq),thisdir->syn)))
                || /* rule 3 */
                (SYN_SET(ptcp) &&
                 (thisdir->syn_count > 1) &&
                 (thisdir->syn != ntohl(ptcp->th_seq)))) {
            
                if (debug>1) {
                  printf("%s: Marking %p %s<->%s INACTIVE (idle: %f sec)\n",
                         ts2ascii(&current_time),
                         ptp,
                         ptp->a_endpoint, ptp->b_endpoint,
                         elapsed(ptp->last_time,
                               current_time)/1000000);
                  if (debug > 3)
                      PrintTrace(ptp);
                }
            
                /* we won't need this one anymore, remove it from the */
                /* hash table so we won't have to skip over it */
                ptp->inactive = TRUE;
            
                if (debug > 4)
                  printf("Removing connection from hashtable:\
                          FindTTP() calling SnapRemove()\n");
            
                /* Removes connection snapshot from AVL tree */
                SnapRemove(pptph_head, ptph->addr_pair); 
            
                break;
            }
          }
        
          if (run_continuously) 
            (*tcp_ptr) = (ptp_ptr *)ptph->ptp;
        
          *pdir = dir;
          return (ptp);
      } else {  // WhichDir returned 0, meaning if it exists, it's deeper 
          conn_status = AVL_WhichDir(&tp_in,&ptph->addr_pair);    
          if (conn_status == LT)
            ptph = ptph->left;
          else if (conn_status == RT)
            ptph = ptph->right;
          else if (!conn_status)  {
            fprintf(stderr, "WARNING!! AVL_WhichDir() should not return 0 if\n"
                        "\tWhichDir() didn't return A2B or B2A previously\n");
            break;
          }
      }
    }
   
   
    /* Didn't find it, make a new one, if possible */
    if (0) {
      printf("trace.c:FindTTP() calling MakePtpSnap()\n");
    }
    ptph = MakePtpSnap();
  
    if (run_continuously) {
      ptp_ptr *ptr = (ptp_ptr *)MakePtpPtr();
      ptr->prev = NULL;

      if (live_conn_list_head == NULL) {
          ptr->next = NULL;
          live_conn_list_head = ptr;
          live_conn_list_tail = ptr;
      }
      else {
          ptr->next = live_conn_list_head;
          live_conn_list_head->prev = ptr;
          live_conn_list_head = ptr;
      }
      ptr->from = ptph;
      ptr->ptp = NewTTP(pip, ptcp);
      ptph->addr_pair = ptr->ptp->addr_pair;
      ptph->ptp = (void *)ptr;
      if (conn_num_threshold) {
          active_conn_count++;
          if (active_conn_count > max_conn_num) {
            ptp_ptr *last_ptr = live_conn_list_tail;
            live_conn_list_tail = last_ptr->prev;
            live_conn_list_tail->next = NULL;
            RemoveConn(last_ptr);
            num_removed_tcp_pairs++;
            active_conn_count--;
            FreePtpPtr(last_ptr);
          }
      }
    }
    else {
      tcp_pair *tmp = NewTTP(pip,ptcp);
      ptph->addr_pair = tmp->addr_pair;
      ptph->ptp = tmp;
    }

    /* To insert the new connection snapshot into the AVL tree */
   
    if (debug > 4)
      printf("Inserting connection into hashtable:\
             FindTTP() calling SnapInsert() \n");
    SnapInsert(pptph_head, ptph);
   
    if (pse) {
      /* search efficiency instrumentation */
      ++pse->num_connections;
      if (depth > pse->max_depth)
          pse->max_depth = depth;
      if (pse->num_connections > pse->max_connections)
          pse->max_connections = pse->num_connections;
    }


    *pdir = A2B;
    if (run_continuously) {
      *tcp_ptr = (ptp_ptr *)ptph->ptp;
      return ((*tcp_ptr)->ptp);
    }
    else
      return (tcp_pair *)(ptph->ptp);
}
     
static void 
UpdateConnLists(
            ptp_ptr *tcp_ptr,
            struct tcphdr *ptcp)
{
  time_t real_time;
  static int minutes = 0;

  if (0) {
    printf("trace.c: UpdateConnLists() called\n");
  }

  if ((FinCount(tcp_ptr->ptp) > 0) || (ConnReset(tcp_ptr->ptp))) { 
    /* we have FIN or RST */
    if (!tcp_ptr->ptp->inactive) {
       /* this is the only FIN or new RST - remove from list of active conns */
      if (debug > 6) {
      printf("UpdateConnLists: removing conn from list of active conns\n");
      }
      UpdateConnList(tcp_ptr, FALSE, 
                 &live_conn_list_head, 
                 &live_conn_list_tail);
      tcp_ptr->ptp->inactive = TRUE;

      if (conn_num_threshold) {
      active_conn_count--;
      closed_conn_count++;
      if (closed_conn_count > max_conn_num) {
        ptp_ptr *last_ptr = closed_conn_list_tail;
        closed_conn_list_tail = last_ptr->prev;
        closed_conn_list_tail->next = NULL;
        RemoveConn(last_ptr);
        num_removed_tcp_pairs++;
        closed_conn_count--;
        FreePtpPtr(last_ptr);
      }
      }

      /* put entry into the list of inactive connections */
      if (closed_conn_list_head) {
      tcp_ptr->next = closed_conn_list_head;
      tcp_ptr->prev = NULL;
      closed_conn_list_head->prev = tcp_ptr;
      closed_conn_list_head = tcp_ptr;
      }
      else {
      tcp_ptr->next = NULL;
      tcp_ptr->prev = NULL;
      closed_conn_list_head = tcp_ptr;
      closed_conn_list_tail = tcp_ptr;
      }
    }
    else {
    /* update the list of closed connecitons */
    UpdateConnList(tcp_ptr, TRUE, &closed_conn_list_head, 
               &closed_conn_list_tail);
    }
  }
  else {/* don't have FIN(s)/RST */
    /* update only list of active connections */
     if (tcp_ptr->ptp->inactive == TRUE) {
      printf("WARNING!!! con is inactive, ptr=%p, con=%p, fin=%i, rst=%i\n", tcp_ptr,
             tcp_ptr->ptp, FinCount(tcp_ptr->ptp), ConnReset(tcp_ptr->ptp));
      printf("a2b.reset_count=%i, b2a.reset_count=%i, RESET_SET(tcph)=%i\n",
             tcp_ptr->ptp->a2b.reset_count, tcp_ptr->ptp->b2a.reset_count,
             RESET_SET(ptcp));
     }
    UpdateConnList(tcp_ptr, TRUE, &live_conn_list_head, &live_conn_list_tail);
  }
  
  /* if we haven't updated the structures for at least update_interval number 
   * of seconds, update list of connections and hash table */
  if ((elapsed(last_update_time, current_time) / 1000000) >= update_interval) {

    real_time = time(&real_time);
    if (debug > 10)
      fprintf(stderr, "%3i program time: %i\tcurrent time: %i\tdifference: %i\n",
              ++minutes, (int)current_time.tv_sec, (int)real_time, 
              (int)(real_time - current_time.tv_sec));
    if (conn_num_threshold) {
      RemoveOldConns(&live_conn_list_head, 
                 &live_conn_list_tail, 
                 remove_live_conn_interval, 
                 TRUE,
                 &active_conn_count);
      RemoveOldConns(&closed_conn_list_head,
                 &closed_conn_list_tail, 
                 remove_closed_conn_interval, 
                 TRUE,
                 &closed_conn_count);
    }
    else {
      RemoveOldConns(&live_conn_list_head, 
                 &live_conn_list_tail, 
                 remove_live_conn_interval, 
                 FALSE,
                 0);
      RemoveOldConns(&closed_conn_list_head,
                 &closed_conn_list_tail, 
                 remove_closed_conn_interval, 
                 FALSE,
                 0);
    }
    last_update_time = current_time;
  }
}



static void
UpdateConnList(
             ptp_ptr *tcp_ptr,
             const Bool valid,
             ptp_ptr **conn_list_head,
             ptp_ptr **conn_list_tail)
{
  ptp_ptr *ptr_prev;
  ptp_ptr *ptr_next;

  if (0) {
    printf("UpdateConnList() called\n");
  }
  if (tcp_ptr == (*conn_list_head)) {
    if (valid) {
      return;
    }
    else {
      *conn_list_head = tcp_ptr->next;
      if ((*conn_list_tail) == tcp_ptr)
      *conn_list_tail = NULL;
      else
      (*conn_list_head)->prev = NULL;
      return;
    }
  }

  ptr_prev = tcp_ptr->prev;
  ptr_next = tcp_ptr->next;

  ptr_prev->next = ptr_next;

  if (ptr_next)
    ptr_next->prev = ptr_prev;
  if (tcp_ptr == (*conn_list_tail))
    *conn_list_tail = ptr_prev;

  if (valid) {
    tcp_ptr->next = (*conn_list_head);
    tcp_ptr->prev = NULL;
    (*conn_list_head)->prev = tcp_ptr;
    *conn_list_head = tcp_ptr;
  }
  return;
}



static void
RemoveOldConns(
             ptp_ptr **conn_list_head,
             ptp_ptr **conn_list_tail,
             const unsigned expire_interval,
             const Bool num_conn_check,
             int *conn_count)
{
  ptp_ptr   *ptr;
  ptp_ptr   *prev_ptr;

  if (0) {
    printf("trace.c: RemoveOldConns() called\n");
  }

  if ((*conn_list_tail) == NULL) {
    return;
  }

  ptr = (*conn_list_tail);
  prev_ptr = ptr->prev;
  for (; prev_ptr != NULL; ptr = prev_ptr, prev_ptr = ptr->prev) {
    if ((elapsed(ptr->ptp->last_time, current_time) / 1000000) >= 
      expire_interval) {
      /* if the connection is old enough, remove the snap from the linked-list
       and the hash_table */
      ptr->prev->next = NULL;
      *conn_list_tail = ptr->prev;
      RemoveConn(ptr);
      num_removed_tcp_pairs++;
      if (0) {
      printf("trace.c:RemoveOldConns() calling FreePtpSnap()\n");
      }
      FreePtpPtr(ptr);
      if (num_conn_check)
      --(*conn_count);
    }
    else {
      break;
    }
  }

  if (((*conn_list_head)->ptp->last_time.tv_sec != 0) &&
      ((elapsed((*conn_list_head)->ptp->last_time, current_time) / 1000000) >= 
       expire_interval)) {
    *conn_list_head = NULL;
    *conn_list_tail = NULL;
    RemoveConn(ptr);
    num_removed_tcp_pairs++;
    FreePtpPtr(ptr);
    if (num_conn_check)
      --(*conn_count);
  }
}



/* remove tcp pair from the hash table */
static void
RemoveConn(
         const ptp_ptr *tcp_ptr)
{
  hash            hval;
   
   if (0) {
      printf("trace.c: RemoveConn(%p %s<->%s) called\n", 
           tcp_ptr->ptp, tcp_ptr->ptp->a_endpoint, tcp_ptr->ptp->b_endpoint);
   }
   
   ModulesPerOldConn(tcp_ptr->ptp);
   
   hval = tcp_ptr->ptp->addr_pair.hash % HASH_TABLE_SIZE;
   
   /* Remove the connection snapshot from AVL tree */
   if (debug > 4)
     printf("Removing connection from hashtable:\
             RemoveConn() calling SnapRemove()\n");
   
   SnapRemove(&ptp_hashtable[hval], tcp_ptr->ptp->addr_pair);
   
   RemoveTcpPair(tcp_ptr);
}



static void
RemoveTcpPair(
            const ptp_ptr *tcp_ptr)
{
  int i = 0;
  tcp_pair *ptp = tcp_ptr->ptp;

  if (0) {
    printf("trace.c: RemoveTcpPair(%p) called\n", tcp_ptr->ptp);
  }
  
  free(ptp->a2b.host_letter);
  free(ptp->b2a.host_letter);

  free(ptp->a_hostname);
  free(ptp->a_portname);
  free(ptp->a_endpoint);

  free(ptp->b_hostname);
  free(ptp->b_portname);
  free(ptp->b_endpoint);

  if (ptp->a2b.owin_line) {
    free(ptp->a2b.owin_line);
  }
  
  if (show_rwinline) {
    if (ptp->a2b.rwin_line) {
      free(ptp->a2b.rwin_line);
    }
  }
    
  if (ptp->a2b.owin_avg_line) {
    free(ptp->a2b.owin_avg_line);
  }
  if (ptp->a2b.owin_wavg_line) {
    free(ptp->a2b.owin_avg_line);
  }
  if (ptp->b2a.owin_line) {
    free(ptp->b2a.owin_line);
  }
  
  if (show_rwinline) {
    if (ptp->b2a.rwin_line) {
      free(ptp->b2a.rwin_line);
    }
  }
  
  if (ptp->b2a.owin_avg_line) {
    free(ptp->b2a.owin_avg_line);
  }
  if (ptp->b2a.owin_wavg_line) {
    free(ptp->b2a.owin_wavg_line);
  }

  if (ptp->a2b.segsize_line) {
    free(ptp->a2b.segsize_line);
  }
  if (ptp->a2b.segsize_avg_line) {
    free(ptp->a2b.segsize_avg_line);
  }
  if (ptp->b2a.segsize_line) {
    free(ptp->b2a.segsize_line);
  }
  if (ptp->b2a.segsize_avg_line) {
    free(ptp->b2a.segsize_avg_line);
  }

  if (ptp->a2b.ss) {
    for (i = 0; i < 4; i++) {
      if (ptp->a2b.ss->pquad[i] != NULL) {
      freequad(&ptp->a2b.ss->pquad[i]);
      }
    }
    FreeSeqspace(ptp->a2b.ss);
  }

  if (ptp->b2a.ss) {
    for (i = 0; i < 4; i++) {
      if (ptp->b2a.ss->pquad[i] != NULL) {
      freequad(&ptp->b2a.ss->pquad[i]);
      }
    }
    FreeSeqspace(ptp->b2a.ss);
  }

  FreeTcpPair(ptp);
}



tcp_pair *
dotrace(
    struct ip *pip,
    struct tcphdr *ptcp,
    void *plast)
{
    struct tcp_options *ptcpo;
    tcp_pair      *ptp_save;
    int           tcp_length;
    int           tcp_data_length;
    u_long  start;
    u_long  end;
    tcb           *thisdir;
    tcb           *otherdir;
    tcp_pair      tp_in;
    PLOTTER to_tsgpl;
    PLOTTER from_tsgpl;
    PLOTTER     tlinepl;
    int           dir;
    Bool    retrans;
    Bool    probe;
    Bool    hw_dup = FALSE;   /* duplicate at the hardware level */
    Bool    ecn_ce = FALSE;
    Bool    ecn_echo = FALSE;
    Bool    cwr = FALSE;
    Bool        urg = FALSE;
    int           retrans_num_bytes;
    Bool    out_order;  /* out of order */
    u_short th_sport;   /* source port */
    u_short th_dport;   /* destination port */
    tcp_seq th_seq;           /* sequence number */
    tcp_seq th_ack;           /* acknowledgement number */
    u_short th_win;           /* window */
    u_long  eff_win;    /* window after scaling */
    u_short     th_urp;         /* URGENT pointer */
    short   ip_len;           /* total length */
    enum t_ack    ack_type=NORMAL; /* how should we draw the ACK */
    seqnum  old_this_windowend; /* for graphing */
    ptp_ptr *tcp_ptr = NULL;

    /* make sure we have enough of the packet */
    if ((char *)ptcp + sizeof(struct tcphdr)-1 > (char *)plast) {
      if (warn_printtrunc)
          fprintf(stderr,
                "TCP packet %lu truncated too short to trace, ignored\n",
                pnum);
      ++ctrunc;
      return(NULL);
    }


    /* convert interesting fields to local byte order */
    th_seq   = ntohl(ptcp->th_seq);
    th_ack   = ntohl(ptcp->th_ack);
    th_sport = ntohs(ptcp->th_sport);
    th_dport = ntohs(ptcp->th_dport);
    th_win   = ntohs(ptcp->th_win);
    th_urp   = ntohs(ptcp->th_urp);
    ip_len   = gethdrlength(pip, plast) + getpayloadlength(pip,plast);

    /* make sure this is one of the connections we want */
    ptp_save = FindTTP(pip,ptcp,&dir, &tcp_ptr);

    ++tcp_packet_count;

    if (ptp_save == NULL) {
      return(NULL);
    }

    ++tcp_trace_count;

    if (run_continuously && (tcp_ptr == NULL)) {
      fprintf(stderr, "Did not initialize tcp pair pointer\n");
      exit(1);
    }

    /* do time stats */
    if (ZERO_TIME(&ptp_save->first_time)) {
      ptp_save->first_time = current_time;
    }
    ptp_save->last_time = current_time;


    /* bug fix:  it's legal to have the same end points reused.  The */
    /* program uses a heuristic of looking at the elapsed time from */
    /* the last packet on the previous instance and the number of FINs */
    /* in the last instance.  If we don't increment the fin_count */
    /* before bailing out in "ignore_pair" below, this heuristic breaks */

    /* figure out which direction this packet is going */
    if (dir == A2B) {
      thisdir  = &ptp_save->a2b;
      otherdir = &ptp_save->b2a;
    } else {
      thisdir  = &ptp_save->b2a;
      otherdir = &ptp_save->a2b;
    }

    /* meta connection stats */
    if (SYN_SET(ptcp))
      ++thisdir->syn_count;
    if (RESET_SET(ptcp))
      ++thisdir->reset_count;
    if (FIN_SET(ptcp))
      ++thisdir->fin_count;

    /* end bug fix */


    /* compute the "effective window", which is the advertised window */
    /* with scaling */
    if (ACK_SET(ptcp) || SYN_SET(ptcp)) {
      eff_win = (u_long) th_win;

      /* N.B., the window_scale stored for the connection DURING 3way */
      /* handshaking is the REQUESTED scale.  It's only valid if both */
      /* sides request scaling.  AFTER we've seen both SYNs, that field */
      /* is reset (above) to contain zero.  Note that if we */
      /* DIDN'T see the SYNs, the windows will be off. */
      /* Jamshid: Remember that the window is never scaled in SYN */
      /* packets, as per RFC 1323. */
      if (thisdir->f1323_ws && otherdir->f1323_ws && !SYN_SET(ptcp))
          eff_win <<= thisdir->window_scale;
    } else {
      eff_win = 0;
    }

    
    /* idle-time stats */
    if (!ZERO_TIME(&thisdir->last_time)) {
      u_llong itime = elapsed(thisdir->last_time,current_time);
      if (itime > thisdir->idle_max)
          thisdir->idle_max = itime;
    }
    thisdir->last_time = current_time;
    

    /* calculate data length */
    tcp_length = getpayloadlength(pip, plast);
    tcp_data_length = tcp_length - (4 * TH_OFF(ptcp));

    /* calc. data range */
    start = th_seq;
    end = start + tcp_data_length;

    /* seq. space wrap around stats */
    /* If all four quadrants have been visited and the current packet
     * is in the same quadrant as the syn, check if latest seq. num is
     * wrapping past the syn. If it is, increment wrap_count
     */
    if ((thisdir->quad1 && thisdir->quad2 && thisdir->quad3 && thisdir->quad4)) {
      if ((IN_Q1(thisdir->syn) && (IN_Q1(end))) ||  (IN_Q2(thisdir->syn) && (IN_Q2(end))) || ((IN_Q3(thisdir->syn) && (IN_Q3(end))) || ((IN_Q4(thisdir->syn) && (IN_Q4(end)))))) {
          if (end >= thisdir->syn) {
            if (debug>1)
                fprintf(stderr, "\nWARNING : sequence space wrapped around here \n");
            thisdir->seq_wrap_count++;
            thisdir->quad1=0;
            thisdir->quad2=0;
            thisdir->quad3=0;
            thisdir->quad4=0;
          }
      }
    } 

    /* Mark the visited quadrants */
    if (!thisdir->quad1) {
      if (IN_Q1(start) || IN_Q1(end))
          thisdir->quad1=1;
    } 
    if (!thisdir->quad2) {
      if (IN_Q2(start) || IN_Q2(end))
          thisdir->quad2=1;
    }
    if (!thisdir->quad3) {
      if (IN_Q3(start) || IN_Q3(end))
          thisdir->quad3=1;
    }
    if (!thisdir->quad4) {
      if (IN_Q4(start) || IN_Q4(end))
          thisdir->quad4=1;
    }
    
    /* record sequence limits */
    if (SYN_SET(ptcp)) {
      /* error checking - better not change! */
      if ((thisdir->syn_count > 1) && (thisdir->syn != start)) {
          /* it changed, that shouldn't happen! */
          if (warn_printbad_syn_fin_seq)
            fprintf(stderr, "\
%s->%s: rexmitted SYN had diff. seqnum! (was %lu, now %lu, etime: %d sec)\n",
                  thisdir->host_letter,thisdir->ptwin->host_letter,
                  thisdir->syn, start,
                  (int)(elapsed(ptp_save->first_time,current_time)/1000000));
          thisdir->bad_behavior = TRUE;
      }
      thisdir->syn = start;
      otherdir->ack = start;
            /* bug fix for Rob Austein <sra@epilogue.com> */
    }
    if (FIN_SET(ptcp)) {
      /* bug fix, if there's data here too, we need to bump up the FIN */
      /* (psc data file shows example) */
      u_long fin = start + tcp_data_length;
      /* error checking - better not change! */
      if ((thisdir->fin_count > 1) && (thisdir->fin != fin)) {
          /* it changed, that shouldn't happen! */
          if (warn_printbad_syn_fin_seq)
            fprintf(stderr, "\
%s->%s: rexmitted FIN had diff. seqnum! (was %lu, now %lu, etime: %d sec)\n",
                  thisdir->host_letter,thisdir->ptwin->host_letter,
                  thisdir->fin, fin,
                  (int)(elapsed(ptp_save->first_time,current_time)/1000000));
          thisdir->bad_behavior = TRUE;
      }
      thisdir->fin = fin;
    }

    /* "ONLY" bug fix - Wed Feb 24, 1999 */
    /* the tcp-splicing heuristic needs "windowend", which was only being */
    /* calculated BELOW the "only" point below.  Move that part of the */
    /* calculation up here! */

    /* remember the OLD window end for graphing */
    /* (bug fix - Thu Aug 12, 1999) */
    old_this_windowend = thisdir->windowend;

    if (ACK_SET(ptcp)) {
      thisdir->windowend = th_ack + eff_win;
    }
    /* end bugfix */



    /***********************************************************************/
    /***********************************************************************/
    /* if we're ignoring this connection, do no further processing         */
    /***********************************************************************/
    /***********************************************************************/
    if (ptp_save->ignore_pair) {
      return(ptp_save);
    }

    /* save to a file if requested */
    if (output_filename) {
      PcapSavePacket(output_filename,pip,plast);
    }

    /* now, print it if requested */
    if (printem && !printallofem) {
      printf("Packet %lu\n", pnum);
      printpacket(0,          /* original length not available */
                (char *)plast - (char *)pip + 1,
                NULL,0, /* physical stuff not known here */
                pip,plast,thisdir);
    }

    /* grab the address from this packet */
    CopyAddr(&tp_in.addr_pair, pip,
           th_sport, th_dport);


    /* simple bookkeeping */
    if (PIP_ISV6(pip)) {
      ++thisdir->ipv6_segments;
    }


    /* plotter shorthand */
    to_tsgpl     = otherdir->tsg_plotter;
    from_tsgpl   = thisdir->tsg_plotter;
   
    /* plotter shorthand (NOTE: we are using one plotter for both directions) */
    tlinepl      = thisdir->tline_plotter;

    /* check the options */
    ptcpo = ParseOptions(ptcp,plast);
    if (ptcpo->mss != -1)
      thisdir->mss = ptcpo->mss;
    if (ptcpo->ws != -1) {
      thisdir->window_scale = ptcpo->ws;
      thisdir->f1323_ws = TRUE;
    }
    if (ptcpo->tsval != -1) {
      thisdir->f1323_ts = TRUE;
    }
    /* NOW, unless BOTH sides asked for window scaling in their SYN     */
    /* segments, we aren't using window scaling */
    if (!SYN_SET(ptcp) &&
      ((!thisdir->f1323_ws) || (!otherdir->f1323_ws))) {
      thisdir->window_scale = otherdir->window_scale = 0;
    }

    /* check sacks */
    if (ptcpo->sack_req) {
      thisdir->fsack_req = 1;
    }
    if (ptcpo->sack_count > 0) {
      ++thisdir->sacks_sent;
    }

    /* unless both sides advertised sack, we shouldn't see them, otherwise
       we hope they actually send them */
    if (!SYN_SET(ptcp) && (thisdir->fsack_req && otherdir->fsack_req)) {
      thisdir->tcp_strain = otherdir->tcp_strain = TCP_SACK;
    }

    /* do data stats */
    urg = FALSE;
    if (tcp_data_length > 0) {
      thisdir->data_pkts += 1;
      if (PUSH_SET(ptcp))
          thisdir->data_pkts_push += 1;
      thisdir->data_bytes += tcp_data_length;
        if (URGENT_SET(ptcp)) {     /* Checking if URGENT bit is set */
          urg = TRUE; 
          thisdir->urg_data_pkts += 1;
          thisdir->urg_data_bytes += th_urp;
      }
            if (tcp_data_length > thisdir->max_seg_size)
          thisdir->max_seg_size = tcp_data_length;
      if ((thisdir->min_seg_size == 0) ||
          (tcp_data_length < thisdir->min_seg_size))
          thisdir->min_seg_size = tcp_data_length;
      /* record first and last times for data (Mallman) */
      if (ZERO_TIME(&thisdir->first_data_time))
          thisdir->first_data_time = current_time;
      thisdir->last_data_time = current_time;
    }

    /* total packets stats */
    ++ptp_save->packets;
    ++thisdir->packets;

    /* If we are using window scaling, update the win_scaled_pkts counter */
    if (thisdir->window_stats_updated_for_scaling)
      ++thisdir->win_scaled_pkts;
    
    /* instantaneous throughput stats */
    if (graph_tput) {
      DoThru(thisdir,tcp_data_length);
    }

    /* segment size graphs */
    if ((tcp_data_length > 0) && (thisdir->segsize_plotter != NO_PLOTTER)) {
      extend_line(thisdir->segsize_line, current_time, tcp_data_length);
      extend_line(thisdir->segsize_avg_line, current_time,
                thisdir->data_bytes / thisdir->data_pkts);
    }

    /* sequence number stats */
    if ((thisdir->min_seq == 0) && (start != 0)) {
      thisdir->min_seq = start; /* first byte in this segment */
      thisdir->max_seq = end;   /* last byte in this segment */
    }
    if (SEQ_GREATERTHAN (end,thisdir->max_seq)) {
      thisdir->max_seq = end;
    }
    thisdir->latest_seq = end;


    /* check for hardware duplicates */
    /* only works for IPv4, IPv6 has no mandatory ID field */
    if (PIP_ISV4(pip) && docheck_hw_dups)
      hw_dup = check_hw_dups(pip->ip_id, th_seq, thisdir);


    /* Kevin Lahey's ECN code */
    /* only works for IPv4 */
    if (PIP_ISV4(pip)) {
      ecn_ce = IP_ECT(pip) && IP_CE(pip);
    }
    cwr = CWR_SET(ptcp);
    ecn_echo = ECN_ECHO_SET(ptcp);

    /* save the stream contents, if requested */
    if (tcp_data_length > 0) {
      u_char *pdata = (u_char *)ptcp + TH_OFF(ptcp)*4;
      u_long saved;
      u_long      missing;

      saved = tcp_data_length;
      if ((char *)pdata + tcp_data_length > ((char *)plast+1))
          saved = (char *)plast - (char *)pdata + 1;

      /* see what's missing */
      missing = tcp_data_length - saved;
      if (missing > 0) {
          thisdir->trunc_bytes += missing;
          ++thisdir->trunc_segs;
      }

      if (save_tcp_data)
          ExtractContents(start,tcp_data_length,saved,pdata,thisdir);
    }

    /* do rexmit stats */
    retrans = FALSE;
    probe = FALSE;
    out_order = FALSE;
    retrans_num_bytes = 0;
    if (SYN_SET(ptcp) || FIN_SET(ptcp) || tcp_data_length > 0) {
      int len = tcp_data_length;
      int retrans_cnt=0;
      
      if (SYN_SET(ptcp)) ++len;
      if (FIN_SET(ptcp)) ++len;

                                                
      /* Don't consider for rexmit, if the send window is 0 */
      /* We are probably doing window probing.. */
      /* Patch from Ulisses Alonso Camaro : Not treat the SYN segments
       * as probes, even though a zero window was advertised from the 
       * opposite direction */
      if( (otherdir->win_last==0) && (otherdir->packets > 0) &&
         /* Patch from Ulisses Alonso Camaro : Not treat the SYN segments
          * as probes, even though a zero window was advertised from the 
          * opposite direction */
            (!SYN_SET(ptcp)) ) {
            probe=TRUE;
            thisdir->num_zwnd_probes++;   
            thisdir->zwnd_probe_bytes += tcp_data_length;
      }
      else
            retrans_cnt = retrans_num_bytes = rexmit(thisdir,start, len, &out_order);

      if (out_order)
          ++thisdir->out_order_pkts;

      /* count anything NOT retransmitted as "unique" */
      /* exclude SYN and FIN */
      if (SYN_SET(ptcp)) {
          /* don't count the SYN as data */
          --len;
          /* if the SYN was rexmitted, then don't count it */
          if (thisdir->syn_count > 1)
            --retrans_cnt;
      }
      if (FIN_SET(ptcp)) {
          /* don't count the FIN as data */
          --len;
          /* if the FIN was rexmitted, then don't count it */
          if (thisdir->fin_count > 1)
            --retrans_cnt;
      }
      if (!probe){
            if(retrans_cnt < len)
            thisdir->unique_bytes += (len - retrans_cnt);
          }
    }


    /* do rtt stats */
    if (ACK_SET(ptcp)) {
      ack_type = ack_in(otherdir,th_ack,tcp_data_length,eff_win);

      if ( (th_ack == (otherdir->syn+1)) &&
             (otherdir->syn_count == 1) )
             otherdir->rtt_3WHS=otherdir->rtt_last; 
             /* otherdir->rtt_last was set in the call to ack_in() */
      
        otherdir->lastackno = th_ack;     
    }

    /* LEAST */
    if (thisdir->tcp_strain == TCP_RENO) {
      if (thisdir->in_rto && tcp_data_length > 0) {
        if (retrans_num_bytes>0 && th_seq < thisdir->recovered)
          thisdir->event_retrans++;
        if (IsRTO(thisdir, th_seq)) {
          thisdir->recovered = thisdir->recovered_orig = thisdir->seq;
          thisdir->rto_segment = th_seq;
        }
        if (!(retrans_num_bytes>0) && thisdir->ack <= thisdir->recovered_orig)
          thisdir->recovered = th_seq;
      }
      if (otherdir->in_rto && ACK_SET(ptcp)) {
        if (th_ack > otherdir->recovered) {
          otherdir->LEAST -=
            (otherdir->event_dupacks < otherdir->event_retrans)?
             otherdir->event_dupacks:otherdir->event_retrans;
          otherdir->in_rto = FALSE;
        } else if (th_ack == otherdir->lastackno &&
                   th_ack >= otherdir->rto_segment) otherdir->event_dupacks++;
      }
    }

    /* plot out-of-order segments, if asked */
    if (out_order && (from_tsgpl != NO_PLOTTER) && show_out_order) {
      plotter_perm_color(from_tsgpl, out_order_color);
      plotter_text(from_tsgpl, current_time, SeqRep(thisdir,end),
                 "a", "O");
      if (bottom_letters)
          plotter_text(from_tsgpl, current_time,
                   SeqRep(thisdir,thisdir->min_seq)-1500,
                   "c", "O");
    }

    /* stats for rexmitted data */
    if (retrans_num_bytes>0) {
      retrans = TRUE;
        /* for reno LEAST estimate */
        if (thisdir->tcp_strain == TCP_RENO &&
            !thisdir->in_rto && IsRTO(thisdir, th_seq)) {
          thisdir->in_rto = TRUE;
          thisdir->recovered = thisdir->recovered_orig = thisdir->seq;
          thisdir->rto_segment = th_seq;
          thisdir->event_retrans = 1; thisdir->event_dupacks = 0;
        }
      thisdir->rexmit_pkts += 1;
      thisdir->LEAST++;
      thisdir->rexmit_bytes += retrans_num_bytes;
      /* don't color the SYNs and FINs, it's confusing, we'll do them */
      /* differently below... */
      if (!(FIN_SET(ptcp)||SYN_SET(ptcp)) &&
          from_tsgpl != NO_PLOTTER && show_rexmit) {
          plotter_perm_color(from_tsgpl, retrans_color);
          plotter_text(from_tsgpl, current_time, SeqRep(thisdir,end),
                   "a", hw_dup?"HD":"R");
          if (bottom_letters)
            plotter_text(from_tsgpl, current_time,
                       SeqRep(thisdir,thisdir->min_seq)-1500,
                       "c", hw_dup?"HD":"R");
      }
    } else {
      thisdir->seq = end;
    }
   
    if(probe) {
        if(from_tsgpl != NO_PLOTTER && show_zwnd_probes){
          plotter_perm_color(from_tsgpl,probe_color);
          plotter_text(from_tsgpl,current_time,SeqRep (thisdir,end),
                    "b", "P");
       }
     }

    /* draw the packet */
    if (from_tsgpl != NO_PLOTTER) {
      plotter_perm_color(from_tsgpl, data_color);
      if (SYN_SET(ptcp)) {          /* SYN  */
          /* if we're using time offsets from zero, it's easier if */
          /* both graphs (a2b and b2a) start at the same point.  That */
          /* will only happen if the "left-most" graphic is in the */
          /* same place in both.  To make sure, mark the SYNs */
          /* as a green dot in the other direction */
          if (ACK_SET(ptcp)) {
            plotter_temp_color(from_tsgpl, ack_color);
            plotter_dot(from_tsgpl,
                      ptp_save->first_time, SeqRep(thisdir,start));
          }
          plotter_perm_color(from_tsgpl,
                         hw_dup?hw_dup_color:
                         retrans_num_bytes>0?retrans_color:
                         synfin_color);
          plotter_diamond(from_tsgpl, current_time, SeqRep(thisdir,start));
          plotter_text(from_tsgpl, current_time,
                   SeqRep(thisdir,start+1), "a",
                   hw_dup?"HD SYN":
                   retrans_num_bytes>0?"R SYN":
                   "SYN");
          plotter_uarrow(from_tsgpl, current_time, SeqRep(thisdir,start+1));
          plotter_line(from_tsgpl,
                   current_time, SeqRep(thisdir,start),
                   current_time, SeqRep(thisdir,start+1));
      } else if (FIN_SET(ptcp)) {   /* FIN  */
         /* Wed Sep 18, 2002 - bugfix
          * Check if data is present in the last packet.
          * We will draw the data bytes with the normal color
          * and then change the color for the last byte of FIN.
          */
          if(tcp_data_length > 0) { /* DATA + FIN */
             /* Data - default color */
             plotter_darrow(from_tsgpl, current_time, SeqRep(thisdir,start));
             plotter_line(from_tsgpl,
                      current_time, SeqRep(thisdir,start),
                      current_time, SeqRep(thisdir,end));
             /* FIN - synfin color */
             plotter_perm_color(from_tsgpl,
                          hw_dup?hw_dup_color:
                          retrans_num_bytes>0?retrans_color:
                          synfin_color);
          }
          else { /* Only FIN */
             /* FIN - synfin color */            
             plotter_perm_color(from_tsgpl,
                          hw_dup?hw_dup_color:
                          retrans_num_bytes>0?retrans_color:
                          synfin_color);
             plotter_darrow(from_tsgpl, current_time, SeqRep(thisdir,end));
          }
          plotter_line(from_tsgpl,
                   current_time, SeqRep(thisdir,end),
                   current_time, SeqRep(thisdir,end+1));
          plotter_box(from_tsgpl, current_time, SeqRep(thisdir,end+1));
          plotter_text(from_tsgpl, current_time,
                   SeqRep(thisdir,end+1), "a",
                   hw_dup?"HD FIN":
                   retrans_num_bytes>0?"R FIN":
                   "FIN");
         
      } else if (tcp_data_length > 0) {         /* DATA */
          if (hw_dup) {
            plotter_perm_color(from_tsgpl, hw_dup_color);
          } else if (retrans) {
            plotter_perm_color(from_tsgpl, retrans_color);
          }
          plotter_darrow(from_tsgpl, current_time, SeqRep(thisdir,start));
          if (PUSH_SET(ptcp)) {
            /* colored diamond is PUSH */
            plotter_temp_color(from_tsgpl, push_color);
            plotter_diamond(from_tsgpl,
                        current_time, SeqRep(thisdir,end));
            plotter_temp_color(from_tsgpl, push_color);
            plotter_dot(from_tsgpl, current_time, SeqRep(thisdir,end));
          } else {
            plotter_uarrow(from_tsgpl, current_time, SeqRep(thisdir,end));
          }
          plotter_line(from_tsgpl,
                   current_time, SeqRep(thisdir,start),
                   current_time, SeqRep(thisdir,end));
      } else if (tcp_data_length == 0) {
          /* for Brian Utterback */
          if (graph_zero_len_pkts) {
            /* draw zero-length packets */
            /* shows up as an X, really two arrow heads */
            plotter_darrow(from_tsgpl,
                         current_time, SeqRep(thisdir,start));
            plotter_uarrow(from_tsgpl,
                         current_time, SeqRep(thisdir,start));
          }
      }

      /* Kevin Lahey's code */
      /* XXX:  can this overwrite other labels!? */
      if (cwr || ecn_ce) {
          plotter_perm_color(from_tsgpl, ecn_color);
          plotter_diamond(from_tsgpl,
                      current_time, SeqRep(thisdir,start));
          plotter_text(from_tsgpl, current_time, SeqRep(thisdir, start), "a",
                   cwr ? (ecn_ce ? "CWR CE" : "CWR") : "CE");
      }
       
    }
   
    /* Plotting URGENT data */
    if(urg) {
        if(from_tsgpl != NO_PLOTTER && show_urg){
          plotter_perm_color(from_tsgpl,urg_color);
          plotter_text(from_tsgpl,current_time,SeqRep (thisdir,end),
                     "a", "U");
       } 
    }
   
   /* graph time line */
   /* Since the axis types have been switched specially for these graphs,
    * x is actually used as y and y as x
    * -Avinash.
    * 
    * NOTE: This code is lacking about a 1000 lines of intellegence that is needed
    * ----- to draw these graphs correctly. I have left it in here as the starting
    *       point to work on. Whoever is working on this project would want to clean
    *       up this file trace.c (based on the patches in the README.tline_graphs
    *       file), and continue development as a seperate module. We started this
    *       project thinking it is easy to draw these graphs, and then realized that
    *       it is infact quite a complicated task. All this works with a -L option at
    *       command line.
    */ 
   if (tlinepl != NO_PLOTTER) {
      char buf1[200];
      char buf2[50];
      static seqnum a2b_first_seqnum = 0;
      static seqnum b2a_first_seqnum = 0;
      /* 1/3rd rtt. Since we have the timestamps only on one side, we calculate the 
       * arrrival/departure time of the segments on the other side by adding/subtracting
       * 1/3rd rtt. We assume that it takes 1/3rd time for the segment to travel in
       * either direction, and 1/3rd time for processing.
       * We also skew the calculated times so that the acks are not seen before the 
       * segments actually arrive.
       */ 
      struct timeval one3rd_rtt;                  
      struct timeval copy_current_time;   
      /* Make a copy of the current time (Needed for calculations) */
      copy_current_time.tv_sec  = current_time.tv_sec;
      copy_current_time.tv_usec = current_time.tv_usec;
      /* Compute 1/3rd rtt */
      one3rd_rtt.tv_sec  = 0;
      one3rd_rtt.tv_usec = thisdir->rtt_last/3;
      /* Adjust seconds and microseconds */
      while(one3rd_rtt.tv_usec >= US_PER_SEC) {
       one3rd_rtt.tv_usec -= US_PER_SEC;
       one3rd_rtt.tv_sec += 1;
      }
      
      /* Initializations */
      memset(&buf1, 0, sizeof(buf1));
      memset(&buf2, 0, sizeof(buf2));
      
      /* Segment information */
      /* Check the flags */
      if(SYN_SET(ptcp))
      strncat(buf1, "SYN ", 4);
      if(FIN_SET(ptcp))
      strncat(buf1, "FIN ", 4);
      if(RESET_SET(ptcp))
      strncat(buf1, "RST ", 4);
      if(PUSH_SET(ptcp))
      strncat(buf1, "PSH ", 4);
      if(URGENT_SET(ptcp))
      strncat(buf1, "URG ", 4);
      
      
      /* Write the sequence numbers */
      if(dir == A2B) {
       /* Use relative sequence numbers after the first segment in either direction */
       snprintf(buf2, sizeof(buf2), "%lu:%lu(%lu) ", (start - a2b_first_seqnum),
              (end - a2b_first_seqnum), (end-start));
       strncat(buf1, buf2, strlen(buf2));
       if(a2b_first_seqnum == 0 && !SYN_SET(ptcp)) // Don't use relative sequence numbers until handshake is complete.
         a2b_first_seqnum = thisdir->min_seq;
      }else if(dir == B2A) {
       /* Use relative sequence numbers after the first segment in either direction */
       snprintf(buf2, sizeof(buf2), "%lu:%lu(%lu) ", (start - b2a_first_seqnum),
              (end - b2a_first_seqnum), (end-start));
       strncat(buf1, buf2, strlen(buf2));
       if(b2a_first_seqnum == 0 && !SYN_SET(ptcp))
         b2a_first_seqnum = thisdir->min_seq;
      }
      
      /* Acknowledgements */
      if(ACK_SET(ptcp)) {
       memset(&buf2, 0, sizeof(buf2));
       if(dir == A2B)
         snprintf(buf2, sizeof(buf2), "ack %lu ", (th_ack - b2a_first_seqnum));
       else if(dir == B2A)
         snprintf(buf2, sizeof(buf2), "ack %lu ", (th_ack - a2b_first_seqnum));
       strncat(buf1, buf2, strlen(buf2));
      }
      
      /* Advertised Window */
       memset(&buf2, 0, sizeof(buf2));
       snprintf(buf2, sizeof(buf2), "win %lu ", eff_win);
       strncat(buf1, buf2, strlen(buf2));
      
      /* Retransmits */
      if(retrans) {
       memset(&buf2, 0, sizeof(buf2));
       snprintf(buf2, sizeof(buf2), "R ");
       strncat(buf1, buf2, strlen(buf2));
      }
      
      /* Hardware Duplicates */ 
      if(hw_dup) {
       memset(&buf2, 0, sizeof(buf2));
       snprintf(buf2, sizeof(buf2), "HD ");
       strncat(buf1, buf2, strlen(buf2));
      }
      
      /* Draw the segment ------>/<------- */
      if(dir == A2B) {
       tv_add(&copy_current_time, one3rd_rtt);
       plotter_line(tlinepl, ptp_save->first_time, tline_left, copy_current_time, tline_left);
       plotter_line(tlinepl, ptp_save->first_time, tline_right, copy_current_time, tline_right);
       if(SYN_SET(ptcp)|| FIN_SET(ptcp) || RESET_SET(ptcp))
         plotter_perm_color(tlinepl, synfin_color);
       else
         plotter_perm_color(tlinepl, a2b_seg_color);
       plotter_line(tlinepl, current_time, tline_left, copy_current_time, tline_right);
       plotter_rarrow(tlinepl, copy_current_time, tline_right);
       plotter_perm_color(tlinepl, default_color);
       plotter_text(tlinepl, current_time, tline_left, "l", buf1);
      }
      else if(dir == B2A) {
       tv_sub(&copy_current_time, one3rd_rtt);
       plotter_line(tlinepl, ptp_save->first_time, tline_left, copy_current_time, tline_left);
       plotter_line(tlinepl, ptp_save->first_time, tline_right, copy_current_time, tline_right);
       if(SYN_SET(ptcp)|| FIN_SET(ptcp) || RESET_SET(ptcp))
         plotter_perm_color(tlinepl, synfin_color);
       else
         plotter_perm_color(tlinepl, b2a_seg_color);
       plotter_line(tlinepl, copy_current_time, tline_right, current_time, tline_left);
       plotter_larrow(tlinepl, current_time, tline_left);
       plotter_perm_color(tlinepl, default_color);          
       plotter_text(tlinepl, copy_current_time, tline_right, "r", buf1);
      }
      
   }
   

    /* check for RESET */
    if (RESET_SET(ptcp)) {
      u_long plot_at;

      /* if there's an ACK in this packet, plot it there */
      /* otherwise, plot it at the last valid ACK we have */
      if (ACK_SET(ptcp))
          plot_at = th_ack;
      else
          plot_at = thisdir->ack;

      if (to_tsgpl != NO_PLOTTER) {
          plotter_temp_color(to_tsgpl, text_color);
          plotter_text(to_tsgpl,
                   current_time, SeqRep(otherdir,plot_at),
                   "a", "RST_IN");
      }
      if (from_tsgpl != NO_PLOTTER) {
          plotter_temp_color(from_tsgpl, text_color);
          plotter_text(from_tsgpl,
                   current_time, SeqRep(thisdir,start),
                   "a", "RST_OUT");
      }
      if (ACK_SET(ptcp))
          ++thisdir->ack_pkts;

        if (run_continuously) {
            UpdateConnLists(tcp_ptr, ptcp); 
        }
      return(ptp_save);
    }
   
    /* do window stats (include first SYN too!) */
      thisdir->win_last=eff_win;

    if (ACK_SET(ptcp) || SYN_SET(ptcp)) {
      if (eff_win > thisdir->win_max)
          thisdir->win_max = eff_win;

      /* If we *are* going to use window scaling,
       * i.e., if we saw both SYN segments of the connection requesting
       * window scaling, we flush out all the window stats we gathered till
       * now from the SYN segments.
       * 
       * o We set the flag window_stats_updated_for_scaling to TRUE
       * o Set win_min and win_max to the value found in this first
       *   window-scaled segment
       * o Reset win_tot value too, as this is used to calculate the
       *   average window advertisement seen in this direction at the end
       * o We also use the field win_scaled_pkts for this purpose, so that
       *   in the end we calculate
       * 
       *   avg_win_adv = win_tot/win_scaled_pkts // Refer output.c
       * 
       * Note : for a connection that doesn't use window scaling,
       * 
       *   avg_win_adv = win_tot/packets        // Refer again to output.c
       */

      if ( (eff_win>0) && 
           ( thisdir->f1323_ws && otherdir->f1323_ws && !SYN_SET(ptcp) &&
            !thisdir->window_stats_updated_for_scaling  
           ) 
         ) {
           thisdir->window_stats_updated_for_scaling=TRUE;
           thisdir->win_min = thisdir->win_max = eff_win;
           thisdir->win_tot = 0;
           thisdir->win_scaled_pkts = 1;
      }
      else if ((eff_win > 0) &&
          ((thisdir->win_min == 0) ||
           (eff_win < thisdir->win_min)))
          thisdir->win_min = eff_win;
      
      /* Add the window advertisement to win_tot */
      thisdir->win_tot += eff_win;
    }

    /* draw the ack and win in the other plotter */
    if (ACK_SET(ptcp)) {
      seqnum ack = th_ack;
      u_long winend;

      winend = ack + eff_win;
      
      if (eff_win == 0) {
          ++thisdir->win_zero_ct;
          if (to_tsgpl != NO_PLOTTER && show_zero_window) {
            plotter_temp_color(to_tsgpl, text_color);
            plotter_text(to_tsgpl,
                       current_time, SeqRep(otherdir,winend),
                       "a", "Z");
            if (bottom_letters) {
                plotter_temp_color(to_tsgpl, text_color);
                plotter_text(to_tsgpl,
                         current_time,
                         SeqRep(otherdir,otherdir->min_seq)-1500,
                         "a", "Z");
            }
          }
      }

      ++thisdir->ack_pkts;
      if ((tcp_data_length == 0) &&
          !SYN_SET(ptcp) && !FIN_SET(ptcp) && !RESET_SET(ptcp)) {
          ++thisdir->pureack_pkts;
      }
          

      if (to_tsgpl != NO_PLOTTER && thisdir->time.tv_sec != -1) {
          plotter_perm_color(to_tsgpl, ack_color);
          plotter_line(to_tsgpl,
                   thisdir->time, SeqRep(otherdir,thisdir->ack),
                   current_time, SeqRep(otherdir,thisdir->ack));
          if (thisdir->ack != ack) {
            plotter_line(to_tsgpl,
                       current_time, SeqRep(otherdir,thisdir->ack),
                       current_time, SeqRep(otherdir,ack));
            if (show_rtt_dongles) {
                /* draw dongles for "interesting" acks */
                switch (ack_type) {
                  case NORMAL:      /* normal case */
                  /* no dongle */
                  break;
                  case CUMUL: /* cumulative */
                  /* won't happen, not plotted here */
                  break;
                  case TRIPLE:      /* triple dupacks */
                  /* won't happen, not plotted here */
                  break;
                  case AMBIG: /* ambiguous */
                  plotter_temp_color(to_tsgpl, ackdongle_ambig_color);
                  plotter_diamond(to_tsgpl, current_time,
                              SeqRep(otherdir,ack));
                  break;
                  case NOSAMP:      /* acks retransmitted stuff cumulatively */
                  plotter_temp_color(to_tsgpl, ackdongle_nosample_color);
                  plotter_diamond(to_tsgpl, current_time,
                              SeqRep(otherdir,ack));
                  break;
                }
            }
          } else {
            plotter_dtick(to_tsgpl, current_time, SeqRep(otherdir,ack));
            if (show_triple_dupack && (ack_type == TRIPLE)) {
                plotter_text(to_tsgpl, current_time,
                         SeqRep(otherdir,ack),
                         "a", "3");  /* '3' is for triple dupack */
            }
          }

          /* Kevin Lahey's code */
          if (ecn_echo && !SYN_SET(ptcp)) {
              plotter_perm_color(to_tsgpl, ecn_color);
            plotter_diamond(to_tsgpl, current_time, SeqRep(otherdir, ack));
          }

          plotter_perm_color(to_tsgpl, window_color);
          plotter_line(to_tsgpl,
                   thisdir->time, SeqRep(otherdir,old_this_windowend),
                   current_time, SeqRep(otherdir,old_this_windowend));
          if (old_this_windowend != winend) {
            plotter_line(to_tsgpl,
                       current_time, SeqRep(otherdir,old_this_windowend),
                       current_time, SeqRep(otherdir,winend));
          } else {
            plotter_utick(to_tsgpl, current_time, SeqRep(otherdir,winend));
          }
      }

      /* track the most sack blocks in a single ack */
      if (ptcpo->sack_count > 0) {
          ++thisdir->num_sacks;
          if (ptcpo->sack_count > thisdir->max_sack_blocks) 
            thisdir->max_sack_blocks = ptcpo->sack_count;

      /* also see if any of them are DSACKS - weddy */
      /* eventually may come back and fix this, what if we+++++
         didn't see all the rexmits and so LEAST wesn't set
         high enough, now it's too low */
          /* case 1, first block under cumack */
          if (ptcpo->sacks[0].sack_right <= th_ack) {
              thisdir->num_dsacks++;
              if (otherdir->LEAST > 0) otherdir->LEAST--;
          /* case 2, first block inside second */
          } else if (ptcpo->sack_count > 1) {
              if (ptcpo->sacks[0].sack_right <= ptcpo->sacks[1].sack_right
                  && ptcpo->sacks[0].sack_left >= ptcpo->sacks[1].sack_left)
              {
                  thisdir->num_dsacks++;
                  if (otherdir->LEAST > 0) otherdir->LEAST--;
          /* case 3, first and second block overlap */
              } else if ((ptcpo->sacks[0].sack_left <=
                          ptcpo->sacks[1].sack_left &&
                        ptcpo->sacks[0].sack_right >
                          ptcpo->sacks[1].sack_left) ||
                         (ptcpo->sacks[0].sack_right >=
                          ptcpo->sacks[1].sack_right &&
                        ptcpo->sacks[0].sack_left <
                          ptcpo->sacks[1].sack_right)) {
                    thisdir->num_dsacks++;
                  if (otherdir->LEAST > 0) otherdir->LEAST--;
              }
          }
          /* if we saw any dsacks from the other guy, we'll assume he did
               it on purpose and is a dsack tcp */
            if (thisdir->num_dsacks > 0) thisdir->tcp_strain = TCP_DSACK;
      }

      /* draw sacks, if appropriate */
      if (to_tsgpl != NO_PLOTTER && show_sacks
          && (ptcpo->sack_count > 0)) {
          int scount;
          seqnum sack_top = ptcpo->sacks[0].sack_right;

          plotter_perm_color(to_tsgpl, sack_color);
          for (scount = 0; scount < ptcpo->sack_count; ++scount) {
            plotter_line(to_tsgpl,
                       current_time,
                       SeqRep(otherdir,ptcpo->sacks[scount].sack_left),
                       current_time,
                       SeqRep(otherdir,ptcpo->sacks[scount].sack_right));
            /* make it easier to read multiple sacks by making them look like
               |-----|  (sideways)
            */
            plotter_htick(to_tsgpl,
                        current_time,
                        SeqRep(otherdir,ptcpo->sacks[scount].sack_left));
            plotter_htick(to_tsgpl,
                        current_time,
                        SeqRep(otherdir,ptcpo->sacks[scount].sack_right));

            /* if there's more than one, label the order */
            /* purple number to the right of the top ("right" edge) */
            if (ptcpo->sack_count > 1) {
                char buf[5]; /* can't be more than 1 digit! */
                snprintf(buf,sizeof(buf),"%u",scount+1);    /* 1-base, rather than 0-base */
                plotter_text(to_tsgpl,
                         current_time,
                         SeqRep(otherdir,ptcpo->sacks[scount].sack_right),
                         "r", buf);
            }

            /* maintain the highest SACK so we can label them all at once */
            if (SEQ_GREATERTHAN(ptcpo->sacks[scount].sack_right, sack_top))
                sack_top = ptcpo->sacks[scount].sack_right;
          }
          /* change - just draw the 'S' above the highest one */
          plotter_text(to_tsgpl, current_time,
                   SeqRep(otherdir,sack_top),
                   "a", "S");  /* 'S' is for Sack */
      }
      thisdir->time = current_time;
      thisdir->ack = ack;

/*    thisdir->windowend = winend;  (moved above "only" point) */
    }  /* end ACK_SET(ptcp) */

    /* do stats for initial window (first slow start) */
    /* (if there's data in this and we've NEVER seen */
    /*  an ACK coming back from the other side) */
    /* this is for Mark Allman for slow start testing -- Mon Mar 10, 1997 */
    if (!otherdir->data_acked && ACK_SET(ptcp)
      && ((otherdir->syn+1) != th_ack)) {
      otherdir->data_acked = TRUE;
    }
    if ((tcp_data_length > 0) && (!thisdir->data_acked)) {
      if (!retrans) {
          /* don't count it if it was retransmitted */
          thisdir->initialwin_bytes += tcp_data_length;
          thisdir->initialwin_segs += 1;
      }
    }

    /* do stats for congestion window (estimated) */
    /* estimate the congestion window as the number of outstanding */
    /* un-acked bytes */
    if (!SYN_SET(ptcp) && !out_order && !retrans) {
      u_long owin;
      /* If there has been no ack from the other direction, owin is just 
       * bytes in this pkt.
       */
      if (otherdir->ack == 0){
            owin = end - start ;
      }
      else {
            /* ack  always acks 'received + 1' bytes, so subtract 1 
             * for owin */
            owin = end - (otherdir->ack - 1);
      }
      
      if (owin > thisdir->owin_max)
          thisdir->owin_max = owin;
      if ((owin > 0) &&
          ((thisdir->owin_min == 0) ||
           (owin < thisdir->owin_min)))
          thisdir->owin_min = owin;
      
      thisdir->owin_tot += owin;    
            thisdir->owin_count++;

      /* adding mark's suggestion of weighted owin */
      if (thisdir->previous_owin_sample_time.tv_sec == 0) {
        /* if this is first ever sample for thisdir */
            thisdir->previous_owin_sample_time = thisdir->last_time;
            thisdir->previous_owin_sample = owin;
      }
      else { 
            /* weight each owin sample with the duration that it exists for */
            sample_elapsed_time = (elapsed(thisdir->previous_owin_sample_time, ptp_save->last_time))/1000000;
            total_elapsed_time = (elapsed(ptp_save->first_time, ptp_save->last_time))/1000000;
            thisdir->owin_wavg += (u_llong)((thisdir->previous_owin_sample) * sample_elapsed_time);
            /* graph owin_wavg */
            if (thisdir->owin_plotter != NO_PLOTTER) {
                  extend_line(thisdir->owin_wavg_line, thisdir->previous_owin_sample_time,
                        (total_elapsed_time)?((u_llong)((thisdir->owin_wavg)/total_elapsed_time)):0);
            } 
            thisdir->previous_owin_sample_time = thisdir->last_time;
            thisdir->previous_owin_sample = owin;
      }

      /* graph owin */
      if (thisdir->owin_plotter != NO_PLOTTER) {
          extend_line(thisdir->owin_line, current_time, owin);
          if (show_rwinline) {
            extend_line(thisdir->rwin_line, current_time, 
                    otherdir->win_last);
          }
          extend_line(thisdir->owin_avg_line, current_time,
                  (thisdir->owin_count?(thisdir->owin_tot/thisdir->owin_count):0)); 
      }
    }
    if (run_continuously) {
      UpdateConnLists(tcp_ptr, ptcp);
    }

    return(ptp_save);
}



void
trace_done(void)
{
  tcp_pair *ptp;
  FILE *f_passfilter = NULL;
  int ix;
  static int count = 0;
  Bool incomplete_pkt_capture = FALSE;
  
  if (!run_continuously) {
    if (!printsuppress) {
      if (tcp_trace_count == 0) {
          fprintf(stdout,"%sno traced TCP packets\n", comment);
          return;
      } else {
          fprintf(stdout,"%sTCP connection info:\n", comment);
      }
    }

    if (!printbrief)
      fprintf(stdout,"%s%d TCP %s traced:\n",
            comment,
            num_tcp_pairs + 1,
            num_tcp_pairs==0?"connection":"connections");
    if (ctrunc > 0) {
      fprintf(stdout,
            "%s*** %lu packets were too short to process at some point\n",
            comment,
            ctrunc);
      if (!warn_printtrunc)
          fprintf(stdout,"%s\t(use -w option to show details)\n", comment);
    }

    /* generate statistics for data storage efficiency */
    if (debug>1) {
      int h;
      int occupied_buckets = 0;
      int max_bucket_occupancy = 0;
      int max_searches = 0;
      int max_depth = 0;
      int max_comparisons = 0;
      float max_searches_compare = 0.0;
      fprintf(stdout,"%sTotal searches: %u\n", comment, tcp_packet_count);
      fprintf(stdout,"%s  Total comparisons: %u\n", comment, search_count);
      fprintf(stdout,"%s  Average compares/search: %.2f\n",
            comment, (float)search_count / (float)tcp_packet_count);
      fprintf(stdout,"%sHash table size: %u\n", comment, HASH_TABLE_SIZE);
      for (h=0; h < HASH_TABLE_SIZE; ++h) {
          struct search_efficiency *pse = &hashtable_efficiency[h];
          float searches_compare;
          if (pse->num_connections > 0)
            ++occupied_buckets;
          if (pse->max_connections > max_bucket_occupancy)
            max_bucket_occupancy = pse->max_connections;
          if (pse->num_searches > max_searches)
            max_searches = pse->num_searches;
          if (pse->num_comparisons > max_comparisons)
            max_comparisons = pse->num_comparisons;
          if (pse->max_depth > max_depth)
            max_depth = pse->max_depth;
          searches_compare = (float) pse->num_comparisons / (float) pse->num_searches;
          if (searches_compare > max_searches_compare)
            max_searches_compare = searches_compare;
      }
      fprintf(stdout,"%s    Occupied hash buckets: %u\n", comment, occupied_buckets);
      fprintf(stdout,"%s    Max entries/bucket: %u\n", comment, max_bucket_occupancy);
      fprintf(stdout,"%s    Max searches/bucket: %u\n", comment, max_searches);
      fprintf(stdout,"%s    Max comparisons/bucket: %u\n", comment, max_comparisons);
      fprintf(stdout,"%s    Max avg compares/search: %.2f\n", comment, max_searches_compare);
      fprintf(stdout,"%s    Max tree depth: %u\n", comment, max_depth);
    }

    /* complete the "idle time" calculations using NOW */
    for (ix = 0; ix <= num_tcp_pairs; ++ix) {
      tcp_pair *ptp = ttp[ix];
      tcb *thisdir; 
      u_llong itime;

      /* if it's CLOSED, skip it */
      if ((FinCount(ptp)>=2) || (ConnReset(ptp)))
          continue;

      /* a2b direction */
      thisdir = &ptp->a2b;
      if (!ZERO_TIME(&thisdir->last_time)) {
          itime = elapsed(thisdir->last_time,current_time);
          if (itime > thisdir->idle_max)
            thisdir->idle_max = itime;
      }
          

      /* b2a direction */
      thisdir = &ptp->b2a;
      if (!ZERO_TIME(&thisdir->last_time)) {
          itime = elapsed(thisdir->last_time,current_time);
          if (itime > thisdir->idle_max)
            thisdir->idle_max = itime;
      }
    }
  }

    /* if we're filtering, see which connections pass */
    if (filter_output || ignore_non_comp) {

      /* file to dump matching connection numbers into */
      f_passfilter = fopen(PASS_FILTER_FILENAME,"w+");
      if (f_passfilter == NULL) {
          perror(PASS_FILTER_FILENAME);
          exit(-1);
      }

      if (filter_output) {
       if (!run_continuously) {
          /* mark the connections to ignore */
          for (ix = 0; ix <= num_tcp_pairs; ++ix) {
             ptp = ttp[ix];
             if (PassesFilter(ptp)) {
              if (++count == 1)
                  fprintf(f_passfilter,"%d", ix+1);
              else
                  fprintf(f_passfilter,",%d", ix+1);
             } else {
              /* else ignore it */
              ptp->ignore_pair = TRUE;
             }
          }
       }
      }
    }
   
  if (!run_continuously) {
    /* print each connection */
    if (!printsuppress) {
        Bool first = TRUE; /* Used with <SP>-separated-values
                      * Keeps track of whether header has already
                      * been printed */
      for (ix = 0; ix <= num_tcp_pairs; ++ix) {
          ptp = ttp[ix];

          if (!ptp->ignore_pair) {
            if ((printbrief) && (!ignore_non_comp || ConnComplete(ptp))) {
                fprintf(stdout,"%3d: ", ix+1);
                PrintBrief(ptp);
            } else if (!ignore_non_comp || ConnComplete(ptp)) {
                if(csv || tsv || (sv != NULL)) {
                   if(first) {
                    PrintSVHeader();
                    first = FALSE;
                   }
                   fprintf(stdout, "%d%s", ix+1, sp);
                }
                else {
                   if (ix > 0)
                   fprintf(stdout,"================================\n");
                   fprintf(stdout,"TCP connection %d:\n", ix+1);
                   
                }
                PrintTrace(ptp);
            }
             /* This piece of code dumps PF file when filtered with '-c' 
              option, this option says to select only complete connections.
              The PF file will contain the connection numbers which are
              selected to be complete */
             if (ignore_non_comp)
               if (ConnComplete(ptp)) {
                  if (++count == 1)
                    fprintf(f_passfilter, "%d", ix+1);
                  else
                    fprintf(f_passfilter, ",%d", ix+1);
               }
             /******************************/
            
            /* If we are extracting packet contents (-e option), we shall check to
             * see if we missed segments during packet capture causing the
             * X2Y_contents.dat files that we drop to contain voids in them.
             * We shall emit a warning upon such an event below. */
            if (save_tcp_data && !incomplete_pkt_capture && MissingData(ptp)) 
            incomplete_pkt_capture = TRUE;
          }   
      }
    }
  }
  
    /* if we're filtering, close the file */
    if (filter_output || ignore_non_comp) {
      fprintf(f_passfilter,"\n");
      fclose(f_passfilter);
    }

    if (incomplete_pkt_capture) {
      fprintf(stderr, "\nWarning : some extracted files are incomplete!\n");
      fprintf(stderr, "          Please see -l output for more detail.\n");
    }
  
    if ((debug>2) && !nonames)
      cadump();
}

static void
MoreTcpPairs(
    int num_needed)
{
    int new_max_tcp_pairs;
    int i;

    if (num_needed < max_tcp_pairs)
      return;

    new_max_tcp_pairs = max_tcp_pairs * 4;
    while (new_max_tcp_pairs < num_needed)
      new_max_tcp_pairs *= 4;
    
    if (debug)
      printf("trace: making more space for %d total TCP pairs\n",
             new_max_tcp_pairs);

    /* enlarge array to hold any pairs that we might create */
    ttp = ReallocZ(ttp,
               max_tcp_pairs * sizeof(tcp_pair *),
               new_max_tcp_pairs * sizeof(tcp_pair *));

    /* enlarge array to keep track of which ones to ignore */
    ignore_pairs = ReallocZ(ignore_pairs,
                      max_tcp_pairs * sizeof(Bool),
                      new_max_tcp_pairs * sizeof(Bool));
    if (more_conns_ignored)
      for (i=max_tcp_pairs; i < new_max_tcp_pairs;++i)
          ignore_pairs[i] = TRUE;

    max_tcp_pairs = new_max_tcp_pairs;
}


void
trace_init(void)
{
    static Bool initted = FALSE;

    if (0) {
      printf("trace_init called\n");
    }

    if (run_continuously) {
      if (ignore_pairs) {
      free(ignore_pairs);
      ignore_pairs = NULL;
      }
      if (ttp) {
      free(ttp);
      ttp = NULL;
      }
      more_conns_ignored = FALSE;
    }

    if (initted)
      return;

    initted = TRUE;

    /* create an array to hold any pairs that we might create */
    ttp = (tcp_pair **) MallocZ(max_tcp_pairs * sizeof(tcp_pair *));

    /* create an array to keep track of which ones to ignore */
    ignore_pairs = (Bool *) MallocZ(max_tcp_pairs * sizeof(Bool));
    if (!run_continuously) {
        /* create an array to hold any pairs that we might create */
        ttp = (tcp_pair **) MallocZ(max_tcp_pairs * sizeof(tcp_pair *));
      
        /* create an array to keep track of which ones to ignore */
        ignore_pairs = (Bool *) MallocZ(max_tcp_pairs * sizeof(Bool));
    }

    cainit();
    Minit();
}


void
IgnoreConn(
    int ix)
{
    if (debug) fprintf(stderr,"ignoring conn %d\n", ix);

//    trace_init();
      
    --ix;

    MoreTcpPairs(ix);

    more_conns_ignored = FALSE;
    ignore_pairs[ix] = TRUE;
}


void
OnlyConn(
    int ix_only)
{
    int ix;
    static Bool cleared = FALSE;
      
    if (debug) fprintf(stderr,"only printing conn %d\n", ix_only);

//    trace_init();
      
    --ix_only;

    MoreTcpPairs(ix_only);

    if (!cleared) {
      for (ix = 0; ix < max_tcp_pairs; ++ix) {
          ignore_pairs[ix] = TRUE;
      }
      cleared = TRUE;
    }

    more_conns_ignored = TRUE;
    ignore_pairs[ix_only] = FALSE;
}


/* get a long (4 byte) option (to avoid address alignment problems) */
static u_long
get_long_opt(
    void *ptr)
{
    u_long l;
    memcpy(&l,ptr,sizeof(u_long));
    return(l);
}


/* get a short (2 byte) option (to avoid address alignment problems) */
static u_short
get_short_opt(
    void *ptr)
{
    u_short s;
    memcpy(&s,ptr,sizeof(u_short));
    return(s);
}


struct tcp_options *
ParseOptions(
    struct tcphdr *ptcp,
    void *plast)
{
    static struct tcp_options tcpo;
    struct sack_block *psack;
    u_char *pdata;
    u_char *popt;
    u_char *plen;

    popt  = (u_char *)ptcp + sizeof(struct tcphdr);
    pdata = (u_char *)ptcp + TH_OFF(ptcp)*4;

    /* init the options structure */
    memset(&tcpo,0,sizeof(tcpo));
    tcpo.mss = tcpo.ws = tcpo.tsval = tcpo.tsecr = -1;
    tcpo.sack_req = 0;
    tcpo.sack_count = -1;
    tcpo.echo_req = tcpo.echo_repl = -1;
    tcpo.cc = tcpo.ccnew = tcpo.ccecho = -1;

    /* a quick sanity check, the unused (MBZ) bits must BZ! */
    if (warn_printbadmbz) {
      if (TH_X2(ptcp) != 0) {
          fprintf(stderr,
                "TCP packet %lu: 4 reserved bits are not zero (0x%01x)\n",
                pnum, TH_X2(ptcp));
      }
      if ((ptcp->th_flags & 0xc0) != 0) {
          fprintf(stderr,
                "TCP packet %lu: upper flag bits are not zero (0x%02x)\n",
                pnum, ptcp->th_flags);
      }
    } else {
      static int warned = 0;
      if (!warned &&
          ((TH_X2(ptcp) != 0) || ((ptcp->th_flags & 0xc0) != 0))) {
          warned = 1;
          fprintf(stderr, "\
TCP packet %lu: reserved bits are not all zero.  \n\
\tFurther warnings disabled, use '-w' for more info\n",
                pnum);
      }
    }

    /* looks good, now check each option in turn */
    while (popt < pdata) {
      plen = popt+1;

      /* check for truncation error */
      if ((char *)popt > (char *)plast) {
          if (warn_printtrunc)
            fprintf(stderr,"\
ParseOptions: packet %lu too short to parse remaining options\n", pnum);
          ++ctrunc;
          break;
      }

#define CHECK_O_LEN(opt) \
      if (*plen == 0) { \
          if (warn_printtrunc) fprintf(stderr, "\
ParseOptions: packet %lu %s option has length 0, skipping other options\n", \
                                           pnum,opt); \
          popt = pdata; break;} \
      if ((char *)popt + *plen - 1 > (char *)(plast)) { \
          if (warn_printtrunc) \
            fprintf(stderr, "\
ParseOptions: packet %lu %s option truncated, skipping other options\n", \
              pnum,opt); \
          ++ctrunc; \
          popt = pdata; break;} \


      switch (*popt) {
        case TCPOPT_EOL: ++popt; break;
        case TCPOPT_NOP: ++popt; break;
        case TCPOPT_MAXSEG:
          CHECK_O_LEN("TCPOPT_MAXSEG");
          tcpo.mss = ntohs(get_short_opt(popt+2));
          popt += *plen;
          break;
        case TCPOPT_WS:
          CHECK_O_LEN("TCPOPT_WS");
          tcpo.ws = *((u_char *)(popt+2));
          popt += *plen;
          break;
        case TCPOPT_TS:
          CHECK_O_LEN("TCPOPT_TS");
          tcpo.tsval = ntohl(get_long_opt(popt+2));
          tcpo.tsecr = ntohl(get_long_opt(popt+6));
          popt += *plen;
          break;
        case TCPOPT_ECHO:
          CHECK_O_LEN("TCPOPT_ECHO");
          tcpo.echo_req = ntohl(get_long_opt(popt+2));
          popt += *plen;
          break;
        case TCPOPT_ECHOREPLY:
          CHECK_O_LEN("TCPOPT_ECHOREPLY");
          tcpo.echo_repl = ntohl(get_long_opt(popt+2));
          popt += *plen;
          break;
        case TCPOPT_CC:
          CHECK_O_LEN("TCPOPT_CC");
          tcpo.cc = ntohl(get_long_opt(popt+2));
          popt += *plen;
          break;
        case TCPOPT_CCNEW:
          CHECK_O_LEN("TCPOPT_CCNEW");
          tcpo.ccnew = ntohl(get_long_opt(popt+2));
          popt += *plen;
          break;
        case TCPOPT_CCECHO:
          CHECK_O_LEN("TCPOPT_CCECHO");
          tcpo.ccecho = ntohl(get_long_opt(popt+2));
          popt += *plen;
          break;
        case TCPOPT_SACK_PERM:
          CHECK_O_LEN("TCPOPT_SACK_PERM");
          tcpo.sack_req = 1;
          popt += *plen;
          break;
        case TCPOPT_SACK:
          /* see which bytes are acked */
          CHECK_O_LEN("TCPOPT_SACK");
          tcpo.sack_count = 0;
          psack = (sack_block *)(popt+2);  /* past the kind and length */
          popt += *plen;
          while ((char *)psack < (char *)popt) {
            struct sack_block *psack_local =
                &tcpo.sacks[(unsigned)tcpo.sack_count];
            /* warning, possible alignment problem here, so we'll
               use memcpy() and hope for the best */
            /* better use -fno-builtin to avoid gcc alignment error
               in GCC 2.7.2 */
            memcpy(psack_local, psack, sizeof(sack_block));

            /* convert to local byte order (Jamshid Mahdavi) */
            psack_local->sack_left  = ntohl(psack_local->sack_left);
            psack_local->sack_right = ntohl(psack_local->sack_right);

            ++psack;
            if ((char *)psack > ((char *)plast+1)) {
                /* this SACK block isn't all here */
                if (warn_printtrunc)
                  fprintf(stderr,
                        "packet %lu: SACK block truncated\n",
                        pnum);
                ++ctrunc;
                break;
            }
            ++tcpo.sack_count;
            if (tcpo.sack_count > MAX_SACKS) {
                /* this isn't supposed to be able to happen */
                fprintf(stderr,
                      "Warning, internal error, too many sacks!!\n");
                tcpo.sack_count = MAX_SACKS;
            }
          }
          break;
        default:
          if (debug)
            fprintf(stderr,
                  "Warning, ignoring unknown TCP option 0x%x\n",
                  *popt);
          CHECK_O_LEN("TCPOPT_UNKNOWN");

          /* record it anyway... */
          if (tcpo.unknown_count < MAX_UNKNOWN) {
            int ix = tcpo.unknown_count; /* make lint happy */
            tcpo.unknowns[ix].unkn_opt = *popt;
            tcpo.unknowns[ix].unkn_len = *plen;
          }
          ++tcpo.unknown_count;
          
          popt += *plen;
          break;
      }
    }

    return(&tcpo);
}



static void
ExtractContents(
    u_long seq,
    u_long tcp_data_bytes,
    u_long saved_data_bytes,
    void *pdata,
    tcb *ptcb)
{
    u_long missing;
    long offset;
    u_long fptr;
      /* Maximum filename could be :
            aaaaaaaa2bbbbbbbb_contents.dat which
            takes 8+1+8+ size of the extension */
    static char filename[MAX_HOSTLETTER_LEN
                              +1      /* for "2" */
                              +MAX_HOSTLETTER_LEN
                              +sizeof(CONTENTS_FILE_EXTENSION)
                              +1];    /* for terminating NULL. */

    if (debug > 2)
      fprintf(stderr,
            "ExtractContents(seq:%ld  bytes:%ld  saved_bytes:%ld) called\n",
            seq, tcp_data_bytes, saved_data_bytes);

    if (saved_data_bytes == 0)
      return;

    /* how many bytes do we have? */
    missing = tcp_data_bytes - saved_data_bytes;
    if ((debug > 2) && (missing > 0)) {
      fprintf(stderr,"ExtractContents: missing %ld bytes (%ld-%ld)\n",
            missing,tcp_data_bytes,saved_data_bytes);
    }

    
    /* if the FILE is "-1", couldn't open file */
    if (ptcb->extr_contents_file == (MFILE *) -1) {
      return;
    }

    /* if the FILE is NULL, open file */
    snprintf(filename,sizeof(filename),"%s2%s%s", ptcb->host_letter, ptcb->ptwin->host_letter,
          CONTENTS_FILE_EXTENSION);
    if (ptcb->extr_contents_file == (MFILE *) NULL) {
      MFILE *f;

      if ((f = Mfopen(filename,"w")) == NULL) {
          perror(filename);
          ptcb->extr_contents_file = (MFILE *) -1;
      }

      if (debug)
          fprintf(stderr,"TCP contents file is '%s'\n", filename);

      ptcb->extr_contents_file = f;

      if (ptcb->syn_count == 0) {
          /* we haven't seen the SYN.  This is bad because we can't tell */
          /* if there is data BEFORE this, which makes it tough to store */
          /* the file.  Let's be optimistic and hope we don't see */
          /* anything before this point.  Otherwise, we're stuck */
          ptcb->extr_lastseq = seq;
      } else {
          /* beginning of the file is the data just past the SYN */
          ptcb->extr_lastseq = ptcb->syn+1;
      }
      /* in any case, anything before HERE is illegal (fails for very */
      /* long files - FIXME */
      ptcb->extr_initseq = ptcb->extr_lastseq;
    }

    /* it's illegal for the bytes to be BEFORE extr_initseq unless the file */
    /* is "really long" (seq space has wrapped around) - FIXME(ugly) */
    if ((SEQCMP(seq,ptcb->extr_initseq) < 0) &&
      (ptcb->data_bytes < (0xffffffff/2))) {
      /* if we haven't (didn't) seen the SYN, then can't do this!! */
      if (debug>1) {
          fprintf(stderr,
                "ExtractContents: skipping data, preceeds first segment\n");
          fprintf(stderr,"\t and I didnt' see the SYN\n");
      }
      return;
    }

    /* see where we should start writing */
    /* a little complicated, because we want to support really long files */
    offset = SEQCMP(seq,ptcb->extr_lastseq);
    

    if (debug>10)
      fprintf(stderr,
            "TRYING to save %ld bytes from stream '%s2%s' at offset %ld\n",
            saved_data_bytes,
            ptcb->host_letter, ptcb->ptwin->host_letter,
            offset);

    /* seek to the correct place in the file */
    if (Mfseek(ptcb->extr_contents_file, offset, SEEK_CUR) == -1) {
      perror("fseek");
      exit(-1);
    }

    /* see where we are */
    fptr = Mftell(ptcb->extr_contents_file);

    if (debug>1)
      fprintf(stderr,
            "Saving %ld bytes from '%s2%s' at offset %ld in file '%s'\n",
            saved_data_bytes,
            ptcb->host_letter, ptcb->ptwin->host_letter,
            fptr, filename);

    /* store the bytes */
    if (Mfwrite(pdata,1,saved_data_bytes,ptcb->extr_contents_file)
      != saved_data_bytes) {
      perror("fwrite");
      exit(-1);
    }

    /* go back to where we started to not confuse the next write */
    ptcb->extr_lastseq = seq;
    if (Mfseek(ptcb->extr_contents_file, fptr, SEEK_SET) == -1) {
      perror("fseek 2");
      exit(-1);
    }
}


/* check for not-uncommon error of hardware-level duplicates
   (same IP ID and TCP sequence number) */
static Bool
check_hw_dups(
    u_short id,
    seqnum seq,
    tcb *tcb)
{
    int i;
    struct str_hardware_dups *pshd;

    /* see if we've seen this one before */
    for (i=0; i < SEGS_TO_REMEMBER; ++i) {
      pshd = &tcb->hardware_dups[i];
      
      if ((pshd->hwdup_seq == seq) && (pshd->hwdup_id == id) &&
          (pshd->hwdup_seq != 0) && (pshd->hwdup_id != 0)) {
          /* count it */
          ++tcb->num_hardware_dups;
          if (warn_printhwdups) {
            printf("%s->%s: saw hardware duplicate of TCP seq %lu, IP ID %u (packet %lu == %lu)\n",
                   tcb->host_letter,tcb->ptwin->host_letter,
                   seq, id, pnum,pshd->hwdup_packnum);
          }
          return(TRUE);
      }
    }

    /* remember it */
    pshd = &tcb->hardware_dups[tcb->hardware_dups_ix];
    pshd->hwdup_seq = seq;
    pshd->hwdup_id = id;
    pshd->hwdup_packnum = pnum;
    tcb->hardware_dups_ix = (tcb->hardware_dups_ix+1) % SEGS_TO_REMEMBER;

    return(FALSE);
}


/* given a tcp_pair and a packet, tell me which tcb it is */
struct tcb *
ptp2ptcb(
    tcp_pair *ptp,
    struct ip *pip,
    struct tcphdr *ptcp)
{
    int dir = 0;
    tcp_pair tp_in;

    /* grab the address from this packet */
    CopyAddr(&tp_in.addr_pair, pip,
           ntohs(ptcp->th_sport), ntohs(ptcp->th_dport));

    /* check the direction */
    if (!SameConn(&tp_in.addr_pair,&ptp->addr_pair,&dir))
      return(NULL);  /* not found, internal error */

    if (dir == A2B)
      return(&ptp->a2b);
    else
      return(&ptp->b2a);
}


/* represent the sequence numbers absolute or relative to 0 */
static u_long
SeqRep(
    tcb *ptcb,
    u_long seq)
{
    if (graph_seq_zero) {
      return(seq - ptcb->min_seq);
    } else {
      return(seq);
    }
}


/*------------------------------------------------------------------------
 *  cksum  -  Return 16-bit ones complement of 16-bit ones complement sum 
 *------------------------------------------------------------------------
 */
static u_short
cksum(
    void *pvoid,        /* any alignment is legal */
    int nbytes)
{
    u_char *pchar = pvoid;
    u_long sum = 0;

    while (nbytes >= 2) {
      /* can't assume pointer alignment :-( */
      sum += (pchar[0]<<8);
      sum += pchar[1];

      pchar+=2;
      nbytes -= 2;
    }

    /* special check for odd length */
    if (nbytes == 1) {
      sum += (pchar[0]<<8);
      /* lower byte is assumed to be 0 */
    }

    sum = (sum >> 16) + (sum & 0xffff);   /* add in carry   */
    sum += (sum >> 16);             /* maybe one more */

    return(sum);
}

/* compute IP checksum */
static u_short
ip_cksum(
    struct ip *pip,
    void *plast)
{
    u_short sum;
    
    if (PIP_ISV6(pip))
      return(0);        /* IPv6 has no header checksum */
    if (!PIP_ISV4(pip))
      return(1);        /* I have no idea! */


    /* quick sanity check, if the packet is truncated, pretend it's valid */
    if ((char *)plast < (char *)((char *)pip+IP_HL(pip)*4-1)) {
      return(0);
    }

    /* ... else IPv4 */
    sum = cksum(pip, IP_HL(pip)*4);
    return(sum);
}


/* is the IP checksum valid? */
Bool
ip_cksum_valid(
    struct ip *pip,
    void *plast)
{
    u_short sum;
/*     PrintRawDataHex("IP header",pip,plast); */

    sum = ip_cksum(pip,plast);

    return((sum == 0) || (sum == 0xffff));
}


/* compute the TCP checksum */
static u_short
tcp_cksum(
    struct ip *pip,
    struct tcphdr *ptcp,
    void *plast)
{
    u_long sum = 0;
    unsigned tcp_length = 0;

    /* verify version */
    if (!PIP_ISV4(pip) && !PIP_ISV6(pip)) {
      fprintf(stderr,"Internal error, tcp_cksum: neither IPv4 nor IPv6\n");
      exit(-1);
    }


    /* TCP checksum includes: */
    /* - IP source */
    /* - IP dest */
    /* - IP type */
    /* - TCP header length + TCP data length */
    /* - TCP header and data */

    if (PIP_ISV4(pip)) {
      /* quick sanity check, if the packet is fragmented,
         pretend it's valid */
      /* Thu Jul  6, 2000 - bugfix, bad check */
      if (((ntohs(pip->ip_off) << 2) & 0xffff) != 0) {
          /* both the offset AND the MF bit must be 0 */
          /* (we shifted off the DF bit, which might be on) */
          return(0);
      }

      /* 2 4-byte numbers, next to each other */
      sum += cksum(&pip->ip_src,4*2);

      /* type */
      sum += (u_short) pip->ip_p;

      /* length (TCP header length + TCP data length) */
      tcp_length = ntohs(pip->ip_len) - (4 * IP_HL(pip));
      sum += (u_short) tcp_length;
    } else /* if (PIP_ISV6(pip))*/  {
              
        /* Support for IPv6 checksums has been added on Aug31, 2001 
       * and has not been thoroughly tested. - Avinash 
       */

        int total_length = 0;  /* Total length of the extension headers */
        struct ipv6 *pip6 = (struct ipv6 *)pip;
       
        /* quick sanity check, it the packet is truncated,
       * pertend it is valid.
       */ 
        if(gettcp(pip, &ptcp, &plast) != 0)
       return(0);
       
        /* Forming the pseudo-header */
        /* source address */
        sum += cksum(&pip6->ip6_saddr,16);
       
        /* Looking for the destination address.
       * May be in the IPv6 header or the last address in the 
       * routing header (if present)
       */
       
        /* No extension headers, hence, routing header not present */
        if(pip6->ip6_nheader == IPPROTO_TCP) {
         sum += cksum(&pip6->ip6_daddr,16);
      }
        /* Some extension headers present. Searching for routing header */
        else {
         /* find the first header */
         struct ipv6_ext *pipv6_ext = (struct ipv6_ext *)(pip6+1);

         /* Searching for the routing header */
         int ret = getroutingheader(pip, &pipv6_ext, &plast);
         
         if(!ret) {  /* Found the routing header */
            if(pipv6_ext->ip6ext_len >= 2) { /* Sanity check */
             char *daddr = (char *)((char *)pipv6_ext + 8 + ((pipv6_ext->ip6ext_len - 2) * 8));
             sum += cksum(&daddr,16);
            }
            else {  /* Not a valid routing header */
             return(-1);
            }
         }
         else {  /* Routing header not found */
            sum += cksum(&pip6->ip6_daddr,16);
         }
      }
       
       /* Upper-Layer Packet Length */
       total_length = total_length_ext_headers(pip6);
       if(total_length >= 0)
       tcp_length = pip6->ip6_lngth - total_length;
       else /* Unknown extension header seen */ 
       return(-1);
       sum += (u_short) tcp_length;
       
       /* Next Header (Type) */
       sum += (u_short) IPPROTO_TCP;
    }
   
      
    /* quick sanity check, if the packet is truncated, pretend it's valid */
    if ((char *)plast < (char *)((char *)ptcp+tcp_length-1)) {
      return(0);
    }

    /* checksum the TCP header and data */
    sum += cksum(ptcp,tcp_length);

    /* roll down into a 16-bit number */
    sum = (sum >> 16) + (sum & 0xffff);
    sum += (sum >> 16);

    return (u_short)(~sum & 0xffff);
}



/* compute the UDP checksum */
static u_short
udp_cksum(
    struct ip *pip,
    struct udphdr *pudp,
    void *plast)
{
    u_long sum = 0;
    unsigned udp_length;

    /* WARNING -- this routine has not been extensively tested */

    /* verify version */
    if (!PIP_ISV4(pip) && !PIP_ISV6(pip)) {
      fprintf(stderr,"Internal error, udp_cksum: neither IPv4 nor IPv6\n");
      exit(-1);
    }


    /* UDP checksum includes: */
    /* - IP source */
    /* - IP dest */
    /* - IP type */
    /* - UDP length field */
    /* - UDP header and data */

    if (PIP_ISV4(pip)) {
      /* 2 4-byte numbers, next to each other */
      sum += cksum(&pip->ip_src,4*2);

      /* type */
      sum += (u_short) pip->ip_p;

      /* UDP length */
      udp_length = ntohs(pudp->uh_ulen);
      sum += htons(pudp->uh_ulen);
    } else /* if (PIP_ISV6(pip))*/  {
              
        /* Support for IPv6 checksums has been added on Aug31, 2001 
       * and has not been thoroughly tested. - Avinash 
       */

        struct ipv6 *pip6 = (struct ipv6 *)pip;
             
        /* quick sanity check, it the packet is truncated,
       * pertend it is valid.
       */ 
        if(getudp(pip, &pudp, &plast) != 0)
       return(0);
       
        /* Forming the pseudo-header */
        /* source address */
        sum += cksum(&pip6->ip6_saddr,16);
       
        /* Looking for the destination address.
       * May be in the IPv6 header or the last address in the 
       * routing header (if present)
       */
       
        /* No extension headers, hence, routing header not present */
        if(pip6->ip6_nheader == IPPROTO_UDP) {
         sum += cksum(&pip6->ip6_daddr,16);
      }
        /* Some extension headers present. Searching for routing header */
        else {
         /* find the first header */
         struct ipv6_ext *pipv6_ext = (struct ipv6_ext *)(pip6+1);

         /* Searching for the routing header */
         int ret = getroutingheader(pip, &pipv6_ext, &plast);
         
         if(!ret) {  /* Found the routing header */
            if(pipv6_ext->ip6ext_len >= 2) { /* Sanity check */
             char *daddr = (char *)((char *)pipv6_ext + 8 + ((pipv6_ext->ip6ext_len - 2) * 8));
             sum += cksum(&daddr,16);
            }
            else {  /* Not a valid routing header */
             return(-1);
            }
         }
         else {  /* Routing header not found */
            sum += cksum(&pip6->ip6_daddr,16);
         }
      }
       
       /* Upper-Layer Packet Length */
      udp_length = ntohs(pudp->uh_ulen);
      sum += htons(pudp->uh_ulen);

       /* Next Header (Type) */
        sum += (u_short) IPPROTO_UDP;
    }
   
 
    /* quick sanity check, if the packet is truncated, pretend it's valid */
    if ((char *)plast < (char *)((char *)pudp+udp_length-1)) {
      return(0);
    }

    /* checksum the UDP header and data */
    sum += cksum(pudp,udp_length);

    /* roll down into a 16-bit number */
    sum = (sum >> 16) + (sum & 0xffff);
    sum += (sum >> 16);

    return (u_short)(~sum & 0xffff);
}


/* is the TCP checksum valid? */
Bool
tcp_cksum_valid(
    struct ip *pip,
    struct tcphdr *ptcp,
    void *plast)
{
    return(tcp_cksum(pip,ptcp,plast) == 0);
}


/* is the UDP checksum valid? */
Bool
udp_cksum_valid(
    struct ip *pip,
    struct udphdr *pudp,
    void *plast)
{
    if (ntohs(pudp->uh_sum) == 0) {
      /* checksum not used */
      return(1);        /* valid */
    }
    
    return(udp_cksum(pip,pudp,plast) == 0);
}

/* Did we miss any segment during packet capture? */
static Bool
MissingData(tcp_pair *ptp)
{
  tcb *pab = &ptp->a2b;
  tcb *pba = &ptp->b2a;
  
  u_llong stream_length_pab=0, stream_length_pba=0;
  u_long pab_last, pba_last;
  
  /* If packets were truncated (due to shorter snaplen) we miss data */
  if ( (pab->trunc_bytes > 0) || (pba->trunc_bytes > 0) )
    return TRUE;
  
  /* Also, if we missed whole segments (pcap dozing off) we miss data.
   * The following code yanked off from output.c handles seq-space
   * wrap around - Mani
   * 
   * Compare to theoretical length of the stream (not just what
   * we saw) using the SYN and FIN
   * Seq. Space wrap around calculations:
   * Calculate stream length using last_seq_num seen, first_seq_num
   * seen and wrap_count.
   * first_seq_num = syn
   * If reset_set, last_seq_num = latest_seq
   *          else last_seq_num = fin
   */
    
    pab_last = (pab->reset_count>0)?pab->latest_seq:pab->fin;    
    pba_last = (pba->reset_count>0)?pba->latest_seq:pba->fin;
    
    /* calculating stream length for direction pab */
    if ((pab->syn_count > 0) && (pab->fin_count > 0)) {
      if (pab->seq_wrap_count > 0) {
          if (pab_last > pab->syn) {
            stream_length_pab = pab_last + (MAX_32 * pab->seq_wrap_count) - pab->syn - 1;
          }
          else {
            stream_length_pab = pab_last + (MAX_32 * (pab->seq_wrap_count+1)) - pab->syn - 1;
          }
      }
      else {
          if (pab_last > pab->syn) {
            stream_length_pab = pab_last - pab->syn - 1;
          }
          else {
            stream_length_pab = MAX_32 + pab_last - pab->syn - 1;
          }
      }
    }

    /* calculating stream length for direction pba */
    if ((pba->syn_count > 0) && (pba->fin_count > 0)) {
      if (pba->seq_wrap_count > 0) {
          if (pba_last > pba->syn) {
            stream_length_pba = pba_last + (MAX_32 * pba->seq_wrap_count) - pba->syn - 1;
          }
          else {
            stream_length_pba = pba_last + (MAX_32 * (pba->seq_wrap_count+1)) - pba->syn - 1;
          }
      }
      else {
          if (pba_last > pba->syn) {
            stream_length_pba = pba_last - pba->syn - 1;
          }
          else {
            stream_length_pba = MAX_32 + pba_last - pba->syn - 1;
          }
      }
    }

    /* Alright, now that we have the stream length in either direction,
     * if the stream length is not equal to the total unique bytes we 
     * seen, we must have missed whole segments
     */
     if ( (stream_length_pab != pab->unique_bytes) ||
        (stream_length_pba != pba->unique_bytes) )
       return TRUE;

  return FALSE;
}

Generated by  Doxygen 1.6.0   Back to index