Frame numbers are unsigned.
[obnox/wireshark/wip.git] / packet-ieee80211.c
index f9a5eff408a9008cff010a741e79eaa50160b211..8bf2295675028afba907c52cfc516027dbba2243 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.79 2002/12/19 11:22:23 sahlberg Exp $
+ * $Id: packet-ieee80211.c,v 1.91 2003/06/05 22:10:49 gerald Exp $
  *
  * Ethereal - Network traffic analyzer
  * By Gerald Combs <gerald@ethereal.com>
@@ -222,6 +222,12 @@ static char *wep_keystr[] = {NULL, NULL, NULL, NULL};
 #define TAG_TIM            0x05
 #define TAG_IBSS_PARAMETER 0x06
 #define TAG_CHALLENGE_TEXT 0x10
+#define TAG_ERP_INFO       0x2A
+#define TAG_ERP_INFO_OLD   0x2F        /* IEEE Std 802.11g/D4.0 */
+#define TAG_EXT_SUPP_RATES 0x32
+#define TAG_VENDOR_SPECIFIC_IE    0xDD
+
+#define WPA_OUI        "\x00\x50\xF2"
 
 /* ************************************************************************* */
 /*                         Frame types, and their names                      */
@@ -326,6 +332,7 @@ 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 hf_reassembled_in = -1;
 
 
 static int proto_wlan_mgt = -1;
@@ -346,14 +353,16 @@ static int ff_status_code = -1;   /* Status code                               */
 /*            Flags found in the capability field (fixed field)              */
 /* ************************************************************************* */
 static int ff_capture = -1;
-static int ff_cf_sta_poll = -1; /* CF pollable status for a STA            */
-static int ff_cf_ap_poll = -1; /* CF pollable status for an AP            */
 static int ff_cf_ess = -1;
 static int ff_cf_ibss = -1;
+static int ff_cf_sta_poll = -1; /* CF pollable status for a STA            */
+static int ff_cf_ap_poll = -1; /* CF pollable status for an AP            */
 static int ff_cf_privacy = -1;
 static int ff_cf_preamble = -1;
 static int ff_cf_pbcc = -1;
 static int ff_cf_agility = -1;
+static int ff_short_slot_time = -1;
+static int ff_dsss_ofdm = -1;
 
 /* ************************************************************************* */
 /*                       Tagged value format fields                          */
@@ -395,6 +404,7 @@ static const fragment_items frag_items = {
        &hf_fragment_multiple_tails,
        &hf_fragment_too_long_fragment,
        &hf_fragment_error,
+       &hf_reassembled_in,
        "fragments"
 };
 
@@ -604,25 +614,29 @@ add_fixed_field (proto_tree * tree, tvbuff_t * tvb, int offset, int lfcode)
                                             "Capability Information: 0x%04X",
                                             capability);
       cap_tree = proto_item_add_subtree (cap_item, ett_cap_tree);
