From Pierre Juhen:
[obnox/wireshark/wip.git] / epan / dissectors / packet-tcp.c
index 2c291dba0e8e0d4f53cf93b191f3dd99c1db9aeb..a8998cf5e3a5d5835faa21f0fcb9999d33b09105 100644 (file)
@@ -3,8 +3,8 @@
  *
  * $Id$
  *
- * Ethereal - Network traffic analyzer
- * By Gerald Combs <gerald@ethereal.com>
+ * Wireshark - Network traffic analyzer
+ * By Gerald Combs <gerald@wireshark.org>
  * Copyright 1998 Gerald Combs
  *
  * This program is free software; you can redistribute it and/or
@@ -19,7 +19,7 @@
  *
  * You should have received a copy of the GNU General Public License
  * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
  */
 
 #ifdef HAVE_CONFIG_H
 #include <epan/packet.h>
 #include <epan/addr_resolv.h>
 #include <epan/ipproto.h>
-#include "ip_opts.h"
-#include "follow.h"
+#include <epan/ip_opts.h>
+#include <epan/follow.h>
 #include <epan/prefs.h>
+#include <epan/emem.h>
 #include "packet-tcp.h"
 #include "packet-ip.h"
 #include "packet-frame.h"
 #include <epan/conversation.h>
 #include <epan/strutil.h>
-#include "reassemble.h"
+#include <epan/reassemble.h>
 #include <epan/tap.h>
+#include <epan/slab.h>
+#include <epan/expert.h>
 
 static int tcp_tap = -1;
 
@@ -90,6 +93,8 @@ static int hf_tcp_urgent_pointer = -1;
 static int hf_tcp_analysis_flags = -1;
 static int hf_tcp_analysis_acks_frame = -1;
 static int hf_tcp_analysis_ack_rtt = -1;
+static int hf_tcp_analysis_rto = -1;
+static int hf_tcp_analysis_rto_frame = -1;
 static int hf_tcp_analysis_retransmission = -1;
 static int hf_tcp_analysis_fast_retransmission = -1;
 static int hf_tcp_analysis_out_of_order = -1;
@@ -104,9 +109,10 @@ static int hf_tcp_analysis_duplicate_ack_num = -1;
 static int hf_tcp_analysis_duplicate_ack_frame = -1;
 static int hf_tcp_analysis_zero_window = -1;
 static int hf_tcp_analysis_zero_window_probe = -1;
-static int hf_tcp_analysis_zero_window_violation = -1;
+static int hf_tcp_analysis_zero_window_probe_ack = -1;
 static int hf_tcp_continuation_to = -1;
 static int hf_tcp_pdu_time = -1;
+static int hf_tcp_pdu_size = -1;
 static int hf_tcp_pdu_last_frame = -1;
 static int hf_tcp_reassembled_in = -1;
 static int hf_tcp_segments = -1;
@@ -116,6 +122,7 @@ static int hf_tcp_segment_overlap_conflict = -1;
 static int hf_tcp_segment_multiple_tails = -1;
 static int hf_tcp_segment_too_long_fragment = -1;
 static int hf_tcp_segment_error = -1;
+static int hf_tcp_options = -1;
 static int hf_tcp_option_mss = -1;
 static int hf_tcp_option_mss_val = -1;
 static int hf_tcp_option_wscale = -1;
@@ -142,8 +149,8 @@ static gint ett_tcp_segments = -1;
 static gint ett_tcp_segment  = -1;
 
 
