From Peter Paluch via https://bugs.wireshark.org/bugzilla/show_bug.cgi?id=9160
authorEvan Huus <eapache@gmail.com>
Mon, 30 Sep 2013 11:57:14 +0000 (11:57 -0000)
committerEvan Huus <eapache@gmail.com>
Mon, 30 Sep 2013 11:57:14 +0000 (11:57 -0000)
Extend the BPDU dissector in packet-bpdu.c so that it recognizes and displays
the PVID TLV in Cisco's PVST+/RPVST+ BPDUs.

svn path=/trunk/; revision=52294

epan/dissectors/packet-bpdu.c

index e345b51e359246b77877b88fdf37573212d6f876..f9b9316e7f01c617af4339e52d3b9b54e271b923 100644 (file)
@@ -34,6 +34,7 @@
 #include <epan/etypes.h>
 #include <epan/addr_resolv.h>
 #include <epan/prefs.h>
+#include <epan/expert.h>
 
 /* Offsets of fields within a BPDU */
 
@@ -60,6 +61,8 @@
 #define BPDU_CIST_REMAINING_HOPS               101
 #define BPDU_MSTI                              102
 
+#define BPDU_PVST_TLV                          36
+
 #define MSTI_FLAGS                             0
 #define MSTI_REGIONAL_ROOT                     1
 #define MSTI_INTERNAL_ROOT_PATH_COST           9
@@ -156,6 +159,11 @@ static int hf_bpdu_flags_agree_valid = -1;
 static int hf_bpdu_flags_restricted_role = -1;
 static int hf_bpdu_spt_agreement_digest = -1;
 
+static int hf_bpdu_pvst_tlvtype = -1;
+static int hf_bpdu_pvst_tlvlength = -1;
+static int hf_bpdu_pvst_tlvvalue = -1;
+static int hf_bpdu_pvst_tlv_origvlan = -1;
+
 static gint ett_bpdu = -1;
 static gint ett_bpdu_flags = -1;
 static gint ett_root_id = -1;
@@ -166,6 +174,12 @@ static gint ett_cist_bridge_id = -1;
 static gint ett_spt = -1;
 static gint ett_aux_mcid = -1;
 static gint ett_agreement = -1;
+static gint ett_bpdu_pvst_tlv = -1;
+
+static expert_field ei_pvst_tlv_length_invalid = EI_INIT;
+static expert_field ei_pvst_tlv_origvlan_missing = EI_INIT;
+static expert_field ei_pvst_tlv_truncated = EI_INIT;
+static expert_field ei_pvst_tlv_unknown = EI_INIT;
 
 static gboolean bpdu_use_system_id_extensions = TRUE;
 
@@ -178,6 +192,14 @@ static const value_string protocol_id_vals[] = {
   { 0, NULL }
 };
 
+#define BPDU_PVST_TLV_ORIGVLAN 0               /* Originating VLAN TLV in Cisco (R)PVST+ BPDUs */
+
+static const value_string bpdu_pvst_tlv_vals[] = {
+  { BPDU_PVST_TLV_ORIGVLAN,            "Originating VLAN" },
+  { 0,                                 NULL }
+};
+
+
 #define BPDU_TYPE_CONF                 0x00    /* STP Configuration BPDU */
 #define BPDU_TYPE_RST                  0x02    /* RST BPDU (or MST) */
 #define BPDU_TYPE_TOPOLOGY_CHANGE      0x80    /* STP TCN (Topology change notify) BPDU */
@@ -223,7 +245,77 @@ static const char cont_sep[] = ", ";
   }
 
 static void
