aboutsummaryrefslogtreecommitdiffstats
path: root/packet-tcp.c
diff options
context:
space:
mode:
authorRonnie Sahlberg <ronnie_sahlberg@ozemail.com.au>2002-08-02 22:41:56 +0000
committerRonnie Sahlberg <ronnie_sahlberg@ozemail.com.au>2002-08-02 22:41:56 +0000
commitff72b97ee01caee4dff97d07195d802086c65f38 (patch)
treea99f78fcb5951949970184bb890cfa174af1b07a /packet-tcp.c
parent96ab70e2093d35c0361221658a61284264c50c22 (diff)
Two new options added to TCP.
1, Analyze TCP sequence numbers. This option will keep track of sequence numbers for all tcp sessions and flag the following: a, If a new segment is seen which is beyong the right edge this is an indication that the previous segment was lost and this will be flagged as previous segment lost. b, If a segment is seen which lies left of the right edge this is flagged as retransmission. c, if a keep-alive is seen (empty segment, seq==expected seq-1) this is flagged as a retransmission. d, if an ACK is seen which is beyond the right edge this is an indication that a segment has been lost and it will be flagged as segment lost. All ACKs which advance the left edge get the RTT displayed between the ACKed segment and the ACK itself. The ACK also gets an indication of WHICH segment it is an ACK for. 2, Relative sequence numbers. This option needs the first option to be selected as well. This option will as best as it can try to get ethereal to use relative sequence numbers instead of absolute ones. The patch does not handle sequence number wrapping and unexpected results can probably happen for such. svn path=/trunk/; revision=5931
Diffstat (limited to 'packet-tcp.c')
-rw-r--r--packet-tcp.c628
1 files changed, 603 insertions, 25 deletions
diff --git a/packet-tcp.c b/packet-tcp.c
index dbe7c1d277..3bf46c6323 100644
--- a/packet-tcp.c
+++ b/packet-tcp.c
@@ -1,7 +1,7 @@
/* packet-tcp.c
* Routines for TCP packet disassembly
*
- * $Id: packet-tcp.c,v 1.146 2002/07/17 00:42:42 guy Exp $
+ * $Id: packet-tcp.c,v 1.147 2002/08/02 22:41:56 sahlberg Exp $
*
* Ethereal - Network traffic analyzer
* By Gerald Combs <gerald@ethereal.com>
@@ -90,12 +90,19 @@ static int hf_tcp_checksum = -1;
static int hf_tcp_checksum_bad = -1;
static int hf_tcp_len = -1;
static int hf_tcp_urgent_pointer = -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_lost_packet = -1;
+static int hf_tcp_analysis_ack_lost_packet = -1;
+static int hf_tcp_analysis_keep_alive = -1;
static gint ett_tcp = -1;
static gint ett_tcp_flags = -1;
static gint ett_tcp_options = -1;
static gint ett_tcp_option_sack = -1;
static gint ett_tcp_segments = -1;
+static gint ett_tcp_analysis = -1;
static dissector_table_t subdissector_table;
static heur_dissector_list_t heur_subdissector_list;
@@ -112,6 +119,528 @@ static dissector_handle_t data_handle;
#define TH_ECN 0x40
#define TH_CWR 0x80
+
+
+
+/* **************************************************************************
+ * stuff to analyze TCP sequencenumbers for retransmissions, missing segments,
+ * RTT and reltive sequence numbers.
+ * **************************************************************************/
+static gboolean tcp_analyze_seq = FALSE;
+static gboolean tcp_relative_seq = FALSE;
+
+static GMemChunk *tcp_unacked_chunk = NULL;
+static int tcp_unacked_count = 500; /* one for each packet until it is acked*/
+struct tcp_unacked {
+ struct tcp_unacked *next;
+ guint32 frame;
+ guint32 seq;
+ guint32 nextseq;
+ nstime_t ts;
+};
+
+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
+struct tcp_acked {
+ guint32 frame_acked;
+ nstime_t ts;
+ guint8 flags;
+};
+static GHashTable *tcp_analyze_acked_table = NULL;
+
+static GMemChunk *tcp_rel_seq_chunk = NULL;
+static int tcp_rel_seq_count = 10000; /* one for each segment in the capture */
+struct tcp_rel_seq {
+ guint32 seq_base;
+ guint32 ack_base;
+};
+static GHashTable *tcp_rel_seq_table = NULL;
+
+static GMemChunk *tcp_analysis_chunk = NULL;
+static int tcp_analysis_count = 20; /* one for each conversation */
+struct tcp_analysis {
+ /* these two structs are managed such as CMP_ADDRESS(src,dst)
+ * ==1, then stuff sent from src is in ual1
+ * ==-1, then stuff sent from src is in ual2 and vv
+ */
+ struct tcp_unacked *ual1; /* UnAcked List 1*/
+ guint32 base_seq1;
+ struct tcp_unacked *ual2; /* UnAcked List 2*/
+ guint32 base_seq2;
+};
+
+static void
+tcp_get_relative_seq_ack(guint32 frame, guint32 *seq, guint32 *ack)
+{
+ struct tcp_rel_seq *trs;
+
+ trs=g_hash_table_lookup(tcp_rel_seq_table, (void *)frame);
+ if(!trs){
+ return;
+ }
+
+ (*seq) -= trs->seq_base;
+ (*ack) -= trs->ack_base;
+}
+
+static struct tcp_acked *
+tcp_analyze_get_acked_struct(guint32 frame, gboolean createflag)
+{
+ struct tcp_acked *ta;
+
+ ta=g_hash_table_lookup(tcp_analyze_acked_table, (void *)frame);
+ if((!ta) && createflag){
+ ta=g_mem_chunk_alloc(tcp_acked_chunk);
+ ta->frame_acked=0;
+ ta->ts.secs=0;
+ ta->ts.nsecs=0;
+ ta->flags=0;
+ 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)
+{
+ conversation_t *conv=NULL;
+ struct tcp_analysis *tcpd=NULL;
+ int direction;
+ struct tcp_unacked *ual1=NULL;
+ struct tcp_unacked *ual2=NULL;
+ struct tcp_unacked *ual=NULL;
+ guint32 base_seq;
+ guint32 base_ack;
+
+ /* 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);
+ }
+
+ /* check direction and get ua lists */
+ direction=CMP_ADDRESS(&pinfo->src, &pinfo->dst);
+ if(direction==1){
+ ual1=tcpd->ual1;
+ ual2=tcpd->ual2;
+ base_seq=tcpd->base_seq1;
+ base_ack=tcpd->base_seq2;
+ } else {
+ ual1=tcpd->ual2;
+ ual2=tcpd->ual1;
+ base_seq=tcpd->base_seq2;
+ base_ack=tcpd->base_seq1;
+ }
+
+ if(base_seq==0){
+ base_seq=seq;
+ }
+ if(base_ack==0){
+ base_ack=ack;
+ }
+
+ /* handle the sequence numbers */
+ /* if this was a SYN packet, then remove existing list and
+ * put SEQ+1 first the list */
+ if(flags&TH_SYN){
+ for(ual=ual1;ual1;ual1=ual){
+ ual=ual1->next;
+ g_mem_chunk_free(tcp_unacked_chunk, ual1);
+ }
+ ual1=g_mem_chunk_alloc(tcp_unacked_chunk);
+ ual1->next=NULL;
+ ual1->frame=pinfo->fd->num;
+ ual1->seq=seq+1;
+ ual1->nextseq=seq+1;
+ ual1->ts.secs=pinfo->fd->abs_secs;
+ ual1->ts.nsecs=pinfo->fd->abs_usecs*1000;
+ base_seq=seq;
+ base_ack=ack;
+ goto seq_finished;
+ }
+
+ /* if this is the first segment we see then just add it */
+ if( !ual1 ){
+ ual1=g_mem_chunk_alloc(tcp_unacked_chunk);
+ ual1->next=NULL;
+ ual1->frame=pinfo->fd->num;
+ ual1->seq=seq;
+ ual1->nextseq=seq+seglen;
+ ual1->ts.secs=pinfo->fd->abs_secs;
+ ual1->ts.nsecs=pinfo->fd->abs_usecs*1000;
+ base_seq=seq;
+ goto seq_finished;
+ }
+
+ /* if we get past here we know that ual1 points to a segment */
+
+ /* if seq is beyond ual1->nextseq we have lost a segment */
+ if( seq>ual1->nextseq ){
+ struct tcp_acked *ta;
+
+ ta=tcp_analyze_get_acked_struct(pinfo->fd->num, TRUE);
+ ta->flags|=TCP_A_LOST_PACKET;
+
+ /* just add the segment to the beginning of the list */
+ ual=g_mem_chunk_alloc(tcp_unacked_chunk);
+ ual->next=ual1;
+ ual->frame=pinfo->fd->num;
+ ual->seq=seq;
+ ual->nextseq=seq+seglen;
+ ual->ts.secs=pinfo->fd->abs_secs;
+ ual->ts.nsecs=pinfo->fd->abs_usecs*1000;
+ ual1=ual;
+ goto seq_finished;
+ }
+
+ /* keep-alives are empty semgents with a sequence number -1 of what
+ * we would expect.
+ */
+ if( (!seglen) && (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 this is an empty segment, just skip it all */
+ if( !seglen ){
+ goto seq_finished;
+ }
+
+ /* check if the sequence number is lower than expected, i.e. retransmission */
+ if( seq < ual1->nextseq ){
+ 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((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;
+ }
+
+ /* just add the segment to the beginning of the list */
+ ual=g_mem_chunk_alloc(tcp_unacked_chunk);
+ ual->next=ual1;
+ ual->frame=pinfo->fd->num;
+ ual->seq=seq;
+ ual->nextseq=seq+seglen;
+ ual->ts.secs=pinfo->fd->abs_secs;
+ ual->ts.nsecs=pinfo->fd->abs_usecs*1000;
+ ual1=ual;
+
+seq_finished:
+
+
+ /* handle the ack numbers */
+
+ /* if we dont have the ack flag its not much we can do */
+ if( !(flags&TH_ACK)){
+ goto ack_finished;
+ }
+
+ /* if we havent seen anything yet in the other direction we dont
+ * know what this one acks */
+ if( !ual2 ){
+ goto ack_finished;
+ }
+
+ /* if we dont have any real segments in the other direction not
+ * acked yet (as we see from the magic frame==0 entry)
+ * then there is no point in continuing
+ */
+ if( !ual2->frame ){
+ goto ack_finished;
+ }
+
+ /* if we get here we know ual2 is valid */
+
+ /* if we are acking beyong what we have seen in the other direction
+ * we must have lost packets. Not much point in keeping the segments
+ * in the other direction either.
+ */
+ if( ack>ual2->nextseq ){
+ struct tcp_acked *ta;
+
+ ta=tcp_analyze_get_acked_struct(pinfo->fd->num, TRUE);
+ ta->flags|=TCP_A_ACK_LOST_PACKET;
+ for(ual=ual2;ual2;ual2=ual){
+ ual=ual2->next;
+ g_mem_chunk_free(tcp_unacked_chunk, ual2);
+ }
+ goto ack_finished;
+ }
+
+
+ /* does this ACK ack all semgents we have seen in the other direction?*/
+ if( ack==ual2->nextseq ){
+ struct tcp_acked *ta;
+
+ ta=tcp_analyze_get_acked_struct(pinfo->fd->num, TRUE);
+ ta->frame_acked=ual2->frame;
+ ta->ts.secs=pinfo->fd->abs_secs-ual2->ts.secs;
+ ta->ts.nsecs=pinfo->fd->abs_usecs*1000-ual2->ts.nsecs;
+ if(ta->ts.nsecs<0){
+ ta->ts.nsecs+=1000000000;
+ ta->ts.secs--;
+ }
+
+ /* its all been ACKed so we dont need to keep them anymore */
+ for(ual=ual2;ual2;ual2=ual){
+ ual=ual2->next;
+ g_mem_chunk_free(tcp_unacked_chunk, ual2);
+ }
+ goto ack_finished;
+ }
+
+ /* ok it only ACKs part of what we have seen. Find out how much
+ * update and remove the ACKed segments
+ */
+ for(ual=ual2;ual->next;ual=ual->next){
+ if(ack>=ual->next->nextseq){
+ break;
+ }
+ }
+ if(ual->next){
+ struct tcp_unacked *tmpual=NULL;
+ struct tcp_unacked *ackedual=NULL;
+ struct tcp_acked *ta;
+
+ /* XXX normal ACK*/
+ ackedual=ual->next;
+
+ ta=tcp_analyze_get_acked_struct(pinfo->fd->num, TRUE);
+ ta->frame_acked=ackedual->frame;
+ ta->ts.secs=pinfo->fd->abs_secs-ackedual->ts.secs;
+ ta->ts.nsecs=pinfo->fd->abs_usecs*1000-ackedual->ts.nsecs;
+ if(ta->ts.nsecs<0){
+ ta->ts.nsecs+=1000000000;
+ ta->ts.secs--;
+ }
+
+ /* just delete all ACKed segments */
+ tmpual=ual->next;
+ ual->next=NULL;
+ for(ual=tmpual;ual;ual=tmpual){
+ tmpual=ual->next;
+ g_mem_chunk_free(tcp_unacked_chunk, ual);
+ }
+
+ }
+
+
+ack_finished:
+ /* we might have deleted the entire ual2 list, if this is an ACK,
+ make sure ual2 at least has a dummy entry for the current ACK */
+ if( (!ual2) && (flags&TH_ACK) ){
+ ual2=g_mem_chunk_alloc(tcp_unacked_chunk);
+ ual2->next=NULL;
+ ual2->frame=0;
+ ual2->seq=ack;
+ ual2->nextseq=ack;
+ ual2->ts.secs=0;
+ ual2->ts.nsecs=0;
+ }
+
+
+ /* store the lists back in our struct */
+ if(direction==1){
+ tcpd->ual1=ual1;
+ tcpd->ual2=ual2;
+ tcpd->base_seq1=base_seq;
+ } else {
+ tcpd->ual1=ual2;
+ tcpd->ual2=ual1;
+ tcpd->base_seq2=base_seq;
+ }
+
+ if(tcp_relative_seq){
+ struct tcp_rel_seq *trs;
+ /* remember relative seq/ack number base for this packet */
+ trs=g_mem_chunk_alloc(tcp_rel_seq_chunk);
+ trs->seq_base=base_seq;
+ trs->ack_base=base_ack;
+ g_hash_table_insert(tcp_rel_seq_table, (void *)pinfo->fd->num, trs);
+ }
+}
+
+static void
+tcp_print_sequence_number_analysis(packet_info *pinfo, tvbuff_t *tvb, proto_tree *parent_tree)
+{
+ struct tcp_acked *ta;
+ proto_item *item;
+ proto_tree *tree;
+
+ ta=tcp_analyze_get_acked_struct(pinfo->fd->num, FALSE);
+ if(!ta){
+ return;
+ }
+
+ item=proto_tree_add_text(parent_tree, tvb, 0, 0, "SEQ/ACK analysis");
+ tree=proto_item_add_subtree(item, ett_tcp_analysis);
+
+ /* encapsulate all proto_tree_add_xxx in ifs so we only print what
+ data we actually have */
+ if(ta->frame_acked){
+ proto_tree_add_uint(tree, hf_tcp_analysis_acks_frame,
+ tvb, 0, 0, ta->frame_acked);
+ }
+ if( ta->ts.secs || ta->ts.nsecs ){
+ proto_tree_add_time(tree, hf_tcp_analysis_ack_rtt,
+ tvb, 0, 0, &ta->ts);
+ }
+ if( ta->flags&TCP_A_RETRANSMISSION ){
+ proto_tree_add_boolean_format(tree, hf_tcp_analysis_retransmission, tvb, 0, 0, TRUE, "This frame is a (suspected) retransmission");
+ if(check_col(pinfo->cinfo, COL_INFO)){
+ col_prepend_fstr(pinfo->cinfo, COL_INFO, "[TCP Retransmission] ");
+ }
+ }
+ if( ta->flags&TCP_A_LOST_PACKET ){
+ proto_tree_add_boolean_format(tree, hf_tcp_analysis_lost_packet, tvb, 0, 0, TRUE, "A segment before this frame was lost");
+ if(check_col(pinfo->cinfo, COL_INFO)){
+ col_prepend_fstr(pinfo->cinfo, COL_INFO, "[TCP Previous segment lost] ");
+ }
+ }
+ if( ta->flags&TCP_A_ACK_LOST_PACKET ){
+ proto_tree_add_boolean_format(tree, hf_tcp_analysis_ack_lost_packet, tvb, 0, 0, TRUE, "This frame ACKs a segment we have not seen (lost?)");
+ if(check_col(pinfo->cinfo, COL_INFO)){
+ col_prepend_fstr(pinfo->cinfo, COL_INFO, "[TCP ACKed lost segment] ");
+ }
+ }
+ if( ta->flags&TCP_A_KEEP_ALIVE ){
+ proto_tree_add_boolean_format(tree, hf_tcp_analysis_keep_alive, tvb, 0, 0, TRUE, "This is a TCP keep-alive segment");
+ if(check_col(pinfo->cinfo, COL_INFO)){
+ col_prepend_fstr(pinfo->cinfo, COL_INFO, "[TCP Keep-Alive] ");
+ }
+ }
+
+}
+
+
+/* Do we still need to do this ...remove_all() even though we dont need
+ * to do anything special? The glib docs are not clear on this and
+ * its better safe than sorry
+ */
+static gboolean
+free_all_acked(gpointer key_arg _U_, gpointer value _U_, gpointer user_data _U_)
+{
+ return TRUE;
+}
+
+static guint
+tcp_acked_hash(gconstpointer k)
+{
+ guint32 frame = (guint32)k;
+
+ return frame;
+}
+static gint
+tcp_acked_equal(gconstpointer k1, gconstpointer k2)
+{
+ guint32 frame1 = (guint32)k1;
+ guint32 frame2 = (guint32)k2;
+
+ return frame1==frame2;
+}
+
+static void
+tcp_analyze_seq_init(void)
+{
+ /* first destroy the tables */
+ if( tcp_analyze_acked_table ){
+ g_hash_table_foreach_remove(tcp_analyze_acked_table,
+ free_all_acked, NULL);
+ g_hash_table_destroy(tcp_analyze_acked_table);
+ tcp_analyze_acked_table = NULL;
+ }
+ if( tcp_rel_seq_table ){
+ g_hash_table_foreach_remove(tcp_rel_seq_table,
+ free_all_acked, NULL);
+ g_hash_table_destroy(tcp_rel_seq_table);
+ tcp_rel_seq_table = NULL;
+ }
+
+ /*
+ * Now destroy the chunk from which the conversation table
+ * structures were allocated.
+ */
+ if (tcp_analysis_chunk) {
+ g_mem_chunk_destroy(tcp_analysis_chunk);
+ tcp_analysis_chunk = NULL;
+ }
+ if (tcp_unacked_chunk) {
+ g_mem_chunk_destroy(tcp_unacked_chunk);
+ tcp_unacked_chunk = NULL;
+ }
+ if (tcp_acked_chunk) {
+ g_mem_chunk_destroy(tcp_acked_chunk);
+ tcp_acked_chunk = NULL;
+ }
+ if (tcp_rel_seq_chunk) {
+ g_mem_chunk_destroy(tcp_rel_seq_chunk);
+ tcp_rel_seq_chunk = NULL;
+ }
+
+ if(tcp_analyze_seq){
+ tcp_analyze_acked_table = g_hash_table_new(tcp_acked_hash,
+ tcp_acked_equal);
+ tcp_rel_seq_table = g_hash_table_new(tcp_acked_hash,
+ tcp_acked_equal);
+ tcp_analysis_chunk = g_mem_chunk_new("tcp_analysis_chunk",
+ sizeof(struct tcp_analysis),
+ tcp_analysis_count * sizeof(struct tcp_analysis),
+ G_ALLOC_ONLY);
+ tcp_unacked_chunk = g_mem_chunk_new("tcp_unacked_chunk",
+ sizeof(struct tcp_unacked),
+ tcp_unacked_count * sizeof(struct tcp_unacked),
+ G_ALLOC_ONLY);
+ tcp_acked_chunk = g_mem_chunk_new("tcp_acked_chunk",
+ sizeof(struct tcp_acked),
+ tcp_acked_count * sizeof(struct tcp_acked),
+ G_ALLOC_ONLY);
+ if(tcp_relative_seq){
+ tcp_rel_seq_chunk = g_mem_chunk_new("tcp_rel_seq_chunk",
+ sizeof(struct tcp_rel_seq),
+ tcp_rel_seq_count * sizeof(struct tcp_rel_seq),
+ G_ALLOC_ONLY);
+ }
+ }
+
+}
+
+/* **************************************************************************
+ * End of tcp sequence number analysis
+ * **************************************************************************/
+
+
+
+
/* Minimum TCP header length. */
#define TCPH_MIN_LEN 20
@@ -1122,6 +1651,39 @@ dissect_tcp(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree)
pinfo->srcport = th_sport;
pinfo->destport = th_dport;
+ th_seq = tvb_get_ntohl(tvb, offset + 4);
+ th_ack = tvb_get_ntohl(tvb, offset + 8);
+ th_off_x2 = tvb_get_guint8(tvb, offset + 12);
+ th_flags = tvb_get_guint8(tvb, offset + 13);
+ th_win = tvb_get_ntohs(tvb, offset + 14);
+ hlen = hi_nibble(th_off_x2) * 4; /* TCP header length, in bytes */
+
+ reported_len = tvb_reported_length(tvb);
+ len = tvb_length(tvb);
+
+ /* Compute the length of data in this segment. */
+ seglen = reported_len - hlen;
+
+ if (tree) { /* Add the seglen as an invisible field */
+
+ proto_tree_add_uint_hidden(ti, hf_tcp_len, tvb, offset, 4, 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, th_seq, th_ack, seglen, th_flags);
+ }
+ if(tcp_relative_seq){
+ tcp_get_relative_seq_ack(pinfo->fd->num, &th_seq, &th_ack);
+ }
+ }
+
+
+ /* Compute the sequence number of next octet after this segment. */
+ nxtseq = th_seq + seglen;
+
if (tree) {
if (tcp_summary_in_tree) {
ti = proto_tree_add_protocol_format(tree, proto_tcp, tvb, 0, -1,
@@ -1141,12 +1703,6 @@ dissect_tcp(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree)
proto_tree_add_uint_hidden(tcp_tree, hf_tcp_port, tvb, offset + 2, 2, th_dport);
}
- th_seq = tvb_get_ntohl(tvb, offset + 4);
- th_ack = tvb_get_ntohl(tvb, offset + 8);
- th_off_x2 = tvb_get_guint8(tvb, offset + 12);
- th_flags = tvb_get_guint8(tvb, offset + 13);
- th_win = tvb_get_ntohs(tvb, offset + 14);
-
if (check_col(pinfo->cinfo, COL_INFO) || tree) {
for (i = 0; i < 8; i++) {
bpos = 1 << i;
@@ -1173,8 +1729,6 @@ dissect_tcp(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree)
proto_tree_add_uint(tcp_tree, hf_tcp_seq, tvb, offset + 4, 4, th_seq);
}
- hlen = hi_nibble(th_off_x2) * 4; /* TCP header length, in bytes */
-
if (hlen < TCPH_MIN_LEN) {
/* Give up at this point; we put the source and destination port in
the tree, before fetching the header length, so that they'll
@@ -1191,21 +1745,6 @@ dissect_tcp(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree)
return;
}
- reported_len = tvb_reported_length(tvb);
- len = tvb_length(tvb);
-
- /* Compute the length of data in this segment. */
- seglen = reported_len - hlen;
-
- if (tree) { /* Add the seglen as an invisible field */
-
- proto_tree_add_uint_hidden(ti, hf_tcp_len, tvb, offset, 4, seglen);
-
- }
-
- /* Compute the sequence number of next octet after this segment. */
- nxtseq = th_seq + seglen;
-
if (tree) {
if (tcp_summary_in_tree)
proto_item_append_text(ti, ", Ack: %u, Len: %u", th_ack, seglen);
@@ -1411,6 +1950,11 @@ dissect_tcp(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree)
}
}
}
+
+ /* handle TCP seq# analysis, print any extra SEQ/ACK data for this segment*/
+ if(tcp_analyze_seq){
+ tcp_print_sequence_number_analysis(pinfo, tvb, tcp_tree);
+ }
}
void
@@ -1494,10 +2038,34 @@ proto_register_tcp(void)
{ "Bad Checksum", "tcp.checksum_bad", FT_BOOLEAN, BASE_NONE, NULL, 0x0,
"", HFILL }},
+ { &hf_tcp_analysis_retransmission,
+ { "", "tcp.analysis.retransmission", FT_BOOLEAN, BASE_NONE, NULL, 0x0,
+ "This frame is a suspected TCP retransmission", HFILL }},
+
+ { &hf_tcp_analysis_lost_packet,
+ { "", "tcp.analysis.lost_segment", FT_BOOLEAN, BASE_NONE, NULL, 0x0,
+ "A segment before this one was lost from the capture", HFILL }},
+
+ { &hf_tcp_analysis_ack_lost_packet,
+ { "", "tcp.analysis.ack_lost_segment", FT_BOOLEAN, BASE_NONE, NULL, 0x0,
+ "This frame ACKs a lost segment", HFILL }},
+
+ { &hf_tcp_analysis_keep_alive,
+ { "", "tcp.analysis.keep_alive", FT_BOOLEAN, BASE_NONE, NULL, 0x0,
+ "This is a keep-alive segment", HFILL }},
+
{ &hf_tcp_len,
{ "TCP Segment Len", "tcp.len", FT_UINT32, BASE_DEC, NULL, 0x0,
"", 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,
+ "Which previous segment is this an ACK for", HFILL}},
+
+ { &hf_tcp_analysis_ack_rtt,
+ { "The RTT to ACK the segment was", "tcp.analysis.ack_rtt", FT_RELATIVE_TIME, BASE_NONE, NULL, 0x0,
+ "How long time it took to ACK the segment (RTT)", HFILL}},
+
{ &hf_tcp_urgent_pointer,
{ "Urgent pointer", "tcp.urgent_pointer", FT_UINT16, BASE_DEC, NULL, 0x0,
"", HFILL }},
@@ -1508,6 +2076,7 @@ proto_register_tcp(void)
&ett_tcp_options,
&ett_tcp_option_sack,
&ett_tcp_segments,
+ &ett_tcp_analysis
};
module_t *tcp_module;
@@ -1535,7 +2104,16 @@ proto_register_tcp(void)
"Allow subdissector to desegment TCP streams",
"Whether subdissector can request TCP streams to be desegmented",
&tcp_desegment);
-
+ prefs_register_bool_preference(tcp_module, "tcp_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, "tcp_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\".",
+ &tcp_relative_seq);
+
+ register_init_routine(tcp_analyze_seq_init);
register_init_routine(tcp_desegment_init);
register_init_routine(tcp_fragment_init);
}