#if 0 out the stuff to set the reported length, as it'd throw an
[obnox/wireshark/wip.git] / packet-tcp.c
index 79f2993aa4e252fd4a790cbf1b22f4f0850bc2a4..da7717cf0ef49280d7794d7eb2fb33c19bdaa24a 100644 (file)
@@ -1,7 +1,7 @@
 /* packet-tcp.c
  * Routines for TCP packet disassembly
  *
- * $Id: packet-tcp.c,v 1.203 2003/08/29 11:15:13 sahlberg Exp $
+ * $Id: packet-tcp.c,v 1.211 2003/10/28 08:50:39 sahlberg Exp $
  *
  * Ethereal - Network traffic analyzer
  * By Gerald Combs <gerald@ethereal.com>
@@ -91,9 +91,12 @@ 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_retransmission = -1;
+static int hf_tcp_analysis_fast_retransmission = -1;
+static int hf_tcp_analysis_out_of_order = -1;
 static int hf_tcp_analysis_lost_packet = -1;
 static int hf_tcp_analysis_ack_lost_packet = -1;
 static int hf_tcp_analysis_keep_alive = -1;
+static int hf_tcp_analysis_keep_alive_ack = -1;
 static int hf_tcp_analysis_duplicate_ack = -1;
 static int hf_tcp_analysis_duplicate_ack_num = -1;
 static int hf_tcp_analysis_duplicate_ack_frame = -1;
@@ -174,13 +177,10 @@ struct tcp_unacked {
        guint32 nextseq;
        nstime_t ts;
 
-       /* these are used for detection of duplicate acks and nothing else */
-       guint32 ack_frame;
-       guint32 ack;
-       guint32 num_acks;
-
        /* 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) */
@@ -192,18 +192,21 @@ struct tcp_unacked {
 
 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           0x01
-#define TCP_A_LOST_PACKET              0x02
-#define TCP_A_ACK_LOST_PACKET          0x04
-#define TCP_A_KEEP_ALIVE               0x08
-#define TCP_A_DUPLICATE_ACK            0x10
-#define TCP_A_ZERO_WINDOW              0x20
-#define TCP_A_ZERO_WINDOW_PROBE                0x40
-#define TCP_A_ZERO_WINDOW_VIOLATION    0x80
+#define TCP_A_RETRANSMISSION           0x0001
+#define TCP_A_LOST_PACKET              0x0002
+#define TCP_A_ACK_LOST_PACKET          0x0004
+#define TCP_A_KEEP_ALIVE               0x0008
+#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_KEEP_ALIVE_ACK           0x0100
+#define TCP_A_OUT_OF_ORDER             0x0200
+#define TCP_A_FAST_RETRANSMISSION      0x0400
 struct tcp_acked {
        guint32 frame_acked;
        nstime_t ts;
-       guint8 flags;
+       guint16 flags;
        guint32 dupack_num;     /* dup ack number */
        guint32 dupack_frame;   /* dup ack to frame # */
 };
@@ -214,6 +217,7 @@ 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;
 
@@ -237,6 +241,12 @@ struct tcp_analysis {
        guint32 base_seq1;
        struct tcp_unacked *ual2;       /* UnAcked List 2*/
        guint32 base_seq2;
+       gint16 win_scale1;
+       gint16 win_scale2;
+       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.
@@ -274,8 +284,20 @@ get_tcp_conversation_data(packet_info *pinfo)
                tcpd=g_mem_chunk_alloc(tcp_analysis_chunk);
                tcpd->ual1=NULL;
                tcpd->base_seq1=0;
+               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->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;
@@ -430,8 +452,32 @@ pdu_store_sequencenumber_of_next_pdu(packet_info *pinfo, guint32 nxtpdu)
        */
 }
 
+/* if we saw a window scaling option, store it for future reference 
+*/
+static void
+pdu_store_window_scale_option(packet_info *pinfo, guint8 ws)
+{
+       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;
+       }
+}
+
 static void
-tcp_get_relative_seq_ack(guint32 frame, guint32 *seq, guint32 *ack)
+tcp_get_relative_seq_ack(guint32 frame, guint32 *seq, guint32 *ack, guint32 *win)
 {
        struct tcp_rel_seq *trs;
 
@@ -442,6 +488,9 @@ tcp_get_relative_seq_ack(guint32 frame, guint32 *seq, guint32 *ack)
 
        (*seq) -= trs->seq_base;
        (*ack) -= trs->ack_base;
+       if(trs->win_scale!=-1){
+               (*win)<<=trs->win_scale;
+       }
 }
 
 static struct tcp_acked *