-/* not all of the hf_fields below make sense for TCP but we have to provide 
-   them anyways to comply with the api (which was aimed for ip fragment 
+/* not all of the hf_fields below make sense for TCP but we have to provide
+   them anyways to comply with the api (which was aimed for ip fragment
    reassembly) */
 static const fragment_items tcp_segment_items = {
        &ett_tcp_segment,
@@ -165,32 +172,22 @@ static dissector_handle_t data_handle;
 
 /* TCP structs and definitions */
 
-static void
-process_tcp_payload(tvbuff_t *tvb, volatile int offset, packet_info *pinfo,
-       proto_tree *tree, proto_tree *tcp_tree, int src_port, int dst_port,
-       guint32 seq, guint32 nxtseq, gboolean is_tcp_segment);
-
 /* **************************************************************************
- * stuff to analyze TCP sequencenumbers for retransmissions, missing segments,
+
  * RTT and reltive sequence numbers.
  * **************************************************************************/
 static gboolean tcp_analyze_seq = TRUE;
 static gboolean tcp_relative_seq = TRUE;
 
-static GMemChunk *tcp_unacked_chunk = NULL;
-static int tcp_unacked_count = 500;    /* one for each packet until it is acked*/
-struct tcp_unacked {
-       struct tcp_unacked *next;
-       guint32 frame;
-       guint32 seq;
-       guint32 nextseq;
-       nstime_t ts;
+/* SLAB allocator for tcp_unacked structures
+ */
+SLAB_ITEM_TYPE_DEFINE(tcp_unacked_t)
+static SLAB_FREE_LIST_DEFINE(tcp_unacked_t)
+#define TCP_UNACKED_NEW(fi)                                    \
+       SLAB_ALLOC(fi, tcp_unacked_t)
+#define TCP_UNACKED_FREE(fi)                                   \
+       SLAB_FREE(fi, tcp_unacked_t)
 
-       /* this is to keep track of zero window and zero window probe */
-       guint32 window;
-
-       guint32 flags;
-};
 
 /* Idea for gt: either x > y, or y is much bigger (assume wrap) */
 #define GT_SEQ(x, y) ((gint32)((y) - (x)) < 0)
@@ -199,8 +196,6 @@ struct tcp_unacked {
 #define LE_SEQ(x, y) ((gint32)((x) - (y)) <= 0)
 #define EQ_SEQ(x, y) ((x) == (y))
 
-static GMemChunk *tcp_acked_chunk = NULL;
-static int tcp_acked_count = 5000;     /* one for almost every other segment in the capture */
 #define TCP_A_RETRANSMISSION           0x0001
 #define TCP_A_LOST_PACKET              0x0002
 #define TCP_A_ACK_LOST_PACKET          0x0004
@@ -208,324 +203,181 @@ static int tcp_acked_count = 5000;      /* one for almost every other segment in the
 #define TCP_A_DUPLICATE_ACK            0x0010
 #define TCP_A_ZERO_WINDOW              0x0020
 #define TCP_A_ZERO_WINDOW_PROBE                0x0040
-#define TCP_A_ZERO_WINDOW_VIOLATION    0x0080
+#define TCP_A_ZERO_WINDOW_PROBE_ACK    0x0080
 #define TCP_A_KEEP_ALIVE_ACK           0x0100
 #define TCP_A_OUT_OF_ORDER             0x0200
 #define TCP_A_FAST_RETRANSMISSION      0x0400
 #define TCP_A_WINDOW_UPDATE            0x0800
 #define TCP_A_WINDOW_FULL              0x1000
-struct tcp_acked {
-       guint32 frame_acked;
-       nstime_t ts;
-       guint16 flags;
-       guint32 dupack_num;     /* dup ack number */
-       guint32 dupack_frame;   /* dup ack to frame # */
-};
-static GHashTable *tcp_analyze_acked_table = NULL;
-
-static GMemChunk *tcp_rel_seq_chunk = NULL;
-static int tcp_rel_seq_count = 10000; /* one for each segment in the capture */
-struct tcp_rel_seq {
-       guint32 seq_base;
-       guint32 ack_base;
-       gint16  win_scale;
-};
-static GHashTable *tcp_rel_seq_table = NULL;
-
-static GMemChunk *tcp_analysis_chunk = NULL;
-static int tcp_analysis_count = 20;    /* one for each conversation */
-struct tcp_analysis {
-       /* These two structs are managed based on comparing the source
-        * and destination addresses and, if they're equal, comparing
-        * the source and destination ports.
-        *
-        * If the source is greater than the destination, then stuff
-        * sent from src is in ual1.
-        *
-        * If the source is less than the destination, then stuff
-        * sent from src is in ual2.
-        *
-        * XXX - if the addresses and ports are equal, we don't guarantee
-        * the behavior.
-        */
-       struct tcp_unacked *ual1;       /* UnAcked List 1*/
-       guint32 base_seq1;
-       struct tcp_unacked *ual2;       /* UnAcked List 2*/
-       guint32 base_seq2;
-       gint16 win_scale1, win_scale2;
-       gint32 win1, win2;
-       guint32 ack1, ack2;
-       guint32 ack1_frame, ack2_frame;
-       nstime_t ack1_time, ack2_time;
-       guint32 num1_acks, num2_acks;
-
-       /* these two lists are used to track when PDUs may start
-          inside a segment.
-       */
-       struct tcp_next_pdu *pdu_seq1;
-       struct tcp_next_pdu *pdu_seq2;
-};
 
 
-static GMemChunk *tcp_next_pdu_chunk = NULL;
-static int tcp_next_pdu_count = 20;
-struct tcp_next_pdu {
-       struct tcp_next_pdu *next;
-       guint32 seq;
-       guint32 nxtpdu;
-       guint32 first_frame;
-       guint32 last_frame;
-        nstime_t last_frame_time;
-};
-static GHashTable *tcp_pdu_tracking_table = NULL;
-static GHashTable *tcp_pdu_skipping_table = NULL;
-static GHashTable *tcp_pdu_time_table = NULL;
+static void
+process_tcp_payload(tvbuff_t *tvb, volatile int offset, packet_info *pinfo,
+       proto_tree *tree, proto_tree *tcp_tree, int src_port, int dst_port,
+       guint32 seq, guint32 nxtseq, gboolean is_tcp_segment,
+       struct tcp_analysis *tcpd);
 
 
-static struct tcp_analysis *
+struct tcp_analysis *
 get_tcp_conversation_data(packet_info *pinfo)
 {
+       int direction;
        conversation_t *conv=NULL;
        struct tcp_analysis *tcpd=NULL;
 
        /* Have we seen this conversation before? */
-       if( (conv=find_conversation(&pinfo->src, &pinfo->dst, pinfo->ptype, pinfo->srcport, pinfo->destport, 0)) == NULL){
+       if( (conv=find_conversation(pinfo->fd->num, &pinfo->src, &pinfo->dst, pinfo->ptype, pinfo->srcport, pinfo->destport, 0)) == NULL){
                /* No this is a new conversation. */
-               conv=conversation_new(&pinfo->src, &pinfo->dst, pinfo->ptype, pinfo->srcport, pinfo->destport, 0);
+               conv=conversation_new(pinfo->fd->num, &pinfo->src, &pinfo->dst, pinfo->ptype, pinfo->srcport, pinfo->destport, 0);
        }
 
        /* check if we have any data for this conversation */
        tcpd=conversation_get_proto_data(conv, proto_tcp);
        if(!tcpd){
                /* No no such data yet. Allocate and init it */
-               tcpd=g_mem_chunk_alloc(tcp_analysis_chunk);
-               tcpd->ual1=NULL;
-               tcpd->base_seq1=0;
-               tcpd->win1=-1;
-               tcpd->win_scale1=-1;
-               tcpd->ack1=0;
-               tcpd->ack1_frame=0;
-               tcpd->ack1_time.secs=0;
-               tcpd->ack1_time.nsecs=0;
-               tcpd->num1_acks=0;
-               tcpd->ual2=NULL;
-               tcpd->base_seq2=0;
-               tcpd->win2=-1;
-               tcpd->win_scale2=-1;
-               tcpd->ack2=0;
-               tcpd->ack2_frame=0;
-               tcpd->ack2_time.secs=0;
-               tcpd->ack2_time.nsecs=0;
-               tcpd->num2_acks=0;
-
-               tcpd->pdu_seq1=NULL;
-               tcpd->pdu_seq2=NULL;
+               tcpd=se_alloc(sizeof(struct tcp_analysis));
+               tcpd->flow1.segments=NULL;
+               tcpd->flow1.base_seq=0;
+               tcpd->flow1.lastack=0;
+               tcpd->flow1.lastacktime.secs=0;
+               tcpd->flow1.lastacktime.nsecs=0;
+               tcpd->flow1.lastnondupack=0;
+               tcpd->flow1.nextseq=0;
+               tcpd->flow1.nextseqtime.secs=0;
+               tcpd->flow1.nextseqtime.nsecs=0;
+               tcpd->flow1.nextseqframe=0;
+               tcpd->flow1.window=0;
+               tcpd->flow1.win_scale=-1;
+               tcpd->flow1.flags=0;
+               tcpd->flow1.multisegment_pdus=se_tree_create_non_persistent(EMEM_TREE_TYPE_RED_BLACK, "tcp_multisegment_pdus");
+               tcpd->flow2.segments=NULL;
+               tcpd->flow2.base_seq=0;
+               tcpd->flow2.lastack=0;
+               tcpd->flow2.lastacktime.secs=0;
+               tcpd->flow2.lastacktime.nsecs=0;
+               tcpd->flow2.lastnondupack=0;
+               tcpd->flow2.nextseq=0;
+               tcpd->flow2.nextseqtime.secs=0;
+               tcpd->flow2.nextseqtime.nsecs=0;
+               tcpd->flow2.nextseqframe=0;
+               tcpd->flow2.window=0;
+               tcpd->flow2.win_scale=-1;
+               tcpd->flow2.flags=0;
+               tcpd->flow2.multisegment_pdus=se_tree_create_non_persistent(EMEM_TREE_TYPE_RED_BLACK, "tcp_multisegment_pdus");
+               tcpd->acked_table=se_tree_create_non_persistent(EMEM_TREE_TYPE_RED_BLACK, "tcp_analyze_acked_table");
+
 
                conversation_add_proto_data(conv, proto_tcp, tcpd);
        }
 
-       return tcpd;
-}
 
-/* This function is called from the tcp analysis code to provide
-   clues on how the seq and ack numbers are changed.
-   To prevent the next_pdu lists from growing uncontrollable in size we
-   use this function to do the following :
-   IF we see an ACK then we assume that the left edge of the window has changed
-      at least to this point and assuming it is rare with reordering and
-      trailing duplicate/retransmitted segments, we just assume that after
-      we have seen the ACK we will not see any more segments prior to the 
-      ACK value.
-      If we will not see any segments prior to the ACK value then we can just
-      delete all next_pdu entries that describe pdu's starting prior to the 
-      ACK.
-      If this heuristics is prooved to be too simplistic we can just enhance it
-      later.
-*/   
-/* XXX this function should be ehnanced to handle sequence number wrapping */
-/* XXX to handle retransmissions and reordered packets maybe we should only
-       discard entries that are more than (guesstimate) 50kb older than the
-       specified sequence number ?
-*/
-static void
-prune_next_pdu_list(struct tcp_next_pdu **tnp, guint32 seq)
-{
-       struct tcp_next_pdu *tmptnp;
-
-       if(*tnp == NULL){
-               return;
+       /* check direction and get ua lists */
+       direction=CMP_ADDRESS(&pinfo->src, &pinfo->dst);
+       /* if the addresses are equal, match the ports instead */
+       if(direction==0) {
+               direction= (pinfo->srcport > pinfo->destport)*2-1;
        }
-
-       for(tmptnp=*tnp;tmptnp;tmptnp=tmptnp->next){
-               if(tmptnp->nxtpdu<=seq){
-                       struct tcp_next_pdu *oldtnp;
-                       oldtnp=tmptnp;
-
-                       if(tmptnp==*tnp){
-                               tmptnp=tmptnp->next;
-                               *tnp=tmptnp;
-                               if(!tmptnp){
-                                       return;
-                               }
-                               continue;
-                       } else {
-                               for(tmptnp=*tnp;tmptnp;tmptnp=tmptnp->next){
-                                       if(tmptnp->next==oldtnp){
-                                               tmptnp->next=oldtnp->next;
-                                               break;
-                                       }
-                               }
-                               if(!tmptnp){
-                                       return;
-                               }
-                       }
-               }
+       if(direction>=0){
+               tcpd->fwd=&(tcpd->flow1);
+               tcpd->rev=&(tcpd->flow2);
+       } else {
+               tcpd->fwd=&(tcpd->flow2);
+               tcpd->rev=&(tcpd->flow1);
        }
+
+       tcpd->ta=NULL;
+       return tcpd;
 }
-               
 
 static void
-print_pdu_tracking_data(packet_info *pinfo, tvbuff_t *tvb, proto_tree *tcp_tree, struct tcp_next_pdu *tnp)
+print_pdu_tracking_data(packet_info *pinfo, tvbuff_t *tvb, proto_tree *tcp_tree, struct tcp_multisegment_pdu *msp)
 {
        proto_item *item;
 
        if (check_col(pinfo->cinfo, COL_INFO)){
-               col_prepend_fstr(pinfo->cinfo, COL_INFO, "[Continuation to #%u] ", tnp->first_frame);
+               col_prepend_fence_fstr(pinfo->cinfo, COL_INFO, "[Continuation to #%u] ", msp->first_frame);
        }
        item=proto_tree_add_uint(tcp_tree, hf_tcp_continuation_to,
-               tvb, 0, 0, tnp->first_frame);
+               tvb, 0, 0, msp->first_frame);
        PROTO_ITEM_SET_GENERATED(item);
 }
 
-/* if we know that a PDU starts inside this segment, return the adjusted 
+/* if we know that a PDU starts inside this segment, return the adjusted
    offset to where that PDU starts or just return offset back
    and let TCP try to find out what it can about this segment
 */
 static int
-scan_for_next_pdu(tvbuff_t *tvb, proto_tree *tcp_tree, packet_info *pinfo, int offset, guint32 seq, guint32 nxtseq)
+scan_for_next_pdu(tvbuff_t *tvb, proto_tree *tcp_tree, packet_info *pinfo, int offset, guint32 seq, guint32 nxtseq, struct tcp_analysis *tcpd)
 {
-       struct tcp_analysis *tcpd=NULL;
-       struct tcp_next_pdu *tnp=NULL;
-       int direction;
+        struct tcp_multisegment_pdu *msp=NULL;
 
        if(!pinfo->fd->flags.visited){
-               /* find(or create if needed) the conversation for this tcp session */
-               tcpd=get_tcp_conversation_data(pinfo);
-               /* check direction and get pdu start lists */
-               direction=CMP_ADDRESS(&pinfo->src, &pinfo->dst);
-               /* if the addresses are equal, match the ports instead */
-               if(direction==0) {
-                       direction= (pinfo->srcport > pinfo->destport)*2-1;
-               }
-               if(direction>=0){
-                       tnp=tcpd->pdu_seq1;
-               } else {
-                       tnp=tcpd->pdu_seq2;
-               }
-
-               /* scan and see if we find any pdus starting inside this tvb */
-               for(;tnp;tnp=tnp->next){
-                       /* XXX here we should also try to handle sequence number
-                          wrapping
-                       */
+               msp=se_tree_lookup32_le(tcpd->fwd->multisegment_pdus, seq-1);
+               if(msp){
                        /* If this segment is completely within a previous PDU
                         * then we just skip this packet
                         */
-                       if(seq>tnp->seq && nxtseq<=tnp->nxtpdu){
-                               tnp->last_frame=pinfo->fd->num;
-                               tnp->last_frame_time.secs=pinfo->fd->abs_secs;
-                               tnp->last_frame_time.nsecs=pinfo->fd->abs_usecs*1000;
-                               g_hash_table_insert(tcp_pdu_skipping_table, 
-                                       (void *)pinfo->fd->num, (void *)tnp);
-                               print_pdu_tracking_data(pinfo, tvb, tcp_tree, tnp);
-
+                       if(seq>msp->seq && nxtseq<=msp->nxtpdu){
+                               msp->last_frame=pinfo->fd->num;
+                               msp->last_frame_time=pinfo->fd->abs_ts;
+                               print_pdu_tracking_data(pinfo, tvb, tcp_tree, msp);
                                return -1;
-                       }                       
-                       if(seq<tnp->nxtpdu && nxtseq>tnp->nxtpdu){
-                               g_hash_table_insert(tcp_pdu_tracking_table, 
-                                       (void *)pinfo->fd->num, (void *)tnp->nxtpdu);
-                               offset+=tnp->nxtpdu-seq;
-                               break;
                        }
-               }
-       } else {
-               guint32 pduseq;
-
-               tnp=(struct tcp_next_pdu *)g_hash_table_lookup(tcp_pdu_time_table, (void *)pinfo->fd->num);
-               if(tnp){
-                       proto_item *item;
-                       nstime_t ns;
-
-                       item=proto_tree_add_uint(tcp_tree, hf_tcp_pdu_last_frame, tvb, 0, 0, tnp->last_frame);
-                       PROTO_ITEM_SET_GENERATED(item);
-
-                       ns.secs =tnp->last_frame_time.secs-pinfo->fd->abs_secs;
-                       ns.nsecs=tnp->last_frame_time.nsecs-pinfo->fd->abs_usecs*1000;
-                       if(ns.nsecs<0){
-                               ns.nsecs+=1000000000;
-                               ns.secs--;
+                       if(seq<msp->nxtpdu && nxtseq>msp->nxtpdu){
+                               offset+=msp->nxtpdu-seq;
+                               return offset;
                        }
-                       item = proto_tree_add_time(tcp_tree, hf_tcp_pdu_time,
-                                       tvb, 0, 0, &ns);
-                       PROTO_ITEM_SET_GENERATED(item);
 
                }
+       } else {
+               msp=se_tree_lookup32_le(tcpd->fwd->multisegment_pdus, seq-1);
+               if(msp){
+                       if(pinfo->fd->num==msp->first_frame){
+                               proto_item *item;
+                               nstime_t ns;
+
+                               item=proto_tree_add_uint(tcp_tree, hf_tcp_pdu_last_frame, tvb, 0, 0, msp->last_frame);
+                               PROTO_ITEM_SET_GENERATED(item);
+
+                               nstime_delta(&ns, &msp->last_frame_time, &pinfo->fd->abs_ts);
+                               item = proto_tree_add_time(tcp_tree, hf_tcp_pdu_time,
+                                               tvb, 0, 0, &ns);
+                               PROTO_ITEM_SET_GENERATED(item);
+                       }
 
-               /* check if this is a segment in the middle of a pdu */
-               tnp=(struct tcp_next_pdu *)g_hash_table_lookup(tcp_pdu_skipping_table, (void *)pinfo->fd->num);
-               if(tnp){
-                       print_pdu_tracking_data(pinfo, tvb, tcp_tree, tnp);
-                       return -1;
-               }
+                       /* If this segment is completely within a previous PDU
+                        * then we just skip this packet
+                        */
+                       if(seq>msp->seq && nxtseq<=msp->nxtpdu){
+                               print_pdu_tracking_data(pinfo, tvb, tcp_tree, msp);
+                               return -1;
+                       }
 
-               pduseq=(guint32)g_hash_table_lookup(tcp_pdu_tracking_table, (void *)pinfo->fd->num);
-               if(pduseq){
-                       offset+=pduseq-seq;
+                       if(seq<msp->nxtpdu && nxtseq>msp->nxtpdu){
+                               offset+=msp->nxtpdu-seq;
+                               return offset;
+                       }
                }
        }
-
        return offset;
 }
 
 /* if we saw a PDU that extended beyond the end of the segment,
    use this function to remember where the next pdu starts
 */
-static void
-pdu_store_sequencenumber_of_next_pdu(packet_info *pinfo, guint32 seq, guint32 nxtpdu)
+static struct tcp_multisegment_pdu *
+pdu_store_sequencenumber_of_next_pdu(packet_info *pinfo, guint32 seq, guint32 nxtpdu, struct tcp_analysis *tcpd)
 {
-       struct tcp_analysis *tcpd=NULL;
-       struct tcp_next_pdu *tnp=NULL;
-       int direction;
-
-       /* find(or create if needed) the conversation for this tcp session */
-       tcpd=get_tcp_conversation_data(pinfo);
-
-       tnp=g_mem_chunk_alloc(tcp_next_pdu_chunk);
-       tnp->nxtpdu=nxtpdu;
-       tnp->seq=seq;
-       tnp->first_frame=pinfo->fd->num;
-       tnp->last_frame=pinfo->fd->num;
-       tnp->last_frame_time.secs=pinfo->fd->abs_secs;
-       tnp->last_frame_time.nsecs=pinfo->fd->abs_usecs*1000;
-
-       /* check direction and get pdu start list */
-       direction=CMP_ADDRESS(&pinfo->src, &pinfo->dst);
-       /* if the addresses are equal, match the ports instead */
-       if(direction==0) {
-               direction= (pinfo->srcport > pinfo->destport)*2-1;
-       }
-       if(direction>=0){
-               tnp->next=tcpd->pdu_seq1;
-               tcpd->pdu_seq1=tnp;
-       } else {
-               tnp->next=tcpd->pdu_seq2;
-               tcpd->pdu_seq2=tnp;
-       }
-       /*QQQ 
-         Add check for ACKs and purge list of sequence numbers
-         already acked.
-       */
-       g_hash_table_insert(tcp_pdu_time_table, (void *)pinfo->fd->num, (void *)tnp);
+       struct tcp_multisegment_pdu *msp;
+
+       msp=se_alloc(sizeof(struct tcp_multisegment_pdu));
+       msp->nxtpdu=nxtpdu;
+       msp->seq=seq;
+       msp->first_frame=pinfo->fd->num;
+       msp->last_frame=pinfo->fd->num;
+       msp->last_frame_time=pinfo->fd->abs_ts;
+       se_tree_insert32(tcpd->fwd->multisegment_pdus, seq, (void *)msp);
+       return msp;
 }
 
 /* This is called for SYN+ACK packets and the purpose is to verify that we
@@ -535,719 +387,451 @@ pdu_store_sequencenumber_of_next_pdu(packet_info *pinfo, guint32 seq, guint32 nx
  * (or the SYN was missing) and then we disable the window scaling
  * for this tcp session.
  */
-static void verify_tcp_window_scaling(packet_info *pinfo)
+static void
+verify_tcp_window_scaling(struct tcp_analysis *tcpd)
 {
-       struct tcp_analysis *tcpd=NULL;
-
-       /* find(or create if needed) the conversation for this tcp session */
-       tcpd=get_tcp_conversation_data(pinfo);
-
-       if( (tcpd->win_scale1==-1) || (tcpd->win_scale2==-1) ){
-               tcpd->win_scale1=-1;
-               tcpd->win_scale2=-1;
+       if( tcpd && ((tcpd->flow1.win_scale==-1) || (tcpd->flow2.win_scale==-1)) ){
+               tcpd->flow1.win_scale=-1;
+               tcpd->flow2.win_scale=-1;
        }
 }
 
-/* if we saw a window scaling option, store it for future reference 
+/* if we saw a window scaling option, store it for future reference
 */
-static void pdu_store_window_scale_option(packet_info *pinfo, guint8 ws)
+static void
+pdu_store_window_scale_option(guint8 ws, struct tcp_analysis *tcpd)
 {
-       struct tcp_analysis *tcpd=NULL;
-       int direction;
-
-       /* find(or create if needed) the conversation for this tcp session */
-       tcpd=get_tcp_conversation_data(pinfo);
-
-       /* check direction and get pdu start list */
-       direction=CMP_ADDRESS(&pinfo->src, &pinfo->dst);
-       /* if the addresses are equal, match the ports instead */
-       if(direction==0) {
-               direction= (pinfo->srcport > pinfo->destport)*2-1;
-       }
-       if(direction>=0){
-               tcpd->win_scale1=ws;
-       } else {
-               tcpd->win_scale2=ws;
-       }
+       tcpd->fwd->win_scale=ws;
 }
 
 static void
-tcp_get_relative_seq_ack(guint32 frame, guint32 *seq, guint32 *ack, guint32 *win)
+tcp_get_relative_seq_ack(guint32 *seq, guint32 *ack, guint32 *win, struct tcp_analysis *tcpd)
 {
-       struct tcp_rel_seq *trs;
-
-       trs=g_hash_table_lookup(tcp_rel_seq_table, (void *)frame);
-       if(!trs){
-               return;
-       }
-
-       (*seq) -= trs->seq_base;
-       (*ack) -= trs->ack_base;
-       if(trs->win_scale!=-1){
-               (*win)<<=trs->win_scale;
+       if(tcp_relative_seq){
+               (*seq) -= tcpd->fwd->base_seq;
+               (*ack) -= tcpd->rev->base_seq;
+               if(tcpd->fwd->win_scale!=-1){
+                       (*win)<<=tcpd->fwd->win_scale;
+               }
        }
 }
 
-static struct tcp_acked *
-tcp_analyze_get_acked_struct(guint32 frame, gboolean createflag)
-{
-       struct tcp_acked *ta;
-
-       ta=g_hash_table_lookup(tcp_analyze_acked_table, (void *)frame);
-       if((!ta) && createflag){
-               ta=g_mem_chunk_alloc(tcp_acked_chunk);
-               ta->frame_acked=0;
-               ta->ts.secs=0;
-               ta->ts.nsecs=0;
-               ta->flags=0;
-               ta->dupack_num=0;
-               ta->dupack_frame=0;
-               g_hash_table_insert(tcp_analyze_acked_table, (void *)frame, ta);
-       }
-       return ta;
-}
 
+/* when this function returns, it will (if createflag) populate the ta pointer.
+ */
 static void
-tcp_analyze_sequence_number(packet_info *pinfo, guint32 seq, guint32 ack, guint32 seglen, guint8 flags, guint32 window)
+tcp_analyze_get_acked_struct(guint32 frame, gboolean createflag, struct tcp_analysis *tcpd)
 {
-       struct tcp_analysis *tcpd=NULL;
-       int direction;
-       struct tcp_unacked *ual1=NULL;
-       struct tcp_unacked *ual2=NULL;
-       struct tcp_unacked *ual=NULL;
-       guint32 base_seq;
-       guint32 base_ack;
-       guint32 ack1, ack2;
-       guint32 ack1_frame, ack2_frame;
-       nstime_t *ack1_time, *ack2_time;
-       guint32 num1_acks, num2_acks;
-       gint32 win1,win2;
-       gint16  win_scale1,win_scale2;
-       struct tcp_next_pdu **tnp=NULL;
-
-       /* find(or create if needed) the conversation for this tcp session */
-       tcpd=get_tcp_conversation_data(pinfo);
-
-       /* check direction and get ua lists */
-       direction=CMP_ADDRESS(&pinfo->src, &pinfo->dst);
-       /* if the addresses are equal, match the ports instead */
-       if(direction==0) {
-               direction= (pinfo->srcport > pinfo->destport)*2-1;
-       }
-       if(direction>=0){
-               ual1=tcpd->ual1;
-               ual2=tcpd->ual2;
-               ack1=tcpd->ack1;
-               ack2=tcpd->ack2;
-               ack1_frame=tcpd->ack1_frame;
-               ack2_frame=tcpd->ack2_frame;
-               ack1_time=&tcpd->ack1_time;
-               ack2_time=&tcpd->ack2_time;
-               num1_acks=tcpd->num1_acks;
-               num2_acks=tcpd->num2_acks;
-               tnp=&tcpd->pdu_seq2;
-               base_seq=(tcp_relative_seq && (ual1==NULL))?seq:tcpd->base_seq1;
-               base_ack=(tcp_relative_seq && (ual2==NULL))?ack:tcpd->base_seq2;
-               win_scale1=tcpd->win_scale1;
-               win1=tcpd->win1;
-               win_scale2=tcpd->win_scale2;
-               win2=tcpd->win2;
-       } else {
-               ual1=tcpd->ual2;
-               ual2=tcpd->ual1;
-               ack1=tcpd->ack2;
-               ack2=tcpd->ack1;
-               ack1_frame=tcpd->ack2_frame;
-               ack2_frame=tcpd->ack1_frame;
-               ack1_time=&tcpd->ack2_time;
-               ack2_time=&tcpd->ack1_time;
-               num1_acks=tcpd->num2_acks;
-               num2_acks=tcpd->num1_acks;
-               tnp=&tcpd->pdu_seq1;
-               base_seq=(tcp_relative_seq && (ual1==NULL))?seq:tcpd->base_seq2;
-               base_ack=(tcp_relative_seq && (ual2==NULL))?ack:tcpd->base_seq1;
-               win_scale1=tcpd->win_scale2;
-               win1=tcpd->win2;
-               win_scale2=tcpd->win_scale1;
-               win2=tcpd->win1;
+       tcpd->ta=se_tree_lookup32(tcpd->acked_table, frame);
+       if((!tcpd->ta) && createflag){
+               tcpd->ta=se_alloc(sizeof(struct tcp_acked));
+               tcpd->ta->frame_acked=0;
+               tcpd->ta->ts.secs=0;
+               tcpd->ta->ts.nsecs=0;
+               tcpd->ta->flags=0;
+               tcpd->ta->dupack_num=0;
+               tcpd->ta->dupack_frame=0;
+               se_tree_insert32(tcpd->acked_table, frame, (void *)tcpd->ta);
        }
+}
 
-       if(!seglen){
-               if(!ack2_frame){
-                       ack2_frame=pinfo->fd->num;
-                       ack2=ack;
-                       ack2_time->secs=pinfo->fd->abs_secs;
-                       ack2_time->nsecs=pinfo->fd->abs_usecs*1000;
-                       num2_acks=0;
-               } else if(GT_SEQ(ack, ack2)){
-                       ack2_frame=pinfo->fd->num;
-                       ack2=ack;
-                       ack2_time->secs=pinfo->fd->abs_secs;
-                       ack2_time->nsecs=pinfo->fd->abs_usecs*1000;
-                       num2_acks=0;
-               }
-       }
 
-#ifdef REMOVED
-/* useful debug ouput   
- * it prints the two lists of the sliding window emulation 
+/* fwd contains a list of all segments processed but not yet ACKed in the
+ *     same direction as the current segment.
+ * rev contains a list of all segments received but not yet ACKed in the
+ *     opposite direction to the current segment.
+ *
+ * New segments are always added to the head of the fwd/rev lists.
+ *
  */
+static void
+tcp_analyze_sequence_number(packet_info *pinfo, guint32 seq, guint32 ack, guint32 seglen, guint8 flags, guint32 window, struct tcp_analysis *tcpd)
 {
-struct tcp_unacked *u=NULL;
-printf("\n");
-printf("analyze_sequence_number(frame:%d seq:%d nextseq:%d ack:%d  baseseq:0x%08x baseack:0x%08x)\n",pinfo->fd->num,seq,seq+seglen,ack,base_seq,base_ack);
-printf("UAL1:\n");
-for(u=ual1;u;u=u->next){
-printf("  Frame:%d seq:%d nseq:%d time:%d.%09d ack:%d:%d\n",u->frame,u->seq,u->nextseq,u->ts.secs,u->ts.nsecs,ack1,ack2);
-}
-printf("UAL2:\n");
-for(u=ual2;u;u=u->next){
-printf("  Frame:%d seq:%d nseq:%d time:%d.%09d ack:%d:%d\n",u->frame,u->seq,u->nextseq,u->ts.secs,u->ts.nsecs,ack1,ack2);
-}
-}
+       tcp_unacked_t *ual=NULL;
+       int ackcount;
+
+#ifdef REMOVED
+printf("analyze_sequence numbers   frame:%d  direction:%s\n",pinfo->fd->num,direction>=0?"FWD":"REW");
+printf("FWD list lastflags:0x%04x base_seq:0x%08x:\n",tcpd->fwd->lastsegmentflags,tcpd->fwd->base_seq);for(ual=tcpd->fwd->segments;ual;ual=ual->next)printf("Frame:%d Seq:%d Nextseq:%d\n",ual->frame,ual->seq,ual->nextseq);
+printf("REV list lastflags:0x%04x base_seq:0x%08x:\n",tcpd->rev->lastsegmentflags,tcpd->rev->base_seq);for(ual=tcpd->rev->segments;ual;ual=ual->next)printf("Frame:%d Seq:%d Nextseq:%d\n",ual->frame,ual->seq,ual->nextseq);
 #endif
 
-       /* To handle FIN, just add 1 to the length.
-          else the ACK following the FIN-ACK will look like it was
-          outside the window. */
-       if( flags&TH_FIN ){
-               seglen+=1;
-       }
 
-       /* handle the sequence numbers */
-       /* if this was a SYN packet, then remove existing list and
-        * put SEQ+1 first the list */
-       if(flags&TH_SYN){
-               for(ual=ual1;ual1;ual1=ual){
-                       ual=ual1->next;
-                       g_mem_chunk_free(tcp_unacked_chunk, ual1);
-               }
-               ual1=g_mem_chunk_alloc(tcp_unacked_chunk);
-               ual1->next=NULL;
-               ual1->frame=pinfo->fd->num;
-               ack1_frame=0;
-               ack2_frame=0;
-               ack1=0;
-               ack2=0;
-               num1_acks=0;
-               num2_acks=0;
-               ual1->seq=seq;
-               ual1->nextseq=seq+1;
-               ual1->ts.secs=pinfo->fd->abs_secs;
-               ual1->ts.nsecs=pinfo->fd->abs_usecs*1000;
-               ual1->window=window;
-               ual1->flags=0;
-               if(tcp_relative_seq){
-                       base_seq=seq;
-                       /* if this was an SYN|ACK packet then set base_ack
-                        * reflect the start of the sequence, i.e. one less 
-                        */
-                       if(flags&TH_ACK){
-                               base_ack=ack-1;
-                       } else {
-                               base_ack=ack;
-                       }
-               }
-               goto seq_finished;
-       }
 
-       /* if this is the first segment we see then just add it */
-       if( !ual1 ){
-               ual1=g_mem_chunk_alloc(tcp_unacked_chunk);
-               ual1->next=NULL;
-               ual1->frame=pinfo->fd->num;
-               ual1->seq=seq;
-               ual1->nextseq=seq+seglen;
-               ual1->ts.secs=pinfo->fd->abs_secs;
-               ual1->ts.nsecs=pinfo->fd->abs_usecs*1000;
-               ual1->window=window;
-               ual1->flags=0;
-               if(tcp_relative_seq){
-                       base_seq=seq;
-                       base_ack=ack;
-               }
-               goto seq_finished;
+       /* if this is the first segment for this list we need to store the
+        * base_seq
+        */
+       if(tcpd->fwd->base_seq==0){
+               tcpd->fwd->base_seq=seq;
+       }
+       /* if we have spotted a new base_Seq in the reverse direction
+        * store it.
+        */
+       if(tcpd->rev->base_seq==0){
+               tcpd->rev->base_seq=ack;
        }
 
-       /* if we get past here we know that ual1 points to a segment */
-
-
-       /* if seq is beyond ual1->nextseq we have lost a segment */
-       if (GT_SEQ(seq, ual1->nextseq)) {
-               struct tcp_acked *ta;
 
-               ta=tcp_analyze_get_acked_struct(pinfo->fd->num, TRUE);
-               ta->flags|=TCP_A_LOST_PACKET;
 
-               /* just add the segment to the beginning of the list */
-               ual=g_mem_chunk_alloc(tcp_unacked_chunk);
-               ual->next=ual1;
-               ual->frame=pinfo->fd->num;
-               ual->seq=seq;
-               ual->nextseq=seq+seglen;
-               ual->ts.secs=pinfo->fd->abs_secs;
-               ual->ts.nsecs=pinfo->fd->abs_usecs*1000;
-               ual->window=window;
-               ual->flags=0;
-               ual1=ual;
-               goto seq_finished;
+       /* ZERO WINDOW PROBE
+        * it is a zero window probe if
+        *  the sequnece number is the next expected one
+        *  the window in the other direction is 0
+        *  the segment is exactly 1 byte
+        */
+/*QQQ tested*/
+       if( seglen==1
+       &&  seq==tcpd->fwd->nextseq
+       &&  tcpd->rev->window==0 ){
+               if(!tcpd->ta){
+                       tcp_analyze_get_acked_struct(pinfo->fd->num, TRUE, tcpd);
+               }
+               tcpd->ta->flags|=TCP_A_ZERO_WINDOW_PROBE;
+               goto finished_fwd;
        }
 
-       /* keep-alives are empty segments with a sequence number -1 of what
-        * we would expect.
-         *
-        * Solaris is an exception, Solaris does not really use KeepAlives
-        * according to RFC793, instead they move the left window edge one
-        * byte to the left and makes up a fake byte to fill in this position
-        * of the enlarged window.
-        * This means that Solaris will do "weird" KeepAlives that actually
-        * contains a one-byte segment with "random" junk data which the
-        * Solaris host then will try to transmit, and posisbly retransmit
-        * to the other side. Of course the other side will ignore this junk
-        * byte since it is outside (left of) the window.
-        * This is actually a brilliant trick that gives them, for free, 
-        * semi-reliable KeepAlives.
-        * (since normal retransmission will handle any lost keepalive segments
-        * , brilliant)
+
+       /* ZERO WINDOW
+        * a zero window packet has window == 0   but none of the SYN/FIN/RST set
         */
-       if( (seglen<=1) && EQ_SEQ(seq, (ual1->nextseq-1)) ){
-               if(!(flags&TH_FIN)){ /* FIN segments are not keepalives */
-                       struct tcp_acked *ta;
-       
-                       ta=tcp_analyze_get_acked_struct(pinfo->fd->num, TRUE);
-                       ta->flags|=TCP_A_KEEP_ALIVE;
-                       ual1->flags|=TCP_A_KEEP_ALIVE;
-                       goto seq_finished;
+/*QQQ tested*/
+       if( window==0
+       && (flags&(TH_RST|TH_FIN|TH_SYN))==0 ){
+               if(!tcpd->ta){
+                       tcp_analyze_get_acked_struct(pinfo->fd->num, TRUE, tcpd);
                }
+               tcpd->ta->flags|=TCP_A_ZERO_WINDOW;
        }
 
-       /* if this is an empty segment, just skip it all */
-       if( !seglen ){
-               goto seq_finished;
-       }
 
-       /* check if the sequence number is lower than expected, i.e. either a 
-        * retransmission a fast retransmission or an out of order segment
+       /* LOST PACKET
+        * If this segment is beyond the last seen nextseq we must
+        * have missed some previous segment
+        *
+        * We only check for this if we have actually seen segments prior to this
+        * one.
+        * RST packets are not checked for this.
         */
-       if( LT_SEQ(seq, ual1->nextseq )){
-               gboolean outoforder;
-               struct tcp_unacked *tu,*ntu;
-
-               /* assume it is a fast retransmission if
-                * 1 we have seen >=3 dupacks in the other direction for this 
-                *   segment (i.e. >=4 acks)
-                * 2 if this segment is the next unacked segment
-                * 3 this segment came within 10ms of the last dupack
-                *   (10ms is arbitrary but should be low enough not to be
-                *   confused with a retransmission timeout 
-                */
-               if( (num1_acks>=4) && (seq==ack1) ){
-                       guint32 t;
-
-                       t=(pinfo->fd->abs_secs-ack1_time->secs)*1000000000;
-                       t=t+(pinfo->fd->abs_usecs*1000)-ack1_time->nsecs;
-                       if(t<10000000){
-                               /* has to be a retransmission then */
-                               struct tcp_acked *ta;
-
-                               ta=tcp_analyze_get_acked_struct(pinfo->fd->num, TRUE);
-                               ta->flags|=TCP_A_FAST_RETRANSMISSION;
-                               goto seq_finished;
-                       }
+       if( tcpd->fwd->nextseq
+       &&  GT_SEQ(seq, tcpd->fwd->nextseq)
+       &&  (flags&(TH_RST))==0 ){
+               if(!tcpd->ta){
+                       tcp_analyze_get_acked_struct(pinfo->fd->num, TRUE, tcpd);
                }
+               tcpd->ta->flags|=TCP_A_LOST_PACKET;
+       }
 
-               /* check it is a suspected out of order segment.
-                * we assume it is an out of order segment if 
-                * 1 it has not been ACKed yet.
-                * 2 we have not seen the segment before
-                * 3 it arrived within (arbitrary value) 4ms of the
-                *      next semgent in the sequence.
-                *   4 there were no dupacks in the opposite direction.
-                */
-               outoforder=TRUE;
-#ifdef REMOVED
-               /* dont do this test.  For full-duplex capture devices that 
-                * capture in both directions using two NICs it is more common
-                * than one would expect for this to happen since they often
-                * lose the time integrity between the two NICs
-                */
-               /* 1 has it already been ACKed ? */
-               if(LT_SEQ(seq,ack1)){
-                       outoforder=FALSE;
-               }
-#endif
-               /* 2 have we seen this segment before ? */
-               for(tu=ual1;tu;tu=tu->next){
-                       if((tu->frame)&&(tu->seq==seq)){
-                               outoforder=FALSE;
-                       }
-               }
-               /* 3 was it received within 4ms of the next segment ?*/
-               ntu=NULL;
-               for(tu=ual1;tu;tu=tu->next){
-                       if(LT_SEQ(seq,tu->seq)){
-                               if(tu->frame){
-                                       ntu=tu;
-                               }
-                       }
-               }
-               if(ntu){
-                       if(pinfo->fd->abs_secs>(guint32)(ntu->ts.secs+2)){
-                               outoforder=FALSE;
-                       } else if((pinfo->fd->abs_secs+2)<(guint32)ntu->ts.secs){
-                               outoforder=FALSE;
-                       } else {
-                               guint32 t;
 
-                               t=(ntu->ts.secs-pinfo->fd->abs_secs)*1000000000;
-                               t=t+ntu->ts.nsecs-(pinfo->fd->abs_usecs*1000);
-                               if(t>4000000){
-                                       outoforder=FALSE;
-                               }
-                       }
+       /* KEEP ALIVE
+        * a keepalive contains 0 or 1 bytes of data and starts one byte prior
+        * to what should be the next sequence number.
+        * SYN/FIN/RST segments are never keepalives
+        */
+/*QQQ tested */
+       if( (seglen==0||seglen==1)
+       &&  seq==(tcpd->fwd->nextseq-1)
+       &&  (flags&(TH_SYN|TH_FIN|TH_RST))==0 ){
+               if(!tcpd->ta){
+                       tcp_analyze_get_acked_struct(pinfo->fd->num, TRUE, tcpd);
                }
+               tcpd->ta->flags|=TCP_A_KEEP_ALIVE;
+       }
 
-               
-               if(outoforder) {
-                       struct tcp_acked *ta;
-
-                       ta=tcp_analyze_get_acked_struct(pinfo->fd->num, TRUE);
-                       ta->flags|=TCP_A_OUT_OF_ORDER;
-               } else {
-                       /* has to be a retransmission then */
-                       struct tcp_acked *ta;
+       /* WINDOW UPDATE
+        * A window update is a 0 byte segment with the same SEQ/ACK numbers as
+        * the previous seen segment and with a new window value
+        */
+       if( seglen==0
+       &&  window
+       &&  window!=tcpd->fwd->window
+       &&  seq==tcpd->fwd->nextseq
+       &&  ack==tcpd->fwd->lastack
+       &&  (flags&(TH_SYN|TH_FIN|TH_RST))==0 ){
+               if(!tcpd->ta){
+                       tcp_analyze_get_acked_struct(pinfo->fd->num, TRUE, tcpd);
+               }
+               tcpd->ta->flags|=TCP_A_WINDOW_UPDATE;
+       }
 
-                       ta=tcp_analyze_get_acked_struct(pinfo->fd->num, TRUE);
-                       ta->flags|=TCP_A_RETRANSMISSION;
 
-                       /* did this segment contain any more data we havent seen yet?
-                        * if so we can just increase nextseq
-                        */
-                       if(GT_SEQ((seq+seglen), ual1->nextseq)){
-                               ual1->nextseq=seq+seglen;
-                               ual1->frame=pinfo->fd->num;
-                               ual1->ts.secs=pinfo->fd->abs_secs;
-                               ual1->ts.nsecs=pinfo->fd->abs_usecs*1000;
-                       }
+       /* WINDOW FULL
+        * If we know the window scaling
+        * and if this segment contains data ang goes all the way to the
+        * edge of the advertized window
+        * then we mark it as WINDOW FULL
+        * SYN/RST/FIN packets are never WINDOW FULL
+        */
+/*QQQ tested*/
+       if( seglen>0
+       &&  tcpd->fwd->win_scale!=-1
+       &&  tcpd->rev->win_scale!=-1
+       &&  (seq+seglen)==(tcpd->rev->lastack+(tcpd->rev->window<<tcpd->rev->win_scale))
+       &&  (flags&(TH_SYN|TH_FIN|TH_RST))==0 ){
+               if(!tcpd->ta){
+                       tcp_analyze_get_acked_struct(pinfo->fd->num, TRUE, tcpd);
                }
-               goto seq_finished;
+               tcpd->ta->flags|=TCP_A_WINDOW_FULL;
        }
 
-       /* just add the segment to the beginning of the list */
-       ual=g_mem_chunk_alloc(tcp_unacked_chunk);
-       ual->next=ual1;
-       ual->frame=pinfo->fd->num;
-       ual->seq=seq;
-       ual->nextseq=seq+seglen;
-       ual->ts.secs=pinfo->fd->abs_secs;
-       ual->ts.nsecs=pinfo->fd->abs_usecs*1000;
-       ual->window=window;
-       ual->flags=0;
-       ual1=ual;
 
-seq_finished:
+       /* KEEP ALIVE ACK
+        * It is a keepalive ack if it repeats the previous ACK and if
+        * the last segment in the reverse direction was a keepalive
+        */
+/*QQQ tested*/
+       if( seglen==0
+       &&  window
+       &&  window==tcpd->fwd->window
+       &&  seq==tcpd->fwd->nextseq
+       &&  ack==tcpd->fwd->lastack
+       && (tcpd->rev->lastsegmentflags&TCP_A_KEEP_ALIVE)
+       &&  (flags&(TH_SYN|TH_FIN|TH_RST))==0 ){
+               if(!tcpd->ta){
+                       tcp_analyze_get_acked_struct(pinfo->fd->num, TRUE, tcpd);
+               }
+               tcpd->ta->flags|=TCP_A_KEEP_ALIVE_ACK;
+               goto finished_fwd;
+       }
 
 
+       /* ZERO WINDOW PROBE ACK
+        * It is a zerowindowprobe ack if it repeats the previous ACK and if
+        * the last segment in the reverse direction was a zerowindowprobe
+        * It also repeats the previous zero window indication
+        */
+/*QQQ tested*/
+       if( seglen==0
+       &&  window==0
+       &&  window==tcpd->fwd->window
+       &&  seq==tcpd->fwd->nextseq
+       &&  ack==tcpd->fwd->lastack
+       && (tcpd->rev->lastsegmentflags&TCP_A_ZERO_WINDOW_PROBE)
+       &&  (flags&(TH_SYN|TH_FIN|TH_RST))==0 ){
+               if(!tcpd->ta){
+                       tcp_analyze_get_acked_struct(pinfo->fd->num, TRUE, tcpd);
+               }
+               tcpd->ta->flags|=TCP_A_ZERO_WINDOW_PROBE_ACK;
+               goto finished_fwd;
+       }
 
-       /* handle the ack numbers */
 
-       /* if we dont have the ack flag its not much we can do */
-       if( !(flags&TH_ACK)){
-               goto ack_finished;
+       /* DUPLICATE ACK
+        * It is a duplicate ack if window/seq/ack is the same as the previous
+        * segment and if the segment length is 0
+        */
+       if( seglen==0
+       &&  window
+       &&  window==tcpd->fwd->window
+       &&  seq==tcpd->fwd->nextseq
+       &&  ack==tcpd->fwd->lastack
+       &&  (flags&(TH_SYN|TH_FIN|TH_RST))==0 ){
+               tcpd->fwd->dupacknum++;
+               if(!tcpd->ta){
+                       tcp_analyze_get_acked_struct(pinfo->fd->num, TRUE, tcpd);
+               }
+               tcpd->ta->flags|=TCP_A_DUPLICATE_ACK;
+               tcpd->ta->dupack_num=tcpd->fwd->dupacknum;
+               tcpd->ta->dupack_frame=tcpd->fwd->lastnondupack;
        }
 
-       /* if we havent seen anything yet in the other direction we dont
-        * know what this one acks */
-       if( !ual2 ){
-               goto ack_finished;
-       }
 
-       /* if we dont have any real segments in the other direction not
-        * acked yet (as we see from the magic frame==0 entry)
-        * then there is no point in continuing
-        */
-       if( !ual2->frame ){
-               goto ack_finished;
+finished_fwd:
+       /* If this was NOT a dupack we must reset the dupack counters */
+       if( (!tcpd->ta) || !(tcpd->ta->flags&TCP_A_DUPLICATE_ACK) ){
+               tcpd->fwd->lastnondupack=pinfo->fd->num;
+               tcpd->fwd->dupacknum=0;
        }
 
-       /* if we get here we know ual2 is valid */
 
-       /* if we are acking beyong what we have seen in the other direction
-        * we must have lost packets. Not much point in keeping the segments
-        * in the other direction either.
+       /* ACKED LOST PACKET
+        * If this segment acks beyond the nextseqnum in the other direction
+        * then that means we have missed packets going in the
+        * other direction
+        *
+        * We only check this if we have actually seen some seq numbers
+        * in the other direction.
         */
-       if( GT_SEQ(ack, ual2->nextseq )){
-               struct tcp_acked *ta;
-
-               ta=tcp_analyze_get_acked_struct(pinfo->fd->num, TRUE);
-               ta->flags|=TCP_A_ACK_LOST_PACKET;
-               for(ual=ual2;ual2;ual2=ual){
-                       ual=ual2->next;
-                       g_mem_chunk_free(tcp_unacked_chunk, ual2);
+       if( tcpd->rev->nextseq
+       &&  GT_SEQ(ack, tcpd->rev->nextseq )
+       &&  (flags&(TH_ACK))!=0 ){
+/*QQQ tested*/
+               if(!tcpd->ta){
+                       tcp_analyze_get_acked_struct(pinfo->fd->num, TRUE, tcpd);
                }
-               prune_next_pdu_list(tnp, ack-base_ack);
-               goto ack_finished;
+               tcpd->ta->flags|=TCP_A_ACK_LOST_PACKET;
+               /* update nextseq in the other direction so we dont get
+                * this indication again.
+                */
+               tcpd->rev->nextseq=ack;
        }
 
 
-       /* does this ACK ack all semgents we have seen in the other direction?*/
-       if( EQ_SEQ(ack, ual2->nextseq )){
-               struct tcp_acked *ta;
+       /* RETRANSMISSION/FAST RETRANSMISSION/OUT-OF-ORDER
+        * If the segments contains data and if it does not advance
+        * sequence number it must be either of these three.
+        * Only test for this if we know what the seq number should be
+        * (tcpd->fwd->nextseq)
+        *
+        * Note that a simple KeepAlive is not a retransmission
+        */
+       if( seglen>0
+       &&  tcpd->fwd->nextseq
+       &&  (LT_SEQ(seq, tcpd->fwd->nextseq)) ){
+               guint32 t;
 
-               ta=tcp_analyze_get_acked_struct(pinfo->fd->num, TRUE);
-               ta->frame_acked=ual2->frame;
-               ta->ts.secs=pinfo->fd->abs_secs-ual2->ts.secs;
-               ta->ts.nsecs=pinfo->fd->abs_usecs*1000-ual2->ts.nsecs;
-               if(ta->ts.nsecs<0){
-                       ta->ts.nsecs+=1000000000;
-                       ta->ts.secs--;
+               if(tcpd->ta && (tcpd->ta->flags&TCP_A_KEEP_ALIVE) ){
+                       goto finished_checking_retransmission_type;
                }
 
-               /* its all been ACKed so we dont need to keep them anymore */
-               for(ual=ual2;ual2;ual2=ual){
-                       ual=ual2->next;
-                       g_mem_chunk_free(tcp_unacked_chunk, ual2);
+               /* If there were >=2 duplicate ACKs in the reverse direction
+                * (there might be duplicate acks missing from the trace)
+                * and if this sequence number matches those ACKs
+                * and if the packet occurs within 20ms of the last
+                * duplicate ack
+                * then this is a fast retransmission
+                */
+               t=(pinfo->fd->abs_ts.secs-tcpd->rev->lastacktime.secs)*1000000000;
+               t=t+(pinfo->fd->abs_ts.nsecs)-tcpd->rev->lastacktime.nsecs;
+               if( tcpd->rev->dupacknum>=2
+               &&  tcpd->rev->lastack==seq
+               &&  t<20000000 ){
+                       if(!tcpd->ta){
+                               tcp_analyze_get_acked_struct(pinfo->fd->num, TRUE, tcpd);
+                       }
+                       tcpd->ta->flags|=TCP_A_FAST_RETRANSMISSION;
+                       goto finished_checking_retransmission_type;
                }
-               prune_next_pdu_list(tnp, ack-base_ack);
-               goto ack_finished;
-       }
 
-       /* ok it only ACKs part of what we have seen. Find out how much
-        * update and remove the ACKed segments
-        */
-       for(ual=ual2;ual->next;ual=ual->next){
-               if( GE_SEQ(ack, ual->next->nextseq)){
-                       break;
-               }
-       }
-       if(ual->next){
-               struct tcp_unacked *tmpual=NULL;
-               struct tcp_unacked *ackedual=NULL;
-               struct tcp_acked *ta;
-
-               /* XXX normal ACK*/
-               ackedual=ual->next;
-
-               ta=tcp_analyze_get_acked_struct(pinfo->fd->num, TRUE);
-               ta->frame_acked=ackedual->frame;
-               ta->ts.secs=pinfo->fd->abs_secs-ackedual->ts.secs;
-               ta->ts.nsecs=pinfo->fd->abs_usecs*1000-ackedual->ts.nsecs;
-               if(ta->ts.nsecs<0){
-                       ta->ts.nsecs+=1000000000;
-                       ta->ts.secs--;
+               /* If the segment came <3ms since the segment with the highest
+                * seen sequence number, then it is an OUT-OF-ORDER segment.
+                *   (3ms is an arbitrary number)
+                */
+               t=(pinfo->fd->abs_ts.secs-tcpd->fwd->nextseqtime.secs)*1000000000;
+               t=t+(pinfo->fd->abs_ts.nsecs)-tcpd->fwd->nextseqtime.nsecs;
+               if( t<3000000 ){
+                       if(!tcpd->ta){
+                               tcp_analyze_get_acked_struct(pinfo->fd->num, TRUE, tcpd);
+                       }
+                       tcpd->ta->flags|=TCP_A_OUT_OF_ORDER;
+                       goto finished_checking_retransmission_type;
                }
 
-               /* just delete all ACKed segments */
-               tmpual=ual->next;
-               ual->next=NULL;
-               for(ual=tmpual;ual;ual=tmpual){
-                       tmpual=ual->next;
-                       g_mem_chunk_free(tcp_unacked_chunk, ual);
+               /* Then it has to be a generic retransmission */
+               if(!tcpd->ta){
+                       tcp_analyze_get_acked_struct(pinfo->fd->num, TRUE, tcpd);
                }
-               prune_next_pdu_list(tnp, ack-base_ack);
+               tcpd->ta->flags|=TCP_A_RETRANSMISSION;
+               nstime_delta(&tcpd->ta->rto_ts, &pinfo->fd->abs_ts, &tcpd->fwd->nextseqtime);
+               tcpd->ta->rto_frame=tcpd->fwd->nextseqframe;
        }
+finished_checking_retransmission_type:
 
-ack_finished:
-       /* we might have deleted the entire ual2 list, if this is an ACK,
-          make sure ual2 at least has a dummy entry for the current ACK */
-       if( (!ual2) && (flags&TH_ACK) ){
-               ual2=g_mem_chunk_alloc(tcp_unacked_chunk);
-               ual2->next=NULL;
-               ual2->frame=0;
-               ual2->seq=ack;
-               ual2->nextseq=ack;
-               ual2->ts.secs=0;
-               ual2->ts.nsecs=0;
-               ual2->window=window;
-               ual2->flags=0;
-       }
 
-       /* update the ACK counter and check for
-          duplicate ACKs*/
-       /* go to the oldest segment in the list of segments 
-          in the other direction */
-       /* XXX we should guarantee ual2 to always be non NULL here
-          so we can skip the ual/ual2 tests */
-       for(ual=ual2;ual&&ual->next;ual=ual->next)
-               ;
-       if(ual2){
-               /* we only consider this being a potential duplicate ack
-                  if the segment length is 0 (ack only segment)
-                  and if it acks something previous to oldest segment
-                  in the other direction */
-               if((!seglen)&&LE_SEQ(ack,ual->seq)){
-                       /* if this is the first ack to keep track of, it is not
-                          a duplicate */
-                       if(num2_acks==0){
-                               ack2=ack;
-                               ack2_frame=pinfo->fd->num;
-                               num2_acks=1;
-                       /* if this ack is different, store this one 
-                          instead and forget the previous one(s) */
-                       } else if(ack2!=ack){
-                               ack2=ack;
-                               ack2_frame=pinfo->fd->num;
-                               num2_acks=1;
-                       /* this has to be a duplicate ack */
-                       } else {
-                               num2_acks++;
-                       }       
-                       
-                       /* is this an ACK to a KeepAlive? */
-                       if( (ual->flags&TCP_A_KEEP_ALIVE)
-                       && (ack==ual->seq) ){
-                               struct tcp_acked *ta;
-                               ta=tcp_analyze_get_acked_struct(pinfo->fd->num, TRUE);
-                               ta->flags|=TCP_A_KEEP_ALIVE_ACK;
-                               ual->flags^=TCP_A_KEEP_ALIVE;
-                       } else if(num2_acks>1) {
-                       /* ok we have found a potential duplicate ack */
-                               struct tcp_acked *ta;
-                               ta=tcp_analyze_get_acked_struct(pinfo->fd->num, TRUE);
-                               /* keepalives are not dupacks and 
-                                * netiher are RST/FIN segments
-                                */
-                               if( (!(ta->flags&TCP_A_KEEP_ALIVE))
-                                 &&(!(flags&(TH_RST|TH_FIN))) ){
-                                       /* well then   
-                                        * this could then either be a dupack
-                                        * or maybe just a window update.
-                                        */
-                                       if(win1==(gint32)window){
-                                               ta->flags|=TCP_A_DUPLICATE_ACK;
-                                               ta->dupack_num=num2_acks-1;
-                                               ta->dupack_frame=ack2_frame;
-                                       } else {
-                                               ta->flags|=TCP_A_WINDOW_UPDATE;
-                                       }
-                               }
-                       }
-               }               
+       /* add this new sequence number to the fwd list */
+       TCP_UNACKED_NEW(ual);
+       ual->next=tcpd->fwd->segments;
+       tcpd->fwd->segments=ual;
+       ual->frame=pinfo->fd->num;
+       ual->seq=seq;
+       ual->ts=pinfo->fd->abs_ts;
 
+       /* next sequence number is seglen bytes away, plus SYN/FIN which counts as one byte */
+       ual->nextseq=seq+seglen;
+       if( flags&(TH_SYN|TH_FIN) ){
+               ual->nextseq+=1;
        }
 
-       /* see if this semgent has filled up the window completely,
-        * i.e. same thing as if the other side would start sending
-        * zero windows back to us.
+       /* Store the highest number seen so far for nextseq so we can detect
+        * when we receive segments that arrive with a "hole"
+        * If we dont have anything since before, just store what we got.
+        * ZeroWindowProbes are special and dont really advance the nextseq
         */
-       if( !(flags&TH_RST)){ /* RST segments are never WindowFull segments*/
-         if(win_scale2==-1){
-           if( EQ_SEQ( (seq+seglen), (win2+ack1) ) ){
-             struct tcp_acked *ta;
-             ta=tcp_analyze_get_acked_struct(pinfo->fd->num, TRUE);
-             ta->flags|=TCP_A_WINDOW_FULL;
-           }
-         } else {
-           if( EQ_SEQ( (seq+seglen), ((win2<<win_scale2)+ack1) ) ){
-             struct tcp_acked *ta;
-             ta=tcp_analyze_get_acked_struct(pinfo->fd->num, TRUE);
-             ta->flags|=TCP_A_WINDOW_FULL;
-           }
-         }
-       }
-
-       /* check for zero window probes 
-          a zero window probe is when a TCP tries to write 1 byte segments
-          where the remote side has advertised a window of 0 bytes.
-          We only do this check if we actually have seen anything from the
-          other side of this connection.
-
-          We also assume ual still points to the last entry in the ual2
-          list from the section above.
-
-          At the same time, check for violations, i.e. attempts to write >1
-          byte to a zero-window.
-       */
-       /* XXX we should not need to do the ual->frame check here?
-          might be a bug somewhere. look for it later .
-       */
-       if(ual2&&(ual->frame)){
-               if((seglen==1)&&(ual->window==0)){
-                       struct tcp_acked *ta;
-                       ta=tcp_analyze_get_acked_struct(pinfo->fd->num, TRUE);
-                       ta->flags|=TCP_A_ZERO_WINDOW_PROBE;
-               }
-               if((seglen>1)&&(ual->window==0)){
-                       struct tcp_acked *ta;
-                       ta=tcp_analyze_get_acked_struct(pinfo->fd->num, TRUE);
-                       ta->flags|=TCP_A_ZERO_WINDOW_VIOLATION;
+       if(GT_SEQ(ual->nextseq, tcpd->fwd->nextseq) || !tcpd->fwd->nextseq) {
+               if( !tcpd->ta || !(tcpd->ta->flags&TCP_A_ZERO_WINDOW_PROBE) ){
+                       tcpd->fwd->nextseq=ual->nextseq;
+                       tcpd->fwd->nextseqframe=pinfo->fd->num;
+                       tcpd->fwd->nextseqtime.secs=pinfo->fd->abs_ts.secs;
+                       tcpd->fwd->nextseqtime.nsecs=pinfo->fd->abs_ts.nsecs;
                }
        }
 
-       /* check for zero window
-        * dont check for RST/FIN segments since the window field is 
-        * meaningless for those
-        */
-       if( (!window)
-         &&(!(flags&(TH_RST|TH_FIN))) ){
-               struct tcp_acked *ta;
-               ta=tcp_analyze_get_acked_struct(pinfo->fd->num, TRUE);
-               ta->flags|=TCP_A_ZERO_WINDOW;
-       }
 
+       /* remember what the ack/window is so we can track window updates and retransmissions */
+       tcpd->fwd->window=window;
+       tcpd->fwd->lastack=ack;
+       tcpd->fwd->lastacktime.secs=pinfo->fd->abs_ts.secs;
+       tcpd->fwd->lastacktime.nsecs=pinfo->fd->abs_ts.nsecs;
 
-       /* store the lists back in our struct */
-       if(direction>=0){
-               /*
-                * XXX - if direction == 0, that'll be true for packets
-                * from both sides of the connection, so this won't
-                * work.
-                *
-                * That'd be a connection from a given port on a machine
-                * to that same port on the same machine; does that ever
-                * happen?
-                */
-               tcpd->ual1=ual1;
-               tcpd->ual2=ual2;
-               tcpd->ack1=ack1;
-               tcpd->ack2=ack2;
-               tcpd->ack1_frame=ack1_frame;
-               tcpd->ack2_frame=ack2_frame;
-               tcpd->num1_acks=num1_acks;
-               tcpd->num2_acks=num2_acks;
-               tcpd->base_seq1=base_seq;
-               tcpd->base_seq2=base_ack;
-               tcpd->win1=window;
+
+       /* if there were any flags set for this segment we need to remember them
+        * we only remember the flags for the very last segment though.
+        */
+       if(tcpd->ta){
+               tcpd->fwd->lastsegmentflags=tcpd->ta->flags;
        } else {
-               tcpd->ual1=ual2;
-               tcpd->ual2=ual1;
-               tcpd->ack1=ack2;
-               tcpd->ack2=ack1;
-               tcpd->ack1_frame=ack2_frame;
-               tcpd->ack2_frame=ack1_frame;
-               tcpd->num1_acks=num2_acks;
-               tcpd->num2_acks=num1_acks;
-               tcpd->base_seq2=base_seq;
-               tcpd->base_seq1=base_ack;
-               tcpd->win2=window;
+               tcpd->fwd->lastsegmentflags=0;
        }
 
 
-       if(tcp_relative_seq){
-               struct tcp_rel_seq *trs;
-               /* remember relative seq/ack number base for this packet */
-               trs=g_mem_chunk_alloc(tcp_rel_seq_chunk);
-               trs->seq_base=base_seq;
-               trs->ack_base=base_ack;
-               trs->win_scale=win_scale1;
-               g_hash_table_insert(tcp_rel_seq_table, (void *)pinfo->fd->num, trs);
+       /* remove all segments this ACKs and we dont need to keep around any more
+        */
+       ackcount=0;
+       /* first we remove all such segments at the head of the list */
+       while((ual=tcpd->rev->segments)){
+               tcp_unacked_t *tmpual;
+               if(ack==ual->nextseq){
+                       tcp_analyze_get_acked_struct(pinfo->fd->num, TRUE, tcpd);
+                       tcpd->ta->frame_acked=ual->frame;
+                       nstime_delta(&tcpd->ta->ts, &pinfo->fd->abs_ts, &ual->ts);
+               }
+               if(GT_SEQ(ual->nextseq,ack)){
+                       break;
+               }
+               if(!ackcount){
+/*qqq do the ACKs segment x  delta y */
+               }
+               ackcount++;
+               tmpual=tcpd->rev->segments->next;
+               TCP_UNACKED_FREE(ual);
+               tcpd->rev->segments=tmpual;
+       }
+       /* now we remove all such segments that are NOT at the head of the list */
+       ual=tcpd->rev->segments;
+       while(ual && ual->next){
+               tcp_unacked_t *tmpual;
+               if(GT_SEQ(ual->next->nextseq,ack)){
+                       ual=ual->next;
+                       continue;
+               }
+               if(!ackcount){
+/*qqq do the ACKs segment x  delta y */
+               }
+               ackcount++;
+               tmpual=ual->next->next;
+               TCP_UNACKED_FREE(ual->next);
+               ual->next=tmpual;
+               ual=ual->next;
        }
+
 }
 
 static void
-tcp_print_sequence_number_analysis(packet_info *pinfo, tvbuff_t *tvb, proto_tree *parent_tree)
+tcp_print_sequence_number_analysis(packet_info *pinfo, tvbuff_t *tvb, proto_tree *parent_tree, struct tcp_analysis *tcpd)
 {
        struct tcp_acked *ta;
        proto_item *item;
        proto_tree *tree;
-
-       ta=tcp_analyze_get_acked_struct(pinfo->fd->num, FALSE);
+       
+       if(!tcpd->ta){
+               tcp_analyze_get_acked_struct(pinfo->fd->num, FALSE, tcpd);
+       }
+       ta=tcpd->ta;
        if(!ta){
                return;
        }
 
        item=proto_tree_add_text(parent_tree, tvb, 0, 0, "SEQ/ACK analysis");
-    PROTO_ITEM_SET_GENERATED(item);
+       PROTO_ITEM_SET_GENERATED(item);
        tree=proto_item_add_subtree(item, ett_tcp_analysis);
 
        /* encapsulate all proto_tree_add_xxx in ifs so we only print what
@@ -1275,66 +859,82 @@ tcp_print_sequence_number_analysis(packet_info *pinfo, tvbuff_t *tvb, proto_tree
                if( ta->flags&TCP_A_RETRANSMISSION ){
                        flags_item=proto_tree_add_none_format(flags_tree, hf_tcp_analysis_retransmission, tvb, 0, 0, "This frame is a (suspected) retransmission");
                        PROTO_ITEM_SET_GENERATED(flags_item);
+                       expert_add_info_format(pinfo, flags_item, PI_SEQUENCE, PI_NOTE, "Retransmission (suspected)");
                        if(check_col(pinfo->cinfo, COL_INFO)){
-                               col_prepend_fstr(pinfo->cinfo, COL_INFO, "[TCP Retransmission] ");
+                               col_prepend_fence_fstr(pinfo->cinfo, COL_INFO, "[TCP Retransmission] ");
+                       }
+                       if( ta->rto_ts.secs || ta->rto_ts.nsecs ){
+                               item = proto_tree_add_time(flags_tree, hf_tcp_analysis_rto,
+                                       tvb, 0, 0, &ta->rto_ts);
+                               PROTO_ITEM_SET_GENERATED(item);
+                               item=proto_tree_add_uint(flags_tree, hf_tcp_analysis_rto_frame, tvb, 0, 0, ta->rto_frame);
+                               PROTO_ITEM_SET_GENERATED(item);
                        }
                }
                if( ta->flags&TCP_A_FAST_RETRANSMISSION ){
                        flags_item=proto_tree_add_none_format(flags_tree, hf_tcp_analysis_fast_retransmission, tvb, 0, 0, "This frame is a (suspected) fast retransmission");
                        PROTO_ITEM_SET_GENERATED(flags_item);
+                       expert_add_info_format(pinfo, flags_item, PI_SEQUENCE, PI_WARN, "Fast retransmission (suspected)");
                        flags_item=proto_tree_add_none_format(flags_tree, hf_tcp_analysis_retransmission, tvb, 0, 0, "This frame is a (suspected) retransmission");
                        PROTO_ITEM_SET_GENERATED(flags_item);
                        if(check_col(pinfo->cinfo, COL_INFO)){
-                               col_prepend_fstr(pinfo->cinfo, COL_INFO, "[TCP Fast Retransmission] ");
+                               col_prepend_fence_fstr(pinfo->cinfo, COL_INFO, "[TCP Fast Retransmission] ");
                        }
                }
                if( ta->flags&TCP_A_OUT_OF_ORDER ){
                        flags_item=proto_tree_add_none_format(flags_tree, hf_tcp_analysis_out_of_order, tvb, 0, 0, "This frame is a (suspected) out-of-order segment");
                        PROTO_ITEM_SET_GENERATED(flags_item);
+                       expert_add_info_format(pinfo, flags_item, PI_SEQUENCE, PI_WARN, "Out-Of-Order segment");
                        if(check_col(pinfo->cinfo, COL_INFO)){
-                               col_prepend_fstr(pinfo->cinfo, COL_INFO, "[TCP Out-Of-Order] ");
+                               col_prepend_fence_fstr(pinfo->cinfo, COL_INFO, "[TCP Out-Of-Order] ");
                        }
                }
                if( ta->flags&TCP_A_LOST_PACKET ){
                        flags_item=proto_tree_add_none_format(flags_tree, hf_tcp_analysis_lost_packet, tvb, 0, 0, "A segment before this frame was lost");
                        PROTO_ITEM_SET_GENERATED(flags_item);
+                       expert_add_info_format(pinfo, flags_item, PI_SEQUENCE, PI_WARN, "Previous segment lost (common at capture start)");
                        if(check_col(pinfo->cinfo, COL_INFO)){
-                               col_prepend_fstr(pinfo->cinfo, COL_INFO, "[TCP Previous segment lost] ");
+                               col_prepend_fence_fstr(pinfo->cinfo, COL_INFO, "[TCP Previous segment lost] ");
                        }
                }
                if( ta->flags&TCP_A_ACK_LOST_PACKET ){
                        flags_item=proto_tree_add_none_format(flags_tree, hf_tcp_analysis_ack_lost_packet, tvb, 0, 0, "This frame ACKs a segment we have not seen (lost?)");
                        PROTO_ITEM_SET_GENERATED(flags_item);
+                       expert_add_info_format(pinfo, flags_item, PI_SEQUENCE, PI_WARN, "ACKed lost segment (common at capture start)");
                        if(check_col(pinfo->cinfo, COL_INFO)){
-                               col_prepend_fstr(pinfo->cinfo, COL_INFO, "[TCP ACKed lost segment] ");
+                               col_prepend_fence_fstr(pinfo->cinfo, COL_INFO, "[TCP ACKed lost segment] ");
                        }
                }
                if( ta->flags&TCP_A_WINDOW_UPDATE ){
                        flags_item=proto_tree_add_none_format(flags_tree, hf_tcp_analysis_window_update, tvb, 0, 0, "This is a tcp window update");
                        PROTO_ITEM_SET_GENERATED(flags_item);
+                       expert_add_info_format(pinfo, flags_item, PI_SEQUENCE, PI_NOTE, "Window update");
                        if(check_col(pinfo->cinfo, COL_INFO)){
-                               col_prepend_fstr(pinfo->cinfo, COL_INFO, "[TCP Window Update] ");
+                               col_prepend_fence_fstr(pinfo->cinfo, COL_INFO, "[TCP Window Update] ");
                        }
                }
                if( ta->flags&TCP_A_WINDOW_FULL ){
                        flags_item=proto_tree_add_none_format(flags_tree, hf_tcp_analysis_window_full, tvb, 0, 0, "The transmission window is now completely full");
                        PROTO_ITEM_SET_GENERATED(flags_item);
+                       expert_add_info_format(pinfo, flags_item, PI_SEQUENCE, PI_NOTE, "Window is full");
                        if(check_col(pinfo->cinfo, COL_INFO)){
-                               col_prepend_fstr(pinfo->cinfo, COL_INFO, "[TCP Window Full] ");
+                               col_prepend_fence_fstr(pinfo->cinfo, COL_INFO, "[TCP Window Full] ");
                        }
                }
                if( ta->flags&TCP_A_KEEP_ALIVE ){
                        flags_item=proto_tree_add_none_format(flags_tree, hf_tcp_analysis_keep_alive, tvb, 0, 0, "This is a TCP keep-alive segment");
                        PROTO_ITEM_SET_GENERATED(flags_item);
+                       expert_add_info_format(pinfo, flags_item, PI_SEQUENCE, PI_NOTE, "Keep-Alive");
                        if(check_col(pinfo->cinfo, COL_INFO)){
-                               col_prepend_fstr(pinfo->cinfo, COL_INFO, "[TCP Keep-Alive] ");
+                               col_prepend_fence_fstr(pinfo->cinfo, COL_INFO, "[TCP Keep-Alive] ");
                        }
                }
                if( ta->flags&TCP_A_KEEP_ALIVE_ACK ){
                        flags_item=proto_tree_add_none_format(flags_tree, hf_tcp_analysis_keep_alive_ack, tvb, 0, 0, "This is an ACK to a TCP keep-alive segment");
                        PROTO_ITEM_SET_GENERATED(flags_item);
+                       expert_add_info_format(pinfo, flags_item, PI_SEQUENCE, PI_NOTE, "Keep-Alive ACK");
                        if(check_col(pinfo->cinfo, COL_INFO)){
-                               col_prepend_fstr(pinfo->cinfo, COL_INFO, "[TCP Keep-Alive ACK] ");
+                               col_prepend_fence_fstr(pinfo->cinfo, COL_INFO, "[TCP Keep-Alive ACK] ");
                        }
                }
                if( ta->dupack_num){
@@ -1342,7 +942,7 @@ tcp_print_sequence_number_analysis(packet_info *pinfo, tvbuff_t *tvb, proto_tree
                                flags_item=proto_tree_add_none_format(flags_tree, hf_tcp_analysis_duplicate_ack, tvb, 0, 0, "This is a TCP duplicate ack");
                                PROTO_ITEM_SET_GENERATED(flags_item);
                                if(check_col(pinfo->cinfo, COL_INFO)){
-                                       col_prepend_fstr(pinfo->cinfo, COL_INFO, "[TCP Dup ACK %u#%u] ", ta->dupack_frame, ta->dupack_num);
+                                       col_prepend_fence_fstr(pinfo->cinfo, COL_INFO, "[TCP Dup ACK %u#%u] ", ta->dupack_frame, ta->dupack_num);
                                }
                        }
                        flags_item=proto_tree_add_uint(tree, hf_tcp_analysis_duplicate_ack_num,
@@ -1351,26 +951,31 @@ tcp_print_sequence_number_analysis(packet_info *pinfo, tvbuff_t *tvb, proto_tree
                        flags_item=proto_tree_add_uint(tree, hf_tcp_analysis_duplicate_ack_frame,
                                tvb, 0, 0, ta->dupack_frame);
                        PROTO_ITEM_SET_GENERATED(flags_item);
+                       expert_add_info_format(pinfo, flags_item, PI_SEQUENCE, PI_NOTE, "Duplicate ACK (#%u)",
+                               ta->dupack_num);
                }
                if( ta->flags&TCP_A_ZERO_WINDOW_PROBE ){
                        flags_item=proto_tree_add_none_format(flags_tree, hf_tcp_analysis_zero_window_probe, tvb, 0, 0, "This is a TCP zero-window-probe");
                        PROTO_ITEM_SET_GENERATED(flags_item);
+                       expert_add_info_format(pinfo, flags_item, PI_SEQUENCE, PI_NOTE, "Zero window probe");
                        if(check_col(pinfo->cinfo, COL_INFO)){
-                               col_prepend_fstr(pinfo->cinfo, COL_INFO, "[TCP ZeroWindowProbe] ");
+                               col_prepend_fence_fstr(pinfo->cinfo, COL_INFO, "[TCP ZeroWindowProbe] ");
                        }
                }
                if( ta->flags&TCP_A_ZERO_WINDOW ){
                        flags_item=proto_tree_add_none_format(flags_tree, hf_tcp_analysis_zero_window, tvb, 0, 0, "This is a ZeroWindow segment");
                        PROTO_ITEM_SET_GENERATED(flags_item);
+                       expert_add_info_format(pinfo, flags_item, PI_SEQUENCE, PI_NOTE, "Zero window");
                        if(check_col(pinfo->cinfo, COL_INFO)){
-                               col_prepend_fstr(pinfo->cinfo, COL_INFO, "[TCP ZeroWindow] ");
+                               col_prepend_fence_fstr(pinfo->cinfo, COL_INFO, "[TCP ZeroWindow] ");
                        }
                }
-               if( ta->flags&TCP_A_ZERO_WINDOW_VIOLATION ){
-                       flags_item=proto_tree_add_none_format(flags_tree, hf_tcp_analysis_zero_window_violation, tvb, 0, 0, "This is a ZeroWindow violation, attempts to write >1 byte of data to a zero-window");
+               if( ta->flags&TCP_A_ZERO_WINDOW_PROBE_ACK ){
+                       flags_item=proto_tree_add_none_format(flags_tree, hf_tcp_analysis_zero_window_probe_ack, tvb, 0, 0, "This is an ACK to a TCP zero-window-probe");
                        PROTO_ITEM_SET_GENERATED(flags_item);
+                       expert_add_info_format(pinfo, flags_item, PI_SEQUENCE, PI_NOTE, "Zero window probe ACK");
                        if(check_col(pinfo->cinfo, COL_INFO)){
-                               col_prepend_fstr(pinfo->cinfo, COL_INFO, "[TCP ZeroWindowViolation] ");
+                               col_prepend_fence_fstr(pinfo->cinfo, COL_INFO, "[TCP ZeroWindowProbeAck] ");
                        }
                }
        }
@@ -1378,129 +983,6 @@ tcp_print_sequence_number_analysis(packet_info *pinfo, tvbuff_t *tvb, proto_tree
 }
 
 
-/* Do we still need to do this ...remove_all() even though we dont need
- * to do anything special?  The glib docs are not clear on this and
- * its better safe than sorry
- */
-static gboolean
-free_all_acked(gpointer key_arg _U_, gpointer value _U_, gpointer user_data _U_)
-{
-       return TRUE;
-}
-
-static guint
-tcp_acked_hash(gconstpointer k)
-{
-       guint32 frame = (guint32)k;
-
-       return frame;
-}
-static gint
-tcp_acked_equal(gconstpointer k1, gconstpointer k2)
-{
-       guint32 frame1 = (guint32)k1;
-       guint32 frame2 = (guint32)k2;
-
-       return frame1==frame2;
-}
-
-static void
-tcp_analyze_seq_init(void)
-{
-       /* first destroy the tables */
-       if( tcp_analyze_acked_table ){
-               g_hash_table_foreach_remove(tcp_analyze_acked_table,
-                       free_all_acked, NULL);
-               g_hash_table_destroy(tcp_analyze_acked_table);
-               tcp_analyze_acked_table = NULL;
-       }
-       if( tcp_rel_seq_table ){
-               g_hash_table_foreach_remove(tcp_rel_seq_table,
-                       free_all_acked, NULL);
-               g_hash_table_destroy(tcp_rel_seq_table);
-               tcp_rel_seq_table = NULL;
-       }
-       if( tcp_pdu_tracking_table ){
-               g_hash_table_foreach_remove(tcp_pdu_tracking_table,
-                       free_all_acked, NULL);
-               g_hash_table_destroy(tcp_pdu_tracking_table);
-               tcp_pdu_tracking_table = NULL;
-       }
-       if( tcp_pdu_time_table ){
-               g_hash_table_foreach_remove(tcp_pdu_time_table,
-                       free_all_acked, NULL);
-               g_hash_table_destroy(tcp_pdu_time_table);
-               tcp_pdu_time_table = NULL;
-       }
-       if( tcp_pdu_skipping_table ){
-               g_hash_table_foreach_remove(tcp_pdu_skipping_table,
-                       free_all_acked, NULL);
-               g_hash_table_destroy(tcp_pdu_skipping_table);
-               tcp_pdu_skipping_table = NULL;
-       }
-
-       /*
-        * Now destroy the chunk from which the conversation table
-        * structures were allocated.
-        */
-       if (tcp_next_pdu_chunk) {
-               g_mem_chunk_destroy(tcp_next_pdu_chunk);
-               tcp_next_pdu_chunk = NULL;
-       }
-       if (tcp_analysis_chunk) {
-               g_mem_chunk_destroy(tcp_analysis_chunk);
-               tcp_analysis_chunk = NULL;
-       }
-       if (tcp_unacked_chunk) {
-               g_mem_chunk_destroy(tcp_unacked_chunk);
-               tcp_unacked_chunk = NULL;
-       }
-       if (tcp_acked_chunk) {
-               g_mem_chunk_destroy(tcp_acked_chunk);
-               tcp_acked_chunk = NULL;
-       }
-       if (tcp_rel_seq_chunk) {
-               g_mem_chunk_destroy(tcp_rel_seq_chunk);
-               tcp_rel_seq_chunk = NULL;
-       }
-
-       if(tcp_analyze_seq){
-               tcp_analyze_acked_table = g_hash_table_new(tcp_acked_hash,
-                       tcp_acked_equal);
-               tcp_rel_seq_table = g_hash_table_new(tcp_acked_hash,
-                       tcp_acked_equal);
-               tcp_pdu_time_table = g_hash_table_new(tcp_acked_hash,
-                       tcp_acked_equal);
-               tcp_pdu_tracking_table = g_hash_table_new(tcp_acked_hash,
-                       tcp_acked_equal);
-               tcp_pdu_skipping_table = g_hash_table_new(tcp_acked_hash,
-                       tcp_acked_equal);
-               tcp_next_pdu_chunk = g_mem_chunk_new("tcp_next_pdu_chunk",
-                       sizeof(struct tcp_next_pdu),
-                       tcp_next_pdu_count * sizeof(struct tcp_next_pdu),
-                       G_ALLOC_ONLY);
-               tcp_analysis_chunk = g_mem_chunk_new("tcp_analysis_chunk",
-                       sizeof(struct tcp_analysis),
-                       tcp_analysis_count * sizeof(struct tcp_analysis),
-                       G_ALLOC_ONLY);
-               tcp_unacked_chunk = g_mem_chunk_new("tcp_unacked_chunk",
-                       sizeof(struct tcp_unacked),
-                       tcp_unacked_count * sizeof(struct tcp_unacked),
-                       G_ALLOC_ONLY);
-               tcp_acked_chunk = g_mem_chunk_new("tcp_acked_chunk",
-                       sizeof(struct tcp_acked),
-                       tcp_acked_count * sizeof(struct tcp_acked),
-                       G_ALLOC_ONLY);
-               if(tcp_relative_seq){
-                       tcp_rel_seq_chunk = g_mem_chunk_new("tcp_rel_seq_chunk",
-                               sizeof(struct tcp_rel_seq),
-                               tcp_rel_seq_count * sizeof(struct tcp_rel_seq),
-                               G_ALLOC_ONLY);
-               }
-       }
-
-}
-
 /* **************************************************************************
  * End of tcp sequence number analysis
  * **************************************************************************/
@@ -1558,126 +1040,34 @@ tcp_fragment_init(void)
 
 /* functions to trace tcp segments */
 /* Enable desegmenting of TCP streams */
-static gboolean tcp_desegment = FALSE;
-
-static GHashTable *tcp_segment_table = NULL;
-static GMemChunk *tcp_segment_key_chunk = NULL;
-static int tcp_segment_init_count = 200;
-static GMemChunk *tcp_segment_address_chunk = NULL;
-static int tcp_segment_address_init_count = 500;
-
-typedef struct _tcp_segment_key {
-       /* for own bookkeeping inside packet-tcp.c */
-       address *src;
-       address *dst;
-       guint32 seq;
-       /* xxx */
-       guint16 sport;
-       guint16 dport;
-       guint32 start_seq;
-       guint32 tot_len;
-       guint32 first_frame;
-} tcp_segment_key;
-
-static gboolean
-free_all_segments(gpointer key_arg, gpointer value _U_, gpointer user_data _U_)
-{
-       tcp_segment_key *key = key_arg;
-
-       if((key->src)&&(key->src->data)){
-               g_free((gpointer)key->src->data);
-               key->src->data=NULL;
-       }
-
-       if((key->dst)&&(key->dst->data)){
-               g_free((gpointer)key->dst->data);
-               key->dst->data=NULL;
-       }
-
-       return TRUE;
-}
-
-static guint
-tcp_segment_hash(gconstpointer k)
-{
-       const tcp_segment_key *key = (const tcp_segment_key *)k;
-
-       return key->seq+key->sport;
-}
-
-static gint
-tcp_segment_equal(gconstpointer k1, gconstpointer k2)
-{
-       const tcp_segment_key *key1 = (const tcp_segment_key *)k1;
-       const tcp_segment_key *key2 = (const tcp_segment_key *)k2;
-
-       return ( ( (key1->seq==key2->seq)
-                &&(ADDRESSES_EQUAL(key1->src, key2->src))
-                &&(ADDRESSES_EQUAL(key1->dst, key2->dst))
-                &&(key1->sport==key2->sport)
-                &&(key1->dport==key2->dport)
-                ) ? TRUE:FALSE);
-}
-
-static void
-tcp_desegment_init(void)
-{
-       /*
-        * Free this before freeing any memory chunks; those
-        * chunks contain data we'll look at in "free_all_segments()".
-        */
-       if(tcp_segment_table){
-               g_hash_table_foreach_remove(tcp_segment_table,
-                       free_all_segments, NULL);
-               g_hash_table_destroy(tcp_segment_table);
-               tcp_segment_table = NULL;
-       }
-
-       if(tcp_segment_key_chunk){
-               g_mem_chunk_destroy(tcp_segment_key_chunk);
-               tcp_segment_key_chunk = NULL;
-       }
-       if(tcp_segment_address_chunk){
-               g_mem_chunk_destroy(tcp_segment_address_chunk);
-               tcp_segment_address_chunk = NULL;
-       }
-
-       /* dont allocate any hash table or memory chunks unless the user
-          really uses this option
-       */
-       if(!tcp_desegment){
-               return;
-       }
-
-       tcp_segment_table = g_hash_table_new(tcp_segment_hash,
-               tcp_segment_equal);
-
-       tcp_segment_key_chunk = g_mem_chunk_new("tcp_segment_key_chunk",
-               sizeof(tcp_segment_key),
-               tcp_segment_init_count*sizeof(tcp_segment_key),
-               G_ALLOC_ONLY);
-
-       tcp_segment_address_chunk = g_mem_chunk_new("tcp_segment_address_chunk",
-               sizeof(address),
-               tcp_segment_address_init_count*sizeof(address),
-               G_ALLOC_ONLY);
-}
+static gboolean tcp_desegment = TRUE;
 
 static void
 desegment_tcp(tvbuff_t *tvb, packet_info *pinfo, int offset,
                guint32 seq, guint32 nxtseq,
                guint32 sport, guint32 dport,
-               proto_tree *tree, proto_tree *tcp_tree)
+               proto_tree *tree, proto_tree *tcp_tree,
+               struct tcp_analysis *tcpd)
 {
        struct tcpinfo *tcpinfo = pinfo->private_data;
-       fragment_data *ipfd_head=NULL;
-       tcp_segment_key old_tsk, *tsk;
-       gboolean must_desegment = FALSE;
-       gboolean called_dissector = FALSE;
+       fragment_data *ipfd_head;
+       gboolean must_desegment;
+       gboolean called_dissector;
+       int another_pdu_follows;
        int deseg_offset;
        guint32 deseg_seq;
        gint nbytes;
-    proto_item *item;
+       proto_item *item;
+       proto_item *frag_tree_item;
+       proto_item *tcp_tree_item;
+        struct tcp_multisegment_pdu *msp;
+
+again:
+       ipfd_head=NULL;
+       must_desegment = FALSE;
+       called_dissector = FALSE;
+       another_pdu_follows = 0;
+       msp=NULL;
 
        /*
         * Initialize these to assume no desegmentation.
@@ -1695,46 +1085,30 @@ desegment_tcp(tvbuff_t *tvb, packet_info *pinfo, int offset,
         */
        deseg_offset = offset;
 
-       /* First we must check if this TCP segment should be desegmented.
-          This is only to check if we should desegment this packet,
-          so we dont spend time doing COPY_ADDRESS/g_free.
-          We just "borrow" some address structures from pinfo instead. Cheaper.
-       */
-       old_tsk.src = &pinfo->src;
-       old_tsk.dst = &pinfo->dst;
-       old_tsk.sport = sport;
-       old_tsk.dport = dport;
-       old_tsk.seq = seq;
-       tsk = g_hash_table_lookup(tcp_segment_table, &old_tsk);
-
-       if(tsk){
-               /* OK, this segment was found, which means it continues
-                  a higher-level PDU. This means we must desegment it.
-                  Add it to the defragmentation lists.
+       /* find the most previous PDU starting before this sequence number */
+       msp=se_tree_lookup32_le(tcpd->fwd->multisegment_pdus, seq-1);
+       if(msp && msp->seq<=seq && msp->nxtpdu>seq){
+               int len;
+
+               if(!pinfo->fd->flags.visited){
+                       msp->last_frame=pinfo->fd->num;
+                       msp->last_frame_time=pinfo->fd->abs_ts;
+               }
+
+               /* OK, this PDU was found, which means the segment continues
+                  a higher-level PDU and that we must desegment it.
                */
-               ipfd_head = fragment_add(tvb, offset, pinfo, tsk->first_frame,
+               len=MIN(nxtseq, msp->nxtpdu) - seq;
+               ipfd_head = fragment_add(tvb, offset, pinfo, msp->first_frame,
                        tcp_fragment_table,
-                       seq - tsk->start_seq,
-                       nxtseq - seq,
-                       (LT_SEQ (nxtseq,tsk->start_seq + tsk->tot_len)) );
-
-               if(!ipfd_head){
-                       /* fragment_add() returned NULL, This means that
-                          desegmentation is not completed yet.
-                          (its like defragmentation but we know we will
-                           always add the segments in order).
-                          XXX - no, we don't; there is no guarantee that
-                          TCP segments are in order on the wire.
-
-                          we must add next segment to our table so we will
-                          find it later.
-                       */
-                       tcp_segment_key *new_tsk;
-
-                       new_tsk = g_mem_chunk_alloc(tcp_segment_key_chunk);
-                       memcpy(new_tsk, tsk, sizeof(tcp_segment_key));
-                       new_tsk->seq=nxtseq;
-                       g_hash_table_insert(tcp_segment_table,new_tsk,new_tsk);
+                       seq - msp->seq,
+                       len,
+                       (LT_SEQ (nxtseq,msp->nxtpdu)) );
+               /* if we didnt consume the entire segment there is another pdu
+                * starting beyong the end of this one 
+                */
+               if(msp->nxtpdu<nxtseq && len>0){
+                       another_pdu_follows=len;
                }
        } else {
                /* This segment was not found in our table, so it doesn't
@@ -1742,7 +1116,7 @@ desegment_tcp(tvbuff_t *tvb, packet_info *pinfo, int offset,
                   Call the normal subdissector.
                */
                process_tcp_payload(tvb, offset, pinfo, tree, tcp_tree,
-                               sport, dport, 0, 0, FALSE);
+                               sport, dport, 0, 0, FALSE, tcpd);
                called_dissector = TRUE;
 
                /* Did the subdissector ask us to desegment some more data
@@ -1771,17 +1145,16 @@ desegment_tcp(tvbuff_t *tvb, packet_info *pinfo, int offset,
                ipfd_head = NULL;
        }
 
+
        /* is it completely desegmented? */
        if(ipfd_head){
-               fragment_data *ipfd;
-
                /*
                 * Yes, we think it is.
                 * We only call subdissector for the last segment.
                 * Note that the last segment may include more than what
                 * we needed.
                 */
-               if(GE_SEQ(nxtseq, tsk->start_seq + tsk->tot_len)){
+               if(ipfd_head->reassembled_in==pinfo->fd->num){
                        /*
                         * OK, this is the last segment.
                         * Let's call the subdissector with the desegmented
@@ -1804,14 +1177,14 @@ desegment_tcp(tvbuff_t *tvb, packet_info *pinfo, int offset,
                         * Supply the sequence number of the first of the
                         * reassembled bytes.
                         */
-                       tcpinfo->seq = tsk->start_seq;
+                       tcpinfo->seq = msp->seq;
 
                        /* indicate that this is reassembled data */
                        tcpinfo->is_reassembled = TRUE;
 
                        /* call subdissector */
                        process_tcp_payload(next_tvb, 0, pinfo, tree,
-                           tcp_tree, sport, dport, 0, 0, FALSE);
+                           tcp_tree, sport, dport, 0, 0, FALSE, tcpd);
                        called_dissector = TRUE;
 
                        /*
@@ -1822,8 +1195,6 @@ desegment_tcp(tvbuff_t *tvb, packet_info *pinfo, int offset,
                        old_len=(int)(tvb_reported_length(next_tvb)-tvb_reported_length_remaining(tvb, offset));
                        if(pinfo->desegment_len &&
                            pinfo->desegment_offset<=old_len){
-                               tcp_segment_key *new_tsk;
-
                                /*
                                 * "desegment_len" isn't 0, so it needs more
                                 * data for something - and "desegment_offset"
@@ -1835,25 +1206,8 @@ desegment_tcp(tvbuff_t *tvb, packet_info *pinfo, int offset,
                                 * being a new higher-level PDU that also
                                 * needs desegmentation).
                                 */
-                               fragment_set_partial_reassembly(pinfo,tsk->first_frame,tcp_fragment_table);
-                               tsk->tot_len = tvb_reported_length(next_tvb) + pinfo->desegment_len;
-
-                               /*
-                                * Update tsk structure.
-                                * Can ask ->next->next because at least there's a hdr and one
-                                * entry in fragment_add()
-                                */
-                               for(ipfd=ipfd_head->next; ipfd->next; ipfd=ipfd->next){
-                                       old_tsk.seq = tsk->start_seq + ipfd->offset;
-                                       new_tsk = g_hash_table_lookup(tcp_segment_table, &old_tsk);
-                                       new_tsk->tot_len = tsk->tot_len;
-                               }
-
-                               /* this is the next segment in the sequence we want */
-                               new_tsk = g_mem_chunk_alloc(tcp_segment_key_chunk);
-                               memcpy(new_tsk, tsk, sizeof(tcp_segment_key));
-                               new_tsk->seq = nxtseq;
-                               g_hash_table_insert(tcp_segment_table,new_tsk,new_tsk);
+                               fragment_set_partial_reassembly(pinfo,msp->first_frame,tcp_fragment_table);
+                               msp->nxtpdu=msp->seq+tvb_reported_length(next_tvb) + pinfo->desegment_len;
                        } else {
                                /*
                                 * Show the stuff in this TCP segment as
@@ -1872,7 +1226,16 @@ desegment_tcp(tvbuff_t *tvb, packet_info *pinfo, int offset,
                                 * so we show a tree with all segments.
                                 */
                                show_fragment_tree(ipfd_head, &tcp_segment_items,
-                                       tcp_tree, pinfo, next_tvb);
+                                       tree, pinfo, next_tvb, &frag_tree_item);
+                               /*
+                                * The toplevel fragment subtree is now
+                                * behind all desegmented data; move it
+                                * right behind the TCP tree.
+                                */
+                               tcp_tree_item = proto_tree_get_parent(tcp_tree);
+                               if(frag_tree_item && tcp_tree_item) {
+                                       proto_tree_move_item(tree, tcp_tree_item, frag_tree_item);
+                               }
 
                                /* Did the subdissector ask us to desegment
                                   some more data?  This means that the data
@@ -1932,7 +1295,15 @@ desegment_tcp(tvbuff_t *tvb, packet_info *pinfo, int offset,
        }
 
        if (must_desegment) {
-           tcp_segment_key *tsk, *new_tsk;
+           /* If the dissector requested "reassemble until FIN"
+            * just set this flag for the flow and let reassembly
+            * proceed at normal.  We will check/pick up these
+            * reassembled PDUs later down in dissect_tcp() when checking
+            * for the FIN flag.
+            */
+           if(pinfo->desegment_len==DESEGMENT_UNTIL_FIN){
+               tcpd->fwd->flags|=TCP_FLOW_REASSEMBLE_UNTIL_FIN;
+           }
 
            /*
             * The sequence number at which the stuff to be desegmented
@@ -1946,60 +1317,30 @@ desegment_tcp(tvbuff_t *tvb, packet_info *pinfo, int offset,
             */
            deseg_seq = seq + (deseg_offset - offset);
 
-           /*
-            * XXX - how do we detect out-of-order transmissions?
-            * We can't just check for "nxtseq" being greater than
-            * "tsk->start_seq"; for now, we check for the difference
-            * being less than a megabyte, but this is a really
-            * gross hack - we really need to handle out-of-order
-            * transmissions correctly.
-            */
-           if ((nxtseq - deseg_seq) <= 1024*1024) {
-               /* OK, subdissector wants us to desegment
-                  some data before it can process it. Add
-                  what remains of this packet and set
-                  up next packet/sequence number as well.
+           if( ((nxtseq - deseg_seq) <= 1024*1024)
+           &&  (!pinfo->fd->flags.visited) ){
+               msp = pdu_store_sequencenumber_of_next_pdu(pinfo, deseg_seq,
+                       nxtseq + pinfo->desegment_len, tcpd);
 
-                  We must remember this segment
-               */
-               tsk = g_mem_chunk_alloc(tcp_segment_key_chunk);
-               tsk->src = g_mem_chunk_alloc(tcp_segment_address_chunk);
-               COPY_ADDRESS(tsk->src, &pinfo->src);
-               tsk->dst = g_mem_chunk_alloc(tcp_segment_address_chunk);
-               COPY_ADDRESS(tsk->dst, &pinfo->dst);
-               tsk->seq = deseg_seq;
-               tsk->start_seq = tsk->seq;
-               tsk->tot_len = nxtseq - tsk->start_seq + pinfo->desegment_len;
-               tsk->first_frame = pinfo->fd->num;
-               tsk->sport=sport;
-               tsk->dport=dport;
-               g_hash_table_insert(tcp_segment_table, tsk, tsk);
-
-               /* Add portion of segment unprocessed by the subdissector
-                  to defragmentation lists */
-               fragment_add(tvb, deseg_offset, pinfo, tsk->first_frame,
-                   tcp_fragment_table,
-                   tsk->seq - tsk->start_seq,
-                   nxtseq - tsk->start_seq,
-                   LT_SEQ (nxtseq, tsk->start_seq + tsk->tot_len));
-
-               /* this is the next segment in the sequence we want */
-               new_tsk = g_mem_chunk_alloc(tcp_segment_key_chunk);
-               memcpy(new_tsk, tsk, sizeof(tcp_segment_key));
-               new_tsk->seq = nxtseq;
-               g_hash_table_insert(tcp_segment_table,new_tsk,new_tsk);
-           }
+               /* add this segment as the first one for this new pdu */
+               fragment_add(tvb, deseg_offset, pinfo, msp->first_frame,
+                       tcp_fragment_table,
+                       0,
+                       nxtseq - deseg_seq,
+                       LT_SEQ(nxtseq, msp->nxtpdu));
+               }
        }
 
        if (!called_dissector || pinfo->desegment_len != 0) {
-               if (ipfd_head != NULL && ipfd_head->reassembled_in != 0) {
+               if (ipfd_head != NULL && ipfd_head->reassembled_in != 0 &&
+                   !(ipfd_head->flags & FD_PARTIAL_REASSEMBLY)) {
                        /*
                         * We know what frame this PDU is reassembled in;
                         * let the user know.
                         */
                        item=proto_tree_add_uint(tcp_tree, hf_tcp_reassembled_in,
                            tvb, 0, 0, ipfd_head->reassembled_in);
-            PROTO_ITEM_SET_GENERATED(item);
+                       PROTO_ITEM_SET_GENERATED(item);
                }
 
                /*
@@ -2041,6 +1382,23 @@ desegment_tcp(tvbuff_t *tvb, packet_info *pinfo, int offset,
        pinfo->can_desegment=0;
        pinfo->desegment_offset = 0;
        pinfo->desegment_len = 0;
+
+       if(another_pdu_follows){
+               /* there was another pdu following this one. */
+               pinfo->can_desegment=2;
+               /* we also have to prevent the dissector from changing the 
+                * PROTOCOL and INFO colums since what follows may be an 
+                * incomplete PDU and we dont want it be changed back from
+                *  <Protocol>   to <TCP>
+                * XXX There is no good way to block the PROTOCOL column
+                * from being changed yet so we set the entire row unwritable.
+                */
+               col_set_fence(pinfo->cinfo, COL_INFO);
+               col_set_writable(pinfo->cinfo, FALSE);
+               offset += another_pdu_follows;
+               seq += another_pdu_follows;
+               goto again;
+       }
 }
 
 /*
@@ -2065,7 +1423,7 @@ void
 tcp_dissect_pdus(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree,
                 gboolean proto_desegment, guint fixed_len,
                 guint (*get_pdu_len)(tvbuff_t *, int),
-                void (*dissect_pdu)(tvbuff_t *, packet_info *, proto_tree *))
+                dissector_t dissect_pdu)
 {
   volatile int offset = 0;
   int offset_before;
@@ -2073,6 +1431,7 @@ tcp_dissect_pdus(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree,
   guint plen;
   guint length;
   tvbuff_t *next_tvb;
+  proto_item *item=NULL;
 
   while (tvb_reported_length_remaining(tvb, offset) != 0) {
     /*
@@ -2122,6 +1481,13 @@ tcp_dissect_pdus(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree,
       show_reported_bounds_error(tvb, pinfo, tree);
       return;
     }
+    /*
+     * Display the PDU length as a field 
+     */ 
+    item=proto_tree_add_uint(pinfo->tcp_tree, hf_tcp_pdu_size, tvb, 0, 0, plen);
+    PROTO_ITEM_SET_GENERATED(item);
+        
+
 
     /* give a hint to TCP where the next PDU starts
      * so that it can attempt to find it in case it starts
@@ -2191,7 +1557,7 @@ tcp_dissect_pdus(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree,
       RETHROW;
     }
     CATCH(ReportedBoundsError) {
-      show_reported_bounds_error(tvb, pinfo, tree);
+     show_reported_bounds_error(tvb, pinfo, tree);
     }
     ENDTRY;
 
@@ -2232,16 +1598,19 @@ dissect_tcpopt_wscale(const ip_tcp_opt *optp, tvbuff_t *tvb,
     int offset, guint optlen, packet_info *pinfo, proto_tree *opt_tree)
 {
   guint8 ws;
+  struct tcp_analysis *tcpd=NULL;
+
+  tcpd=get_tcp_conversation_data(pinfo);
 
   ws = tvb_get_guint8(tvb, offset + 2);
-  proto_tree_add_boolean_hidden(opt_tree, hf_tcp_option_wscale, tvb, 
+  proto_tree_add_boolean_hidden(opt_tree, hf_tcp_option_wscale, tvb,
                                offset, optlen, TRUE);
   proto_tree_add_uint_format(opt_tree, hf_tcp_option_wscale_val, tvb,
-                            offset, optlen, ws, "%s: %u (multiply by %u)", 
+                            offset, optlen, ws, "%s: %u (multiply by %u)",
                             optp->name, ws, 1 << ws);
   tcp_info_append_uint(pinfo, "WS", ws);
   if(!pinfo->fd->flags.visited && tcp_analyze_seq && tcp_relative_seq){
-    pdu_store_window_scale_option(pinfo, ws);
+    pdu_store_window_scale_option(ws, tcpd);
   }
 }
 
@@ -2250,8 +1619,17 @@ dissect_tcpopt_sack(const ip_tcp_opt *optp, tvbuff_t *tvb,
     int offset, guint optlen, packet_info *pinfo, proto_tree *opt_tree)
 {
   proto_tree *field_tree = NULL;
-  proto_item *tf;
-  guint leftedge, rightedge;
+  proto_item *tf=NULL;
+  guint32 leftedge, rightedge;
+  struct tcp_analysis *tcpd=NULL;
+  guint32 base_ack=0;
+
+  if(tcp_analyze_seq && tcp_relative_seq){
+    /* find(or create if needed) the conversation for this tcp session */
+    tcpd=get_tcp_conversation_data(pinfo);
+
+    base_ack=tcpd->rev->base_seq;
+  }
 
   tf = proto_tree_add_text(opt_tree, tvb, offset,      optlen, "%s:", optp->name);
   offset += 2; /* skip past type and length */
@@ -2260,7 +1638,7 @@ dissect_tcpopt_sack(const ip_tcp_opt *optp, tvbuff_t *tvb,
     if (field_tree == NULL) {
       /* Haven't yet made a subtree out of this option.  Do so. */
       field_tree = proto_item_add_subtree(tf, *optp->subtree_index);
-      proto_tree_add_boolean_hidden(field_tree, hf_tcp_option_sack, tvb, 
+      proto_tree_add_boolean_hidden(field_tree, hf_tcp_option_sack, tvb,
                                    offset, optlen, TRUE);
     }
     if (optlen < 4) {
@@ -2268,10 +1646,12 @@ dissect_tcpopt_sack(const ip_tcp_opt *optp, tvbuff_t *tvb,
         "(suboption would go past end of option)");
       break;
     }
-    leftedge = tvb_get_ntohl(tvb, offset);
-    proto_tree_add_uint_format(field_tree, hf_tcp_option_sack_sle, tvb, 
-                              offset, 4, leftedge, 
-                              "left edge = %u", leftedge);
+    leftedge = tvb_get_ntohl(tvb, offset)-base_ack;
+    proto_tree_add_uint_format(field_tree, hf_tcp_option_sack_sle, tvb,
+                              offset, 4, leftedge,
+                              "left edge = %u%s", leftedge,
+                              tcp_relative_seq ? " (relative)" : "");
+
     optlen -= 4;
     if (optlen < 4) {
       proto_tree_add_text(field_tree, tvb, offset,      optlen,
@@ -2279,13 +1659,15 @@ dissect_tcpopt_sack(const ip_tcp_opt *optp, tvbuff_t *tvb,
       break;
     }
     /* XXX - check whether it goes past end of packet */
-    rightedge = tvb_get_ntohl(tvb, offset + 4);
+    rightedge = tvb_get_ntohl(tvb, offset + 4)-base_ack;
     optlen -= 4;
-    proto_tree_add_uint_format(field_tree, hf_tcp_option_sack_sre, tvb, 
-                              offset+4, 4, rightedge, 
-                              "right edge = %u", rightedge);
+    proto_tree_add_uint_format(field_tree, hf_tcp_option_sack_sre, tvb,
+                              offset+4, 4, rightedge,
+                              "right edge = %u%s", rightedge,
+                              tcp_relative_seq ? " (relative)" : "");
     tcp_info_append_uint(pinfo, "SLE", leftedge);
     tcp_info_append_uint(pinfo, "SRE", rightedge);
+    proto_item_append_text(field_tree, " %u-%u", leftedge, rightedge);
     offset += 8;
   }
 }
@@ -2312,10 +1694,10 @@ dissect_tcpopt_timestamp(const ip_tcp_opt *optp, tvbuff_t *tvb,
 
   tsv = tvb_get_ntohl(tvb, offset + 2);
   tser = tvb_get_ntohl(tvb, offset + 6);
-  proto_tree_add_boolean_hidden(opt_tree, hf_tcp_option_time_stamp, tvb, 
+  proto_tree_add_boolean_hidden(opt_tree, hf_tcp_option_time_stamp, tvb,
                                offset, optlen, TRUE);
   proto_tree_add_text(opt_tree, tvb, offset,      optlen,
-    "%s: tsval %u, tsecr %u", optp->name, tsv, tser);
+    "%s: TSval %u, TSecr %u", optp->name, tsv, tser);
   tcp_info_append_uint(pinfo, "TSV", tsv);
   tcp_info_append_uint(pinfo, "TSER", tser);
 }
@@ -2401,7 +1783,7 @@ static const ip_tcp_opt tcpopts[] = {
   },
   {
     TCPOPT_TIMESTAMP,
-    "Time stamp",
+    "Timestamps",
     NULL,
     FIXED_LENGTH,
     TCPOLEN_TIMESTAMP,
@@ -2451,12 +1833,28 @@ static const ip_tcp_opt tcpopts[] = {
 
 static gboolean try_heuristic_first = FALSE;
 
+
+/* this function can be called with tcpd==NULL as from the msproxy dissector */
 gboolean
 decode_tcp_ports(tvbuff_t *tvb, int offset, packet_info *pinfo,
-       proto_tree *tree, int src_port, int dst_port)
+       proto_tree *tree, int src_port, int dst_port,
+       struct tcp_analysis *tcpd)
 {
   tvbuff_t *next_tvb;
   int low_port, high_port;
+  int save_desegment_offset;
+  guint32 save_desegment_len;
+
+  /* dont call subdissectors for keepalive or zerowindowprobes
+   * even though they do contain payload "data"
+   * keeaplives just contain garbage and zwp contain too little data (1 byte)
+   * so why bother.
+   */
+  if(tcpd && tcpd->ta){
+    if(tcpd->ta->flags&(TCP_A_ZERO_WINDOW_PROBE|TCP_A_KEEP_ALIVE)){
+      return TRUE;
+    }
+  }
 
   next_tvb = tvb_new_subset(tvb, offset, -1, -1);
 
@@ -2471,10 +1869,21 @@ decode_tcp_ports(tvbuff_t *tvb, int offset, packet_info *pinfo,
 
   if (try_heuristic_first) {
     /* do lookup with the heuristic subdissector table */
+    save_desegment_offset = pinfo->desegment_offset;
+    save_desegment_len = pinfo->desegment_len;
     if (dissector_try_heuristic(heur_subdissector_list, next_tvb, pinfo, tree)){
        pinfo->want_pdu_tracking -= !!(pinfo->want_pdu_tracking);
        return TRUE;
     }
+    /*
+     * They rejected the packet; make sure they didn't also request
+     * desegmentation (we could just override the request, but
+     * rejecting a packet *and* requesting desegmentation is a sign
+     * of the dissector's code needing clearer thought, so we fail
+     * so that the problem is made more obvious).
+     */
+    DISSECTOR_ASSERT(save_desegment_offset == pinfo->desegment_offset &&
+                     save_desegment_len == pinfo->desegment_len);
   }
 
   /* Do lookups with the subdissector table.
@@ -2512,10 +1921,21 @@ decode_tcp_ports(tvbuff_t *tvb, int offset, packet_info *pinfo,
 
   if (!try_heuristic_first) {
     /* do lookup with the heuristic subdissector table */
+    save_desegment_offset = pinfo->desegment_offset;
+    save_desegment_len = pinfo->desegment_len;
     if (dissector_try_heuristic(heur_subdissector_list, next_tvb, pinfo, tree)){
        pinfo->want_pdu_tracking -= !!(pinfo->want_pdu_tracking);
        return TRUE;
     }
+    /*
+     * They rejected the packet; make sure they didn't also request
+     * desegmentation (we could just override the request, but
+     * rejecting a packet *and* requesting desegmentation is a sign
+     * of the dissector's code needing clearer thought, so we fail
+     * so that the problem is made more obvious).
+     */
+    DISSECTOR_ASSERT(save_desegment_offset == pinfo->desegment_offset &&
+                     save_desegment_len == pinfo->desegment_len);
   }
 
   /* Oh, well, we don't know this; dissect it as data. */
@@ -2528,7 +1948,8 @@ decode_tcp_ports(tvbuff_t *tvb, int offset, packet_info *pinfo,
 static void
 process_tcp_payload(tvbuff_t *tvb, volatile int offset, packet_info *pinfo,
        proto_tree *tree, proto_tree *tcp_tree, int src_port, int dst_port,
-       guint32 seq, guint32 nxtseq, gboolean is_tcp_segment)
+       guint32 seq, guint32 nxtseq, gboolean is_tcp_segment,
+       struct tcp_analysis *tcpd)
 {
        pinfo->want_pdu_tracking=0;
 
@@ -2538,7 +1959,7 @@ process_tcp_payload(tvbuff_t *tvb, volatile int offset, packet_info *pinfo,
                        if(tcp_analyze_seq && (!tcp_desegment)){
                                if(seq || nxtseq){
                                        offset=scan_for_next_pdu(tvb, tcp_tree, pinfo, offset,
-                                               seq, nxtseq);
+                                               seq, nxtseq, tcpd);
                                }
                        }
                }
@@ -2548,7 +1969,7 @@ process_tcp_payload(tvbuff_t *tvb, volatile int offset, packet_info *pinfo,
                 */
                if( (offset!=-1) &&
                    decode_tcp_ports(tvb, offset, pinfo, tree, src_port,
-                       dst_port) ){
+                       dst_port, tcpd) ){
                        /*
                         * We succeeded in handing off to a subdissector.
                         *
@@ -2564,7 +1985,8 @@ process_tcp_payload(tvbuff_t *tvb, volatile int offset, packet_info *pinfo,
                                                pdu_store_sequencenumber_of_next_pdu(
                                                    pinfo,
                                                    seq,
-                                                   nxtseq+pinfo->bytes_until_next_pdu);
+                                                   nxtseq+pinfo->bytes_until_next_pdu,
+                                                   tcpd);
                                        }
                                }
                        }
@@ -2585,21 +2007,15 @@ process_tcp_payload(tvbuff_t *tvb, volatile int offset, packet_info *pinfo,
                        /*
                         * It's from a TCP segment.
                         *
-                        * Handle TCP seq# analysis, print any extra SEQ/ACK
-                        * data for this segment.
-                        */
-                       if(tcp_analyze_seq){
-                               tcp_print_sequence_number_analysis(pinfo, tvb, tcp_tree);
-                       }
-                       /*
                         * if !visited, check want_pdu_tracking and store it
-                        * in table 
+                        * in table
                         */
                        if((!pinfo->fd->flags.visited) && tcp_analyze_seq && pinfo->want_pdu_tracking){
                                if(seq || nxtseq){
                                        pdu_store_sequencenumber_of_next_pdu(pinfo,
                                            seq,
-                                           nxtseq+pinfo->bytes_until_next_pdu);
+                                           nxtseq+pinfo->bytes_until_next_pdu,
+                                           tcpd);
                                }
                        }
                }
@@ -2611,7 +2027,8 @@ process_tcp_payload(tvbuff_t *tvb, volatile int offset, packet_info *pinfo,
 void
 dissect_tcp_payload(tvbuff_t *tvb, packet_info *pinfo, int offset, guint32 seq,
                    guint32 nxtseq, guint32 sport, guint32 dport,
-                   proto_tree *tree, proto_tree *tcp_tree)
+                   proto_tree *tree, proto_tree *tcp_tree,
+                   struct tcp_analysis *tcpd)
 {
   gboolean save_fragmented;
 
@@ -2619,7 +2036,7 @@ dissect_tcp_payload(tvbuff_t *tvb, packet_info *pinfo, int offset, guint32 seq,
   if (pinfo->can_desegment) {
     /* Yes. */
     desegment_tcp(tvb, pinfo, offset, seq, nxtseq, sport, dport, tree,
-        tcp_tree);
+        tcp_tree, tcpd);
   } else {
     /* No - just call the subdissector.
        Mark this as fragmented, so if somebody throws an exception,
@@ -2627,7 +2044,7 @@ dissect_tcp_payload(tvbuff_t *tvb, packet_info *pinfo, int offset, guint32 seq,
     save_fragmented = pinfo->fragmented;
     pinfo->fragmented = TRUE;
     process_tcp_payload(tvb, offset, pinfo, tree, tcp_tree, sport, dport,
-        seq, nxtseq, TRUE);
+        seq, nxtseq, TRUE, tcpd);
     pinfo->fragmented = save_fragmented;
   }
 }
@@ -2641,9 +2058,10 @@ dissect_tcp(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree)
   proto_tree *tcp_tree = NULL, *field_tree = NULL;
   proto_item *ti = NULL, *tf;
   int        offset = 0;
-  gchar      flags[64] = "<None>";
-  gchar     *fstr[] = {"FIN", "SYN", "RST", "PSH", "ACK", "URG", "ECN", "CWR" };
-  gint       fpos = 0, i;
+  gchar      *flags = "<None>";
+  const gchar *fstr[] = {"FIN", "SYN", "RST", "PSH", "ACK", "URG", "ECN", "CWR" };
+  size_t     fpos = 0, returned_length;
+  gint       i;
   guint      bpos;
   guint      optlen;
   guint32    nxtseq = 0;
@@ -2655,14 +2073,11 @@ dissect_tcp(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree)
   guint      length_remaining;
   gboolean   desegment_ok;
   struct tcpinfo tcpinfo;
-  static struct tcpheader tcphstruct[4], *tcph;
-  static int tcph_count=0;
+  struct tcpheader *tcph;
+  proto_item *tf_syn = NULL, *tf_fin = NULL, *tf_rst = NULL;
+  struct tcp_analysis *tcpd=NULL;
 
-  tcph_count++;
-  if(tcph_count>=4){
-     tcph_count=0;
-  }
-  tcph=&tcphstruct[tcph_count];
+  tcph=ep_alloc(sizeof(struct tcpheader));
   SET_ADDRESS(&tcph->ip_src, pinfo->src.type, pinfo->src.len, pinfo->src.data);
   SET_ADDRESS(&tcph->ip_dst, pinfo->dst.type, pinfo->dst.len, pinfo->dst.data);
 
@@ -2690,6 +2105,8 @@ dissect_tcp(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree)
            ti = proto_tree_add_item(tree, proto_tcp, tvb, 0, -1, FALSE);
     }
     tcp_tree = proto_item_add_subtree(ti, ett_tcp);
+    pinfo->tcp_tree=tcp_tree;
+
     proto_tree_add_uint_format(tcp_tree, hf_tcp_srcport, tvb, offset, 2, tcph->th_sport,
        "Source port: %s (%u)", get_tcp_port(tcph->th_sport), tcph->th_sport);
     proto_tree_add_uint_format(tcp_tree, hf_tcp_dstport, tvb, offset + 2, 2, tcph->th_dport,
@@ -2713,6 +2130,9 @@ dissect_tcp(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree)
   real_window = tcph->th_win;
   tcph->th_hlen = hi_nibble(th_off_x2) * 4;  /* TCP header length, in bytes */
 
+  /* find(or create if needed) the conversation for this tcp session */
+  tcpd=get_tcp_conversation_data(pinfo);
+
   /*
    * If we've been handed an IP fragment, we don't know how big the TCP
    * segment is, so don't do anything that requires that we know that.
@@ -2732,9 +2152,12 @@ dissect_tcp(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree)
 
   if (!pinfo->fragmented && !pinfo->in_error_pkt) {
     if (reported_len < tcph->th_hlen) {
-      proto_tree_add_text(tcp_tree, tvb, offset, 0,
+      proto_item *pi;
+      pi = proto_tree_add_text(tcp_tree, tvb, offset, 0,
         "Short segment. Segment/fragment does not contain a full TCP header"
         " (might be NMAP or someone else deliberately sending unusual packets)");
+      PROTO_ITEM_SET_GENERATED(pi);
+      expert_add_info_format(pinfo, pi, PI_MALFORMED, PI_WARN, "Short segment");
       tcph->th_have_seglen = FALSE;
     } else {
       /* Compute the length of data in this segment. */
@@ -2747,13 +2170,14 @@ dissect_tcp(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree)
 
       }
 
       /* handle TCP seq# analysis parse all new segments we see */
       if(tcp_analyze_seq){
           if(!(pinfo->fd->flags.visited)){
-              tcp_analyze_sequence_number(pinfo, tcph->th_seq, tcph->th_ack, tcph->th_seglen, tcph->th_flags, tcph->th_win);
+              tcp_analyze_sequence_number(pinfo, tcph->th_seq, tcph->th_ack, tcph->th_seglen, tcph->th_flags, tcph->th_win, tcpd);
           }
           if(tcp_relative_seq){
-              tcp_get_relative_seq_ack(pinfo->fd->num, &(tcph->th_seq), &(tcph->th_ack), &(tcph->th_win));
+              tcp_get_relative_seq_ack(&(tcph->th_seq), &(tcph->th_ack), &(tcph->th_win), tcpd);
           }
       }
 
@@ -2764,23 +2188,28 @@ dissect_tcp(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree)
     tcph->th_have_seglen = FALSE;
 
   if (check_col(pinfo->cinfo, COL_INFO) || tree) {
+#define MAX_FLAGS_LEN 64
+    flags=ep_alloc(MAX_FLAGS_LEN);
+    flags[0]=0;
     for (i = 0; i < 8; i++) {
       bpos = 1 << i;
       if (tcph->th_flags & bpos) {
-        if (fpos) {
-          strcpy(&flags[fpos], ", ");
-          fpos += 2;
-        }
-        strcpy(&flags[fpos], fstr[i]);
-        fpos += 3;
+        returned_length = g_snprintf(&flags[fpos], MAX_FLAGS_LEN-fpos, "%s%s",
+               fpos?", ":"",
+               fstr[i]);
+       fpos += MIN(returned_length, MAX_FLAGS_LEN-fpos);
       }
     }
-    flags[fpos] = '\0';
   }
 
   if (check_col(pinfo->cinfo, COL_INFO)) {
-    col_append_fstr(pinfo->cinfo, COL_INFO, " [%s] Seq=%u Ack=%u Win=%u",
-      flags, tcph->th_seq, tcph->th_ack, tcph->th_win);
+    if(tcph->th_flags&TH_ACK){
+      col_append_fstr(pinfo->cinfo, COL_INFO, " [%s] Seq=%u Ack=%u Win=%u",
+        flags, tcph->th_seq, tcph->th_ack, tcph->th_win);
+    } else {
+      col_append_fstr(pinfo->cinfo, COL_INFO, " [%s] Seq=%u",
+        flags, tcph->th_seq);
+    }
   }
 
   if (tree) {
@@ -2812,7 +2241,9 @@ dissect_tcp(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree)
 
   if (tree) {
     if (tcp_summary_in_tree) {
-      proto_item_append_text(ti, ", Ack: %u", tcph->th_ack);
+      if(tcph->th_flags&TH_ACK){
+        proto_item_append_text(ti, ", Ack: %u", tcph->th_ack);
+      }
       if (tcph->th_have_seglen)
         proto_item_append_text(ti, ", Len: %u", tcph->th_seglen);
     }
@@ -2837,23 +2268,40 @@ dissect_tcp(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree)
     proto_tree_add_uint_format(tcp_tree, hf_tcp_hdr_len, tvb, offset + 12, 1, tcph->th_hlen,
        "Header length: %u bytes", tcph->th_hlen);
     tf = proto_tree_add_uint_format(tcp_tree, hf_tcp_flags, tvb, offset + 13, 1,
-       tcph->th_flags, "Flags: 0x%04x (%s)", tcph->th_flags, flags);
+       tcph->th_flags, "Flags: 0x%02x (%s)", tcph->th_flags, flags);
     field_tree = proto_item_add_subtree(tf, ett_tcp_flags);
     proto_tree_add_boolean(field_tree, hf_tcp_flags_cwr, tvb, offset + 13, 1, tcph->th_flags);
     proto_tree_add_boolean(field_tree, hf_tcp_flags_ecn, tvb, offset + 13, 1, tcph->th_flags);
     proto_tree_add_boolean(field_tree, hf_tcp_flags_urg, tvb, offset + 13, 1, tcph->th_flags);
     proto_tree_add_boolean(field_tree, hf_tcp_flags_ack, tvb, offset + 13, 1, tcph->th_flags);
     proto_tree_add_boolean(field_tree, hf_tcp_flags_push, tvb, offset + 13, 1, tcph->th_flags);
-    proto_tree_add_boolean(field_tree, hf_tcp_flags_reset, tvb, offset + 13, 1, tcph->th_flags);
-    proto_tree_add_boolean(field_tree, hf_tcp_flags_syn, tvb, offset + 13, 1, tcph->th_flags);
-    proto_tree_add_boolean(field_tree, hf_tcp_flags_fin, tvb, offset + 13, 1, tcph->th_flags);
-    if(tcp_relative_seq && (tcph->th_win!=real_window)){
+    tf_rst = proto_tree_add_boolean(field_tree, hf_tcp_flags_reset, tvb, offset + 13, 1, tcph->th_flags);
+    tf_syn = proto_tree_add_boolean(field_tree, hf_tcp_flags_syn, tvb, offset + 13, 1, tcph->th_flags);
+    tf_fin = proto_tree_add_boolean(field_tree, hf_tcp_flags_fin, tvb, offset + 13, 1, tcph->th_flags);
+    if(tcp_relative_seq 
+    && (tcph->th_win!=real_window) 
+    && !(tcph->th_flags&TH_SYN) ){   /* SYNs are never scaled */
       proto_tree_add_uint_format(tcp_tree, hf_tcp_window_size, tvb, offset + 14, 2, tcph->th_win, "Window size: %u (scaled)", tcph->th_win);
     } else {
-      proto_tree_add_uint(tcp_tree, hf_tcp_window_size, tvb, offset + 14, 2, tcph->th_win);
+      proto_tree_add_uint(tcp_tree, hf_tcp_window_size, tvb, offset + 14, 2, real_window);
     }
   }
 
+  if(tcph->th_flags & TH_SYN) {
+    if(tcph->th_flags & TH_ACK)
+      expert_add_info_format(pinfo, tf_syn, PI_SEQUENCE, PI_CHAT, "Connection establish acknowledge (SYN+ACK): server port %s",
+                             get_tcp_port(tcph->th_sport));
+    else
+      expert_add_info_format(pinfo, tf_syn, PI_SEQUENCE, PI_CHAT, "Connection establish request (SYN): server port %s",
+                             get_tcp_port(tcph->th_dport));
+  }
+  if(tcph->th_flags & TH_FIN)
+    /* XXX - find a way to know the server port and output only that one */
+    expert_add_info_format(pinfo, tf_fin, PI_SEQUENCE, PI_CHAT, "Connection finish (FIN)");
+  if(tcph->th_flags & TH_RST)
+    /* XXX - find a way to know the server port and output only that one */
+    expert_add_info_format(pinfo, tf_rst, PI_SEQUENCE, PI_CHAT, "Connection reset (RST)");
+
   /* Supply the sequence number of the first byte and of the first byte
      after the segment. */
   tcpinfo.seq = tcph->th_seq;
@@ -2900,7 +2348,7 @@ dissect_tcp(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree)
 
       default:
         /* TCP runs only atop IPv4 and IPv6.... */
-        g_assert_not_reached();
+        DISSECTOR_ASSERT_NOT_REACHED();
         break;
       }
       cksum_vec[3].ptr = tvb_get_ptr(tvb, offset, reported_len);
@@ -2908,28 +2356,41 @@ dissect_tcp(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree)
       computed_cksum = in_cksum(&cksum_vec[0], 4);
       if (computed_cksum == 0) {
         proto_tree_add_uint_format(tcp_tree, hf_tcp_checksum, tvb,
-          offset + 16, 2, th_sum, "Checksum: 0x%04x (correct)", th_sum);
+          offset + 16, 2, th_sum, "Checksum: 0x%04x [correct]", th_sum);
 
         /* Checksum is valid, so we're willing to desegment it. */
         desegment_ok = TRUE;
-      } else {
-        proto_tree_add_boolean_hidden(tcp_tree, hf_tcp_checksum_bad, tvb,
-          offset + 16, 2, TRUE);
+      } else if (th_sum == 0) {
+        /* checksum is probably fine but checksum offload is used */
         proto_tree_add_uint_format(tcp_tree, hf_tcp_checksum, tvb,
+          offset + 16, 2, th_sum, "Checksum: 0x%04x [Checksum Offloaded]", th_sum);
+
+        /* Checksum is (probably) valid, so we're willing to desegment it. */
+        desegment_ok = TRUE;
+      } else {
+        proto_item *item;
+
+        item = proto_tree_add_uint_format(tcp_tree, hf_tcp_checksum, tvb,
            offset + 16, 2, th_sum,
-          "Checksum: 0x%04x (incorrect, should be 0x%04x)", th_sum,
+          "Checksum: 0x%04x [incorrect, should be 0x%04x (maybe caused by checksum offloading?)]", th_sum,
           in_cksum_shouldbe(th_sum, computed_cksum));
+               expert_add_info_format(pinfo, item, PI_CHECKSUM, PI_ERROR, "Bad checksum");
+        item = proto_tree_add_boolean(tcp_tree, hf_tcp_checksum_bad, tvb,
+          offset + 16, 2, TRUE);
+        PROTO_ITEM_SET_GENERATED(item);
+               /* XXX - don't use hidden fields for checksums */
+        PROTO_ITEM_SET_HIDDEN(item);
 
         if (check_col(pinfo->cinfo, COL_INFO))
-          col_append_fstr(pinfo->cinfo, COL_INFO, " [CHECKSUM INCORRECT]");
+          col_append_fstr(pinfo->cinfo, COL_INFO, " [TCP CHECKSUM INCORRECT]");
 
         /* Checksum is invalid, so we're not willing to desegment it. */
         desegment_ok = FALSE;
-        pinfo->noreassembly_reason = " (incorrect TCP checksum)";
+        pinfo->noreassembly_reason = " [incorrect TCP checksum]";
       }
     } else {
       proto_tree_add_uint_format(tcp_tree, hf_tcp_checksum, tvb,
-         offset + 16, 2, th_sum, "Checksum: 0x%04x", th_sum);
+         offset + 16, 2, th_sum, "Checksum: 0x%04x [validation disabled]", th_sum);
 
       /* We didn't check the checksum, and don't care if it's valid,
          so we're willing to desegment it. */
@@ -2938,7 +2399,7 @@ dissect_tcp(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree)
   } else {
     /* We don't have all the packet data, so we can't checksum it... */
     proto_tree_add_uint_format(tcp_tree, hf_tcp_checksum, tvb,
-       offset + 16, 2, th_sum, "Checksum: 0x%04x", th_sum);
+       offset + 16, 2, th_sum, "Checksum: 0x%04x [unchecked, not all data available]", th_sum);
 
     /* ...and aren't willing to desegment it. */
     desegment_ok = FALSE;
@@ -2980,9 +2441,11 @@ dissect_tcp(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree)
     /* There's more than just the fixed-length header.  Decode the
        options. */
     optlen = tcph->th_hlen - TCPH_MIN_LEN; /* length of options, in bytes */
+    tvb_ensure_bytes_exist(tvb, offset +  20, optlen);
     if (tcp_tree != NULL) {
-      tf = proto_tree_add_text(tcp_tree, tvb, offset +  20, optlen,
-        "Options: (%u bytes)", optlen);
+      guint8 *p_options = tvb_get_ephemeral_string(tvb, offset + 20, optlen);
+      tf = proto_tree_add_bytes_format(tcp_tree, hf_tcp_options, tvb, offset +  20, 
+        optlen, p_options, "Options: (%u bytes)", optlen);
       field_tree = proto_item_add_subtree(tf, ett_tcp_options);
     } else
       field_tree = NULL;
@@ -2990,13 +2453,13 @@ dissect_tcp(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree)
       tcpopts, N_TCP_OPTS, TCPOPT_EOL, pinfo, field_tree);
   }
 
-  /* If there was window scaling in the SYN packet byt none in the SYN+ACK
+  /* If there was window scaling in the SYN packet but none in the SYN+ACK
    * then we should just forget about the windowscaling completely.
    */
   if(!pinfo->fd->flags.visited){
     if(tcp_analyze_seq && tcp_relative_seq){
       if((tcph->th_flags & (TH_SYN|TH_ACK))==(TH_SYN|TH_ACK)) {
-        verify_tcp_window_scaling(pinfo);
+        verify_tcp_window_scaling(tcpd);
       }
     }
   }
@@ -3022,6 +2485,58 @@ dissect_tcp(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree)
     }
   }
 
+  /* handle TCP seq# analysis, print any extra SEQ/ACK data for this segment*/
+  if(tcp_analyze_seq){
+      tcp_print_sequence_number_analysis(pinfo, tvb, tcp_tree, tcpd);
+  }
+  tap_queue_packet(tcp_tap, pinfo, tcph);
+
+
+  /* A FIN packet might complete reassembly so we need to explicitely
+   * check for this here.
+   * If this segment completes reassembly we add the FIN as a final dummy
+   * byte to the reassembled PDU and check if reassembly completed successfully
+   */
+  if( (tcph->th_flags & TH_FIN)
+  &&  (tcpd->fwd->flags&TCP_FLOW_REASSEMBLE_UNTIL_FIN) ){
+    struct tcp_multisegment_pdu *msp;
+
+    /* find the most previous PDU starting before this sequence number */
+    msp=se_tree_lookup32_le(tcpd->fwd->multisegment_pdus, tcph->th_seq-1);
+    if(msp){
+      fragment_data *ipfd_head;
+
+      ipfd_head = fragment_add(tvb, offset-1, pinfo, msp->first_frame,
+                       tcp_fragment_table,
+                       tcph->th_seq - msp->seq,
+                       1,
+                       FALSE );
+      if(ipfd_head){
+        tvbuff_t *next_tvb;
+
+        /* create a new TVB structure for desegmented data
+         * datalen-1 to strip the dummy FIN byte off
+         */
+        next_tvb = tvb_new_real_data(ipfd_head->data, ipfd_head->datalen-1, ipfd_head->datalen-1);
+
+        /* add this tvb as a child to the original one */
+        tvb_set_child_real_data_tvbuff(tvb, next_tvb);
+
+        /* add desegmented data to the data source list */
+        add_new_data_source(pinfo, next_tvb, "Reassembled TCP");
+
+        /* call the payload dissector
+         * but make sure we dont offer desegmentation any more
+         */
+       pinfo->can_desegment = 0;
+
+        process_tcp_payload(next_tvb, 0, pinfo, tree, tcp_tree, tcph->th_sport, tcph->th_dport, tcph->th_seq, nxtseq, FALSE, tcpd);
+
+        return;
+      }
+    }
+  }
+
   /*
    * XXX - what, if any, of this should we do if this is included in an
    * error packet?  It might be nice to see the details of the packet
@@ -3051,15 +2566,9 @@ dissect_tcp(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree)
                            tvb_format_text(tvb, offset, length_remaining));
     } else {
       dissect_tcp_payload(tvb, pinfo, offset, tcph->th_seq, nxtseq,
-          tcph->th_sport, tcph->th_dport, tree, tcp_tree);
+                          tcph->th_sport, tcph->th_dport, tree, tcp_tree, tcpd);
     }
   }
-
-  /* handle TCP seq# analysis, print any extra SEQ/ACK data for this segment*/
-  if(tcp_analyze_seq){
-      tcp_print_sequence_number_analysis(pinfo, tvb, tcp_tree);
-  }
-  tap_queue_packet(tcp_tap, pinfo, tcph);
 }
 
 void
@@ -3138,11 +2647,11 @@ proto_register_tcp(void)
 
                { &hf_tcp_checksum,
                { "Checksum",                   "tcp.checksum", FT_UINT16, BASE_HEX, NULL, 0x0,
-                       "", HFILL }},
+                       "Details at: http://www.wireshark.org/docs/wsug_html_chunked/ChAdvChecksums.html", HFILL }},
 
                { &hf_tcp_checksum_bad,
                { "Bad Checksum",               "tcp.checksum_bad", FT_BOOLEAN, BASE_NONE, NULL, 0x0,
-                       "", HFILL }},
+                       "Maybe caused by checksum offloading, see: http://www.wireshark.org/docs/wsug_html_chunked/ChAdvChecksums.html", HFILL }},
 
                { &hf_tcp_analysis_flags,
                { "TCP Analysis Flags",         "tcp.analysis.flags", FT_NONE, BASE_NONE, NULL, 0x0,
@@ -3174,7 +2683,7 @@ proto_register_tcp(void)
 
                { &hf_tcp_analysis_window_full,
                { "Window full",                "tcp.analysis.window_full", FT_NONE, BASE_NONE, NULL, 0x0,
-                       "The this segment has caused the allowed window to become 100% full", HFILL }},
+                       "This segment has caused the allowed window to become 100% full", HFILL }},
 
                { &hf_tcp_analysis_keep_alive,
                { "Keep Alive",         "tcp.analysis.keep_alive", FT_NONE, BASE_NONE, NULL, 0x0,
@@ -3200,14 +2709,14 @@ proto_register_tcp(void)
                { "This is a continuation to the PDU in frame",         "tcp.continuation_to", FT_FRAMENUM, BASE_NONE, NULL, 0x0,
                        "This is a continuation to the PDU in frame #", HFILL }},
 
-               { &hf_tcp_analysis_zero_window_violation,
-               { "Zero Window Violation",              "tcp.analysis.zero_window_violation", FT_NONE, BASE_NONE, NULL, 0x0,
-                       "This is a zero-window violation, an attempt to write >1 byte to a zero-window", HFILL }},
-
                { &hf_tcp_analysis_zero_window_probe,
                { "Zero Window Probe",          "tcp.analysis.zero_window_probe", FT_NONE, BASE_NONE, NULL, 0x0,
                        "This is a zero-window-probe", HFILL }},
 
+               { &hf_tcp_analysis_zero_window_probe_ack,
+               { "Zero Window Probe Ack",              "tcp.analysis.zero_window_probe_ack", FT_NONE, BASE_NONE, NULL, 0x0,
+                       "This is an ACK to a zero-window-probe", HFILL }},
+
                { &hf_tcp_analysis_zero_window,
                { "Zero Window",                "tcp.analysis.zero_window", FT_NONE, BASE_NONE, NULL, 0x0,
                        "This is a zero-window", HFILL }},
@@ -3224,6 +2733,14 @@ proto_register_tcp(void)
                  { "The RTT to ACK the segment was",            "tcp.analysis.ack_rtt", FT_RELATIVE_TIME, BASE_NONE, NULL, 0x0,
                    "How long time it took to ACK the segment (RTT)", HFILL}},
 
+               { &hf_tcp_analysis_rto,
+                 { "The RTO for this segment was",            "tcp.analysis.rto", FT_RELATIVE_TIME, BASE_NONE, NULL, 0x0,
+                   "How long transmission was delayed before this segment was retransmitted (RTO)", HFILL}},
+
+               { &hf_tcp_analysis_rto_frame,
+                 { "RTO based on delta from frame", "tcp.analysis.rto_frame", FT_FRAMENUM, BASE_NONE, NULL, 0x0,
+                       "This is the frame we measure the RTO from", HFILL }},
+
                { &hf_tcp_urgent_pointer,
                { "Urgent pointer",             "tcp.urgent_pointer", FT_UINT16, BASE_DEC, NULL, 0x0,
                        "", HFILL }},
@@ -3253,15 +2770,19 @@ proto_register_tcp(void)
                        "TCP Segment", HFILL }},
 
                { &hf_tcp_segments,
-               { "TCP Segments", "tcp.segments", FT_NONE, BASE_NONE, NULL, 0x0,
+               { "Reassembled TCP Segments", "tcp.segments", FT_NONE, BASE_NONE, NULL, 0x0,
                        "TCP Segments", HFILL }},
 
                { &hf_tcp_reassembled_in,
                { "Reassembled PDU in frame", "tcp.reassembled_in", FT_FRAMENUM, BASE_NONE, NULL, 0x0,
                        "The PDU that doesn't end in this segment is reassembled in this frame", HFILL }},
 
+               { &hf_tcp_options,
+                 { "TCP Options", "tcp.options", FT_BYTES,
+                   BASE_HEX, NULL, 0x0, "TCP Options", HFILL }},
+
                { &hf_tcp_option_mss,
-                 { "TCP MSS Option", "tcp.options.mss", FT_BOOLEAN, 
+                 { "TCP MSS Option", "tcp.options.mss", FT_BOOLEAN,
                    BASE_NONE, NULL, 0x0, "TCP MSS Option", HFILL }},
 
                { &hf_tcp_option_mss_val,
@@ -3269,8 +2790,8 @@ proto_register_tcp(void)
                    BASE_DEC, NULL, 0x0, "TCP MSS Option Value", HFILL}},
 
                { &hf_tcp_option_wscale,
-                 { "TCP Window Scale Option", "tcp.options.wscale", 
-                   FT_BOOLEAN, 
+                 { "TCP Window Scale Option", "tcp.options.wscale",
+                   FT_BOOLEAN,
                    BASE_NONE, NULL, 0x0, "TCP Window Option", HFILL}},
 
                { &hf_tcp_option_wscale_val,
@@ -3278,13 +2799,13 @@ proto_register_tcp(void)
                    FT_UINT8, BASE_DEC, NULL, 0x0, "TCP Window Scale Value",
                    HFILL}},
 
-               { &hf_tcp_option_sack_perm, 
-                 { "TCP Sack Perm Option", "tcp.options.sack_perm", 
+               { &hf_tcp_option_sack_perm,
+                 { "TCP Sack Perm Option", "tcp.options.sack_perm",
                    FT_BOOLEAN,
                    BASE_NONE, NULL, 0x0, "TCP Sack Perm Option", HFILL}},
 
                { &hf_tcp_option_sack,
-                 { "TCP Sack Option", "tcp.options.sack", FT_BOOLEAN, 
+                 { "TCP Sack Option", "tcp.options.sack", FT_BOOLEAN,
                    BASE_NONE, NULL, 0x0, "TCP Sack Option", HFILL}},
 
                { &hf_tcp_option_sack_sle,
@@ -3296,16 +2817,16 @@ proto_register_tcp(void)
                   BASE_DEC, NULL, 0x0, "TCP Sack Right Edge", HFILL}},
 
                { &hf_tcp_option_echo,
-                 { "TCP Echo Option", "tcp.options.echo", FT_BOOLEAN, 
+                 { "TCP Echo Option", "tcp.options.echo", FT_BOOLEAN,
                    BASE_NONE, NULL, 0x0, "TCP Sack Echo", HFILL}},
 
                { &hf_tcp_option_echo_reply,
-                 { "TCP Echo Reply Option", "tcp.options.echo_reply", 
+                 { "TCP Echo Reply Option", "tcp.options.echo_reply",
                    FT_BOOLEAN,
                    BASE_NONE, NULL, 0x0, "TCP Echo Reply Option", HFILL}},
 
                { &hf_tcp_option_time_stamp,
-                 { "TCP Time Stamp Option", "tcp.options.time_stamp", 
+                 { "TCP Time Stamp Option", "tcp.options.time_stamp",
                    FT_BOOLEAN,
                    BASE_NONE, NULL, 0x0, "TCP Time Stamp Option", HFILL}},
 
@@ -3314,7 +2835,7 @@ proto_register_tcp(void)
                    NULL, 0x0, "TCP CC Option", HFILL}},
 
                { &hf_tcp_option_ccnew,
-                 { "TCP CC New Option", "tcp.options.ccnew", FT_BOOLEAN, 
+                 { "TCP CC New Option", "tcp.options.ccnew", FT_BOOLEAN,
                    BASE_NONE, NULL, 0x0, "TCP CC New Option", HFILL}},
 
                { &hf_tcp_option_ccecho,
@@ -3328,6 +2849,10 @@ proto_register_tcp(void)
                { &hf_tcp_pdu_time,
                  { "Time until the last segment of this PDU", "tcp.pdu.time", FT_RELATIVE_TIME, BASE_NONE, NULL, 0x0,
                    "How long time has passed until the last frame of this PDU", HFILL}},
+               { &hf_tcp_pdu_size,
+                 { "PDU Size", "tcp.pdu.size", FT_UINT32, BASE_DEC, NULL, 0x0,
+                   "The size of this PDU", HFILL}},
+
                { &hf_tcp_pdu_last_frame,
                  { "Last frame of this PDU", "tcp.pdu.last_frame", FT_FRAMENUM, BASE_NONE, NULL, 0x0,
                        "This is the last frame of the PDU starting in this segment", HFILL }},
@@ -3362,8 +2887,8 @@ proto_register_tcp(void)
            "Whether the TCP summary line should be shown in the protocol tree",
            &tcp_summary_in_tree);
        prefs_register_bool_preference(tcp_module, "check_checksum",
-           "Check the validity of the TCP checksum when possible",
-           "Whether to check the validity of the TCP checksum",
+           "Validate the TCP checksum if possible",
+           "Whether to validate the TCP checksum",
            &tcp_check_checksum);
        prefs_register_bool_preference(tcp_module, "desegment_tcp_streams",
            "Allow subdissector to reassemble TCP streams",
@@ -3384,8 +2909,6 @@ proto_register_tcp(void)
            "Try to decode a packet using an heuristic sub-dissector before using a sub-dissector registered to a specific port",
            &try_heuristic_first);
 
-       register_init_routine(tcp_analyze_seq_init);
-       register_init_routine(tcp_desegment_init);
        register_init_routine(tcp_fragment_init);
 }