Ethereal-dev: [Ethereal-dev] GVRP dissector for Ethereal

Note: This archive is from the project's previous web site, ethereal.com. This list is no longer active.

Date: Thu, 30 Nov 100 09:44:16 +0800 (CST)
Dear Sirs,

Here is my codes for GVRP dissector. Since I did not have CVS nor Cygwin
installed on my computer, I could only submit the whole modified file and
the files I created in this email. Following are the codes.

Kevin Shi

=================================================================================

[Beginning of the modified version of "packet-bpdu.c"]
-------------------------------------------------
/* packet-bpdu.c
 * Routines for BPDU (Spanning Tree Protocol) disassembly
 *
 * $Id: packet-bpdu.c,v 1.15 2000/11/19 08:53:55 guy Exp $
 *
 * Copyright 1999 Christophe Tronche <ch.tronche@xxxxxxxxxxxx>
 * 
 * Ethereal - Network traffic analyzer
 * By Gerald Combs <gerald@xxxxxxxx>
 * 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.
 */

#ifdef HAVE_CONFIG_H
# 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 "llcsaps.h"
#include "resolv.h"

/* Include this for GVRP dissector */
#include "packet-gvrp.h"

/* Offsets of fields within a BPDU */

#define BPDU_IDENTIFIER          0
#define BPDU_VERSION_IDENTIFIER  2
#define BPDU_TYPE                3
#define BPDU_FLAGS               4
#define BPDU_ROOT_IDENTIFIER     5
#define BPDU_ROOT_PATH_COST     13
#define BPDU_BRIDGE_IDENTIFIER  17
#define BPDU_PORT_IDENTIFIER    25
#define BPDU_MESSAGE_AGE        27
#define BPDU_MAX_AGE            29
#define BPDU_HELLO_TIME         31
#define BPDU_FORWARD_DELAY      33

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_root_mac = -1;
static int hf_bpdu_root_cost = -1;
static int hf_bpdu_bridge_mac = -1;
static int hf_bpdu_port_id = -1;
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 gint ett_bpdu = -1;

static void
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;
      gchar   *root_identifier_mac_str;
      guint32 root_path_cost;
      guint16 bridge_identifier_bridge_priority;
      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;

      CHECK_DISPLAY_AS_DATA(proto_bpdu, tvb, pinfo, tree);

      /* GARP application frames require special interpretation of the
destination
         address field; otherwise, they will be mistaken as 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. */
      if (	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)) {

	    protocol_identifier = tvb_get_ntohs(tvb, BPDU_IDENTIFIER);

		/* Future expansion for GMRP */
		if (pinfo->dl_dst.data[5] == 0x20) {
		}

		/* for GVRP */
		if (pinfo->dl_dst.data[5] == 0x21) {
			dissect_gvrp(tvb, pinfo, tree);
			return;
		}

		pinfo->current_proto = "GARP";

        if (check_col(pinfo->fd, COL_PROTOCOL)) {
	        col_set_str(pinfo->fd, COL_PROTOCOL, "GARP"); /* Generic
Attribute Registration Protocol */
        }

        if (check_col(pinfo->fd, COL_INFO)) {
	        col_set_str(pinfo->fd, COL_PROTOCOL, "Unknown GARP
application");
        }

		return;
      }

      pinfo->current_proto = "STP";

      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 */
      }

      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", 
			       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");
      }

      if (tree) {
	    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, 
				protocol_version_identifier);
	    if (protocol_version_identifier != 0)
		  proto_tree_add_text(bpdu_tree, tvb,
BPDU_VERSION_IDENTIFIER, 1, "   (Warning: this version of packet-bpdu 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);
	      return;
	    }

	    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, 
				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");

	    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, 
				root_identifier_mac_str);
	    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, 
				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, 
				port_identifier);
	    proto_tree_add_double(bpdu_tree, hf_bpdu_msg_age, tvb,
				BPDU_MESSAGE_AGE, 2, 
				message_age);
	    proto_tree_add_double(bpdu_tree, hf_bpdu_max_age, tvb,
				BPDU_MAX_AGE, 2, 
				max_age);
	    proto_tree_add_double(bpdu_tree, hf_bpdu_hello_time, tvb,
				BPDU_HELLO_TIME, 2, 
				hello_time);
	    proto_tree_add_double(bpdu_tree, hf_bpdu_forward_delay, tvb,
				BPDU_FORWARD_DELAY, 2, 
				forward_delay);
      }
}