-      proto_tree_add_boolean (cap_tree, ff_cf_ess, tvb, offset, 1,
-                             capability);
-      proto_tree_add_boolean (cap_tree, ff_cf_ibss, tvb, offset, 1,
-                             capability);
-      proto_tree_add_boolean (cap_tree, ff_cf_privacy, tvb, offset, 1,
-                             capability);
-      proto_tree_add_boolean (cap_tree, ff_cf_preamble, tvb, offset, 1,
-                             capability);
-      proto_tree_add_boolean (cap_tree, ff_cf_pbcc, tvb, offset, 1,
+      proto_tree_add_boolean (cap_tree, ff_cf_ess, tvb, offset, 2,
                              capability);
-      proto_tree_add_boolean (cap_tree, ff_cf_agility, tvb, offset, 1,
+      proto_tree_add_boolean (cap_tree, ff_cf_ibss, tvb, offset, 2,
                              capability);
       if (ESS_SET (capability) != 0)   /* This is an AP */
        proto_tree_add_uint (cap_tree, ff_cf_ap_poll, tvb, offset, 2,
-                            ((capability & 0xC) >> 2));
+                            capability);
 
       else                     /* This is a STA */
        proto_tree_add_uint (cap_tree, ff_cf_sta_poll, tvb, offset, 2,
-                            ((capability & 0xC) >> 2));
+                            capability);
+      proto_tree_add_boolean (cap_tree, ff_cf_privacy, tvb, offset, 2,
+                             capability);
+      proto_tree_add_boolean (cap_tree, ff_cf_preamble, tvb, offset, 2,
+                             capability);
+      proto_tree_add_boolean (cap_tree, ff_cf_pbcc, tvb, offset, 2,
+                             capability);
+      proto_tree_add_boolean (cap_tree, ff_cf_agility, tvb, offset, 2,
+                             capability);
+      proto_tree_add_boolean (cap_tree, ff_short_slot_time, tvb, offset, 2,
+                             capability);
+      proto_tree_add_boolean (cap_tree, ff_dsss_ofdm, tvb, offset, 2,
+                             capability);
       break;
 
     case FIELD_AUTH_ALG:
@@ -655,6 +669,126 @@ add_fixed_field (proto_tree * tree, tvbuff_t * tvb, int offset, int lfcode)
     }
 }
 
