Update manuf to current IEEE entries.
[obnox/wireshark/wip.git] / packet-bpdu.c
index db992d9a86547b9671cb0658631b7f60a3ee24e6..292a551f66d17fffe84ca7c437053905194f8406 100644 (file)
@@ -1,25 +1,24 @@
 /* packet-bpdu.c
  * Routines for BPDU (Spanning Tree Protocol) disassembly
  *
- * $Id: packet-bpdu.c,v 1.19 2001/01/09 06:31:34 guy Exp $
+ * $Id: packet-bpdu.c,v 1.45 2003/01/11 10:16:22 guy Exp $
  *
  * Copyright 1999 Christophe Tronche <ch.tronche@computer.org>
- * 
+ *
  * Ethereal - Network traffic analyzer
- * By Gerald Combs <gerald@zing.org>
+ * By Gerald Combs <gerald@ethereal.com>
  * Copyright 1998 Gerald Combs
  *
- * 
  * This program is free software; you can redistribute it and/or
  * modify it under the terms of the GNU General Public License
  * as published by the Free Software Foundation; either version 2
  * of the License, or (at your option) any later version.
- * 
+ *
  * This program is distributed in the hope that it will be useful,
  * but WITHOUT ANY WARRANTY; without even the implied warranty of
  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  * GNU General Public License for more details.
- * 
+ *
  * You should have received a copy of the GNU General Public License
  * along with this program; if not, write to the Free Software
  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
 # include "config.h"
 #endif
 
-#ifdef HAVE_SYS_TYPES_H
-# include <sys/types.h>
-#endif
-
-#ifdef HAVE_NETINET_IN_H
-# include <netinet/in.h>
-#endif
-
 #include <stdio.h>
 #include <string.h>
 #include <glib.h>
-#include "packet.h"
+#include <epan/packet.h>
 #include "llcsaps.h"
-#include "resolv.h"
-
-/* Include this for GVRP dissector */
-#include "packet-gvrp.h"
+#include "ppptypes.h"
+#include "chdlctypes.h"
+#include <epan/resolv.h>
 
 /* Offsets of fields within a BPDU */
 
 #define BPDU_MAX_AGE            29
 #define BPDU_HELLO_TIME         31
 #define BPDU_FORWARD_DELAY      33
+#define BPDU_VERSION_1_LENGTH  35
+#define BPDU_VERSION_3_LENGTH  36
+#define BPDU_MST_CONFIG_FORMAT_SELECTOR 38
+#define BPDU_MST_CONFIG_NAME 39
+#define BPDU_MST_CONFIG_REVISION_LEVEL 71
+#define BPDU_MST_CONFIG_DIGEST 73
+#define BPDU_CIST_INTERNAL_ROOT_PATH_COST 89
+#define BPDU_CIST_BRIDGE_IDENTIFIER 93
+#define BPDU_CIST_REMAINING_HOPS       101
+#define BPDU_MSTI                      102
+#define MSTI_FLAGS                     0
+#define MSTI_REGIONAL_ROOT             1
+#define MSTI_INTERNAL_ROOT_PATH_COST   9
+#define MSTI_BRIDGE_IDENTIFIER_PRIORITY        13
+#define MSTI_PORT_IDENTIFIER_PRIORITY  14
+#define MSTI_REMAINING_HOPS            15
+
+#define CONF_BPDU_SIZE         35
+#define TC_BPDU_SIZE           4
+#define RST_BPDU_SIZE          36
+#define VERSION_3_STATIC_LENGTH 64
+#define        MSTI_MESSAGE_SIZE       16
+
+/* Flag bits */
+
+#define BPDU_FLAGS_TCACK               0x80
+#define BPDU_FLAGS_AGREEMENT           0x40
+#define BPDU_FLAGS_FORWARDING          0x20
+#define BPDU_FLAGS_LEARNING            0x10
+#define BPDU_FLAGS_PORT_ROLE_MASK      0x0C
+#define BPDU_FLAGS_PORT_ROLE_SHIFT     2
+#define BPDU_FLAGS_PROPOSAL            0x02
+#define BPDU_FLAGS_TC                  0x01
 
 static int proto_bpdu = -1;
 static int hf_bpdu_proto_id = -1;
 static int hf_bpdu_version_id = -1;
 static int hf_bpdu_type = -1;
 static int hf_bpdu_flags = -1;
+static int hf_bpdu_flags_tcack = -1;
+static int hf_bpdu_flags_agreement = -1;
+static int hf_bpdu_flags_forwarding = -1;
+static int hf_bpdu_flags_learning = -1;
+static int hf_bpdu_flags_port_role = -1;
+static int hf_bpdu_flags_proposal = -1;
+static int hf_bpdu_flags_tc = -1;
 static int hf_bpdu_root_mac = -1;
 static int hf_bpdu_root_cost = -1;
 static int hf_bpdu_bridge_mac = -1;
@@ -75,36 +105,121 @@ static int hf_bpdu_msg_age = -1;
 static int hf_bpdu_max_age = -1;
 static int hf_bpdu_hello_time = -1;
 static int hf_bpdu_forward_delay = -1;
+static int hf_bpdu_version_1_length = -1;
+static int hf_bpdu_version_3_length = -1;
+static int hf_bpdu_mst_config_format_selector = -1;
+static int hf_bpdu_mst_config_name = -1;
+static int hf_bpdu_mst_config_revision_level = -1;
+static int hf_bpdu_mst_config_digest = -1;
+static int hf_bpdu_cist_internal_root_path_cost = -1;
+static int hf_bpdu_cist_bridge_identifier_mac = -1;
+static int hf_bpdu_cist_remaining_hops = -1;
+static int hf_bpdu_msti_flags = -1;
+static int hf_bpdu_msti_regional_root_mac = -1;
+static int hf_bpdu_msti_internal_root_path_cost = -1;
+static int hf_bpdu_msti_bridge_identifier_priority = -1;
+static int hf_bpdu_msti_port_identifier_priority = -1;
+static int hf_bpdu_msti_remaining_hops = -1;
 
 static gint ett_bpdu = -1;
