Add a separate hash table to the reassembly code for reassembled
authorguy <guy@f5534014-38df-0310-8fa8-9805f1628bb7>
Wed, 17 Apr 2002 08:25:05 +0000 (08:25 +0000)
committerguy <guy@f5534014-38df-0310-8fa8-9805f1628bb7>
Wed, 17 Apr 2002 08:25:05 +0000 (08:25 +0000)
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).

git-svn-id: http://anonsvn.wireshark.org/wireshark/trunk@5177 f5534014-38df-0310-8fa8-9805f1628bb7

packet-ieee80211.c
reassemble.c
reassemble.h

index 6d805fe5d52b6ab3b73a293f441906ba524118f1..3990c5b0da9f106f20fede491bef106245594132 100644 (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
 #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
index 8122fadb9ef878b3e8bee388bf3ef3560295491e..26a3dd849b9b1773186b531e4e67bf1383accd35 100644 (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,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;
+       }
 }
index 522fc0abef8efbe7f809eeb45aeb918955934b86..4fc8daad7328df51dddc1b7aea95239e0dd29b04 100644 (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);
-
-