@@ -471,8 +520,13 @@ tcp_analyze_sequence_number(packet_info *pinfo, guint32 seq, guint32 ack, guint3
        struct tcp_unacked *ual1=NULL;
        struct tcp_unacked *ual2=NULL;
        struct tcp_unacked *ual=NULL;
-       guint32 base_seq=0;
-       guint32 base_ack=0;
+       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;
+       gint16  win_scale;
        struct tcp_next_pdu **tnp=NULL;
 
        /* find(or create if needed) the conversation for this tcp session */
@@ -487,26 +541,70 @@ tcp_analyze_sequence_number(packet_info *pinfo, guint32 seq, guint32 ack, guint3
        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=tcpd->base_seq1;
-               base_ack=tcpd->base_seq2;
+               base_seq=(tcp_relative_seq && (ual1==NULL))?seq:tcpd->base_seq1;
+               base_ack=(tcp_relative_seq && (ual2==NULL))?ack:tcpd->base_seq2;
+               win_scale=tcpd->win_scale1;
        } 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=tcpd->base_seq2;
-               base_ack=tcpd->base_seq1;
+               base_seq=(tcp_relative_seq && (ual1==NULL))?seq:tcpd->base_seq2;
+               base_ack=(tcp_relative_seq && (ual2==NULL))?ack:tcpd->base_seq1;
+               win_scale=tcpd->win_scale2;
        }
 
-       if(tcp_relative_seq){
-               if(base_seq==0){
-                       base_seq=seq;
-               }
-               if(base_ack==0){
-                       base_ack=ack;
+       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 
+ */
+{
+struct tcp_unacked *u=NULL;
+printf("\n");
+printf("analyze_sequence_number(frame:%d seq:%d nextseq:%d ack:%d)\n",pinfo->fd->num,seq,seq+seglen,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);
+}
+}
+#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. */
@@ -525,14 +623,18 @@ tcp_analyze_sequence_number(packet_info *pinfo, guint32 seq, guint32 ack, guint3
                ual1=g_mem_chunk_alloc(tcp_unacked_chunk);
                ual1->next=NULL;
                ual1->frame=pinfo->fd->num;
-               ual1->ack_frame=0;
-               ual1->ack=0;
-               ual1->num_acks=0;
+               ack1_frame=0;
+               ack2_frame=0;
+               ack1=0;
+               ack2=0;
+               num1_acks=0;
+               num2_acks=0;
                ual1->seq=seq+1;
                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;
                        base_ack=ack;
@@ -545,14 +647,12 @@ tcp_analyze_sequence_number(packet_info *pinfo, guint32 seq, guint32 ack, guint3
                ual1=g_mem_chunk_alloc(tcp_unacked_chunk);
                ual1->next=NULL;
                ual1->frame=pinfo->fd->num;
-               ual1->ack_frame=0;
-               ual1->ack=0;
-               ual1->num_acks=0;
                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;
@@ -574,14 +674,12 @@ tcp_analyze_sequence_number(packet_info *pinfo, guint32 seq, guint32 ack, guint3
                ual=g_mem_chunk_alloc(tcp_unacked_chunk);
                ual->next=ual1;
                ual->frame=pinfo->fd->num;
-               ual->ack_frame=0;
-               ual->ack=0;
-               ual->num_acks=0;
                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;
        }
@@ -609,6 +707,7 @@ tcp_analyze_sequence_number(packet_info *pinfo, guint32 seq, guint32 ack, guint3
        
                        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;
                }
        }
@@ -619,21 +718,109 @@ tcp_analyze_sequence_number(packet_info *pinfo, guint32 seq, guint32 ack, guint3
                goto seq_finished;
        }
 
-       /* check if the sequence number is lower than expected, i.e. retransmission */
+       /* check if the sequence number is lower than expected, i.e. either a 
+        * retransmission a fast retransmission or an out of order segment
+        */
        if( LT_SEQ(seq, ual1->nextseq )){
-               struct tcp_acked *ta;
+               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;
 
-               ta=tcp_analyze_get_acked_struct(pinfo->fd->num, TRUE);
-               ta->flags|=TCP_A_RETRANSMISSION;
+                       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;
+                       }
+               }
 
-               /* did this segment contain any more data we havent seen yet?
-                * if so we can just increase nextseq
+               /* 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.
                 */
-               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;
+               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;
+                               }
+                       }
+               }
+
+               
+               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;
+
+                       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;
+                       }
                }
                goto seq_finished;
        }