+static gint ett_bpdu_flags = -1;
+static gint ett_mstp = -1;
+static gint ett_msti = -1;
+
+static dissector_handle_t gvrp_handle;
+static dissector_handle_t gmrp_handle;
+static dissector_handle_t data_handle;
+
+static const value_string protocol_id_vals[] = {
+       { 0, "Spanning Tree Protocol" },
+       { 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 */
+
+static const value_string bpdu_type_vals[] = {
+       { BPDU_TYPE_CONF,            "Configuration" },
+       { BPDU_TYPE_RST,             "Rapid/Multiple Spanning Tree" },
+       { BPDU_TYPE_TOPOLOGY_CHANGE, "Topology Change Notification" },
+       { 0,                         NULL }
+};
+
+#define PROTO_VERSION_STP      0
+#define PROTO_VERSION_RSTP     2
+#define PROTO_VERSION_MSTP     3
+
+static const value_string version_id_vals[] = {
+       { PROTO_VERSION_STP,    "Spanning Tree" },
+       { PROTO_VERSION_RSTP,   "Rapid Spanning Tree" },
+       { PROTO_VERSION_MSTP,   "Multiple Spanning Tree" },
+       { 0,                    NULL}
+};
+static const value_string role_vals[] = {
+       { 1, "Alternate or Backup" },
+       { 2, "Root" },
+       { 3, "Designated" },
+       { 0, NULL }
+};
+
+static const char initial_sep[] = " (";
+static const char cont_sep[] = ", ";
+
+#define APPEND_BOOLEAN_FLAG(flag, item, string) \
+       if(flag){                                                       \
+               if(item)                                                \
+                       proto_item_append_text(item, string, sep);      \
+               sep = 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)
+{
       guint16 protocol_identifier;
       guint8  protocol_version_identifier;
       guint8  bpdu_type;
       guint8  flags;
       guint16 root_identifier_bridge_priority;
-      guint8  *root_identifier_mac;
+      const guint8  *root_identifier_mac;
       gchar   *root_identifier_mac_str;
       guint32 root_path_cost;
       guint16 bridge_identifier_bridge_priority;
-      guint8  *bridge_identifier_mac;
+      const guint8  *bridge_identifier_mac;
       gchar   *bridge_identifier_mac_str;
       guint16 port_identifier;
       double message_age;
       double max_age;
       double hello_time;
       double forward_delay;
-      
-      proto_tree *bpdu_tree;
-      proto_item *ti;
+      guint16 version_3_length;
+      guint32 cist_internal_root_path_cost;
+      guint8 mst_config_format_selector;
+      guint16 cist_bridge_identifier_bridge_priority;
+      const guint8  *cist_bridge_identifier_mac;
+      gchar   *cist_bridge_identifier_mac_str;
+      const guint8 *mst_config_name;
+      guint16 mst_config_revision_level;
+      guint8 cist_remaining_hops, msti_remaining_hops;
+      guint32 msti_internal_root_path_cost;
+      guint32 msti_regional_root_mstid, msti_regional_root_priority;
+      const guint8  *msti_regional_root_mac;
+      gchar   *msti_regional_root_mac_str;
+      guint8 msti_bridge_identifier_priority, msti_port_identifier_priority;
+      int      length, offset, msti;
 
-      CHECK_DISPLAY_AS_DATA(proto_bpdu, tvb, pinfo, tree);
+      proto_tree *bpdu_tree;
+      proto_tree *mstp_tree, *msti_tree;
+      proto_item *bpdu_item;
+      proto_item *mstp_item, *msti_item;
+      proto_tree *flags_tree;
+      proto_item *flags_item;
+      guint8   rstp_bpdu, mstp_bpdu=0;
+      const char *sep;
 
       /* GARP application frames require special interpretation of the
          destination address field; otherwise, they will be mistaken as
-         BPDU frames.  
+         BPDU frames.
          Fortunately, they can be recognized by checking the first 6 octets
          of the destination address, which are in the range from
          01-80-C2-00-00-20 to 01-80-C2-00-00-2F.
@@ -115,31 +230,32 @@ dissect_bpdu(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree) {
       if (pinfo->dl_dst.type == AT_ETHER &&
          pinfo->dl_dst.data[0] == 0x01 && pinfo->dl_dst.data[1] == 0x80 &&
          pinfo->dl_dst.data[2] == 0xC2 && pinfo->dl_dst.data[3] == 0x00 &&
-         pinfo->dl_dst.data[4] == 0x00 && ((pinfo->dl_dst.data[5] & 0x20) == 0x20)) {
+         pinfo->dl_dst.data[4] == 0x00 && ((pinfo->dl_dst.data[5] & 0xF0) == 0x20)) {
 
            protocol_identifier = tvb_get_ntohs(tvb, BPDU_IDENTIFIER);
 
            switch (pinfo->dl_dst.data[5]) {
 
            case 0x20:
-                 /* Future expansion for GMRP */
-                 break;
+                  /* for GMRP */
+                 call_dissector(gmrp_handle, tvb, pinfo, tree);
+                 return;
 
            case 0x21:
                  /* for GVRP */
-                 dissect_gvrp(tvb, pinfo, tree);
+                 call_dissector(gvrp_handle, tvb, pinfo, tree);
                  return;
            }
 
            pinfo->current_proto = "GARP";
 
-           if (check_col(pinfo->fd, COL_PROTOCOL)) {
-                   col_set_str(pinfo->fd, COL_PROTOCOL, "GARP");
+           if (check_col(pinfo->cinfo, COL_PROTOCOL)) {
+                   col_set_str(pinfo->cinfo, COL_PROTOCOL, "GARP");
                    /* Generic Attribute Registration Protocol */
            }
 
-           if (check_col(pinfo->fd, COL_INFO)) {
-                   col_add_fstr(pinfo->fd, COL_INFO,
+           if (check_col(pinfo->cinfo, COL_INFO)) {
+                   col_add_fstr(pinfo->cinfo, COL_INFO,
                        "Unknown GARP application (0x%02X)",
                        pinfo->dl_dst.data[5]);
             }
@@ -147,116 +263,336 @@ dissect_bpdu(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree) {
            return;
       }
 
-      pinfo->current_proto = "STP";
+      if (check_col(pinfo->cinfo, COL_PROTOCOL)) {
+           col_set_str(pinfo->cinfo, COL_PROTOCOL, "STP"); /* Spanning Tree Protocol */
+      }
+      if (check_col(pinfo->cinfo, COL_INFO)) {
+           col_clear(pinfo->cinfo, COL_INFO);
+      }
 
       bpdu_type = tvb_get_guint8(tvb, BPDU_TYPE);
-      flags = tvb_get_guint8(tvb, BPDU_FLAGS);
-      root_identifier_bridge_priority = tvb_get_ntohs(tvb, BPDU_ROOT_IDENTIFIER);
-      root_identifier_mac = tvb_get_ptr(tvb, BPDU_ROOT_IDENTIFIER + 2, 6);
-      root_identifier_mac_str = ether_to_str(root_identifier_mac);
-      root_path_cost = tvb_get_ntohl(tvb, BPDU_ROOT_PATH_COST);
-      port_identifier = tvb_get_ntohs(tvb, BPDU_PORT_IDENTIFIER);
-
-      if (check_col(pinfo->fd, COL_PROTOCOL)) {
-           col_set_str(pinfo->fd, COL_PROTOCOL, "STP"); /* Spanning Tree Protocol */
+
+      protocol_version_identifier = tvb_get_guint8(tvb, BPDU_VERSION_IDENTIFIER);
+
+      switch (bpdu_type) {
+
+      case BPDU_TYPE_CONF:
+      case BPDU_TYPE_RST:
+           flags = tvb_get_guint8(tvb, BPDU_FLAGS);
+           root_identifier_bridge_priority = tvb_get_ntohs(tvb,BPDU_ROOT_IDENTIFIER);
+           root_identifier_mac = tvb_get_ptr(tvb, BPDU_ROOT_IDENTIFIER + 2, 6);
+           root_identifier_mac_str = ether_to_str(root_identifier_mac);
+           root_path_cost = tvb_get_ntohl(tvb, BPDU_ROOT_PATH_COST);
+           port_identifier = tvb_get_ntohs(tvb, BPDU_PORT_IDENTIFIER);
+           break;
+
+      default:
+           /* Squelch GCC complaints. */
+           flags = 0;
+           root_identifier_bridge_priority = 0;
+           root_identifier_mac = NULL;
+           root_identifier_mac_str = NULL;
+           root_path_cost = 0;
+           port_identifier = 0;
+           break;
       }
 
-      if (check_col(pinfo->fd, COL_INFO)) {
-           if (bpdu_type == 0)
-                 col_add_fstr(pinfo->fd, COL_INFO, "Conf. %sRoot = %d/%s  Cost = %d  Port = 0x%04x", 
+      if (check_col(pinfo->cinfo, COL_INFO)) {
+           switch (bpdu_type) {
+
+           case BPDU_TYPE_CONF:
+                 col_add_fstr(pinfo->cinfo, COL_INFO, "Conf. %sRoot = %d/%s  Cost = %d  Port = 0x%04x",
+                              flags & 0x1 ? "TC + " : "",
+                              root_identifier_bridge_priority, root_identifier_mac_str, root_path_cost,
+                              port_identifier);
+                 break;
+
+           case BPDU_TYPE_TOPOLOGY_CHANGE:
+                 col_add_fstr(pinfo->cinfo, COL_INFO, "Topology Change Notification");
+                 break;
+
+           case BPDU_TYPE_RST:
+                 col_add_fstr(pinfo->cinfo, COL_INFO, "%cST. %sRoot = %d/%s  Cost = %d  Port = 0x%04x",
+                              protocol_version_identifier == 3 ? 'M':'R',
                               flags & 0x1 ? "TC + " : "",
                               root_identifier_bridge_priority, root_identifier_mac_str, root_path_cost,
                               port_identifier);
-           else if (bpdu_type == 0x80)
-                 col_add_fstr(pinfo->fd, COL_INFO, "Topology Change Notification");
+                 break;
+
+           default:
+                 col_add_fstr(pinfo->cinfo, COL_INFO, "Unknown BPDU type (%u)",
+                              bpdu_type);
+                  break;
+            }
+      }
+
+      switch (bpdu_type) {
+
+      case BPDU_TYPE_CONF:
+        set_actual_length(tvb, CONF_BPDU_SIZE);
+        break;
+
+      case BPDU_TYPE_TOPOLOGY_CHANGE:
+        set_actual_length(tvb, TC_BPDU_SIZE);
+        break;
+
+      case BPDU_TYPE_RST:
+       if (protocol_version_identifier == 3) {
+           version_3_length = tvb_get_ntohs(tvb, BPDU_VERSION_3_LENGTH);
+           set_actual_length(tvb, RST_BPDU_SIZE + 2 + version_3_length);
+       } else
+           set_actual_length(tvb, RST_BPDU_SIZE);
+        break;
       }
 
       if (tree) {
+           bpdu_item = proto_tree_add_protocol_format(tree, proto_bpdu, tvb,
+                               0, -1, "Spanning Tree Protocol");
+           bpdu_tree = proto_item_add_subtree(bpdu_item, ett_bpdu);
+
            protocol_identifier = tvb_get_ntohs(tvb, BPDU_IDENTIFIER);
-           protocol_version_identifier = tvb_get_guint8(tvb, BPDU_VERSION_IDENTIFIER);
-
-           ti = proto_tree_add_protocol_format(tree, proto_bpdu, tvb, 0, 35,
-                               "Spanning Tree Protocol");
-           bpdu_tree = proto_item_add_subtree(ti, ett_bpdu);
-           proto_tree_add_uint_format(bpdu_tree, hf_bpdu_proto_id, tvb,
-                                      BPDU_IDENTIFIER, 2, 
-                                      protocol_identifier,
-                                      "Protocol Identifier: 0x%04x (%s)", 
-                                      protocol_identifier,
-                                      protocol_identifier == 0 ? 
-                                      "Spanning Tree" : "Unknown Protocol");
-
-           proto_tree_add_uint(bpdu_tree, hf_bpdu_version_id, tvb, 
-                               BPDU_VERSION_IDENTIFIER, 1, 
+           proto_tree_add_uint(bpdu_tree, hf_bpdu_proto_id, tvb,
+                               BPDU_IDENTIFIER, 2, protocol_identifier);
+
+           proto_tree_add_uint(bpdu_tree, hf_bpdu_version_id, tvb,
+                               BPDU_VERSION_IDENTIFIER, 1,
                                protocol_version_identifier);
-           if (protocol_version_identifier != 0)
+            switch (protocol_version_identifier) {
+              case 0:
+                break;
+              case 2:
+              case 3:
+                break;
+              default:
                  proto_tree_add_text(bpdu_tree, tvb, BPDU_VERSION_IDENTIFIER, 1,
-                 "   (Warning: this version of Ethereal only knows about version = 0)");
-           proto_tree_add_uint_format(bpdu_tree, hf_bpdu_type, tvb,
-                                      BPDU_TYPE, 1, 
-                                      bpdu_type,
-                                      "BPDU Type: 0x%02x (%s)", 
-                                      bpdu_type,
-                                      bpdu_type == 0 ? "Configuration" :
-                                      bpdu_type == 0x80 ? "Topology Change Notification" : "Unknown");
-
-           if (bpdu_type != 0) {
-             dissect_data(tvb, BPDU_TYPE + 1, pinfo, tree);
+                 "   (Warning: this version of Ethereal only knows about versions 0, 2 & 3)");
+                break;
+            }
+           proto_tree_add_uint(bpdu_tree, hf_bpdu_type, tvb,
+                                      BPDU_TYPE, 1,
+                                      bpdu_type);
+
+           if (bpdu_type != BPDU_TYPE_CONF && bpdu_type != BPDU_TYPE_RST) {
+             call_dissector(data_handle,
+                            tvb_new_subset(tvb, BPDU_TYPE + 1, -1, -1),
+                            pinfo, tree);
              return;
            }
 
+            rstp_bpdu = (bpdu_type == BPDU_TYPE_RST);
+            if (rstp_bpdu) mstp_bpdu = (protocol_version_identifier == 3);
+
            bridge_identifier_bridge_priority = tvb_get_ntohs(tvb, BPDU_BRIDGE_IDENTIFIER);
            bridge_identifier_mac = tvb_get_ptr(tvb, BPDU_BRIDGE_IDENTIFIER + 2, 6);
            bridge_identifier_mac_str = ether_to_str(bridge_identifier_mac);
-           message_age = tvb_get_ntohs(tvb, BPDU_MESSAGE_AGE) / 256.0;
-           max_age = tvb_get_ntohs(tvb, BPDU_MAX_AGE) / 256.0;
-           hello_time = tvb_get_ntohs(tvb, BPDU_HELLO_TIME) / 256.0;
-           forward_delay = tvb_get_ntohs(tvb, BPDU_FORWARD_DELAY) / 256.0;
 
-           proto_tree_add_uint(bpdu_tree, hf_bpdu_flags, tvb, 
+           flags_item = proto_tree_add_uint(bpdu_tree, hf_bpdu_flags, tvb,
                                BPDU_FLAGS, 1, flags);
-           if (flags & 0x80)
-                 proto_tree_add_text(bpdu_tree, tvb, BPDU_FLAGS, 1, "   1... ....  Topology Change Acknowledgment");
-           if (flags & 0x01)
-                 proto_tree_add_text(bpdu_tree, tvb, BPDU_FLAGS, 1, "   .... ...1  Topology Change");
+           flags_tree = proto_item_add_subtree(flags_item, ett_bpdu_flags);
+           sep = initial_sep;
+           APPEND_BOOLEAN_FLAG(flags & BPDU_FLAGS_TCACK, flags_item,
+                               "%sTopology Change Acknowledgment");
+           proto_tree_add_boolean(flags_tree, hf_bpdu_flags_tcack, tvb,
+                               BPDU_FLAGS, 1, flags);
+            if (rstp_bpdu) {
+             APPEND_BOOLEAN_FLAG(flags & BPDU_FLAGS_AGREEMENT, flags_item,
+                                 "%sAgreement");
+             proto_tree_add_boolean(flags_tree, hf_bpdu_flags_agreement, tvb,
+                                 BPDU_FLAGS, 1, flags);
+             APPEND_BOOLEAN_FLAG(flags & BPDU_FLAGS_FORWARDING, flags_item,
+                                 "%sForwarding");
+             proto_tree_add_boolean(flags_tree, hf_bpdu_flags_forwarding, tvb,
+                                 BPDU_FLAGS, 1, flags);
+             APPEND_BOOLEAN_FLAG(flags & BPDU_FLAGS_LEARNING, flags_item,
+                                 "%sLearning");
+             proto_tree_add_boolean(flags_tree, hf_bpdu_flags_learning, tvb,
+                                 BPDU_FLAGS, 1, flags);
+             if (flags_item) {
+               guint8 port_role;
+
+               port_role = (flags & BPDU_FLAGS_PORT_ROLE_MASK) >> BPDU_FLAGS_PORT_ROLE_SHIFT;
+               proto_item_append_text(flags_item, "%sPort Role: %s", sep,
+                                      val_to_str(port_role, role_vals,
+                                                 "Unknown (%u)"));
+             }
+             sep = cont_sep;
+             proto_tree_add_uint(flags_tree, hf_bpdu_flags_port_role, tvb,
+                                 BPDU_FLAGS, 1, flags);
+             APPEND_BOOLEAN_FLAG(flags & BPDU_FLAGS_PROPOSAL, flags_item,
+                                 "%sProposal");
+             proto_tree_add_boolean(flags_tree, hf_bpdu_flags_proposal, tvb,
+                                 BPDU_FLAGS, 1, flags);
+            }
+           APPEND_BOOLEAN_FLAG(flags & BPDU_FLAGS_TC, flags_item,
+                               "%sTopology Change");
+           proto_tree_add_boolean(flags_tree, hf_bpdu_flags_tc, tvb,
+                               BPDU_FLAGS, 1, flags);
+           if (sep != initial_sep) {
+             /* We put something in; put in the terminating ")" */
+             proto_item_append_text(flags_item, ")");
+           }
 
            proto_tree_add_ether_hidden(bpdu_tree, hf_bpdu_root_mac, tvb,
                                       BPDU_ROOT_IDENTIFIER + 2, 6,
                                       root_identifier_mac);
-           proto_tree_add_text(bpdu_tree, tvb, 
-                               BPDU_ROOT_IDENTIFIER, 8, 
-                               "Root Identifier: %d / %s", 
-                               root_identifier_bridge_priority, 
+           proto_tree_add_text(bpdu_tree, tvb,
+                               BPDU_ROOT_IDENTIFIER, 8,
+                               "Root Identifier: %d / %s",
+                               root_identifier_bridge_priority,
                                root_identifier_mac_str);
-           proto_tree_add_uint(bpdu_tree, hf_bpdu_root_cost, tvb, 
-                               BPDU_ROOT_PATH_COST, 4, 
+           proto_tree_add_uint(bpdu_tree, hf_bpdu_root_cost, tvb,
+                               BPDU_ROOT_PATH_COST, 4,
                                root_path_cost);
-           proto_tree_add_text(bpdu_tree, tvb, 
-                               BPDU_BRIDGE_IDENTIFIER, 8, 
-                               "Bridge Identifier: %d / %s", 
-                               bridge_identifier_bridge_priority, 
+           proto_tree_add_text(bpdu_tree, tvb,
+                               BPDU_BRIDGE_IDENTIFIER, 8,
+                               "Bridge Identifier: %d / %s",
+                               bridge_identifier_bridge_priority,
                                bridge_identifier_mac_str);
            proto_tree_add_ether_hidden(bpdu_tree, hf_bpdu_bridge_mac, tvb,
                                       BPDU_BRIDGE_IDENTIFIER + 2, 6,
                                       bridge_identifier_mac);
            proto_tree_add_uint(bpdu_tree, hf_bpdu_port_id, tvb,
-                               BPDU_PORT_IDENTIFIER, 2, 
+                               BPDU_PORT_IDENTIFIER, 2,
                                port_identifier);
+           message_age = tvb_get_ntohs(tvb, BPDU_MESSAGE_AGE) / 256.0;
            proto_tree_add_double(bpdu_tree, hf_bpdu_msg_age, tvb,
-                               BPDU_MESSAGE_AGE, 2, 
+                               BPDU_MESSAGE_AGE, 2,
                                message_age);
+           max_age = tvb_get_ntohs(tvb, BPDU_MAX_AGE) / 256.0;
            proto_tree_add_double(bpdu_tree, hf_bpdu_max_age, tvb,
-                               BPDU_MAX_AGE, 2, 
+                               BPDU_MAX_AGE, 2,
                                max_age);
+           hello_time = tvb_get_ntohs(tvb, BPDU_HELLO_TIME) / 256.0;
            proto_tree_add_double(bpdu_tree, hf_bpdu_hello_time, tvb,
-                               BPDU_HELLO_TIME, 2, 
+                               BPDU_HELLO_TIME, 2,
                                hello_time);
+           forward_delay = tvb_get_ntohs(tvb, BPDU_FORWARD_DELAY) / 256.0;
            proto_tree_add_double(bpdu_tree, hf_bpdu_forward_delay, tvb,
-                               BPDU_FORWARD_DELAY, 2, 
+                               BPDU_FORWARD_DELAY, 2,
                                forward_delay);
+            if (rstp_bpdu) {
+             proto_tree_add_item(bpdu_tree, hf_bpdu_version_1_length, tvb,
+                               BPDU_VERSION_1_LENGTH, 1, FALSE);
+           }
+           if (mstp_bpdu) {
+               version_3_length = tvb_get_ntohs(tvb, BPDU_VERSION_3_LENGTH);
+
+               mstp_item = proto_tree_add_uint(bpdu_tree, hf_bpdu_version_3_length, tvb,
+                       BPDU_VERSION_3_LENGTH, 2, version_3_length);
+               mstp_tree = proto_item_add_subtree(mstp_item, ett_mstp);
+
+
+               mst_config_format_selector = tvb_get_guint8(tvb, BPDU_MST_CONFIG_FORMAT_SELECTOR);
+               proto_tree_add_uint(mstp_tree, hf_bpdu_mst_config_format_selector, tvb,
+                       BPDU_MST_CONFIG_FORMAT_SELECTOR, 1, mst_config_format_selector);
+               mst_config_name =  tvb_get_ptr (tvb, BPDU_MST_CONFIG_NAME, 32);
+               proto_tree_add_string(mstp_tree, hf_bpdu_mst_config_name, tvb, BPDU_MST_CONFIG_NAME, 32, mst_config_name);
+
+               mst_config_revision_level = tvb_get_ntohs(tvb, BPDU_MST_CONFIG_REVISION_LEVEL);
+               proto_tree_add_uint(mstp_tree, hf_bpdu_mst_config_revision_level, tvb,
+                       BPDU_MST_CONFIG_REVISION_LEVEL, 2, mst_config_revision_level);
+               proto_tree_add_bytes(mstp_tree, hf_bpdu_mst_config_digest, tvb,
+                       BPDU_MST_CONFIG_DIGEST, 16, tvb_get_ptr(tvb, BPDU_MST_CONFIG_DIGEST, 16));
+
+               cist_internal_root_path_cost = tvb_get_ntohl(tvb, BPDU_CIST_INTERNAL_ROOT_PATH_COST);
+               proto_tree_add_uint(mstp_tree, hf_bpdu_cist_internal_root_path_cost, tvb,
+                       BPDU_CIST_INTERNAL_ROOT_PATH_COST, 4, cist_internal_root_path_cost);
+
+               cist_bridge_identifier_bridge_priority = tvb_get_ntohs(tvb,BPDU_CIST_BRIDGE_IDENTIFIER);
+               cist_bridge_identifier_mac = tvb_get_ptr(tvb, BPDU_CIST_BRIDGE_IDENTIFIER + 2, 6);
+               cist_bridge_identifier_mac_str = ether_to_str(cist_bridge_identifier_mac);
+               proto_tree_add_text(mstp_tree, tvb, BPDU_CIST_BRIDGE_IDENTIFIER, 8,
+                               "CIST Bridge Identifier: %d / %s",
+                               cist_bridge_identifier_bridge_priority,
+                               cist_bridge_identifier_mac_str);
+               proto_tree_add_ether_hidden(mstp_tree, hf_bpdu_cist_bridge_identifier_mac, tvb,
+                                      BPDU_CIST_BRIDGE_IDENTIFIER + 2, 6,
+                                      cist_bridge_identifier_mac);
+
+               cist_remaining_hops = tvb_get_guint8(tvb, BPDU_CIST_REMAINING_HOPS);
+               proto_tree_add_uint(mstp_tree, hf_bpdu_cist_remaining_hops, tvb,
+                       BPDU_CIST_REMAINING_HOPS, 1, cist_remaining_hops);
+
+       /* MSTI messages */
+               length = version_3_length - VERSION_3_STATIC_LENGTH;
+               offset = BPDU_MSTI;
+               msti = 1;
+               while (length >= MSTI_MESSAGE_SIZE) {
+                   msti_regional_root_mstid = tvb_get_guint8(tvb,  offset+ MSTI_REGIONAL_ROOT);
+                   msti_regional_root_priority = (msti_regional_root_mstid &0xf0) << 8;
+                   msti_regional_root_mstid = ((msti_regional_root_mstid & 0x0f) << 8) +
+                               tvb_get_guint8(tvb,  offset+ MSTI_REGIONAL_ROOT+1);;
+                   msti_regional_root_mac = tvb_get_ptr(tvb, offset+ MSTI_REGIONAL_ROOT + 2, 6);
+                   msti_regional_root_mac_str = ether_to_str(msti_regional_root_mac);
+
+                   msti_item = proto_tree_add_text(mstp_tree, tvb, offset, 16,
+                       "MSTID %d, Regional Root Identifier %d / %s",
+                       msti_regional_root_mstid, msti_regional_root_priority,
+                       msti_regional_root_mac_str);
+                   msti_tree = proto_item_add_subtree(msti_item, ett_msti);
+
+                   /* flags */
+                   flags_item = proto_tree_add_uint(msti_tree, hf_bpdu_msti_flags, tvb,
+                       offset+MSTI_FLAGS, 1, flags);
+
+                   sep = initial_sep;
+                   APPEND_BOOLEAN_FLAG(flags & BPDU_FLAGS_TCACK, flags_item, "%sMaster");
+                   APPEND_BOOLEAN_FLAG(flags & BPDU_FLAGS_AGREEMENT, flags_item, "%sAgreement");
+                   APPEND_BOOLEAN_FLAG(flags & BPDU_FLAGS_FORWARDING, flags_item, "%sForwarding");
+                   APPEND_BOOLEAN_FLAG(flags & BPDU_FLAGS_LEARNING, flags_item, "%sLearning");
+                   if (flags_item) {
+                       guint8 port_role;
+                       port_role = (flags & BPDU_FLAGS_PORT_ROLE_MASK) >> BPDU_FLAGS_PORT_ROLE_SHIFT;
+                       proto_item_append_text(flags_item, "%sPort Role: %s", sep,
+                                      val_to_str(port_role, role_vals, "Unknown (%u)"));
+                   }
+                   sep = cont_sep;
+                   APPEND_BOOLEAN_FLAG(flags & BPDU_FLAGS_PROPOSAL, flags_item, "%sProposal");
+                   APPEND_BOOLEAN_FLAG(flags & BPDU_FLAGS_TC, flags_item, "%sTopology Change");
+                   if (sep != initial_sep) {         /* We put something in; put in the terminating ")" */
+                       proto_item_append_text(flags_item, ")");
+                   }
+
+                   /* pri, MSTID, Regional root */
+                   proto_tree_add_ether_hidden(msti_tree, hf_bpdu_msti_regional_root_mac, tvb,
+                                      offset + MSTI_REGIONAL_ROOT + 2, 6,
+                                      msti_regional_root_mac);
+                   proto_tree_add_text(msti_tree, tvb, offset + MSTI_REGIONAL_ROOT, 8,
+                               "MSTID %d, priority %d Root Identifier %s",
+                               msti_regional_root_mstid, msti_regional_root_priority,
+                               msti_regional_root_mac_str);
+
+
+                   msti_internal_root_path_cost = tvb_get_ntohs(tvb, offset+MSTI_INTERNAL_ROOT_PATH_COST);
+                   proto_tree_add_uint(msti_tree, hf_bpdu_msti_internal_root_path_cost, tvb,
+                       offset+MSTI_INTERNAL_ROOT_PATH_COST, 4, msti_internal_root_path_cost);
+
+                   msti_bridge_identifier_priority = tvb_get_guint8(tvb, offset+MSTI_BRIDGE_IDENTIFIER_PRIORITY) >> 4;
+                   msti_port_identifier_priority = tvb_get_guint8(tvb, offset+MSTI_PORT_IDENTIFIER_PRIORITY) >> 4;
+
+                   proto_tree_add_uint(msti_tree, hf_bpdu_msti_bridge_identifier_priority, tvb,
+                       offset+MSTI_BRIDGE_IDENTIFIER_PRIORITY, 1, msti_bridge_identifier_priority);
+                   proto_tree_add_uint(msti_tree, hf_bpdu_msti_port_identifier_priority, tvb,
+                       offset+MSTI_PORT_IDENTIFIER_PRIORITY, 1, msti_port_identifier_priority);
+                   
+                   msti_remaining_hops = tvb_get_guint8(tvb, offset+MSTI_REMAINING_HOPS);
+                   proto_tree_add_uint(msti_tree, hf_bpdu_msti_remaining_hops, tvb,
+                       offset + MSTI_REMAINING_HOPS, 1, msti_remaining_hops);
+
+                   length -= MSTI_MESSAGE_SIZE;
+                   offset += MSTI_MESSAGE_SIZE;
+                   msti++;
+               }
+               
+           }
       }
 }
 
+static const true_false_string yesno = {
+       "Yes",
+       "No"
+};
+
 void
 proto_register_bpdu(void)
 {
@@ -264,55 +600,147 @@ proto_register_bpdu(void)
   static hf_register_info hf[] = {
     { &hf_bpdu_proto_id,
       { "Protocol Identifier",         "stp.protocol",
-       FT_UINT16,      BASE_HEX,       NULL,   0x0,
-       "" }},
+       FT_UINT16,      BASE_HEX,       VALS(&protocol_id_vals), 0x0,
+       "", HFILL }},
     { &hf_bpdu_version_id,
       { "Protocol Version Identifier", "stp.version",
-       FT_UINT8,       BASE_DEC,       NULL,   0x0,
-       "" }},
+       FT_UINT8,       BASE_DEC,       VALS(&version_id_vals), 0x0,
+       "", HFILL }},
     { &hf_bpdu_type,
-      { "BPDU type",                   "stp.type",
-       FT_UINT8,       BASE_HEX,       NULL,   0x0,
-       "" }},
+      { "BPDU Type",                   "stp.type",
+       FT_UINT8,       BASE_HEX,       VALS(&bpdu_type_vals),  0x0,
+       "", HFILL }},
     { &hf_bpdu_flags,
       { "BPDU flags",                  "stp.flags",
        FT_UINT8,       BASE_HEX,       NULL,   0x0,
-       "" }},
+       "", HFILL }},
+    { &hf_bpdu_flags_tcack,
+      { "Topology Change Acknowledgment",  "stp.flags.tcack",
+       FT_BOOLEAN,     8,              TFS(&yesno),    BPDU_FLAGS_TCACK,
+       "", HFILL }},
+    { &hf_bpdu_flags_agreement,
+      { "Agreement",                   "stp.flags.agreement",
+       FT_BOOLEAN,     8,              TFS(&yesno),    BPDU_FLAGS_AGREEMENT,
+       "", HFILL }},
+    { &hf_bpdu_flags_forwarding,
+      { "Forwarding",                  "stp.flags.forwarding",
+       FT_BOOLEAN,     8,              TFS(&yesno),    BPDU_FLAGS_FORWARDING,
+       "", HFILL }},
+    { &hf_bpdu_flags_learning,
+      { "Learning",                    "stp.flags.learning",
+       FT_BOOLEAN,     8,              TFS(&yesno),    BPDU_FLAGS_LEARNING,
+       "", HFILL }},
+    { &hf_bpdu_flags_port_role,
+      { "Port Role",                   "stp.flags.port_role",
+       FT_UINT8,       BASE_DEC,       VALS(role_vals),        BPDU_FLAGS_PORT_ROLE_MASK,
+       "", HFILL }},
+    { &hf_bpdu_flags_proposal,
+      { "Proposal",                    "stp.flags.proposal",
+       FT_BOOLEAN,     8,              TFS(&yesno),    BPDU_FLAGS_PROPOSAL,
+       "", HFILL }},
+    { &hf_bpdu_flags_tc,
+      { "Topology Change",             "stp.flags.tc",
+       FT_BOOLEAN,     8,              TFS(&yesno),    BPDU_FLAGS_TC,
+       "", HFILL }},
     { &hf_bpdu_root_mac,
       { "Root Identifier",             "stp.root.hw",
        FT_ETHER,       BASE_NONE,      NULL,   0x0,
-       "" }},
+       "", HFILL }},
     { &hf_bpdu_root_cost,
       { "Root Path Cost",              "stp.root.cost",
        FT_UINT32,      BASE_DEC,       NULL,   0x0,
-       "" }},
+       "", HFILL }},
     { &hf_bpdu_bridge_mac,
       { "Bridge Identifier",           "stp.bridge.hw",
        FT_ETHER,       BASE_NONE,      NULL,   0x0,
-       ""}},
+       "", HFILL }},
     { &hf_bpdu_port_id,
       { "Port identifier",             "stp.port",
        FT_UINT16,      BASE_HEX,       NULL,   0x0,
-       ""}},
+       "", HFILL }},
     { &hf_bpdu_msg_age,
       { "Message Age",                 "stp.msg_age",
        FT_DOUBLE,      BASE_NONE,      NULL,   0x0,
-       "" }},
+       "", HFILL }},
     { &hf_bpdu_max_age,
       { "Max Age",                     "stp.max_age",
        FT_DOUBLE,      BASE_NONE,      NULL,   0x0,
-       "" }},
+       "", HFILL }},
     { &hf_bpdu_hello_time,
       { "Hello Time",                  "stp.hello",
        FT_DOUBLE,      BASE_NONE,      NULL,   0x0,
-       "" }},
+       "", HFILL }},
     { &hf_bpdu_forward_delay,
       { "Forward Delay",               "stp.forward",
        FT_DOUBLE,      BASE_NONE,      NULL,   0x0,
-       "" }},
+       "", HFILL }},
+    { &hf_bpdu_version_1_length,
+      { "Version 1 Length",            "stp.version_1_length",
+       FT_UINT8,       BASE_DEC,       NULL,   0x0,
+       "", HFILL }},
+    { &hf_bpdu_version_3_length,
+      { "MST Extension, Length",               "mstp.version_3_length",
+       FT_UINT16,      BASE_DEC,       NULL,   0x0,
+       "", HFILL }},
+    { &hf_bpdu_mst_config_format_selector,
+      { "MST Config ID format selector",               "mstp.config_format_selector",
+       FT_UINT8,       BASE_DEC,       NULL,   0x0,
+       "", HFILL }},
+    { &hf_bpdu_mst_config_name,
+      { "MST Config name",             "mstp.config_name",
+       FT_STRING,      BASE_DEC,       NULL,   0x0,
+       "", HFILL }},
+    { &hf_bpdu_mst_config_revision_level,
+      { "MST Config revision",         "mstp.config_revision_level",
+       FT_UINT16,      BASE_DEC,       NULL,   0x0,
+       "", HFILL }},
+    { &hf_bpdu_mst_config_digest,
+      { "MST Config digest",           "mstp.config_digest",
+       FT_BYTES,       BASE_DEC,       NULL,   0x0,
+       "", HFILL }},
+    { &hf_bpdu_cist_internal_root_path_cost,
+      { "CIST Internal Root Path Cost",                "mstp.cist_internal_root_path_cost",
+       FT_UINT32,      BASE_DEC,       NULL,   0x0,
+       "", HFILL }},
+    { &hf_bpdu_cist_bridge_identifier_mac,
+      { "CIST Bridge Identifier",              "mstp.cist_bridge.hw",
+       FT_ETHER,       BASE_DEC,       NULL,   0x0,
+       "", HFILL }},
+    { &hf_bpdu_cist_remaining_hops,
+      { "CIST Remaining hops",         "mstp.cist_remaining_hops",
+       FT_UINT8,       BASE_DEC,       NULL,   0x0,
+       "", HFILL }},
+    { &hf_bpdu_msti_flags,
+      { "MSTI flags",                  "mstp.msti.flags",
+       FT_UINT8,       BASE_HEX,       NULL,   0x0,
+       "", HFILL }},
+    { &hf_bpdu_msti_regional_root_mac,
+      { "Regional Root",               "mstp.msti.root.hw",
+       FT_ETHER,       BASE_DEC,       NULL,   0x0,
+       "", HFILL }},
+    { &hf_bpdu_msti_internal_root_path_cost,
+      { "Internal root path cost",             "mstp.msti.root_cost",
+       FT_UINT32,      BASE_DEC,       NULL,   0x0,
+       "", HFILL }},
+    { &hf_bpdu_msti_bridge_identifier_priority,
+      { "Bridge Identifier Priority",          "mstp.msti.bridge_priority",
+       FT_UINT8,       BASE_DEC,       NULL,   0x0,
+       "", HFILL }},
+    { &hf_bpdu_msti_port_identifier_priority,
+      { "Port identifier prority",             "mstp.msti.port_priority",
+       FT_UINT8,       BASE_DEC,       NULL,   0x0,
+       "", HFILL }},
+    { &hf_bpdu_msti_remaining_hops,
+      { "Remaining hops",              "mstp.msti.remaining_hops",
+       FT_UINT8,       BASE_DEC,       NULL,   0x0,
+       "", HFILL }},
+
   };
   static gint *ett[] = {
     &ett_bpdu,
+    &ett_bpdu_flags,
+    &ett_mstp,
+    &ett_msti
   };
 
   proto_bpdu = proto_register_protocol("Spanning Tree Protocol", "STP", "stp");
@@ -325,5 +753,21 @@ proto_register_bpdu(void)
 void
 proto_reg_handoff_bpdu(void)
 {
-  dissector_add("llc.dsap", SAP_BPDU, dissect_bpdu, proto_bpdu);
+  dissector_handle_t bpdu_handle;
+
+  /*
+   * Get handle for the GVRP dissector.
+   */
+  gvrp_handle = find_dissector("gvrp");
+
+  /*
+   * Get handle for the GMRP dissector.
+   */
+  gmrp_handle = find_dissector("gmrp");
+  data_handle = find_dissector("data");
+
+  bpdu_handle = find_dissector("bpdu");
+  dissector_add("llc.dsap", SAP_BPDU, bpdu_handle);
+  dissector_add("ppp.protocol", PPP_BPDU, bpdu_handle);
+  dissector_add("chdlctype", CHDLCTYPE_BPDU, bpdu_handle);
 }