+static char *wpa_cipher_str[] = 
+{
+  "NONE",
+  "WEP (40-bit)",
+  "TKIP",
+  "AES (OCB)",
+  "AES (CCM)",
+  "WEP (104-bit)",
+};
+
+static char *
+wpa_cipher_idx2str(guint idx)
+{
+  if (idx < sizeof(wpa_cipher_str)/sizeof(wpa_cipher_str[0]))
+    return wpa_cipher_str[idx];
+  return "UNKNOWN";
+}
+
+static char *wpa_keymgmt_str[] = 
+{
+  "NONE",
+  "WPA",
+  "PSK",
+};
+
+static char *
+wpa_keymgmt_idx2str(guint idx)
+{
+  if (idx < sizeof(wpa_keymgmt_str)/sizeof(wpa_keymgmt_str[0]))
+    return wpa_keymgmt_str[idx];
+  return "UNKNOWN";
+}
+
+void 
+dissect_vendor_specific_ie(proto_tree * tree, tvbuff_t * tvb, int offset,
+               int tag_number, int tag_length, int tag_interpretation)
+{
+      guint8 tag_no;
+      guint32 tag_len;
+      const guint8 *tag_val;
+      guint32 tag_val_off = 0;
+      char out_buff[SHORT_STR];
+      int i;
+       
+      tag_no = tvb_get_guint8(tvb, offset);
+      tag_len = tvb_get_guint8(tvb, offset + 1);
+      tag_val = tvb_get_ptr(tvb, offset + 2, tag_len);
+  
+      proto_tree_add_uint(tree, tag_number, tvb, offset, 1, tag_no);
+      offset += 1;
+      
+      proto_tree_add_uint(tree, tag_length, tvb, offset, 1, tag_len);
+      offset += 1;
+
+      if (tag_val_off + 6 <= tag_len && !memcmp(tag_val, WPA_OUI"\x01", 4)) {
+        snprintf(out_buff, SHORT_STR, "WPA IE, type %u, version %u",
+                  tag_val[tag_val_off + 3], pletohs(&tag_val[tag_val_off + 4]));
+        proto_tree_add_string(tree, tag_interpretation, tvb, offset, 6, out_buff);
+        offset += 6;
+        tag_val_off += 6;
+        if (tag_val_off + 4 <= tag_len) {
+          /* multicast cipher suite */
+          if (!memcmp(&tag_val[tag_val_off], WPA_OUI, 3)) {
+            snprintf(out_buff, SHORT_STR, "Multicast cipher suite: %s", 
+                      wpa_cipher_idx2str(tag_val[tag_val_off + 3]));
+            proto_tree_add_string(tree, tag_interpretation, tvb, offset, 4, out_buff);
+            offset += 4;
+            tag_val_off += 4;
+            /* unicast cipher suites */
+            if (tag_val_off + 2 <= tag_len) {
+              snprintf(out_buff, SHORT_STR, "# of unicast cipher suites: %u",
+                        pletohs(tag_val + tag_val_off));
+              proto_tree_add_string(tree, tag_interpretation, tvb, offset, 2, out_buff);
+              offset += 2;
+              tag_val_off += 2;
+              i = 1;
+              while (tag_val_off + 4 <= tag_len) {
+                if (!memcmp(&tag_val[tag_val_off], WPA_OUI, 3)) {
+                  snprintf(out_buff, SHORT_STR, "Unicast cipher suite %u: %s", 
+                            i, wpa_cipher_idx2str(tag_val[tag_val_off + 3]));
+                  proto_tree_add_string(tree, tag_interpretation, tvb, offset, 4, out_buff);
+                  offset += 4;
+                  tag_val_off += 4;
+                  i ++;
+                }
+                else
+                  break;
+              }
+             /* authenticated key management suites */
+              if (tag_val_off + 2 <= tag_len) {
+                snprintf(out_buff, SHORT_STR, "# of auth key management suites: %u",
+                          pletohs(tag_val + tag_val_off));
+                proto_tree_add_string(tree, tag_interpretation, tvb, offset, 2, out_buff);
+                offset += 2;
+                tag_val_off += 2;
+                i = 1;
+                while (tag_val_off + 4 <= tag_len) {
+                  if (!memcmp(&tag_val[tag_val_off], WPA_OUI, 3)) {
+                    snprintf(out_buff, SHORT_STR, "auth key management suite %u: %s", 
+                              i, wpa_keymgmt_idx2str(tag_val[tag_val_off + 3]));
+                    proto_tree_add_string(tree, tag_interpretation, tvb, offset, 4, out_buff);
+                    offset += 4;
+                    tag_val_off += 4;
+                    i ++;
+                  }
+                  else
+                    break;
+                }
+              }
+            }
+          }
+        }
+        if (tag_val_off < tag_len)
+          proto_tree_add_string(tree, tag_interpretation, tvb,
+                                 offset, tag_len - tag_val_off, "Not interpreted");
+      }
+      else
+        proto_tree_add_string(tree, tag_interpretation, 
+                       tvb, offset, tag_len, "Not interpreted");
+}
 
 /* ************************************************************************* */
 /*           Dissect and add tagged (optional) fields to proto tree          */