-dissect_bpdu(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree)
+dissect_bpdu(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, gboolean is_bpdu_pvst);
+
+static void
+dissect_bpdu_cisco(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree)
+{
+  dissect_bpdu(tvb, pinfo, tree, TRUE);
+}
+
+static void
+dissect_bpdu_generic(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree)
+{
+  dissect_bpdu(tvb, pinfo, tree, FALSE);
+}
+
+static void
+dissect_bpdu_pvst_tlv(packet_info *pinfo, proto_tree *tree, tvbuff_t *tvb) {
+  gboolean pvst_tlv_origvlan_present = FALSE;
+  guint16 tlv_type, tlv_length;
+  int offset = BPDU_PVST_TLV;
+  proto_item * ti = NULL;
+  proto_item * tlv_length_item = NULL;
+  proto_tree * tlv_tree = NULL;
+
+  if (tvb_reported_length_remaining(tvb, offset) < 4) /* TLV Type and Length fields occupy 4 bytes in total */
+    expert_add_info(pinfo, tree, &ei_pvst_tlv_truncated);
+
+  while (tvb_reported_length_remaining(tvb, offset) >= 4) { /* TLV Type and Length fields occupy 4 bytes in total */
+    tlv_type = tvb_get_ntohs(tvb, offset);
+    tlv_length = tvb_get_ntohs(tvb, offset + 2);
+
+    ti = proto_tree_add_text(tree, tvb, offset, 4 + tlv_length, "%s",
+                        val_to_str(tlv_type, bpdu_pvst_tlv_vals, "Unknown TLV type: 0x%04x"));
+
+    tlv_tree = proto_item_add_subtree(ti, ett_bpdu_pvst_tlv);
+    proto_tree_add_item(tlv_tree, hf_bpdu_pvst_tlvtype, tvb, offset, 2, ENC_BIG_ENDIAN);
+    tlv_length_item = proto_tree_add_item(tlv_tree, hf_bpdu_pvst_tlvlength,
+                        tvb, offset + 2, 2, ENC_BIG_ENDIAN);
+
+    if (tvb_reported_length_remaining(tvb, offset + 4) < tlv_length) {
+      expert_add_info(pinfo, tlv_length_item, &ei_pvst_tlv_truncated);
+      break;
+    }
+
+    offset += 4;
+
+    switch (tlv_type) {
+      case BPDU_PVST_TLV_ORIGVLAN:
+        if (tlv_length == 2) { /* Originating VLAN ID must be 2 bytes long */
+          proto_item_append_text(ti, " (PVID): %u", tvb_get_ntohs(tvb, offset));
+          proto_tree_add_item(tlv_tree, hf_bpdu_pvst_tlv_origvlan, tvb, offset, tlv_length, ENC_BIG_ENDIAN);
+          pvst_tlv_origvlan_present = TRUE;
+        }
+        else
+          expert_add_info(pinfo, tlv_length_item, &ei_pvst_tlv_length_invalid);
+        break;
+
+      default:
+        proto_tree_add_item(tlv_tree, hf_bpdu_pvst_tlvvalue, tvb, offset, tlv_length, ENC_NA);
+        expert_add_info(pinfo, tlv_tree, &ei_pvst_tlv_unknown);
+        break;
+    }
+
+    offset += tlv_length;
+  }
+
+  if (pvst_tlv_origvlan_present == FALSE) /* If a (R)PVST+ BPDU lacks the Originating VLAN TLV, it is malformed */
+    expert_add_info(pinfo, tree, &ei_pvst_tlv_origvlan_missing);
+}
+
+static void
+dissect_bpdu(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, gboolean is_bpdu_pvst)
 {
   guint16 protocol_identifier;
   guint8  protocol_version_identifier;
@@ -589,6 +681,9 @@ dissect_bpdu(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree)
                           BPDU_FORWARD_DELAY, 2, forward_delay);
 
     if (bpdu_type == BPDU_TYPE_CONF) {
+      if (is_bpdu_pvst)
+        dissect_bpdu_pvst_tlv(pinfo, bpdu_tree, tvb);
+
       /* Nothing more in this BPDU */
       set_actual_length(tvb, CONF_BPDU_SIZE);
       return;
@@ -1077,6 +1172,9 @@ dissect_bpdu(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree)
           }
         }
       }
+    } else { /* It is RSTP BPDU and may still be Rapid PVST+ */
+      if (is_bpdu_pvst)
+        dissect_bpdu_pvst_tlv(pinfo, bpdu_tree, tvb);
     }
   }
 }
@@ -1182,6 +1280,22 @@ proto_register_bpdu(void)
       { "Version 1 Length",            "stp.version_1_length",
        FT_UINT8,       BASE_DEC,       NULL,   0x0,
        NULL, HFILL }},
