aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--epan/dissectors/packet-tcp.c285
1 files changed, 69 insertions, 216 deletions
diff --git a/epan/dissectors/packet-tcp.c b/epan/dissectors/packet-tcp.c
index 0e87cd829d..223c277da7 100644
--- a/epan/dissectors/packet-tcp.c
+++ b/epan/dissectors/packet-tcp.c
@@ -1037,109 +1037,6 @@ tcp_fragment_init(void)
/* Enable desegmenting of TCP streams */
static gboolean tcp_desegment = TRUE;
-static GHashTable *tcp_segment_table = NULL;
-static GMemChunk *tcp_segment_key_chunk = NULL;
-static int tcp_segment_init_count = 200;
-static GMemChunk *tcp_segment_address_chunk = NULL;
-static int tcp_segment_address_init_count = 500;
-
-typedef struct _tcp_segment_key {
- /* for own bookkeeping inside packet-tcp.c */
- address *src;
- address *dst;
- guint32 seq;
- /* xxx */
- guint16 sport;
- guint16 dport;
- guint32 start_seq;
- guint32 tot_len;
- guint32 first_frame;
-} tcp_segment_key;
-
-static gboolean
-free_all_segments(gpointer key_arg, gpointer value _U_, gpointer user_data _U_)
-{
- tcp_segment_key *key = key_arg;
-
- if((key->src)&&(key->src->data)){
- g_free((gpointer)key->src->data);
- key->src->data=NULL;
- }
-
- if((key->dst)&&(key->dst->data)){
- g_free((gpointer)key->dst->data);
- key->dst->data=NULL;
- }
-
- return TRUE;
-}
-
-static guint
-tcp_segment_hash(gconstpointer k)
-{
- const tcp_segment_key *key = (const tcp_segment_key *)k;
-
- return key->seq+key->sport;
-}
-
-static gint
-tcp_segment_equal(gconstpointer k1, gconstpointer k2)
-{
- const tcp_segment_key *key1 = (const tcp_segment_key *)k1;
- const tcp_segment_key *key2 = (const tcp_segment_key *)k2;
-
- return ( ( (key1->seq==key2->seq)
- &&(ADDRESSES_EQUAL(key1->src, key2->src))
- &&(ADDRESSES_EQUAL(key1->dst, key2->dst))
- &&(key1->sport==key2->sport)
- &&(key1->dport==key2->dport)
- ) ? TRUE:FALSE);
-}
-
-static void
-tcp_desegment_init(void)
-{
- /*
- * Free this before freeing any memory chunks; those
- * chunks contain data we'll look at in "free_all_segments()".
- */
- if(tcp_segment_table){
- g_hash_table_foreach_remove(tcp_segment_table,
- free_all_segments, NULL);
- g_hash_table_destroy(tcp_segment_table);
- tcp_segment_table = NULL;
- }
-
- if(tcp_segment_key_chunk){
- g_mem_chunk_destroy(tcp_segment_key_chunk);
- tcp_segment_key_chunk = NULL;
- }
- if(tcp_segment_address_chunk){
- g_mem_chunk_destroy(tcp_segment_address_chunk);
- tcp_segment_address_chunk = NULL;
- }
-
- /* dont allocate any hash table or memory chunks unless the user
- really uses this option
- */
- if(!tcp_desegment){
- return;
- }
-
- tcp_segment_table = g_hash_table_new(tcp_segment_hash,
- tcp_segment_equal);
-
- tcp_segment_key_chunk = g_mem_chunk_new("tcp_segment_key_chunk",
- sizeof(tcp_segment_key),
- tcp_segment_init_count*sizeof(tcp_segment_key),
- G_ALLOC_ONLY);
-
- tcp_segment_address_chunk = g_mem_chunk_new("tcp_segment_address_chunk",
- sizeof(address),
- tcp_segment_address_init_count*sizeof(address),
- G_ALLOC_ONLY);
-}
-
static void
desegment_tcp(tvbuff_t *tvb, packet_info *pinfo, int offset,
guint32 seq, guint32 nxtseq,
@@ -1149,16 +1046,16 @@ desegment_tcp(tvbuff_t *tvb, packet_info *pinfo, int offset,
{
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;
+ int another_pdu_follows = 0;
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=NULL;
/*
* Initialize these to assume no desegmentation.
@@ -1176,46 +1073,30 @@ desegment_tcp(tvbuff_t *tvb, packet_info *pinfo, int offset,
*/
deseg_offset = offset;
- /* First we must check if this TCP segment should be desegmented.
- This is only to check if we should desegment this packet,
- so we dont spend time doing COPY_ADDRESS/g_free.
- We just "borrow" some address structures from pinfo instead. Cheaper.
- */
- old_tsk.src = &pinfo->src;
- old_tsk.dst = &pinfo->dst;
- old_tsk.sport = sport;
- old_tsk.dport = dport;
- old_tsk.seq = seq;
- tsk = g_hash_table_lookup(tcp_segment_table, &old_tsk);
-
- if(tsk){
- /* OK, this segment was found, which means it continues
- a higher-level PDU. This means we must desegment it.
- Add it to the defragmentation lists.
+ /* find the most previous PDU starting before this sequence number */
+ msp=se_tree_lookup32_le(tcpd->fwd->multisegment_pdus, seq);
+ 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
@@ -1252,17 +1133,16 @@ desegment_tcp(tvbuff_t *tvb, packet_info *pinfo, int offset,
ipfd_head = NULL;
}
+
/* is it completely desegmented? */
if(ipfd_head){
- fragment_data *ipfd;
-
/*
* Yes, we think it is.
* We only call subdissector for the last segment.
* Note that the last segment may include more than what
* we needed.
*/
- if(GE_SEQ(nxtseq, tsk->start_seq + tsk->tot_len)){
+ if(ipfd_head->reassembled_in==pinfo->fd->num){
/*
* OK, this is the last segment.
* Let's call the subdissector with the desegmented
@@ -1285,7 +1165,7 @@ desegment_tcp(tvbuff_t *tvb, packet_info *pinfo, int offset,
* Supply the sequence number of the first of the
* reassembled bytes.
*/
- tcpinfo->seq = tsk->start_seq;
+ tcpinfo->seq = msp->seq;
/* indicate that this is reassembled data */
tcpinfo->is_reassembled = TRUE;
@@ -1303,8 +1183,6 @@ desegment_tcp(tvbuff_t *tvb, packet_info *pinfo, int offset,
old_len=(int)(tvb_reported_length(next_tvb)-tvb_reported_length_remaining(tvb, offset));
if(pinfo->desegment_len &&
pinfo->desegment_offset<=old_len){
- tcp_segment_key *new_tsk;
-
/*
* "desegment_len" isn't 0, so it needs more
* data for something - and "desegment_offset"
@@ -1316,26 +1194,8 @@ desegment_tcp(tvbuff_t *tvb, packet_info *pinfo, int offset,
* being a new higher-level PDU that also
* needs desegmentation).
*/
- fragment_set_partial_reassembly(pinfo,tsk->first_frame,tcp_fragment_table);
- tsk->tot_len = tvb_reported_length(next_tvb) + pinfo->desegment_len;
-
- /*
- * Update tsk structure.
- * Can ask ->next->next because at least there's a hdr and one
- * entry in fragment_add()
- */
- for(ipfd=ipfd_head->next; ipfd->next; ipfd=ipfd->next){
- old_tsk.seq = tsk->start_seq + ipfd->offset;
- new_tsk = g_hash_table_lookup(tcp_segment_table, &old_tsk);
- if (new_tsk)
- new_tsk->tot_len = tsk->tot_len;
- }
-
- /* this is the next segment in the sequence we want */
- new_tsk = g_mem_chunk_alloc(tcp_segment_key_chunk);
- memcpy(new_tsk, tsk, sizeof(tcp_segment_key));
- new_tsk->seq = nxtseq;
- g_hash_table_insert(tcp_segment_table,new_tsk,new_tsk);
+ fragment_set_partial_reassembly(pinfo,msp->first_frame,tcp_fragment_table);
+ msp->nxtpdu=msp->seq+tvb_reported_length(next_tvb) + pinfo->desegment_len;
} else {
/*
* Show the stuff in this TCP segment as
@@ -1423,8 +1283,6 @@ desegment_tcp(tvbuff_t *tvb, packet_info *pinfo, int offset,
}
if (must_desegment) {
- tcp_segment_key *tsk, *new_tsk;
-
/*
* The sequence number at which the stuff to be desegmented
* starts is the sequence number of the byte at an offset
@@ -1437,48 +1295,24 @@ desegment_tcp(tvbuff_t *tvb, packet_info *pinfo, int offset,
*/
deseg_seq = seq + (deseg_offset - offset);
- /*
- * XXX - how do we detect out-of-order transmissions?
- * We can't just check for "nxtseq" being greater than
- * "tsk->start_seq"; for now, we check for the difference
- * being less than a megabyte, but this is a really
- * gross hack - we really need to handle out-of-order
- * transmissions correctly.
- */
if ((nxtseq - deseg_seq) <= 1024*1024) {
- /* OK, subdissector wants us to desegment
- some data before it can process it. Add
- what remains of this packet and set
- up next packet/sequence number as well.
-
- 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);
+ if(!pinfo->fd->flags.visited){
+ msp=se_alloc(sizeof(struct tcp_multisegment_pdu));
+ msp->nxtpdu=nxtseq + pinfo->desegment_len;
+ msp->seq=deseg_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, msp->seq, (void *)msp);
+
+
+ /* 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));
+ }
}
}
@@ -1533,6 +1367,26 @@ desegment_tcp(tvbuff_t *tvb, packet_info *pinfo, int offset,
pinfo->can_desegment=0;
pinfo->desegment_offset = 0;
pinfo->desegment_len = 0;
+
+
+ /* there was another pdu following this one. */
+ if(another_pdu_follows){
+ 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);
+ desegment_tcp(tvb, pinfo, offset+another_pdu_follows,
+ seq+another_pdu_follows, nxtseq,
+ sport, dport,
+ tree, tcp_tree,
+ tcpd);
+ }
}
/*
@@ -2954,7 +2808,6 @@ proto_register_tcp(void)
"Try to decode a packet using an heuristic sub-dissector before using a sub-dissector registered to a specific port",
&try_heuristic_first);
- register_init_routine(tcp_desegment_init);
register_init_routine(tcp_fragment_init);
}