@@ -689,7 +823,9 @@ add_tagged_field (proto_tree * tree, tvbuff_t * tvb, int offset)
 
   /* Next See if tag is reserved - if true, skip it! */
   if (((tag_no >= 7) && (tag_no <= 15))
-      || ((tag_no >= 32) && (tag_no <= 255)))
+      || ((tag_no >= 32) && (tag_no <= 255) && (tag_no != TAG_ERP_INFO) &&
+         (tag_no != TAG_EXT_SUPP_RATES) &&
+         (tag_no != TAG_ERP_INFO_OLD) && (tag_no != TAG_VENDOR_SPECIFIC_IE)))
     {
       proto_tree_add_uint_format (tree, tag_number, tvb, offset, 1, tag_no,
                                  "Tag Number: %u (Reserved tag number)",
@@ -726,8 +862,12 @@ add_tagged_field (proto_tree * tree, tvbuff_t * tvb, int offset)
 
 
     case TAG_SUPP_RATES:
+    case TAG_EXT_SUPP_RATES:
       proto_tree_add_uint_format (tree, tag_number, tvb, offset, 1, tag_no,
-                                 "Tag Number: %u (Supported Rates)", tag_no);
+                                 "Tag Number: %u (%sSupported Rates)",
+                                 tag_no,
+                                 tag_no == TAG_EXT_SUPP_RATES ? "Extended " :
+                                 "");
 
       proto_tree_add_uint (tree, tag_length, tvb, offset + 1, 1, tag_len);
 
@@ -740,9 +880,9 @@ add_tagged_field (proto_tree * tree, tvbuff_t * tvb, int offset)
            ret = snprintf (out_buff + n, SHORT_STR - n, "%2.1f%s ",
                           (tag_data_ptr[i] & 0x7F) * 0.5,
                           (tag_data_ptr[i] & 0x80) ? "(B)" : "");
-           if (ret == -1) {
+           if (ret == -1 || ret >= SHORT_STR - n) {
              /* Some versions of snprintf return -1 if they'd truncate
-                the output. */
+                the output. Others return <buf_size> or greater.  */
              break;
            }
            n += ret;
@@ -750,6 +890,7 @@ add_tagged_field (proto_tree * tree, tvbuff_t * tvb, int offset)
       if (n < SHORT_STR)
        snprintf (out_buff + n, SHORT_STR - n, "[Mbit/sec]");
 
+      out_buff[SHORT_STR-1] = '\0';
       proto_tree_add_string (tree, tag_interpretation, tvb, offset + 2,
                             tag_len, out_buff);
       break;
@@ -852,6 +993,33 @@ add_tagged_field (proto_tree * tree, tvbuff_t * tvb, int offset)
 
       break;
 
+
+
+    case TAG_ERP_INFO:
+    case TAG_ERP_INFO_OLD:
+      proto_tree_add_uint_format (tree, tag_number, tvb, offset, 1, tag_no,
+                                 "Tag Number: %u (ERP Information)",
+                                 tag_no);
+
+      proto_tree_add_uint (tree, tag_length, tvb, offset + 1, 1, tag_len);
+      memset (out_buff, 0, SHORT_STR);
+
+      snprintf (out_buff, SHORT_STR,
+               "ERP info: 0x%x (%sNon-ERP STAs, %suse protection, %s preambles)",
+               tag_data_ptr[0],
+               tag_data_ptr[0] & 0x01 ? "" : "no ",
+               tag_data_ptr[0] & 0x02 ? "" : "do not ",
+               tag_data_ptr[0] & 0x04 ? "short or long": "long");
+      proto_tree_add_string (tree, tag_interpretation, tvb, offset + 2,
+                            tag_len, out_buff);
+
+      break;
+
+    case TAG_VENDOR_SPECIFIC_IE:
+      dissect_vendor_specific_ie(tree, tvb, offset, tag_number, tag_length, tag_interpretation);
+      break;
+
+
     default:
       return 0;
     }
@@ -1725,7 +1893,7 @@ dissect_ieee80211_common (tvbuff_t * tvb, packet_info * pinfo,
                                     wlan_fragment_table,
                                     wlan_reassembled_table,
                                     frag_number,
-                                    len,
+                                    reported_len,
                                     more_frags);
     if (fd_head != NULL) {
       /*
@@ -1740,12 +1908,8 @@ dissect_ieee80211_common (tvbuff_t * tvb, packet_info * pinfo,
        * 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, next_tvb, "Reassembled 802.11");
-
-        /* Show all fragments. */
-        show_fragment_seq_tree(fd_head, &frag_items, hdr_tree, pinfo, next_tvb);
+        next_tvb = process_reassembled_data(tvb, pinfo, "Reassembled 802.11",
+                fd_head, &frag_items, NULL, hdr_tree);
       } else {
        /*
         * Not fragmented, really.
@@ -1956,6 +2120,16 @@ proto_register_ieee80211 (void)
     "Channel agility not in use"
   };
 
+  static const true_false_string short_slot_time_flags = {
+    "Short slot time in use",
+    "Short slot time not in use"
+  };
+
+  static const true_false_string dsss_ofdm_flags = {
+    "DSSS-OFDM modulation allowed",
+    "DSSS-OFDM modulation not allowed"
+  };
+
 
   static const true_false_string cf_ibss_flags = {
     "Transmitter belongs to an IBSS",
@@ -2026,6 +2200,16 @@ proto_register_ieee80211 (void)
      "associated stations"},
     {0x12, "Association denied due to requesting station not supporting all "
      "of the datarates in the BSSBasicServiceSet Parameter"},
+    {0x13, "Association denied due to requesting station not supporting "
+     "short preamble operation"},
+    {0x14, "Association denied due to requesting station not supporting "
+     "PBCC encoding"},
+    {0x15, "Association denied due to requesting station not supporting "
+     "channel agility"},
+    {0x19, "Association denied due to requesting station not supporting "
+     "short slot operation"},
+    {0x1A, "Association denied due to requesting station not supporting "
+     "DSSS-OFDM operation"},
     {0x00, NULL}
   };
 
@@ -2178,6 +2362,10 @@ proto_register_ieee80211 (void)
       {"802.11 Fragments", "wlan.fragments", FT_NONE, BASE_NONE, NULL, 0x0,
        "802.11 Fragments", HFILL }},
 
+    {&hf_reassembled_in,
+      {"Reassembled 802.11 in frame", "wlan.reassembled_in", FT_FRAMENUM, BASE_NONE, NULL, 0x0,
+       "This 802.11 packet is reassembled in this frame", HFILL }},
+
     {&hf_wep_iv,
      {"Initialization Vector", "wlan.wep.iv", FT_UINT24, BASE_HEX, NULL, 0,
       "Initialization Vector", HFILL }},
@@ -2216,39 +2404,49 @@ proto_register_ieee80211 (void)
      {"Capabilities", "wlan_mgt.fixed.capabilities", FT_UINT16, BASE_HEX, NULL, 0,
       "Capability information", HFILL }},
 
+    {&ff_cf_ess,
+     {"ESS capabilities", "wlan_mgt.fixed.capabilities.ess",
+      FT_BOOLEAN, 16, TFS (&cf_ess_flags), 0x0001, "ESS capabilities", HFILL }},
+
+    {&ff_cf_ibss,
+     {"IBSS status", "wlan_mgt.fixed.capabilities.ibss",
+      FT_BOOLEAN, 16, TFS (&cf_ibss_flags), 0x0002, "IBSS participation", HFILL }},
+
     {&ff_cf_sta_poll,
      {"CFP participation capabilities", "wlan_mgt.fixed.capabilities.cfpoll.sta",
-      FT_UINT16, BASE_HEX, VALS (&sta_cf_pollable), 0,
+      FT_UINT16, BASE_HEX, VALS (&sta_cf_pollable), 0x000C,
       "CF-Poll capabilities for a STA", HFILL }},
 
     {&ff_cf_ap_poll,
      {"CFP participation capabilities", "wlan_mgt.fixed.capabilities.cfpoll.ap",
-      FT_UINT16, BASE_HEX, VALS (&ap_cf_pollable), 0,
+      FT_UINT16, BASE_HEX, VALS (&ap_cf_pollable), 0x000C,
       "CF-Poll capabilities for an AP", HFILL }},
 
-    {&ff_cf_ess,
-     {"ESS capabilities", "wlan_mgt.fixed.capabilities.ess",
-      FT_BOOLEAN, 8, TFS (&cf_ess_flags), 0x0001, "ESS capabilities", HFILL }},
-
-
-    {&ff_cf_ibss,
-     {"IBSS status", "wlan_mgt.fixed.capabilities.ibss",
-      FT_BOOLEAN, 8, TFS (&cf_ibss_flags), 0x0002, "IBSS participation", HFILL }},
     {&ff_cf_privacy,
      {"Privacy", "wlan_mgt.fixed.capabilities.privacy",
-      FT_BOOLEAN, 8, TFS (&cf_privacy_flags), 0x0010, "WEP support", HFILL }},
+      FT_BOOLEAN, 16, TFS (&cf_privacy_flags), 0x0010, "WEP support", HFILL }},
 
     {&ff_cf_preamble,
      {"Short Preamble", "wlan_mgt.fixed.capabilities.preamble",
-      FT_BOOLEAN, 8, TFS (&cf_preamble_flags), 0x0020, "Short Preamble", HFILL }},
+      FT_BOOLEAN, 16, TFS (&cf_preamble_flags), 0x0020, "Short Preamble", HFILL }},
 
     {&ff_cf_pbcc,
      {"PBCC", "wlan_mgt.fixed.capabilities.pbcc",
-      FT_BOOLEAN, 8, TFS (&cf_pbcc_flags), 0x0040, "PBCC Modulation", HFILL }},
+      FT_BOOLEAN, 16, TFS (&cf_pbcc_flags), 0x0040, "PBCC Modulation", HFILL }},
 
     {&ff_cf_agility,
      {"Channel Agility", "wlan_mgt.fixed.capabilities.agility",
-      FT_BOOLEAN, 8, TFS (&cf_agility_flags), 0x0080, "Channel Agility", HFILL }},
+      FT_BOOLEAN, 16, TFS (&cf_agility_flags), 0x0080, "Channel Agility", HFILL }},
+
+    {&ff_short_slot_time,
+     {"Short Slot Time", "wlan_mgt.fixed.capabilities.short_slot_time",
+      FT_BOOLEAN, 16, TFS (&short_slot_time_flags), 0x0400, "Short Slot Time",
+      HFILL }},
+
+    {&ff_dsss_ofdm,
+     {"DSSS-OFDM", "wlan_mgt.fixed.capabilities.dsss_ofdm",
+      FT_BOOLEAN, 16, TFS (&dsss_ofdm_flags), 0x2000, "DSSS-OFDM Modulation",
+      HFILL }},
 
     {&ff_auth_seq,
      {"Authentication SEQ", "wlan_mgt.fixed.auth_seq",
@@ -2477,7 +2675,7 @@ static tvbuff_t *try_decrypt_wep(tvbuff_t *tvb, guint32 offset, guint32 len) {
   }
 
  done:
-  if ((!decr_tvb) && (tmp))    free(tmp);
+  if ((!decr_tvb) && (tmp))    g_free(tmp);
 
 #if 0
   printf("de-wep %p\n", decr_tvb);
@@ -2574,8 +2772,6 @@ static int wep_decrypt(guint8 *buf, guint32 len, int key_override) {
   return 0;
 }
 
-/* XXX need to verify these malloc()s succeed */
-
 static void init_wepkeys(void) {
   char *tmp, *tmp2;
   guint8 *tmp3;
@@ -2601,8 +2797,8 @@ static void init_wepkeys(void) {
   if (wep_keylens != NULL)
     return;
 
-  wep_keys = malloc(num_wepkeys * sizeof(guint8*));
-  wep_keylens = malloc(num_wepkeys * sizeof(int));
+  wep_keys = g_malloc(num_wepkeys * sizeof(guint8*));
+  wep_keylens = g_malloc(num_wepkeys * sizeof(int));
 
   for (i = 0 ; i < num_wepkeys; i++) {
     wep_keys[i] = NULL;
@@ -2625,7 +2821,7 @@ static void init_wepkeys(void) {
 #endif
 #endif
 
-      wep_keys[i] = malloc(32 * sizeof(guint8));
+      wep_keys[i] = g_malloc(32 * sizeof(guint8));
       memset(wep_keys[i], 0, 32 * sizeof(guint8));
       tmp3 = wep_keys[i];
       while ((tmp != NULL) && (*tmp != 0)) {