nexmon – Rev 1

Subversion Repositories:
Rev:
/* reassemble.c
 * Routines for {fragment,segment} reassembly
 *
 * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
 */

#include "config.h"

#include <string.h>

#include <epan/packet.h>
#include <epan/exceptions.h>
#include <epan/reassemble.h>
#include <epan/tvbuff-int.h>

#include <wsutil/str_util.h>

/*
 * 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_addresses_key;

static guint
fragment_addresses_hash(gconstpointer k)
{
        const fragment_addresses_key* key = (const fragment_addresses_key*) k;
        guint hash_val;
/*
        int i;
*/

        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;
}

static gint
fragment_addresses_equal(gconstpointer k1, gconstpointer k2)
{
        const fragment_addresses_key* key1 = (const fragment_addresses_key*) k1;
        const fragment_addresses_key* key2 = (const fragment_addresses_key*) k2;

        /*
         * 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));
}

/*
 * 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_)
{
        fragment_addresses_key *key = g_slice_new(fragment_addresses_key);

        /*
         * 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;
}

/*
 * 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 (gpointer)key;
}

static void
fragment_addresses_free_temporary_key(gpointer ptr)
{
        fragment_addresses_key *key = (fragment_addresses_key *)ptr;

        if(key)
                g_slice_free(fragment_addresses_key, key);
}

static void
fragment_addresses_free_persistent_key(gpointer ptr)
{
        fragment_addresses_key *key = (fragment_addresses_key *)ptr;

        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_addresses_ports_hash(gconstpointer k)
{
        const fragment_addresses_ports_key* key = (const fragment_addresses_ports_key*) k;
        guint hash_val;
/*
        int i;
*/

        hash_val = 0;

/*      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_addr.data[i];
        for (i = 0; i < key->dst.len; i++)
                hash_val += key->dst_addr.data[i];
        hash_val += key->src_port;
        hash_val += key->dst_port;
*/

        hash_val += key->id;

        return hash_val;
}

static gint
fragment_addresses_ports_equal(gconstpointer k1, gconstpointer 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 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);
}

/*
 * 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_)
{
        fragment_addresses_ports_key *key = g_slice_new(fragment_addresses_ports_key);

        /*
         * 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;

        return (gpointer)key;
}

/*
 * 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;

        if(key)
                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;
} reassembled_key;

static gint
reassembled_equal(gconstpointer k1, gconstpointer k2)
{
        const reassembled_key* key1 = (const reassembled_key*) k1;
        const reassembled_key* key2 = (const reassembled_key*) k2;

        /*
         * We assume that the frame numbers are unlikely to be equal,
         * so we check them first.
         */
        return key1->frame == key2->frame && key1->id == key2->id;
}

static guint
reassembled_hash(gconstpointer k)
{
        const reassembled_key* key = (const reassembled_key*) 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.
 * 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_head *fd_head;
        fragment_item *tmp_fd;

        /* g_hash_table_new_full() was used to supply a function
         * to free the key and anything to which it points
         */
        for (fd_head = (fragment_head *)value; fd_head != NULL; fd_head = tmp_fd) {
                tmp_fd=fd_head->next;

                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_head *new_head(const guint32 flags)
{
        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
        */
        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 and also the key itself.
 */
static gboolean
free_all_reassembled_fragments(gpointer key_arg _U_, gpointer value,
                                   gpointer user_data)
{
        GPtrArray *allocated_fragments = (GPtrArray *) user_data;
        fragment_head *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;
                }
        }

        return TRUE;
}

static void
free_fragments(gpointer data, gpointer user_data _U_)
{
        fragment_item *fd_head = (fragment_item *) data;

        if (fd_head->tvb_data)
                tvb_free(fd_head->tvb_data);
        g_slice_free(fragment_item, fd_head);
}

/*
 * Initialize a reassembly table, with specified functions.
 */
void
reassembly_table_init(reassembly_table *table,
                      const reassembly_table_functions *funcs)
{
        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.
                 *
                 * 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);
        } else {
                /* The fragment table does not exist. Create it */
                table->fragment_table = g_hash_table_new_full(funcs->hash_func,
                    funcs->equal_func, funcs->free_persistent_key_func, NULL);
        }

        if (table->reassembled_table != NULL) {
                GPtrArray *allocated_fragments;

                /*
                 * The reassembled-packet hash table exists.
                 *
                 * Remove all entries and free reassembled packet
                 * data and key for each entry.
                 */

                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 {
                /* The fragment table does not exist. Create it */
                table->reassembled_table = g_hash_table_new_full(reassembled_hash,
                    reassembled_equal, reassembled_key_free, NULL);
        }
}

