From Pierre Juhen:
[obnox/wireshark/wip.git] / epan / dissectors / packet-tcp.c
index 7032e854e3998d52086b9fec4dd533ca4b60bbde..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
@@ -112,6 +112,7 @@ static int hf_tcp_analysis_zero_window_probe = -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;
@@ -121,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;
@@ -209,11 +211,6 @@ static SLAB_FREE_LIST_DEFINE(tcp_unacked_t)
 #define TCP_A_WINDOW_FULL              0x1000
 
 
-static GHashTable *tcp_pdu_tracking_table = NULL;
-static GHashTable *tcp_pdu_skipping_table = NULL;
-
-static se_tree_t *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,
@@ -251,7 +248,8 @@ get_tcp_conversation_data(packet_info *pinfo)
                tcpd->flow1.nextseqframe=0;
                tcpd->flow1.window=0;
                tcpd->flow1.win_scale=-1;
-               tcpd->flow1.pdu_seq=NULL;
+               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;
@@ -264,8 +262,9 @@ get_tcp_conversation_data(packet_info *pinfo)
                tcpd->flow2.nextseqframe=0;
                tcpd->flow2.window=0;
                tcpd->flow2.win_scale=-1;
-               tcpd->flow2.pdu_seq=NULL;
-               tcpd->acked_table=se_tree_create_non_persistent(SE_TREE_TYPE_RED_BLACK, "tcp_analyze_acked_table");
+               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);
@@ -290,73 +289,16 @@ get_tcp_conversation_data(packet_info *pinfo)
        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;
-       }
-
-       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;
-                               }
-                       }
-               }
-       }
-}
-
-
-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_fence_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);
 }
 
