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
This commit is contained in:
Guy Harris 2003-04-20 00:11:28 +00:00
parent d067b0e361
commit 0def9a0b52
3 changed files with 304 additions and 140 deletions

View File

@ -1,7 +1,7 @@
/* packet-ip.c
* Routines for IP and miscellaneous IP protocol packet disassembly
*
* $Id: packet-ip.c,v 1.187 2003/04/18 05:11:44 sahlberg Exp $
* $Id: packet-ip.c,v 1.188 2003/04/20 00:11:28 guy Exp $
*
* Ethereal - Network traffic analyzer
* By Gerald Combs <gerald@ethereal.com>
@ -322,11 +322,13 @@ static gint ett_icmp_mip_flags = -1;
* defragmentation of IPv4
*/
static GHashTable *ip_fragment_table = NULL;
static GHashTable *ip_reassembled_table = NULL;
static void
ip_defragment_init(void)
{
fragment_table_init(&ip_fragment_table);
reassembled_table_init(&ip_reassembled_table);
}
void
@ -1025,14 +1027,47 @@ dissect_ip(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree)
if (ip_defragment && (iph->ip_off & (IP_MF|IP_OFFSET)) &&
tvb_bytes_exist(tvb, offset, pinfo->iplen - pinfo->iphdrlen) &&
ipsum == 0) {
ipfd_head = fragment_add(tvb, offset, pinfo, iph->ip_id,
ipfd_head = fragment_add_check(tvb, offset, pinfo, iph->ip_id,
ip_fragment_table,
ip_reassembled_table,
(iph->ip_off & IP_OFFSET)*8,
pinfo->iplen - pinfo->iphdrlen,
iph->ip_off & IP_MF);
if (ipfd_head != NULL) {
if(pinfo->fd->num==ipfd_head->reassembled_in){
/*
* XXX - Now that we're using "fragment_add_check()", so that we don't
* get confused by reused IP IDs, reassembled fragments are
* hashed by the number of the frame in whch they're reassembled, so
* the only one of the frames for which we'll get the frame info
* is the one in which it's reassembled.
*
* That means we can't put the "reassembled in" information into the
* protocol tree or Info column for packets other than the last
* fragment. In order to do that, we'd need to hash the entry into
* the hash table multiple times - or retroactively attach the
* entry to all the other frames with, say, "p_add_proto_data()"
* and use that. (That could only be done by the reassembly code
* in "reassemble.c" if we either guaranteed that no protocol
* doing reassembly attached its own per-protocol data or if
* we added another list of reassembly data to all frames, growing
* the per-frame overhead by one pointer.)
*
* Note that putting it into the Info column doesn't work when
* the file is read in or reprocessed; it works only when the
* capture is filtered. If we switch to a scheme in which the
* column text is generated on the fly, by having the column
* list widget get the text to draw by calling back to a routine
* that would read and re-dissect the packet, that problem would
* go away, although doing so without running the risk of dragging
* the scroll bar causing stalls requires fast random access even
* to gzipped files and fast generation of protocol trees. The
* former can probably be done by saving the string dictionary at
* "checkpoint" locations; the latter may require that we build
* protocol trees using our own code, as "g_node_append()" is
* linear in the length of the list to which it's appending.)
*/
if (pinfo->fd->num == ipfd_head->reassembled_in) {
/* OK, we have the complete reassembled payload.
Allocate a new tvbuff, referring to the reassembled payload. */
next_tvb = tvb_new_real_data(ipfd_head->data, ipfd_head->datalen,
@ -1049,11 +1084,11 @@ dissect_ip(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree)
/* show all fragments */
update_col_info = !show_fragment_tree(ipfd_head, &ip_frag_items,
ip_tree, pinfo, next_tvb);
} else {
/* We don't have the complete reassembled payload. */
next_tvb = NULL;
proto_tree_add_uint(ip_tree, hf_ip_reassembled_in, tvb, 0, 0, ipfd_head->reassembled_in);
}
} else {
/* We don't have the complete reassembled payload. */
next_tvb = NULL;
proto_tree_add_uint(ip_tree, hf_ip_reassembled_in, tvb, 0, 0, ipfd_head->reassembled_in);
}
} else {
/* We don't have the complete reassembled payload. */
next_tvb = NULL;

View File

@ -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;
}
/*
@ -401,6 +396,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
@ -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
@ -950,42 +1102,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;

View File

@ -1,7 +1,7 @@
/* reassemble.h
* Declarations of outines for {fragment,segment} reassembly
*
* $Id: reassemble.h,v 1.14 2003/04/09 09:04:08 sahlberg Exp $
* $Id: reassemble.h,v 1.15 2003/04/20 00:11:28 guy Exp $
*
* Ethereal - Network traffic analyzer
* By Gerald Combs <gerald@ethereal.com>
@ -94,6 +94,11 @@ extern 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);
extern 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);
/* same as fragment_add() but this one assumes frag_number is a block
sequence number. note that frag_number is 0 for the first fragment. */
extern fragment_data *fragment_add_seq(tvbuff_t *tvb, int offset, packet_info *pinfo,
@ -184,6 +189,13 @@ fragment_get(packet_info *pinfo, guint32 id, GHashTable *fragment_table);
extern unsigned char *
fragment_delete(packet_info *pinfo, guint32 id, GHashTable *fragment_table);
/*
* This function adds fragment_data structure to a reassembled-packet
* hash table, using the frame data structure as the key.
*/
extern void
fragment_reassembled(fragment_data *fd_head, packet_info *pinfo,
GHashTable *reassembled_table);
/* hf_fragment and hf_fragment_error should be FT_FRAMENUM,
the others should be FT_BOOLEAN