/*
 * Destroy a reassembly table.
 */
void
reassembly_table_destroy(reassembly_table *table)
{
        /*
         * 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 and key for each entry.
                 */

                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;
        }
}

/*
 * Look up an fd_head in the fragment table, optionally returning the key
 * for it.
 */
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;
}

/*
 * 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 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 tvb_free() that tvbuff.
 */
tvbuff_t *
fragment_delete(reassembly_table *table, const packet_info *pinfo,
                const guint32 id, const void *data)
{
        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;
        }

        fd_tvb_data=fd_head->tvb_data;
        /* loop over all partial fragments and free any tvbuffs */
        for(fd=fd_head->next;fd;){
                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;
        }
        g_slice_free(fragment_head, fd_head);
        g_hash_table_remove(table->fragment_table, key);

        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_head *
fragment_get(reassembly_table *table, const packet_info *pinfo,
             const guint32 id, const void *data)
{
        return lookup_fd_head(table, pinfo, id, data, NULL);
}

/* id *must* be the frame number for this to work! */
fragment_head *
fragment_get_reassembled(reassembly_table *table, const guint32 id)
{
        fragment_head *fd_head;
        reassembled_key key;

        /* create key to search hash with */
        key.frame = id;
        key.id = id;
        fd_head = (fragment_head *)g_hash_table_lookup(table->reassembled_table, &key);

        return fd_head;
}

