aboutsummaryrefslogtreecommitdiffstats
path: root/reassemble.c
diff options
context:
space:
mode:
authorGuy Harris <guy@alum.mit.edu>2002-04-17 08:25:05 +0000
committerGuy Harris <guy@alum.mit.edu>2002-04-17 08:25:05 +0000
commited3b0cae650759d673f0898febb1f6518774c9f6 (patch)
tree621bae3a8d9d7445807c25615036ede4a0beac26 /reassemble.c
parent8241e67243d65ff53072dadb2bcfa7c0dc06f316 (diff)
Add a separate hash table to the reassembly code for reassembled
packets, using the reassembly ID and the frame number of the final frame as the key. There is no guarantee that reassembly IDs won't be reused, even when talking between the same source and destination address; if, once reassembly is complete, the "fragment_data" structure is moved to the latter hash table, this will keep reused reassembly IDs from causing mis-reassembly. Add a routine "fragment_add_seq_check()", which if a fragment has the "more fragments" flag not set but is the first fragment of a reassembly, treats that as a non-fragmented frame, allocating a "fragment_data" structure for the reassembly but not attaching any fragment to it, and adding it to a reassembled packet list; if a packet has been reassembled, removes it from the table of reassemblies and moves it to the table of reassembled packets; if the frame's been seen already, looks it up in the table of reassembled packets rather than the table of reassemblies. Add reassembly support for fragmented 802.11 frames. Use "fragment_add_seq_check()" to cope with the fact that some hardware+drivers apparently hands us reassembled frames with a non-zero fragment number and the "more fragments" bit clear (as if it puts the 802.11 header of the *last* fragment onto the reassembled data). svn path=/trunk/; revision=5177
Diffstat (limited to 'reassemble.c')
-rw-r--r--reassemble.c410
1 files changed, 343 insertions, 67 deletions
diff --git a/reassemble.c b/reassemble.c
index 8122fadb9e..26a3dd849b 100644
--- a/reassemble.c
+++ b/reassemble.c
@@ -1,7 +1,7 @@
/* reassemble.c
* Routines for {fragment,segment} reassembly
*
- * $Id: reassemble.c,v 1.11 2002/04/17 04:54:30 guy Exp $
+ * $Id: reassemble.c,v 1.12 2002/04/17 08:25:04 guy Exp $
*
* Ethereal - Network traffic analyzer
* By Gerald Combs <gerald@ethereal.com>
@@ -38,7 +38,13 @@ typedef struct _fragment_key {
guint32 id;
} fragment_key;
+typedef struct _reassembled_key {
+ guint32 frame;
+ guint32 id;
+} reassembled_key;
+
static GMemChunk *fragment_key_chunk = NULL;
+static GMemChunk *reassembled_key_chunk = NULL;
static GMemChunk *fragment_data_chunk = NULL;
static int fragment_init_count = 200;
@@ -96,9 +102,43 @@ fragment_hash(gconstpointer k)
return hash_val;
}
+static gint
+reassembled_equal(gconstpointer k1, gconstpointer k2)
+{
+ reassembled_key* key1 = (reassembled_key*) k1;
+ reassembled_key* key2 = (reassembled_key*) k2;
+
+ /*key.frame is the first item to compare since item is most
+ likely to differ between sessions, thus shortcircuiting
+ the comparasion of addresses.
+ */
+ return ( ( (key1->frame == key2->frame) &&
+ (key1->id == key2->id)
+ ) ?
+ TRUE : FALSE);
+}
+
+static guint
+reassembled_hash(gconstpointer k)
+{
+ reassembled_key* key = (reassembled_key*) k;
+ guint hash_val;
+
+ hash_val = 0;
+
+/* The frame number is probably good enough as a hash value;
+ there's probably not going to be too many reassembled
+ packets that end at the same frame.
+*/
+
+ hash_val = key->frame;
+
+ return hash_val;
+}
+
/*
- * For a hash table entry, free the address data to which the key refers
- * and the fragment data to which the value refers.
+ * For a fragment hash table entry, free the address data to which the key
+ * refers and the fragment data to which the value refers.
* (The actual key and value structures get freed by "reassemble_init()".)
*/
static gboolean
@@ -134,6 +174,25 @@ free_all_fragments(gpointer key_arg, gpointer value, gpointer user_data _U_)
}
/*
+ * For a reassembled-packet hash table entry, free the fragment data
+ * to which the value refers.
+ * (The actual key and value structures get freed by "reassemble_init()".)
+ */
+static gboolean
+free_all_reassembled_fragments(gpointer key_arg _U_, gpointer value,
+ gpointer user_data _U_)
+{
+ fragment_data *fd_head;
+
+ for (fd_head = value; fd_head != NULL; fd_head = fd_head->next) {
+ if(fd_head->data && !(fd_head->flags&FD_NOT_MALLOCED))
+ g_free(fd_head->data);
+ }
+
+ return TRUE;
+}
+
+/*
* Initialize a fragment table.
*/
void
@@ -157,6 +216,29 @@ fragment_table_init(GHashTable **fragment_table)
}
/*
+ * Initialize a reassembled-packet table.
+ */
+void
+reassembled_table_init(GHashTable **reassembled_table)
+{
+ if (*reassembled_table != NULL) {
+ /*
+ * The reassembled-packet hash table exists.
+ *
+ * Remove all entries and free fragment data for
+ * each entry. (The key and value data is freed
+ * by "reassemble_init()".)
+ */
+ g_hash_table_foreach_remove(*reassembled_table,
+ free_all_reassembled_fragments, NULL);
+ } else {
+ /* The fragment table does not exist. Create it */
+ *reassembled_table = g_hash_table_new(reassembled_hash,
+ reassembled_equal);
+ }
+}
+
+/*
* Free up all space allocated for fragment keys and data.
*/
void
@@ -164,12 +246,18 @@ reassemble_init(void)
{
if (fragment_key_chunk != NULL)
g_mem_chunk_destroy(fragment_key_chunk);
+ if (reassembled_key_chunk != NULL)
+ g_mem_chunk_destroy(reassembled_key_chunk);
if (fragment_data_chunk != NULL)
g_mem_chunk_destroy(fragment_data_chunk);
fragment_key_chunk = g_mem_chunk_new("fragment_key_chunk",
sizeof(fragment_key),
fragment_init_count * sizeof(fragment_key),
G_ALLOC_ONLY);
+ reassembled_key_chunk = g_mem_chunk_new("reassembled_key_chunk",
+ sizeof(reassembled_key),
+ fragment_init_count * sizeof(reassembled_key),
+ G_ALLOC_ONLY);
fragment_data_chunk = g_mem_chunk_new("fragment_data_chunk",
sizeof(fragment_data),
fragment_init_count * sizeof(fragment_data),
@@ -304,6 +392,7 @@ fragment_set_partial_reassembly(packet_info *pinfo, guint32 id, GHashTable *frag
fd_head->flags |= FD_PARTIAL_REASSEMBLY;
}
}
+
/*
* This function adds a new fragment to the fragment hash table.
* If this is the first fragment seen for this datagram, a new entry
@@ -554,79 +643,28 @@ fragment_add(tvbuff_t *tvb, int offset, packet_info *pinfo, guint32 id,
return fd_head;
}
-
-
/*
- * This function adds a new fragment to the fragment hash table.
- * If this is the first fragment seen for this datagram, a new entry
- * is created in the hash table, otherwise this fragment is just added
- * to the linked list of fragments for this packet.
+ * 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 a pointer to the head of the fragment data list if we have all the
- * fragments, NULL otherwise.
+ * 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.
*/
-fragment_data *
-fragment_add_seq(tvbuff_t *tvb, int offset, packet_info *pinfo, guint32 id,
- GHashTable *fragment_table, guint32 frag_number,
+static gboolean
+fragment_add_seq_work(fragment_data *fd_head, tvbuff_t *tvb, int offset,
+ packet_info *pinfo, guint32 id, guint32 frag_number,
guint32 frag_data_len, gboolean more_frags)
{
- fragment_key key, *new_key;
- fragment_data *fd_head;
fragment_data *fd;
fragment_data *fd_i;
fragment_data *last_fd;
guint32 max, dfpos, size;
- /* create key to search hash with */
- key.src = pinfo->src;
- key.dst = pinfo->dst;
- key.id = id;
-
- fd_head = g_hash_table_lookup(fragment_table, &key);
-
- /* have we already seen this frame ?*/
- if (pinfo->fd->flags.visited) {
- if (fd_head != NULL && fd_head->flags & FD_DEFRAGMENTED) {
- return fd_head;
- } else {
- return NULL;
- }
- }
-
- if (fd_head==NULL){
- /* not found, this must be the first snooped fragment for this
- * packet. Create list-head.
- */
- fd_head=g_mem_chunk_alloc(fragment_data_chunk);
- /* head/first structure in list only holds no other data than
- * 'datalen' then we don't have to change the head of the list
- * even if we want to keep it sorted
- */
- fd_head->next=NULL;
- fd_head->datalen=0;
- fd_head->offset=0;
- fd_head->len=0;
- fd_head->flags=FD_BLOCKSEQUENCE;
- fd_head->data=NULL;
-
- /*
- * We're going to use the key to insert the fragment,
- * so allocate a structure for it, and copy the
- * addresses, allocating new buffers for the address
- * data.
- */
- new_key = g_mem_chunk_alloc(fragment_key_chunk);
- COPY_ADDRESS(&new_key->src, &key.src);
- COPY_ADDRESS(&new_key->dst, &key.dst);
- new_key->id = key.id;
- g_hash_table_insert(fragment_table, new_key, fd_head);
- }
-
/* create new fd describing this fragment */
fd = g_mem_chunk_alloc(fragment_data_chunk);
fd->next = NULL;
@@ -673,7 +711,7 @@ fragment_add_seq(tvbuff_t *tvb, int offset, packet_info *pinfo, guint32 id,
fd->flags |= FD_TOOLONGFRAGMENT;
fd_head->flags |= FD_TOOLONGFRAGMENT;
LINK_FRAG(fd_head,fd);
- return (fd_head);
+ return TRUE;
}
/* make sure it doesnt conflict with previous data */
dfpos=0;
@@ -684,18 +722,18 @@ fragment_add_seq(tvbuff_t *tvb, int offset, packet_info *pinfo, guint32 id,
fd->flags |= FD_OVERLAPCONFLICT;
fd_head->flags |= FD_OVERLAPCONFLICT;
LINK_FRAG(fd_head,fd);
- return (fd_head);
+ return TRUE;
}
if ( memcmp(fd_head->data+dfpos,
tvb_get_ptr(tvb,offset,fd->len),fd->len) ){
fd->flags |= FD_OVERLAPCONFLICT;
fd_head->flags |= FD_OVERLAPCONFLICT;
LINK_FRAG(fd_head,fd);
- return (fd_head);
+ return TRUE;
}
/* it was just an overlap, link it and return */
LINK_FRAG(fd_head,fd);
- return (fd_head);
+ return TRUE;
}
/* If we have reached this point, the packet is not defragmented yet.
@@ -729,7 +767,7 @@ fragment_add_seq(tvbuff_t *tvb, int offset, packet_info *pinfo, guint32 id,
if (max <= fd_head->datalen) {
/* we have not received all packets yet */
- return NULL;
+ return FALSE;
}
@@ -785,5 +823,243 @@ fragment_add_seq(tvbuff_t *tvb, int offset, packet_info *pinfo, guint32 id,
allows us to skip any trailing fragments */
fd_head->flags |= FD_DEFRAGMENTED;
- return fd_head;
+ 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.
+ */
+fragment_data *
+fragment_add_seq(tvbuff_t *tvb, int offset, packet_info *pinfo, guint32 id,
+ GHashTable *fragment_table, guint32 frag_number,
+ guint32 frag_data_len, gboolean more_frags)
+{
+ fragment_key key, *new_key;
+ fragment_data *fd_head;
+ fragment_data *fd;
+ fragment_data *fd_i;
+ fragment_data *last_fd;
+ guint32 max, dfpos, size;
+
+ /* create key to search hash with */
+ key.src = pinfo->src;
+ key.dst = pinfo->dst;
+ key.id = id;
+
+ fd_head = g_hash_table_lookup(fragment_table, &key);
+
+ /* have we already seen this frame ?*/
+ if (pinfo->fd->flags.visited) {
+ if (fd_head != NULL && fd_head->flags & FD_DEFRAGMENTED) {
+ return fd_head;
+ } else {
+ return NULL;
+ }
+ }
+
+ if (fd_head==NULL){
+ /* not found, this must be the first snooped fragment for this
+ * packet. Create list-head.
+ */
+ fd_head=g_mem_chunk_alloc(fragment_data_chunk);
+ /* head/first structure in list only holds no other data than
+ * 'datalen' then we don't have to change the head of the list
+ * even if we want to keep it sorted
+ */
+ fd_head->next=NULL;
+ fd_head->datalen=0;
+ fd_head->offset=0;
+ fd_head->len=0;
+ fd_head->flags=FD_BLOCKSEQUENCE;
+ fd_head->data=NULL;
+
+ /*
+ * We're going to use the key to insert the fragment,
+ * so allocate a structure for it, and copy the
+ * addresses, allocating new buffers for the address
+ * data.
+ */
+ new_key = g_mem_chunk_alloc(fragment_key_chunk);
+ COPY_ADDRESS(&new_key->src, &key.src);
+ COPY_ADDRESS(&new_key->dst, &key.dst);
+ new_key->id = key.id;
+ g_hash_table_insert(fragment_table, new_key, fd_head);
+ }
+
+ if (fragment_add_seq_work(fd_head, tvb, offset, pinfo, id,
+ frag_number, frag_data_len, more_frags)) {
+ /*
+ * Reassembly is complete.
+ */
+ return fd_head;
+ } else {
+ /*
+ * Reassembly isn't complete.
+ */
+ return NULL;
+ }
+}
+
+/*
+ * This function adds fragment_data structure to a reassembled-packet
+ * hash table, using the reassembly ID and frame number as the key.
+ */
+static void
+fragment_reassembled(fragment_data *fd_head, packet_info *pinfo, guint32 id,
+ GHashTable *reassembled_table)
+{
+ reassembled_key *new_key;
+
+ new_key = g_mem_chunk_alloc(reassembled_key_chunk);
+ new_key->frame = pinfo->fd->num;
+ new_key->id = id;
+ g_hash_table_insert(reassembled_table, new_key, fd_head);
+}
+
+/*
+ * This function adds a new fragment to the fragment hash table.
+ * If this is the first fragment seen for this datagram, a new
+ * "fragment_data" structure is allocated to refer to the reassembled,
+ * packet, and:
+ *
+ * if "more_frags" is false, the structure is not added to
+ * the hash table, and not given any fragments to refer to,
+ * but is just returned;
+ *
+ * if "more_frags" is true, this fragment is added to the linked
+ * list of fragments for this packet, and the "fragment_data"
+ * structure is put into 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, and removes
+ * that from the fragment hash table if necessary and adds it to the
+ * table of reassembled fragments, if we have all the fragments or if
+ * this is the only fragment and "more_frags" is false, returns NULL
+ * otherwise.
+ *
+ * This function assumes frag_number being a block sequence number.
+ * The bsn for the first block is 0.
+ */
+fragment_data *
+fragment_add_seq_check(tvbuff_t *tvb, int offset, packet_info *pinfo,
+ guint32 id, GHashTable *fragment_table,
+ GHashTable *reassembled_table, guint32 frag_number,
+ guint32 frag_data_len, gboolean more_frags)
+{
+ reassembled_key reass_key;
+ fragment_key key, *new_key, *old_key;
+ gpointer orig_key, value;
+ fragment_data *fd_head;
+ fragment_data *fd;
+ fragment_data *fd_i;
+ fragment_data *last_fd;
+ guint32 max, dfpos, size;
+
+ /*
+ * Have we already seen this frame?
+ * If so, look for it in the table of reassembled packets.
+ */
+ if (pinfo->fd->flags.visited) {
+ reass_key.frame = pinfo->fd->num;
+ reass_key.id = id;
+
+ return g_hash_table_lookup(reassembled_table, &reass_key);
+ }
+
+ /* create key to search hash with */
+ key.src = pinfo->src;
+ key.dst = pinfo->dst;
+ key.id = id;
+
+ if (!g_hash_table_lookup_extended(fragment_table, &key,
+ &orig_key, &value)) {
+ /* not found, this must be the first snooped fragment for this
+ * packet. Create list-head.
+ */
+ fd_head=g_mem_chunk_alloc(fragment_data_chunk);
+
+ /* head/first structure in list only holds no other data than
+ * 'datalen' then we don't have to change the head of the list
+ * even if we want to keep it sorted
+ */
+ fd_head->next=NULL;
+ fd_head->datalen=0;
+ fd_head->offset=0;
+ fd_head->len=0;
+ fd_head->flags=FD_BLOCKSEQUENCE;
+ fd_head->data=NULL;
+
+ if (!more_frags) {
+ /*
+ * This is the last snooped fragment for this
+ * packet as well; that means it's the only
+ * fragment. Just add it to the table of
+ * reassembled packets, and return it.
+ */
+ fragment_reassembled(fd_head, pinfo, id,
+ reassembled_table);
+ return fd_head;
+ }
+
+ /*
+ * We're going to use the key to insert the fragment,
+ * so allocate a structure for it, and copy the
+ * addresses, allocating new buffers for the address
+ * data.
+ */
+ new_key = g_mem_chunk_alloc(fragment_key_chunk);
+ COPY_ADDRESS(&new_key->src, &key.src);
+ COPY_ADDRESS(&new_key->dst, &key.dst);
+ new_key->id = key.id;
+ g_hash_table_insert(fragment_table, new_key, fd_head);
+ } else {
+ /*
+ * We found it.
+ */
+ fd_head = value;
+ }
+
+ if (fragment_add_seq_work(fd_head, tvb, offset, pinfo, id,
+ frag_number, 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.
+ */
+
+ /*
+ * Free up the copies of the addresses from the old key.
+ */
+ old_key = orig_key;
+ g_free((gpointer)old_key->src.data);
+ g_free((gpointer)old_key->dst.data);
+
+ /*
+ * Remove the entry from the fragment table.
+ */
+ g_hash_table_remove(fragment_table, &key);
+
+ /*
+ * Add it to the table of reassembled packets.
+ */
+ fragment_reassembled(fd_head, pinfo, id, reassembled_table);
+ return fd_head;
+ } else {
+ /*
+ * Reassembly isn't complete.
+ */
+ return NULL;
+ }
}