+    { &hf_bpdu_pvst_tlvtype,
+      { "Type",                                "stp.pvst.tlvtype",
+       FT_UINT16,      BASE_HEX,       VALS(bpdu_pvst_tlv_vals),       0x0,
+       NULL, HFILL }},
+    { &hf_bpdu_pvst_tlvlength,
+      { "Length",                      "stp.pvst.tlvlen",
+       FT_UINT16,      BASE_DEC,       NULL,   0x0,
+       NULL, HFILL }},
+    { &hf_bpdu_pvst_tlv_origvlan,
+      { "Originating VLAN",            "stp.pvst.origvlan",
+       FT_UINT16,      BASE_DEC,       NULL,   0x0,
+       NULL, HFILL }},
+    { &hf_bpdu_pvst_tlvvalue,
+      { "Value",       "stp.pvst.tlvval",
+       FT_BYTES,       BASE_NONE,      NULL,   0x0,
+       NULL, HFILL }},
     { &hf_bpdu_version_3_length,
       { "Version 3 Length",            "mstp.version_3_length",
        FT_UINT16,      BASE_DEC,       NULL,   0x0,
@@ -1301,15 +1415,40 @@ proto_register_bpdu(void)
     &ett_cist_bridge_id,
     &ett_spt,
     &ett_aux_mcid,
-    &ett_agreement
+    &ett_agreement,
+    &ett_bpdu_pvst_tlv
+  };
+
+  static ei_register_info ei[] = {
+  { &ei_pvst_tlv_length_invalid,
+    { "stp.pvst.tlvlen.invalid", PI_MALFORMED, PI_ERROR,
+      "Indicated length is not valid for this record type", EXPFILL }},
+
+  { &ei_pvst_tlv_origvlan_missing,
+    { "stp.pvst.origvlan.missing", PI_MALFORMED, PI_ERROR,
+      "Originating (PVID) VLAN TLV is missing or corrupt", EXPFILL }},
+
+  { &ei_pvst_tlv_truncated,
+    { "stp.pvst.tlv.truncated", PI_MALFORMED, PI_ERROR,
+      "TLV record is truncated prematurely", EXPFILL }},
+
+  { &ei_pvst_tlv_unknown,
+    { "stp.pvst.tlv.unknown", PI_UNDECODED, PI_COMMENT,
+      "TLV type is unknown", EXPFILL }}
   };
+
   module_t *bpdu_module;
+  expert_module_t *expert_bpdu;
 
   proto_bpdu = proto_register_protocol("Spanning Tree Protocol", "STP", "stp");
   proto_register_field_array(proto_bpdu, hf, array_length(hf));
   proto_register_subtree_array(ett, array_length(ett));
 
-  register_dissector("bpdu", dissect_bpdu, proto_bpdu);
+  register_dissector("bpdu", dissect_bpdu_generic, proto_bpdu);
+  register_dissector("bpdu_cisco", dissect_bpdu_cisco, proto_bpdu);
+
+  expert_bpdu = expert_register_protocol(proto_bpdu);
+  expert_register_field_array(expert_bpdu, ei, array_length(ei));
 
   bpdu_module = prefs_register_protocol(proto_bpdu, NULL);
   prefs_register_bool_preference(bpdu_module, "use_system_id_extension",
@@ -1337,7 +1476,9 @@ proto_reg_handoff_bpdu(void)
   bpdu_handle = find_dissector("bpdu");
   dissector_add_uint("llc.dsap", SAP_BPDU, bpdu_handle);
   dissector_add_uint("chdlc.protocol", CHDLCTYPE_BPDU, bpdu_handle);
-  dissector_add_uint("llc.cisco_pid", 0x010b, bpdu_handle);
-  dissector_add_uint("llc.cisco_pid", 0x010c, bpdu_handle);
   dissector_add_uint("ethertype", ETHERTYPE_STP, bpdu_handle);
+  dissector_add_uint("llc.cisco_pid", 0x010c, bpdu_handle); /* Cisco's VLAN-bridge STP is just plain STP */
+
+  bpdu_handle = find_dissector("bpdu_cisco");
+  dissector_add_uint("llc.cisco_pid", 0x010b, bpdu_handle); /* Handle Cisco's (R)PVST+ TLV extensions */
 }