fragment_head *
fragment_get_reassembled_id(reassembly_table *table, const packet_info *pinfo,
                            const guint32 id)
{
        fragment_head *fd_head;
        reassembled_key key;

        /* create key to search hash with */
        key.frame = pinfo->num;
        key.id = id;
        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
 * in the first fragment instead of as for, say, IPv4 where a flag indicates which
 * is the last fragment.
 *
 * Such protocols might fragment_add with a more_frags==TRUE for every fragment
 * and just tell the reassembly engine the expected total length of the reassembled data
 * using fragment_set_tot_len immediately after doing fragment_add for the first packet.
 *
 * Note that for FD_BLOCKSEQUENCE tot_len is the index for the tail fragment.
 * i.e. since the block numbers start at 0, if we specify tot_len==2, that
 * actually means we want to defragment 3 blocks, block 0, 1 and 2.
 */
void
fragment_set_tot_len(reassembly_table *table, const packet_info *pinfo,
                     const guint32 id, const void *data, const guint32 tot_len)
{
        fragment_head *fd_head;
        fragment_item *fd;
        guint32        max_offset = 0;

        fd_head = lookup_fd_head(table, pinfo, id, data, NULL);
        if (!fd_head)
                return;

        /* 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->flags & FD_DEFRAGMENTED) {
                if (max_offset != tot_len) {
                        fd_head->error = "Defragmented complete but total length not satisfied";
                        THROW_MESSAGE(ReassemblyError, fd_head->error);
                }
        }

        /* 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(reassembly_table *table, const packet_info *pinfo,
                     const guint32 id, const void *data)
{
        fragment_head *fd_head;

        fd_head = lookup_fd_head(table, pinfo, id, data, NULL);

        if(fd_head){
                return fd_head->datalen;
        }

        return 0;
}


/* This function will set the partial reassembly flag for a fh.
   When this function is called, the fh MUST already exist, i.e.
   the fh MUST be created by the initial call to fragment_add() before
   this function is called.
   Also note that this function MUST be called to indicate a fh will be
   extended (increase the already stored data)
*/

void
fragment_set_partial_reassembly(reassembly_table *table,
                                const packet_info *pinfo, const guint32 id,
                                const void *data)
{
        fragment_head *fd_head;

        fd_head = lookup_fd_head(table, pinfo, id, data, NULL);

        /*
         * XXX - why not do all the stuff done early in "fragment_add_work()",
         * turning off FD_DEFRAGMENTED and pointing the fragments' data
         * pointers to the appropriate part of the already-reassembled
         * data, and clearing the data length and "reassembled in" frame
         * number, here?  We currently have a hack in the TCP dissector
         * not to set the "reassembled in" value if the "partial reassembly"
         * flag is set, so that in the first pass through the packets
         * we don't falsely set a packet as reassembled in that packet
         * if the dissector decided that even more reassembly was needed.
         */
        if(fd_head){
                fd_head->flags |= FD_PARTIAL_REASSEMBLY;
        }
}

/*
 * This function gets rid of an entry from a fragment table, given
 * a pointer to the key for that entry.
 *
 * The key freeing routine will be called by g_hash_table_remove().
 */
static void
fragment_unhash(reassembly_table *table, gpointer key)
{
        /*
         * Remove the entry from the fragment table.
         */
        g_hash_table_remove(table->fragment_table, key);
}

/*
 * 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(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;
                        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;
}

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;
}

/*
 * This function adds a new fragment to the fragment hash table.
 * If this is the first fragment seen for this datagram, a new entry
 * is created in the hash table, otherwise this fragment is just added
 * to the linked list of fragments for this packet.
 * The list of fragments for a specific datagram is kept sorted for
 * easier handling.
 *
 * Returns a pointer to the head of the fragment data list if we have all the
 * fragments, NULL otherwise.
 *
 * This function assumes frag_offset being a byte offset into the defragment
 * packet.
 *
 * 01-2002
 * Once the fh is defragmented (= FD_DEFRAGMENTED set), it can be
 * extended using the FD_PARTIAL_REASSEMBLY flag. This flag should be set
 * using fragment_set_partial_reassembly() before calling fragment_add
 * with the new fragment. FD_TOOLONGFRAGMENT and FD_MULTIPLETAILS flags
 * are lowered when a new extension process is started.
 */
static gboolean
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_item *fd;
        fragment_item *fd_i;
        guint32 max, dfpos, fraglen;
        tvbuff_t *old_tvb_data;
        guint8 *data;

        /* create new fd describing this fragment */
        fd = g_slice_new(fragment_item);
        fd->next = NULL;
        fd->flags = 0;
        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->tvb_data = NULL;
        fd->error = NULL;

        /*
         * Are we adding to an already-completed reassembly?
         */
        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?)");
                                }
                        }
                } 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?)");
                }
        }

        /* 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.
                 */
                if (fd_head->flags & FD_DATALEN_SET) {
                        /* ok we have already seen other tails for this packet
                         * it might be a duplicate.
                         */
                        if (fd_head->datalen != (fd->offset + fd->len) ){
                                /* Oops, this tail indicates a different packet
                                 * len than the previous ones. Something's wrong.
                                 */
                                fd->flags          |= FD_MULTIPLETAILS;
                                fd_head->flags |= FD_MULTIPLETAILS;
                        }
                } else {
                        /* 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;
                }
        }



        /* If the packet is already defragmented, this MUST be an overlap.
         * The entire defragmented packet is in fd_head->data.
         * Even if we have previously defragmented this packet, we still
         * check it. Someone might play overlap and TTL games.
         */
        if (fd_head->flags & FD_DEFRAGMENTED) {
                guint32 end_offset = fd->offset + fd->len;
                fd->flags          |= FD_OVERLAP;
                fd_head->flags |= FD_OVERLAP;
                /* make sure it's not too long */
                if (end_offset > fd_head->datalen || end_offset < fd->offset || end_offset < fd->len) {
                        fd->flags          |= FD_TOOLONGFRAGMENT;
                        fd_head->flags |= FD_TOOLONGFRAGMENT;
                }
                /* make sure it doesn't conflict with previous data */
                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;
                }
                /* it was just an overlap, link it and return */
                LINK_FRAG(fd_head,fd);
                return TRUE;
        }



        /* If we have reached this point, the packet is not defragmented yet.
         * Save all payload in a buffer until we can defragment.
         */
        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 don't know the datalen, there are still missing
                 * packets. Cheaper than the check below.
                 */
                return FALSE;
        }


        /*
         * Check if we have received the entire fragment.
         * This is easy since the list is sorted and the head is faked.
         *
         * First, we compute the amount of contiguous data that's
         * available.  (The check for fd_i->offset <= max rules out
         * fragments that don't start before or at the end of the
         * previous fragment, i.e. fragments that have a gap between
         * them and the previous fragment.)
         */
        max = 0;
        for (fd_i=fd_head->next;fd_i;fd_i=fd_i->next) {
                if ( ((fd_i->offset)<=max) &&
                        ((fd_i->offset+fd_i->len)>max) ){
                        max = fd_i->offset+fd_i->len;
                }
        }

        if (max < (fd_head->datalen)) {
                /*
                 * The amount of contiguous data we have is less than the
                 * amount of data we're trying to reassemble, so we haven't
                 * received all packets yet.
                 */
                return FALSE;
        }

        /* we have received an entire packet, defragment it and
         * free all fragments
         */
        /* store old data just in case */
        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) {
                        /*
                         * 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_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(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;
                                                }
                                        }
                                        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? */
                                        fd_head->error = "offset + len < offset";
                                }
                        }

                        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;
                }
        }

        if (old_tvb_data)
                tvb_add_to_chain(tvb, 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->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_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_head *fd_head;
        fragment_item *fd_item;
        gboolean already_added;


        /*
         * 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));

        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->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",
                                        fd_item->frame, fd_item->offset, fd_item->len, fd_item->datalen);
                        }
                }
        }
#endif

        /*
         * Is this the first pass through the capture?
         */
        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;
                                        }
                                }
                        }
                }
        } 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.
                 */
                fd_head = new_head(0);

                /*
                 * Insert it into the hash table.
                 */
                insert_fd_head(table, fd_head, pinfo, id, data);
        }

        if (fragment_add_work(fd_head, tvb, offset, pinfo, frag_offset,
                frag_data_len, more_frags)) {
                /*
                 * Reassembly is complete.
                 */
                return fd_head;
        } else {
                /*
                 * Reassembly isn't complete.
                 */
                return NULL;
        }
}

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(table, tvb, offset, pinfo, id, data,
                frag_offset, frag_data_len, more_frags, TRUE);
}

