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
This commit is contained in:
Guy Harris 2002-04-17 08:25:05 +00:00
parent 8241e67243
commit ed3b0cae65
3 changed files with 728 additions and 196 deletions

View File

@ -3,7 +3,7 @@
* Copyright 2000, Axis Communications AB
* Inquiries/bugreports should be sent to Johan.Jorgensen@axis.com
*
* $Id: packet-ieee80211.c,v 1.54 2002/04/13 18:41:47 guy Exp $
* $Id: packet-ieee80211.c,v 1.55 2002/04/17 08:25:05 guy Exp $
*
* Ethereal - Network traffic analyzer
* By Gerald Combs <gerald@ethereal.com>
@ -32,7 +32,6 @@
* Marco Molteni
* Lena-Marie Nilsson
* Magnus Hultman-Persson
*
*/
#ifdef HAVE_CONFIG_H
@ -65,11 +64,20 @@
#include <epan/proto.h>
#include <epan/packet.h>
#include <epan/resolv.h>
#include "prefs.h"
#include "reassemble.h"
#include "packet-ipx.h"
#include "packet-llc.h"
#include "packet-ieee80211.h"
#include "etypes.h"
/* Defragment fragmented 802.11 datagrams */
static gboolean wlan_defragment = TRUE;
/* Tables for reassembly of fragments. */
static GHashTable *wlan_fragment_table = NULL;
static GHashTable *wlan_reassembled_table = NULL;
/* ************************************************************************* */
/* Miscellaneous Constants */
/* ************************************************************************* */
@ -286,6 +294,16 @@ static int hf_seq_number = -1;
/* ************************************************************************* */
static int hf_fcs = -1;
/* ************************************************************************* */
/* Header values for reassembly */
/* ************************************************************************* */
static int hf_fragments = -1;
static int hf_fragment = -1;
static int hf_fragment_overlap = -1;
static int hf_fragment_overlap_conflict = -1;
static int hf_fragment_multiple_tails = -1;
static int hf_fragment_too_long_fragment = -1;
static int hf_fragment_error = -1;
static int proto_wlan_mgt = -1;
@ -337,6 +355,8 @@ static gint ett_80211 = -1;
static gint ett_proto_flags = -1;
static gint ett_cap_tree = -1;
static gint ett_fc_tree = -1;
static gint ett_fragments = -1;
static gint ett_fragment = -1;
static gint ett_80211_mgt = -1;
static gint ett_fixed_parameters = -1;
@ -1061,6 +1081,67 @@ set_dst_addr_cols(packet_info *pinfo, const guint8 *addr, char *type)
ether_to_str(addr), type);
}
static void
show_fragments(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree,
fragment_data *fd_head)
{
guint32 offset;
fragment_data *fd;
proto_tree *ft;
proto_item *fi;
fi = proto_tree_add_item(tree, hf_fragments, tvb, 0, -1, FALSE);
ft = proto_item_add_subtree(fi, ett_fragments);
offset = 0;
for (fd = fd_head->next; fd != NULL; fd = fd->next){
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;
proto_item *fei = NULL;
int hf;
if (fd->flags & (FD_OVERLAPCONFLICT|FD_MULTIPLETAILS|FD_TOOLONGFRAGMENT) ) {
hf = hf_fragment_error;
} else {
hf = hf_fragment;
}
fei = proto_tree_add_none_format(ft, hf, tvb, offset, fd->len,
"Frame:%u payload:%u-%u",
fd->frame, offset, offset+fd->len-1);
fet = proto_item_add_subtree(fei, ett_fragment);
if (fd->flags&FD_OVERLAP)
proto_tree_add_boolean(fet, hf_fragment_overlap, tvb, 0, 0, TRUE);
if (fd->flags&FD_OVERLAPCONFLICT) {
proto_tree_add_boolean(fet, hf_fragment_overlap_conflict, tvb, 0, 0,
TRUE);
}
if (fd->flags&FD_MULTIPLETAILS) {
proto_tree_add_boolean(fet, hf_fragment_multiple_tails, tvb, 0, 0,
TRUE);
}
if (fd->flags&FD_TOOLONGFRAGMENT) {
proto_tree_add_boolean(fet, hf_fragment_too_long_fragment, tvb, 0, 0,
TRUE);
}
} else {
/*
* Nothing of interest for this fragment.
*/
proto_tree_add_none_format(ft, hf_fragment, tvb, offset, fd->len,
"Frame:%u payload:%u-%u",
fd->frame, offset, offset+fd->len-1);
}
offset += fd->len;
}
if (fd_head->flags & (FD_OVERLAPCONFLICT|FD_MULTIPLETAILS|FD_TOOLONGFRAGMENT) ) {
if (check_col(pinfo->cinfo, COL_INFO))
col_set_str(pinfo->cinfo, COL_INFO, "[Illegal fragments]");
}
}
/* ************************************************************************* */
/* Dissect 802.11 frame */
/* ************************************************************************* */
@ -1070,6 +1151,9 @@ dissect_ieee80211_common (tvbuff_t * tvb, packet_info * pinfo,
gboolean has_radio_information)
{
guint16 fcf, flags, frame_type_subtype;
guint16 seq_control;
guint32 seq_number, frag_number;
gboolean more_frags;
const guint8 *src = NULL, *dst = NULL;
proto_item *ti = NULL;
proto_item *flag_item;
@ -1078,7 +1162,7 @@ dissect_ieee80211_common (tvbuff_t * tvb, packet_info * pinfo,
proto_tree *flag_tree;
proto_tree *fc_tree;
guint16 hdr_len;
tvbuff_t *next_tvb;
tvbuff_t *volatile next_tvb;
guint32 addr_type;
volatile gboolean is_802_2;
@ -1099,6 +1183,9 @@ dissect_ieee80211_common (tvbuff_t * tvb, packet_info * pinfo,
val_to_str(frame_type_subtype, frame_type_subtype_vals,
"Unrecognized (Reserved frame)"));
flags = COOK_FLAGS (fcf);
more_frags = HAVE_FRAGMENTS (flags);
/* Add the radio information, if present, and the FC to the current tree */
if (tree)
{
@ -1146,8 +1233,6 @@ dissect_ieee80211_common (tvbuff_t * tvb, packet_info * pinfo,
tvb, 0, 1,
COOK_FRAME_SUBTYPE (fcf));
flags = COOK_FLAGS (fcf);
flag_item =
proto_tree_add_uint_format (fc_tree, hf_fc_flags, tvb, 1, 1,
flags, "Flags: 0x%X", flags);
@ -1184,6 +1269,10 @@ dissect_ieee80211_common (tvbuff_t * tvb, packet_info * pinfo,
* Decode the part of the frame header that isn't the same for all
* frame types.
*/
seq_control = 0;
frag_number = 0;
seq_number = 0;
switch (frame_type_subtype)
{
@ -1209,6 +1298,10 @@ dissect_ieee80211_common (tvbuff_t * tvb, packet_info * pinfo,
SET_ADDRESS(&pinfo->dl_dst, AT_ETHER, 6, dst);
SET_ADDRESS(&pinfo->dst, AT_ETHER, 6, dst);
seq_control = tvb_get_letohs(tvb, 22);
frag_number = COOK_FRAGMENT_NUMBER(seq_control);
seq_number = COOK_SEQUENCE_NUMBER(seq_control);
if (tree)
{
proto_tree_add_ether (hdr_tree, hf_addr_da, tvb, 4, 6, dst);
@ -1223,12 +1316,10 @@ dissect_ieee80211_common (tvbuff_t * tvb, packet_info * pinfo,
tvb_get_ptr (tvb, 16, 6));
proto_tree_add_uint (hdr_tree, hf_frag_number, tvb, 22, 2,
COOK_FRAGMENT_NUMBER (tvb_get_letohs
(tvb, 22)));
frag_number);
proto_tree_add_uint (hdr_tree, hf_seq_number, tvb, 22, 2,
COOK_SEQUENCE_NUMBER (tvb_get_letohs
(tvb, 22)));
seq_number);
}
break;
@ -1359,6 +1450,10 @@ dissect_ieee80211_common (tvbuff_t * tvb, packet_info * pinfo,
SET_ADDRESS(&pinfo->dl_dst, AT_ETHER, 6, dst);
SET_ADDRESS(&pinfo->dst, AT_ETHER, 6, dst);
seq_control = tvb_get_letohs(tvb, 22);
frag_number = COOK_FRAGMENT_NUMBER(seq_control);
seq_number = COOK_SEQUENCE_NUMBER(seq_control);
/* Now if we have a tree we start adding stuff */
if (tree)
{
@ -1373,11 +1468,9 @@ dissect_ieee80211_common (tvbuff_t * tvb, packet_info * pinfo,
proto_tree_add_ether (hdr_tree, hf_addr_bssid, tvb, 16, 6,
tvb_get_ptr (tvb, 16, 6));
proto_tree_add_uint (hdr_tree, hf_frag_number, tvb, 22, 2,
COOK_FRAGMENT_NUMBER (tvb_get_letohs
(tvb, 22)));
frag_number);
proto_tree_add_uint (hdr_tree, hf_seq_number, tvb, 22, 2,
COOK_SEQUENCE_NUMBER (tvb_get_letohs
(tvb, 22)));
seq_number);
/* add items for wlan.addr filter */
proto_tree_add_ether_hidden(hdr_tree, hf_addr, tvb, 4, 6, dst);
@ -1391,11 +1484,9 @@ dissect_ieee80211_common (tvbuff_t * tvb, packet_info * pinfo,
tvb_get_ptr (tvb, 10, 6));
proto_tree_add_ether (hdr_tree, hf_addr_sa, tvb, 16, 6, src);
proto_tree_add_uint (hdr_tree, hf_frag_number, tvb, 22, 2,
COOK_FRAGMENT_NUMBER (tvb_get_letohs
(tvb, 22)));
frag_number);
proto_tree_add_uint (hdr_tree, hf_seq_number, tvb, 22, 2,
COOK_SEQUENCE_NUMBER (tvb_get_letohs
(tvb, 22)));
seq_number);
/* add items for wlan.addr filter */
proto_tree_add_ether_hidden(hdr_tree, hf_addr, tvb, 4, 6, dst);
@ -1410,11 +1501,9 @@ dissect_ieee80211_common (tvbuff_t * tvb, packet_info * pinfo,
proto_tree_add_ether (hdr_tree, hf_addr_da, tvb, 16, 6, dst);
proto_tree_add_uint (hdr_tree, hf_frag_number, tvb, 22, 2,
COOK_FRAGMENT_NUMBER (tvb_get_letohs
(tvb, 22)));
frag_number);
proto_tree_add_uint (hdr_tree, hf_seq_number, tvb, 22, 2,
COOK_SEQUENCE_NUMBER (tvb_get_letohs
(tvb, 22)));
seq_number);
/* add items for wlan.addr filter */
proto_tree_add_ether_hidden(hdr_tree, hf_addr, tvb, 10, 6, src);
@ -1429,11 +1518,9 @@ dissect_ieee80211_common (tvbuff_t * tvb, packet_info * pinfo,
tvb_get_ptr (tvb, 10, 6));
proto_tree_add_ether (hdr_tree, hf_addr_da, tvb, 16, 6, dst);
proto_tree_add_uint (hdr_tree, hf_frag_number, tvb, 22, 2,
COOK_FRAGMENT_NUMBER (tvb_get_letohs
(tvb, 22)));
frag_number);
proto_tree_add_uint (hdr_tree, hf_seq_number, tvb, 22, 2,
COOK_SEQUENCE_NUMBER (tvb_get_letohs
(tvb, 22)));
seq_number);
proto_tree_add_ether (hdr_tree, hf_addr_sa, tvb, 24, 6, src);
/* add items for wlan.addr filter */
@ -1527,7 +1614,99 @@ dissect_ieee80211_common (tvbuff_t * tvb, packet_info * pinfo,
/*
* Now dissect the body of a non-WEP-encrypted frame.
*/
next_tvb = tvb_new_subset (tvb, hdr_len, -1, -1);
/*
* Do defragmentation if "wlan_defragment" is true.
*
* We have to do some special handling to catch frames that
* have the "More Fragments" indicator not set but that
* don't show up as reassembled and don't have any other
* fragments present. Some networking interfaces appear
* to do reassembly even when you're capturing raw packets
* *and* show the reassembled packet without the "More
* Fragments" indicator set *but* with a non-zero fragment
* number.
*
* (This could get some false positives if we really *did* only
* capture the last fragment of a fragmented packet, but that's
* life.)
*
* XXX - what about short frames?
*/
if (wlan_defragment && (more_frags || frag_number != 0)) {
gboolean save_fragmented;
fragment_data *fd_head;
unsigned char *buf;
save_fragmented = pinfo->fragmented;
pinfo->fragmented = TRUE;
/*
* If we've already seen this frame, look it up in the
* table of reassembled packets, otherwise add it to
* whatever reassembly is in progress, if any, and see
* if it's done.
*/
fd_head = fragment_add_seq_check(tvb, hdr_len, pinfo, seq_number,
wlan_fragment_table,
wlan_reassembled_table,
frag_number,
tvb_length_remaining(tvb, hdr_len),
more_frags);
if (fd_head != NULL) {
/*
* Either this is reassembled or it wasn't fragmented
* (see comment above about some networking interfaces).
* In either case, it's now in the table of reassembled
* packets.
*
* If the "fragment_data" structure doesn't have a list of
* fragments, we assume it's a placeholder to mark those
* not-really-fragmented packets, and just treat this as
* a non-fragmented frame.
*/
if (fd_head->next != NULL) {
next_tvb = tvb_new_real_data(fd_head->data, fd_head->len, fd_head->len);
tvb_set_child_real_data_tvbuff(tvb, next_tvb);
add_new_data_source(pinfo->fd, next_tvb, "Reassembled 802.11");
/* Show all fragments. */
show_fragments(next_tvb, pinfo, hdr_tree, fd_head);
} else {
/*
* Not fragmented, really.
* Show it as a regular frame.
*/
next_tvb = tvb_new_subset (tvb, hdr_len, -1, -1);
}
pinfo->fragmented = FALSE;
} else {
/*
* Not yet reassembled.
*/
pinfo->fragmented = save_fragmented;
next_tvb = NULL;
}
} else {
/*
* XXX - how do we know it's the last fragment?
*/
if (frag_number != 0) {
/* Just show this as a fragment. */
next_tvb = NULL;
} else
next_tvb = tvb_new_subset (tvb, hdr_len, -1, -1);
}
if (next_tvb == NULL) {
/* Just show this as a fragment. */
if (check_col(pinfo->cinfo, COL_INFO))
col_set_str(pinfo->cinfo, COL_INFO, "Fragmented IEEE 802.11 frame");
next_tvb = tvb_new_subset (tvb, hdr_len, -1, -1);
call_dissector(data_handle, next_tvb, pinfo, tree);
return;
}
switch (COOK_FRAME_TYPE (fcf))
{
@ -1559,14 +1738,6 @@ dissect_ieee80211_common (tvbuff_t * tvb, packet_info * pinfo,
}
ENDTRY;
if (COOK_FRAGMENT_NUMBER(tvb_get_letohs(tvb, 22)) > 0) {
/* Just show this as a fragment. */
if (check_col(pinfo->cinfo, COL_INFO))
col_add_fstr(pinfo->cinfo, COL_INFO, "Fragmented IEEE 802.11 frame");
call_dissector(data_handle, next_tvb, pinfo, tree);
break;
}
if (is_802_2)
call_dissector(llc_handle, next_tvb, pinfo, tree);
else
@ -1604,6 +1775,13 @@ dissect_ieee80211_fixed (tvbuff_t * tvb, packet_info * pinfo, proto_tree * tree)
dissect_ieee80211_common (tvb, pinfo, tree, TRUE, FALSE);
}
static void
wlan_defragment_init(void)
{
fragment_table_init(&wlan_fragment_table);
reassembled_table_init(&wlan_reassembled_table);
}
void
proto_register_wlan (void)
{
@ -1878,6 +2056,38 @@ proto_register_wlan (void)
{"Frame Check Sequence (not verified)", "wlan.fcs", FT_UINT32, BASE_HEX,
NULL, 0, "", HFILL }},
{&hf_fragment_overlap,
{"Fragment overlap", "wlan.fragment.overlap", FT_BOOLEAN, BASE_NONE,
NULL, 0x0, "Fragment overlaps with other fragments", HFILL }},
{&hf_fragment_overlap_conflict,
{"Conflicting data in fragment overlap", "wlan.fragment.overlap.conflict",
FT_BOOLEAN, BASE_NONE, NULL, 0x0,
"Overlapping fragments contained conflicting data", HFILL }},
{&hf_fragment_multiple_tails,
{"Multiple tail fragments found", "wlan.fragment.multipletails",
FT_BOOLEAN, BASE_NONE, NULL, 0x0,
"Several tails were found when defragmenting the packet", HFILL }},
{&hf_fragment_too_long_fragment,
{"Fragment too long", "wlan.fragment.toolongfragment",
FT_BOOLEAN, BASE_NONE, NULL, 0x0,
"Fragment contained data past end of packet", HFILL }},
{&hf_fragment_error,
{"Defragmentation error", "wlan.fragment.error",
FT_NONE, BASE_NONE, NULL, 0x0,
"Defragmentation error due to illegal fragments", HFILL }},
{&hf_fragment,
{"802.11 Fragment", "wlan.fragment", FT_NONE, BASE_NONE, NULL, 0x0,
"802.11 Fragment", HFILL }},
{&hf_fragments,
{"802.11 Fragments", "wlan.fragments", FT_NONE, BASE_NONE, NULL, 0x0,
"WTP Fragments", HFILL }},
{&hf_wep_iv,
{"Initialization Vector", "wlan.wep.iv", FT_UINT24, BASE_HEX, NULL, 0,
"Initialization Vector", HFILL }},
@ -1996,12 +2206,15 @@ proto_register_wlan (void)
&ett_80211,
&ett_fc_tree,
&ett_proto_flags,
&ett_fragments,
&ett_fragment,
&ett_80211_mgt,
&ett_fixed_parameters,
&ett_tagged_parameters,
&ett_wep_parameters,
&ett_cap_tree,
};
module_t *wlan_module;
proto_wlan = proto_register_protocol ("IEEE 802.11 wireless LAN",
"IEEE 802.11", "wlan");
@ -2013,6 +2226,14 @@ proto_register_wlan (void)
register_dissector("wlan", dissect_ieee80211, proto_wlan);
register_dissector("wlan_fixed", dissect_ieee80211_fixed, proto_wlan);
register_init_routine(wlan_defragment_init);
/* Register configuration options */
wlan_module = prefs_register_protocol(proto_wlan, NULL);
prefs_register_bool_preference(wlan_module, "defragment",
"Reassemble fragmented 802.11 datagrams",
"Whether fragmented 802.11 datagrams should be reassembled",
&wlan_defragment);
}
void