@@ -642,14 +829,12 @@ tcp_analyze_sequence_number(packet_info *pinfo, guint32 seq, guint32 ack, guint3
        ual=g_mem_chunk_alloc(tcp_unacked_chunk);
        ual->next=ual1;
        ual->frame=pinfo->fd->num;
-       ual->ack_frame=0;
-       ual->ack=0;
-       ual->num_acks=0;
        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:
@@ -760,14 +945,12 @@ ack_finished:
                ual2=g_mem_chunk_alloc(tcp_unacked_chunk);
                ual2->next=NULL;
                ual2->frame=0;
-               ual2->ack_frame=0;
-               ual2->ack=0;
-               ual2->num_acks=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
@@ -786,28 +969,38 @@ ack_finished:
                if((!seglen)&&LE_SEQ(ack,ual->seq)){
                        /* if this is the first ack to keep track of, it is not
                           a duplicate */
-                       if(ual->num_acks==0){
-                               ual->ack=ack;
-                               ual->ack_frame=pinfo->fd->num;
-                               ual->num_acks=1;
+                       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(ual->ack!=ack){
-                               ual->ack=ack;
-                               ual->ack_frame=pinfo->fd->num;
-                               ual->num_acks=1;
+                       } else if(ack2!=ack){
+                               ack2=ack;
+                               ack2_frame=pinfo->fd->num;
+                               num2_acks=1;
                        /* this has to be a duplicate ack */
                        } else {
-                               ual->num_acks++;
+                               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 */
-                       if(ual->num_acks>1){
                                struct tcp_acked *ta;
                                ta=tcp_analyze_get_acked_struct(pinfo->fd->num, TRUE);
-                               ta->flags|=TCP_A_DUPLICATE_ACK;
-                               ta->dupack_num=ual->num_acks-1;
-                               ta->dupack_frame=ual->ack_frame;
+                               /* keepalives are not dupacks */
+                               if( (!(ta->flags&TCP_A_KEEP_ALIVE)) ){
+                                       ta->flags|=TCP_A_DUPLICATE_ACK;
+                                       ta->dupack_num=num2_acks-1;
+                                       ta->dupack_frame=ack2_frame;
+                               }
                        }
                }               
 
@@ -863,11 +1056,25 @@ ack_finished:
                 */
                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;
        } 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;
        }
 
 
@@ -877,6 +1084,7 @@ ack_finished:
                trs=g_mem_chunk_alloc(tcp_rel_seq_chunk);
                trs->seq_base=base_seq;
                trs->ack_base=base_ack;
+               trs->win_scale=win_scale;
                g_hash_table_insert(tcp_rel_seq_table, (void *)pinfo->fd->num, trs);
        }
 }
@@ -919,6 +1127,19 @@ tcp_print_sequence_number_analysis(packet_info *pinfo, tvbuff_t *tvb, proto_tree
                                col_prepend_fstr(pinfo->cinfo, COL_INFO, "[TCP Retransmission] ");
                        }
                }
+               if( ta->flags&TCP_A_FAST_RETRANSMISSION ){
+                       proto_tree_add_none_format(flags_tree, hf_tcp_analysis_fast_retransmission, tvb, 0, 0, "This frame is a (suspected) fast retransmission");
+                       proto_tree_add_none_format(flags_tree, hf_tcp_analysis_retransmission, tvb, 0, 0, "This frame is a (suspected) retransmission");
+                       if(check_col(pinfo->cinfo, COL_INFO)){
+                               col_prepend_fstr(pinfo->cinfo, COL_INFO, "[TCP Fast Retransmission] ");
+                       }
+               }
+               if( ta->flags&TCP_A_OUT_OF_ORDER ){
+                       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");
+                       if(check_col(pinfo->cinfo, COL_INFO)){
+                               col_prepend_fstr(pinfo->cinfo, COL_INFO, "[TCP Out-Of-Order] ");
+                       }
+               }
                if( ta->flags&TCP_A_LOST_PACKET ){
                        proto_tree_add_none_format(flags_tree, hf_tcp_analysis_lost_packet, tvb, 0, 0, "A segment before this frame was lost");
                        if(check_col(pinfo->cinfo, COL_INFO)){
@@ -937,6 +1158,12 @@ tcp_print_sequence_number_analysis(packet_info *pinfo, tvbuff_t *tvb, proto_tree
                                col_prepend_fstr(pinfo->cinfo, COL_INFO, "[TCP Keep-Alive] ");
                        }
                }
+               if( ta->flags&TCP_A_KEEP_ALIVE_ACK ){
+                       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");
+                       if(check_col(pinfo->cinfo, COL_INFO)){
+                               col_prepend_fstr(pinfo->cinfo, COL_INFO, "[TCP Keep-Alive ACK] ");
+                       }
+               }
                if( ta->dupack_num){
                        if( ta->flags&TCP_A_DUPLICATE_ACK ){
                                proto_tree_add_none_format(flags_tree, hf_tcp_analysis_duplicate_ack, tvb, 0, 0, "This is a TCP duplicate ack");
@@ -1803,6 +2030,9 @@ dissect_tcpopt_wscale(const ip_tcp_opt *optp, tvbuff_t *tvb,
                             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);
+  }
 }
 
 static void
@@ -2207,7 +2437,7 @@ dissect_tcp(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree)
               tcp_analyze_sequence_number(pinfo, tcph->th_seq, tcph->th_ack, tcph->th_seglen, tcph->th_flags, tcph->th_win);
           }
           if(tcp_relative_seq){
-              tcp_get_relative_seq_ack(pinfo->fd->num, &(tcph->th_seq), &(tcph->th_ack));
+              tcp_get_relative_seq_ack(pinfo->fd->num, &(tcph->th_seq), &(tcph->th_ack), &(tcph->th_win));
           }
       }
 