/*
 * 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_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(table, tvb, offset, pinfo, id, data,
                frag_offset, frag_data_len, more_frags, FALSE);
}

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_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->num;
                reass_key.id = id;
                return (fragment_head *)g_hash_table_lookup(table->reassembled_table, &reass_key);
        }

        /* 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()
         */
        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.
                 */
                fd_head = new_head(0);

                /*
                 * Save the key, for unhashing it later.
                 */
                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_captured_length(tvb))
                return NULL;

        if (fragment_add_work(fd_head, tvb, offset, pinfo, frag_offset,
                frag_data_len, more_frags)) {
                /*
                 * Reassembly is complete.
                 * 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.
                 */
                fragment_unhash(table, orig_key);

                /*
                 * Add this item to the table of reassembled packets.
                 */
                fragment_reassembled(table, fd_head, pinfo, id);
                return fd_head;
        } else {
                /*
                 * Reassembly isn't complete.
                 */
                return NULL;
        }
}

static void
fragment_defragment_and_free (fragment_head *fd_head, const packet_info *pinfo)
{
        fragment_item *fd_i = NULL;
        fragment_item *last_fd = NULL;
        guint32  dfpos = 0, size = 0;
        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){
                        size+=fd_i->len;
                }
                last_fd=fd_i;
        }

        /* store old data in case the fd_i->data pointers refer to it */
        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 */
        last_fd=NULL;
        for (fd_i=fd_head->next; fd_i; fd_i=fd_i->next) {
                if (fd_i->len) {
                        if(!last_fd || last_fd->offset != fd_i->offset) {
                                /* First fragment or in-sequence fragment */
                                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
                                   || 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;
                                }
                        }
                }
                last_fd=fd_i;
        }

        /* we have defragmented the pdu, now free all fragments*/
        for (fd_i=fd_head->next;fd_i;fd_i=fd_i->next) {
                if (fd_i->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;
        }
        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->num;
        fd_head->reas_in_layer_num = pinfo->curr_layer_num;
}

/*
 * This function adds a new fragment to the entry for a reassembly
 * operation.
 *
 * The list of fragments for a specific datagram is kept sorted for
 * easier handling.
 *
 * Returns TRUE if we have all the fragments, FALSE otherwise.
 *
 * This function assumes frag_number being a block sequence number.
 * The bsn for the first block is 0.
 */
