diff options
author | Guy Harris <guy@alum.mit.edu> | 2001-12-15 05:40:32 +0000 |
---|---|---|
committer | Guy Harris <guy@alum.mit.edu> | 2001-12-15 05:40:32 +0000 |
commit | c139b9138af10ff41fc61d1a1f94c55f19b3094b (patch) | |
tree | 4f67ed8597e677ba121723c801a663eba8923602 | |
parent | 7286bb4c0e2ee50c639cfdb98b3055e5389e957b (diff) |
From Ronnie Sahlberg: new infrastructure to reassemble packets where
fragments are identified by block sequence numbers and not byte offsets.
svn path=/trunk/; revision=4398
-rw-r--r-- | reassemble.c | 243 | ||||
-rw-r--r-- | reassemble.h | 16 |
2 files changed, 256 insertions, 3 deletions
diff --git a/reassemble.c b/reassemble.c index 5af1495417..1a021f79c6 100644 --- a/reassemble.c +++ b/reassemble.c @@ -1,7 +1,7 @@ /* reassemble.c * Routines for {fragment,segment} reassembly * - * $Id: reassemble.c,v 1.5 2001/11/24 09:36:40 guy Exp $ + * $Id: reassemble.c,v 1.6 2001/12/15 05:40:31 guy Exp $ * * Ethereal - Network traffic analyzer * By Gerald Combs <gerald@ethereal.com> @@ -53,7 +53,6 @@ static int fragment_init_count = 200; fd_i->next=fd; \ } - static gint fragment_equal(gconstpointer k1, gconstpointer k2) { @@ -252,6 +251,10 @@ fragment_get(packet_info *pinfo, guint32 id, GHashTable *fragment_table) * Such protocols might fragment_add with a more_frags==TRUE for every fragment * and just tell the reassembly engine the expected total length of the reassembled data * using fragment_set_tot_len immediately after doing fragment_add for the first packet. + * + * note that for FD_BLOCKSEQUENCE tot_len is the index for the tail fragment. + * i.e. since the block numbers start at 0, if we specify tot_len==2, that + * actually means we want to defragment 3 blocks, block 0, 1 and 2. */ void fragment_set_tot_len(packet_info *pinfo, guint32 id, GHashTable *fragment_table, @@ -285,6 +288,8 @@ fragment_set_tot_len(packet_info *pinfo, guint32 id, GHashTable *fragment_table, * Returns a pointer to the head of the fragment data list if we have all the * fragments, NULL otherwise. * + * This function assumes frag_offset being a byte offset into the defragment + * packet. */ fragment_data * fragment_add(tvbuff_t *tvb, int offset, packet_info *pinfo, guint32 id, @@ -482,3 +487,237 @@ fragment_add(tvbuff_t *tvb, int offset, packet_info *pinfo, guint32 id, return fd_head; } + + + +/* + * This function adds a new fragment to the fragment hash table. + * If this is the first fragment seen for this datagram, a new entry + * is created in the hash table, otherwise this fragment is just added + * to the linked list of fragments for this packet. + * The list of fragments for a specific datagram is kept sorted for + * easier handling. + * + * Returns a pointer to the head of the fragment data list if we have all the + * fragments, NULL otherwise. + * + * This function assumes frag_offset being a block sequence number. + * the bsn for the first block is 0. + */ +fragment_data * +fragment_add_seq(tvbuff_t *tvb, int offset, packet_info *pinfo, guint32 id, + GHashTable *fragment_table, guint32 frag_offset, + guint32 frag_data_len, gboolean more_frags) +{ + fragment_key key, *new_key; + fragment_data *fd_head; + fragment_data *fd; + fragment_data *fd_i; + fragment_data *last_fd; + guint32 max, dfpos, size; + + /* 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); + + /* have we already seen this frame ?*/ + if (pinfo->fd->flags.visited) { + if (fd_head != NULL && fd_head->flags & FD_DEFRAGMENTED) { + return fd_head; + } else { + return NULL; + } + } + + if (fd_head==NULL){ + /* not found, this must be the first snooped fragment for this + * packet. Create list-head. + */ + fd_head=g_mem_chunk_alloc(fragment_data_chunk); + /* head/first structure in list only holds no other data than + * 'datalen' then we don't have to change the head of the list + * even if we want to keep it sorted + */ + fd_head->next=NULL; + fd_head->datalen=0; + fd_head->offset=0; + fd_head->len=0; + fd_head->flags=FD_BLOCKSEQUENCE; + fd_head->data=NULL; + + /* + * We're going to use the key to insert the fragment, + * so allocate a structure for it, and copy the + * addresses, allocating new buffers for the address + * data. + */ + new_key = g_mem_chunk_alloc(fragment_key_chunk); + COPY_ADDRESS(&new_key->src, &key.src); + COPY_ADDRESS(&new_key->dst, &key.dst); + new_key->id = key.id; + g_hash_table_insert(fragment_table, new_key, fd_head); + } + + /* create new fd describing this fragment */ + fd = g_mem_chunk_alloc(fragment_data_chunk); + fd->next = NULL; + fd->flags = 0; + fd->frame = pinfo->fd->num; + fd->offset = frag_offset; + fd->len = frag_data_len; + fd->data = NULL; + + if (!more_frags) { + /* + * This is the tail fragment in the sequence. + */ + if (fd_head->datalen) { + /* ok we have already seen other tails for this packet + * it might be a duplicate. + */ + if (fd_head->datalen != fd->offset ){ + /* Oops, this tail indicates a different packet + * len than the previous ones. Somethings wrong + */ + fd->flags |= FD_MULTIPLETAILS; + fd_head->flags |= FD_MULTIPLETAILS; + } + } else { + /* this was the first tail fragment, now we know the + * length of the packet + */ + fd_head->datalen = fd->offset; + } + } + + /* If the packet is already defragmented, this MUST be an overlap. + * The entire defragmented packet is in fd_head->data + * Even if we have previously defragmented this packet, we still check + * check it. Someone might play overlap and TTL games. + */ + if (fd_head->flags & FD_DEFRAGMENTED) { + fd->flags |= FD_OVERLAP; + fd_head->flags |= FD_OVERLAP; + + /* make sure its not too long */ + if (fd->offset > fd_head->datalen) { + fd->flags |= FD_TOOLONGFRAGMENT; + fd_head->flags |= FD_TOOLONGFRAGMENT; + LINK_FRAG(fd_head,fd); + return (fd_head); + } + /* make sure it doesnt conflict with previous data */ + dfpos=0; + for (fd_i=fd_head->next;fd_i->offset!=fd->offset;fd_i=fd_i->next) { + dfpos += fd_i->len; + } + if(fd_i->datalen!=fd->datalen){ + fd->flags |= FD_OVERLAPCONFLICT; + fd_head->flags |= FD_OVERLAPCONFLICT; + LINK_FRAG(fd_head,fd); + return (fd_head); + } + if ( memcmp(fd_head->data+dfpos, + tvb_get_ptr(tvb,offset,fd->len),fd->len) ){ + fd->flags |= FD_OVERLAPCONFLICT; + fd_head->flags |= FD_OVERLAPCONFLICT; + LINK_FRAG(fd_head,fd); + return (fd_head); + } + /* it was just an overlap, link it and return */ + LINK_FRAG(fd_head,fd); + return (fd_head); + } + + /* If we have reached this point, the packet is not defragmented yet. + * Save all payload in a buffer until we can defragment. + * XXX - what if we didn't capture the entire fragment due + * to a too-short snapshot length? + */ + fd->data = g_malloc(fd->len); + tvb_memcpy(tvb, fd->data, offset, fd->len); + LINK_FRAG(fd_head,fd); + + + if( !(fd_head->datalen) ){ + /* if we dont know the datalen, there are still missing + * packets. Cheaper than the check below. + */ + return NULL; + } + + + /* check if we have received the entire fragment + * this is easy since the list is sorted and the head is faked. + */ + max = 0; + for(fd_i=fd_head->next;fd_i;fd_i=fd_i->next) { + if ( fd_i->offset==max ){ + max++; + } + } + /* max will now be datalen+1 if all fragments have been seen */ + + if (max <= fd_head->datalen) { + /* we have not received all packets yet */ + return NULL; + } + + + if (max > (fd_head->datalen+1)) { + /* oops, too long fragment detected */ + fd->flags |= FD_TOOLONGFRAGMENT; + fd_head->flags |= FD_TOOLONGFRAGMENT; + } + + + /* we have received an entire packet, defragment it and + * free all fragments + */ + size=0; + last_fd=NULL; + for(fd_i=fd_head->next;fd_i;fd_i=fd_i->next) { + if(last_fd && last_fd->offset!=fd_i->offset){ + size+=fd_i->datalen; + } + last_fd=fd_i; + } + fd_head->data = g_malloc(size); + + /* add all data fragments */ + last_fd=NULL; + for (dfpos=0,fd_i=fd_head->next;fd_i;fd_i=fd_i->next) { + if (fd_i->len) { + if(!last_fd || last_fd->offset!=fd_i->offset){ + memcpy(fd_head->data+dfpos,fd_i->data,fd_i->len); + dfpos += fd_i->len; + } else { + /* duplicate/retransmission/overlap */ + if( (last_fd->len!=fd_i->datalen) + || memcmp(last_fd->data, fd_i->data, last_fd->len) ){ + fd->flags |= FD_OVERLAPCONFLICT; + fd_head->flags |= FD_OVERLAPCONFLICT; + } + } + last_fd=fd_i; + } + } + + /* we have defragmented the pdu, now free all fragments*/ + for (fd_i=fd_head->next;fd_i;fd_i=fd_i->next) { + if(fd_i->data){ + g_free(fd_i->data); + fd_i->data=NULL; + } + } + + /* mark this packet as defragmented. + allows us to skip any trailing fragments */ + fd_head->flags |= FD_DEFRAGMENTED; + + return fd_head; +} + diff --git a/reassemble.h b/reassemble.h index 3d3faba8ec..cdfd15b07c 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.2 2001/11/24 09:36:40 guy Exp $ + * $Id: reassemble.h,v 1.3 2001/12/15 05:40:32 guy Exp $ * * Ethereal - Network traffic analyzer * By Gerald Combs <gerald@ethereal.com> @@ -41,6 +41,10 @@ /* fragment contains data past the end of the datagram */ #define FD_TOOLONGFRAGMENT 0x0010 +/* fragment offset is indicated by sequence number and not byte offset + into the defragmented packet */ +#define FD_BLOCKSEQUENCE 0x0100 + typedef struct _fragment_data { struct _fragment_data *next; guint32 frame; @@ -76,8 +80,18 @@ fragment_data *fragment_add(tvbuff_t *tvb, int offset, packet_info *pinfo, guint32 id, GHashTable *fragment_table, guint32 frag_offset, guint32 frag_data_len, gboolean more_frags); +/* same as fragment_add() but this one assumes frag_offset is a block + sequence number. note that frag_offset is 0 for the first fragment. */ +fragment_data *fragment_add_seq(tvbuff_t *tvb, int offset, packet_info *pinfo, + guint32 id, GHashTable *fragment_table, guint32 frag_offset, + guint32 frag_data_len, gboolean more_frags); + /* to specify how much to reassemble, for fragmentation where last fragment can not be * identified by flags or such. + * note that for FD_BLOCKSEQUENCE tot_len is the index for the tail fragment. + * i.e. since the block numbers start at 0, if we specify tot_len==2, that + * actually means we want to defragment 3 blocks, block 0, 1 and 2. + * */ void fragment_set_tot_len(packet_info *pinfo, guint32 id, GHashTable *fragment_table, |