/* packet-tcp.c
* Routines for TCP packet disassembly
*
- * $Id: packet-tcp.c,v 1.173 2003/02/21 00:22:45 guy 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>
#include <glib.h>
#include "in_cksum.h"
+#include <epan/packet.h>
#include <epan/resolv.h>
#include "ipproto.h"
+#include "ip_opts.h"
#include "follow.h"
#include "prefs.h"
#include "packet-tcp.h"
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;
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_reassembled_in = -1;
static int hf_tcp_segments = -1;
static int hf_tcp_segment = -1;
static int hf_tcp_segment_overlap = -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_option_mss = -1;
+static int hf_tcp_option_mss_val = -1;
+static int hf_tcp_option_wscale = -1;
+static int hf_tcp_option_wscale_val = -1;
+static int hf_tcp_option_sack_perm = -1;
+static int hf_tcp_option_sack = -1;
+static int hf_tcp_option_sack_sle = -1;
+static int hf_tcp_option_sack_sre = -1;
+static int hf_tcp_option_echo = -1;
+static int hf_tcp_option_echo_reply = -1;
+static int hf_tcp_option_time_stamp = -1;
+static int hf_tcp_option_cc = -1;
+static int hf_tcp_option_ccnew = -1;
+static int hf_tcp_option_ccecho = -1;
+static int hf_tcp_option_md5 = -1;
static gint ett_tcp = -1;
static gint ett_tcp_flags = -1;
&hf_tcp_segment_multiple_tails,
&hf_tcp_segment_too_long_fragment,
&hf_tcp_segment_error,
+ &hf_tcp_reassembled_in,
"Segments"
};
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) */
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 # */
};
static GHashTable *tcp_analyze_acked_table = NULL;
struct tcp_rel_seq {
guint32 seq_base;
guint32 ack_base;
+ gint16 win_scale;
};
static GHashTable *tcp_rel_seq_table = NULL;
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.
+ */
+ 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;
};
+static GHashTable *tcp_pdu_tracking_table = NULL;
+
+
+static struct tcp_analysis *
+get_tcp_conversation_data(packet_info *pinfo)
+{
+ 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){
+ /* No this is a new conversation. */
+ conv=conversation_new(&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->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;
+
+ 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
-tcp_get_relative_seq_ack(guint32 frame, guint32 *seq, guint32 *ack)
+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->seq<=seq){
+ struct tcp_next_pdu *oldtnp;
+ oldtnp=tmptnp;
+
+ if(tmptnp==*tnp){
+ tmptnp=tmptnp->next;
+ *tnp=tmptnp;
+ g_mem_chunk_free(tcp_next_pdu_chunk, oldtnp);
+ if(!tmptnp){
+ return;
+ }
+ continue;
+ } else {
+ for(tmptnp=*tnp;tmptnp;tmptnp=tmptnp->next){
+ if(tmptnp->next==oldtnp){
+ tmptnp->next=oldtnp->next;
+ g_mem_chunk_free(tcp_next_pdu_chunk, oldtnp);
+ break;
+ }
+ }
+ if(!tmptnp){
+ return;
+ }
+ }
+ }
+ }
+}
+
+
+/* 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(packet_info *pinfo, int offset, guint32 seq, guint32 nxtseq)
+{
+ struct tcp_analysis *tcpd=NULL;
+ struct tcp_next_pdu *tnp=NULL;
+ int direction;
+
+ 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
+ */
+ if(seq<tnp->seq && nxtseq>tnp->seq){
+ g_hash_table_insert(tcp_pdu_tracking_table,
+ (void *)pinfo->fd->num, (void *)tnp->seq);
+ offset+=tnp->seq-seq;
+ break;
+ }
+ }
+ } else {
+ guint32 pduseq;
+
+ pduseq=(guint32)g_hash_table_lookup(tcp_pdu_tracking_table, (void *)pinfo->fd->num);
+ if(pduseq){
+ offset+=pduseq-seq;
+ }
+ }
+
+ 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 nxtpdu)
+{
+ 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->seq=nxtpdu;
+
+ /* 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.
+ */
+}
+
+/* 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, guint32 *win)
{
struct tcp_rel_seq *trs;
(*seq) -= trs->seq_base;
(*ack) -= trs->ack_base;
+ if(trs->win_scale!=-1){
+ (*win)<<=trs->win_scale;
+ }
}
static struct tcp_acked *
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;
static void
tcp_analyze_sequence_number(packet_info *pinfo, guint32 seq, guint32 ack, guint32 seglen, guint8 flags, guint16 window)
{
- conversation_t *conv=NULL;
struct tcp_analysis *tcpd=NULL;
int direction;
struct tcp_unacked *ual1=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;
+ gint16 win_scale;
+ struct tcp_next_pdu **tnp=NULL;
- /* Have we seen this conversation before? */
- if( (conv=find_conversation(&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);
- }
-
- /* 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->ual2=NULL;
- tcpd->base_seq2=0;
- conversation_add_proto_data(conv, proto_tcp, tcpd);
- }
+ /* 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(direction>=0){
ual1=tcpd->ual1;
ual2=tcpd->ual2;
- base_seq=tcpd->base_seq1;
- base_ack=tcpd->base_seq2;
+ 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_scale=tcpd->win_scale1;
} else {
ual1=tcpd->ual2;
ual2=tcpd->ual1;
- base_seq=tcpd->base_seq2;
- base_ack=tcpd->base_seq1;
+ 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_scale=tcpd->win_scale2;
}
- if(base_seq==0){
- base_seq=seq;
+ 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;
+ }
}
- if(base_ack==0){
- base_ack=ack;
+
+#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. */
+ if( flags&TH_FIN ){
+ seglen+=1;
}
/* handle the sequence numbers */
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;
- base_seq=seq;
- base_ack=ack;
+ ual1->flags=0;
+ if(tcp_relative_seq){
+ base_seq=seq;
+ base_ack=ack;
+ }
goto seq_finished;
}
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;
- base_seq=seq;
+ ual1->flags=0;
+ if(tcp_relative_seq){
+ base_seq=seq;
+ base_ack=ack;
+ }
goto seq_finished;
}
/* if we get past here we know that ual1 points to a segment */
- /* To handle FIN, just pretend they have a length of 1.
- else the ACK following the FIN-ACK will look like it was
- outside the window. */
- if( (!seglen) && (flags&TH_FIN) ){
- seglen=1;
- }
/* if seq is beyond ual1->nextseq we have lost a segment */
if (GT_SEQ(seq, ual1->nextseq)) {
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;
}
- /* keep-alives are empty semgents with a sequence number -1 of what
+ /* 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)
*/
- if( (!seglen) && EQ_SEQ(seq, (ual1->nextseq-1)) ){
- struct tcp_acked *ta;
-
- ta=tcp_analyze_get_acked_struct(pinfo->fd->num, TRUE);
- ta->flags|=TCP_A_KEEP_ALIVE;
- goto seq_finished;
+ 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;
+ }
}
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;
- /* did this segment contain any more data we havent seen yet?
- * if so we can just increase nextseq
+ ta=tcp_analyze_get_acked_struct(pinfo->fd->num, TRUE);
+ ta->flags|=TCP_A_FAST_RETRANSMISSION;
+ goto seq_finished;
+ }
+ }
+
+ /* 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
*/
- 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;
+ /* 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;
}
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:
ual=ual2->next;
g_mem_chunk_free(tcp_unacked_chunk, ual2);
}
+ prune_next_pdu_list(tnp, ack-base_ack);
goto ack_finished;
}
ual=ual2->next;
g_mem_chunk_free(tcp_unacked_chunk, ual2);
}
+ prune_next_pdu_list(tnp, ack-base_ack);
goto ack_finished;
}
tmpual=ual->next;
g_mem_chunk_free(tcp_unacked_chunk, ual);
}
-
+ prune_next_pdu_list(tnp, ack-base_ack);
}
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
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;
+ /* 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;
+ }
}
}
*/
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;
}
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);
}
}
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)){
col_prepend_fstr(pinfo->cinfo, COL_INFO, "[TCP Keep-Alive] ");
}
}
- 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");
+ 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 Duplicate ACK] ");
+ 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");
+ if(check_col(pinfo->cinfo, COL_INFO)){
+ col_prepend_fstr(pinfo->cinfo, COL_INFO, "[TCP Dup ACK %d#%d] ", ta->dupack_frame, ta->dupack_num);
+ }
}
+ proto_tree_add_uint(tree, hf_tcp_analysis_duplicate_ack_num,
+ tvb, 0, 0, ta->dupack_num);
+ proto_tree_add_uint(tree, hf_tcp_analysis_duplicate_ack_frame,
+ tvb, 0, 0, ta->dupack_frame);
}
if( ta->flags&TCP_A_ZERO_WINDOW_PROBE ){
proto_tree_add_none_format(flags_tree, hf_tcp_analysis_zero_window_probe, tvb, 0, 0, "This is a TCP zero-window-probe");
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;
+ }
/*
* 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;
tcp_acked_equal);
tcp_rel_seq_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_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),
Call the normal subdissector.
*/
decode_tcp_ports(tvb, offset, pinfo, tree,
- sport, dport);
+ sport, dport, 0);
called_dissector = TRUE;
/* Did the subdissector ask us to desegment some more data
/* call subdissector */
decode_tcp_ports(next_tvb, 0, pinfo, tree,
- sport, dport);
+ sport, dport, 0);
called_dissector = TRUE;
/*
}
if (!called_dissector || pinfo->desegment_len != 0) {
+ if (ipfd_head != NULL && ipfd_head->reassembled_in != 0) {
+ /*
+ * We know what frame this PDU is reassembled in;
+ * let the user know.
+ */
+ proto_tree_add_uint(tcp_tree, hf_tcp_reassembled_in,
+ tvb, 0, 0, ipfd_head->reassembled_in);
+ }
+
/*
* Either we didn't call the subdissector at all (i.e.,
* this is a segment that contains the middle of a
guint16 mss;
mss = tvb_get_ntohs(tvb, offset + 2);
- proto_tree_add_text(opt_tree, tvb, offset, optlen,
- "%s: %u bytes", optp->name, mss);
+ proto_tree_add_boolean_hidden(opt_tree, hf_tcp_option_mss, tvb, offset,
+ optlen, TRUE);
+ proto_tree_add_uint_format(opt_tree, hf_tcp_option_mss_val, tvb, offset,
+ optlen, mss, "%s: %u bytes", optp->name, mss);
tcp_info_append_uint(pinfo, "MSS", mss);
}
guint8 ws;
ws = tvb_get_guint8(tvb, offset + 2);
- proto_tree_add_text(opt_tree, tvb, offset, optlen,
- "%s: %u (multiply by %u)", optp->name, ws, 1 << ws);
+ 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)",
+ 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
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,
+ offset, optlen, TRUE);
}
if (optlen < 4) {
proto_tree_add_text(field_tree, tvb, offset, optlen,
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);
optlen -= 4;
if (optlen < 4) {
proto_tree_add_text(field_tree, tvb, offset, optlen,
/* XXX - check whether it goes past end of packet */
rightedge = tvb_get_ntohl(tvb, offset + 4);
optlen -= 4;
- proto_tree_add_text(field_tree, tvb, offset, 8,
- "left edge = %u, right edge = %u", leftedge, rightedge);
+ proto_tree_add_uint_format(field_tree, hf_tcp_option_sack_sre, tvb,
+ offset+4, 4, rightedge,
+ "right edge = %u", rightedge);
tcp_info_append_uint(pinfo, "SLE", leftedge);
tcp_info_append_uint(pinfo, "SRE", rightedge);
offset += 8;
guint32 echo;
echo = tvb_get_ntohl(tvb, offset + 2);
+ proto_tree_add_boolean_hidden(opt_tree, hf_tcp_option_echo, tvb, offset,
+ optlen, TRUE);
proto_tree_add_text(opt_tree, tvb, offset, optlen,
"%s: %u", optp->name, echo);
tcp_info_append_uint(pinfo, "ECHO", echo);
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,
+ offset, optlen, TRUE);
proto_tree_add_text(opt_tree, tvb, offset, optlen,
"%s: tsval %u, tsecr %u", optp->name, tsv, tser);
tcp_info_append_uint(pinfo, "TSV", tsv);
guint32 cc;
cc = tvb_get_ntohl(tvb, offset + 2);
+ proto_tree_add_boolean_hidden(opt_tree, hf_tcp_option_cc, tvb, offset,
+ optlen, TRUE);
proto_tree_add_text(opt_tree, tvb, offset, optlen,
"%s: %u", optp->name, cc);
tcp_info_append_uint(pinfo, "CC", cc);
/* separated into a stand alone routine to other protocol dissectors */
/* can call to it, ie. socks */
+static gboolean try_heuristic_first = FALSE;
+
void
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, guint32 nxtseq)
{
tvbuff_t *next_tvb;
int low_port, high_port;
+/*qqq see if it is an unaligned PDU */
+ if(nxtseq && tcp_analyze_seq && (!tcp_desegment)){
+ guint32 seq;
+ seq=nxtseq-tvb_reported_length_remaining(tvb, offset);
+ offset=scan_for_next_pdu(pinfo, offset, seq, nxtseq);
+ }
+
next_tvb = tvb_new_subset(tvb, offset, -1, -1);
/* determine if this packet is part of a conversation and call dissector */
if (try_conversation_dissector(&pinfo->src, &pinfo->dst, PT_TCP,
src_port, dst_port, next_tvb, pinfo, tree))
- return;
+ goto end_decode_tcp_ports;
+
+ if (try_heuristic_first) {
+ /* do lookup with the heuristic subdissector table */
+ if (dissector_try_heuristic(heur_subdissector_list, next_tvb, pinfo, tree))
+ goto end_decode_tcp_ports;
+ }
/* Do lookups with the subdissector table.
We try the port number with the lower value first, followed by the
}
if (low_port != 0 &&
dissector_try_port(subdissector_table, low_port, next_tvb, pinfo, tree))
- return;
+ goto end_decode_tcp_ports;
if (high_port != 0 &&
dissector_try_port(subdissector_table, high_port, next_tvb, pinfo, tree))
- return;
+ goto end_decode_tcp_ports;
- /* do lookup with the heuristic subdissector table */
- if (dissector_try_heuristic(heur_subdissector_list, next_tvb, pinfo, tree))
- return;
+ if (!try_heuristic_first) {
+ /* do lookup with the heuristic subdissector table */
+ if (dissector_try_heuristic(heur_subdissector_list, next_tvb, pinfo, tree))
+ goto end_decode_tcp_ports;
+ }
/* Oh, well, we don't know this; dissect it as data. */
call_dissector(data_handle,next_tvb, pinfo, tree);
+ return;
+
+end_decode_tcp_ports:
+ /* if !visited, check want_pdu_tracking and store it in table */
+ /* XXX fix nxtseq so that it always has valid content and skip the ==0 check */
+ if((!pinfo->fd->flags.visited) && nxtseq && tcp_analyze_seq && pinfo->want_pdu_tracking){
+ pdu_store_sequencenumber_of_next_pdu(pinfo, nxtseq+pinfo->bytes_until_next_pdu);
+ }
+
}
gint fpos = 0, i;
guint bpos;
guint optlen;
- guint32 nxtseq;
- guint len;
+ guint32 nxtseq = 0;
guint reported_len;
vec_t cksum_vec[4];
guint32 phdr[2];
tcph_count=0;
}
tcph=&tcphstruct[tcph_count];
-
+ 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);
if (check_col(pinfo->cinfo, COL_PROTOCOL))
col_set_str(pinfo->cinfo, COL_PROTOCOL, "TCP");
tcph->th_win = tvb_get_ntohs(tvb, offset + 14);
tcph->th_hlen = hi_nibble(th_off_x2) * 4; /* TCP header length, in bytes */
+ /*
+ * 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.
+ *
+ * The same applies if we're part of an error packet. (XXX - if the
+ * ICMP and ICMPv6 dissectors could set a "this is how big the IP
+ * header says it is" length in the tvbuff, we could use that; such
+ * a length might also be useful for handling packets where the IP
+ * length is bigger than the actual data available in the frame; the
+ * dissectors should trust that length, and then throw a
+ * ReportedBoundsError exception when they go past the end of the frame.)
+ *
+ * We also can't determine the segment length if the reported length
+ * of the TCP packet is less than the TCP header length.
+ */
reported_len = tvb_reported_length(tvb);
- len = tvb_length(tvb);
- /* Compute the length of data in this segment. */
- tcph->th_seglen = reported_len - tcph->th_hlen;
-
- if (tree) { /* Add the seglen as an invisible field */
+ if (!pinfo->fragmented && !pinfo->in_error_pkt) {
+ if (reported_len < tcph->th_hlen) {
+ 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)");
+ tcph->th_have_seglen = FALSE;
+ } else {
+ /* Compute the length of data in this segment. */
+ tcph->th_seglen = reported_len - tcph->th_hlen;
+ tcph->th_have_seglen = TRUE;
- proto_tree_add_uint_hidden(ti, hf_tcp_len, tvb, offset, 4, tcph->th_seglen);
+ if (tree) { /* Add the seglen as an invisible field */
- }
+ proto_tree_add_uint_hidden(ti, hf_tcp_len, tvb, offset, 4, tcph->th_seglen);
- /* 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);
}
- if(tcp_relative_seq){
- tcp_get_relative_seq_ack(pinfo->fd->num, &(tcph->th_seq), &(tcph->th_ack));
- }
- }
+ /* 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);
+ }
+ if(tcp_relative_seq){
+ tcp_get_relative_seq_ack(pinfo->fd->num, &(tcph->th_seq), &(tcph->th_ack), &(tcph->th_win));
+ }
+ }
- /* Compute the sequence number of next octet after this segment. */
- nxtseq = tcph->th_seq + tcph->th_seglen;
+ /* Compute the sequence number of next octet after this segment. */
+ nxtseq = tcph->th_seq + tcph->th_seglen;
+ }
+ } else
+ tcph->th_have_seglen = FALSE;
if (check_col(pinfo->cinfo, COL_INFO) || tree) {
for (i = 0; i < 8; i++) {
}
if (tree) {
- if (tcp_summary_in_tree)
+ if (tcp_summary_in_tree) {
proto_item_append_text(ti, ", Seq: %u", tcph->th_seq);
+ }
proto_tree_add_uint(tcp_tree, hf_tcp_seq, tvb, offset + 4, 4, tcph->th_seq);
}
}
if (tree) {
- if (tcp_summary_in_tree)
- proto_item_append_text(ti, ", Ack: %u, Len: %u", tcph->th_ack, tcph->th_seglen);
+ if (tcp_summary_in_tree) {
+ 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);
+ }
proto_item_set_len(ti, tcph->th_hlen);
- if (nxtseq != tcph->th_seq)
- proto_tree_add_uint(tcp_tree, hf_tcp_nxtseq, tvb, offset, 0, nxtseq);
+ if (tcph->th_have_seglen) {
+ if (nxtseq != tcph->th_seq) {
+ proto_tree_add_uint(tcp_tree, hf_tcp_nxtseq, tvb, offset, 0, nxtseq);
+ }
+ }
if (tcph->th_flags & TH_ACK)
proto_tree_add_uint(tcp_tree, hf_tcp_ack, tvb, offset + 8, 4, tcph->th_ack);
proto_tree_add_uint_format(tcp_tree, hf_tcp_hdr_len, tvb, offset + 12, 1, tcph->th_hlen,
* Assume, initially, that we can't desegment.
*/
pinfo->can_desegment = 0;
-
th_sum = tvb_get_ntohs(tvb, offset + 16);
- if (!pinfo->fragmented && len >= reported_len) {
+ if (!pinfo->fragmented && tvb_bytes_exist(tvb, 0, reported_len)) {
/* The packet isn't part of an un-reassembled fragmented datagram
and isn't truncated. This means we have all the data, and thus
can checksum it and, unless it's being returned in an error
g_assert_not_reached();
break;
}
- cksum_vec[3].ptr = tvb_get_ptr(tvb, offset, len);
+ cksum_vec[3].ptr = tvb_get_ptr(tvb, offset, reported_len);
cksum_vec[3].len = reported_len;
computed_cksum = in_cksum(&cksum_vec[0], 4);
if (computed_cksum == 0) {
/* Checksum is invalid, so we're not willing to desegment it. */
desegment_ok = FALSE;
+ pinfo->noreassembly_reason = " (incorrect TCP checksum)";
}
} else {
proto_tree_add_uint_format(tcp_tree, hf_tcp_checksum, tvb,
} else
tcpinfo.urgent = FALSE;
- if (check_col(pinfo->cinfo, COL_INFO))
- col_append_fstr(pinfo->cinfo, COL_INFO, " Len=%u", tcph->th_seglen);
+ if (tcph->th_have_seglen) {
+ if (check_col(pinfo->cinfo, COL_INFO))
+ col_append_fstr(pinfo->cinfo, COL_INFO, " Len=%u", tcph->th_seglen);
+ }
/* Decode TCP options, if any. */
if (tree && tcph->th_hlen > TCPH_MIN_LEN) {
(it could be an ACK-only packet) */
length_remaining = tvb_length_remaining(tvb, offset);
- if( data_out_file ) {
- reassemble_tcp( tcph->th_seq, /* sequence number */
- tcph->th_seglen, /* data length */
- tvb_get_ptr(tvb, offset, length_remaining), /* data */
- length_remaining, /* captured data length */
- ( tcph->th_flags & TH_SYN ), /* is syn set? */
- &pinfo->net_src,
- &pinfo->net_dst,
- pinfo->srcport,
- pinfo->destport);
+ if (tcph->th_have_seglen) {
+ if( data_out_file ) {
+ reassemble_tcp( tcph->th_seq, /* sequence number */
+ tcph->th_seglen, /* data length */
+ tvb_get_ptr(tvb, offset, length_remaining), /* data */
+ length_remaining, /* captured data length */
+ ( tcph->th_flags & TH_SYN ), /* is syn set? */
+ &pinfo->net_src,
+ &pinfo->net_dst,
+ pinfo->srcport,
+ pinfo->destport);
+ }
}
+ /*
+ * 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
+ * that caused the ICMP error, but it might not be nice to have the
+ * dissector update state based on it.
+ * Also, we probably don't want to run TCP taps on those packets.
+ */
if (length_remaining != 0) {
if (tcph->th_flags & TH_RST) {
/*
we don't report it as a malformed frame. */
save_fragmented = pinfo->fragmented;
pinfo->fragmented = TRUE;
- decode_tcp_ports(tvb, offset, pinfo, tree, tcph->th_sport, tcph->th_dport);
+ decode_tcp_ports(tvb, offset, pinfo, tree, tcph->th_sport, tcph->th_dport, nxtseq);
pinfo->fragmented = save_fragmented;
}
}
{ "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,
{ "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 }},
{ "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 }},
+ { &hf_tcp_analysis_duplicate_ack_num,
+ { "Duplicate ACK #", "tcp.analysis.duplicate_ack_num", FT_UINT32, BASE_DEC, NULL, 0x0,
+ "This is duplicate ACK number #", HFILL }},
+
+ { &hf_tcp_analysis_duplicate_ack_frame,
+ { "Duplicate to the ACK in frame", "tcp.analysis.duplicate_ack_frame", FT_FRAMENUM, BASE_NONE, NULL, 0x0,
+ "This is a duplicate to the ACK 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 }},
"", HFILL}},
{ &hf_tcp_analysis_acks_frame,
- { "This is an ACK to the segment in frame", "tcp.analysis.acks_frame", FT_UINT32, BASE_DEC, NULL, 0x0,
+ { "This is an ACK to the segment in frame", "tcp.analysis.acks_frame", FT_FRAMENUM, BASE_NONE, NULL, 0x0,
"Which previous segment is this an ACK for", HFILL}},
{ &hf_tcp_analysis_ack_rtt,
{ &hf_tcp_segments,
{ "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 starts but doesn't end in this segment is reassembled in this frame", HFILL }},
+
+ { &hf_tcp_option_mss,
+ { "TCP MSS Option", "tcp.options.mss", FT_BOOLEAN,
+ BASE_NONE, NULL, 0x0, "TCP MSS Option", HFILL }},
+
+ { &hf_tcp_option_mss_val,
+ { "TCP MSS Option Value", "tcp.options.mss_val", FT_UINT16,
+ BASE_DEC, NULL, 0x0, "TCP MSS Option Value", HFILL}},
+
+ { &hf_tcp_option_wscale,
+ { "TCP Window Scale Option", "tcp.options.wscale",
+ FT_BOOLEAN,
+ BASE_NONE, NULL, 0x0, "TCP Window Option", HFILL}},
+
+ { &hf_tcp_option_wscale_val,
+ { "TCP Windows Scale Option Value", "tcp.options.wscale_val",
+ FT_UINT8, BASE_DEC, NULL, 0x0, "TCP Window Scale Value",
+ HFILL}},
+
+ { &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,
+ BASE_NONE, NULL, 0x0, "TCP Sack Option", HFILL}},
+
+ { &hf_tcp_option_sack_sle,
+ {"TCP Sack Left Edge", "tcp.options.sack_le", FT_UINT32,
+ BASE_DEC, NULL, 0x0, "TCP Sack Left Edge", HFILL}},
+
+ { &hf_tcp_option_sack_sre,
+ {"TCP Sack Right Edge", "tcp.options.sack_re", FT_UINT32,
+ BASE_DEC, NULL, 0x0, "TCP Sack Right Edge", HFILL}},
+
+ { &hf_tcp_option_echo,
+ { "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",
+ FT_BOOLEAN,
+ BASE_NONE, NULL, 0x0, "TCP Echo Reply Option", HFILL}},
+
+ { &hf_tcp_option_time_stamp,
+ { "TCP Time Stamp Option", "tcp.options.time_stamp",
+ FT_BOOLEAN,
+ BASE_NONE, NULL, 0x0, "TCP Time Stamp Option", HFILL}},
+
+ { &hf_tcp_option_cc,
+ { "TCP CC Option", "tcp.options.cc", FT_BOOLEAN, BASE_NONE,
+ NULL, 0x0, "TCP CC Option", HFILL}},
+
+ { &hf_tcp_option_ccnew,
+ { "TCP CC New Option", "tcp.options.ccnew", FT_BOOLEAN,
+ BASE_NONE, NULL, 0x0, "TCP CC New Option", HFILL}},
+
+ { &hf_tcp_option_ccecho,
+ { "TCP CC Echo Option", "tcp.options.ccecho", FT_BOOLEAN,
+ BASE_NONE, NULL, 0x0, "TCP CC Echo Option", HFILL}},
+
+ { &hf_tcp_option_md5,
+ { "TCP MD5 Option", "tcp.options.md5", FT_BOOLEAN, BASE_NONE,
+ NULL, 0x0, "TCP MD5 Option", HFILL}},
};
static gint *ett[] = {
&ett_tcp,
tcp_module = prefs_register_protocol(proto_tcp, NULL);
prefs_register_bool_preference(tcp_module, "summary_in_tree",
"Show TCP summary in protocol tree",
-"Whether the TCP summary line should be shown in the protocol tree",
+ "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",
+ "Whether to check the validity of the TCP checksum",
&tcp_check_checksum);
prefs_register_bool_preference(tcp_module, "desegment_tcp_streams",
"Allow subdissector to desegment TCP streams",
-"Whether subdissector can request TCP streams to be desegmented",
+ "Whether subdissector can request TCP streams to be desegmented",
&tcp_desegment);
prefs_register_bool_preference(tcp_module, "analyze_sequence_numbers",
"Analyze TCP sequence numbers",
"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",
+ "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);