static gboolean
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)
{
        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_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->tvb_data ) {
                                if( fd_i->flags & FD_OVERLAP ) {
                                        /* this is a duplicate of the previous
                                         * fragment. */
                                        fd_i->tvb_data = tvb_new_subset_remaining(fd_head->tvb_data, lastdfpos);
                                } else {
                                        fd_i->tvb_data = tvb_new_subset_remaining(fd_head->tvb_data, dfpos);
                                        lastdfpos = dfpos;
                                        dfpos += fd_i->len;
                                }
                                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;
        }


        /* create new fd describing this fragment */
        fd = g_slice_new(fragment_item);
        fd->next = NULL;
        fd->flags = 0;
        fd->frame = pinfo->num;
        fd->offset = frag_number_work;
        fd->len  = frag_data_len;
        fd->tvb_data = NULL;
        fd->error = NULL;

        if (!more_frags) {
                /*
                 * This is the tail fragment in the sequence.
                 */
                if (fd_head->flags&FD_DATALEN_SET) {
                        /* ok we have already seen other tails for this packet
                         * it might be a duplicate.
                         */
                        if (fd_head->datalen != fd->offset ){
                                /* Oops, this tail indicates a different packet
                                 * len than the previous ones. Something's wrong.
                                 */
                                fd->flags       |= FD_MULTIPLETAILS;
                                fd_head->flags  |= FD_MULTIPLETAILS;
                        }
                } else {
                        /* this was the first tail fragment, now we know the
                         * sequence number of that fragment (which is NOT
                         * the length of the packet!)
                         */
                        fd_head->datalen = fd->offset;
                        fd_head->flags |= FD_DATALEN_SET;
                }
        }

        /* If the packet is already defragmented, this MUST be an overlap.
         * The entire defragmented packet is in fd_head->data
         * Even if we have previously defragmented this packet, we still check
         * check it. Someone might play overlap and TTL games.
         */
        if (fd_head->flags & FD_DEFRAGMENTED) {
                fd->flags       |= FD_OVERLAP;
                fd_head->flags  |= FD_OVERLAP;

                /* make sure it's not past the end */
                if (fd->offset > fd_head->datalen) {
                        /* new fragment comes after the end */
                        fd->flags       |= FD_TOOLONGFRAGMENT;
                        fd_head->flags  |= FD_TOOLONGFRAGMENT;
                        LINK_FRAG(fd_head,fd);
                        return TRUE;
                }
                /* make sure it doesn't conflict with previous data */
                dfpos=0;
                last_fd=NULL;
                for (fd_i=fd_head->next;fd_i && (fd_i->offset!=fd->offset);fd_i=fd_i->next) {
                  if (!last_fd || last_fd->offset!=fd_i->offset){
                        dfpos += fd_i->len;
                  }
                  last_fd=fd_i;
                }
                if(fd_i){
                        /* new fragment overlaps existing fragment */
                        if(fd_i->len!=fd->len){
                                /*
                                 * They have different lengths; this
                                 * is definitely a conflict.
                                 */
                                fd->flags       |= FD_OVERLAPCONFLICT;
                                fd_head->flags  |= FD_OVERLAPCONFLICT;
                                LINK_FRAG(fd_head,fd);
                                return TRUE;
                        }
                        DISSECTOR_ASSERT(fd_head->len >= dfpos + fd->len);
                        if (tvb_memeql(fd_head->tvb_data, dfpos,
                                tvb_get_ptr(tvb,offset,fd->len),fd->len) ){
                                /*
                                 * They have the same length, but the
                                 * data isn't the same.
                                 */
                                fd->flags       |= FD_OVERLAPCONFLICT;
                                fd_head->flags  |= FD_OVERLAPCONFLICT;
                                LINK_FRAG(fd_head,fd);
                                return TRUE;
                        }
                        /* it was just an overlap, link it and return */
                        LINK_FRAG(fd_head,fd);
                        return TRUE;
                } else {
                        /*
                         * New fragment doesn't overlap an existing
                         * fragment - there was presumably a gap in
                         * the sequence number space.
                         *
                         * XXX - what should we do here?  Is it always
                         * the case that there are no gaps, or are there
                         * protcols using sequence numbers where there
                         * can be gaps?
                         *
                         * If the former, the check below for having
                         * received all the fragments should check for
                         * holes in the sequence number space and for the
                         * first sequence number being 0.  If we do that,
                         * the only way we can get here is if this fragment
                         * is past the end of the sequence number space -
                         * but the check for "fd->offset > fd_head->datalen"
                         * would have caught that above, so it can't happen.
                         *
                         * If the latter, we don't have a good way of
                         * knowing whether reassembly is complete if we
                         * get packet out of order such that the "last"
                         * fragment doesn't show up last - but, unless
                         * in-order reliable delivery of fragments is
                         * guaranteed, an implementation of the protocol
                         * has no way of knowing whether reassembly is
                         * complete, either.
                         *
                         * For now, we just link the fragment in and
                         * return.
                         */
                        LINK_FRAG(fd_head,fd);
                        return TRUE;
                }
        }

        /* If we have reached this point, the packet is not defragmented yet.
         * Save all payload in a buffer until we can defragment.
         */
        /* check len, there may be a fragment with 0 len, that is actually the tail */
        if (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 don't know the sequence number of the last fragment,
                 * there are definitely still missing packets. Cheaper than
                 * the check below.
                 */
                return FALSE;
        }


        /* check if we have received the entire fragment
         * this is easy since the list is sorted and the head is faked.
         * common case the whole list is scanned.
         */
        max = 0;
        for(fd_i=fd_head->next;fd_i;fd_i=fd_i->next) {
          if ( fd_i->offset==max ){
                max++;
          }
        }
        /* max will now be datalen+1 if all fragments have been seen */

        if (max <= fd_head->datalen) {
                /* we have not received all packets yet */
                return FALSE;
        }


        if (max > (fd_head->datalen+1)) {
                /* oops, too long fragment detected */
                fd->flags       |= FD_TOOLONGFRAGMENT;
                fd_head->flags  |= FD_TOOLONGFRAGMENT;
        }


        /* we have received an entire packet, defragment it and
         * free all fragments
         */
        fragment_defragment_and_free(fd_head, pinfo);

        return TRUE;
}