@@ -367,91 +309,75 @@ print_pdu_tracking_data(packet_info *pinfo, tvbuff_t *tvb, proto_tree *tcp_tree,
 static int
 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_next_pdu *tnp=NULL;
+        struct tcp_multisegment_pdu *msp=NULL;
 
        if(!pinfo->fd->flags.visited){
-               /* find(or create if needed) the conversation for this tcp session */
-               tnp=tcpd->fwd->pdu_seq;
-
-               /* 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=pinfo->fd->abs_ts;
-                               g_hash_table_insert(tcp_pdu_skipping_table,
-                                       GINT_TO_POINTER(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,
-                                       GINT_TO_POINTER(pinfo->fd->num), GUINT_TO_POINTER(tnp->nxtpdu));
-                               offset+=tnp->nxtpdu-seq;
-                               break;
+                       if(seq<msp->nxtpdu && nxtseq>msp->nxtpdu){
+                               offset+=msp->nxtpdu-seq;
+                               return offset;
                        }
+
                }
        } else {
-               guint32 pduseq;
+               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;
 
-               tnp=(struct tcp_next_pdu *)se_tree_lookup32(tcp_pdu_time_table, 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);
+                               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, &tnp->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);
-               }
+                               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, GINT_TO_POINTER(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=GPOINTER_TO_UINT(g_hash_table_lookup(tcp_pdu_tracking_table, GINT_TO_POINTER(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
+static struct tcp_multisegment_pdu *
 pdu_store_sequencenumber_of_next_pdu(packet_info *pinfo, guint32 seq, guint32 nxtpdu, struct tcp_analysis *tcpd)
 {
-       struct tcp_next_pdu *tnp=NULL;
-
-       tnp=se_alloc(sizeof(struct tcp_next_pdu));
-       tnp->nxtpdu=nxtpdu;
-       tnp->seq=seq;
-       tnp->first_frame=pinfo->fd->num;
-       tnp->last_frame=pinfo->fd->num;
-       tnp->last_frame_time=pinfo->fd->abs_ts;
-
-       tnp->next=tcpd->fwd->pdu_seq;
-       tcpd->fwd->pdu_seq=tnp;
-       /*QQQ
-         Add check for ACKs and purge list of sequence numbers
-         already acked.
-       */
-       se_tree_insert32(tcp_pdu_time_table, 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
@@ -710,7 +636,7 @@ printf("REV list lastflags:0x%04x base_seq:0x%08x:\n",tcpd->rev->lastsegmentflag
 
 
 finished_fwd:
-       /* If this was NOT a dupack we must reset the dupack countres */
+       /* 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;
@@ -726,7 +652,8 @@ finished_fwd:
         * in the other direction.
         */
        if( tcpd->rev->nextseq
-       &&  GT_SEQ(ack, 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);
@@ -756,7 +683,7 @@ finished_fwd:
                        goto finished_checking_retransmission_type;
                }
 
-               /* If there were >=1 duplicate ACKs in the reverse direction
+               /* 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
@@ -765,7 +692,7 @@ finished_fwd:
                 */
                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>=1
+               if( tcpd->rev->dupacknum>=2
                &&  tcpd->rev->lastack==seq
                &&  t<20000000 ){
                        if(!tcpd->ta){
@@ -846,14 +773,17 @@ finished_checking_retransmission_type:
        }
 
 
-       prune_next_pdu_list(&(tcpd->rev->pdu_seq), ack-tcpd->rev->base_seq);
-
        /* 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;
                }
@@ -883,11 +813,6 @@ finished_checking_retransmission_type:
                ual=ual->next;
        }
 
-#ifdef REMOVED
-               tcp_analyze_get_acked_struct(pinfo->fd->num, TRUE, tcpd);
-               tcpd->ta->frame_acked=tcpd->rev->segments->frame;
-               nstime_delta(&tcpd->ta->ts, &pinfo->fd->abs_ts, &tcpd->rev->segments->ts);
-#endif
 }
 
 static void
@@ -1026,8 +951,8 @@ 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) to ACK in packet #%u",
-                               ta->dupack_num, ta->dupack_frame);
+                       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");
@@ -1058,58 +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 = GPOINTER_TO_UINT(k);
-
-       return frame;
-}
-static gint
-tcp_acked_equal(gconstpointer k1, gconstpointer k2)
-{
-       guint32 frame1 = GPOINTER_TO_UINT(k1);
-       guint32 frame2 = GPOINTER_TO_UINT(k2);
-
-       return frame1==frame2;
-}
-
-static void
-tcp_analyze_seq_init(void)
-{
-       /* first destroy the tables */
-       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_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;
-       }
-
-       if(tcp_analyze_seq){
-               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);
-       }
-
-}
-
 /* **************************************************************************
  * End of tcp sequence number analysis
  * **************************************************************************/
@@ -1169,109 +1042,6 @@ tcp_fragment_init(void)
 /* Enable desegmenting of TCP streams */
 static gboolean tcp_desegment = TRUE;
 
-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 void
 desegment_tcp(tvbuff_t *tvb, packet_info *pinfo, int offset,
                guint32 seq, guint32 nxtseq,
@@ -1280,17 +1050,24 @@ desegment_tcp(tvbuff_t *tvb, packet_info *pinfo, int offset,
                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 *frag_tree_item;
-    proto_item *tcp_tree_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.
@@ -1308,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
@@ -1384,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
@@ -1417,7 +1177,7 @@ 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;
@@ -1435,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"
@@ -1448,26 +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);
-                                       if (new_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
@@ -1555,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
@@ -1569,49 +1317,18 @@ 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) {
@@ -1665,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;
+       }
 }
 
 /*
@@ -1697,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) {
     /*
@@ -1746,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
@@ -1815,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;
 
@@ -1955,7 +1697,7 @@ dissect_tcpopt_timestamp(const ip_tcp_opt *optp, tvbuff_t *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);
 }
@@ -2041,7 +1783,7 @@ static const ip_tcp_opt tcpopts[] = {
   },
   {
     TCPOPT_TIMESTAMP,
-    "Time stamp",
+    "Timestamps",
     NULL,
     FIXED_LENGTH,
     TCPOLEN_TIMESTAMP,
@@ -2100,9 +1842,11 @@ decode_tcp_ports(tvbuff_t *tvb, int offset, packet_info *pinfo,
 {
   tvbuff_t *next_tvb;
   int low_port, high_port;
+  int save_desegment_offset;
+  guint32 save_desegment_len;
 
   /* dont call subdissectors for keepalive or zerowindowprobes
-   * eventhough tehy do contain payload "data"
+   * even though they do contain payload "data"
    * keeaplives just contain garbage and zwp contain too little data (1 byte)
    * so why bother.
    */
@@ -2125,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.
@@ -2166,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. */
@@ -2307,16 +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);
 
@@ -2344,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,
@@ -2389,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. */
@@ -2502,7 +2268,7 @@ 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);
@@ -2512,27 +2278,29 @@ dissect_tcp(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree)
     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)){
+    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): %s -> %s",
-                             get_tcp_port(tcph->th_sport), get_tcp_port(tcph->th_dport));
+      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): %s -> %s",
-                             get_tcp_port(tcph->th_sport), get_tcp_port(tcph->th_dport));
+      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)
-    expert_add_info_format(pinfo, tf_fin, PI_SEQUENCE, PI_CHAT, "Connection finish (FIN): %s -> %s",
-                           get_tcp_port(tcph->th_sport), get_tcp_port(tcph->th_dport));
+    /* 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)
-    expert_add_info_format(pinfo, tf_rst, PI_SEQUENCE, PI_CHAT, "Connection reset (RST): %s -> %s",
-                           get_tcp_port(tcph->th_sport), get_tcp_port(tcph->th_dport));
+    /* 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. */
@@ -2604,7 +2372,7 @@ dissect_tcp(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree)
 
         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,
@@ -2675,8 +2443,9 @@ dissect_tcp(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree)
     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;
@@ -2722,6 +2491,52 @@ dissect_tcp(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree)
   }
   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
@@ -2832,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,
@@ -2962,6 +2777,10 @@ proto_register_tcp(void)
                { "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,
                    BASE_NONE, NULL, 0x0, "TCP MSS Option", HFILL }},
@@ -3030,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 }},
@@ -3086,9 +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);
 
-       tcp_pdu_time_table=se_tree_create(SE_TREE_TYPE_RED_BLACK, "tcp_pdu_time_table");
-       register_init_routine(tcp_analyze_seq_init);
-       register_init_routine(tcp_desegment_init);
        register_init_routine(tcp_fragment_init);
 }