diff options
author | Guy Harris <guy@alum.mit.edu> | 2003-04-20 00:11:28 +0000 |
---|---|---|
committer | Guy Harris <guy@alum.mit.edu> | 2003-04-20 00:11:28 +0000 |
commit | 0def9a0b52a6bbca03a55ea15759e8724dd2ac08 (patch) | |
tree | 8bcbaf35b1f4fb610dc2165847f7baf6f721e487 /reassemble.c | |
parent | d067b0e361bd049fa3a608a788343a984291291c (diff) | |
download | wireshark-0def9a0b52a6bbca03a55ea15759e8724dd2ac08.tar.gz wireshark-0def9a0b52a6bbca03a55ea15759e8724dd2ac08.tar.bz2 wireshark-0def9a0b52a6bbca03a55ea15759e8724dd2ac08.zip |
We can't use the frame_data structure as a key structure when looking
for reassembled frames - in Tethereal, there's only one frame_data
structure used for all frames. Instead, use the frame number itself as
the key.
Add a "fragment_add_check()" routine, for fragments where there's a
fragment offset rather than a fragment sequence number, which does the
same sort of thing as "fragment_add_seq_check()" - i.e., once reassembly
is done, it puts the reassembled fragment into a separate hash table, so
that there're only incomplete reassemblies in the fragment hash table.
That's necessary in order to handle cases where the packet ID field can
be reused.
Use that routine for IPv4 fragment reassembly - IP IDs can be reused (in
fact, RFC 791 suggests that doing so might be a feature:
It is appropriate for some higher level protocols to choose the
identifier. For example, TCP protocol modules may retransmit an
identical TCP segment, and the probability for correct reception
would be enhanced if the retransmission carried the same identifier
as the original transmission since fragments of either datagram
could be used to construct a correct TCP segment.
and RFC 1122 says that it's permitted to do so, although it also says
"we believe that retransmitting the same Identification field is not
useful":
3.2.1.5 Identification: RFC-791 Section 3.2
When sending an identical copy of an earlier datagram, a
host MAY optionally retain the same Identification field in
the copy.
DISCUSSION:
Some Internet protocol experts have maintained that
when a host sends an identical copy of an earlier
datagram, the new copy should contain the same
Identification value as the original. There are two
suggested advantages: (1) if the datagrams are
fragmented and some of the fragments are lost, the
receiver may be able to reconstruct a complete datagram
from fragments of the original and the copies; (2) a
congested gateway might use the IP Identification field
(and Fragment Offset) to discard duplicate datagrams
from the queue.
However, the observed patterns of datagram loss in the
Internet do not favor the probability of retransmitted
fragments filling reassembly gaps, while other
mechanisms (e.g., TCP repacketizing upon
retransmission) tend to prevent retransmission of an
identical datagram [IP:9]. Therefore, we believe that
retransmitting the same Identification field is not
useful. Also, a connectionless transport protocol like
UDP would require the cooperation of the application
programs to retain the same Identification value in
identical datagrams.
and, in any case, I've seen that in at least one capture, and it
confuses the current reassembly code).
Unfortunately, that means that fragments other than the last fragment
can't be tagged with the frame number in which the reassembly was done;
see the comment in packet-ip.c for a discussion of that problem.
svn path=/trunk/; revision=7506
Diffstat (limited to 'reassemble.c')
-rw-r--r-- | reassemble.c | 379 |
1 files changed, 248 insertions, 131 deletions
diff --git a/reassemble.c b/reassemble.c index 383285a4dd..a113c14c15 100644 --- a/reassemble.c +++ b/reassemble.c @@ -1,7 +1,7 @@ /* reassemble.c * Routines for {fragment,segment} reassembly * - * $Id: reassemble.c,v 1.32 2003/04/19 09:42:53 guy Exp $ + * $Id: reassemble.c,v 1.33 2003/04/20 00:11:28 guy Exp $ * * Ethereal - Network traffic analyzer * By Gerald Combs <gerald@ethereal.com> @@ -98,8 +98,8 @@ fragment_hash(gconstpointer k) } /* - * XXX - we use the frame_data structure for a frame as the key - * structure, with the frame number as the item compared. + * XXX - we use the frame number as the key (we can't use the frame_data + * structure, as in Tethereal there's only one such structure). * * This won't work if there's more than one form of reassembly using * the reassembled-packet hash tables going on in the frame, and two @@ -115,18 +115,13 @@ fragment_hash(gconstpointer k) static gint reassembled_equal(gconstpointer k1, gconstpointer k2) { - const frame_data* key1 = (const frame_data*) k1; - const frame_data* key2 = (const frame_data*) k2; - - return (key1->num == key2->num); + return ((guint32)k1 == (guint32)k2); } static guint reassembled_hash(gconstpointer k) { - const frame_data* key = (const frame_data*) k; - - return key->num; + return (guint32)k; } /* @@ -402,6 +397,43 @@ fragment_set_partial_reassembly(packet_info *pinfo, guint32 id, GHashTable *frag } /* + * This function gets rid of an entry from a fragment table, given + * a pointer to the key for that entry; it also frees up the key + * and the addresses in it. + */ +static void +fragment_unhash(GHashTable *fragment_table, fragment_key *key) +{ + /* + * Free up the copies of the addresses from the old key. + */ + g_free((gpointer)key->src.data); + g_free((gpointer)key->dst.data); + + /* + * Remove the entry from the fragment table. + */ + g_hash_table_remove(fragment_table, key); + + /* + * Free the key itself. + */ + g_mem_chunk_free(fragment_key_chunk, key); +} + +/* + * This function adds fragment_data structure to a reassembled-packet + * hash table, using the frame data structure as the key. + */ +void +fragment_reassembled(fragment_data *fd_head, packet_info *pinfo, + GHashTable *reassembled_table) +{ + g_hash_table_insert(reassembled_table, (gpointer)pinfo->fd->num, + 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 @@ -422,89 +454,15 @@ fragment_set_partial_reassembly(packet_info *pinfo, guint32 id, GHashTable *frag * with the new fragment. FD_TOOLONGFRAGMENT and FD_MULTIPLETAILS flags * are lowered when a new extension process is started. */ -fragment_data * -fragment_add(tvbuff_t *tvb, int offset, packet_info *pinfo, guint32 id, - GHashTable *fragment_table, guint32 frag_offset, +static gboolean +fragment_add_work(fragment_data *fd_head, tvbuff_t *tvb, int offset, + packet_info *pinfo, guint32 frag_offset, guint32 frag_data_len, gboolean more_frags) { - fragment_key key, *new_key; - fragment_data *fd_head; - fragment_data *fd_item; fragment_data *fd; fragment_data *fd_i; guint32 max, dfpos; unsigned char *old_data; - gboolean already_added=pinfo->fd->flags.visited; - - /* 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); - - /* - * "already_added" is true if "pinfo->fd->flags.visited" is true; - * if "pinfo->fd->flags.visited", this isn't the first pass, so - * we've already done all the reassembly and added all the - * fragments. - * - * If it's not true, just check if we have seen this fragment before, - * i.e., if we have already added it to reassembly. - * That can be true even if "pinfo->fd->flags.visited" is false - * since we sometimes might call a subdissector multiple times. - * As an additional check, just make sure we have not already added - * this frame to the reassembly list, if there is a reassembly list; - * note that the first item in the reassembly list is not a - * fragment, it's a data structure for the reassembled packet. - * We don't check it because its "frame" member isn't initialized - * to anything, and because it doesn't count in any case. - */ - if (!already_added && fd_head != NULL) { - for(fd_item=fd_head->next;fd_item;fd_item=fd_item->next){ - if(pinfo->fd->num==fd_item->frame){ - already_added=TRUE; - } - } - } - /* have we already added this frame ?*/ - if (already_added) { - if (fd_head != NULL && fd_head->flags & FD_DEFRAGMENTED) { - return fd_head; - } else { - return NULL; - } - } - - if (fd_head==NULL){ - /* not found, this must be the first snooped fragment for this - * packet. Create list-head. - */ - fd_head=g_mem_chunk_alloc(fragment_data_chunk); - /* head/first structure in list only holds no other data than - * 'datalen' then we don't have to change the head of the list - * even if we want to keep it sorted - */ - fd_head->next=NULL; - fd_head->datalen=0; - fd_head->offset=0; - fd_head->len=0; - fd_head->flags=0; - fd_head->data=NULL; - fd_head->reassembled_in=0; - - /* - * We're going to use the key to insert the fragment, - * so allocate a structure for it, and copy the - * addresses, allocating new buffers for the address - * data. - */ - 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); @@ -572,7 +530,7 @@ fragment_add(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 */ if ( memcmp(fd_head->data+fd->offset, @@ -580,11 +538,11 @@ fragment_add(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; } /* it was just an overlap, link it and return */ LINK_FRAG(fd_head,fd); - return (fd_head); + return TRUE; } @@ -603,7 +561,7 @@ fragment_add(tvbuff_t *tvb, int offset, packet_info *pinfo, guint32 id, /* if we dont know the datalen, there are still missing * packets. Cheaper than the check below. */ - return NULL; + return FALSE; } @@ -620,7 +578,7 @@ fragment_add(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; } @@ -675,8 +633,201 @@ fragment_add(tvbuff_t *tvb, int offset, packet_info *pinfo, guint32 id, allows us to skip any trailing fragments */ fd_head->flags |= FD_DEFRAGMENTED; fd_head->reassembled_in=pinfo->fd->num; - - return fd_head; + + return TRUE; +} + +fragment_data * +fragment_add(tvbuff_t *tvb, int offset, packet_info *pinfo, guint32 id, + GHashTable *fragment_table, guint32 frag_offset, + guint32 frag_data_len, gboolean more_frags) +{ + fragment_key key, *new_key; + fragment_data *fd_head; + fragment_data *fd_item; + gboolean already_added=pinfo->fd->flags.visited; + + /* 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); + + /* + * "already_added" is true if "pinfo->fd->flags.visited" is true; + * if "pinfo->fd->flags.visited", this isn't the first pass, so + * we've already done all the reassembly and added all the + * fragments. + * + * If it's not true, just check if we have seen this fragment before, + * i.e., if we have already added it to reassembly. + * That can be true even if "pinfo->fd->flags.visited" is false + * since we sometimes might call a subdissector multiple times. + * As an additional check, just make sure we have not already added + * this frame to the reassembly list, if there is a reassembly list; + * note that the first item in the reassembly list is not a + * fragment, it's a data structure for the reassembled packet. + * We don't check it because its "frame" member isn't initialized + * to anything, and because it doesn't count in any case. + */ + if (!already_added && fd_head != NULL) { + for(fd_item=fd_head->next;fd_item;fd_item=fd_item->next){ + if(pinfo->fd->num==fd_item->frame){ + already_added=TRUE; + } + } + } + /* have we already added this frame ?*/ + if (already_added) { + if (fd_head != NULL && fd_head->flags & FD_DEFRAGMENTED) { + return fd_head; + } else { + return NULL; + } + } + + if (fd_head==NULL){ + /* not found, this must be the first snooped fragment for this + * packet. Create list-head. + */ + fd_head=g_mem_chunk_alloc(fragment_data_chunk); + + /* head/first structure in list only holds no other data than + * 'datalen' then we don't have to change the head of the list + * even if we want to keep it sorted + */ + fd_head->next=NULL; + fd_head->datalen=0; + fd_head->offset=0; + fd_head->len=0; + fd_head->flags=0; + fd_head->data=NULL; + fd_head->reassembled_in=0; + + /* + * We're going to use the key to insert the fragment, + * so allocate a structure for it, and copy the + * addresses, allocating new buffers for the address + * data. + */ + 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_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_data * +fragment_add_check(tvbuff_t *tvb, int offset, packet_info *pinfo, + guint32 id, GHashTable *fragment_table, + GHashTable *reassembled_table, guint32 frag_offset, + guint32 frag_data_len, gboolean more_frags) +{ + fragment_key key, *new_key, *old_key; + gpointer orig_key, value; + fragment_data *fd_head; + + /* + * If this isn't the first pass, look for this frame in the table + * of reassembled packets. + */ + if (pinfo->fd->flags.visited) + return g_hash_table_lookup(reassembled_table, + (gpointer)pinfo->fd->num); + + /* 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=0; + fd_head->data=NULL; + fd_head->reassembled_in=0; + + /* + * We're going to use the key to insert the fragment, + * so allocate a structure for it, and copy the + * addresses, allocating new buffers for the address + * data. + */ + 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); + + orig_key = new_key; /* for unhashing it later */ + } else { + /* + * We found it. + */ + fd_head = value; + } + + /* + * If this is a short frame, then we can't, and don't, do + * reassembly on it. We just give up. + */ + if (tvb_reported_length(tvb) > tvb_length(tvb)) + 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. + */ + old_key = orig_key; + fragment_unhash(fragment_table, old_key); + + /* + * Add this item to the table of reassembled packets. + */ + fragment_reassembled(fd_head, pinfo, reassembled_table); + return fd_head; + } else { + /* + * Reassembly isn't complete. + */ + return NULL; + } } /* @@ -911,6 +1062,7 @@ fragment_add_seq(tvbuff_t *tvb, int offset, packet_info *pinfo, guint32 id, * 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 @@ -951,42 +1103,6 @@ fragment_add_seq(tvbuff_t *tvb, int offset, packet_info *pinfo, guint32 id, } /* - * This function gets rid of an entry from a fragment table, given - * a pointer to the key for that entry; it also frees up the key - * and the addresses in it. - */ -static void -fragment_unhash(GHashTable *fragment_table, fragment_key *key) -{ - /* - * Free up the copies of the addresses from the old key. - */ - g_free((gpointer)key->src.data); - g_free((gpointer)key->dst.data); - - /* - * Remove the entry from the fragment table. - */ - g_hash_table_remove(fragment_table, key); - - /* - * Free the key itself. - */ - g_mem_chunk_free(fragment_key_chunk, key); -} - -/* - * This function adds fragment_data structure to a reassembled-packet - * hash table, using the frame data structure as the key. - */ -static void -fragment_reassembled(fragment_data *fd_head, packet_info *pinfo, - GHashTable *reassembled_table) -{ - g_hash_table_insert(reassembled_table, pinfo->fd, fd_head); -} - -/* * This does the work for "fragment_add_seq_check()" and * "fragment_add_seq_next()". * @@ -1037,7 +1153,8 @@ fragment_add_seq_check_work(tvbuff_t *tvb, int offset, packet_info *pinfo, * If so, look for it in the table of reassembled packets. */ if (pinfo->fd->flags.visited) - return g_hash_table_lookup(reassembled_table, pinfo->fd); + return g_hash_table_lookup(reassembled_table, + (gpointer)pinfo->fd->num); /* create key to search hash with */ key.src = pinfo->src; |