/*
 * This function adds a new fragment to the fragment hash table.
 * If this is the first fragment seen for this datagram, a new entry
 * is created in the hash table, otherwise this fragment is just added
 * to the linked list of fragments for this packet.
 *
 * Returns a pointer to the head of the fragment data list if we have all the
 * fragments, NULL otherwise.
 *
 * This function assumes frag_number being a block sequence number.
 * The bsn for the first block is 0.
 */
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)
{
        fragment_head *fd_head;
        gpointer orig_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;
                }
        }

        if (fd_head==NULL){
                /* not found, this must be the first snooped fragment for this
                 * packet. Create list-head.
                 */
                fd_head= new_head(FD_BLOCKSEQUENCE);

                if((flags & (REASSEMBLE_FLAGS_NO_FRAG_NUMBER|REASSEMBLE_FLAGS_802_11_HACK))
                   && !more_frags) {
                        /*
                         * This is the last fragment for this packet, and
                         * is the only one we've seen.
                         *
                         * Either we don't have sequence numbers, in which
                         * case we assume this is the first fragment for
                         * this packet, or we're doing special 802.11
                         * processing, in which case we assume it's one
                         * of those reassembled packets with a non-zero
                         * fragment number (see packet-80211.c); just
                         * return a pointer to the head of the list;
                         * fragment_add_seq_check will then add it to the table
                         * of reassembled packets.
                         */
                        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;
                }

                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,
                 * make it 0.
                 */
                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_item *fd;
                        /*
                         * If we weren't given an initial fragment number,
                         * use the next expected fragment number as the fragment
                         * number for this fragment.
                         */
                        for (fd = fd_head; fd != NULL; fd = fd->next) {
                                if (fd->next == NULL)
                                        frag_number = fd->offset + 1;
                        }
                }
        }

        if (fragment_add_seq_work(fd_head, tvb, offset, pinfo,
                                  frag_number, frag_data_len, more_frags)) {
                /*
                 * Reassembly is complete.
                 */
                return fd_head;
        } else {
                /*
                 * Reassembly isn't complete.
                 */
                return NULL;
        }
}

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()".
 *
 * This function assumes frag_number being a block sequence number.
 * The bsn for the first block is 0.
 *
 * 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 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_head" structure is allocated to refer to the reassembled
 * packet.
 *
 * This fragment is added to the linked list of fragments for this packet.
 *
 * If "more_frags" is false and REASSEMBLE_FLAGS_802_11_HACK (as the name
 * implies, a special hack for 802.11) or REASSEMBLE_FLAGS_NO_FRAG_NUMBER
 * (implying messages must be in order since there's no sequence number) are
 * set in "flags", then this (one element) list is returned.
 *
 * If, after processing this fragment, we have all the fragments,
 * "fragment_add_seq_check_work()" removes that from the fragment hash
 * table if necessary and adds it to the table of reassembled fragments,
 * and returns a pointer to the head of the fragment list.
 *
 * Otherwise, it returns NULL.
 *
 * XXX - Should we simply return NULL for zero-length fragments?
 */
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_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->num;
                reass_key.id = id;
                return (fragment_head *)g_hash_table_lookup(table->reassembled_table, &reass_key);
        }

        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) {
                /*
                 * 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(table, fd_head, pinfo, id);
                return fd_head;
        } else {
                /*
                 * Reassembly isn't complete.
                 */
                return NULL;
        }
}

