diff options
-rw-r--r-- | AUTHORS | 3 | ||||
-rw-r--r-- | packet-tcp.c | 175 | ||||
-rw-r--r-- | reassemble.c | 86 | ||||
-rw-r--r-- | reassemble.h | 18 |
4 files changed, 211 insertions, 71 deletions
@@ -1010,6 +1010,9 @@ Jirka Novak <j.novak[AT]netsystem.cz> { Ricardo Barroetaveña <rbarroetavena[AT]veufort.com> { Enhanced LDP support + Support TCP reassembly requiring multiple steps (e.g., + reassemble the PDU header to get the length of the PDU, then + reassemble the PDU based on that length) } Alan Harrison <alanharrison[AT]mail.com> { diff --git a/packet-tcp.c b/packet-tcp.c index 89cbca6892..59e9626b76 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.129 2002/02/03 21:44:52 guy Exp $ + * $Id: packet-tcp.c,v 1.130 2002/02/03 23:28:38 guy Exp $ * * Ethereal - Network traffic analyzer * By Gerald Combs <gerald@ethereal.com> @@ -365,28 +365,20 @@ desegment_tcp(tvbuff_t *tvb, packet_info *pinfo, int offset, proto_tree *st = NULL; proto_item *si = NULL; - /* first we show a tree with all segments */ - si = proto_tree_add_text(tcp_tree, tvb, 0, 0, - "Segments"); - st = proto_item_add_subtree(si, ett_tcp_segments); - for(ipfd=ipfd_head->next; ipfd; ipfd=ipfd->next){ - proto_tree_add_text(st, tvb, 0, 0, - "Frame:%u seq#:%u-%u [%u-%u]", - ipfd->frame, - tsk->start_seq + ipfd->offset, - tsk->start_seq + ipfd->offset + ipfd->len - 1, - ipfd->offset, - ipfd->offset + ipfd->len - 1); - } - /* + * 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(nxtseq >= (tsk->start_seq + tsk->tot_len)){ - /* ok, lest call subdissector with desegmented data */ + /* + * OK, this is the last segment. + * Let's call the subdissector with the desegmented + * data. + */ tvbuff_t *next_tvb; + int old_len; /* create a new TVB structure for desegmented data */ next_tvb = tvb_new_real_data(ipfd_head->data, @@ -407,56 +399,119 @@ desegment_tcp(tvbuff_t *tvb, packet_info *pinfo, int offset, sport, dport); called_dissector = TRUE; - /* Did the subdissector ask us to desegment some more - data? This means that the data at the beginning - of this segment completed a higher-level PDU, - but the data at the end of this segment started - a higher-level PDU but didn't complete it. - - If so we have to create some structures in our - table but this is something we only do the first - time we see this packet. - */ - if(pinfo->desegment_len) { - if (!pinfo->fd->flags.visited) - must_desegment = TRUE; + /* + * OK, did the subdissector think it was completely + * desegmented, or does it think we need even more + * data? + */ + 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; /* - * The stuff we couldn't dissect must have - * come from this segment, so it's all in - * "tvb". - * - * "pinfo->desegment_offset" is relative - * to the beginning of "next_tvb"; - * we want an offset relative to the - * beginning of "tvb". - * - * First, compute the offset relative to - * the *end* of "next_tvb" - i.e., the number - * of bytes before the end of "next_tvb" - * at which the subdissector stopped. - * That's the length of "next_tvb" minus - * the offset, relative to the beginning - * of "next_tvb, at which the subdissector - * stopped. + * "desegment_len" isn't 0, so it needs more + * data for something - and "desegment_offset" + * is before "old_len", so it needs more data + * to dissect the stuff we thought was + * completely desegmented (as opposed to the + * stuff at the beginning being completely + * desegmented, but the stuff at the end + * being a new higher-level PDU that also + * needs desegmentation). */ - deseg_offset = - ipfd_head->datalen - pinfo->desegment_offset; + fragment_set_partial_reassembly(pinfo,tsk->start_seq,tcp_fragment_table); + tsk->tot_len = tvb_reported_length(next_tvb) + pinfo->desegment_len; /* - * "tvb" and "next_tvb" end at the same byte - * of data, so the offset relative to the - * end of "next_tvb" of the byte at which - * we stopped is also the offset relative - * to the end of "tvb" of the byte at which - * we stopped. - * - * Convert that back into an offset relative - * to the beginninng of "tvb", by taking - * the length of "tvb" and subtracting the - * offset relative to the end. + * 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); + 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); + } else { + /* + * The subdissector thought it was completely + * desegmented (although the stuff at the + * end may, in turn, require desegmentation), + * so we show a tree with all segments. */ - deseg_offset = tvb_length(tvb) - deseg_offset; + si = proto_tree_add_text(tcp_tree, tvb, 0, 0, + "Segments"); + st = proto_item_add_subtree(si, ett_tcp_segments); + for(ipfd=ipfd_head->next; ipfd; ipfd=ipfd->next){ + proto_tree_add_text(st, tvb, 0, 0, + "Frame:%u seq#:%u-%u [%u-%u]", + ipfd->frame, + tsk->start_seq + ipfd->offset, + tsk->start_seq + ipfd->offset + ipfd->len-1, + ipfd->offset, + ipfd->offset + ipfd->len - 1); + } + + /* Did the subdissector ask us to desegment + some more data? This means that the data + at the beginning of this segment completed + a higher-level PDU, but the data at the + end of this segment started a higher-level + PDU but didn't complete it. + + If so, we have to create some structures + in our table, but this is something we + only do the first time we see this packet. + */ + if(pinfo->desegment_len) { + if (!pinfo->fd->flags.visited) + must_desegment = TRUE; + + /* The stuff we couldn't dissect + must have come from this segment, + so it's all in "tvb". + + "pinfo->desegment_offset" is + relative to the beginning of + "next_tvb"; we want an offset + relative to the beginning of "tvb". + + First, compute the offset relative + to the *end* of "next_tvb" - i.e., + the number of bytes before the end + of "next_tvb" at which the + subdissector stopped. That's the + length of "next_tvb" minus the + offset, relative to the beginning + of "next_tvb, at which the + subdissector stopped. + */ + deseg_offset = + ipfd_head->datalen - pinfo->desegment_offset; + + /* "tvb" and "next_tvb" end at the + same byte of data, so the offset + relative to the end of "next_tvb" + of the byte at which we stopped + is also the offset relative to + the end of "tvb" of the byte at + which we stopped. + + Convert that back into an offset + relative to the beginninng of + "tvb", by taking the length of + "tvb" and subtracting the offset + relative to the end. + */ + deseg_offset=tvb_reported_length(tvb) - deseg_offset; + } } } } diff --git a/reassemble.c b/reassemble.c index 40a919f507..b043c524e7 100644 --- a/reassemble.c +++ b/reassemble.c @@ -1,7 +1,7 @@ /* reassemble.c * Routines for {fragment,segment} reassembly * - * $Id: reassemble.c,v 1.8 2002/01/21 07:36:48 guy Exp $ + * $Id: reassemble.c,v 1.9 2002/02/03 23:28:38 guy Exp $ * * Ethereal - Network traffic analyzer * By Gerald Combs <gerald@ethereal.com> @@ -45,12 +45,12 @@ static int fragment_init_count = 200; #define LINK_FRAG(fd_head,fd) \ { fragment_data *fd_i; \ /* add fragment to list, keep list sorted */ \ - for(fd_i=fd_head;fd_i->next;fd_i=fd_i->next){ \ - if( (fd->offset) < (fd_i->next->offset) ) \ + for(fd_i=(fd_head);fd_i->next;fd_i=fd_i->next){ \ + if( ((fd)->offset) < (fd_i->next->offset) ) \ break; \ } \ - fd->next=fd_i->next; \ - fd_i->next=fd; \ + (fd)->next=fd_i->next; \ + fd_i->next=(fd); \ } static gint @@ -126,7 +126,7 @@ free_all_fragments(gpointer key_arg, gpointer value, gpointer user_data) g_free((gpointer)key->dst.data); for (fd_head = value; fd_head != NULL; fd_head = fd_head->next) { - if (fd_head->data) + if(fd_head->data && !(fd_head->flags&FD_NOT_MALLOCED)) g_free(fd_head->data); } @@ -213,7 +213,8 @@ fragment_delete(packet_info *pinfo, guint32 id, GHashTable *fragment_table) fragment_data *tmp_fd; tmp_fd=fd->next; - g_free(fd->data); + if( !(fd->flags&FD_NOT_MALLOCED) ) + g_free(fd->data); g_mem_chunk_free(fragment_data_chunk, fd); fd=tmp_fd; } @@ -277,6 +278,32 @@ fragment_set_tot_len(packet_info *pinfo, guint32 id, GHashTable *fragment_table, return; } + +/* This function will set the partial reassembly flag for a fh. + When this function is called, the fh MUST already exist, i.e. + the fh MUST be created by the initial call to fragment_add() before + this function is called. + Also note that this function MUST be called to indicate a fh will be + extended (increase the already stored data) +*/ + +void +fragment_set_partial_reassembly(packet_info *pinfo, guint32 id, GHashTable *fragment_table) +{ + fragment_data *fd_head; + fragment_key key; + + /* create key to search hash with */ + key.src = pinfo->src; + key.dst = pinfo->dst; + key.id = id; + + fd_head = g_hash_table_lookup(fragment_table, &key); + + if(fd_head){ + fd_head->flags |= FD_PARTIAL_REASSEMBLY; + } +} /* * This function adds a new fragment to the fragment hash table. * If this is the first fragment seen for this datagram, a new entry @@ -290,6 +317,13 @@ fragment_set_tot_len(packet_info *pinfo, guint32 id, GHashTable *fragment_table, * * This function assumes frag_offset being a byte offset into the defragment * packet. + * + * 01-2002 + * Once the fh is defragmented (= FD_DEFRAGMENTED set), it can be + * extended using the FD_PARTIAL_REASSEMBLY flag. This flag should be set + * using fragment_set_partial_reassembly() before calling fragment_add + * with the new fragment. FD_TOOLONGFRAGMENT and FD_MULTIPLETAILS flags + * are lowered when a new extension process is started. */ fragment_data * fragment_add(tvbuff_t *tvb, int offset, packet_info *pinfo, guint32 id, @@ -301,6 +335,7 @@ fragment_add(tvbuff_t *tvb, int offset, packet_info *pinfo, guint32 id, fragment_data *fd; fragment_data *fd_i; guint32 max, dfpos; + unsigned char *old_data; /* create key to search hash with */ key.src = pinfo->src; @@ -356,6 +391,24 @@ fragment_add(tvbuff_t *tvb, int offset, packet_info *pinfo, guint32 id, fd->len = frag_data_len; fd->data = NULL; + /* + * If it was already defragmented and this new fragment goes beyond + * data limits, set flag in already empty fds & point old fds to malloc'ed data. + */ + if(fd_head->flags & FD_DEFRAGMENTED && (frag_offset+frag_data_len) >= fd_head->datalen && + fd_head->flags & FD_PARTIAL_REASSEMBLY){ + for(fd_i=fd_head->next; fd_i; fd_i=fd_i->next){ + if( !fd_i->data ) { + fd_i->data = fd_head->data + fd_i->offset; + fd_i->flags |= FD_NOT_MALLOCED; + } + fd_i->flags &= (~FD_TOOLONGFRAGMENT) & (~FD_MULTIPLETAILS); + } + fd_head->flags ^= FD_DEFRAGMENTED|FD_PARTIAL_REASSEMBLY; + fd_head->flags &= (~FD_TOOLONGFRAGMENT) & (~FD_MULTIPLETAILS); + fd_head->datalen=0; + } + if (!more_frags) { /* * This is the tail fragment in the sequence. @@ -448,6 +501,8 @@ fragment_add(tvbuff_t *tvb, int offset, packet_info *pinfo, guint32 id, if (max > (fd_head->datalen)) { + /*XXX not sure if current fd was the TOOLONG*/ + /*XXX is it fair to flag current fd*/ /* oops, too long fragment detected */ fd->flags |= FD_TOOLONGFRAGMENT; fd_head->flags |= FD_TOOLONGFRAGMENT; @@ -457,6 +512,8 @@ fragment_add(tvbuff_t *tvb, int offset, packet_info *pinfo, guint32 id, /* we have received an entire packet, defragment it and * free all fragments */ + /* store old data just in case */ + old_data=fd_head->data; fd_head->data = g_malloc(max); /* add all data fragments */ @@ -472,15 +529,24 @@ fragment_add(tvbuff_t *tvb, int offset, packet_info *pinfo, guint32 id, fd_i->flags |= FD_OVERLAPCONFLICT; fd_head->flags |= FD_OVERLAPCONFLICT; } - } - memcpy(fd_head->data+fd_i->offset,fd_i->data,fd_i->len); - g_free(fd_i->data); + } + /* dfpos is always >= than fd_i->offset */ + /* No gaps can exist here, max_loop(above) does this */ + if( fd_i->offset+fd_i->len > dfpos ) + memcpy(fd_head->data+dfpos, fd_i->data+(dfpos-fd_i->offset), + fd_i->len-(dfpos-fd_i->offset)); + if( fd_i->flags & FD_NOT_MALLOCED ) + fd_i->flags ^= FD_NOT_MALLOCED; + else + g_free(fd_i->data); fd_i->data=NULL; dfpos=MAX(dfpos,(fd_i->offset+fd_i->len)); } } + if( old_data ) + g_free(old_data); /* mark this packet as defragmented. allows us to skip any trailing fragments */ fd_head->flags |= FD_DEFRAGMENTED; diff --git a/reassemble.h b/reassemble.h index cdfd15b07c..15f95195e9 100644 --- a/reassemble.h +++ b/reassemble.h @@ -1,7 +1,7 @@ /* reassemble.h * Declarations of outines for {fragment,segment} reassembly * - * $Id: reassemble.h,v 1.3 2001/12/15 05:40:32 guy Exp $ + * $Id: reassemble.h,v 1.4 2002/02/03 23:28:38 guy Exp $ * * Ethereal - Network traffic analyzer * By Gerald Combs <gerald@ethereal.com> @@ -41,6 +41,12 @@ /* fragment contains data past the end of the datagram */ #define FD_TOOLONGFRAGMENT 0x0010 +/* fragment data not alloced, fd->data pointing to fd_head->data+fd->offset */ +#define FD_NOT_MALLOCED 0x0020 + +/* this flag is used to request fragment_add to continue the reassembly process */ +#define FD_PARTIAL_REASSEMBLY 0x0040 + /* fragment offset is indicated by sequence number and not byte offset into the defragmented packet */ #define FD_BLOCKSEQUENCE 0x0100 @@ -96,6 +102,16 @@ fragment_data *fragment_add_seq(tvbuff_t *tvb, int offset, packet_info *pinfo, void fragment_set_tot_len(packet_info *pinfo, guint32 id, GHashTable *fragment_table, guint32 tot_len); +/* + * This function will set the partial reassembly flag(FD_PARTIAL_REASSEMBLY) for a fh. + * When this function is called, the fh MUST already exist, i.e. + * the fh MUST be created by the initial call to fragment_add() before + * this function is called. Also note that this function MUST be called to indicate + * a fh will be extended (increase the already stored data). After calling this function, + * and if FD_DEFRAGMENTED is set, the reassembly process will be continued. + */ +void +fragment_set_partial_reassembly(packet_info *pinfo, guint32 id, GHashTable *fragment_table); /* This function is used to check if there is partial or completed reassembly state * matching this packet. I.e. Are there reassembly going on or not for this packet? |