/* packet-gmrp.c * Routines for GMRP (GARP Multicast Registration Protocol) dissection * Copyright 2001, Markus Seehofer * * Based on the code from packet-gvrp.c (GVRP) from * Kevin Shi Copyright 2000 * * $Id: packet-gmrp.c,v 1.10 2002/08/28 21:00:13 jmayer Exp $ * * Ethereal - Network traffic analyzer * By Gerald Combs * 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 #include #include #include #include #include "llcsaps.h" /* Initialize the protocol and registered fields */ static int proto_gmrp = -1; static int hf_gmrp_proto_id = -1; static int hf_gmrp_attribute_type = -1; static int hf_gmrp_attribute_length = -1; static int hf_gmrp_attribute_event = -1; static int hf_gmrp_attribute_value_group_membership = -1; static int hf_gmrp_attribute_value_service_requirement = -1; /*static int hf_gmrp_end_of_mark = -1;*/ /* Initialize the subtree pointers */ static gint ett_gmrp = -1; /*static gint ett_gmrp_message = -1; static gint ett_gmrp_attribute_list = -1; static gint ett_gmrp_attribute = -1;*/ static dissector_handle_t data_handle; /* Constant definitions */ #define GARP_DEFAULT_PROTOCOL_ID 0x0001 #define GARP_END_OF_MARK 0x00 #define GMRP_ATTRIBUTE_TYPE_GROUP_MEMBERSHIP 0x01 #define GMRP_ATTRIBUTE_TYPE_SERVICE_REQUIREMENT 0x02 #define GMRP_SERVICE_REQUIREMENT_FORWARD_ALL 0x00 #define GMRP_SERVICE_REQUIREMENT_FORWARD_ALL_UNREGISTERED 0x01 static const value_string attribute_type_vals[] = { { GMRP_ATTRIBUTE_TYPE_GROUP_MEMBERSHIP ,"Group Membership" }, { GMRP_ATTRIBUTE_TYPE_SERVICE_REQUIREMENT ,"Service Requirement" }, { 0, NULL } }; /* The length of GMRP LeaveAll attribute should be 2 octets (one for length * and the other for event) */ #define GMRP_LENGTH_LEAVEALL (sizeof(guint8)+sizeof(guint8)) /* The length of GMRP attribute other than LeaveAll should be: * * 8 bytes for Group Membership (1 for length, 1 for event and 6 for mac address to register) * or * 3 bytes for Service Requirement (1 for length, 1 for event, 1 for attribute value) * */ #define GMRP_GROUP_MEMBERSHIP_NON_LEAVEALL (sizeof(guint8)+sizeof(guint8)+(6*sizeof(guint8))) #define GMRP_SERVICE_REQUIREMENT_NON_LEAVEALL (sizeof(guint8)+sizeof(guint8)+sizeof(guint8)) /* Packet offset definitions */ #define GARP_PROTOCOL_ID 0 /* Event definitions */ #define GMRP_EVENT_LEAVEALL 0 #define GMRP_EVENT_JOINEMPTY 1 #define GMRP_EVENT_JOININ 2 #define GMRP_EVENT_LEAVEEMPTY 3 #define GMRP_EVENT_LEAVEIN 4 #define GMRP_EVENT_EMPTY 5 static const value_string event_vals[] = { { GMRP_EVENT_LEAVEALL, "Leave All" }, { GMRP_EVENT_JOINEMPTY, "Join Empty" }, { GMRP_EVENT_JOININ, "Join In" }, { GMRP_EVENT_LEAVEEMPTY, "Leave Empty" }, { GMRP_EVENT_LEAVEIN, "Leave In" }, { GMRP_EVENT_EMPTY, "Empty" }, { 0, NULL } }; /* Code to actually dissect the packets */ static void dissect_gmrp(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree) { proto_item *ti; proto_tree *gmrp_tree; guint16 protocol_id; guint8 octet; guint8 attribute_type; int msg_index, attr_index, offset = 0, length = tvb_reported_length(tvb); if (check_col(pinfo->cinfo, COL_PROTOCOL)) col_set_str(pinfo->cinfo, COL_PROTOCOL, "GMRP"); if (check_col(pinfo->cinfo, COL_INFO)) col_set_str(pinfo->cinfo, COL_INFO, "GMRP"); if (tree) { ti = proto_tree_add_item(tree, proto_gmrp, tvb, 0, length, FALSE); gmrp_tree = proto_item_add_subtree(ti, ett_gmrp); /* Read in GARP protocol ID */ protocol_id = tvb_get_ntohs(tvb, GARP_PROTOCOL_ID); proto_tree_add_uint_format(gmrp_tree, hf_gmrp_proto_id, tvb, GARP_PROTOCOL_ID, sizeof(guint16), protocol_id, "Protocol Identifier: 0x%04x (%s)", protocol_id, protocol_id == GARP_DEFAULT_PROTOCOL_ID ? "GARP Multicast Registration Protocol" : "Unknown Protocol"); /* Currently only one protocol ID is supported */ if (protocol_id != GARP_DEFAULT_PROTOCOL_ID) { proto_tree_add_text(gmrp_tree, tvb, GARP_PROTOCOL_ID, sizeof(guint16), " (Warning: this version of Ethereal only knows about protocol id = 1)"); call_dissector(data_handle, tvb_new_subset(tvb, GARP_PROTOCOL_ID + sizeof(guint16), -1, -1), 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. */ 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(gmrp_tree, tvb, offset, sizeof(guint8), "End of pdu"); break; } else { call_dissector(data_handle, tvb_new_subset(tvb, offset, -1, -1), pinfo, tree); return; } } offset += sizeof(guint8); length -= sizeof(guint8); msg_item = proto_tree_add_text(gmrp_tree, tvb, msg_start, -1, "Message %d", msg_index + 1); proto_tree_add_uint(gmrp_tree, hf_gmrp_attribute_type, tvb, msg_start, sizeof(guint8), octet); /* GMRP supports Group Membership and Service Requirement as attribute types */ if ( (octet != GMRP_ATTRIBUTE_TYPE_GROUP_MEMBERSHIP) && (octet != GMRP_ATTRIBUTE_TYPE_SERVICE_REQUIREMENT) ) { call_dissector(data_handle, tvb_new_subset(tvb, offset, -1, -1), pinfo, tree); return; } 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(gmrp_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 { call_dissector(data_handle, tvb_new_subset(tvb, offset, -1, -1), pinfo, tree); return; } } else { guint8 event; offset += sizeof(guint8); length -= sizeof(guint8); attr_item = proto_tree_add_text(gmrp_tree, tvb, attr_start, -1, " Attribute %d", attr_index + 1); proto_tree_add_uint(gmrp_tree, hf_gmrp_attribute_length, tvb, attr_start, sizeof(guint8), octet); /* Read in attribute event */ event = tvb_get_guint8(tvb, offset); proto_tree_add_uint(gmrp_tree, hf_gmrp_attribute_event, tvb, offset, sizeof(guint8), event); offset += sizeof(guint8); length -= sizeof(guint8); switch (event) { case GMRP_EVENT_LEAVEALL: if (octet != GMRP_LENGTH_LEAVEALL) { call_dissector(data_handle, tvb_new_subset(tvb, offset, -1, -1), pinfo, tree); return; } break; case GMRP_EVENT_JOINEMPTY: case GMRP_EVENT_JOININ: case GMRP_EVENT_LEAVEEMPTY: case GMRP_EVENT_LEAVEIN: case GMRP_EVENT_EMPTY: if ( (octet != GMRP_GROUP_MEMBERSHIP_NON_LEAVEALL) && (octet != GMRP_SERVICE_REQUIREMENT_NON_LEAVEALL) ) { call_dissector(data_handle, tvb_new_subset(tvb, offset, -1, -1), pinfo, tree); return; } /* Show attribute value */ if ( GMRP_ATTRIBUTE_TYPE_GROUP_MEMBERSHIP == attribute_type ) { /* Group Membership */ proto_tree_add_item(gmrp_tree, hf_gmrp_attribute_value_group_membership, tvb, offset, (6*sizeof(guint8)), FALSE); offset += 6*sizeof(guint8); length -= 6*sizeof(guint8); } else if ( GMRP_ATTRIBUTE_TYPE_SERVICE_REQUIREMENT == attribute_type ) { /* Service Requirement */ proto_tree_add_item(gmrp_tree, hf_gmrp_attribute_value_service_requirement, tvb, offset, sizeof(guint8), FALSE); offset += sizeof(guint8); length -= sizeof(guint8); } else { call_dissector(data_handle, tvb_new_subset(tvb, offset, -1, -1), pinfo, tree); return; } break; default: call_dissector(data_handle, tvb_new_subset(tvb, offset, -1, -1), pinfo, tree); return; } } proto_item_set_len(attr_item, offset - attr_start); attr_index++; } msg_index++; } } } /* Register the protocol with Ethereal */ void proto_register_gmrp(void) { static hf_register_info hf[] = { { &hf_gmrp_proto_id, { "Protocol ID", "garp.protocol_id", FT_UINT16, BASE_HEX, NULL, 0x0, "" , HFILL } }, { &hf_gmrp_attribute_type, { "Type", "garp.attribute_type", FT_UINT8, BASE_HEX, VALS(attribute_type_vals), 0x0, "" , HFILL } }, { &hf_gmrp_attribute_length, { "Length", "garp.attribute_length", FT_UINT8, BASE_DEC, NULL, 0x0, "" , HFILL } }, { &hf_gmrp_attribute_event, { "Event", "garp.attribute_event", FT_UINT8, BASE_DEC, VALS(event_vals), 0x0, "" , HFILL } }, { &hf_gmrp_attribute_value_group_membership, { "Value", "garp.attribute_value_group_membership", FT_ETHER, BASE_HEX, NULL, 0x0, "" , HFILL } }, { &hf_gmrp_attribute_value_service_requirement, { "Value", "garp.attribute_value_service_requirement", FT_UINT8, BASE_HEX, NULL, 0x0, "" , HFILL } } }; static gint *ett[] = { &ett_gmrp }; /* Register the protocol name and description for GMRP */ proto_gmrp = proto_register_protocol("GARP Multicast Registration Protocol", "GMRP", "gmrp"); /* Required function calls to register the header fields and subtrees * used by GMRP */ proto_register_field_array(proto_gmrp, hf, array_length(hf)); proto_register_subtree_array(ett, array_length(ett)); register_dissector("gmrp", dissect_gmrp, proto_gmrp); } void proto_reg_handoff_gmrp(void){ data_handle = find_dissector("data"); }