*
* $Id$
*
- * Ethereal - Network traffic analyzer
- * By Gerald Combs <gerald@ethereal.com>
+ * Wireshark - Network traffic analyzer
+ * By Gerald Combs <gerald@wireshark.org>
* Copyright 1998 Gerald Combs
*
* This program is free software; you can redistribute it and/or
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#ifdef HAVE_CONFIG_H
static int hf_tcp_analysis_zero_window_probe_ack = -1;
static int hf_tcp_continuation_to = -1;
static int hf_tcp_pdu_time = -1;
+static int hf_tcp_pdu_size = -1;
static int hf_tcp_pdu_last_frame = -1;
static int hf_tcp_reassembled_in = -1;
static int hf_tcp_segments = -1;
static int hf_tcp_segment_multiple_tails = -1;
static int hf_tcp_segment_too_long_fragment = -1;
static int hf_tcp_segment_error = -1;
+static int hf_tcp_options = -1;
static int hf_tcp_option_mss = -1;
static int hf_tcp_option_mss_val = -1;
static int hf_tcp_option_wscale = -1;
#define TCP_A_WINDOW_FULL 0x1000
-static GHashTable *tcp_pdu_tracking_table = NULL;
-static GHashTable *tcp_pdu_skipping_table = NULL;
-
-static se_tree_t *tcp_pdu_time_table = NULL;
-
static void
process_tcp_payload(tvbuff_t *tvb, volatile int offset, packet_info *pinfo,
proto_tree *tree, proto_tree *tcp_tree, int src_port, int dst_port,
tcpd->flow1.nextseqframe=0;
tcpd->flow1.window=0;
tcpd->flow1.win_scale=-1;
- tcpd->flow1.pdu_seq=NULL;
+ tcpd->flow1.flags=0;
+ tcpd->flow1.multisegment_pdus=se_tree_create_non_persistent(EMEM_TREE_TYPE_RED_BLACK, "tcp_multisegment_pdus");
tcpd->flow2.segments=NULL;
tcpd->flow2.base_seq=0;
tcpd->flow2.lastack=0;
tcpd->flow2.nextseqframe=0;
tcpd->flow2.window=0;
tcpd->flow2.win_scale=-1;
- tcpd->flow2.pdu_seq=NULL;
- tcpd->acked_table=se_tree_create_non_persistent(SE_TREE_TYPE_RED_BLACK, "tcp_analyze_acked_table");
+ tcpd->flow2.flags=0;
+ tcpd->flow2.multisegment_pdus=se_tree_create_non_persistent(EMEM_TREE_TYPE_RED_BLACK, "tcp_multisegment_pdus");
+ tcpd->acked_table=se_tree_create_non_persistent(EMEM_TREE_TYPE_RED_BLACK, "tcp_analyze_acked_table");
conversation_add_proto_data(conv, proto_tcp, tcpd);
return tcpd;
}
-/* This function is called from the tcp analysis code to provide
- clues on how the seq and ack numbers are changed.
- To prevent the next_pdu lists from growing uncontrollable in size we
- use this function to do the following :
- IF we see an ACK then we assume that the left edge of the window has changed
- at least to this point and assuming it is rare with reordering and
- trailing duplicate/retransmitted segments, we just assume that after
- we have seen the ACK we will not see any more segments prior to the
- ACK value.
- If we will not see any segments prior to the ACK value then we can just
- delete all next_pdu entries that describe pdu's starting prior to the
- ACK.
- If this heuristics is prooved to be too simplistic we can just enhance it
- later.
-*/
-/* XXX this function should be ehnanced to handle sequence number wrapping */
-/* XXX to handle retransmissions and reordered packets maybe we should only
- discard entries that are more than (guesstimate) 50kb older than the
- specified sequence number ?
-*/
static void
-prune_next_pdu_list(struct tcp_next_pdu **tnp, guint32 seq)
-{
- struct tcp_next_pdu *tmptnp;
-
- if(*tnp == NULL){
- return;
- }
-
- for(tmptnp=*tnp;tmptnp;tmptnp=tmptnp->next){
- if(tmptnp->nxtpdu<=seq){
- struct tcp_next_pdu *oldtnp;
- oldtnp=tmptnp;
-
- if(tmptnp==*tnp){
- tmptnp=tmptnp->next;
- *tnp=tmptnp;
- if(!tmptnp){
- return;
- }
- continue;
- } else {
- for(tmptnp=*tnp;tmptnp;tmptnp=tmptnp->next){
- if(tmptnp->next==oldtnp){
- tmptnp->next=oldtnp->next;
- break;
- }
- }
- if(!tmptnp){
- return;
- }
- }
- }
- }
-}
-
-
-static void
-print_pdu_tracking_data(packet_info *pinfo, tvbuff_t *tvb, proto_tree *tcp_tree, struct tcp_next_pdu *tnp)
+print_pdu_tracking_data(packet_info *pinfo, tvbuff_t *tvb, proto_tree *tcp_tree, struct tcp_multisegment_pdu *msp)
{
proto_item *item;
if (check_col(pinfo->cinfo, COL_INFO)){
- col_prepend_fence_fstr(pinfo->cinfo, COL_INFO, "[Continuation to #%u] ", tnp->first_frame);
+ col_prepend_fence_fstr(pinfo->cinfo, COL_INFO, "[Continuation to #%u] ", msp->first_frame);
}
item=proto_tree_add_uint(tcp_tree, hf_tcp_continuation_to,
- tvb, 0, 0, tnp->first_frame);
+ tvb, 0, 0, msp->first_frame);
PROTO_ITEM_SET_GENERATED(item);
}
static int
scan_for_next_pdu(tvbuff_t *tvb, proto_tree *tcp_tree, packet_info *pinfo, int offset, guint32 seq, guint32 nxtseq, struct tcp_analysis *tcpd)
{
- struct tcp_next_pdu *tnp=NULL;
+ struct tcp_multisegment_pdu *msp=NULL;
if(!pinfo->fd->flags.visited){
- /* find(or create if needed) the conversation for this tcp session */
- tnp=tcpd->fwd->pdu_seq;
-
- /* scan and see if we find any pdus starting inside this tvb */
- for(;tnp;tnp=tnp->next){
- /* XXX here we should also try to handle sequence number
- wrapping
- */
+ msp=se_tree_lookup32_le(tcpd->fwd->multisegment_pdus, seq-1);
+ if(msp){
/* If this segment is completely within a previous PDU
* then we just skip this packet
*/
- if(seq>tnp->seq && nxtseq<=tnp->nxtpdu){
- tnp->last_frame=pinfo->fd->num;
- tnp->last_frame_time=pinfo->fd->abs_ts;
- g_hash_table_insert(tcp_pdu_skipping_table,
- GINT_TO_POINTER(pinfo->fd->num), (void *)tnp);
- print_pdu_tracking_data(pinfo, tvb, tcp_tree, tnp);
-
+ if(seq>msp->seq && nxtseq<=msp->nxtpdu){
+ msp->last_frame=pinfo->fd->num;
+ msp->last_frame_time=pinfo->fd->abs_ts;
+ print_pdu_tracking_data(pinfo, tvb, tcp_tree, msp);
return -1;
}
- if(seq<tnp->nxtpdu && nxtseq>tnp->nxtpdu){
- g_hash_table_insert(tcp_pdu_tracking_table,
- GINT_TO_POINTER(pinfo->fd->num), GUINT_TO_POINTER(tnp->nxtpdu));
- offset+=tnp->nxtpdu-seq;
- break;
+ if(seq<msp->nxtpdu && nxtseq>msp->nxtpdu){
+ offset+=msp->nxtpdu-seq;
+ return offset;
}
+
}
} else {
- guint32 pduseq;
+ msp=se_tree_lookup32_le(tcpd->fwd->multisegment_pdus, seq-1);
+ if(msp){
+ if(pinfo->fd->num==msp->first_frame){
+ proto_item *item;
+ nstime_t ns;
- tnp=(struct tcp_next_pdu *)se_tree_lookup32(tcp_pdu_time_table, pinfo->fd->num);
- if(tnp){
- proto_item *item;
- nstime_t ns;
-
- item=proto_tree_add_uint(tcp_tree, hf_tcp_pdu_last_frame, tvb, 0, 0, tnp->last_frame);
- PROTO_ITEM_SET_GENERATED(item);
+ item=proto_tree_add_uint(tcp_tree, hf_tcp_pdu_last_frame, tvb, 0, 0, msp->last_frame);
+ PROTO_ITEM_SET_GENERATED(item);
- nstime_delta(&ns, &tnp->last_frame_time, &pinfo->fd->abs_ts);
- item = proto_tree_add_time(tcp_tree, hf_tcp_pdu_time,
- tvb, 0, 0, &ns);
- PROTO_ITEM_SET_GENERATED(item);
- }
+ nstime_delta(&ns, &msp->last_frame_time, &pinfo->fd->abs_ts);
+ item = proto_tree_add_time(tcp_tree, hf_tcp_pdu_time,
+ tvb, 0, 0, &ns);
+ PROTO_ITEM_SET_GENERATED(item);
+ }
- /* check if this is a segment in the middle of a pdu */
- tnp=(struct tcp_next_pdu *)g_hash_table_lookup(tcp_pdu_skipping_table, GINT_TO_POINTER(pinfo->fd->num));
- if(tnp){
- print_pdu_tracking_data(pinfo, tvb, tcp_tree, tnp);
- return -1;
- }
+ /* If this segment is completely within a previous PDU
+ * then we just skip this packet
+ */
+ if(seq>msp->seq && nxtseq<=msp->nxtpdu){
+ print_pdu_tracking_data(pinfo, tvb, tcp_tree, msp);
+ return -1;
+ }
- pduseq=GPOINTER_TO_UINT(g_hash_table_lookup(tcp_pdu_tracking_table, GINT_TO_POINTER(pinfo->fd->num)));
- if(pduseq){
- offset+=pduseq-seq;
+ if(seq<msp->nxtpdu && nxtseq>msp->nxtpdu){
+ offset+=msp->nxtpdu-seq;
+ return offset;
+ }
}
}
-
return offset;
}
/* if we saw a PDU that extended beyond the end of the segment,
use this function to remember where the next pdu starts
*/
-static void
+static struct tcp_multisegment_pdu *
pdu_store_sequencenumber_of_next_pdu(packet_info *pinfo, guint32 seq, guint32 nxtpdu, struct tcp_analysis *tcpd)
{
- struct tcp_next_pdu *tnp=NULL;
-
- tnp=se_alloc(sizeof(struct tcp_next_pdu));
- tnp->nxtpdu=nxtpdu;
- tnp->seq=seq;
- tnp->first_frame=pinfo->fd->num;
- tnp->last_frame=pinfo->fd->num;
- tnp->last_frame_time=pinfo->fd->abs_ts;
-
- tnp->next=tcpd->fwd->pdu_seq;
- tcpd->fwd->pdu_seq=tnp;
- /*QQQ
- Add check for ACKs and purge list of sequence numbers
- already acked.
- */
- se_tree_insert32(tcp_pdu_time_table, pinfo->fd->num, (void *)tnp);
+ struct tcp_multisegment_pdu *msp;
+
+ msp=se_alloc(sizeof(struct tcp_multisegment_pdu));
+ msp->nxtpdu=nxtpdu;
+ msp->seq=seq;
+ msp->first_frame=pinfo->fd->num;
+ msp->last_frame=pinfo->fd->num;
+ msp->last_frame_time=pinfo->fd->abs_ts;
+ se_tree_insert32(tcpd->fwd->multisegment_pdus, seq, (void *)msp);
+ return msp;
}
/* This is called for SYN+ACK packets and the purpose is to verify that we
finished_fwd:
- /* If this was NOT a dupack we must reset the dupack countres */
+ /* If this was NOT a dupack we must reset the dupack counters */
if( (!tcpd->ta) || !(tcpd->ta->flags&TCP_A_DUPLICATE_ACK) ){
tcpd->fwd->lastnondupack=pinfo->fd->num;
tcpd->fwd->dupacknum=0;
* in the other direction.
*/
if( tcpd->rev->nextseq
- && GT_SEQ(ack, tcpd->rev->nextseq )){
+ && GT_SEQ(ack, tcpd->rev->nextseq )
+ && (flags&(TH_ACK))!=0 ){
/*QQQ tested*/
if(!tcpd->ta){
tcp_analyze_get_acked_struct(pinfo->fd->num, TRUE, tcpd);
goto finished_checking_retransmission_type;
}
- /* If there were >=1 duplicate ACKs in the reverse direction
+ /* If there were >=2 duplicate ACKs in the reverse direction
* (there might be duplicate acks missing from the trace)
* and if this sequence number matches those ACKs
* and if the packet occurs within 20ms of the last
*/
t=(pinfo->fd->abs_ts.secs-tcpd->rev->lastacktime.secs)*1000000000;
t=t+(pinfo->fd->abs_ts.nsecs)-tcpd->rev->lastacktime.nsecs;
- if( tcpd->rev->dupacknum>=1
+ if( tcpd->rev->dupacknum>=2
&& tcpd->rev->lastack==seq
&& t<20000000 ){
if(!tcpd->ta){
}
- prune_next_pdu_list(&(tcpd->rev->pdu_seq), ack-tcpd->rev->base_seq);
-
/* remove all segments this ACKs and we dont need to keep around any more
*/
ackcount=0;
/* first we remove all such segments at the head of the list */
while((ual=tcpd->rev->segments)){
tcp_unacked_t *tmpual;
+ if(ack==ual->nextseq){
+ tcp_analyze_get_acked_struct(pinfo->fd->num, TRUE, tcpd);
+ tcpd->ta->frame_acked=ual->frame;
+ nstime_delta(&tcpd->ta->ts, &pinfo->fd->abs_ts, &ual->ts);
+ }
if(GT_SEQ(ual->nextseq,ack)){
break;
}
ual=ual->next;
}
-#ifdef REMOVED
- tcp_analyze_get_acked_struct(pinfo->fd->num, TRUE, tcpd);
- tcpd->ta->frame_acked=tcpd->rev->segments->frame;
- nstime_delta(&tcpd->ta->ts, &pinfo->fd->abs_ts, &tcpd->rev->segments->ts);
-#endif
}
static void
flags_item=proto_tree_add_uint(tree, hf_tcp_analysis_duplicate_ack_frame,
tvb, 0, 0, ta->dupack_frame);
PROTO_ITEM_SET_GENERATED(flags_item);
- expert_add_info_format(pinfo, flags_item, PI_SEQUENCE, PI_NOTE, "Duplicate ACK (#%u) to ACK in packet #%u",
- ta->dupack_num, ta->dupack_frame);
+ expert_add_info_format(pinfo, flags_item, PI_SEQUENCE, PI_NOTE, "Duplicate ACK (#%u)",
+ ta->dupack_num);
}
if( ta->flags&TCP_A_ZERO_WINDOW_PROBE ){
flags_item=proto_tree_add_none_format(flags_tree, hf_tcp_analysis_zero_window_probe, tvb, 0, 0, "This is a TCP zero-window-probe");
}
-/* Do we still need to do this ...remove_all() even though we dont need
- * to do anything special? The glib docs are not clear on this and
- * its better safe than sorry
- */
-static gboolean
-free_all_acked(gpointer key_arg _U_, gpointer value _U_, gpointer user_data _U_)
-{
- return TRUE;
-}
-
-static guint
-tcp_acked_hash(gconstpointer k)
-{
- guint32 frame = GPOINTER_TO_UINT(k);
-
- return frame;
-}
-static gint
-tcp_acked_equal(gconstpointer k1, gconstpointer k2)
-{
- guint32 frame1 = GPOINTER_TO_UINT(k1);
- guint32 frame2 = GPOINTER_TO_UINT(k2);
-
- return frame1==frame2;
-}
-
-static void
-tcp_analyze_seq_init(void)
-{
- /* first destroy the tables */
- if( tcp_pdu_tracking_table ){
- g_hash_table_foreach_remove(tcp_pdu_tracking_table,
- free_all_acked, NULL);
- g_hash_table_destroy(tcp_pdu_tracking_table);
- tcp_pdu_tracking_table = NULL;
- }
- if( tcp_pdu_skipping_table ){
- g_hash_table_foreach_remove(tcp_pdu_skipping_table,
- free_all_acked, NULL);
- g_hash_table_destroy(tcp_pdu_skipping_table);
- tcp_pdu_skipping_table = NULL;
- }
-
- if(tcp_analyze_seq){
- tcp_pdu_tracking_table = g_hash_table_new(tcp_acked_hash,
- tcp_acked_equal);
- tcp_pdu_skipping_table = g_hash_table_new(tcp_acked_hash,
- tcp_acked_equal);
- }
-
-}
-
/* **************************************************************************
* End of tcp sequence number analysis
* **************************************************************************/
/* Enable desegmenting of TCP streams */
static gboolean tcp_desegment = TRUE;
-static GHashTable *tcp_segment_table = NULL;
-static GMemChunk *tcp_segment_key_chunk = NULL;
-static int tcp_segment_init_count = 200;
-static GMemChunk *tcp_segment_address_chunk = NULL;
-static int tcp_segment_address_init_count = 500;
-
-typedef struct _tcp_segment_key {
- /* for own bookkeeping inside packet-tcp.c */
- address *src;
- address *dst;
- guint32 seq;
- /* xxx */
- guint16 sport;
- guint16 dport;
- guint32 start_seq;
- guint32 tot_len;
- guint32 first_frame;
-} tcp_segment_key;
-
-static gboolean
-free_all_segments(gpointer key_arg, gpointer value _U_, gpointer user_data _U_)
-{
- tcp_segment_key *key = key_arg;
-
- if((key->src)&&(key->src->data)){
- g_free((gpointer)key->src->data);
- key->src->data=NULL;
- }
-
- if((key->dst)&&(key->dst->data)){
- g_free((gpointer)key->dst->data);
- key->dst->data=NULL;
- }
-
- return TRUE;
-}
-
-static guint
-tcp_segment_hash(gconstpointer k)
-{
- const tcp_segment_key *key = (const tcp_segment_key *)k;
-
- return key->seq+key->sport;
-}
-
-static gint
-tcp_segment_equal(gconstpointer k1, gconstpointer k2)
-{
- const tcp_segment_key *key1 = (const tcp_segment_key *)k1;
- const tcp_segment_key *key2 = (const tcp_segment_key *)k2;
-
- return ( ( (key1->seq==key2->seq)
- &&(ADDRESSES_EQUAL(key1->src, key2->src))
- &&(ADDRESSES_EQUAL(key1->dst, key2->dst))
- &&(key1->sport==key2->sport)
- &&(key1->dport==key2->dport)
- ) ? TRUE:FALSE);
-}
-
-static void
-tcp_desegment_init(void)
-{
- /*
- * Free this before freeing any memory chunks; those
- * chunks contain data we'll look at in "free_all_segments()".
- */
- if(tcp_segment_table){
- g_hash_table_foreach_remove(tcp_segment_table,
- free_all_segments, NULL);
- g_hash_table_destroy(tcp_segment_table);
- tcp_segment_table = NULL;
- }
-
- if(tcp_segment_key_chunk){
- g_mem_chunk_destroy(tcp_segment_key_chunk);
- tcp_segment_key_chunk = NULL;
- }
- if(tcp_segment_address_chunk){
- g_mem_chunk_destroy(tcp_segment_address_chunk);
- tcp_segment_address_chunk = NULL;
- }
-
- /* dont allocate any hash table or memory chunks unless the user
- really uses this option
- */
- if(!tcp_desegment){
- return;
- }
-
- tcp_segment_table = g_hash_table_new(tcp_segment_hash,
- tcp_segment_equal);
-
- tcp_segment_key_chunk = g_mem_chunk_new("tcp_segment_key_chunk",
- sizeof(tcp_segment_key),
- tcp_segment_init_count*sizeof(tcp_segment_key),
- G_ALLOC_ONLY);
-
- tcp_segment_address_chunk = g_mem_chunk_new("tcp_segment_address_chunk",
- sizeof(address),
- tcp_segment_address_init_count*sizeof(address),
- G_ALLOC_ONLY);
-}
-
static void
desegment_tcp(tvbuff_t *tvb, packet_info *pinfo, int offset,
guint32 seq, guint32 nxtseq,
struct tcp_analysis *tcpd)
{
struct tcpinfo *tcpinfo = pinfo->private_data;
- fragment_data *ipfd_head=NULL;
- tcp_segment_key old_tsk, *tsk;
- gboolean must_desegment = FALSE;
- gboolean called_dissector = FALSE;
+ fragment_data *ipfd_head;
+ gboolean must_desegment;
+ gboolean called_dissector;
+ int another_pdu_follows;
int deseg_offset;
guint32 deseg_seq;
gint nbytes;
- proto_item *item;
- proto_item *frag_tree_item;
- proto_item *tcp_tree_item;
+ proto_item *item;
+ proto_item *frag_tree_item;
+ proto_item *tcp_tree_item;
+ struct tcp_multisegment_pdu *msp;
+again:
+ ipfd_head=NULL;
+ must_desegment = FALSE;
+ called_dissector = FALSE;
+ another_pdu_follows = 0;
+ msp=NULL;
/*
* Initialize these to assume no desegmentation.
*/
deseg_offset = offset;
- /* First we must check if this TCP segment should be desegmented.
- This is only to check if we should desegment this packet,
- so we dont spend time doing COPY_ADDRESS/g_free.
- We just "borrow" some address structures from pinfo instead. Cheaper.
- */
- old_tsk.src = &pinfo->src;
- old_tsk.dst = &pinfo->dst;
- old_tsk.sport = sport;
- old_tsk.dport = dport;
- old_tsk.seq = seq;
- tsk = g_hash_table_lookup(tcp_segment_table, &old_tsk);
-
- if(tsk){
- /* OK, this segment was found, which means it continues
- a higher-level PDU. This means we must desegment it.
- Add it to the defragmentation lists.
+ /* find the most previous PDU starting before this sequence number */
+ msp=se_tree_lookup32_le(tcpd->fwd->multisegment_pdus, seq-1);
+ if(msp && msp->seq<=seq && msp->nxtpdu>seq){
+ int len;
+
+ if(!pinfo->fd->flags.visited){
+ msp->last_frame=pinfo->fd->num;
+ msp->last_frame_time=pinfo->fd->abs_ts;
+ }
+
+ /* OK, this PDU was found, which means the segment continues
+ a higher-level PDU and that we must desegment it.
*/
- ipfd_head = fragment_add(tvb, offset, pinfo, tsk->first_frame,
+ len=MIN(nxtseq, msp->nxtpdu) - seq;
+ ipfd_head = fragment_add(tvb, offset, pinfo, msp->first_frame,
tcp_fragment_table,
- seq - tsk->start_seq,
- nxtseq - seq,
- (LT_SEQ (nxtseq,tsk->start_seq + tsk->tot_len)) );
-
- if(!ipfd_head){
- /* fragment_add() returned NULL, This means that
- desegmentation is not completed yet.
- (its like defragmentation but we know we will
- always add the segments in order).
- XXX - no, we don't; there is no guarantee that
- TCP segments are in order on the wire.
-
- we must add next segment to our table so we will
- find it later.
- */
- tcp_segment_key *new_tsk;
-
- new_tsk = g_mem_chunk_alloc(tcp_segment_key_chunk);
- memcpy(new_tsk, tsk, sizeof(tcp_segment_key));
- new_tsk->seq=nxtseq;
- g_hash_table_insert(tcp_segment_table,new_tsk,new_tsk);
+ seq - msp->seq,
+ len,
+ (LT_SEQ (nxtseq,msp->nxtpdu)) );
+ /* if we didnt consume the entire segment there is another pdu
+ * starting beyong the end of this one
+ */
+ if(msp->nxtpdu<nxtseq && len>0){
+ another_pdu_follows=len;
}
} else {
/* This segment was not found in our table, so it doesn't
ipfd_head = NULL;
}
+
/* is it completely desegmented? */
if(ipfd_head){
- fragment_data *ipfd;
-
/*
* Yes, we think it is.
* We only call subdissector for the last segment.
* Note that the last segment may include more than what
* we needed.
*/
- if(GE_SEQ(nxtseq, tsk->start_seq + tsk->tot_len)){
+ if(ipfd_head->reassembled_in==pinfo->fd->num){
/*
* OK, this is the last segment.
* Let's call the subdissector with the desegmented
* Supply the sequence number of the first of the
* reassembled bytes.
*/
- tcpinfo->seq = tsk->start_seq;
+ tcpinfo->seq = msp->seq;
/* indicate that this is reassembled data */
tcpinfo->is_reassembled = TRUE;
old_len=(int)(tvb_reported_length(next_tvb)-tvb_reported_length_remaining(tvb, offset));
if(pinfo->desegment_len &&
pinfo->desegment_offset<=old_len){
- tcp_segment_key *new_tsk;
-
/*
* "desegment_len" isn't 0, so it needs more
* data for something - and "desegment_offset"
* being a new higher-level PDU that also
* needs desegmentation).
*/
- fragment_set_partial_reassembly(pinfo,tsk->first_frame,tcp_fragment_table);
- tsk->tot_len = tvb_reported_length(next_tvb) + pinfo->desegment_len;
-
- /*
- * Update tsk structure.
- * Can ask ->next->next because at least there's a hdr and one
- * entry in fragment_add()
- */
- for(ipfd=ipfd_head->next; ipfd->next; ipfd=ipfd->next){
- old_tsk.seq = tsk->start_seq + ipfd->offset;
- new_tsk = g_hash_table_lookup(tcp_segment_table, &old_tsk);
- if (new_tsk)
- new_tsk->tot_len = tsk->tot_len;
- }
-
- /* this is the next segment in the sequence we want */
- new_tsk = g_mem_chunk_alloc(tcp_segment_key_chunk);
- memcpy(new_tsk, tsk, sizeof(tcp_segment_key));
- new_tsk->seq = nxtseq;
- g_hash_table_insert(tcp_segment_table,new_tsk,new_tsk);
+ fragment_set_partial_reassembly(pinfo,msp->first_frame,tcp_fragment_table);
+ msp->nxtpdu=msp->seq+tvb_reported_length(next_tvb) + pinfo->desegment_len;
} else {
/*
* Show the stuff in this TCP segment as
}
if (must_desegment) {
- tcp_segment_key *tsk, *new_tsk;
+ /* If the dissector requested "reassemble until FIN"
+ * just set this flag for the flow and let reassembly
+ * proceed at normal. We will check/pick up these
+ * reassembled PDUs later down in dissect_tcp() when checking
+ * for the FIN flag.
+ */
+ if(pinfo->desegment_len==DESEGMENT_UNTIL_FIN){
+ tcpd->fwd->flags|=TCP_FLOW_REASSEMBLE_UNTIL_FIN;
+ }
/*
* The sequence number at which the stuff to be desegmented
*/
deseg_seq = seq + (deseg_offset - offset);
- /*
- * XXX - how do we detect out-of-order transmissions?
- * We can't just check for "nxtseq" being greater than
- * "tsk->start_seq"; for now, we check for the difference
- * being less than a megabyte, but this is a really
- * gross hack - we really need to handle out-of-order
- * transmissions correctly.
- */
- if ((nxtseq - deseg_seq) <= 1024*1024) {
- /* OK, subdissector wants us to desegment
- some data before it can process it. Add
- what remains of this packet and set
- up next packet/sequence number as well.
+ if( ((nxtseq - deseg_seq) <= 1024*1024)
+ && (!pinfo->fd->flags.visited) ){
+ msp = pdu_store_sequencenumber_of_next_pdu(pinfo, deseg_seq,
+ nxtseq + pinfo->desegment_len, tcpd);
- We must remember this segment
- */
- tsk = g_mem_chunk_alloc(tcp_segment_key_chunk);
- tsk->src = g_mem_chunk_alloc(tcp_segment_address_chunk);
- COPY_ADDRESS(tsk->src, &pinfo->src);
- tsk->dst = g_mem_chunk_alloc(tcp_segment_address_chunk);
- COPY_ADDRESS(tsk->dst, &pinfo->dst);
- tsk->seq = deseg_seq;
- tsk->start_seq = tsk->seq;
- tsk->tot_len = nxtseq - tsk->start_seq + pinfo->desegment_len;
- tsk->first_frame = pinfo->fd->num;
- tsk->sport=sport;
- tsk->dport=dport;
- g_hash_table_insert(tcp_segment_table, tsk, tsk);
-
- /* Add portion of segment unprocessed by the subdissector
- to defragmentation lists */
- fragment_add(tvb, deseg_offset, pinfo, tsk->first_frame,
- tcp_fragment_table,
- tsk->seq - tsk->start_seq,
- nxtseq - tsk->start_seq,
- LT_SEQ (nxtseq, tsk->start_seq + tsk->tot_len));
-
- /* this is the next segment in the sequence we want */
- new_tsk = g_mem_chunk_alloc(tcp_segment_key_chunk);
- memcpy(new_tsk, tsk, sizeof(tcp_segment_key));
- new_tsk->seq = nxtseq;
- g_hash_table_insert(tcp_segment_table,new_tsk,new_tsk);
- }
+ /* add this segment as the first one for this new pdu */
+ fragment_add(tvb, deseg_offset, pinfo, msp->first_frame,
+ tcp_fragment_table,
+ 0,
+ nxtseq - deseg_seq,
+ LT_SEQ(nxtseq, msp->nxtpdu));
+ }
}
if (!called_dissector || pinfo->desegment_len != 0) {
pinfo->can_desegment=0;
pinfo->desegment_offset = 0;
pinfo->desegment_len = 0;
+
+ if(another_pdu_follows){
+ /* there was another pdu following this one. */
+ pinfo->can_desegment=2;
+ /* we also have to prevent the dissector from changing the
+ * PROTOCOL and INFO colums since what follows may be an
+ * incomplete PDU and we dont want it be changed back from
+ * <Protocol> to <TCP>
+ * XXX There is no good way to block the PROTOCOL column
+ * from being changed yet so we set the entire row unwritable.
+ */
+ col_set_fence(pinfo->cinfo, COL_INFO);
+ col_set_writable(pinfo->cinfo, FALSE);
+ offset += another_pdu_follows;
+ seq += another_pdu_follows;
+ goto again;
+ }
}
/*
guint plen;
guint length;
tvbuff_t *next_tvb;
+ proto_item *item=NULL;
while (tvb_reported_length_remaining(tvb, offset) != 0) {
/*
show_reported_bounds_error(tvb, pinfo, tree);
return;
}
+ /*
+ * Display the PDU length as a field
+ */
+ item=proto_tree_add_uint(pinfo->tcp_tree, hf_tcp_pdu_size, tvb, 0, 0, plen);
+ PROTO_ITEM_SET_GENERATED(item);
+
+
/* give a hint to TCP where the next PDU starts
* so that it can attempt to find it in case it starts
RETHROW;
}
CATCH(ReportedBoundsError) {
- show_reported_bounds_error(tvb, pinfo, tree);
+ show_reported_bounds_error(tvb, pinfo, tree);
}
ENDTRY;
proto_tree_add_boolean_hidden(opt_tree, hf_tcp_option_time_stamp, tvb,
offset, optlen, TRUE);
proto_tree_add_text(opt_tree, tvb, offset, optlen,
- "%s: tsval %u, tsecr %u", optp->name, tsv, tser);
+ "%s: TSval %u, TSecr %u", optp->name, tsv, tser);
tcp_info_append_uint(pinfo, "TSV", tsv);
tcp_info_append_uint(pinfo, "TSER", tser);
}
},
{
TCPOPT_TIMESTAMP,
- "Time stamp",
+ "Timestamps",
NULL,
FIXED_LENGTH,
TCPOLEN_TIMESTAMP,
{
tvbuff_t *next_tvb;
int low_port, high_port;
+ int save_desegment_offset;
+ guint32 save_desegment_len;
/* dont call subdissectors for keepalive or zerowindowprobes
- * eventhough tehy do contain payload "data"
+ * even though they do contain payload "data"
* keeaplives just contain garbage and zwp contain too little data (1 byte)
* so why bother.
*/
if (try_heuristic_first) {
/* do lookup with the heuristic subdissector table */
+ save_desegment_offset = pinfo->desegment_offset;
+ save_desegment_len = pinfo->desegment_len;
if (dissector_try_heuristic(heur_subdissector_list, next_tvb, pinfo, tree)){
pinfo->want_pdu_tracking -= !!(pinfo->want_pdu_tracking);
return TRUE;
}
+ /*
+ * They rejected the packet; make sure they didn't also request
+ * desegmentation (we could just override the request, but
+ * rejecting a packet *and* requesting desegmentation is a sign
+ * of the dissector's code needing clearer thought, so we fail
+ * so that the problem is made more obvious).
+ */
+ DISSECTOR_ASSERT(save_desegment_offset == pinfo->desegment_offset &&
+ save_desegment_len == pinfo->desegment_len);
}
/* Do lookups with the subdissector table.
if (!try_heuristic_first) {
/* do lookup with the heuristic subdissector table */
+ save_desegment_offset = pinfo->desegment_offset;
+ save_desegment_len = pinfo->desegment_len;
if (dissector_try_heuristic(heur_subdissector_list, next_tvb, pinfo, tree)){
pinfo->want_pdu_tracking -= !!(pinfo->want_pdu_tracking);
return TRUE;
}
+ /*
+ * They rejected the packet; make sure they didn't also request
+ * desegmentation (we could just override the request, but
+ * rejecting a packet *and* requesting desegmentation is a sign
+ * of the dissector's code needing clearer thought, so we fail
+ * so that the problem is made more obvious).
+ */
+ DISSECTOR_ASSERT(save_desegment_offset == pinfo->desegment_offset &&
+ save_desegment_len == pinfo->desegment_len);
}
/* Oh, well, we don't know this; dissect it as data. */
guint length_remaining;
gboolean desegment_ok;
struct tcpinfo tcpinfo;
- static struct tcpheader tcphstruct[4], *tcph;
- static int tcph_count=0;
+ struct tcpheader *tcph;
proto_item *tf_syn = NULL, *tf_fin = NULL, *tf_rst = NULL;
struct tcp_analysis *tcpd=NULL;
- tcph_count++;
- if(tcph_count>=4){
- tcph_count=0;
- }
- tcph=&tcphstruct[tcph_count];
+ tcph=ep_alloc(sizeof(struct tcpheader));
SET_ADDRESS(&tcph->ip_src, pinfo->src.type, pinfo->src.len, pinfo->src.data);
SET_ADDRESS(&tcph->ip_dst, pinfo->dst.type, pinfo->dst.len, pinfo->dst.data);
ti = proto_tree_add_item(tree, proto_tcp, tvb, 0, -1, FALSE);
}
tcp_tree = proto_item_add_subtree(ti, ett_tcp);
+ pinfo->tcp_tree=tcp_tree;
+
proto_tree_add_uint_format(tcp_tree, hf_tcp_srcport, tvb, offset, 2, tcph->th_sport,
"Source port: %s (%u)", get_tcp_port(tcph->th_sport), tcph->th_sport);
proto_tree_add_uint_format(tcp_tree, hf_tcp_dstport, tvb, offset + 2, 2, tcph->th_dport,
if (!pinfo->fragmented && !pinfo->in_error_pkt) {
if (reported_len < tcph->th_hlen) {
- proto_tree_add_text(tcp_tree, tvb, offset, 0,
+ proto_item *pi;
+ pi = proto_tree_add_text(tcp_tree, tvb, offset, 0,
"Short segment. Segment/fragment does not contain a full TCP header"
" (might be NMAP or someone else deliberately sending unusual packets)");
+ PROTO_ITEM_SET_GENERATED(pi);
+ expert_add_info_format(pinfo, pi, PI_MALFORMED, PI_WARN, "Short segment");
tcph->th_have_seglen = FALSE;
} else {
/* Compute the length of data in this segment. */
proto_tree_add_uint_format(tcp_tree, hf_tcp_hdr_len, tvb, offset + 12, 1, tcph->th_hlen,
"Header length: %u bytes", tcph->th_hlen);
tf = proto_tree_add_uint_format(tcp_tree, hf_tcp_flags, tvb, offset + 13, 1,
- tcph->th_flags, "Flags: 0x%04x (%s)", tcph->th_flags, flags);
+ tcph->th_flags, "Flags: 0x%02x (%s)", tcph->th_flags, flags);
field_tree = proto_item_add_subtree(tf, ett_tcp_flags);
proto_tree_add_boolean(field_tree, hf_tcp_flags_cwr, tvb, offset + 13, 1, tcph->th_flags);
proto_tree_add_boolean(field_tree, hf_tcp_flags_ecn, tvb, offset + 13, 1, tcph->th_flags);
tf_rst = proto_tree_add_boolean(field_tree, hf_tcp_flags_reset, tvb, offset + 13, 1, tcph->th_flags);
tf_syn = proto_tree_add_boolean(field_tree, hf_tcp_flags_syn, tvb, offset + 13, 1, tcph->th_flags);
tf_fin = proto_tree_add_boolean(field_tree, hf_tcp_flags_fin, tvb, offset + 13, 1, tcph->th_flags);
- if(tcp_relative_seq && (tcph->th_win!=real_window)){
+ if(tcp_relative_seq
+ && (tcph->th_win!=real_window)
+ && !(tcph->th_flags&TH_SYN) ){ /* SYNs are never scaled */
proto_tree_add_uint_format(tcp_tree, hf_tcp_window_size, tvb, offset + 14, 2, tcph->th_win, "Window size: %u (scaled)", tcph->th_win);
} else {
- proto_tree_add_uint(tcp_tree, hf_tcp_window_size, tvb, offset + 14, 2, tcph->th_win);
+ proto_tree_add_uint(tcp_tree, hf_tcp_window_size, tvb, offset + 14, 2, real_window);
}
}
if(tcph->th_flags & TH_SYN) {
if(tcph->th_flags & TH_ACK)
- expert_add_info_format(pinfo, tf_syn, PI_SEQUENCE, PI_CHAT, "Connection establish acknowledge (SYN+ACK): %s -> %s",
- get_tcp_port(tcph->th_sport), get_tcp_port(tcph->th_dport));
+ expert_add_info_format(pinfo, tf_syn, PI_SEQUENCE, PI_CHAT, "Connection establish acknowledge (SYN+ACK): server port %s",
+ get_tcp_port(tcph->th_sport));
else
- expert_add_info_format(pinfo, tf_syn, PI_SEQUENCE, PI_CHAT, "Connection establish request (SYN): %s -> %s",
- get_tcp_port(tcph->th_sport), get_tcp_port(tcph->th_dport));
+ expert_add_info_format(pinfo, tf_syn, PI_SEQUENCE, PI_CHAT, "Connection establish request (SYN): server port %s",
+ get_tcp_port(tcph->th_dport));
}
if(tcph->th_flags & TH_FIN)
- expert_add_info_format(pinfo, tf_fin, PI_SEQUENCE, PI_CHAT, "Connection finish (FIN): %s -> %s",
- get_tcp_port(tcph->th_sport), get_tcp_port(tcph->th_dport));
+ /* XXX - find a way to know the server port and output only that one */
+ expert_add_info_format(pinfo, tf_fin, PI_SEQUENCE, PI_CHAT, "Connection finish (FIN)");
if(tcph->th_flags & TH_RST)
- expert_add_info_format(pinfo, tf_rst, PI_SEQUENCE, PI_CHAT, "Connection reset (RST): %s -> %s",
- get_tcp_port(tcph->th_sport), get_tcp_port(tcph->th_dport));
+ /* XXX - find a way to know the server port and output only that one */
+ expert_add_info_format(pinfo, tf_rst, PI_SEQUENCE, PI_CHAT, "Connection reset (RST)");
/* Supply the sequence number of the first byte and of the first byte
after the segment. */
item = proto_tree_add_uint_format(tcp_tree, hf_tcp_checksum, tvb,
offset + 16, 2, th_sum,
- "Checksum: 0x%04x [incorrect, should be 0x%04x]", th_sum,
+ "Checksum: 0x%04x [incorrect, should be 0x%04x (maybe caused by checksum offloading?)]", th_sum,
in_cksum_shouldbe(th_sum, computed_cksum));
expert_add_info_format(pinfo, item, PI_CHECKSUM, PI_ERROR, "Bad checksum");
item = proto_tree_add_boolean(tcp_tree, hf_tcp_checksum_bad, tvb,
optlen = tcph->th_hlen - TCPH_MIN_LEN; /* length of options, in bytes */
tvb_ensure_bytes_exist(tvb, offset + 20, optlen);
if (tcp_tree != NULL) {
- tf = proto_tree_add_text(tcp_tree, tvb, offset + 20, optlen,
- "Options: (%u bytes)", optlen);
+ guint8 *p_options = tvb_get_ephemeral_string(tvb, offset + 20, optlen);
+ tf = proto_tree_add_bytes_format(tcp_tree, hf_tcp_options, tvb, offset + 20,
+ optlen, p_options, "Options: (%u bytes)", optlen);
field_tree = proto_item_add_subtree(tf, ett_tcp_options);
} else
field_tree = NULL;
}
tap_queue_packet(tcp_tap, pinfo, tcph);
+
+ /* A FIN packet might complete reassembly so we need to explicitely
+ * check for this here.
+ * If this segment completes reassembly we add the FIN as a final dummy
+ * byte to the reassembled PDU and check if reassembly completed successfully
+ */
+ if( (tcph->th_flags & TH_FIN)
+ && (tcpd->fwd->flags&TCP_FLOW_REASSEMBLE_UNTIL_FIN) ){
+ struct tcp_multisegment_pdu *msp;
+
+ /* find the most previous PDU starting before this sequence number */
+ msp=se_tree_lookup32_le(tcpd->fwd->multisegment_pdus, tcph->th_seq-1);
+ if(msp){
+ fragment_data *ipfd_head;
+
+ ipfd_head = fragment_add(tvb, offset-1, pinfo, msp->first_frame,
+ tcp_fragment_table,
+ tcph->th_seq - msp->seq,
+ 1,
+ FALSE );
+ if(ipfd_head){
+ tvbuff_t *next_tvb;
+
+ /* create a new TVB structure for desegmented data
+ * datalen-1 to strip the dummy FIN byte off
+ */
+ next_tvb = tvb_new_real_data(ipfd_head->data, ipfd_head->datalen-1, ipfd_head->datalen-1);
+
+ /* add this tvb as a child to the original one */
+ tvb_set_child_real_data_tvbuff(tvb, next_tvb);
+
+ /* add desegmented data to the data source list */
+ add_new_data_source(pinfo, next_tvb, "Reassembled TCP");
+
+ /* call the payload dissector
+ * but make sure we dont offer desegmentation any more
+ */
+ pinfo->can_desegment = 0;
+
+ process_tcp_payload(next_tvb, 0, pinfo, tree, tcp_tree, tcph->th_sport, tcph->th_dport, tcph->th_seq, nxtseq, FALSE, tcpd);
+
+ return;
+ }
+ }
+ }
+
/*
* XXX - what, if any, of this should we do if this is included in an
* error packet? It might be nice to see the details of the packet
{ &hf_tcp_checksum,
{ "Checksum", "tcp.checksum", FT_UINT16, BASE_HEX, NULL, 0x0,
- "", HFILL }},
+ "Details at: http://www.wireshark.org/docs/wsug_html_chunked/ChAdvChecksums.html", HFILL }},
{ &hf_tcp_checksum_bad,
{ "Bad Checksum", "tcp.checksum_bad", FT_BOOLEAN, BASE_NONE, NULL, 0x0,
- "", HFILL }},
+ "Maybe caused by checksum offloading, see: http://www.wireshark.org/docs/wsug_html_chunked/ChAdvChecksums.html", HFILL }},
{ &hf_tcp_analysis_flags,
{ "TCP Analysis Flags", "tcp.analysis.flags", FT_NONE, BASE_NONE, NULL, 0x0,
{ "Reassembled PDU in frame", "tcp.reassembled_in", FT_FRAMENUM, BASE_NONE, NULL, 0x0,
"The PDU that doesn't end in this segment is reassembled in this frame", HFILL }},
+ { &hf_tcp_options,
+ { "TCP Options", "tcp.options", FT_BYTES,
+ BASE_HEX, NULL, 0x0, "TCP Options", HFILL }},
+
{ &hf_tcp_option_mss,
{ "TCP MSS Option", "tcp.options.mss", FT_BOOLEAN,
BASE_NONE, NULL, 0x0, "TCP MSS Option", HFILL }},
{ &hf_tcp_pdu_time,
{ "Time until the last segment of this PDU", "tcp.pdu.time", FT_RELATIVE_TIME, BASE_NONE, NULL, 0x0,
"How long time has passed until the last frame of this PDU", HFILL}},
+ { &hf_tcp_pdu_size,
+ { "PDU Size", "tcp.pdu.size", FT_UINT32, BASE_DEC, NULL, 0x0,
+ "The size of this PDU", HFILL}},
+
{ &hf_tcp_pdu_last_frame,
{ "Last frame of this PDU", "tcp.pdu.last_frame", FT_FRAMENUM, BASE_NONE, NULL, 0x0,
"This is the last frame of the PDU starting in this segment", HFILL }},
"Try to decode a packet using an heuristic sub-dissector before using a sub-dissector registered to a specific port",
&try_heuristic_first);
- tcp_pdu_time_table=se_tree_create(SE_TREE_TYPE_RED_BLACK, "tcp_pdu_time_table");
- register_init_routine(tcp_analyze_seq_init);
- register_init_routine(tcp_desegment_init);
register_init_routine(tcp_fragment_init);
}