void
proto_register_bpdu(void)
{

  static hf_register_info hf[] = {
    { &hf_bpdu_proto_id,
      { "Protocol Identifier",		"stp.protocol",
	FT_UINT16,	BASE_HEX,	NULL,	0x0,
      	"" }},
    { &hf_bpdu_version_id,
      { "Protocol Version Identifier",	"stp.version",
	FT_UINT8,	BASE_DEC,	NULL,	0x0,
      	"" }},
    { &hf_bpdu_type,
      { "BPDU type",			"stp.type",
	FT_UINT8,	BASE_HEX,	NULL,	0x0,
      	"" }},
    { &hf_bpdu_flags,
      { "BPDU flags",			"stp.flags",
	FT_UINT8,	BASE_HEX,	NULL,	0x0,
      	"" }},
    { &hf_bpdu_root_mac,
      { "Root Identifier",		"stp.root.hw",
	FT_ETHER,	BASE_NONE,	NULL,	0x0,
      	"" }},
    { &hf_bpdu_root_cost,
      { "Root Path Cost",		"stp.root.cost",
	FT_UINT32,	BASE_DEC,	NULL,	0x0,
      	"" }},
    { &hf_bpdu_bridge_mac,
      { "Bridge Identifier",		"stp.bridge.hw",
	FT_ETHER,	BASE_NONE,	NULL,	0x0,
      	""}},
    { &hf_bpdu_port_id,
      { "Port identifier",		"stp.port",
	FT_UINT16,	BASE_HEX,	NULL,	0x0,
      	""}},
    { &hf_bpdu_msg_age,
      { "Message Age",			"stp.msg_age",
	FT_DOUBLE,	BASE_NONE,	NULL,	0x0,
      	"" }},
    { &hf_bpdu_max_age,
      { "Max Age",			"stp.max_age",
	FT_DOUBLE,	BASE_NONE,	NULL,	0x0,
      	"" }},
    { &hf_bpdu_hello_time,
      { "Hello Time",			"stp.hello",
	FT_DOUBLE,	BASE_NONE,	NULL,	0x0,
      	"" }},
    { &hf_bpdu_forward_delay,
      { "Forward Delay",		"stp.forward",
	FT_DOUBLE,	BASE_NONE,	NULL,	0x0,
      	"" }},
  };
  static gint *ett[] = {
    &ett_bpdu,
  };

  proto_bpdu = proto_register_protocol("Spanning Tree Protocol", "stp");
  proto_register_field_array(proto_bpdu, hf, array_length(hf));
  proto_register_subtree_array(ett, array_length(ett));

  register_dissector("bpdu", dissect_bpdu);
}

void
proto_reg_handoff_bpdu(void)
{
  dissector_add("llc.dsap", SAP_BPDU, dissect_bpdu);
}
-------------------------------------------------
[End of the modified version of "packet-bpdu.c"]

=================================================================================

[Beginning of "packet-gvrp.h"]
-------------------------------------------------
/* packet-gvrp.c
 * Routines for GVRP (GARP VLAN Registration Protocol) dissection
 * Copyright 2000, Kevin Shi <techishi@xxxxxxxxxxxxxx>
 *
 * $Id: README.developer,v 1.21 2000/11/06 09:56:10 guy Exp $
 *
 * Ethereal - Network traffic analyzer
 * By Gerald Combs <gerald@xxxxxxxxxx>
 * 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.
 */

#ifndef __PACKET_GVRP_H__
#define __PACKET_GVRP_H__

void dissect_gvrp(tvbuff_t *, packet_info *, proto_tree *);

#endif /* packet-atm.h */
-------------------------------------------------
[End of "packet-gvrp.h"]

=================================================================================

[Beginning of "packet-gvrp.c"]
-------------------------------------------------
/* packet-gvrp.c
 * Routines for GVRP (GARP VLAN Registration Protocol) dissection
 * Copyright 2000, Kevin Shi <techishi@xxxxxxxxxxxxxx>
 *
 * $Id: README.developer,v 1.21 2000/11/06 09:56:10 guy Exp $
 *
 * Ethereal - Network traffic analyzer
 * By Gerald Combs <gerald@xxxxxxxxxx>
 * 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.
 */

#ifdef HAVE_CONFIG_H
# include "config.h"
#endif

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#ifdef HAVE_SYS_TYPES_H
# include <sys/types.h>
#endif

#ifdef HAVE_NETINET_IN_H
# include <netinet/in.h>
#endif

#include <glib.h>

#ifdef NEED_SNPRINTF_H
# include "snprintf.h"
#endif

#include "packet.h"
#include "llcsaps.h"

/* Initialize the protocol and registered fields */
static int proto_gvrp = -1;
static int hf_gvrp_proto_id = -1;
static int hf_gvrp_attribute_type = -1;
static int hf_gvrp_attribute_length = -1;
static int hf_gvrp_attribute_event = -1;
static int hf_gvrp_attribute_value = -1;
static int hf_gvrp_end_of_mark = -1;