fragment_head *
fragment_add_seq_check(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(table, tvb, offset, pinfo, id, data,
                                           frag_number, frag_data_len,
                                           more_frags, 0);
}

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(table, tvb, offset, pinfo, id, data,
                                           frag_number, frag_data_len,
                                           more_frags,
                                           REASSEMBLE_FLAGS_802_11_HACK);
}

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)
{
        /* 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);
}

void
fragment_start_seq_check(reassembly_table *table, const packet_info *pinfo,
                         const guint32 id, const void *data,
                         const guint32 tot_len)
{
        fragment_head *fd_head;

        /* Have we already seen this frame ?*/
        if (pinfo->fd->flags.visited) {
                return;
        }

        /* Check if fragment data exists */
        fd_head = lookup_fd_head(table, pinfo, id, data, NULL);

        if (fd_head == NULL) {
                /* Create list-head. */
                fd_head = g_slice_new(fragment_head);
                fd_head->next = NULL;
                fd_head->frame = 0;
                fd_head->offset = 0;
                fd_head->len = 0;
                fd_head->fragment_nr_offset = 0;
                fd_head->datalen = tot_len;
                fd_head->reassembled_in = 0;
                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_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_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->num;
                reass_key.id = id;
                return (fragment_head *)g_hash_table_lookup(table->reassembled_table, &reass_key);
        }

        fd_head = lookup_fd_head(table, pinfo, id, data, &orig_key);

        if (fd_head) {
                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,
                 * and free up any memory used for it in that table.
                 */
                fragment_unhash(table, orig_key);

                /*
                 * Add this item to the table of reassembled packets.
                 */
                fragment_reassembled(table, fd_head, pinfo, id);
                if (fd_head->next != NULL) {
                        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);
                }

                return fd_head;
        } else {
                /*
                 * Fragment data not found.
                 */
                return NULL;
        }
}

/*
 * Process reassembled data; if we're on the frame in which the data
 * was reassembled, put the fragment information into the protocol
 * tree, and construct a tvbuff with the reassembled data, otherwise
 * just put a "reassembled in" item into the protocol tree.
 */
tvbuff_t *
process_reassembled_data(tvbuff_t *tvb, const int offset, packet_info *pinfo,
        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->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
                 * than one fragment?
                 */
                if (fd_head->next != NULL) {
                        /*
                         * Yes.
                         * Allocate a new tvbuff, referring to the
                         * 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.
                         */
                        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);

                        /* show all fragments */
                        if (fd_head->flags & FD_BLOCKSEQUENCE) {
                                update_col_info = !show_fragment_seq_tree(
                                        fd_head, fit,  tree, pinfo, next_tvb, &frag_tree_item);
                        } else {
                                update_col_info = !show_fragment_tree(fd_head,
                                        fit, tree, pinfo, next_tvb, &frag_tree_item);
                        }
                } else {
                        /*
                         * No.
                         * Return a tvbuff with the payload.
                         */
                        next_tvb = tvb_new_subset_remaining(tvb, offset);
                        pinfo->fragmented = FALSE;      /* one-fragment packet */
                        update_col_info = TRUE;
                }
                if (update_col_infop != NULL)
                        *update_col_infop = update_col_info;
        } else {
                /*
                 * We don't have the complete reassembled payload, or this
                 * isn't the final frame of that payload.
                 */
                next_tvb = NULL;

                /*
                 * If we know what frame this was reassembled in,
                 * and if there's a field to use for the number of
                 * the frame in which the packet was reassembled,
                 * add it to the protocol tree.
                 */
                if (fd_head != NULL && fit->hf_reassembled_in != NULL) {
                        proto_tree_add_uint(tree,
                                *(fit->hf_reassembled_in), tvb,
                                0, 0, fd_head->reassembled_in);
                }
        }
        return next_tvb;
}

/*
 * Show a single fragment in a fragment subtree, and put information about
 * it in the top-level item for that subtree.
 */
