aboutsummaryrefslogtreecommitdiffstats
path: root/reassemble.c
diff options
context:
space:
mode:
authorGuy Harris <guy@alum.mit.edu>2001-06-08 06:27:16 +0000
committerGuy Harris <guy@alum.mit.edu>2001-06-08 06:27:16 +0000
commitc9da803a083959710644b51b2063a58e16854a81 (patch)
tree7c0357f6cc77b6e5400dce6519619e41c1a8388d /reassemble.c
parent988785407fd46b441f75f44178f1bc55ddbae5c1 (diff)
Move the fragment reassembly code into "reassemble.c" and
"reassemble.h", and remove IPv4 dependencies from it. Use it for OSI CLNP segment reassembly as well. svn path=/trunk/; revision=3525
Diffstat (limited to 'reassemble.c')
-rw-r--r--reassemble.c371
1 files changed, 371 insertions, 0 deletions
diff --git a/reassemble.c b/reassemble.c
new file mode 100644
index 0000000000..30dad7024b
--- /dev/null
+++ b/reassemble.c
@@ -0,0 +1,371 @@
+/* reassemble.c
+ * Routines for {fragment,segment} reassembly
+ *
+ * $Id: reassemble.c,v 1.1 2001/06/08 06:27:16 guy Exp $
+ *
+ * Ethereal - Network traffic analyzer
+ * By Gerald Combs <gerald@ethereal.com>
+ * Copyright 1998 Gerald Combs
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * 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.
+ */
+
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+#include "packet.h"
+
+#include "reassemble.h"
+
+typedef struct _fragment_key {
+ address src;
+ address dst;
+ guint32 id;
+} fragment_key;
+
+static GMemChunk *fragment_key_chunk = NULL;
+static GMemChunk *fragment_data_chunk = NULL;
+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) ) \
+ break; \
+ } \
+ fd->next=fd_i->next; \
+ fd_i->next=fd; \
+ }
+
+
+static gint
+fragment_equal(gconstpointer k1, gconstpointer k2)
+{
+ fragment_key* key1 = (fragment_key*) k1;
+ fragment_key* key2 = (fragment_key*) k2;
+
+ return ( ( (ADDRESSES_EQUAL(&key1->src, &key2->src)) &&
+ (ADDRESSES_EQUAL(&key1->dst, &key2->dst)) &&
+ (key1->id == key2->id)
+ ) ?
+ TRUE : FALSE);
+}
+
+static guint
+fragment_hash(gconstpointer k)
+{
+ fragment_key* key = (fragment_key*) k;
+ guint hash_val;
+ int i;
+
+ hash_val = 0;
+ for (i = 0; i < key->src.len; i++)
+ hash_val += key->src.data[i];
+ for (i = 0; i < key->dst.len; i++)
+ hash_val += key->dst.data[i];
+ hash_val += key->id;
+
+ return hash_val;
+}
+
+/*
+ * For a hash table entry, free the address data to which the key refers
+ * and the fragment data to which the value refers.
+ * (The actual key and value structures get freed by "reassemble_init()".)
+ */
+static gboolean
+free_all_fragments(gpointer key_arg, gpointer value, gpointer user_data)
+{
+ fragment_key *key = key_arg;
+ fragment_data *fd_head;
+
+ /*
+ * Grr. I guess the theory here is that freeing
+ * something sure as heck modifies it, so you
+ * want to ban attempts to free it, but, alas,
+ * if we make the "data" field of an "address"
+ * structure not a "const", the compiler whines if
+ * we try to make it point into the data for a packet,
+ * as that's a "const" array (and should be, as dissectors
+ * shouldn't trash it).
+ *
+ * So we cast the complaint into oblivion, and rely on
+ * the fact that these addresses are known to have had
+ * their data mallocated, i.e. they don't point into,
+ * say, the middle of the data for a packet.
+ */
+ g_free((gpointer)key->src.data);
+ g_free((gpointer)key->dst.data);
+
+ for (fd_head = value; fd_head != NULL; fd_head = fd_head->next) {
+ if (fd_head->data)
+ g_free(fd_head->data);
+ }
+
+ return TRUE;
+}
+
+/*
+ * Initialize a fragment table.
+ */
+void
+fragment_table_init(GHashTable **fragment_table)
+{
+ if (*fragment_table != NULL) {
+ /*
+ * The fragment hash table exists.
+ *
+ * Remove all entries and free fragment data for
+ * each entry. (The key and value data is freed
+ * by "reassemble_init()".)
+ */
+ g_hash_table_foreach_remove(*fragment_table,
+ free_all_fragments, NULL);
+ } else {
+ /* The fragment table does not exist. Create it */
+ *fragment_table = g_hash_table_new(fragment_hash,
+ fragment_equal);
+ }
+}
+
+/*
+ * Free up all space allocated for fragment keys and data.
+ */
+void
+reassemble_init(void)
+{
+ if (fragment_key_chunk != NULL)
+ g_mem_chunk_destroy(fragment_key_chunk);
+ if (fragment_data_chunk != NULL)
+ g_mem_chunk_destroy(fragment_data_chunk);
+ fragment_key_chunk = g_mem_chunk_new("fragment_key_chunk",
+ sizeof(fragment_key),
+ fragment_init_count * sizeof(fragment_key),
+ G_ALLOC_ONLY);
+ fragment_data_chunk = g_mem_chunk_new("fragment_data_chunk",
+ sizeof(fragment_data),
+ fragment_init_count * sizeof(fragment_data),
+ G_ALLOC_ONLY);
+}
+
+/*
+ * 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.
+ */
+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)
+{
+ fragment_key key, *new_key;
+ fragment_data *fd_head;
+ fragment_data *fd;
+ fragment_data *fd_i;
+ guint32 max, dfpos;
+
+ /* 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=0;
+ 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 + fd->len) ){
+ /* 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 + fd->len;
+ }
+ }
+
+
+
+
+ /* 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->len > 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 */
+ if ( memcmp(fd_head->data+fd->offset,
+ 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) &&
+ ((fd_i->offset+fd_i->len)>max) ){
+ max = fd_i->offset+fd_i->len;
+ }
+ }
+
+ if (max < (fd_head->datalen)) {
+ /* we have not received all packets yet */
+ return NULL;
+ }
+
+
+ if (max > (fd_head->datalen)) {
+ /* 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
+ */
+ fd_head->data = g_malloc(max);
+
+ /* add all data fragments */
+ for (dfpos=0,fd_i=fd_head;fd_i;fd_i=fd_i->next) {
+ if (fd_i->len) {
+ if (fd_i->offset < dfpos) {
+ fd_i->flags |= FD_OVERLAP;
+ fd_head->flags |= FD_OVERLAP;
+ if ( memcmp(fd_head->data+fd_i->offset,
+ fd_i->data,
+ MIN(fd_i->len,(dfpos-fd_i->offset))
+ ) ){
+ 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);
+ fd_i->data=NULL;
+
+ dfpos=MAX(dfpos,(fd_i->offset+fd_i->len));
+ }
+ }
+
+ /* mark this packet as defragmented.
+ allows us to skip any trailing fragments */
+ fd_head->flags |= FD_DEFRAGMENTED;
+
+ return fd_head;
+}