/* Initialize the subtree pointers */
static gint ett_gvrp = -1;
static gint ett_gvrp_message = -1;
static gint ett_gvrp_attribute_list = -1;
static gint ett_gvrp_attribute = -1;

/* Constant definitions */
#define GARP_DEFAULT_PROTOCOL_ID        0x0001
#define GARP_END_OF_MARK                0x00
#define GVRP_ATTRIBUTE_TYPE             0x01

/* The length of GVRP LeaveAll attribute should be 2 octets (one for length
and the other for event) */
#define GVRP_LENGTH_LEAVEALL            (sizeof(guint8)+sizeof(guint8))

/* The length of GVRP attribute other than LeaveAll should be 4 octets (one
for length, one for event, 
 * and the last two for VID value).
 */
#define GVRP_LENGTH_NON_LEAVEALL
(sizeof(guint8)+sizeof(guint8)+sizeof(guint16))

/* Packet offset definitions */
#define GARP_PROTOCOL_ID                0

/* Event definitions */
#define GVRP_EVENT_LEAVEALL             0
#define GVRP_EVENT_JOINEMPTY            1
#define GVRP_EVENT_JOININ               2
#define GVRP_EVENT_LEAVEEMPTY           3
#define GVRP_EVENT_LEAVEIN              4
#define GVRP_EVENT_EMPTY                5

static char  gvrp_event_LeaveAll[]   = "Leave All";
static char  gvrp_event_JoinIn[]     = "Join In";
static char  gvrp_event_JoinEmpty[]  = "Join Empty";
static char  gvrp_event_LeaveIn[]    = "Leave In";
static char  gvrp_event_LeaveEmpty[] = "Leave Empty";
static char  gvrp_event_Empty[]      = "Empty";
static char  gvrp_event_unknown[]    = "Unknown Event";

