* 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.59 2002/04/22 08:14:12 guy Exp $
*
* Ethereal - Network traffic analyzer
* By Gerald Combs <gerald@ethereal.com>
* Marco Molteni
* Lena-Marie Nilsson
* Magnus Hultman-Persson
- *
*/
#ifdef HAVE_CONFIG_H
#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 */
/* ************************************************************************* */
/* ************************************************************************* */
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;
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;
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 */
/* ************************************************************************* */
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;
proto_tree *flag_tree;
proto_tree *fc_tree;
guint16 hdr_len;
- tvbuff_t *next_tvb;
+ gboolean save_fragmented;
+ tvbuff_t *volatile next_tvb;
guint32 addr_type;
volatile gboolean is_802_2;
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)
{
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);
* 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)
{
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);
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;
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)
{
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);
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);
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);
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 */
/*
* 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.)
+ */
+ save_fragmented = pinfo->fragmented;
+ if (wlan_defragment && (more_frags || frag_number != 0)) {
+ fragment_data *fd_head;
+
+ /*
+ * 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);
+ }
+
+ /* It's not fragmented. */
+ pinfo->fragmented = FALSE;
+ } else {
+ /* We don't have the complete reassembled payload. */
+ next_tvb = NULL;
+ }
+ } else {
+ /*
+ * If this is the first fragment, dissect its contents, otherwise
+ * just show it as a fragment.
+ */
+ if (frag_number != 0) {
+ /* Not the first fragment - don't dissect it. */
+ next_tvb = NULL;
+ } else {
+ /* First fragment, or not fragmented. Dissect what we have here. */
+
+ /* Get a tvbuff for the payload. */
+ next_tvb = tvb_new_subset (tvb, hdr_len, -1, -1);
+
+ /*
+ * If this is the first fragment, but not the only fragment,
+ * tell the next protocol that.
+ */
+ if (more_frags)
+ pinfo->fragmented = TRUE;
+ else
+ pinfo->fragmented = FALSE;
+ }
+ }
+
+ 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);
+ pinfo->fragmented = save_fragmented;
+ return;
+ }
+
switch (COOK_FRAME_TYPE (fcf))
{
}
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
call_dissector(ipx_handle, next_tvb, pinfo, tree);
break;
}
+ pinfo->fragmented = save_fragmented;
}
/*
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)
{
};
static const true_false_string more_frags = {
- "MSDU/MMPDU is fragmented",
- "No fragments"
+ "More fragments follow",
+ "This is the last fragment"
};
static const true_false_string retry_flags = {
"From DS flag", HFILL }}, /* 5 */
{&hf_fc_more_frag,
- {"Fragments", "wlan.fc.frag", FT_BOOLEAN, 8, TFS (&more_frags), FLAG_MORE_FRAGMENTS,
+ {"More Fragments", "wlan.fc.frag", FT_BOOLEAN, 8, TFS (&more_frags), FLAG_MORE_FRAGMENTS,
"More Fragments flag", HFILL }}, /* 6 */
{&hf_fc_retry,
{"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 }},
&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");
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