static void
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;

        if (first_frag) {
                gchar *name;
                if (count == 1) {
                        name = g_strdup(proto_registrar_get_name(*(fit->hf_fragment)));
                } else {
                        name = g_strdup(proto_registrar_get_name(*(fit->hf_fragments)));
                }
                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, ", ");
        }
        proto_item_append_text(fi, "#%u(%u)", fd->frame, fd->len);

        if (fd->flags & (FD_OVERLAPCONFLICT
                |FD_MULTIPLETAILS|FD_TOOLONGFRAGMENT) ) {
                hf = *(fit->hf_fragment_error);
        } else {
                hf = *(fit->hf_fragment);
        }
        if (fd->len == 0) {
                fei = proto_tree_add_uint_format(ft, hf,
                        tvb, offset, fd->len,
                        fd->frame,
                        "Frame: %u (no data)",
                        fd->frame);
        } else {
                fei = proto_tree_add_uint_format(ft, hf,
                        tvb, offset, fd->len,
                        fd->frame,
                        "Frame: %u, payload: %u-%u (%u byte%s)",
                        fd->frame,
                        offset,
                        offset+fd->len-1,
                        fd->len,
                        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
                 * for it and display the flags.
                 */
                proto_tree *fet=NULL;

                fet = proto_item_add_subtree(fei, *(fit->ett_fragment));
                if (fd->flags&FD_OVERLAP) {
                        fei=proto_tree_add_boolean(fet,
                                *(fit->hf_fragment_overlap),
                                tvb, 0, 0,
                                TRUE);
                        PROTO_ITEM_SET_GENERATED(fei);
                }
                if (fd->flags&FD_OVERLAPCONFLICT) {
                        fei=proto_tree_add_boolean(fet,
                                *(fit->hf_fragment_overlap_conflict),
                                tvb, 0, 0,
                                TRUE);
                        PROTO_ITEM_SET_GENERATED(fei);
                }
                if (fd->flags&FD_MULTIPLETAILS) {
                        fei=proto_tree_add_boolean(fet,
                                *(fit->hf_fragment_multiple_tails),
                                tvb, 0, 0,
                                TRUE);
                        PROTO_ITEM_SET_GENERATED(fei);
                }
                if (fd->flags&FD_TOOLONGFRAGMENT) {
                        fei=proto_tree_add_boolean(fet,
                                *(fit->hf_fragment_too_long_fragment),
                                tvb, 0, 0,
                                TRUE);
                        PROTO_ITEM_SET_GENERATED(fei);
                }
        }
}

static gboolean
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) ) {
                col_add_fstr(pinfo->cinfo, COL_INFO, "[Illegal %s]", fit->tag);
                return TRUE;
        }

        return FALSE;
}

/* This function will build the fragment subtree; it's for fragments
   reassembled with "fragment_add()".

   It will return TRUE if there were fragmentation errors
   or FALSE if fragmentation was ok.
*/
gboolean
show_fragment_tree(fragment_head *fd_head, const fragment_items *fit,
        proto_tree *tree, packet_info *pinfo, tvbuff_t *tvb, proto_item **fi)
{
        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, ENC_NA);
        PROTO_ITEM_SET_GENERATED(*fi);

        ft = proto_item_add_subtree(*fi, *(fit->ett_fragments));
        first_frag = TRUE;
        for (fd = fd_head->next; fd != NULL; fd = fd->next) {
                count++;
        }
        for (fd = fd_head->next; fd != NULL; fd = fd->next) {
                show_fragment(fd, fd->offset, fit, ft, *fi, first_frag, count, tvb, pinfo);
                first_frag = FALSE;
        }

        if (fit->hf_fragment_count) {
                proto_item *fli = proto_tree_add_uint(ft, *(fit->hf_fragment_count),
                                                      tvb, 0, 0, count);
                PROTO_ITEM_SET_GENERATED(fli);
        }

        if (fit->hf_reassembled_length) {
                proto_item *fli = proto_tree_add_uint(ft, *(fit->hf_reassembled_length),
                                                      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);
}

/* This function will build the fragment subtree; it's for fragments
   reassembled with "fragment_add_seq()" or "fragment_add_seq_check()".

   It will return TRUE if there were fragmentation errors
   or FALSE if fragmentation was ok.
*/
gboolean
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_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, ENC_NA);
        PROTO_ITEM_SET_GENERATED(*fi);

        ft = proto_item_add_subtree(*fi, *(fit->ett_fragments));
        offset = 0;
        next_offset = 0;
        last_fd = NULL;
        first_frag = TRUE;
        for (fd = fd_head->next; fd != NULL; fd = fd->next){
                count++;
        }
        for (fd = fd_head->next; fd != NULL; fd = fd->next){
                if (last_fd == NULL || last_fd->offset != fd->offset) {
                        offset = next_offset;
                        next_offset += fd->len;
                }
                last_fd = fd;
                show_fragment(fd, offset, fit, ft, *fi, first_frag, count, tvb, pinfo);
                first_frag = FALSE;
        }

        if (fit->hf_fragment_count) {
                proto_item *fli = proto_tree_add_uint(ft, *(fit->hf_fragment_count),
                                                      tvb, 0, 0, count);
                PROTO_ITEM_SET_GENERATED(fli);
        }

        if (fit->hf_reassembled_length) {
                proto_item *fli = proto_tree_add_uint(ft, *(fit->hf_reassembled_length),
                                                      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);
}

/*
 * Editor modelines  -  http://www.wireshark.org/tools/modelines.html
 *
 * Local variables:
 * c-basic-offset: 8
 * tab-width: 8
 * indent-tabs-mode: t
 * End:
 *
 * vi: set shiftwidth=8 tabstop=8 noexpandtab:
 * :indentSize=8:tabSize=8:noTabs=false:
 */