/* packet-ecp-oui.c * ECP/VDP dissector for wireshark (according to IEEE 802.1Qbg draft 0) * By Jens Osterkamp * Mijo Safradin * Copyright 2011,2012 IBM Corp. * * Wireshark - Network traffic analyzer * By Gerald Combs * Copyright 1998 Gerald Combs * * SPDX-License-Identifier: GPL-2.0-or-later */ #include "config.h" #include #include #include #include #include #include "packet-ieee802a.h" void proto_register_ecp_oui(void); void proto_reg_handoff_ecp(void); #define ECP_SUBTYPE 0x00 #define END_OF_VDPDU_TLV_TYPE 0x00 /* Mandatory */ #define VDP_TLV_TYPE 0x02 #define ORG_SPECIFIC_TLV_TYPE 0x7F /* IEEE 802.1Qbg VDP filter info formats */ #define VDP_FIF_VID 0x01 #define VDP_FIF_MACVID 0x02 #define VDP_FIF_GROUPVID 0x03 #define VDP_FIF_GROUPVMACVID 0x04 /* Masks */ #define TLV_TYPE_MASK 0xFE00 #define TLV_TYPE(value) (((value) & TLV_TYPE_MASK) >> 9) #define TLV_INFO_LEN_MASK 0x01FF #define TLV_INFO_LEN(value) ((value) & TLV_INFO_LEN_MASK) static gint proto_ecp = -1; static gint hf_ecp_pid = -1; static gint hf_ecp_tlv_type = -1; static gint hf_ecp_tlv_len = -1; static gint hf_ecp_subtype = -1; static gint hf_ecp_mode = -1; static gint hf_ecp_sequence = -1; /* static gint hf_ecp_vdp_oui = -1; */ static gint hf_ecp_vdp_mode = -1; static gint hf_ecp_vdp_response = -1; static gint hf_ecp_vdp_mgrid = -1; static gint hf_ecp_vdp_vsitypeid = -1; static gint hf_ecp_vdp_vsitypeidversion = -1; static gint hf_ecp_vdp_instanceid = -1; static gint hf_ecp_vdp_format = -1; static gint hf_ecp_vdp_mac = -1; static gint hf_ecp_vdp_vlan = -1; static gint ett_ecp = -1; static gint ett_end_of_vdpdu = -1; static gint ett_802_1qbg_capabilities_flags = -1; static dissector_handle_t ecp_handle; static const value_string ecp_pid_vals[] = { { 0x0000, "ECP draft 0" }, { 0, NULL } }; /* IEEE 802.1Qbg ECP subtypes */ static const value_string ecp_subtypes[] = { { 0x00, "ECP default subtype" }, { 0, NULL } }; /* IEEE 802.1Qbg ECP modes */ static const value_string ecp_modes[] = { { 0x00, "REQUEST" }, { 0x01, "ACK" }, { 0, NULL } }; /* IEEE 802.1Qbg VDP modes */ static const value_string ecp_vdp_modes[] = { { 0x00, "Pre-Associate" }, { 0x01, "Pre-Associate with resource reservation" }, { 0x02, "Associate" }, { 0x03, "De-Associate" }, { 0, NULL } }; /* IEEE 802.1Qbg VDP responses */ static const value_string ecp_vdp_responses[] = { { 0x00, "success" }, { 0x01, "invalid format" }, { 0x02, "insufficient resources" }, { 0x03, "unused VTID" }, { 0x04, "VTID violation" }, { 0x05, "VTID version violation" }, { 0x06, "out of sync" }, { 0, NULL } }; /* IEEE 802.1Qbg VDP filter info formats */ static const value_string ecp_vdp_formats[] = { { VDP_FIF_VID, "VID values" }, { VDP_FIF_MACVID, "MAC/VID pairs" }, { VDP_FIF_GROUPVID, "GROUPID/VID pairs" }, { VDP_FIF_GROUPVMACVID, "GROUPID/MAC/VID triples" }, { 0, NULL } }; /* IEEE 802.1Qbg Subtypes */ static const value_string ieee_802_1qbg_subtypes[] = { { 0x00, "EVB" }, { 0x01, "CDCP" }, { 0x02, "VDP" }, { 0, NULL } }; /* Dissect Unknown TLV */ static gint32 dissect_ecp_unknown_tlv(tvbuff_t *tvb, packet_info *pinfo _U_, proto_tree *tree, guint32 offset) { guint16 tempLen; guint16 tempShort; proto_tree *ecp_unknown_tlv_tree; /* Get tlv type and length */ tempShort = tvb_get_ntohs(tvb, offset); /* Get tlv length */ tempLen = TLV_INFO_LEN(tempShort); ecp_unknown_tlv_tree = proto_tree_add_subtree(tree, tvb, offset, (tempLen + 2), ett_ecp, NULL, "Unknown TLV"); proto_tree_add_item(ecp_unknown_tlv_tree, hf_ecp_subtype, tvb, offset, 2, ENC_BIG_ENDIAN); return -1; } /* Dissect mac/vid pairs in VDP TLVs */ static gint32 dissect_vdp_fi_macvid(tvbuff_t *tvb, packet_info *pinfo _U_, proto_tree *tree, guint32 offset) { gint i; guint16 entries; guint32 tempOffset = offset; proto_tree *ecp_vdp_tlv_fi_subtree; entries = tvb_get_ntohs(tvb, offset); ecp_vdp_tlv_fi_subtree = proto_tree_add_subtree_format(tree, tvb, tempOffset, 2, ett_ecp, NULL, "%i MAC/VID pair%s", entries, plurality(entries, "", "s")); tempOffset += 2; for (i=0; i < entries; i++) { proto_tree_add_item(ecp_vdp_tlv_fi_subtree, hf_ecp_vdp_mac, tvb, tempOffset, 6, ENC_NA); tempOffset += 6; proto_tree_add_item(ecp_vdp_tlv_fi_subtree, hf_ecp_vdp_vlan, tvb, tempOffset, 2, ENC_BIG_ENDIAN); tempOffset += 2; } return tempOffset-offset; } /* Dissect Organizationally Defined TLVs */ static gint32 dissect_vdp_org_specific_tlv(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, guint32 offset) { guint16 tempLen; guint16 len; guint16 tempShort; guint32 tempOffset = offset; guint32 oui; const char *ouiStr; guint8 subType, format; const char *subTypeStr; proto_tree *ecp_vdp_tlv_subtree; tempLen = 0; tempShort = tvb_get_ntohs(tvb, offset); len = TLV_INFO_LEN(tempShort); tempOffset += 2; oui = tvb_get_ntoh24(tvb, (tempOffset)); /* Look in manuf database for OUI */ ouiStr = uint_get_manuf_name_if_known(oui); if(ouiStr==NULL) ouiStr="Unknown"; tempOffset += 3; subType = tvb_get_guint8(tvb, tempOffset); tempOffset++; switch(oui) { case OUI_IEEE_802_1QBG: subTypeStr = val_to_str(subType, ieee_802_1qbg_subtypes, "Unknown subtype 0x%x"); break; default: subTypeStr = "Unknown"; break; } ecp_vdp_tlv_subtree = proto_tree_add_subtree_format(tree, tvb, offset, (len + 2), ett_ecp, NULL, "%s - %s", ouiStr, subTypeStr); proto_tree_add_item(ecp_vdp_tlv_subtree, hf_ecp_vdp_mode, tvb, tempOffset, 1, ENC_BIG_ENDIAN); tempOffset++; proto_tree_add_item(ecp_vdp_tlv_subtree, hf_ecp_vdp_response, tvb, tempOffset, 1, ENC_BIG_ENDIAN); tempOffset++; proto_tree_add_item(ecp_vdp_tlv_subtree, hf_ecp_vdp_mgrid, tvb, tempOffset, 1, ENC_BIG_ENDIAN); tempOffset++; proto_tree_add_item(ecp_vdp_tlv_subtree, hf_ecp_vdp_vsitypeid, tvb, tempOffset, 3, ENC_BIG_ENDIAN); tempOffset += 3; proto_tree_add_item(ecp_vdp_tlv_subtree, hf_ecp_vdp_vsitypeidversion, tvb, tempOffset, 1, ENC_BIG_ENDIAN); tempOffset += 1; proto_tree_add_item(ecp_vdp_tlv_subtree, hf_ecp_vdp_instanceid, tvb, tempOffset, 16, ENC_NA); tempOffset += 16; format = tvb_get_guint8(tvb, tempOffset); proto_tree_add_item(ecp_vdp_tlv_subtree, hf_ecp_vdp_format, tvb, tempOffset, 1, ENC_BIG_ENDIAN); tempOffset++; switch (format) { case VDP_FIF_VID: /* place holder for future enablement */ /* For compatibility of different implementations proceed to next entry */ case VDP_FIF_MACVID: tempLen = dissect_vdp_fi_macvid(tvb, pinfo, ecp_vdp_tlv_subtree, tempOffset); break; case VDP_FIF_GROUPVID: /* place holder for future enablement */ break; case VDP_FIF_GROUPVMACVID: /* place holder for future enablement */ break; default: break; } tempOffset += tempLen; return tempOffset-offset; } /* Dissect End of VDP TLV (Mandatory) */ static gint32 dissect_vdp_end_of_vdpdu_tlv(tvbuff_t *tvb, packet_info *pinfo _U_, proto_tree *tree, guint32 offset) { guint16 tempLen; guint16 tempShort; proto_tree *end_of_vdpdu_tree; /* Get tlv type and length */ tempShort = tvb_get_ntohs(tvb, offset); /* Get tlv length */ tempLen = TLV_INFO_LEN(tempShort); if (tree) { /* Set port tree */ end_of_vdpdu_tree = proto_tree_add_subtree(tree, tvb, offset, (tempLen + 2), ett_end_of_vdpdu, NULL, "End of VDPDU"); proto_tree_add_item(end_of_vdpdu_tree, hf_ecp_tlv_type, tvb, offset, 2, ENC_BIG_ENDIAN); proto_tree_add_item(end_of_vdpdu_tree, hf_ecp_tlv_len, tvb, offset, 2, ENC_BIG_ENDIAN); } return -1; /* Force the VDP dissector to terminate */ } static int dissect_ecp(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void* data _U_) { proto_tree *ecp_tree; proto_item *ti; gint32 tempLen = 0; guint32 offset = 0; guint16 tempShort; guint8 tempType; gboolean end = FALSE; col_set_str(pinfo->cinfo, COL_PROTOCOL, "ECP"); ti = proto_tree_add_item(tree, proto_ecp, tvb, 0, -1, ENC_NA); ecp_tree = proto_item_add_subtree(ti, ett_ecp); proto_tree_add_item(ecp_tree, hf_ecp_subtype, tvb, offset, 1, ENC_BIG_ENDIAN); proto_tree_add_item(ecp_tree, hf_ecp_mode, tvb, offset+1, 1, ENC_BIG_ENDIAN); proto_tree_add_item(ecp_tree, hf_ecp_sequence, tvb, offset+2, 2, ENC_BIG_ENDIAN); offset += 4; while (!end) { if (!tvb_bytes_exist(tvb, offset, 1)) break; tempShort = tvb_get_ntohs(tvb, offset); tempType = TLV_TYPE(tempShort); switch (tempType) { case ORG_SPECIFIC_TLV_TYPE: tempLen = dissect_vdp_org_specific_tlv(tvb, pinfo, ecp_tree, offset); break; case END_OF_VDPDU_TLV_TYPE: tempLen = dissect_vdp_end_of_vdpdu_tlv(tvb, pinfo, ecp_tree, offset); break; default: tempLen = dissect_ecp_unknown_tlv(tvb, pinfo, ecp_tree, offset); break; } offset += tempLen; if (tempLen < 0) end = TRUE; } return tvb_captured_length(tvb); } void proto_register_ecp_oui(void) { static hf_register_info hf_reg = { &hf_ecp_pid, { "PID", "ieee802a.ecp_pid", FT_UINT16, BASE_HEX, VALS(ecp_pid_vals), 0x0, NULL, HFILL }, }; static hf_register_info hf[] = { { &hf_ecp_tlv_type, { "TLV Type", "ecp.tlv.type", FT_UINT16, BASE_DEC, NULL, TLV_TYPE_MASK, NULL, HFILL } }, { &hf_ecp_tlv_len, { "TLV Length", "ecp.tlv.len", FT_UINT16, BASE_DEC, NULL, TLV_INFO_LEN_MASK, NULL, HFILL } }, { &hf_ecp_subtype, { "subtype", "ecp.subtype", FT_UINT8, BASE_HEX, VALS(ecp_subtypes), 0x0, NULL, HFILL }, }, { &hf_ecp_mode, { "mode", "ecp.mode", FT_UINT8, BASE_HEX, VALS(ecp_modes), 0x0, NULL, HFILL }, }, { &hf_ecp_sequence, { "sequence number", "ecp.seq", FT_UINT16, BASE_HEX, NULL, 0x0, NULL, HFILL }, }, #if 0 { &hf_ecp_vdp_oui, { "Organization Unique Code", "ecp.vdp.oui", FT_UINT24, BASE_OUI, NULL, 0x0, NULL, HFILL } }, #endif { &hf_ecp_vdp_mode, { "mode", "ecp.vdp.mode", FT_UINT8, BASE_HEX, VALS(ecp_vdp_modes), 0x0, NULL, HFILL }, }, { &hf_ecp_vdp_response, { "response", "ecp.vdp.response", FT_UINT8, BASE_HEX, VALS(ecp_vdp_responses), 0x0, NULL, HFILL }, }, { &hf_ecp_vdp_mgrid, { "Manager ID", "ecp.vdp.mgrid", FT_UINT8, BASE_HEX, NULL, 0x0, NULL, HFILL }, }, { &hf_ecp_vdp_vsitypeid, { "VSI type ID", "ecp.vdp.vsitypeid", FT_UINT24, BASE_HEX, NULL, 0x0, NULL, HFILL }, }, { &hf_ecp_vdp_vsitypeidversion, { "VSI type ID version", "ecp.vdp.vsitypeidversion", FT_UINT8, BASE_HEX, NULL, 0x0, NULL, HFILL }, }, { &hf_ecp_vdp_instanceid, { "VSI Instance ID version", "ecp.vdp.instanceid", FT_BYTES, BASE_NONE, NULL, 0x0, NULL, HFILL }, }, { &hf_ecp_vdp_format, { "VSI filter info format", "ecp.vdp.format", FT_UINT8, BASE_HEX, VALS(ecp_vdp_formats), 0x0, NULL, HFILL }, }, { &hf_ecp_vdp_mac, { "VSI Mac Address", "ecp.vdp.mac", FT_ETHER, BASE_NONE, NULL, 0x0, NULL, HFILL } }, { &hf_ecp_vdp_vlan, { "VSI VLAN ID", "ecp.vdp.vlan", FT_UINT16, BASE_DEC, NULL, 0x0, NULL, HFILL } }, }; static gint *ett[] = { &ett_ecp, &ett_end_of_vdpdu, &ett_802_1qbg_capabilities_flags, }; proto_ecp = proto_register_protocol("ECP Protocol", "ECP", "ecp"); proto_register_field_array(proto_ecp, hf, array_length(hf)); proto_register_subtree_array(ett, array_length(ett)); ieee802a_add_oui(OUI_IEEE_802_1QBG, "ieee802a.ecp_pid", "IEEE802a ECP PID", &hf_reg, proto_ecp); ecp_handle = register_dissector("ecp", dissect_ecp, proto_ecp); } void proto_reg_handoff_ecp(void) { dissector_add_uint("ieee802a.ecp_pid", 0x0000, ecp_handle); } /* * Editor modelines - http://www.wireshark.org/tools/modelines.html * * Local variables: * c-basic-offset: 8 * tab-width: 8 * indent-tabs-mode: t * End: * * vi: set shiftwidth=8 tabstop=8 noexpandtab: * :indentSize=8:tabSize=8:noTabs=false: */