@@ -2559,8 +2789,9 @@ proto_register_tcp(void)
                { "Fin",                        "tcp.flags.fin", FT_BOOLEAN, 8, TFS(&flags_set_truth), TH_FIN,
                        "", HFILL }},
 
+               /* 32 bits so we can present some values adjusted to window scaling */
                { &hf_tcp_window_size,
-               { "Window size",                "tcp.window_size", FT_UINT16, BASE_DEC, NULL, 0x0,
+               { "Window size",                "tcp.window_size", FT_UINT32, BASE_DEC, NULL, 0x0,
                        "", HFILL }},
 
                { &hf_tcp_checksum,
@@ -2579,6 +2810,14 @@ proto_register_tcp(void)
                { "Retransmission",             "tcp.analysis.retransmission", FT_NONE, BASE_NONE, NULL, 0x0,
                        "This frame is a suspected TCP retransmission", HFILL }},
 
+               { &hf_tcp_analysis_fast_retransmission,
+               { "Fast Retransmission",                "tcp.analysis.fast_retransmission", FT_NONE, BASE_NONE, NULL, 0x0,
+                       "This frame is a suspected TCP fast retransmission", HFILL }},
+
+               { &hf_tcp_analysis_out_of_order,
+               { "Out Of Order",               "tcp.analysis.out_of_order", FT_NONE, BASE_NONE, NULL, 0x0,
+                       "This frame is a suspected Out-Of-Order segment", HFILL }},
+
                { &hf_tcp_analysis_lost_packet,
                { "Previous Segment Lost",              "tcp.analysis.lost_segment", FT_NONE, BASE_NONE, NULL, 0x0,
                        "A segment before this one was lost from the capture", HFILL }},
@@ -2591,6 +2830,10 @@ proto_register_tcp(void)
                { "Keep Alive",         "tcp.analysis.keep_alive", FT_NONE, BASE_NONE, NULL, 0x0,
                        "This is a keep-alive segment", HFILL }},
 
+               { &hf_tcp_analysis_keep_alive_ack,
+               { "Keep Alive ACK",             "tcp.analysis.keep_alive_ack", FT_NONE, BASE_NONE, NULL, 0x0,
+                       "This is an ACK to a keep-alive segment", HFILL }},
+
                { &hf_tcp_analysis_duplicate_ack,
                { "Duplicate ACK",              "tcp.analysis.duplicate_ack", FT_NONE, BASE_NONE, NULL, 0x0,
                        "This is a duplicate ACK", HFILL }},
@@ -2769,8 +3012,10 @@ proto_register_tcp(void)
            "Make the TCP dissector analyze TCP sequence numbers to find and flag segment retransmissions, missing segments and RTT",
            &tcp_analyze_seq);
        prefs_register_bool_preference(tcp_module, "relative_sequence_numbers",
-           "Use relative sequence numbers",
-           "Make the TCP dissector use relative sequence numbers instead of absolute ones. To use this option you must also enable \"Analyze TCP sequence numbers\".",
+           "Relative sequence numbers and window scaling",
+           "Make the TCP dissector use relative sequence numbers instead of absolute ones. "
+           "To use this option you must also enable \"Analyze TCP sequence numbers\". "
+           "This option will also try to track and adjust the window field according to any TCP window scaling options seen.",
            &tcp_relative_seq);
        prefs_register_bool_preference(tcp_module, "try_heuristic_first",
            "Try heuristic sub-dissectors first",