/* Code to actually dissect the packets */
void
dissect_gvrp(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree)
{
    proto_item   *ti;
    proto_tree   *gvrp_tree;
    guint16       protocol_id;
    guint8        octet;
    int           msg_index, attr_index, offset = 0, length =
tvb_reported_length(tvb);

    CHECK_DISPLAY_AS_DATA(proto_gvrp, tvb, pinfo, tree);

    pinfo->current_proto = "GVRP";
    
    if (check_col(pinfo->fd, COL_PROTOCOL)) 
        col_add_str(pinfo->fd, COL_PROTOCOL, "GVRP");
    
    if (check_col(pinfo->fd, COL_INFO)) 
        col_add_str(pinfo->fd, COL_INFO, "GVRP");

    if (tree)
    {
        ti = proto_tree_add_item(tree, proto_gvrp, tvb, 0, length, FALSE);

        gvrp_tree = proto_item_add_subtree(ti, ett_gvrp);

        /* Read in GARP protocol ID */
        protocol_id = tvb_get_ntohs(tvb, GARP_PROTOCOL_ID);
    
        proto_tree_add_uint_format(gvrp_tree, hf_gvrp_proto_id, tvb,
                                   GARP_PROTOCOL_ID, sizeof(guint16), 
                                   protocol_id,
                                   "Protocol Identifier: 0x%04x (%s)", 
                                   protocol_id,
                                   protocol_id == GARP_DEFAULT_PROTOCOL_ID ? 
                                   "GARP VLAN Registration Protocol" :
"Unknown Protocol");

        /* Currently only one protocol ID is supported */
        if (protocol_id != GARP_DEFAULT_PROTOCOL_ID)
        {
            proto_tree_add_text(gvrp_tree, tvb, GARP_PROTOCOL_ID,
sizeof(guint16), 
                                "   (Warning: this version of packet-gvrp
only knows about protocol id = 1)");
            dissect_data(tvb, GARP_PROTOCOL_ID + sizeof(guint16), pinfo,
tree);
            return;
        }

        offset += sizeof(guint16);
        length -= sizeof(guint16);

        msg_index = 0;

        /* Begin to parse GARP messages */
        while (length)
        {
            proto_item   *msg_item;
            int           msg_start = offset;

            /* Read in attribute type. */
            octet = tvb_get_guint8(tvb, offset);

            /* Check for end of mark */
            if (octet == GARP_END_OF_MARK)
            {
                /* End of GARP PDU */
                if (msg_index)
                {
                    proto_tree_add_text(gvrp_tree, tvb, offset,
sizeof(guint8), "End of mark");
                    break;
                }
                else
                {
                    dissect_data(tvb, offset, pinfo, tree);
                    return;
                }
            }

            offset += sizeof(guint8);
            length -= sizeof(guint8);

            /* GVRP only support one attribute type. */
            if (octet != GVRP_ATTRIBUTE_TYPE)
            {
                dissect_data(tvb, offset, pinfo, tree);
                return;
            }

            msg_item = proto_tree_add_text(gvrp_tree, tvb, msg_start, 0,
"Message %d", msg_index + 1);

            proto_tree_add_text(gvrp_tree, tvb, msg_start, sizeof(guint8), 
                                " Attribute Type: %d (%s)", octet,
                                (octet == GVRP_ATTRIBUTE_TYPE) ? "VID
Attribute Type" : "Unknown Attribute Type");

            attr_index = 0;

            while (length)
            {
                int           attr_start = offset;
                proto_item   *attr_item;

                /* Read in attribute length. */
                octet = tvb_get_guint8(tvb, offset);

                /* Check for end of mark */
                if (octet == GARP_END_OF_MARK)
                {
                    /* If at least one message has been already read, 
                     * check for another end of mark.
                     */
                    if (attr_index)
                    {
                        proto_tree_add_text(gvrp_tree, tvb, offset,
sizeof(guint8), "  End of mark");

                        offset += sizeof(guint8);
                        length -= sizeof(guint8);

                        proto_item_set_len(msg_item, offset - msg_start);
                        break;
                    }
                    else
                    {
                        dissect_data(tvb, offset, pinfo, tree);
                        return;
                    }
                }
                else
                {
                    guint8   event;
                    guint16  value;
                    char    *string;

                    offset += sizeof(guint8);
                    length -= sizeof(guint8);

                    attr_item = proto_tree_add_text(gvrp_tree, tvb,
attr_start, 0, "  Attribute %d", attr_index + 1);

                    proto_tree_add_text(gvrp_tree, tvb, attr_start,
sizeof(guint8), "   Length: %d", octet);

                    /* Read in attribute event */
                    event = tvb_get_guint8(tvb, offset);

                    if (((event == GVRP_EVENT_LEAVEALL) && (octet !=
GVRP_LENGTH_LEAVEALL)) ||
                        ((event != GVRP_EVENT_LEAVEALL) && (octet !=
GVRP_LENGTH_NON_LEAVEALL)))
                    {
                        dissect_data(tvb, offset, pinfo, tree);
                        return;
                    }

                    switch (event)
                    {
                        case GVRP_EVENT_LEAVEALL:
                            string = gvrp_event_LeaveAll;
                            break;

                        case GVRP_EVENT_JOINEMPTY:
                            string = gvrp_event_JoinEmpty;
                            break;

                        case GVRP_EVENT_JOININ:
                            string = gvrp_event_JoinIn;
                            break;

                        case GVRP_EVENT_LEAVEEMPTY:
                            string = gvrp_event_LeaveEmpty;
                            break;

                        case GVRP_EVENT_LEAVEIN:
                            string = gvrp_event_LeaveIn;
                            break;

                        case GVRP_EVENT_EMPTY:
                            string = gvrp_event_Empty;
                            break;

                        default: /* Unknown attribute event */
                            string = gvrp_event_unknown;
                            break;
                    }

                    proto_tree_add_text(gvrp_tree, tvb, offset,
sizeof(guint8), "   Event:  %d (%s)", event, string);

                    offset += sizeof(guint8);
                    length -= sizeof(guint8);

                    if (string == gvrp_event_unknown)
                    {
                        dissect_data(tvb, offset, pinfo, tree);
                        return;
                    }

                    if (string != gvrp_event_LeaveAll)
                    {
                        /* Read in attribute value */
                        value = tvb_get_ntohs(tvb, offset);

                        proto_tree_add_text(gvrp_tree, tvb, offset,
sizeof(guint16), "   Value:  %d", value);

                        offset += sizeof(guint16);
                        length -= sizeof(guint16);
                    }
                }

                proto_item_set_len(attr_item, offset - attr_start);

                attr_index++;
            }

            msg_index++;
        }
    }
}


/* Register the protocol with Ethereal */
void
proto_register_gvrp(void)
{                 
    static hf_register_info hf[] = {
        { &hf_gvrp_proto_id,
            { "GARP Protocol ID",          "garp.protocol_id",
            FT_UINT16,      BASE_HEX,      NULL,  0x0,
            "" }
        }
    };

    static gint *ett[] = {
        &ett_gvrp
    };

    /* Register the protocol name and description for GVRP */
    proto_gvrp = proto_register_protocol("GARP VLAN Registration Protocol",
"GVRP");

    /* Required function calls to register the header fields and subtrees
used by GVRP */
    proto_register_field_array(proto_gvrp, hf, array_length(hf));
    proto_register_subtree_array(ett, array_length(ett));
}

-------------------------------------------------
[End of "packet-gvrp.c"]