Get rid of TestBigEndian and AC_C_BIGENDIAN.
[metze/wireshark/wip.git] / epan / reassemble.c
index 4a80d04fbbdddaa5fe628b3b64f84cda742c6f94..02ba1a2db4716af75da29744ef1740e9336015ad 100644 (file)
 /* reassemble.c
  * Routines for {fragment,segment} reassembly
  *
- * $Id$
- *
  * 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
- * 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.
+ * SPDX-License-Identifier: GPL-2.0-or-later
  */
 
-#ifdef HAVE_CONFIG_H
-# include "config.h"
-#endif
+#include "config.h"
 
 #include <string.h>
 
 #include <epan/packet.h>
-
+#include <epan/exceptions.h>
 #include <epan/reassemble.h>
+#include <epan/tvbuff-int.h>
 
-#include <epan/emem.h>
-
-#include <epan/dissectors/packet-dcerpc.h>
+#include <wsutil/str_util.h>
 
-typedef struct _fragment_key {
+/*
+ * Functions for reassembly tables where the endpoint addresses, and a
+ * fragment ID, are used as the key.
+ */
+typedef struct _fragment_addresses_key {
        address src;
        address dst;
        guint32 id;
-} fragment_key;
+} fragment_addresses_key;
 
-typedef struct _dcerpc_fragment_key {
-       address src;
-       address dst;
-       guint32 id;
-       e_uuid_t act_id;
-} dcerpc_fragment_key;
-
-#if GLIB_CHECK_VERSION(2,10,0)
-#else
-static GMemChunk *fragment_key_chunk = NULL;
-static GMemChunk *fragment_data_chunk = NULL;
-static GMemChunk *dcerpc_fragment_key_chunk = NULL;
-static int fragment_init_count = 200;
-#endif
+GList* reassembly_table_list = NULL;
 