View File

@ -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
@ -133,6 +173,25 @@ free_all_fragments(gpointer key_arg, gpointer value, gpointer user_data _U_)
return TRUE;
}
/*
* 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.
*/
@ -156,6 +215,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.
*/
@ -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,15 +643,194 @@ fragment_add(tvbuff_t *tvb, int offset, packet_info *pinfo, guint32 id,
return fd_head;
}
/*
* 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_data *fd_head, tvbuff_t *tvb, int offset,
packet_info *pinfo, guint32 id, guint32 frag_number,
guint32 frag_data_len, gboolean more_frags)
{
fragment_data *fd;
fragment_data *fd_i;
fragment_data *last_fd;
guint32 max, dfpos, size;
/* create new fd describing this fragment */
fd = g_mem_chunk_alloc(fragment_data_chunk);
fd->next = NULL;
fd->flags = 0;
fd->frame = pinfo->fd->num;
fd->offset = frag_number;
fd->len = frag_data_len;
fd->data = NULL;
if (!more_frags) {
/*
* This is the tail fragment in the sequence.
*/
if (fd_head->datalen) {
/* 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. Somethings wrong
*/
fd->flags |= FD_MULTIPLETAILS;
fd_head->flags |= FD_MULTIPLETAILS;
}
} else {
/* this was the first tail fragment, now we know the
* length of the packet
*/
fd_head->datalen = fd->offset;
}
}
/* If the packet is already defragmented, this MUST be an overlap.
* The entire defragmented packet is in fd_head->data
* Even if we have previously defragmented this packet, we still 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 its not too long */
if (fd->offset > fd_head->datalen) {
fd->flags |= FD_TOOLONGFRAGMENT;
fd_head->flags |= FD_TOOLONGFRAGMENT;
LINK_FRAG(fd_head,fd);
return TRUE;
}
/* make sure it doesnt conflict with previous data */
dfpos=0;
for (fd_i=fd_head->next;fd_i->offset!=fd->offset;fd_i=fd_i->next) {
dfpos += fd_i->len;
}
if(fd_i->datalen!=fd->datalen){
fd->flags |= FD_OVERLAPCONFLICT;
fd_head->flags |= FD_OVERLAPCONFLICT;
LINK_FRAG(fd_head,fd);
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 TRUE;
}
/* 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.
* XXX - what if we didn't capture the entire fragment due
* to a too-short snapshot length?
*/
fd->data = g_malloc(fd->len);
tvb_memcpy(tvb, fd->data, offset, fd->len);
LINK_FRAG(fd_head,fd);
if( !(fd_head->datalen) ){
/* if we dont know the datalen, there are still missing
* packets. Cheaper than the check below.
*/
return NULL;
}
/* check if we have received the entire fragment
* this is easy since the list is sorted and the head is faked.
*/
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
*/
size=0;
last_fd=NULL;
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;
}
fd_head->data = g_malloc(size);
fd_head->len = size; /* record size for caller */
/* add all data fragments */
last_fd=NULL;
for (dfpos=0,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){
memcpy(fd_head->data+dfpos,fd_i->data,fd_i->len);
dfpos += fd_i->len;
} else {
/* duplicate/retransmission/overlap */
if( (last_fd->len!=fd_i->datalen)
|| memcmp(last_fd->data, fd_i->data, last_fd->len) ){
fd->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->data){
g_free(fd_i->data);
fd_i->data=NULL;
}
}
/* mark this packet as defragmented.
allows us to skip any trailing fragments */
fd_head->flags |= FD_DEFRAGMENTED;
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.
* 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.
@ -627,163 +895,171 @@ fragment_add_seq(tvbuff_t *tvb, int offset, packet_info *pinfo, guint32 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;
fd->flags = 0;
fd->frame = pinfo->fd->num;
fd->offset = frag_number;
fd->len = frag_data_len;
fd->data = NULL;
if (!more_frags) {
if (fragment_add_seq_work(fd_head, tvb, offset, pinfo, id,
frag_number, frag_data_len, more_frags)) {
/*
* This is the tail fragment in the sequence.
* Reassembly is complete.
*/
if (fd_head->datalen) {
/* 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. Somethings wrong
*/
fd->flags |= FD_MULTIPLETAILS;
fd_head->flags |= FD_MULTIPLETAILS;
}
} else {
/* this was the first tail fragment, now we know the
* length of the packet
*/
fd_head->datalen = fd->offset;
}
}
/* If the packet is already defragmented, this MUST be an overlap.
* The entire defragmented packet is in fd_head->data
* Even if we have previously defragmented this packet, we still 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 its not too long */
if (fd->offset > fd_head->datalen) {
fd->flags |= FD_TOOLONGFRAGMENT;
fd_head->flags |= FD_TOOLONGFRAGMENT;
LINK_FRAG(fd_head,fd);
return (fd_head);
}
/* make sure it doesnt conflict with previous data */
dfpos=0;
for (fd_i=fd_head->next;fd_i->offset!=fd->offset;fd_i=fd_i->next) {
dfpos += fd_i->len;
}
if(fd_i->datalen!=fd->datalen){
fd->flags |= FD_OVERLAPCONFLICT;
fd_head->flags |= FD_OVERLAPCONFLICT;
LINK_FRAG(fd_head,fd);
return (fd_head);
}
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);
}
/* it was just an overlap, link it and return */
LINK_FRAG(fd_head,fd);
return (fd_head);
}
/* If we have reached this point, the packet is not defragmented yet.
* Save all payload in a buffer until we can defragment.
* XXX - what if we didn't capture the entire fragment due
* to a too-short snapshot length?
*/
fd->data = g_malloc(fd->len);
tvb_memcpy(tvb, fd->data, offset, fd->len);
LINK_FRAG(fd_head,fd);
if( !(fd_head->datalen) ){
/* if we dont know the datalen, there are still missing
* packets. Cheaper than the check below.
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;
}
/* check if we have received the entire fragment
* this is easy since the list is sorted and the head is faked.
*/
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 NULL;
}
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
*/
size=0;
last_fd=NULL;
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;
}
fd_head->data = g_malloc(size);
fd_head->len = size; /* record size for caller */
/* add all data fragments */
last_fd=NULL;
for (dfpos=0,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){
memcpy(fd_head->data+dfpos,fd_i->data,fd_i->len);
dfpos += fd_i->len;
} else {
/* duplicate/retransmission/overlap */
if( (last_fd->len!=fd_i->datalen)
|| memcmp(last_fd->data, fd_i->data, last_fd->len) ){
fd->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->data){
g_free(fd_i->data);
fd_i->data=NULL;
}
}
/* mark this packet as defragmented.
allows us to skip any trailing fragments */
fd_head->flags |= FD_DEFRAGMENTED;
return fd_head;
}

View File

@ -1,7 +1,7 @@
/* reassemble.h
* Declarations of outines for {fragment,segment} reassembly
*
* $Id: reassemble.h,v 1.5 2002/04/17 04:54:30 guy Exp $
* $Id: reassemble.h,v 1.6 2002/04/17 08:25:05 guy Exp $
*
* Ethereal - Network traffic analyzer
* By Gerald Combs <gerald@ethereal.com>
@ -66,6 +66,11 @@ typedef struct _fragment_data {
*/
void fragment_table_init(GHashTable **fragment_table);
/*
* Initialize a reassembled-packet table.
*/
void reassembled_table_init(GHashTable **reassembled_table);
/*
* Free up all space allocated for fragment keys and data.
*/
@ -92,6 +97,38 @@ 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);
/*
* 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);
/* to specify how much to reassemble, for fragmentation where last fragment can not be
* identified by flags or such.
* note that for FD_BLOCKSEQUENCE tot_len is the index for the tail fragment.
@ -129,5 +166,3 @@ fragment_get(packet_info *pinfo, guint32 id, GHashTable *fragment_table);
*/
unsigned char *
fragment_delete(packet_info *pinfo, guint32 id, GHashTable *fragment_table);