-static void LINK_FRAG(fragment_data *fd_head,fragment_data *fd)
+static guint
+fragment_addresses_hash(gconstpointer k)
 {
-       fragment_data *fd_i;
+       const fragment_addresses_key* key = (const fragment_addresses_key*) k;
+       guint hash_val;
+/*
+       int 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;
+       hash_val = 0;
+
+/*     More than likely: in most captures src and dst addresses are the
+       same, and would hash the same.
+       We only use id as the hash as an optimization.
+
+       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;
 }
 
-/* copy a fragment key to heap store to insert in the hash */
-static void *fragment_key_copy(const void *k)
+static gint
+fragment_addresses_equal(gconstpointer k1, gconstpointer k2)
 {
-       const fragment_key* key = (const fragment_key*) k;
-#if GLIB_CHECK_VERSION(2,10,0)
-       fragment_key *new_key = g_slice_new(fragment_key);
-#else
-       fragment_key *new_key = g_mem_chunk_alloc(fragment_key_chunk);
-#endif
+       const fragment_addresses_key* key1 = (const fragment_addresses_key*) k1;
+       const fragment_addresses_key* key2 = (const fragment_addresses_key*) k2;
 
-       COPY_ADDRESS(&new_key->src, &key->src);
-       COPY_ADDRESS(&new_key->dst, &key->dst);
-       new_key->id = key->id;
-       return new_key;
+       /*
+        * key.id is the first item to compare since it's the item most
+        * likely to differ between sessions, thus short-circuiting
+        * the comparison of addresses.
+        */
+       return (key1->id == key2->id) &&
+              (addresses_equal(&key1->src, &key2->src)) &&
+              (addresses_equal(&key1->dst, &key2->dst));
 }
 
-/* copy a dcerpc fragment key to heap store to insert in the hash */
-static void *dcerpc_fragment_key_copy(const void *k)
+/*
+ * Create a fragment key for temporary use; it can point to non-
+ * persistent data, and so must only be used to look up and
+ * delete entries, not to add them.
+ */
+static gpointer
+fragment_addresses_temporary_key(const packet_info *pinfo, const guint32 id,
+                                const void *data _U_)
 {
-       const dcerpc_fragment_key* key = (const dcerpc_fragment_key*) k;
+       fragment_addresses_key *key = g_slice_new(fragment_addresses_key);
 
-#if GLIB_CHECK_VERSION(2,10,0)
-       dcerpc_fragment_key *new_key = g_slice_new(dcerpc_fragment_key);
-#else
-       dcerpc_fragment_key *new_key = g_mem_chunk_alloc(dcerpc_fragment_key_chunk);
-#endif
+       /*
+        * Do a shallow copy of the addresses.
+        */
+       copy_address_shallow(&key->src, &pinfo->src);
+       copy_address_shallow(&key->dst, &pinfo->dst);
+       key->id = id;
+
+       return (gpointer)key;
+}
 
-       COPY_ADDRESS(&new_key->src, &key->src);
-       COPY_ADDRESS(&new_key->dst, &key->dst);
-       new_key->id = key->id;
-       new_key->act_id = key->act_id;
+/*
+ * Create a fragment key for permanent use; it must point to persistent
+ * data, so that it can be used to add entries.
+ */
+static gpointer
+fragment_addresses_persistent_key(const packet_info *pinfo, const guint32 id,
+                                 const void *data _U_)
+{
+       fragment_addresses_key *key = g_slice_new(fragment_addresses_key);
+
+       /*
+        * Do a deep copy of the addresses.
+        */
+       copy_address(&key->src, &pinfo->src);
+       copy_address(&key->dst, &pinfo->dst);
+       key->id = id;
 
-       return new_key;
+       return (gpointer)key;
 }
 
+static void
+fragment_addresses_free_temporary_key(gpointer ptr)
+{
+       fragment_addresses_key *key = (fragment_addresses_key *)ptr;
+       g_slice_free(fragment_addresses_key, key);
+}
 
-static gint
-fragment_equal(gconstpointer k1, gconstpointer k2)
+static void
+fragment_addresses_free_persistent_key(gpointer ptr)
 {
-       const fragment_key* key1 = (const fragment_key*) k1;
-       const fragment_key* key2 = (const fragment_key*) k2;
+       fragment_addresses_key *key = (fragment_addresses_key *)ptr;
 
-       /*key.id is the first item to compare since item is most
-         likely to differ between sessions, thus shortcircuiting
-         the comparasion of addresses.
-       */
-       return ( ( (key1->id    == key2->id) &&
-                  (ADDRESSES_EQUAL(&key1->src, &key2->src)) &&
-                  (ADDRESSES_EQUAL(&key1->dst, &key2->dst))
-                ) ?
-                TRUE : FALSE);
+       if(key){
+               /*
+                * Free up the copies of the addresses from the old key.
+                */
+               free_address(&key->src);
+               free_address(&key->dst);
+
+               g_slice_free(fragment_addresses_key, key);
+       }
 }
 
+const reassembly_table_functions
+addresses_reassembly_table_functions = {
+       fragment_addresses_hash,
+       fragment_addresses_equal,
+       fragment_addresses_temporary_key,
+       fragment_addresses_persistent_key,
+       fragment_addresses_free_temporary_key,
+       fragment_addresses_free_persistent_key
+};
+
+/*
+ * Functions for reassembly tables where the endpoint addresses and ports,
+ * and a fragment ID, are used as the key.
+ */
+typedef struct _fragment_addresses_ports_key {
+       address src_addr;
+       address dst_addr;
+       guint32 src_port;
+       guint32 dst_port;
+       guint32 id;
+} fragment_addresses_ports_key;
+
 static guint
-fragment_hash(gconstpointer k)
+fragment_addresses_ports_hash(gconstpointer k)
 {
-       const fragment_key* key = (const fragment_key*) k;
+       const fragment_addresses_ports_key* key = (const fragment_addresses_ports_key*) k;
        guint hash_val;
 /*
        int i;
@@ -134,14 +170,16 @@ fragment_hash(gconstpointer k)
 
        hash_val = 0;
 
-/*     More than likely: in most captures src and dst addresses are the
-       same, and would hash the same.
+/*     More than likely: in most captures src and dst addresses and ports
+       are the same, and would hash the same.
        We only use id as the hash as an optimization.
 
        for (i = 0; i < key->src.len; i++)
-               hash_val += key->src.data[i];
+               hash_val += key->src_addr.data[i];
        for (i = 0; i < key->dst.len; i++)
-               hash_val += key->dst.data[i];
+               hash_val += key->dst_addr.data[i];
+       hash_val += key->src_port;
+       hash_val += key->dst_port;
 */
 
        hash_val += key->id;
@@ -150,38 +188,101 @@ fragment_hash(gconstpointer k)
 }
 
 static gint
-dcerpc_fragment_equal(gconstpointer k1, gconstpointer k2)
+fragment_addresses_ports_equal(gconstpointer k1, gconstpointer k2)
 {
-       const dcerpc_fragment_key* key1 = (const dcerpc_fragment_key*) k1;
-       const dcerpc_fragment_key* key2 = (const dcerpc_fragment_key*) k2;
+       const fragment_addresses_ports_key* key1 = (const fragment_addresses_ports_key*) k1;
+       const fragment_addresses_ports_key* key2 = (const fragment_addresses_ports_key*) k2;
 
-       /*key.id is the first item to compare since item is most
-         likely to differ between sessions, thus shortcircuiting
-         the comparison of addresses.
-       */
-       return (((key1->id == key2->id)
-                 && (ADDRESSES_EQUAL(&key1->src, &key2->src))
-                 && (ADDRESSES_EQUAL(&key1->dst, &key2->dst))
-                 && (memcmp (&key1->act_id, &key2->act_id, sizeof (e_uuid_t)) == 0))
-                ? TRUE : FALSE);
+       /*
+        * key.id is the first item to compare since it's the item most
+        * likely to differ between sessions, thus short-circuiting
+        * the comparison of addresses and ports.
+        */
+       return (key1->id == key2->id) &&
+              (addresses_equal(&key1->src_addr, &key2->src_addr)) &&
+              (addresses_equal(&key1->dst_addr, &key2->dst_addr)) &&
+              (key1->src_port == key2->src_port) &&
+              (key1->dst_port == key2->dst_port);
 }
 
-static guint
-dcerpc_fragment_hash(gconstpointer k)
+/*
+ * Create a fragment key for temporary use; it can point to non-
+ * persistent data, and so must only be used to look up and
+ * delete entries, not to add them.
+ */
+static gpointer
+fragment_addresses_ports_temporary_key(const packet_info *pinfo, const guint32 id,
+                                      const void *data _U_)
 {
-       const dcerpc_fragment_key* key = (const dcerpc_fragment_key*) k;
-       guint hash_val;
+       fragment_addresses_ports_key *key = g_slice_new(fragment_addresses_ports_key);
 
-       hash_val = 0;
+       /*
+        * Do a shallow copy of the addresses.
+        */
+       copy_address_shallow(&key->src_addr, &pinfo->src);
+       copy_address_shallow(&key->dst_addr, &pinfo->dst);
+       key->src_port = pinfo->srcport;
+       key->dst_port = pinfo->destport;
+       key->id = id;
 
-       hash_val += key->id;
-       hash_val += key->act_id.Data1;
-       hash_val += key->act_id.Data2 << 16;
-       hash_val += key->act_id.Data3;
+       return (gpointer)key;
+}
 
-       return hash_val;
+/*
+ * Create a fragment key for permanent use; it must point to persistent
+ * data, so that it can be used to add entries.
+ */
+static gpointer
+fragment_addresses_ports_persistent_key(const packet_info *pinfo,
+                                       const guint32 id, const void *data _U_)
+{
+       fragment_addresses_ports_key *key = g_slice_new(fragment_addresses_ports_key);
+
+       /*
+        * Do a deep copy of the addresses.
+        */
+       copy_address(&key->src_addr, &pinfo->src);
+       copy_address(&key->dst_addr, &pinfo->dst);
+       key->src_port = pinfo->srcport;
+       key->dst_port = pinfo->destport;
+       key->id = id;
+
+       return (gpointer)key;
+}
+
+static void
+fragment_addresses_ports_free_temporary_key(gpointer ptr)
+{
+       fragment_addresses_ports_key *key = (fragment_addresses_ports_key *)ptr;
+       g_slice_free(fragment_addresses_ports_key, key);
 }
 
+static void
+fragment_addresses_ports_free_persistent_key(gpointer ptr)
+{
+       fragment_addresses_ports_key *key = (fragment_addresses_ports_key *)ptr;
+
+       if(key){
+               /*
+                * Free up the copies of the addresses from the old key.
+                */
+               free_address(&key->src_addr);
+               free_address(&key->dst_addr);
+
+               g_slice_free(fragment_addresses_ports_key, key);
+       }
+}
+
+const reassembly_table_functions
+addresses_ports_reassembly_table_functions = {
+       fragment_addresses_ports_hash,
+       fragment_addresses_ports_equal,
+       fragment_addresses_ports_temporary_key,
+       fragment_addresses_ports_persistent_key,
+       fragment_addresses_ports_free_temporary_key,
+       fragment_addresses_ports_free_persistent_key
+};
+
 typedef struct _reassembled_key {
        guint32 id;
        guint32 frame;
@@ -208,398 +309,377 @@ reassembled_hash(gconstpointer k)
        return key->frame;
 }
 
+static void
+reassembled_key_free(gpointer ptr)
+{
+       g_slice_free(reassembled_key, (reassembled_key *)ptr);
+}
+
 /*
  * For a fragment hash table entry, free the associated fragments.
- * If slices are used (GLIB >= 2.10) the entry value (fd_chain) is
- * freed herein and the entry is freed when fragment_free_key()
- * [or dcerpc_fragment_free_key()] is called (as a consequence of
- * returning TRUE from this function).
- * If mem_chunks are used, free the address data to which the key
- * refers; the the actual key and value structures get freed
- * by "reassemble_cleanup()").
+ * The entry value (fd_chain) is freed herein and the entry is freed
+ * when the key freeing routine is called (as a consequence of returning
+ * TRUE from this function).
  */
 static gboolean
 free_all_fragments(gpointer key_arg _U_, gpointer value, gpointer user_data _U_)
 {
-       fragment_data *fd_head, *tmp_fd;
+       fragment_head *fd_head;
+       fragment_item *tmp_fd;
 
-#if GLIB_CHECK_VERSION(2,10,0)
-       /* If Glib version => 2.10 we do g_hash_table_new_full() and supply a function
-        * to free the key and the addresses.
-        */
-#else
-       fragment_key *key = key_arg;
-       /*
-        * 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_hash_table_new_full() was used to supply a function
+        * to free the key and anything to which it points
         */
-       g_free((gpointer)key->src.data);
-       g_free((gpointer)key->dst.data);
-#endif
-       for (fd_head = value; fd_head != NULL; fd_head = tmp_fd) {
+       for (fd_head = (fragment_head *)value; fd_head != NULL; fd_head = tmp_fd) {
                tmp_fd=fd_head->next;
 
-               if(fd_head->data && !(fd_head->flags&FD_NOT_MALLOCED))
-                       g_free(fd_head->data);
-#if GLIB_CHECK_VERSION(2,10,0)
-               g_slice_free(fragment_data, fd_head);
-#endif
+               if(fd_head->tvb_data && !(fd_head->flags&FD_SUBSET_TVB))
+                       tvb_free(fd_head->tvb_data);
+               g_slice_free(fragment_item, fd_head);
        }
 
        return TRUE;
 }
 
 /* ------------------------- */
-static fragment_data *new_head(const guint32 flags)
+static fragment_head *new_head(const guint32 flags)
 {
-       fragment_data *fd_head;
+       fragment_head *fd_head;
        /* If 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
        */
-#if GLIB_CHECK_VERSION(2,10,0)
-       fd_head=g_slice_new0(fragment_data);
-#else
-       fd_head=g_mem_chunk_alloc0(fragment_data_chunk);
-#endif
+       fd_head=g_slice_new0(fragment_head);
 
        fd_head->flags=flags;
        return fd_head;
 }
 
+#define FD_VISITED_FREE 0xffff
+
 /*
  * For a reassembled-packet hash table entry, free the fragment data
- * to which the value refers.
- * (Pre glib 2.10:The actual value structures get freed by "reassemble_cleanup()".)
- * http://www.wireshark.org/lists/wireshark-dev/200910/msg00074.html
+ * to which the value refers and also the key itself.
  */
 static gboolean
-free_all_reassembled_fragments(gpointer key_arg, gpointer value,
-                                  gpointer user_data _U_)
+free_all_reassembled_fragments(gpointer key_arg _U_, gpointer value,
+                                  gpointer user_data)
 {
-       fragment_data *fd_head, *tmp_fd;
-       reassembled_key *key = (reassembled_key *)key_arg;
-
-       for (fd_head = value; fd_head != NULL; fd_head = tmp_fd) {
-               tmp_fd=fd_head->next;
-               if(fd_head->data && !(fd_head->flags&FD_NOT_MALLOCED)) {
-                       g_free(fd_head->data);
+       GPtrArray *allocated_fragments = (GPtrArray *) user_data;
+       fragment_head *fd_head;
 
-                       /*
-                        * A reassembled packet is inserted into the
-                        * hash table once for every frame that made
-                        * up the reassembled packet; clear the data
-                        * pointer so that we only free the data the
-                        * first time we see it.
-                        */
-                       fd_head->data = NULL;
-               }
-#if GLIB_CHECK_VERSION(2,10,0)
-               if(key->frame == fd_head->reassembled_in){
-                       g_slice_free(fragment_data, fd_head);
+       for (fd_head = (fragment_head *)value; fd_head != NULL; fd_head = fd_head->next) {
+               /*
+                * A reassembled packet is inserted into the
+                * hash table once for every frame that made
+                * up the reassembled packet; add first seen
+                * fragments to array and later free them in
+                * free_fragments()
+                */
+               if (fd_head->flags != FD_VISITED_FREE) {
+                       if (fd_head->flags & FD_SUBSET_TVB)
+                               fd_head->tvb_data = NULL;
+                       g_ptr_array_add(allocated_fragments, fd_head);
+                       fd_head->flags = FD_VISITED_FREE;
                }
-#endif
        }
 
        return TRUE;
 }
 
-#if GLIB_CHECK_VERSION(2,10,0)
 static void
-fragment_free_key(void *ptr)
+free_fragments(gpointer data, gpointer user_data _U_)
 {
-       fragment_key *key = (fragment_key *)ptr;
+       fragment_item *fd_head = (fragment_item *) data;
 
-       if(key){
-               /*
-                * Free up the copies of the addresses from the old key.
-                */
-               g_free((gpointer)key->src.data);
-               g_free((gpointer)key->dst.data);
-
-               g_slice_free(fragment_key, key);
-       }
+       if (fd_head->tvb_data)
+               tvb_free(fd_head->tvb_data);
+       g_slice_free(fragment_item, fd_head);
 }
 
-static void
-dcerpc_fragment_free_key(void *ptr)
+typedef struct register_reassembly_table {
+       reassembly_table *table;
+       const reassembly_table_functions *funcs;
+} register_reassembly_table_t;
+
+/*
+ * Register a reassembly table.
+ */
+void
+reassembly_table_register(reassembly_table *table,
+                     const reassembly_table_functions *funcs)
 {
-       dcerpc_fragment_key *key = (dcerpc_fragment_key *)ptr;
+       register_reassembly_table_t* reg_table;
 
-       if(key){
-               /*
-                * Free up the copies of the addresses from the old key.
-                */
-               g_free((gpointer)key->src.data);
-               g_free((gpointer)key->dst.data);
+       DISSECTOR_ASSERT(table);
+       DISSECTOR_ASSERT(funcs);
 
-               g_slice_free(dcerpc_fragment_key, key);
-       }
-}
-#endif
+       reg_table = g_new(register_reassembly_table_t,1);
 
-#if GLIB_CHECK_VERSION(2,10,0)
-static void
-reassembled_key_free(void *ptr)
-{
-       reassembled_key *key = (reassembled_key *)ptr;
+       reg_table->table = table;
+       reg_table->funcs = funcs;
 
-       g_slice_free(reassembled_key, key);
+       reassembly_table_list = g_list_prepend(reassembly_table_list, reg_table);
 }
-#endif
 
 /*
- * Initialize a fragment table.
+ * Initialize a reassembly table, with specified functions.
  */
 void
-fragment_table_init(GHashTable **fragment_table)
+reassembly_table_init(reassembly_table *table,
+                     const reassembly_table_functions *funcs)
 {
-       if (*fragment_table != NULL) {
+       if (table->temporary_key_func == NULL)
+               table->temporary_key_func = funcs->temporary_key_func;
+       if (table->persistent_key_func == NULL)
+               table->persistent_key_func = funcs->persistent_key_func;
+       if (table->free_temporary_key_func == NULL)
+               table->free_temporary_key_func = funcs->free_temporary_key_func;
+       if (table->fragment_table != NULL) {
                /*
                 * The fragment hash table exists.
                 *
                 * Remove all entries and free fragment data for each entry.
                 *
-                * If slices are used (GLIB >= 2.10)
-                * the keys are freed by calling fragment_free_key()
-                * and the values are freed in free_all_fragments().
-                *
-                * If mem_chunks are used, the key and value data
-                * are freed by "reassemble_cleanup()". free_all_fragments()
-                * will free the adrress data associated with the key
+                * The keys, and anything to which they point, are freed by
+                * calling the table's key freeing function.  The values
+                * are freed in free_all_fragments().
                 */
-               g_hash_table_foreach_remove(*fragment_table,
-                               free_all_fragments, NULL);
+               g_hash_table_foreach_remove(table->fragment_table,
+                                           free_all_fragments, NULL);
        } else {
-#if GLIB_CHECK_VERSION(2,10,0)
-               /* The fragment table does not exist. Create it */
-               *fragment_table = g_hash_table_new_full(fragment_hash,
-                                                       fragment_equal, fragment_free_key, NULL);
-#else
                /* The fragment table does not exist. Create it */
-               *fragment_table = g_hash_table_new(fragment_hash,
-                               fragment_equal);
-#endif
+               table->fragment_table = g_hash_table_new_full(funcs->hash_func,
+                   funcs->equal_func, funcs->free_persistent_key_func, NULL);
        }
-}
 
-void
-dcerpc_fragment_table_init(GHashTable **fragment_table)
-{
-       if (*fragment_table != NULL) {
+       if (table->reassembled_table != NULL) {
+               GPtrArray *allocated_fragments;
+
                /*
-                * The fragment hash table exists.
-                *
-                * Remove all entries and free fragment data for each entry.
-                *
-                * If slices are used (GLIB >= 2.10)
-                * the keys are freed by calling dcerpc_fragment_free_key()
-                * and the values are freed in free_all_fragments().
+                * The reassembled-packet hash table exists.
                 *
-                * If mem_chunks are used, the key and value data
-                * are freed by "reassemble_cleanup()". free_all_fragments()
-                * will free the adrress data associated with the key
+                * Remove all entries and free reassembled packet
+                * data and key for each entry.
                 */
-                  g_hash_table_foreach_remove(*fragment_table,
-                                                  free_all_fragments, NULL);
+
+               allocated_fragments = g_ptr_array_new();
+               g_hash_table_foreach_remove(table->reassembled_table,
+                               free_all_reassembled_fragments, allocated_fragments);
+
+               g_ptr_array_foreach(allocated_fragments, free_fragments, NULL);
+               g_ptr_array_free(allocated_fragments, TRUE);
        } else {
-#if GLIB_CHECK_VERSION(2,10,0)
-                  /* The fragment table does not exist. Create it */
-               *fragment_table = g_hash_table_new_full(dcerpc_fragment_hash,
-                                                       dcerpc_fragment_equal, dcerpc_fragment_free_key, NULL);
-#else
                /* The fragment table does not exist. Create it */
-                  *fragment_table = g_hash_table_new(dcerpc_fragment_hash,
-                                                  dcerpc_fragment_equal);
-#endif
+               table->reassembled_table = g_hash_table_new_full(reassembled_hash,
+                   reassembled_equal, reassembled_key_free, NULL);
        }
 }
 
 /*
- * Initialize a reassembled-packet table.
+ * Destroy a reassembly table.
  */
 void
-reassembled_table_init(GHashTable **reassembled_table)
+reassembly_table_destroy(reassembly_table *table)
 {
-       if (*reassembled_table != NULL) {
+       /*
+        * Clear the function pointers.
+        */
+       table->temporary_key_func = NULL;
+       table->persistent_key_func = NULL;
+       table->free_temporary_key_func = NULL;
+       if (table->fragment_table != NULL) {
+               /*
+                * The fragment hash table exists.
+                *
+                * Remove all entries and free fragment data for each entry.
+                *
+                * The keys, and anything to which they point, are freed by
+                * calling the table's key freeing function.  The values
+                * are freed in free_all_fragments().
+                */
+               g_hash_table_foreach_remove(table->fragment_table,
+                                           free_all_fragments, NULL);
+
+               /*
+                * Now destroy the hash table.
+                */
+               g_hash_table_destroy(table->fragment_table);
+               table->fragment_table = NULL;
+       }
+       if (table->reassembled_table != NULL) {
+               GPtrArray *allocated_fragments;
+
                /*
                 * The reassembled-packet hash table exists.
                 *
                 * Remove all entries and free reassembled packet
-                * data for each entry.  (The key data is freed
-                * by "reassemble_cleanup()".)
+                * data and key for each entry.
                 */
-               g_hash_table_foreach_remove(*reassembled_table,
-                               free_all_reassembled_fragments, NULL);
-       } else {
-               /* The fragment table does not exist. Create it */
-#if GLIB_CHECK_VERSION(2,10,0)
-               *reassembled_table = g_hash_table_new_full(reassembled_hash,
-                                                       reassembled_equal, reassembled_key_free, NULL);
-#else
-               *reassembled_table = g_hash_table_new(reassembled_hash,
-                               reassembled_equal);
-#endif
+
+               allocated_fragments = g_ptr_array_new();
+               g_hash_table_foreach_remove(table->reassembled_table,
+                               free_all_reassembled_fragments, allocated_fragments);
+
+               g_ptr_array_foreach(allocated_fragments, free_fragments, NULL);
+               g_ptr_array_free(allocated_fragments, TRUE);
+
+               /*
+                * Now destroy the hash table.
+                */
+               g_hash_table_destroy(table->reassembled_table);
+               table->reassembled_table = NULL;
        }
 }
 
 /*
- * Free up all space allocated for fragment keys and data and
- * reassembled keys.
+ * Look up an fd_head in the fragment table, optionally returning the key
+ * for it.
  */
-void
-reassemble_cleanup(void)
-{
-#if GLIB_CHECK_VERSION(2,10,0)
-#else
-       if (fragment_key_chunk != NULL)
-               g_mem_chunk_destroy(fragment_key_chunk);
-       if (dcerpc_fragment_key_chunk != NULL)
-               g_mem_chunk_destroy(dcerpc_fragment_key_chunk);
-       if (fragment_data_chunk != NULL)
-               g_mem_chunk_destroy(fragment_data_chunk);
-
-       fragment_key_chunk = NULL;
-       dcerpc_fragment_key_chunk = NULL;
-       fragment_data_chunk = NULL;
-#endif
+static fragment_head *
+lookup_fd_head(reassembly_table *table, const packet_info *pinfo,
+              const guint32 id, const void *data, gpointer *orig_keyp)
+{
+       gpointer key;
+       gpointer value;
+
+       /* Create key to search hash with */
+       key = table->temporary_key_func(pinfo, id, data);
+
+       /*
+        * Look up the reassembly in the fragment table.
+        */
+       if (!g_hash_table_lookup_extended(table->fragment_table, key, orig_keyp,
+                                         &value))
+               value = NULL;
+       /* Free the key */
+       table->free_temporary_key_func(key);
+
+       return (fragment_head *)value;
 }
 
-void
-reassemble_init(void)
-{
-#if GLIB_CHECK_VERSION(2,10,0)
-#else
-       fragment_key_chunk = g_mem_chunk_new("fragment_key_chunk",
-               sizeof(fragment_key),
-               fragment_init_count * sizeof(fragment_key),
-               G_ALLOC_AND_FREE);
-       dcerpc_fragment_key_chunk = g_mem_chunk_new("dcerpc_fragment_key_chunk",
-               sizeof(dcerpc_fragment_key),
-               fragment_init_count * sizeof(dcerpc_fragment_key),
-               G_ALLOC_AND_FREE);
-       fragment_data_chunk = g_mem_chunk_new("fragment_data_chunk",
-               sizeof(fragment_data),
-               fragment_init_count * sizeof(fragment_data),
-               G_ALLOC_ONLY);
-#endif
+/*
+ * Insert an fd_head into the fragment table, and return the key used.
+ */
+static gpointer
+insert_fd_head(reassembly_table *table, fragment_head *fd_head,
+              const packet_info *pinfo, const guint32 id, const void *data)
+{
+       gpointer key;
+
+       /*
+        * We're going to use the key to insert the fragment,
+        * so make a persistent version of it.
+        */
+       key = table->persistent_key_func(pinfo, id, data);
+       g_hash_table_insert(table->fragment_table, key, fd_head);
+       return key;
 }
 
 /* This function cleans up the stored state and removes the reassembly data and
  * (with one exception) all allocated memory for matching reassembly.
  *
  * The exception is :
- * If the PDU was already completely reassembled, then the buffer containing the
- * reassembled data WILL NOT be free()d, and the pointer to that buffer will be
+ * If the PDU was already completely reassembled, then the tvbuff containing the
+ * reassembled data WILL NOT be free()d, and the pointer to that tvbuff will be
  * returned.
  * Othervise the function will return NULL.
  *
- * So, if you call fragment_delete and it returns non-NULL, YOU are responsible to
- * g_free() that buffer.
+ * So, if you call fragment_delete and it returns non-NULL, YOU are responsible
+ * to tvb_free() that tvbuff.
  */
-unsigned char *
-fragment_delete(const packet_info *pinfo, const guint32 id, GHashTable *fragment_table)
+tvbuff_t *
+fragment_delete(reassembly_table *table, const packet_info *pinfo,
+               const guint32 id, const void *data)
 {
-       fragment_data *fd_head, *fd;
-       fragment_key key;
-       unsigned char *data=NULL;
-
-       /* 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);
+       fragment_head *fd_head;
+       fragment_item *fd;
+       tvbuff_t *fd_tvb_data=NULL;
+       gpointer key;
 
+       fd_head = lookup_fd_head(table, pinfo, id, data, &key);
        if(fd_head==NULL){
                /* We do not recognize this as a PDU we have seen before. return */
                return NULL;
        }
 
-       data=fd_head->data;
-       /* loop over all partial fragments and free any buffers */
+       fd_tvb_data=fd_head->tvb_data;
+       /* loop over all partial fragments and free any tvbuffs */
        for(fd=fd_head->next;fd;){
-               fragment_data *tmp_fd;
+               fragment_item *tmp_fd;
                tmp_fd=fd->next;
 
-               if( !(fd->flags&FD_NOT_MALLOCED) )
-                       g_free(fd->data);
-#if GLIB_CHECK_VERSION(2,10,0)
-               g_slice_free(fragment_data, fd);
-#else
-               g_mem_chunk_free(fragment_data_chunk, fd);
-#endif
+               if (fd->tvb_data && !(fd->flags & FD_SUBSET_TVB))
+                       tvb_free(fd->tvb_data);
+               g_slice_free(fragment_item, fd);
                fd=tmp_fd;
        }
-#if GLIB_CHECK_VERSION(2,10,0)
-       g_slice_free(fragment_data, fd_head);
-#else
-       g_mem_chunk_free(fragment_data_chunk, fd_head);
-#endif
-       g_hash_table_remove(fragment_table, &key);
+       g_slice_free(fragment_head, fd_head);
+       g_hash_table_remove(table->fragment_table, key);
 
-       return data;
+       return fd_tvb_data;
 }
 
 /* This function is used to check if there is partial or completed reassembly state
  * matching this packet. I.e. Is there reassembly going on or not for this packet?
  */
-fragment_data *
-fragment_get(const packet_info *pinfo, const guint32 id, GHashTable *fragment_table)
+fragment_head *
+fragment_get(reassembly_table *table, const packet_info *pinfo,
+            const guint32 id, const void *data)
 {
-       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);
-
-       return fd_head;
+       return lookup_fd_head(table, pinfo, id, data, NULL);
 }
 
 /* id *must* be the frame number for this to work! */
-fragment_data *
-fragment_get_reassembled(const guint32 id, GHashTable *reassembled_table)
+fragment_head *
+fragment_get_reassembled(reassembly_table *table, const guint32 id)
 {
-       fragment_data *fd_head;
+       fragment_head *fd_head;
        reassembled_key key;
 
        /* create key to search hash with */
        key.frame = id;
        key.id = id;
-       fd_head = g_hash_table_lookup(reassembled_table, &key);
+       fd_head = (fragment_head *)g_hash_table_lookup(table->reassembled_table, &key);
 
        return fd_head;
 }
 
-fragment_data *
-fragment_get_reassembled_id(const packet_info *pinfo, const guint32 id, GHashTable *reassembled_table)
+fragment_head *
+fragment_get_reassembled_id(reassembly_table *table, const packet_info *pinfo,
+                           const guint32 id)
 {
-       fragment_data *fd_head;
+       fragment_head *fd_head;
        reassembled_key key;
 
        /* create key to search hash with */
-       key.frame = pinfo->fd->num;
+       key.frame = pinfo->num;
        key.id = id;
-       fd_head = g_hash_table_lookup(reassembled_table, &key);
+       fd_head = (fragment_head *)g_hash_table_lookup(table->reassembled_table, &key);
 
        return fd_head;
 }
 
+/* To specify the offset for the fragment numbering, the first fragment is added with 0, and
+ * afterwards this offset is set. All additional calls to off_seq_check will calculate
+ * the number in sequence in regards to the offset */
+void
+fragment_add_seq_offset(reassembly_table *table, const packet_info *pinfo, const guint32 id,
+               const void *data, const guint32 fragment_offset)
+{
+       fragment_head *fd_head;
+
+       fd_head = lookup_fd_head(table, pinfo, id, data, NULL);
+       if (!fd_head)
+               return;
+
+       /* Reseting the offset is not allowed */
+       if ( fd_head->fragment_nr_offset != 0 )
+               return;
+
+       fd_head->fragment_nr_offset = fragment_offset;
+}
+
 /* This function can be used to explicitly set the total length (if known)
  * for reassembly of a PDU.
  * This is useful for reassembly of PDUs where one may have the total length specified
@@ -615,39 +695,54 @@ fragment_get_reassembled_id(const packet_info *pinfo, const guint32 id, GHashTab
  * actually means we want to defragment 3 blocks, block 0, 1 and 2.
  */
 void
-fragment_set_tot_len(const packet_info *pinfo, const guint32 id, GHashTable *fragment_table,
-                        const guint32 tot_len)
+fragment_set_tot_len(reassembly_table *table, const packet_info *pinfo,
+                    const guint32 id, const void *data, const guint32 tot_len)
 {
-       fragment_data *fd_head;
-       fragment_key key;
+       fragment_head *fd_head;
+       fragment_item *fd;
+       guint32        max_offset = 0;
 
-       /* create key to search hash with */
-       key.src = pinfo->src;
-       key.dst = pinfo->dst;
-       key.id  = id;
+       fd_head = lookup_fd_head(table, pinfo, id, data, NULL);
+       if (!fd_head)
+               return;
 
-       fd_head = g_hash_table_lookup(fragment_table, &key);
+       /* If we're setting a block sequence number, verify that it
+        * doesn't conflict with values set by existing fragments.
+        * XXX - eliminate this check?
+        */
+       fd = fd_head;
+       if (fd_head->flags & FD_BLOCKSEQUENCE) {
+               while (fd) {
+                       if (fd->offset > max_offset) {
+                               max_offset = fd->offset;
+                               if (max_offset > tot_len) {
+                                       fd_head->error = "Bad total reassembly block count";
+                                       THROW_MESSAGE(ReassemblyError, fd_head->error);
+                               }
+                       }
+                       fd = fd->next;
+               }
+       }
 
-       if(fd_head){
-               fd_head->datalen = tot_len;
-               fd_head->flags |= FD_DATALEN_SET;
+       if (fd_head->flags & FD_DEFRAGMENTED) {
+               if (max_offset != tot_len) {
+                       fd_head->error = "Defragmented complete but total length not satisfied";
+                       THROW_MESSAGE(ReassemblyError, fd_head->error);
+               }
        }
 
-       return;
+       /* We got this far so the value is sane. */
+       fd_head->datalen = tot_len;
+       fd_head->flags |= FD_DATALEN_SET;
 }
 
 guint32
-fragment_get_tot_len(const packet_info *pinfo, const guint32 id, GHashTable *fragment_table)
+fragment_get_tot_len(reassembly_table *table, const packet_info *pinfo,
+                    const guint32 id, const void *data)
 {
-       fragment_data *fd_head;
-       fragment_key key;
-
-       /* create key to search hash with */
-       key.src = pinfo->src;
-       key.dst = pinfo->dst;
-       key.id  = id;
+       fragment_head *fd_head;
 
-       fd_head = g_hash_table_lookup(fragment_table, &key);
+       fd_head = lookup_fd_head(table, pinfo, id, data, NULL);
 
        if(fd_head){
                return fd_head->datalen;
@@ -666,17 +761,13 @@ fragment_get_tot_len(const packet_info *pinfo, const guint32 id, GHashTable *fra
 */
 
 void
-fragment_set_partial_reassembly(const packet_info *pinfo, const guint32 id, GHashTable *fragment_table)
+fragment_set_partial_reassembly(reassembly_table *table,
+                               const packet_info *pinfo, const guint32 id,
+                               const void *data)
 {
-       fragment_data *fd_head;
-       fragment_key key;
-
-       /* create key to search hash with */
-       key.src = pinfo->src;
-       key.dst = pinfo->dst;
-       key.id  = id;
+       fragment_head *fd_head;
 
-       fd_head = g_hash_table_lookup(fragment_table, &key);
+       fd_head = lookup_fd_head(table, pinfo, id, data, NULL);
 
        /*
         * XXX - why not do all the stuff done early in "fragment_add_work()",
@@ -696,81 +787,126 @@ fragment_set_partial_reassembly(const packet_info *pinfo, const guint32 id, GHas
 
 /*
  * This function gets rid of an entry from a fragment table, given
- * a pointer to the key for that entry; it also frees up the key
- * and the addresses in it.
- * Note: If we use slices keys are freed by fragment_free_key()
-         [or dcerpc_fragment_free_key()] being called
- *       during g_hash_table_remove().
+ * a pointer to the key for that entry.
+ *
+ * The key freeing routine will be called by g_hash_table_remove().
  */
 static void
-fragment_unhash(GHashTable *fragment_table, fragment_key *key)
+fragment_unhash(reassembly_table *table, gpointer key)
 {
        /*
         * Remove the entry from the fragment table.
         */
-       g_hash_table_remove(fragment_table, key);
-
-       /*
-        * Free the key itself.
-        */
-#if GLIB_CHECK_VERSION(2,10,0)
-#else
-       /*
-        * Free up the copies of the addresses from the old key.
-        */
-       g_free((gpointer)key->src.data);
-       g_free((gpointer)key->dst.data);
-
-       g_mem_chunk_free(fragment_key_chunk, key);
-#endif
+       g_hash_table_remove(table->fragment_table, key);
 }
 
 /*
- * This function adds fragment_data structure to a reassembled-packet
+ * This function adds fragment_head structure to a reassembled-packet
  * hash table, using the frame numbers of each of the frames from
  * which it was reassembled as keys, and sets the "reassembled_in"
  * frame number.
  */
 static void
-fragment_reassembled(fragment_data *fd_head, const packet_info *pinfo,
-                GHashTable *reassembled_table, const guint32 id)
+fragment_reassembled(reassembly_table *table, fragment_head *fd_head,
+                    const packet_info *pinfo, const guint32 id)
 {
        reassembled_key *new_key;
-       fragment_data *fd;
+       fragment_item *fd;
 
        if (fd_head->next == NULL) {
                /*
                 * This was not fragmented, so there's no fragment
                 * table; just hash it using the current frame number.
                 */
-#if GLIB_CHECK_VERSION(2,10,0)
                new_key = g_slice_new(reassembled_key);
-#else
-               new_key = se_alloc(sizeof(reassembled_key));
-#endif
-               new_key->frame = pinfo->fd->num;
+               new_key->frame = pinfo->num;
                new_key->id = id;
-               g_hash_table_insert(reassembled_table, new_key, fd_head);
+               g_hash_table_insert(table->reassembled_table, new_key, fd_head);
        } else {
                /*
                 * Hash it with the frame numbers for all the frames.
                 */
                for (fd = fd_head->next; fd != NULL; fd = fd->next){
-#if GLIB_CHECK_VERSION(2,10,0)
                        new_key = g_slice_new(reassembled_key);
-#else
-                       new_key = se_alloc(sizeof(reassembled_key));
-#endif
                        new_key->frame = fd->frame;
                        new_key->id = id;
-                       g_hash_table_insert(reassembled_table, new_key,
+                       g_hash_table_insert(table->reassembled_table, new_key,
+                               fd_head);
+               }
+       }
+       fd_head->flags |= FD_DEFRAGMENTED;
+       fd_head->reassembled_in = pinfo->num;
+       fd_head->reas_in_layer_num = pinfo->curr_layer_num;
+}
+
+/*
+ * This function is a variant of the above for the single sequence
+ * case, using id+offset (i.e., the original sequence number) for the id
+ * in the key.
+ */
+static void
+fragment_reassembled_single(reassembly_table *table, fragment_head *fd_head,
+                           const packet_info *pinfo, const guint32 id)
+{
+       reassembled_key *new_key;
+       fragment_item *fd;
+
+       if (fd_head->next == NULL) {
+               /*
+                * This was not fragmented, so there's no fragment
+                * table; just hash it using the current frame number.
+                */
+               new_key = g_slice_new(reassembled_key);
+               new_key->frame = pinfo->num;
+               new_key->id = id;
+               g_hash_table_insert(table->reassembled_table, new_key, fd_head);
+       } else {
+               /*
+                * Hash it with the frame numbers for all the frames.
+                */
+               for (fd = fd_head->next; fd != NULL; fd = fd->next){
+                       new_key = g_slice_new(reassembled_key);
+                       new_key->frame = fd->frame;
+                       new_key->id = id + fd->offset;
+                       g_hash_table_insert(table->reassembled_table, new_key,
                                fd_head);
                }
        }
        fd_head->flags |= FD_DEFRAGMENTED;
-       fd_head->reassembled_in = pinfo->fd->num;
+       fd_head->reassembled_in = pinfo->num;
+       fd_head->reas_in_layer_num = pinfo->curr_layer_num;
 }
 
+static void
+LINK_FRAG(fragment_head *fd_head,fragment_item *fd)
+{
+       fragment_item *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 void
+MERGE_FRAG(fragment_head *fd_head, fragment_item *fd)
+{
+       fragment_item *fd_i, *tmp;
+
+       if (fd == NULL) return;
+
+       for(fd_i = fd_head; fd_i->next; fd_i=fd_i->next) {
+               if (fd->offset < fd_i->next->offset) {
+                       tmp = fd_i->next;
+                       fd_i->next = fd;
+                       fd = tmp;
+               }
+       }
+       fd_i->next = fd;
+}
 /*
  * This function adds a new fragment to the fragment hash table.
  * If this is the first fragment seen for this datagram, a new entry
@@ -793,49 +929,107 @@ fragment_reassembled(fragment_data *fd_head, const packet_info *pinfo,
  * are lowered when a new extension process is started.
  */
 static gboolean
-fragment_add_work(fragment_data *fd_head, tvbuff_t *tvb, const int offset,
+fragment_add_work(fragment_head *fd_head, tvbuff_t *tvb, const int offset,
                 const packet_info *pinfo, const guint32 frag_offset,
                 const guint32 frag_data_len, const gboolean more_frags)
 {
-       fragment_data *fd;
-       fragment_data *fd_i;
-       guint32 max, dfpos;
-       unsigned char *old_data;
+       fragment_item *fd;
+       fragment_item *fd_i;
+       guint32 max, dfpos, fraglen;
+       tvbuff_t *old_tvb_data;
+       guint8 *data;
 
        /* create new fd describing this fragment */
-#if GLIB_CHECK_VERSION(2,10,0)
-       fd = g_slice_new(fragment_data);
-#else
-       fd = g_mem_chunk_alloc(fragment_data_chunk);
-#endif
+       fd = g_slice_new(fragment_item);
        fd->next = NULL;
        fd->flags = 0;
-       fd->frame = pinfo->fd->num;
-       if (fd->frame > fd_head->frame)
-               fd_head->frame = fd->frame;
+       fd->frame = pinfo->num;
        fd->offset = frag_offset;
+       fd->fragment_nr_offset = 0; /* will only be used with sequence */
        fd->len  = frag_data_len;
-       fd->data = NULL;
+       fd->tvb_data = NULL;
+       fd->error = 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.
+        * Are we adding to an already-completed reassembly?
         */
-       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;
+       if (fd_head->flags & FD_DEFRAGMENTED) {
+               /*
+                * Yes.  Does this fragment go past the end of the results
+                * of that reassembly?
+                * XXX - shouldn't this be ">"?  If frag_offset + frag_data_len
+                * == fd_head->datalen, this overlaps the end of the
+                * reassembly, but doesn't go past it, right?
+                */
+               if (frag_offset + frag_data_len >= fd_head->datalen) {
+                       /*
+                        * Yes.  Have we been requested to continue reassembly?
+                        */
+                       if (fd_head->flags & FD_PARTIAL_REASSEMBLY) {
+                               /*
+                                * Yes.  Set flag in already empty fds &
+                                * point old fds to malloc'ed data.
+                                */
+                               for(fd_i=fd_head->next; fd_i; fd_i=fd_i->next){
+                                       if( !fd_i->tvb_data ) {
+                                               fd_i->tvb_data = tvb_new_subset_remaining(fd_head->tvb_data, fd_i->offset);
+                                               fd_i->flags |= FD_SUBSET_TVB;
+                                       }
+                                       fd_i->flags &= (~FD_TOOLONGFRAGMENT) & (~FD_MULTIPLETAILS);
+                               }
+                               fd_head->flags &= ~(FD_DEFRAGMENTED|FD_PARTIAL_REASSEMBLY|FD_DATALEN_SET);
+                               fd_head->flags &= (~FD_TOOLONGFRAGMENT) & (~FD_MULTIPLETAILS);
+                               fd_head->datalen=0;
+                               fd_head->reassembled_in=0;
+                               fd_head->reas_in_layer_num = 0;
+                       } else {
+                               /*
+                                * No.  Bail out since we have no idea what to
+                                * do with this fragment (and if we keep going
+                                * we'll run past the end of a buffer sooner
+                                * or later).
+                                */
+                               g_slice_free(fragment_item, fd);
+
+                               /*
+                                * This is an attempt to add a fragment to a
+                                * reassembly that had already completed.
+                                * If it had no error, we don't want to
+                                * mark it with an error, and if it had an
+                                * error, we don't want to overwrite it, so
+                                * we don't set fd_head->error.
+                                */
+                               if (frag_offset >= fd_head->datalen) {
+                                       /*
+                                        * The fragment starts past the end
+                                        * of the reassembled data.
+                                        */
+                                       THROW_MESSAGE(ReassemblyError, "New fragment past old data limits");
+                               } else {
+                                       /*
+                                        * The fragment starts before the end
+                                        * of the reassembled data, but
+                                        * runs past the end.  That could
+                                        * just be a retransmission.
+                                        */
+                                       THROW_MESSAGE(ReassemblyError, "New fragment overlaps old data (retransmission?)");
+                               }
                        }
-                       fd_i->flags &= (~FD_TOOLONGFRAGMENT) & (~FD_MULTIPLETAILS);
+               } else {
+                       /*
+                        * No.  That means it still overlaps that, so report
+                        * this as a problem, possibly a retransmission.
+                        */
+                       g_slice_free(fragment_item, fd);
+                       THROW_MESSAGE(ReassemblyError, "New fragment overlaps old data (retransmission?)");
                }
-               fd_head->flags &= ~(FD_DEFRAGMENTED|FD_PARTIAL_REASSEMBLY|FD_DATALEN_SET);
-               fd_head->flags &= (~FD_TOOLONGFRAGMENT) & (~FD_MULTIPLETAILS);
-               fd_head->datalen=0;
-               fd_head->reassembled_in=0;
        }
 
+       /* Do this after we may have bailed out (above) so that we don't leave
+        * fd_head->frame in a bad state if we do */
+       if (fd->frame > fd_head->frame)
+               fd_head->frame = fd->frame;
+
        if (!more_frags) {
                /*
                 * This is the tail fragment in the sequence.
@@ -852,8 +1046,8 @@ fragment_add_work(fragment_data *fd_head, tvbuff_t *tvb, const int offset,
                                fd_head->flags |= FD_MULTIPLETAILS;
                        }
                } else {
-                       /* this was the first tail fragment, now we know the
-                        * length of the packet
+                       /* This was the first tail fragment; now we know
+                        * what the length of the packet should be.
                         */
                        fd_head->datalen = fd->offset + fd->len;
                        fd_head->flags |= FD_DATALEN_SET;
@@ -862,7 +1056,6 @@ fragment_add_work(fragment_data *fd_head, tvbuff_t *tvb, const int 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
@@ -878,7 +1071,7 @@ fragment_add_work(fragment_data *fd_head, tvbuff_t *tvb, const int offset,
                        fd_head->flags |= FD_TOOLONGFRAGMENT;
                }
                /* make sure it doesn't conflict with previous data */
-               else if ( memcmp(fd_head->data+fd->offset,
+               else if ( tvb_memeql(fd_head->tvb_data, fd->offset,
                        tvb_get_ptr(tvb,offset,fd->len),fd->len) ){
                        fd->flags          |= FD_OVERLAPCONFLICT;
                        fd_head->flags |= FD_OVERLAPCONFLICT;
@@ -892,16 +1085,17 @@ fragment_add_work(fragment_data *fd_head, tvbuff_t *tvb, const int offset,
 
        /* 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);
+       if (!tvb_bytes_exist(tvb, offset, fd->len)) {
+               g_slice_free(fragment_item, fd);
+               THROW(BoundsError);
+       }
+       fd->tvb_data = tvb_clone_offset_len(tvb, offset, fd->len);
        LINK_FRAG(fd_head,fd);
 
 
        if( !(fd_head->flags & FD_DATALEN_SET) ){
-               /* if we dont know the datalen, there are still missing
+               /* if we don't know the datalen, there are still missing
                 * packets. Cheaper than the check below.
                 */
                return FALSE;
@@ -935,121 +1129,178 @@ fragment_add_work(fragment_data *fd_head, tvbuff_t *tvb, const int offset,
                return FALSE;
        }
 
-
-       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;
-       }
-
        /* 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);
+       old_tvb_data=fd_head->tvb_data;
+       data = (guint8 *) g_malloc(fd_head->datalen);
+       fd_head->tvb_data = tvb_new_real_data(data, fd_head->datalen, fd_head->datalen);
+       tvb_set_free_cb(fd_head->tvb_data, g_free);
 
        /* add all data fragments */
        for (dfpos=0,fd_i=fd_head;fd_i;fd_i=fd_i->next) {
                if (fd_i->len) {
-                       /* dfpos is always >= than fd_i->offset */
-                       /* No gaps can exist here, max_loop(above) does this */
-                       /* XXX - true? Can we get fd_i->offset+fd-i->len */
-                       /* overflowing, for example? */
-                       /*      Actually: there is at least one pathological case wherein there can be fragments
-                        *      on the list which are for offsets greater than max (i.e.: following a gap after max).
-                        *      (Apparently a "DESEGMENT_UNTIL_FIN" was involved wherein the FIN packet had an offset
-                        *      less than the highest fragment offset seen. [Seen from a fuzz-test: bug #2470]).
-                        *      Note that the "overlap" compare must only be done for fragments with (offset+len) <= max
-                        *      and thus within the newly g_malloc'd buffer.
+                       /*
+                        * The loop above that calculates max also
+                        * ensures that the only gaps that exist here
+                        * are ones where a fragment starts past the
+                        * end of the reassembled datagram, and there's
+                        * a gap between the previous fragment and
+                        * that fragment.
+                        *
+                        * A "DESEGMENT_UNTIL_FIN" was involved wherein the
+                        * FIN packet had an offset less than the highest
+                        * fragment offset seen. [Seen from a fuzz-test:
+                        * bug #2470]).
+                        *
+                        * Note that the "overlap" compare must only be
+                        * done for fragments with (offset+len) <= fd_head->datalen
+                        * and thus within the newly g_malloc'd buffer.
                         */
-
-                       if ( fd_i->offset+fd_i->len > dfpos ) {
-                               if (fd_i->offset+fd_i->len > max)
-                                       g_warning("Reassemble error in frame %u: offset %u + len %u > max %u",
-                                               pinfo->fd->num, fd_i->offset,
-                                               fd_i->len, max);
-                               else if (dfpos < fd_i->offset)
-                                       g_warning("Reassemble error in frame %u: dfpos %u < offset %u",
-                                               pinfo->fd->num, dfpos, fd_i->offset);
-                               else if (dfpos-fd_i->offset > fd_i->len)
-                                       g_warning("Reassemble error in frame %u: dfpos %u - offset %u > len %u",
-                                               pinfo->fd->num, dfpos, fd_i->offset,
-                                               fd_i->len);
-                               else if (!fd_head->data)
-                                       g_warning("Reassemble error in frame %u: no data",
-                                               pinfo->fd->num);
+                       if (fd_i->offset + fd_i->len > dfpos) {
+                               if (fd_i->offset >= fd_head->datalen) {
+                                       /*
+                                        * Fragment starts after the end
+                                        * of the reassembled packet.
+                                        *
+                                        * This can happen if the length was
+                                        * set after the offending fragment
+                                        * was added to the reassembly.
+                                        *
+                                        * Flag this fragment, but don't
+                                        * try to extract any data from
+                                        * it, as there's no place to put
+                                        * it.
+                                        *
+                                        * XXX - add different flag value
+                                        * for this.
+                                        */
+                                       fd_i->flags    |= FD_TOOLONGFRAGMENT;
+                                       fd_head->flags |= FD_TOOLONGFRAGMENT;
+                               } else if (dfpos < fd_i->offset) {
+                                       /*
+                                        * XXX - can this happen?  We've
+                                        * already rejected fragments that
+                                        * start past the end of the
+                                        * reassembled datagram, and
+                                        * the loop that calculated max
+                                        * should have ruled out gaps,
+                                        * but could fd_i->offset +
+                                        * fd_i->len overflow?
+                                        */
+                                       fd_head->error = "dfpos < offset";
+                               } else if (dfpos - fd_i->offset > fd_i->len)
+                                       fd_head->error = "dfpos - offset > len";
+                               else if (!fd_head->tvb_data)
+                                       fd_head->error = "no data";
                                else {
+                                       fraglen = fd_i->len;
+                                       if (fd_i->offset + fraglen > fd_head->datalen) {
+                                               /*
+                                                * Fragment goes past the end
+                                                * of the packet, as indicated
+                                                * by the last fragment.
+                                                *
+                                                * This can happen if the
+                                                * length was set after the
+                                                * offending fragment was
+                                                * added to the reassembly.
+                                                *
+                                                * Mark it as such, and only
+                                                * copy from it what fits in
+                                                * the packet.
+                                                */
+                                               fd_i->flags    |= FD_TOOLONGFRAGMENT;
+                                               fd_head->flags |= FD_TOOLONGFRAGMENT;
+                                               fraglen = fd_head->datalen - fd_i->offset;
+                                       }
                                        if (fd_i->offset < dfpos) {
+                                               guint32 cmp_len = MIN(fd_i->len,(dfpos-fd_i->offset));
+
                                                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))
-                                                                ) {
+                                               if ( memcmp(data + fd_i->offset,
+                                                               tvb_get_ptr(fd_i->tvb_data, 0, cmp_len),
+                                                               cmp_len)
+                                                                ) {
                                                        fd_i->flags    |= FD_OVERLAPCONFLICT;
                                                        fd_head->flags |= FD_OVERLAPCONFLICT;
                                                }
                                        }
-                                       memcpy(fd_head->data+dfpos,
-                                               fd_i->data+(dfpos-fd_i->offset),
-                                               fd_i->len-(dfpos-fd_i->offset));
+                                       if (fraglen < dfpos - fd_i->offset) {
+                                               /*
+                                                * XXX - can this happen?
+                                                */
+                                               fd_head->error = "fraglen < dfpos - offset";
+                                       } else {
+                                               memcpy(data+dfpos,
+                                                       tvb_get_ptr(fd_i->tvb_data, (dfpos-fd_i->offset), fraglen-(dfpos-fd_i->offset)),
+                                                       fraglen-(dfpos-fd_i->offset));
+                                               dfpos=MAX(dfpos, (fd_i->offset + fraglen));
+                                       }
                                }
                        } else {
-                               if (fd_i->offset + fd_i->len < fd_i->offset) /* Integer overflow? */
-                                       g_warning("Reassemble error in frame %u: offset %u + len %u < offset",
-                                               pinfo->fd->num, fd_i->offset,
-                                               fd_i->len);
+                               if (fd_i->offset + fd_i->len < fd_i->offset) {
+                                       /* Integer overflow? */
+                                       fd_head->error = "offset + len < 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 (fd_i->flags & FD_SUBSET_TVB)
+                               fd_i->flags &= ~FD_SUBSET_TVB;
+                       else if (fd_i->tvb_data)
+                               tvb_free(fd_i->tvb_data);
+
+                       fd_i->tvb_data=NULL;
                }
        }
 
-       g_free(old_data);
+       if (old_tvb_data)
+               tvb_add_to_chain(tvb, old_tvb_data);
        /* mark this packet as defragmented.
-                  allows us to skip any trailing fragments */
+          allows us to skip any trailing fragments */
        fd_head->flags |= FD_DEFRAGMENTED;
-       fd_head->reassembled_in=pinfo->fd->num;
+       fd_head->reassembled_in=pinfo->num;
+       fd_head->reas_in_layer_num = pinfo->curr_layer_num;
+
+       /* we don't throw until here to avoid leaking old_data and others */
+       if (fd_head->error) {
+               THROW_MESSAGE(ReassemblyError, fd_head->error);
+       }
 
        return TRUE;
 }
 
-static fragment_data *
-fragment_add_common(tvbuff_t *tvb, const int offset, const packet_info *pinfo, const guint32 id,
-                GHashTable *fragment_table, const guint32 frag_offset,
-                const guint32 frag_data_len, const gboolean more_frags,
-                const gboolean check_already_added)
+static fragment_head *
+fragment_add_common(reassembly_table *table, tvbuff_t *tvb, const int offset,
+                   const packet_info *pinfo, const guint32 id,
+                   const void *data, const guint32 frag_offset,
+                   const guint32 frag_data_len, const gboolean more_frags,
+                   const gboolean check_already_added)
 {
-       fragment_key key, *new_key;
-       fragment_data *fd_head;
-       fragment_data *fd_item;
-       gboolean already_added=pinfo->fd->flags.visited;
+       fragment_head *fd_head;
+       fragment_item *fd_item;
+       gboolean already_added;
 
 
-       /* dissector shouldn't give us garbage tvb info */
+       /*
+        * Dissector shouldn't give us garbage tvb info.
+        *
+        * XXX - should this code take responsibility for preventing
+        * reassembly if data is missing due to the packets being
+        * sliced, rather than leaving it up to dissectors?
+        */
        DISSECTOR_ASSERT(tvb_bytes_exist(tvb, offset, frag_data_len));
 
-       /* 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);
+       fd_head = lookup_fd_head(table, pinfo, id, data, NULL);
 
 #if 0
        /* debug output of associated fragments. */
        /* leave it here for future debugging sessions */
        if(strcmp(pinfo->current_proto, "DCERPC") == 0) {
                printf("proto:%s num:%u id:%u offset:%u len:%u more:%u visited:%u\n",
-                       pinfo->current_proto, pinfo->fd->num, id, frag_offset, frag_data_len, more_frags, pinfo->fd->flags.visited);
+                       pinfo->current_proto, pinfo->num, id, frag_offset, frag_data_len, more_frags, pinfo->fd->flags.visited);
                if(fd_head != NULL) {
                        for(fd_item=fd_head->next;fd_item;fd_item=fd_item->next){
                                printf("fd_frame:%u fd_offset:%u len:%u datalen:%u\n",
@@ -1060,66 +1311,141 @@ fragment_add_common(tvbuff_t *tvb, const int offset, const packet_info *pinfo, c
 #endif
 
        /*
-        * "already_added" is true if "pinfo->fd->flags.visited" is true;
-        * if "pinfo->fd->flags.visited", this isn't the first pass, so
-        * we've already done all the reassembly and added all the
-        * fragments.
-        *
-        * If it's not true, but "check_already_added" is true, just check
-        * if we have seen this fragment before, i.e., if we have already
-        * added it to reassembly.
-        * That can be true even if "pinfo->fd->flags.visited" is false
-        * since we sometimes might call a subdissector multiple times.
-        * As an additional check, just make sure we have not already added
-        * this frame to the reassembly list, if there is a reassembly list;
-        * note that the first item in the reassembly list is not a
-        * fragment, it's a data structure for the reassembled packet.
-        * We don't check it because its "frame" member isn't initialized
-        * to anything, and because it doesn't count in any case.
-        *
-        * And as another additional check, make sure the fragment offsets are
-        * the same, as otherwise we get into trouble if multiple fragments
-        * are in one PDU.
+        * Is this the first pass through the capture?
         */
-       if (!already_added && check_already_added && fd_head != NULL) {
-               if (pinfo->fd->num <= fd_head->frame) {
-                       for(fd_item=fd_head->next;fd_item;fd_item=fd_item->next){
-                               if(pinfo->fd->num==fd_item->frame && frag_offset==fd_item->offset){
-                                       already_added=TRUE;
+       if (!pinfo->fd->flags.visited) {
+               /*
+                * Yes, so we could be doing reassembly.  If
+                * "check_already_added" is true, and fd_head is non-null,
+                * meaning that this fragment would be added to an
+                * in-progress reassembly, check if we have seen this
+                * fragment before, i.e., if we have already added it to
+                * that reassembly. That can be true even on the first pass
+                * since we sometimes might call a subdissector multiple
+                * times.
+                *
+                * We check both the frame number and the fragment offset,
+                * so that we support multiple fragments from the same
+                * frame being added to the same reassembled PDU.
+                */
+               if (check_already_added && fd_head != NULL) {
+                       /*
+                        * fd_head->frame is the maximum of the frame
+                        * numbers of all the fragments added to this
+                        * reassembly; if this frame is later than that
+                        * frame, we know it hasn't been added yet.
+                        */
+                       if (pinfo->num <= fd_head->frame) {
+                               already_added = FALSE;
+                               /*
+                                * The first item in the reassembly list
+                                * is not a fragment, it's a data structure
+                                * for the reassembled packet, so we
+                                * start checking with the next item.
+                                */
+                               for (fd_item = fd_head->next; fd_item;
+                                   fd_item = fd_item->next) {
+                                       if (pinfo->num == fd_item->frame &&
+                                           frag_offset == fd_item->offset) {
+                                               already_added = TRUE;
+                                               break;
+                                       }
+                               }
+                               if (already_added) {
+                                       /*
+                                        * Have we already finished
+                                        * reassembling?
+                                        */
+                                       if (fd_head->flags & FD_DEFRAGMENTED) {
+                                               /*
+                                                * Yes.
+                                                * XXX - can this ever happen?
+                                                */
+                                               THROW_MESSAGE(ReassemblyError,
+                                                   "Frame already added in first pass");
+                                       } else {
+                                               /*
+                                                * No.
+                                                */
+                                               return NULL;
+                                       }
                                }
                        }
                }
-       }
-       /* have we already added this frame ?*/
-       if (already_added) {
+       } else {
+               /*
+                * No, so we've already done all the reassembly and added
+                * all the fragments.  Do we have a reassembly and, if so,
+                * have we finished reassembling?
+                */
                if (fd_head != NULL && fd_head->flags & FD_DEFRAGMENTED) {
+                       /*
+                        * Yes.  This is probably being done after the
+                        * first pass, and we've already done the work
+                        * on the first pass.
+                        *
+                        * If the reassembly got a fatal error, throw that
+                        * error again.
+                        */
+                       if (fd_head->error)
+                               THROW_MESSAGE(ReassemblyError, fd_head->error);
+
+                       /*
+                        * Is it later in the capture than all of the
+                        * fragments in the reassembly?
+                        */
+                       if (pinfo->num > fd_head->frame) {
+                               /*
+                                * Yes, so report this as a problem,
+                                * possibly a retransmission.
+                                */
+                               THROW_MESSAGE(ReassemblyError, "New fragment overlaps old data (retransmission?)");
+                       }
+
+                       /*
+                        * Does this fragment go past the end of the
+                        * results of that reassembly?
+                        */
+                       if (frag_offset + frag_data_len > fd_head->datalen) {
+                               /*
+                                * Yes.
+                                */
+                               if (frag_offset >= fd_head->datalen) {
+                                       /*
+                                        * The fragment starts past the
+                                        * end of the reassembled data.
+                                        */
+                                       THROW_MESSAGE(ReassemblyError, "New fragment past old data limits");
+                               } else {
+                                       /*
+                                        * The fragment starts before the end
+                                        * of the reassembled data, but
+                                        * runs past the end.  That could
+                                        * just be a retransmission.
+                                        */
+                                       THROW_MESSAGE(ReassemblyError, "New fragment overlaps old data (retransmission?)");
+                               }
+                       }
+
                        return fd_head;
                } else {
+                       /*
+                        * No.
+                        */
                        return NULL;
                }
        }
 
        if (fd_head==NULL){
                /* not found, this must be the first snooped fragment for this
-                                * packet. Create list-head.
+                * packet. Create list-head.
                 */
                fd_head = new_head(0);
 
                /*
-                * 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.
+                * Insert it into the hash table.
                 */
-#if GLIB_CHECK_VERSION(2,10,0)
-               new_key = g_slice_new(fragment_key);
-#else
-               new_key = g_mem_chunk_alloc(fragment_key_chunk);
-#endif
-               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);
+               insert_fd_head(table, fd_head, pinfo, id, data);
        }
 
        if (fragment_add_work(fd_head, tvb, offset, pinfo, frag_offset,
@@ -1136,12 +1462,13 @@ fragment_add_common(tvbuff_t *tvb, const int offset, const packet_info *pinfo, c
        }
 }
 
-fragment_data *
-fragment_add(tvbuff_t *tvb, const int offset, const packet_info *pinfo, const guint32 id,
-                GHashTable *fragment_table, const guint32 frag_offset,
-                const guint32 frag_data_len, const gboolean more_frags)
+fragment_head *
+fragment_add(reassembly_table *table, tvbuff_t *tvb, const int offset,
+            const packet_info *pinfo, const guint32 id, const void *data,
+            const guint32 frag_offset, const guint32 frag_data_len,
+            const gboolean more_frags)
 {
-       return fragment_add_common(tvb, offset, pinfo, id, fragment_table,
+       return fragment_add_common(table, tvb, offset, pinfo, id, data,
                frag_offset, frag_data_len, more_frags, TRUE);
 }
 
@@ -1149,82 +1476,59 @@ fragment_add(tvbuff_t *tvb, const int offset, const packet_info *pinfo, const gu
  * For use when you can have multiple fragments in the same frame added
  * to the same reassembled PDU, e.g. with ONC RPC-over-TCP.
  */
-fragment_data *
-fragment_add_multiple_ok(tvbuff_t *tvb, const int offset, const packet_info *pinfo,
-                        const guint32 id, GHashTable *fragment_table,
-                        const guint32 frag_offset, const guint32 frag_data_len,
-                        const gboolean more_frags)
+fragment_head *
+fragment_add_multiple_ok(reassembly_table *table, tvbuff_t *tvb,
+                        const int offset, const packet_info *pinfo,
+                        const guint32 id, const void *data,
+                        const guint32 frag_offset,
+                        const guint32 frag_data_len, const gboolean more_frags)
 {
-       return fragment_add_common(tvb, offset, pinfo, id, fragment_table,
+       return fragment_add_common(table, tvb, offset, pinfo, id, data,
                frag_offset, frag_data_len, more_frags, FALSE);
 }
 
-fragment_data *
-fragment_add_check(tvbuff_t *tvb, const int offset, const packet_info *pinfo,
-                const guint32 id, GHashTable *fragment_table,
-                GHashTable *reassembled_table, const guint32 frag_offset,
-                const guint32 frag_data_len, const gboolean more_frags)
+fragment_head *
+fragment_add_check(reassembly_table *table, tvbuff_t *tvb, const int offset,
+                  const packet_info *pinfo, const guint32 id,
+                  const void *data, const guint32 frag_offset,
+                  const guint32 frag_data_len, const gboolean more_frags)
 {
        reassembled_key reass_key;
-       fragment_key key, *new_key, *old_key;
-       gpointer orig_key, value;
-       fragment_data *fd_head;
+       fragment_head *fd_head;
+       gpointer orig_key;
 
        /*
         * If this isn't the first pass, look for this frame in the table
         * of reassembled packets.
         */
        if (pinfo->fd->flags.visited) {
-               reass_key.frame = pinfo->fd->num;
+               reass_key.frame = pinfo->num;
                reass_key.id = id;
-               return g_hash_table_lookup(reassembled_table, &reass_key);
+               return (fragment_head *)g_hash_table_lookup(table->reassembled_table, &reass_key);
        }
 
-       /* create key to search hash with */
-       key.src = pinfo->src;
-       key.dst = pinfo->dst;
-       key.id  = id;
-
        /* Looks up a key in the GHashTable, returning the original key and the associated value
         * and a gboolean which is TRUE if the key was found. This is useful if you need to free
         * the memory allocated for the original key, for example before calling g_hash_table_remove()
         */
-       if (!g_hash_table_lookup_extended(fragment_table, &key,
-                                         &orig_key, &value)) {
+       fd_head = lookup_fd_head(table, pinfo, id, data, &orig_key);
+       if (fd_head == NULL) {
                /* not found, this must be the first snooped fragment for this
-                                * packet. Create list-head.
+                * packet. Create list-head.
                 */
                fd_head = new_head(0);
 
                /*
-                * 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.
-                */
-#if GLIB_CHECK_VERSION(2,10,0)
-               new_key = g_slice_new(fragment_key);
-#else
-               new_key = g_mem_chunk_alloc(fragment_key_chunk);
-#endif
-               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);
-
-               orig_key = new_key; /* for unhashing it later */
-       } else {
-               /*
-                * We found it.
+                * Save the key, for unhashing it later.
                 */
-               fd_head = value;
+               orig_key = insert_fd_head(table, fd_head, pinfo, id, data);
        }
 
        /*
         * If this is a short frame, then we can't, and don't, do
         * reassembly on it.  We just give up.
         */
-       if (tvb_reported_length(tvb) > tvb_length(tvb))
+       if (tvb_reported_length(tvb) > tvb_captured_length(tvb))
                return NULL;
 
        if (fragment_add_work(fd_head, tvb, offset, pinfo, frag_offset,
@@ -1240,13 +1544,12 @@ fragment_add_check(tvbuff_t *tvb, const int offset, const packet_info *pinfo,
                 * Remove this from the table of in-progress reassemblies,
                 * and free up any memory used for it in that table.
                 */
-               old_key = orig_key;
-               fragment_unhash(fragment_table, old_key);
+               fragment_unhash(table, orig_key);
 
                /*
                 * Add this item to the table of reassembled packets.
                 */
-               fragment_reassembled(fd_head, pinfo, reassembled_table, id);
+               fragment_reassembled(table, fd_head, pinfo, id);
                return fd_head;
        } else {
                /*
@@ -1257,12 +1560,13 @@ fragment_add_check(tvbuff_t *tvb, const int offset, const packet_info *pinfo,
 }
 
 static void
-fragment_defragment_and_free (fragment_data *fd_head, const packet_info *pinfo)
+fragment_defragment_and_free (fragment_head *fd_head, const packet_info *pinfo)
 {
-       fragment_data *fd_i = NULL;
-       fragment_data *last_fd = NULL;
+       fragment_item *fd_i = NULL;
+       fragment_item *last_fd = NULL;
        guint32  dfpos = 0, size = 0;
-       void *old_data = NULL;
+       tvbuff_t *old_tvb_data = NULL;
+       guint8 *data;
 
        for(fd_i=fd_head->next;fd_i;fd_i=fd_i->next) {
                if(!last_fd || last_fd->offset!=fd_i->offset){
@@ -1272,8 +1576,10 @@ fragment_defragment_and_free (fragment_data *fd_head, const packet_info *pinfo)
        }
 
        /* store old data in case the fd_i->data pointers refer to it */
-       old_data=fd_head->data;
-       fd_head->data = g_malloc(size);
+       old_tvb_data=fd_head->tvb_data;
+       data = (guint8 *) g_malloc(size);
+       fd_head->tvb_data = tvb_new_real_data(data, size, size);
+       tvb_set_free_cb(fd_head->tvb_data, g_free);
        fd_head->len = size;            /* record size for caller       */
 
        /* add all data fragments */
@@ -1282,14 +1588,14 @@ fragment_defragment_and_free (fragment_data *fd_head, const packet_info *pinfo)
                if (fd_i->len) {
                        if(!last_fd || last_fd->offset != fd_i->offset) {
                                /* First fragment or in-sequence fragment */
-                               memcpy(fd_head->data+dfpos, fd_i->data, fd_i->len);
+                               memcpy(data+dfpos, tvb_get_ptr(fd_i->tvb_data, 0, fd_i->len), fd_i->len);
                                dfpos += fd_i->len;
                        } else {
                                /* duplicate/retransmission/overlap */
                                fd_i->flags    |= FD_OVERLAP;
                                fd_head->flags |= FD_OVERLAP;
                                if(last_fd->len != fd_i->len
-                                  || memcmp(last_fd->data, fd_i->data, last_fd->len) ) {
+                                  || tvb_memeql(last_fd->tvb_data, 0, tvb_get_ptr(fd_i->tvb_data, 0, last_fd->len), last_fd->len) ) {
                                        fd_i->flags    |= FD_OVERLAPCONFLICT;
                                        fd_head->flags |= FD_OVERLAPCONFLICT;
                                }
@@ -1300,19 +1606,21 @@ fragment_defragment_and_free (fragment_data *fd_head, const packet_info *pinfo)
 
        /* 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->flags & FD_NOT_MALLOCED )
-                       fd_i->flags &= ~FD_NOT_MALLOCED;
-               else
-                       g_free(fd_i->data);
-               fd_i->data=NULL;
+               if (fd_i->flags & FD_SUBSET_TVB)
+                       fd_i->flags &= ~FD_SUBSET_TVB;
+               else if (fd_i->tvb_data)
+                       tvb_free(fd_i->tvb_data);
+               fd_i->tvb_data=NULL;
        }
-       g_free(old_data);
+       if (old_tvb_data)
+               tvb_free(old_tvb_data);
 
        /* mark this packet as defragmented.
         * allows us to skip any trailing fragments.
         */
        fd_head->flags |= FD_DEFRAGMENTED;
-       fd_head->reassembled_in=pinfo->fd->num;
+       fd_head->reassembled_in=pinfo->num;
+       fd_head->reas_in_layer_num = pinfo->curr_layer_num;
 }
 
 /*
@@ -1328,35 +1636,41 @@ fragment_defragment_and_free (fragment_data *fd_head, const packet_info *pinfo)
  * The bsn for the first block is 0.
  */
 static gboolean
-fragment_add_seq_work(fragment_data *fd_head, tvbuff_t *tvb, const int offset,
+fragment_add_seq_work(fragment_head *fd_head, tvbuff_t *tvb, const int offset,
                 const packet_info *pinfo, const guint32 frag_number,
-                const guint32 frag_data_len, const gboolean more_frags,
-                const guint32 flags _U_)
+                const guint32 frag_data_len, const gboolean more_frags)
 {
-       fragment_data *fd;
-       fragment_data *fd_i;
-       fragment_data *last_fd;
+       fragment_item *fd;
+       fragment_item *fd_i;
+       fragment_item *last_fd;
        guint32 max, dfpos;
+       guint32 frag_number_work;
+
+       /* Enables the use of fragment sequence numbers, which do not start with 0 */
+       frag_number_work = frag_number;
+       if ( fd_head->fragment_nr_offset != 0 )
+               if ( frag_number_work >= fd_head->fragment_nr_offset )
+                       frag_number_work = frag_number - fd_head->fragment_nr_offset;
 
        /* if the partial reassembly flag has been set, and we are extending
         * the pdu, un-reassemble the pdu. This means pointing old fds to malloc'ed data.
         */
-       if(fd_head->flags & FD_DEFRAGMENTED && frag_number >= fd_head->datalen &&
+       if(fd_head->flags & FD_DEFRAGMENTED && frag_number_work >= fd_head->datalen &&
                fd_head->flags & FD_PARTIAL_REASSEMBLY){
                guint32 lastdfpos = 0;
                dfpos = 0;
                for(fd_i=fd_head->next; fd_i; fd_i=fd_i->next){
-                       if( !fd_i->data ) {
+                       if( !fd_i->tvb_data ) {
                                if( fd_i->flags & FD_OVERLAP ) {
                                        /* this is a duplicate of the previous
                                         * fragment. */
-                                       fd_i->data = fd_head->data + lastdfpos;
+                                       fd_i->tvb_data = tvb_new_subset_remaining(fd_head->tvb_data, lastdfpos);
                                } else {
-                                       fd_i->data = fd_head->data + dfpos;
+                                       fd_i->tvb_data = tvb_new_subset_remaining(fd_head->tvb_data, dfpos);
                                        lastdfpos = dfpos;
                                        dfpos += fd_i->len;
                                }
-                               fd_i->flags |= FD_NOT_MALLOCED;
+                               fd_i->flags |= FD_SUBSET_TVB;
                        }
                        fd_i->flags &= (~FD_TOOLONGFRAGMENT) & (~FD_MULTIPLETAILS);
                }
@@ -1364,21 +1678,24 @@ fragment_add_seq_work(fragment_data *fd_head, tvbuff_t *tvb, const int offset,
                fd_head->flags &= (~FD_TOOLONGFRAGMENT) & (~FD_MULTIPLETAILS);
                fd_head->datalen=0;
                fd_head->reassembled_in=0;
+               fd_head->reas_in_layer_num = 0;
        }
 
 
        /* create new fd describing this fragment */
-#if GLIB_CHECK_VERSION(2,10,0)
-       fd = g_slice_new(fragment_data);
-#else
-       fd = g_mem_chunk_alloc(fragment_data_chunk);
-#endif
+       fd = g_slice_new(fragment_item);
        fd->next = NULL;
        fd->flags = 0;
-       fd->frame = pinfo->fd->num;
-       fd->offset = frag_number;
+       fd->frame = pinfo->num;
+       fd->offset = frag_number_work;
        fd->len  = frag_data_len;
-       fd->data = NULL;
+       fd->tvb_data = NULL;
+       fd->error = NULL;
+
+       /* fd_head->frame is the maximum of the frame numbers of all the
+        * fragments added to the reassembly. */
+       if (fd->frame > fd_head->frame)
+               fd_head->frame = fd->frame;
 
        if (!more_frags) {
                /*
@@ -1444,7 +1761,7 @@ fragment_add_seq_work(fragment_data *fd_head, tvbuff_t *tvb, const int offset,
                                return TRUE;
                        }
                        DISSECTOR_ASSERT(fd_head->len >= dfpos + fd->len);
-                       if ( memcmp(fd_head->data+dfpos,
+                       if (tvb_memeql(fd_head->tvb_data, dfpos,
                                tvb_get_ptr(tvb,offset,fd->len),fd->len) ){
                                /*
                                 * They have the same length, but the
@@ -1497,19 +1814,23 @@ fragment_add_seq_work(fragment_data *fd_head, tvbuff_t *tvb, const int offset,
 
        /* 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?
         */
        /* check len, there may be a fragment with 0 len, that is actually the tail */
        if (fd->len) {
-               fd->data = g_malloc(fd->len);
-               tvb_memcpy(tvb, fd->data, offset, fd->len);
+               if (!tvb_bytes_exist(tvb, offset, fd->len)) {
+                       /* abort if we didn't capture the entire fragment due
+                        * to a too-short snapshot length */
+                       g_slice_free(fragment_item, fd);
+                       return FALSE;
+               }
+
+               fd->tvb_data = tvb_clone_offset_len(tvb, offset, fd->len);
        }
        LINK_FRAG(fd_head,fd);
 
 
        if( !(fd_head->flags & FD_DATALEN_SET) ){
-               /* if we dont know the sequence number of the last fragment,
+               /* if we don't know the sequence number of the last fragment,
                 * there are definitely still missing packets. Cheaper than
                 * the check below.
                 */
@@ -1562,59 +1883,24 @@ fragment_add_seq_work(fragment_data *fd_head, tvbuff_t *tvb, const int offset,
  * This function assumes frag_number being a block sequence number.
  * The bsn for the first block is 0.
  */
-fragment_data *
-fragment_add_seq(tvbuff_t *tvb, const int offset, const packet_info *pinfo, const guint32 id,
-                GHashTable *fragment_table, const guint32 frag_number,
-                const guint32 frag_data_len, const gboolean more_frags)
-{
-       fragment_key key;
-
-       /* create key to search hash with */
-       key.src = pinfo->src;
-       key.dst = pinfo->dst;
-       key.id  = id;
-
-       return fragment_add_seq_key(tvb, offset, pinfo,
-                                       &key, fragment_key_copy,
-                                       fragment_table, frag_number,
-                                       frag_data_len, more_frags, 0);
-}
-
-
-fragment_data *
-fragment_add_dcerpc_dg(tvbuff_t *tvb, const int offset, const packet_info *pinfo, const guint32 id,
-                                       void *v_act_id,
-                                       GHashTable *fragment_table, const guint32 frag_number,
-                                       const guint32 frag_data_len, const gboolean more_frags)
+static fragment_head *
+fragment_add_seq_common(reassembly_table *table, tvbuff_t *tvb,
+                       const int offset, const packet_info *pinfo,
+                       const guint32 id, const void *data,
+                       guint32 frag_number, const guint32 frag_data_len,
+                       const gboolean more_frags, const guint32 flags,
+                       gpointer *orig_keyp)
 {
-       e_uuid_t *act_id = (e_uuid_t *)v_act_id;
-       dcerpc_fragment_key key;
-
-       /* create key to search hash with */
-       key.src = pinfo->src;
-       key.dst = pinfo->dst;
-       key.id  = id;
-       key.act_id      = *act_id;
-
-       return fragment_add_seq_key(tvb, offset, pinfo,
-                                       &key, dcerpc_fragment_key_copy,
-                                       fragment_table, frag_number,
-                                       frag_data_len, more_frags, 0);
-}
+       fragment_head *fd_head;
+       gpointer orig_key;
 
-fragment_data *
-fragment_add_seq_key(tvbuff_t *tvb, const int offset, const packet_info *pinfo,
-                                        void *key, fragment_key_copier key_copier,
-                                       GHashTable *fragment_table, guint32 frag_number,
-                                       const guint32 frag_data_len, const gboolean more_frags,
-                                       const guint32 flags)
-{
-       fragment_data *fd_head;
-       fd_head = g_hash_table_lookup(fragment_table, key);
+       fd_head = lookup_fd_head(table, pinfo, id, data, &orig_key);
 
        /* have we already seen this frame ?*/
        if (pinfo->fd->flags.visited) {
                if (fd_head != NULL && fd_head->flags & FD_DEFRAGMENTED) {
+                       if (orig_keyp != NULL)
+                               *orig_keyp = orig_key;
                        return fd_head;
                } else {
                        return NULL;
@@ -1623,7 +1909,7 @@ fragment_add_seq_key(tvbuff_t *tvb, const int offset, const packet_info *pinfo,
 
        if (fd_head==NULL){
                /* not found, this must be the first snooped fragment for this
-                                * packet. Create list-head.
+                * packet. Create list-head.
                 */
                fd_head= new_head(FD_BLOCKSEQUENCE);
 
@@ -1643,17 +1929,16 @@ fragment_add_seq_key(tvbuff_t *tvb, const int offset, const packet_info *pinfo,
                         * fragment_add_seq_check will then add it to the table
                         * of reassembled packets.
                         */
-                       fd_head->reassembled_in=pinfo->fd->num;
+                       if (orig_keyp != NULL)
+                               *orig_keyp = NULL;
+                       fd_head->reassembled_in=pinfo->num;
+                       fd_head->reas_in_layer_num = pinfo->curr_layer_num;
                        return fd_head;
                }
 
-               /*
-                * We're going to use the key to insert the fragment,
-                * so copy it to a long-term store.
-                */
-               if(key_copier != NULL)
-                       key = key_copier(key);
-               g_hash_table_insert(fragment_table, key, fd_head);
+               orig_key = insert_fd_head(table, fd_head, pinfo, id, data);
+               if (orig_keyp != NULL)
+                       *orig_keyp = orig_key;
 
                /*
                 * If we weren't given an initial fragment number,
@@ -1662,8 +1947,11 @@ fragment_add_seq_key(tvbuff_t *tvb, const int offset, const packet_info *pinfo,
                if (flags & REASSEMBLE_FLAGS_NO_FRAG_NUMBER)
                        frag_number = 0;
        } else {
+               if (orig_keyp != NULL)
+                       *orig_keyp = orig_key;
+
                if (flags & REASSEMBLE_FLAGS_NO_FRAG_NUMBER) {
-                       fragment_data *fd;
+                       fragment_item *fd;
                        /*
                         * If we weren't given an initial fragment number,
                         * use the next expected fragment number as the fragment
@@ -1676,40 +1964,8 @@ fragment_add_seq_key(tvbuff_t *tvb, const int offset, const packet_info *pinfo,
                }
        }
 
-       /*
-        * XXX I've copied this over from the old separate
-        * fragment_add_seq_check_work, but I'm not convinced it's doing the
-        * right thing -- rav
-        *
-        * If we don't have all the data that is in this fragment,
-        * then we can't, and don't, do reassembly on it.
-        *
-        * If it's the first frame, handle it as an unfragmented packet.
-        * Otherwise, just handle it as a fragment.
-        *
-        * If "more_frags" isn't set, we get rid of the entry in the
-        * hash table for this reassembly, as we don't need it any more.
-        */
-       if ((flags & REASSEMBLE_FLAGS_CHECK_DATA_PRESENT) &&
-               !tvb_bytes_exist(tvb, offset, frag_data_len)) {
-               if (!more_frags) {
-                       gpointer orig_key;
-                       /*
-                        * Remove this from the table of in-progress
-                        * reassemblies, and free up any memory used for
-                        * it in that table.
-                        */
-                       if (g_hash_table_lookup_extended(fragment_table, key,
-                                                        &orig_key, NULL)) {
-                               fragment_unhash(fragment_table, (fragment_key *)orig_key);
-                       }
-               }
-               fd_head -> flags |= FD_DATA_NOT_PRESENT;
-               return frag_number == 0 ? fd_head : NULL;
-       }
-
        if (fragment_add_seq_work(fd_head, tvb, offset, pinfo,
-                                 frag_number, frag_data_len, more_frags, flags)) {
+                                 frag_number, frag_data_len, more_frags)) {
                /*
                 * Reassembly is complete.
                 */
@@ -1722,6 +1978,17 @@ fragment_add_seq_key(tvbuff_t *tvb, const int offset, const packet_info *pinfo,
        }
 }
 
+fragment_head *
+fragment_add_seq(reassembly_table *table, tvbuff_t *tvb, const int offset,
+                const packet_info *pinfo, const guint32 id, const void *data,
+                const guint32 frag_number, const guint32 frag_data_len,
+                const gboolean more_frags, const guint32 flags)
+{
+       return fragment_add_seq_common(table, tvb, offset, pinfo, id, data,
+                                      frag_number, frag_data_len,
+                                      more_frags, flags, NULL);
+}
+
 /*
  * This does the work for "fragment_add_seq_check()" and
  * "fragment_add_seq_next()".
@@ -1729,15 +1996,15 @@ fragment_add_seq_key(tvbuff_t *tvb, const int offset, const packet_info *pinfo,
  * This function assumes frag_number being a block sequence number.
  * The bsn for the first block is 0.
  *
- * If "no_frag_number" is TRUE, it uses the next expected fragment number
+ * If REASSEMBLE_FLAGS_NO_FRAG_NUMBER, it uses the next expected fragment number
  * as the fragment number if there is a reassembly in progress, otherwise
  * it uses 0.
  *
- * If "no_frag_number" is FALSE, it uses the "frag_number" argument as
+ * If not REASSEMBLE_FLAGS_NO_FRAG_NUMBER, it uses the "frag_number" argument as
  * the fragment number.
  *
  * If this is the first fragment seen for this datagram, a new
- * "fragment_data" structure is allocated to refer to the reassembled
+ * "fragment_head" structure is allocated to refer to the reassembled
  * packet.
  *
  * This fragment is added to the linked list of fragments for this packet.
@@ -1756,67 +2023,49 @@ fragment_add_seq_key(tvbuff_t *tvb, const int offset, const packet_info *pinfo,
  *
  * XXX - Should we simply return NULL for zero-length fragments?
  */
-static fragment_data *
-fragment_add_seq_check_work(tvbuff_t *tvb, const int offset,
-                           const packet_info *pinfo, const guint32 id,
-                           GHashTable *fragment_table,
-                           GHashTable *reassembled_table,
+static fragment_head *
+fragment_add_seq_check_work(reassembly_table *table, tvbuff_t *tvb,
+                           const int offset, const packet_info *pinfo,
+                           const guint32 id, const void *data,
                            const guint32 frag_number,
                            const guint32 frag_data_len,
                            const gboolean more_frags, const guint32 flags)
 {
        reassembled_key reass_key;
-       fragment_key key;
-       fragment_data *fd_head;
+       fragment_head *fd_head;
+       gpointer orig_key;
 
        /*
         * Have we already seen this frame?
         * If so, look for it in the table of reassembled packets.
         */
        if (pinfo->fd->flags.visited) {
-               reass_key.frame = pinfo->fd->num;
+               reass_key.frame = pinfo->num;
                reass_key.id = id;
-               return g_hash_table_lookup(reassembled_table, &reass_key);
+               return (fragment_head *)g_hash_table_lookup(table->reassembled_table, &reass_key);
        }
 
-       /* create key to search hash with */
-       key.src = pinfo->src;
-       key.dst = pinfo->dst;
-       key.id  = id;
-
-       fd_head = fragment_add_seq_key(tvb, offset, pinfo,
-                                          &key, fragment_key_copy,
-                                          fragment_table, frag_number,
-                                          frag_data_len, more_frags, flags|REASSEMBLE_FLAGS_CHECK_DATA_PRESENT);
+       fd_head = fragment_add_seq_common(table, tvb, offset, pinfo, id, data,
+                                         frag_number, frag_data_len,
+                                         more_frags,
+                                         flags,
+                                         &orig_key);
        if (fd_head) {
-               gpointer orig_key;
-
-               if(fd_head->flags & FD_DATA_NOT_PRESENT) {
-                       /* this is the first fragment of a datagram with
-                        * truncated fragments. Don't move it to the
-                        * reassembled table. */
-                       return fd_head;
-               }
-
                /*
                 * Reassembly is complete.
-                * Remove this from the table of in-progress
-                * reassemblies, add it to the table of
-                * reassembled packets, and return it.
+                *
+                * If this is in the table of in-progress reassemblies,
+                * remove it from that table.  (It could be that this
+                * was the first and last fragment, so that no
+                * reassembly was done.)
                 */
-               if (g_hash_table_lookup_extended(fragment_table, &key,
-                                                &orig_key, NULL)) {
-                       /*
-                        * Remove this from the table of in-progress reassemblies,
-                        * and free up any memory used for it in that table.
-                        */
-                       fragment_unhash(fragment_table, (fragment_key *)orig_key);
-               }
+               if (orig_key != NULL)
+                       fragment_unhash(table, orig_key);
 
                /*
                 * Add this item to the table of reassembled packets.
                 */
-               fragment_reassembled(fd_head, pinfo, reassembled_table, id);
+               fragment_reassembled(table, fd_head, pinfo, id);
                return fd_head;
        } else {
                /*
@@ -1826,156 +2075,426 @@ fragment_add_seq_check_work(tvbuff_t *tvb, const int offset,
        }
 }
 
-fragment_data *
-fragment_add_seq_check(tvbuff_t *tvb, const int offset,
+fragment_head *
+fragment_add_seq_check(reassembly_table *table, tvbuff_t *tvb, const int offset,
                       const packet_info *pinfo, const guint32 id,
-                      GHashTable *fragment_table,
-                      GHashTable *reassembled_table, const guint32 frag_number,
-                      const guint32 frag_data_len, const gboolean more_frags)
+                      const void *data,
+                      const guint32 frag_number, const guint32 frag_data_len,
+                      const gboolean more_frags)
 {
-       return fragment_add_seq_check_work(tvb, offset, pinfo, id,
-                                          fragment_table, reassembled_table,
+       return fragment_add_seq_check_work(table, tvb, offset, pinfo, id, data,
                                           frag_number, frag_data_len,
                                           more_frags, 0);
 }
 
-fragment_data *
-fragment_add_seq_802_11(tvbuff_t *tvb, const int offset,
-                       const packet_info *pinfo, const guint32 id,
-                       GHashTable *fragment_table,
-                       GHashTable *reassembled_table,
+fragment_head *
+fragment_add_seq_802_11(reassembly_table *table, tvbuff_t *tvb,
+                       const int offset, const packet_info *pinfo,
+                       const guint32 id, const void *data,
                        const guint32 frag_number, const guint32 frag_data_len,
                        const gboolean more_frags)
 {
-       return fragment_add_seq_check_work(tvb, offset, pinfo, id,
-                                          fragment_table, reassembled_table,
+       return fragment_add_seq_check_work(table, tvb, offset, pinfo, id, data,
                                           frag_number, frag_data_len,
                                           more_frags,
                                           REASSEMBLE_FLAGS_802_11_HACK);
 }
 
-fragment_data *
-fragment_add_seq_next(tvbuff_t *tvb, const int offset, const packet_info *pinfo,
-                     const guint32 id, GHashTable *fragment_table,
-                     GHashTable *reassembled_table, const guint32 frag_data_len,
+fragment_head *
+fragment_add_seq_next(reassembly_table *table, tvbuff_t *tvb, const int offset,
+                     const packet_info *pinfo, const guint32 id,
+                     const void *data, const guint32 frag_data_len,
                      const gboolean more_frags)
 {
-       return fragment_add_seq_check_work(tvb, offset, pinfo, id,
-                                          fragment_table, reassembled_table, 0,
-                                          frag_data_len, more_frags,
+       /* Use a dummy frag_number (0), it is ignored since
+        * REASSEMBLE_FLAGS_NO_FRAG_NUMBER is set. */
+       return fragment_add_seq_check_work(table, tvb, offset, pinfo, id, data,
+                                          0, frag_data_len, more_frags,
                                           REASSEMBLE_FLAGS_NO_FRAG_NUMBER);
 }
 
+static void
+fragment_add_seq_single_move(reassembly_table *table, const packet_info *pinfo,
+                            const guint32 id, const void *data,
+                            const guint32 offset)
+{
+       fragment_head *fh, *new_fh;
+       fragment_item *fd, *prev_fd;
+       tvbuff_t *old_tvb_data;
+       if (offset == 0) {
+               return;
+       }
+       fh = lookup_fd_head(table, pinfo, id, data, NULL);
+       if (fh == NULL) {
+               /* Shouldn't be called this way.
+                * Probably wouldn't hurt to just create fh in this case. */
+               g_assert_not_reached();
+               return;
+       }
+       if (fh->flags & FD_DATALEN_SET && fh->datalen <= offset) {
+               /* Don't take from past the end. <= because we don't
+                * want to take a First fragment from the next one
+                * either */
+               return;
+       }
+       new_fh = lookup_fd_head(table, pinfo, id+offset, data, NULL);
+       if (new_fh != NULL) {
+               /* Attach to the end of the sorted list. */
+               for(prev_fd = fh; prev_fd->next != NULL; prev_fd=prev_fd->next) {}
+               /* Don't take a reassembly starting with a First fragment. */
+               fd = new_fh->next;
+               if (fd && fd->offset != 0) {
+                       prev_fd->next = fd;
+                       for (; fd; fd=fd->next) {
+                               fd->offset += offset;
+                               if (fh->frame < fd->frame) {
+                                       fh->frame = fd->frame;
+                               }
+                       }
+                       /* If previously found a Last fragment,
+                        * transfer that info to the new one. */
+                       if (new_fh->flags & FD_DATALEN_SET) {
+                               fh->flags |= FD_DATALEN_SET;
+                               fh->datalen = new_fh->datalen + offset;
+                       }
+                       /* Now remove and delete */
+                       new_fh->next = NULL;
+                       old_tvb_data = fragment_delete(table, pinfo, id+offset, data);
+                       if (old_tvb_data)
+                               tvb_free(old_tvb_data);
+               }
+       }
+}
+
+static fragment_head *
+fragment_add_seq_single_work(reassembly_table *table, tvbuff_t *tvb,
+                            const int offset, const packet_info *pinfo,
+                            const guint32 id, const void* data,
+                            const guint32 frag_data_len,
+                            const gboolean first, const gboolean last,
+                            const guint32 max_frags, const guint32 max_age,
+                            const guint32 flags)
+{
+       reassembled_key reass_key;
+       tvbuff_t *old_tvb_data;
+       gpointer orig_key;
+       fragment_head *fh, *new_fh;
+       fragment_item *fd, *prev_fd;
+       guint32 frag_number, tmp_offset;
+       /* Have we already seen this frame?
+        * If so, look for it in the table of reassembled packets.
+        * Note here we store in the reassembly table by the single sequence
+        * number rather than the sequence number of the First fragment. */
+       if (pinfo->fd->flags.visited) {
+               reass_key.frame = pinfo->num;
+               reass_key.id = id;
+               fh = (fragment_head *)g_hash_table_lookup(table->reassembled_table, &reass_key);
+               return fh;
+       }
+       /* First let's figure out where we want to add our new fragment */
+       fh = NULL;
+       if (first) {
+               frag_number = 0;
+               fh = lookup_fd_head(table, pinfo, id-frag_number, data, NULL);
+               if ((flags & REASSEMBLE_FLAGS_AGING) &&
+                   fh && ((fh->frame + max_age) < pinfo->num)) {
+                       old_tvb_data = fragment_delete(table, pinfo, id-frag_number, data);
+                       if (old_tvb_data)
+                               tvb_free(old_tvb_data);
+                       fh = NULL;
+               }
+               if (fh == NULL) {
+                       /* Not found. Create list-head. */
+                       fh = new_head(FD_BLOCKSEQUENCE);
+                       insert_fd_head(table, fh, pinfo, id-frag_number, data);
+               }
+               /* As this is the first fragment, we might have added segments
+                * for this reassembly to the previous one in-progress. */
+               fd = NULL;
+               for (frag_number=1; frag_number < max_frags; frag_number++) {
+                       new_fh = lookup_fd_head(table, pinfo, id-frag_number, data, NULL);
+                       if (new_fh != NULL) {
+                               prev_fd = new_fh;
+                               new_fh->frame = 0;
+                               for (fd=new_fh->next; fd && fd->offset < frag_number; fd=fd->next) {
+                                       prev_fd = fd;
+                                       if (new_fh->frame < fd->frame) {
+                                               new_fh->frame = fd->frame;
+                                       }
+                               }
+                               prev_fd->next = NULL;
+                               break;
+                       }
+               }
+               if (fd != NULL) {
+                       tmp_offset = 0;
+                       for (prev_fd = fd; prev_fd; prev_fd = prev_fd->next) {
+                               prev_fd->offset -= frag_number;
+                               tmp_offset = prev_fd->offset;
+                               if (fh->frame < prev_fd->frame) {
+                                       fh->frame = prev_fd->frame;
+                               }
+                       }
+                       MERGE_FRAG(fh, fd);
+                       if (new_fh != NULL) {
+                               /* If we've moved a Last packet, change datalen.
+                                * Second part of this test prob. redundant? */
+                               if (new_fh->flags & FD_DATALEN_SET &&
+                                   new_fh->datalen >= frag_number) {
+                                       fh->flags |= FD_DATALEN_SET;
+                                       fh->datalen = new_fh->datalen - frag_number;
+                                       new_fh->flags &= ~FD_DATALEN_SET;
+                                       new_fh->datalen = 0;
+                               }
+                               /* If we've moved all the fragments,
+                                * delete the old head */
+                               if (new_fh->next == NULL) {
+                                       old_tvb_data = fragment_delete(table, pinfo, id-frag_number, data);
+                                       if (old_tvb_data)
+                                               tvb_free(old_tvb_data);
+                               }
+                       } else {
+                       /* Look forward and take off the next (this is
+                        * necessary in some edge cases where max_frags
+                        * prevented some fragments from going on the
+                        * previous First, but they can go on this one. */
+                               fragment_add_seq_single_move(table, pinfo, id,
+                                                            data, tmp_offset);
+                       }
+               }
+               frag_number = 0; /* For the rest of the function */
+       } else {
+               for (frag_number=1; frag_number < max_frags; frag_number++) {
+                       fh = lookup_fd_head(table, pinfo, id-frag_number, data, NULL);
+                       if ((flags & REASSEMBLE_FLAGS_AGING) &&
+                           fh && ((fh->frame + max_age) < pinfo->num)) {
+                               old_tvb_data = fragment_delete(table, pinfo, id-frag_number, data);
+                               if (old_tvb_data)
+                                       tvb_free(old_tvb_data);
+                               fh = NULL;
+                       }
+                       if (fh != NULL) {
+                               if (fh->flags & FD_DATALEN_SET &&
+                                   fh->datalen < frag_number) {
+                                       /* This fragment is after the Last
+                                        * fragment, so must go after here. */
+                                       fh = NULL;
+                               }
+                               break;
+                       }
+               }
+               if (fh == NULL) { /* Didn't find location, use default */
+                       frag_number = 1;
+                       /* Already looked for frag_number 1, so just create */
+                       fh = new_head(FD_BLOCKSEQUENCE);
+                       insert_fd_head(table, fh, pinfo, id-frag_number, data);
+               }
+       }
+       if (last) {
+               /* Look for fragments past the end set by this Last fragment. */
+               prev_fd = fh;
+               for (fd=fh->next; fd && fd->offset <= frag_number; fd=fd->next) {
+                       prev_fd = fd;
+               }
+               /* fd is now all fragments offset > frag_number (the Last).
+                * It shouldn't have a fragment with offset frag_number+1,
+                * as that would be a First fragment not marked as such.
+                * However, this can happen if we had unreassembled fragments
+                * (missing, or at the start of the capture) and we've also
+                * looped around on the sequence numbers. It can also happen
+                * if bit errors mess up Last or First. */
+               if (fd != NULL) {
+                       prev_fd->next = NULL;
+                       fh->frame = 0;
+                       for (prev_fd=fh->next; prev_fd; prev_fd=prev_fd->next) {
+                               if (fh->frame < prev_fd->frame) {
+                                       fh->frame = prev_fd->frame;
+                               }
+                       }
+                       while (fd && fd->offset == frag_number+1) {
+                               /* Definitely have bad data here. Best to
+                                * delete these and leave unreassembled. */
+                               fragment_item *tmp_fd;
+                               tmp_fd=fd->next;
+
+                               if (fd->tvb_data && !(fd->flags & FD_SUBSET_TVB))
+                                       tvb_free(fd->tvb_data);
+                               g_slice_free(fragment_item, fd);
+                               fd=tmp_fd;
+                       }
+               }
+               if (fd != NULL) {
+                       /* Move these onto the next frame. */
+                       new_fh = lookup_fd_head(table, pinfo, id+1, data, NULL);
+                       if (new_fh==NULL) {
+                               /* Not found. Create list-head. */
+                               new_fh = new_head(FD_BLOCKSEQUENCE);
+                               insert_fd_head(table, new_fh, pinfo, id+1, data);
+                       }
+                       tmp_offset = 0;
+                       for (prev_fd = fd; prev_fd; prev_fd = prev_fd->next) {
+                               prev_fd->offset -= (frag_number+1);
+                               tmp_offset = prev_fd->offset;
+                               if (new_fh->frame < fd->frame) {
+                                       new_fh->frame = fd->frame;
+                               }
+                       }
+                       MERGE_FRAG(new_fh, fd);
+                       /* If we previously found a different Last fragment,
+                        * transfer that information to the new reassembly. */
+                       if (fh->flags & FD_DATALEN_SET &&
+                           fh->datalen > frag_number) {
+                               new_fh->flags |= FD_DATALEN_SET;
+                               new_fh->datalen = fh->datalen - (frag_number+1);
+                               fh->flags &= ~FD_DATALEN_SET;
+                               fh->datalen = 0;
+                       } else {
+                       /* Look forward and take off the next (this is
+                        * necessary in some edge cases where max_frags
+                        * prevented some fragments from going on the
+                        * previous First, but they can go on this one. */
+                               fragment_add_seq_single_move(table, pinfo, id+1,
+                                                            data, tmp_offset);
+                       }
+               }
+       } else {
+               fragment_add_seq_single_move(table, pinfo, id-frag_number, data,
+                                            frag_number+1);
+       }
+       /* Having cleaned up everything, finally ready to add our new
+        * fragment. Note that only this will ever complete a reassembly. */
+       fh = fragment_add_seq_common(table, tvb, offset, pinfo,
+                                        id-frag_number, data,
+                                        frag_number, frag_data_len,
+                                        !last, 0, &orig_key);
+       if (fh) {
+               /*
+                * Reassembly is complete.
+                *
+                * If this is in the table of in-progress reassemblies,
+                * remove it from that table.  (It could be that this
+                * was the first and last fragment, so that no
+                * reassembly was done.)
+                */
+               if (orig_key != NULL)
+                       fragment_unhash(table, orig_key);
+
+               /*
+                * Add this item to the table of reassembled packets.
+                */
+               fragment_reassembled_single(table, fh, pinfo, id-frag_number);
+               return fh;
+       } else {
+               /*
+                * Reassembly isn't complete.
+                */
+               return NULL;
+       }
+}
+
+fragment_head *
+fragment_add_seq_single(reassembly_table *table, tvbuff_t *tvb,
+                            const int offset, const packet_info *pinfo,
+                            const guint32 id, const void* data,
+                            const guint32 frag_data_len,
+                            const gboolean first, const gboolean last,
+                            const guint32 max_frags)
+{
+       return fragment_add_seq_single_work(table, tvb, offset, pinfo,
+                                           id, data, frag_data_len,
+                                           first, last, max_frags, 0, 0);
+}
+
+fragment_head *
+fragment_add_seq_single_aging(reassembly_table *table, tvbuff_t *tvb,
+                            const int offset, const packet_info *pinfo,
+                            const guint32 id, const void* data,
+                            const guint32 frag_data_len,
+                            const gboolean first, const gboolean last,
+                            const guint32 max_frags, const guint32 max_age)
+{
+       return fragment_add_seq_single_work(table, tvb, offset, pinfo,
+                                           id, data, frag_data_len,
+                                           first, last, max_frags, max_age,
+                                           REASSEMBLE_FLAGS_AGING);
+}
+
 void
-fragment_start_seq_check(const packet_info *pinfo, const guint32 id, GHashTable *fragment_table,
+fragment_start_seq_check(reassembly_table *table, const packet_info *pinfo,
+                        const guint32 id, const void *data,
                         const guint32 tot_len)
 {
-       fragment_key key, *new_key;
-       fragment_data *fd_head;
+       fragment_head *fd_head;
 
        /* Have we already seen this frame ?*/
        if (pinfo->fd->flags.visited) {
                return;
        }
 
-       /* Create key to search hash with */
-       key.src = pinfo->src;
-       key.dst = pinfo->dst;
-       key.id  = id;
-
-       /* Check if fragment data exist for this key */
-       fd_head = g_hash_table_lookup(fragment_table, &key);
+       /* Check if fragment data exists */
+       fd_head = lookup_fd_head(table, pinfo, id, data, NULL);
 
        if (fd_head == NULL) {
                /* Create list-head. */
-#if GLIB_CHECK_VERSION(2,10,0)
-               fd_head = g_slice_new(fragment_data);
-#else
-               fd_head = g_mem_chunk_alloc(fragment_data_chunk);
-#endif
-
+               fd_head = g_slice_new(fragment_head);
                fd_head->next = NULL;
-               fd_head->datalen = tot_len;
+               fd_head->frame = 0;
                fd_head->offset = 0;
                fd_head->len = 0;
-               fd_head->flags = FD_BLOCKSEQUENCE|FD_DATALEN_SET;
-               fd_head->data = NULL;
+               fd_head->fragment_nr_offset = 0;
+               fd_head->datalen = tot_len;
                fd_head->reassembled_in = 0;
-               /*
-                * We're going to use the key to insert the fragment,
-                * so copy it to a long-term store.
-                */
-               new_key = fragment_key_copy(&key);
-               g_hash_table_insert(fragment_table, new_key, fd_head);
+               fd_head->reas_in_layer_num = 0;
+               fd_head->flags = FD_BLOCKSEQUENCE|FD_DATALEN_SET;
+               fd_head->tvb_data = NULL;
+               fd_head->error = NULL;
+
+               insert_fd_head(table, fd_head, pinfo, id, data);
        }
 }
 
-fragment_data *
-fragment_end_seq_next(const packet_info *pinfo, const guint32 id, GHashTable *fragment_table,
-                         GHashTable *reassembled_table)
+fragment_head *
+fragment_end_seq_next(reassembly_table *table, const packet_info *pinfo,
+                     const guint32 id, const void *data)
 {
        reassembled_key reass_key;
        reassembled_key *new_key;
-       fragment_key key;
-       fragment_data *fd_head;
+       fragment_head *fd_head;
+       gpointer orig_key;
 
        /*
         * Have we already seen this frame?
         * If so, look for it in the table of reassembled packets.
         */
        if (pinfo->fd->flags.visited) {
-               reass_key.frame = pinfo->fd->num;
+               reass_key.frame = pinfo->num;
                reass_key.id = id;
-               return g_hash_table_lookup(reassembled_table, &reass_key);
+               return (fragment_head *)g_hash_table_lookup(table->reassembled_table, &reass_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);
+       fd_head = lookup_fd_head(table, pinfo, id, data, &orig_key);
 
        if (fd_head) {
-               gpointer orig_key;
-
-               if (fd_head->flags & FD_DATA_NOT_PRESENT) {
-                       /* No data added */
-                       return NULL;
-               }
-
                fd_head->datalen = fd_head->offset;
                fd_head->flags |= FD_DATALEN_SET;
 
                fragment_defragment_and_free (fd_head, pinfo);
 
                /*
-                * Remove this from the table of in-progress
-                * reassemblies, add it to the table of
-                * reassembled packets, and return it.
+                * Remove this from the table of in-progress reassemblies,
+                * and free up any memory used for it in that table.
                 */
-               if (g_hash_table_lookup_extended(fragment_table, &key,
-                                                &orig_key, NULL)) {
-                       /*
-                        * Remove this from the table of in-progress reassemblies,
-                        * and free up any memory used for it in that table.
-                        */
-                       fragment_unhash(fragment_table, (fragment_key *)orig_key);
-               }
+               fragment_unhash(table, orig_key);
 
                /*
                 * Add this item to the table of reassembled packets.
                 */
-               fragment_reassembled(fd_head, pinfo, reassembled_table, id);
+               fragment_reassembled(table, fd_head, pinfo, id);
                if (fd_head->next != NULL) {
-#if GLIB_CHECK_VERSION(2,10,0)
                        new_key = g_slice_new(reassembled_key);
-#else
-                       new_key = se_alloc(sizeof(reassembled_key));
-#endif
-                       new_key->frame = pinfo->fd->num;
+                       new_key->frame = pinfo->num;
                        new_key->id = id;
-                       g_hash_table_insert(reassembled_table, new_key, fd_head);
+                       g_hash_table_insert(table->reassembled_table, new_key, fd_head);
                }
 
                return fd_head;
@@ -1995,14 +2514,14 @@ fragment_end_seq_next(const packet_info *pinfo, const guint32 id, GHashTable *fr
  */
 tvbuff_t *
 process_reassembled_data(tvbuff_t *tvb, const int offset, packet_info *pinfo,
-       const char *name, fragment_data *fd_head, const fragment_items *fit,
+       const char *name, fragment_head *fd_head, const fragment_items *fit,
        gboolean *update_col_infop, proto_tree *tree)
 {
        tvbuff_t *next_tvb;
        gboolean update_col_info;
        proto_item *frag_tree_item;
 
-       if (fd_head != NULL && pinfo->fd->num == fd_head->reassembled_in) {
+       if (fd_head != NULL && pinfo->num == fd_head->reassembled_in && pinfo->curr_layer_num == fd_head->reas_in_layer_num) {
                /*
                 * OK, we've reassembled this.
                 * Is this something that's been reassembled from more
@@ -2012,22 +2531,12 @@ process_reassembled_data(tvbuff_t *tvb, const int offset, packet_info *pinfo,
                        /*
                         * Yes.
                         * Allocate a new tvbuff, referring to the
-                        * reassembled payload.
-                        */
-                       if (fd_head->flags & FD_BLOCKSEQUENCE) {
-                               next_tvb = tvb_new_real_data(fd_head->data,
-                                         fd_head->len, fd_head->len);
-                       } else {
-                               next_tvb = tvb_new_real_data(fd_head->data,
-                                         fd_head->datalen, fd_head->datalen);
-                       }
-
-                       /*
-                        * Add the tvbuff to the list of tvbuffs to which
+                        * reassembled payload, and set
+                        * the tvbuff to the list of tvbuffs to which
                         * the tvbuff we were handed refers, so it'll get
                         * cleaned up when that tvbuff is cleaned up.
                         */
-                       tvb_set_child_real_data_tvbuff(tvb, next_tvb);
+                       next_tvb = tvb_new_chain(tvb, fd_head->tvb_data);
 
                        /* Add the defragmented data to the data source list. */
                        add_new_data_source(pinfo, next_tvb, name);
@@ -2078,8 +2587,9 @@ process_reassembled_data(tvbuff_t *tvb, const int offset, packet_info *pinfo,
  * it in the top-level item for that subtree.
  */
 static void
-show_fragment(fragment_data *fd, const int offset, const fragment_items *fit,
-       proto_tree *ft, proto_item *fi, const gboolean first_frag, const guint32 count, tvbuff_t *tvb)
+show_fragment(fragment_item *fd, const int offset, const fragment_items *fit,
+       proto_tree *ft, proto_item *fi, const gboolean first_frag,
+       const guint32 count, tvbuff_t *tvb, packet_info *pinfo)
 {
        proto_item *fei=NULL;
        int hf;
@@ -2091,8 +2601,8 @@ show_fragment(fragment_data *fd, const int offset, const fragment_items *fit,
                } else {
                        name = g_strdup(proto_registrar_get_name(*(fit->hf_fragments)));
                }
-               proto_item_set_text(fi, "%u %s (%u byte%s): ", count, name, tvb_length(tvb),
-                                   plurality(tvb_length(tvb), "", "s"));
+               proto_item_set_text(fi, "%u %s (%u byte%s): ", count, name, tvb_captured_length(tvb),
+                                   plurality(tvb_captured_length(tvb), "", "s"));
                g_free(name);
        } else {
                proto_item_append_text(fi, ", ");
@@ -2123,6 +2633,7 @@ show_fragment(fragment_data *fd, const int offset, const fragment_items *fit,
                        plurality(fd->len, "", "s"));
        }
        PROTO_ITEM_SET_GENERATED(fei);
+       mark_frame_as_depended_upon(pinfo, fd->frame);
        if (fd->flags & (FD_OVERLAP|FD_OVERLAPCONFLICT
                |FD_MULTIPLETAILS|FD_TOOLONGFRAGMENT) ) {
                /* this fragment has some flags set, create a subtree
@@ -2163,16 +2674,13 @@ show_fragment(fragment_data *fd, const int offset, const fragment_items *fit,
 }
 
 static gboolean
-show_fragment_errs_in_col(fragment_data *fd_head, const fragment_items *fit,
+show_fragment_errs_in_col(fragment_head *fd_head, const fragment_items *fit,
        packet_info *pinfo)
 {
        if (fd_head->flags & (FD_OVERLAPCONFLICT
                |FD_MULTIPLETAILS|FD_TOOLONGFRAGMENT) ) {
-               if (check_col(pinfo->cinfo, COL_INFO)) {
-                       col_add_fstr(pinfo->cinfo, COL_INFO,
-                               "[Illegal %s]", fit->tag);
-                       return TRUE;
-               }
+               col_add_fstr(pinfo->cinfo, COL_INFO, "[Illegal %s]", fit->tag);
+               return TRUE;
        }
 
        return FALSE;
@@ -2185,18 +2693,17 @@ show_fragment_errs_in_col(fragment_data *fd_head, const fragment_items *fit,
    or FALSE if fragmentation was ok.
 */
 gboolean
-show_fragment_tree(fragment_data *fd_head, const fragment_items *fit,
+show_fragment_tree(fragment_head *fd_head, const fragment_items *fit,
        proto_tree *tree, packet_info *pinfo, tvbuff_t *tvb, proto_item **fi)
 {
-       fragment_data *fd;
+       fragment_item *fd;
        proto_tree *ft;
        gboolean first_frag;
        guint32 count = 0;
        /* It's not fragmented. */
        pinfo->fragmented = FALSE;
 
-       *fi = proto_tree_add_item(tree, *(fit->hf_fragments),
-               tvb, 0, -1, FALSE);
+       *fi = proto_tree_add_item(tree, *(fit->hf_fragments), tvb, 0, -1, ENC_NA);
        PROTO_ITEM_SET_GENERATED(*fi);
 
        ft = proto_item_add_subtree(*fi, *(fit->ett_fragments));
@@ -2205,7 +2712,7 @@ show_fragment_tree(fragment_data *fd_head, const fragment_items *fit,
                count++;
        }
        for (fd = fd_head->next; fd != NULL; fd = fd->next) {
-               show_fragment(fd, fd->offset, fit, ft, *fi, first_frag, count, tvb);
+               show_fragment(fd, fd->offset, fit, ft, *fi, first_frag, count, tvb, pinfo);
                first_frag = FALSE;
        }
 
@@ -2217,7 +2724,13 @@ show_fragment_tree(fragment_data *fd_head, const fragment_items *fit,
 
        if (fit->hf_reassembled_length) {
                proto_item *fli = proto_tree_add_uint(ft, *(fit->hf_reassembled_length),
-                                                     tvb, 0, 0, tvb_length (tvb));
+                                                     tvb, 0, 0, tvb_captured_length (tvb));
+               PROTO_ITEM_SET_GENERATED(fli);
+       }
+
+       if (fit->hf_reassembled_data) {
+               proto_item *fli = proto_tree_add_item(ft, *(fit->hf_reassembled_data),
+                                                     tvb, 0, tvb_captured_length(tvb), ENC_NA);
                PROTO_ITEM_SET_GENERATED(fli);
        }
 
@@ -2231,19 +2744,18 @@ show_fragment_tree(fragment_data *fd_head, const fragment_items *fit,
    or FALSE if fragmentation was ok.
 */
 gboolean
-show_fragment_seq_tree(fragment_data *fd_head, const fragment_items *fit,
+show_fragment_seq_tree(fragment_head *fd_head, const fragment_items *fit,
        proto_tree *tree, packet_info *pinfo, tvbuff_t *tvb, proto_item **fi)
 {
        guint32 offset, next_offset, count = 0;
-       fragment_data *fd, *last_fd;
+       fragment_item *fd, *last_fd;
        proto_tree *ft;
        gboolean first_frag;
 
        /* It's not fragmented. */
        pinfo->fragmented = FALSE;
 
-       *fi = proto_tree_add_item(tree, *(fit->hf_fragments),
-               tvb, 0, -1, FALSE);
+       *fi = proto_tree_add_item(tree, *(fit->hf_fragments), tvb, 0, -1, ENC_NA);
        PROTO_ITEM_SET_GENERATED(*fi);
 
        ft = proto_item_add_subtree(*fi, *(fit->ett_fragments));
@@ -2260,7 +2772,7 @@ show_fragment_seq_tree(fragment_data *fd_head, const fragment_items *fit,
                        next_offset += fd->len;
                }
                last_fd = fd;
-               show_fragment(fd, offset, fit, ft, *fi, first_frag, count, tvb);
+               show_fragment(fd, offset, fit, ft, *fi, first_frag, count, tvb, pinfo);
                first_frag = FALSE;
        }
 
@@ -2272,17 +2784,75 @@ show_fragment_seq_tree(fragment_data *fd_head, const fragment_items *fit,
 
        if (fit->hf_reassembled_length) {
                proto_item *fli = proto_tree_add_uint(ft, *(fit->hf_reassembled_length),
-                                                     tvb, 0, 0, tvb_length (tvb));
+                                                     tvb, 0, 0, tvb_captured_length (tvb));
+               PROTO_ITEM_SET_GENERATED(fli);
+       }
+
+       if (fit->hf_reassembled_data) {
+               proto_item *fli = proto_tree_add_item(ft, *(fit->hf_reassembled_data),
+                                                     tvb, 0, tvb_captured_length(tvb), ENC_NA);
                PROTO_ITEM_SET_GENERATED(fli);
        }
 
        return show_fragment_errs_in_col(fd_head, fit, pinfo);
 }
 
+static void
+reassembly_table_init_reg_table(gpointer p, gpointer user_data _U_)
+{
+       register_reassembly_table_t* reg_table = (register_reassembly_table_t*)p;
+       reassembly_table_init(reg_table->table, reg_table->funcs);
+}
+
+static void
+reassembly_table_init_reg_tables(void)
+{
+       g_list_foreach(reassembly_table_list, reassembly_table_init_reg_table, NULL);
+}
+
+static void
+reassembly_table_cleanup_reg_table(gpointer p, gpointer user_data _U_)
+{
+       register_reassembly_table_t* reg_table = (register_reassembly_table_t*)p;
+       reassembly_table_destroy(reg_table->table);
+}
+
+static void
+reassembly_table_cleanup_reg_tables(void)
+{
+       g_list_foreach(reassembly_table_list, reassembly_table_cleanup_reg_table, NULL);
+}
+
+void reassembly_tables_init(void)
+{
+       register_init_routine(&reassembly_table_init_reg_tables);
+       register_cleanup_routine(&reassembly_table_cleanup_reg_tables);
+}
+
+static void
+reassembly_table_free(gpointer p, gpointer user_data _U_)
+{
+       register_reassembly_table_t* reg_table = (register_reassembly_table_t*)p;
+       reassembly_table_destroy(reg_table->table);
+       g_free(reg_table);
+}
+
+void
+reassembly_table_cleanup(void)
+{
+       g_list_foreach(reassembly_table_list, reassembly_table_free, NULL);
+       g_list_free(reassembly_table_list);
+}
+
 /*
- * Local Variables:
+ * Editor modelines  -  http://www.wireshark.org/tools/modelines.html
+ *
+ * Local variables:
  * c-basic-offset: 8
- * indent-tabs-mode: t
  * tab-width: 8
+ * indent-tabs-mode: t
  * End:
+ *
+ * vi: set shiftwidth=8 tabstop=8 noexpandtab:
+ * :indentSize=8:tabSize=8:noTabs=false:
  */