2005-09-14 19:06:54 +00:00
|
|
|
/* packet-lldp.c
|
|
|
|
* Routines for LLDP dissection
|
|
|
|
* By Juan Gonzalez <juan.gonzalez@pikatech.com>
|
|
|
|
* Copyright 2005 MITEL
|
|
|
|
*
|
|
|
|
* July 2005
|
|
|
|
* Modified by: Brian Bogora <brian_bogora@mitel.com>
|
|
|
|
*
|
|
|
|
* Ethereal - Network traffic analyzer
|
|
|
|
* 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.
|
|
|
|
*/
|
|
|
|
|
|
|
|
#ifdef HAVE_CONFIG_H
|
|
|
|
# include "config.h"
|
|
|
|
#endif
|
|
|
|
|
|
|
|
#include <stdio.h>
|
|
|
|
#include <stdlib.h>
|
|
|
|
#include <string.h>
|
|
|
|
|
|
|
|
#include <gmodule.h>
|
|
|
|
#include <glib.h>
|
|
|
|
|
|
|
|
#include <epan/packet.h>
|
|
|
|
#include <epan/prefs.h>
|
|
|
|
|
|
|
|
/* Ethernet Type */
|
|
|
|
#define ETHERNET_TYPE_LLDP 0x88CC
|
|
|
|
|
|
|
|
/* TLV Types */
|
|
|
|
#define END_OF_LLDPDU_TLV_TYPE 0x00 /* Mandatory */
|
|
|
|
#define CHASSIS_ID_TLV_TYPE 0x01 /* Mandatory */
|
|
|
|
#define PORT_ID_TLV_TYPE 0x02 /* Mandatory */
|
|
|
|
#define TIME_TO_LIVE_TLV_TYPE 0x03 /* Mandatory */
|
|
|
|
#define PORT_DESCRIPTION_TLV_TYPE 0x04
|
|
|
|
#define SYSTEM_NAME_TLV_TYPE 0x05
|
|
|
|
#define SYSTEM_DESCRIPTION_TLV_TYPE 0x06
|
|
|
|
#define SYSTEM_CAPABILITIES_TLV_TYPE 0x07
|
|
|
|
#define MANAGEMENT_ADDR_TLV_TYPE 0x08
|
|
|
|
#define ORGANIZATION_SPECIFIC_TLV_TYPE 0x7F
|
|
|
|
|
|
|
|
/* Masks */
|
|
|
|
#define TLV_TYPE_MASK 0xFE00
|
|
|
|
#define TLV_TYPE(value) (((value) & TLV_TYPE_MASK) >> 9)
|
|
|
|
#define TLV_INFO_MASK 0x01FF
|
|
|
|
#define TLV_INFO_LEN(value) ((value) & TLV_INFO_MASK)
|
|
|
|
|
|
|
|
/* Initialize the protocol and registered fields */
|
|
|
|
static int proto_lldp = -1;
|
|
|
|
static int hf_lldp_tlv_type = -1;
|
|
|
|
static int hf_lldp_tlv_len = -1;
|
|
|
|
static int hf_chassis_id_subtype = -1;
|
|
|
|
static int hf_chassis_id = -1;
|
|
|
|
static int hf_chassis_id_mac = -1;
|
|
|
|
static int hf_chassis_id_ip4 = -1;
|
|
|
|
static int hf_chassis_id_ip6 = -1;
|
|
|
|
static int hf_port_id_subtype = -1;
|
|
|
|
static int hf_port_id_mac = -1;
|
|
|
|
static int hf_port_id_ip4 = -1;
|
|
|
|
static int hf_port_id_ip6 = -1;
|
|
|
|
static int hf_time_to_live = -1;
|
|
|
|
static int hf_mgn_addr_ipv4 = -1;
|
|
|
|
static int hf_mgn_addr_hex = -1;
|
|
|
|
static int hf_mgn_obj_id = -1;
|
|
|
|
static int hf_org_spc_oui = -1;
|
|
|
|
static int hf_ieee_802_1_subtype = -1;
|
|
|
|
static int hf_ieee_802_3_subtype = -1;
|
|
|
|
static int hf_media_tlv_subtype = -1;
|
|
|
|
|
|
|
|
/* Initialize the subtree pointers */
|
|
|
|
static gint ett_lldp = -1;
|
|
|
|
static gint ett_chassis_id = -1;
|
|
|
|
static gint ett_port_id = -1;
|
|
|
|
static gint ett_time_to_live = -1;
|
|
|
|
static gint ett_end_of_lldpdu = -1;
|
|
|
|
static gint ett_port_description = -1;
|
|
|
|
static gint ett_system_name = -1;
|
|
|
|
static gint ett_system_cap = -1;
|
|
|
|
static gint ett_system_cap_summary = -1;
|
|
|
|
static gint ett_system_cap_enabled = -1;
|
|
|
|
static gint ett_management_address = -1;
|
|
|
|
static gint ett_unknown_tlv = -1;
|
|
|
|
static gint ett_org_spc_tlv = -1;
|
|
|
|
static gint ett_port_vlan_flags = -1;
|
|
|
|
static gint ett_802_3_flags = -1;
|
|
|
|
static gint ett_802_3_power = -1;
|
|
|
|
static gint ett_802_3_aggregation = -1;
|
|
|
|
static gint ett_media_capabilities = -1;
|
|
|
|
|
|
|
|
const value_string tlv_types[] = {
|
|
|
|
{ END_OF_LLDPDU_TLV_TYPE, "End of LLDPDU"},
|
|
|
|
{ CHASSIS_ID_TLV_TYPE, "Chassis Id"},
|
|
|
|
{ PORT_ID_TLV_TYPE, "Port Id"},
|
|
|
|
{ TIME_TO_LIVE_TLV_TYPE, "Time to Live"},
|
|
|
|
{ PORT_DESCRIPTION_TLV_TYPE, "Port Description"},
|
|
|
|
{ SYSTEM_NAME_TLV_TYPE, "System Name"},
|
|
|
|
{ SYSTEM_DESCRIPTION_TLV_TYPE, "System Description"},
|
|
|
|
{ SYSTEM_CAPABILITIES_TLV_TYPE, "System Capabilities"},
|
|
|
|
{ MANAGEMENT_ADDR_TLV_TYPE, "Management Address"},
|
|
|
|
{ ORGANIZATION_SPECIFIC_TLV_TYPE, "Organization Specific"},
|
|
|
|
{ 0, NULL}
|
|
|
|
};
|
|
|
|
|
|
|
|
const value_string chassis_id_subtypes[] = {
|
|
|
|
{ 0, "Reserved"},
|
|
|
|
{ 1, "Chassis component"},
|
|
|
|
{ 2, "Interface alias"},
|
|
|
|
{ 3, "Port component"},
|
|
|
|
{ 4, "MAC address"},
|
|
|
|
{ 5, "Network address"},
|
|
|
|
{ 6, "Interface name"},
|
|
|
|
{ 7, "Locally assigned"},
|
|
|
|
{ 0, NULL}
|
|
|
|
};
|
|
|
|
|
|
|
|
const value_string port_id_subtypes[] = {
|
|
|
|
{ 0, "Reserved"},
|
|
|
|
{ 1, "Interface alias"},
|
|
|
|
{ 2, "Port component"},
|
|
|
|
{ 3, "MAC address"},
|
|
|
|
{ 4, "Network address"},
|
|
|
|
{ 5, "Interface name"},
|
|
|
|
{ 6, "Agent circuit Id"},
|
|
|
|
{ 7, "Locally assigned"},
|
|
|
|
{ 0, NULL}
|
|
|
|
};
|
|
|
|
|
|
|
|
const value_string management_addr_values[] = {
|
|
|
|
{ 0, "Reserved"},
|
|
|
|
{ 1, "IPv4"},
|
|
|
|
{ 2, "IPv6"},
|
|
|
|
{ 3, "NSAP"},
|
|
|
|
{ 4, "HDLC (8-bit multidrop)"},
|
|
|
|
{ 5, "BBN 1822"},
|
|
|
|
{ 6, "802 (includes all 802 media plus Ethernet)"},
|
|
|
|
{ 7, "E.163"},
|
|
|
|
{ 8, "E.164 (SMDS, Frame Relay, ATM)"},
|
|
|
|
{ 9, "F.69 (Telex)"},
|
|
|
|
{ 10, "X.121 (X.25, Frame Relay)"},
|
|
|
|
{ 11, "IPX"},
|
|
|
|
{ 12, "Appletalk"},
|
|
|
|
{ 13, "Decnet IV"},
|
|
|
|
{ 14, "Banyan Vines"},
|
|
|
|
{ 15, "E.164 with NSAP format subaddress"},
|
|
|
|
{ 16, "DNS (Domain Name System)"},
|
|
|
|
{ 17, "Distinguished Name"},
|
|
|
|
{ 18, "AS Number"},
|
|
|
|
{ 19, "XTP over IP version 4"},
|
|
|
|
{ 20, "XTP over IP version 6"},
|
|
|
|
{ 21, "XTP native mode XTP"},
|
|
|
|
{ 22, "Fibre Channel World-Wide Port Name"},
|
|
|
|
{ 23, "Fibre Channel World-Wide Node Name"},
|
|
|
|
{ 24, "GWID"},
|
|
|
|
{ 0, NULL}
|
|
|
|
};
|
|
|
|
|
|
|
|
const value_string interface_subtype_values[] = {
|
|
|
|
{ 1, "Unknown"},
|
|
|
|
{ 2, "ifIndex"},
|
|
|
|
{ 3, "System port number"},
|
|
|
|
{ 0, NULL}
|
|
|
|
};
|
|
|
|
|
|
|
|
#define OUI_IEEE_802_1 0x0080C2 /* IEEE 802.1 */
|
|
|
|
#define OUI_IEEE_802_3 0x00120F /* IEEE 802.3 */
|
|
|
|
#define OUI_MEDIA_ENDPOINT 0x0012BB /* Media */
|
|
|
|
|
|
|
|
const value_string tlv_oui_subtype_vals[] = {
|
|
|
|
{ OUI_IEEE_802_1, "IEEE 802.1" },
|
|
|
|
{ OUI_IEEE_802_3, "IEEE 802.3" },
|
|
|
|
{ OUI_MEDIA_ENDPOINT, "TIA" },
|
|
|
|
{ 0, NULL }
|
|
|
|
};
|
|
|
|
|
|
|
|
/* IEEE 802.1 Subtypes */
|
|
|
|
const value_string ieee_802_1_subtypes[] = {
|
|
|
|
{ 0x01, "Port VLAN ID" },
|
|
|
|
{ 0x02, "Port and Protocol VLAN ID" },
|
|
|
|
{ 0x03, "VLAN Name" },
|
|
|
|
{ 0x04, "Protocol Identity" },
|
|
|
|
{ 0, NULL }
|
|
|
|
};
|
|
|
|
|
|
|
|
/* IEEE 802.3 Subtypes */
|
|
|
|
const value_string ieee_802_3_subtypes[] = {
|
|
|
|
{ 0x01, "MAC/PHY Configuration/Status" },
|
|
|
|
{ 0x02, "Power Via MDI" },
|
|
|
|
{ 0x03, "Link Aggregatiion" },
|
|
|
|
{ 0x04, "Maximum Frame Size" },
|
|
|
|
{ 0, NULL }
|
|
|
|
};
|
|
|
|
|
|
|
|
/* Media Subtypes */
|
|
|
|
const value_string media_subtypes[] = {
|
|
|
|
{ 1, "Media Capabilities" },
|
|
|
|
{ 2, "Network Policy" },
|
|
|
|
{ 3, "Location Identification" },
|
|
|
|
{ 4, "Extended Power-via-MDI" },
|
|
|
|
{ 5, "Inventory - Hardware Revision" },
|
|
|
|
{ 6, "Inventory - Firmware Revision" },
|
|
|
|
{ 7, "Inventory - Software Revision" },
|
|
|
|
{ 8, "Inventory - Serial Number" },
|
|
|
|
{ 9, "Inventory - Manufacturer Name" },
|
|
|
|
{ 10, "Inventory - Model Name" },
|
|
|
|
{ 11, "Inventory - Asset ID" },
|
|
|
|
{ 0, NULL }
|
|
|
|
};
|
|
|
|
|
|
|
|
/* Media Class Values */
|
|
|
|
const value_string media_class_values[] = {
|
|
|
|
{ 0, "Type Not Defined" },
|
|
|
|
{ 1, "Endpoint Class I" },
|
|
|
|
{ 2, "Endpoint Class II" },
|
|
|
|
{ 3, "Endpoint Class III" },
|
|
|
|
{ 4, "Network Connectivity" },
|
|
|
|
{ 0, NULL }
|
|
|
|
};
|
|
|
|
|
|
|
|
/* Media Application Types */
|
|
|
|
const value_string media_application_type[] = {
|
|
|
|
{ 0, "Reserved" },
|
|
|
|
{ 1, "Voice" },
|
|
|
|
{ 2, "Voice Signaling" },
|
|
|
|
{ 3, "Guest Voice" },
|
|
|
|
{ 4, "Guest Voice Signaling" },
|
|
|
|
{ 5, "Softphone Voice" },
|
|
|
|
{ 6, "Video Conferencing" },
|
|
|
|
{ 7, "Streaming Video" },
|
|
|
|
{ 8, "Video Signaling" },
|
|
|
|
{ 0, NULL }
|
|
|
|
};
|
|
|
|
|
|
|
|
/* Power Type */
|
|
|
|
const value_string media_power_type[] = {
|
|
|
|
{ 0, "PSE Device" },
|
|
|
|
{ 1, "PD Device" },
|
|
|
|
{ 0, NULL }
|
|
|
|
};
|
|
|
|
|
|
|
|
/* Power Priority */
|
|
|
|
const value_string media_power_priority[] = {
|
|
|
|
{ 0, "Unknown" },
|
|
|
|
{ 1, "Critical" },
|
|
|
|
{ 2, "High" },
|
|
|
|
{ 3, "Low" },
|
|
|
|
{ 0, NULL }
|
|
|
|
};
|
|
|
|
|
|
|
|
/* Power Sources */
|
|
|
|
const value_string media_power_pd_device[] = {
|
|
|
|
{ 0, "Unknown" },
|
|
|
|
{ 1, "PSE" },
|
|
|
|
{ 2, "Local" },
|
|
|
|
{ 3, "PSE and Local" },
|
|
|
|
{ 0, NULL }
|
|
|
|
};
|
|
|
|
const value_string media_power_pse_device[] = {
|
|
|
|
{ 0, "Unknown" },
|
|
|
|
{ 1, "Primary Power Source" },
|
|
|
|
{ 2, "Backup Power Source" },
|
|
|
|
{ 0, NULL }
|
|
|
|
};
|
|
|
|
|
|
|
|
/* Location data format */
|
|
|
|
const value_string location_data_format[] = {
|
|
|
|
{ 0, "Invalid " },
|
|
|
|
{ 1, "Coordinate-based LCI" },
|
|
|
|
{ 2, "Civic Address LCI" },
|
|
|
|
{ 3, "ECS ELIN" },
|
|
|
|
{ 0, NULL }
|
|
|
|
};
|
|
|
|
|
|
|
|
/* Civic Address LCI - What field */
|
|
|
|
const value_string civic_address_what_values[] = {
|
|
|
|
{ 0, "Location of the DHCP server" },
|
|
|
|
{ 1, "Location of the network element believed to be closest to the client" },
|
|
|
|
{ 2, "Location of the client"},
|
|
|
|
{ 0, NULL}
|
|
|
|
};
|
|
|
|
|
|
|
|
/* Civic Address Type field */
|
|
|
|
const value_string civic_address_type_values[] = {
|
|
|
|
{ 0, "Language" },
|
|
|
|
{ 1, "National subdivisions (province, state, etc)" },
|
|
|
|
{ 2, "County, parish, district" },
|
|
|
|
{ 3, "City, township" },
|
|
|
|
{ 4, "City division, borough, ward" },
|
|
|
|
{ 5, "Neighborhood, block" },
|
|
|
|
{ 6, "Street" },
|
|
|
|
{ 16, "Leading street direction" },
|
|
|
|
{ 17, "Trailing street suffix" },
|
|
|
|
{ 18, "Street suffix" },
|
|
|
|
{ 19, "House number" },
|
|
|
|
{ 20, "House number suffix" },
|
|
|
|
{ 21, "Landmark or vanity address" },
|
|
|
|
{ 22, "Additional location information" },
|
|
|
|
{ 23, "Name" },
|
|
|
|
{ 24, "Postal/ZIP code" },
|
|
|
|
{ 25, "Building" },
|
|
|
|
{ 26, "Unit" },
|
|
|
|
{ 27, "Floor" },
|
|
|
|
{ 28, "Room number" },
|
|
|
|
{ 29, "Place type" },
|
|
|
|
{ 128, "Script" },
|
|
|
|
{ 0, NULL }
|
|
|
|
};
|
|
|
|
|
|
|
|
/* System Capabilities */
|
|
|
|
#define SYSTEM_CAPABILITY_OTHER 0x0001
|
|
|
|
#define SYSTEM_CAPABILITY_REPEATER 0x0002
|
|
|
|
#define SYSTEM_CAPABILITY_BRIDGE 0x0004
|
|
|
|
#define SYSTEM_CAPABILITY_WLAN 0x0008
|
|
|
|
#define SYSTEM_CAPABILITY_ROUTER 0x0010
|
|
|
|
#define SYSTEM_CAPABILITY_TELEPHONE 0x0020
|
|
|
|
#define SYSTEM_CAPABILITY_DOCSIS 0x0040
|
|
|
|
#define SYSTEM_CAPABILITY_STATION 0x0080
|
|
|
|
|
|
|
|
/* Media Capabilities */
|
|
|
|
#define MEDIA_CAPABILITY_LLDP 0x0001
|
|
|
|
#define MEDIA_CAPABILITY_NETWORK_POLICY 0x0002
|
|
|
|
#define MEDIA_CAPABILITY_LOCATION_ID 0x0004
|
|
|
|
#define MEDIA_CAPABILITY_MDI_PSE 0x0008
|
|
|
|
#define MEDIA_CAPABILITY_MDI_PD 0x0010
|
|
|
|
#define MEDIA_CAPABILITY_INVENTORY 0x0020
|
|
|
|
|
|
|
|
#define MAX_MAC_LEN 6
|
|
|
|
|
|
|
|
/* Create MAC address */
|
|
|
|
static gchar *
|
|
|
|
MAC_to_str(const guint8 *ad, guint32 len, char punct) {
|
|
|
|
static gchar str[3][MAX_MAC_LEN*3];
|
|
|
|
static gchar *cur;
|
|
|
|
gchar *p;
|
|
|
|
int i;
|
|
|
|
guint32 octet;
|
|
|
|
/* At least one version of Apple's C compiler/linker is buggy, causing
|
|
|
|
a complaint from the linker about the "literal C string section"
|
|
|
|
not ending with '\0' if we initialize a 16-element "char" array with
|
|
|
|
a 16-character string, the fact that initializing such an array with
|
|
|
|
such a string is perfectly legitimate ANSI C nonwithstanding, the 17th
|
|
|
|
'\0' byte in the string nonwithstanding. */
|
|
|
|
static const gchar hex_digits[16] =
|
|
|
|
{ '0', '1', '2', '3', '4', '5', '6', '7',
|
|
|
|
'8', '9', 'a', 'b', 'c', 'd', 'e', 'f' };
|
|
|
|
|
|
|
|
g_assert(len > 0 && len <= MAX_MAC_LEN);
|
|
|
|
len--;
|
|
|
|
|
|
|
|
if (cur == &str[0][0]) {
|
|
|
|
cur = &str[1][0];
|
|
|
|
} else if (cur == &str[1][0]) {
|
|
|
|
cur = &str[2][0];
|
|
|
|
} else {
|
|
|
|
cur = &str[0][0];
|
|
|
|
}
|
|
|
|
p = &cur[18];
|
|
|
|
*--p = '\0';
|
|
|
|
i = len;
|
|
|
|
for (;;) {
|
|
|
|
octet = ad[i];
|
|
|
|
*--p = hex_digits[octet&0xF];
|
|
|
|
octet >>= 4;
|
|
|
|
*--p = hex_digits[octet&0xF];
|
|
|
|
if (i == 0)
|
|
|
|
break;
|
|
|
|
if (punct)
|
|
|
|
*--p = punct;
|
|
|
|
i--;
|
|
|
|
}
|
|
|
|
return p;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Calculate Latitude and Longitude string */
|
|
|
|
/*
|
|
|
|
Parameters:
|
|
|
|
option = 0 -> Latitude
|
|
|
|
option = 1 -> Longitude
|
|
|
|
*/
|
|
|
|
static void
|
|
|
|
get_latitude_or_longitude(int option, guint64 value, guint8 *strPtr)
|
|
|
|
{
|
|
|
|
guint64 tempValue = value;
|
|
|
|
guint8 negativeNum = 0; /* False */
|
|
|
|
guint32 integerPortion = 0;
|
|
|
|
|
|
|
|
gchar tempStr[100];
|
|
|
|
|
|
|
|
strcpy(strPtr," ");
|
|
|
|
|
|
|
|
/* The latitude and longitude are 34 bit fixed point value consisting
|
|
|
|
of 9 bits of integer and 25 bits of fraction.
|
|
|
|
When option is equal to 0, positive numbers are represent a location
|
|
|
|
north of the equator and negative (2s complement) numbers are south of the equator.
|
|
|
|
When option is equal to 1, positive values are east of the prime
|
|
|
|
meridian and negative (2s complement) numbers are west of the prime meridian.
|
|
|
|
*/
|
|
|
|
|
2005-09-15 03:09:40 +00:00
|
|
|
if (value & G_GINT64_CONSTANT(0x0000000200000000))
|
2005-09-14 19:06:54 +00:00
|
|
|
{
|
|
|
|
/* Have a negative number (2s complement) */
|
|
|
|
negativeNum = 1;
|
|
|
|
|
|
|
|
tempValue = ~value;
|
|
|
|
tempValue += 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Get the integer portion */
|
2005-09-15 03:09:40 +00:00
|
|
|
integerPortion = (guint32)((tempValue & G_GINT64_CONSTANT(0x00000003FE000000)) >> 25);
|
2005-09-14 19:06:54 +00:00
|
|
|
|
|
|
|
/* Calculate decimal portion (using 25 bits for fraction) */
|
2005-09-15 03:09:40 +00:00
|
|
|
tempValue = (tempValue & G_GINT64_CONSTANT(0x0000000001FFFFFF))/33554432;
|
2005-09-14 19:06:54 +00:00
|
|
|
|
2005-09-15 03:09:40 +00:00
|
|
|
sprintf(tempStr,"%u.%04" PRIu64 " degrees ", integerPortion, tempValue);
|
2005-09-14 19:06:54 +00:00
|
|
|
|
|
|
|
if (option == 0)
|
|
|
|
{
|
|
|
|
/* Latitude - north/south directions */
|
|
|
|
if (negativeNum == 1)
|
|
|
|
strcat(tempStr,"South");
|
|
|
|
else
|
|
|
|
strcat(tempStr,"North");
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
/* Longitude - east/west directions */
|
|
|
|
if (negativeNum == 1)
|
|
|
|
strcat(tempStr,"West");
|
|
|
|
else
|
|
|
|
strcat(tempStr,"East");
|
|
|
|
}
|
|
|
|
|
|
|
|
strcpy(strPtr,tempStr);
|
|
|
|
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Dissect Chassis Id TLV (Mandatory) */
|
|
|
|
static gint32
|
|
|
|
dissect_lldp_chassis_id(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, guint32 offset)
|
|
|
|
{
|
|
|
|
guint8 tempType;
|
|
|
|
guint16 tempShort;
|
|
|
|
guint32 tempLen = 0;
|
|
|
|
char tempStr[255];
|
|
|
|
guint8 incorrectLen = 0; /* incorrect length if 1 */
|
|
|
|
|
|
|
|
const char *mac_addr;
|
|
|
|
guint32 ip_addr;
|
|
|
|
guint8 ip6_addr[16];
|
|
|
|
|
|
|
|
proto_tree *chassis_tree = NULL;
|
|
|
|
proto_item *tf = NULL;
|
|
|
|
|
|
|
|
/* Get tlv type */
|
|
|
|
tempShort = tvb_get_ntohs(tvb, offset);
|
|
|
|
tempType = TLV_TYPE(tempShort);
|
|
|
|
if (tempType != CHASSIS_ID_TLV_TYPE)
|
|
|
|
{
|
|
|
|
if (tree)
|
|
|
|
{
|
|
|
|
tf = proto_tree_add_text(tree, tvb, offset, 2, "Invalid Chassis ID (0x%02X)", tempType);
|
|
|
|
chassis_tree = proto_item_add_subtree(tf, ett_chassis_id);
|
|
|
|
proto_tree_add_text(chassis_tree, tvb, offset, 2, "%s Invalid Chassis ID (%u)",
|
|
|
|
decode_boolean_bitfield(tempType, TLV_TYPE_MASK, 16, "", ""), tempType);
|
|
|
|
}
|
|
|
|
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Get tlv length */
|
|
|
|
tempLen = TLV_INFO_LEN(tempShort);
|
|
|
|
if (tempLen < 2)
|
|
|
|
{
|
|
|
|
if (tree)
|
|
|
|
{
|
|
|
|
tf = proto_tree_add_text(tree, tvb, offset, 2, "Invalid Chassis ID Length (%u)", tempLen);
|
|
|
|
chassis_tree = proto_item_add_subtree(tf, ett_chassis_id);
|
|
|
|
proto_tree_add_item(chassis_tree, hf_lldp_tlv_type, tvb, offset, 2, FALSE);
|
|
|
|
proto_tree_add_text(chassis_tree, tvb, offset, 2, "%s Invalid Length: %u",
|
|
|
|
decode_boolean_bitfield(tempLen, TLV_INFO_MASK, 16, "", ""), tempLen);
|
|
|
|
}
|
|
|
|
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Get tlv subtype */
|
|
|
|
tempType = tvb_get_guint8(tvb, (offset+2));
|
|
|
|
|
|
|
|
switch (tempType)
|
|
|
|
{
|
|
|
|
case 4: /* MAC address */
|
|
|
|
{
|
|
|
|
if (tempLen != 7)
|
|
|
|
{
|
|
|
|
incorrectLen = 1; /* Invalid length */
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
mac_addr=tvb_get_ptr(tvb, (offset+3), 6);
|
|
|
|
strcpy(tempStr,MAC_to_str(mac_addr, 6, ':'));
|
|
|
|
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
case 5: /* Network address */
|
|
|
|
{
|
|
|
|
/* Check for IPv4 or IPv6 */
|
|
|
|
if (tempLen == 5)
|
|
|
|
{
|
|
|
|
tvb_memcpy(tvb, (guint8 *)&ip_addr, (offset+3), 4);
|
|
|
|
strcpy(tempStr,ip_to_str((guint8 *)&ip_addr));
|
|
|
|
}
|
|
|
|
else if (tempLen == 17)
|
|
|
|
{
|
|
|
|
tvb_memcpy(tvb, ip6_addr, (offset+3), 16);
|
|
|
|
strcpy(tempStr,ip6_to_str((struct e_in6_addr *)ip6_addr));
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
incorrectLen = 1; /* Invalid length */
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
case 2: /* Interface alias */
|
|
|
|
case 6: /* Interface name */
|
|
|
|
case 7: /* Locally assigned */
|
|
|
|
{
|
|
|
|
if (tempLen > 256)
|
|
|
|
{
|
|
|
|
incorrectLen = 1; /* Invalid length */
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
tvb_memcpy(tvb, tempStr, (offset+3), (tempLen-1));
|
|
|
|
tempStr[(tempLen-1)] = '\0';
|
|
|
|
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
case 1: /* Chassis component */
|
|
|
|
case 3: /* Port component */
|
|
|
|
{
|
|
|
|
if (tempLen > 256)
|
|
|
|
{
|
|
|
|
incorrectLen = 1; /* Invalid length */
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
strcpy(tempStr,tvb_bytes_to_str(tvb, (offset+3), (tempLen-1)));
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
default: /* Reserved types */
|
|
|
|
{
|
|
|
|
if (tempLen > 256)
|
|
|
|
{
|
|
|
|
incorrectLen = 1; /* Invalid length */
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
strcpy(tempStr,"Reserved");
|
|
|
|
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (incorrectLen == 1)
|
|
|
|
{
|
|
|
|
if (tree)
|
|
|
|
{
|
|
|
|
tf = proto_tree_add_text(tree, tvb, offset, 2, "Invalid Chassis ID Length (%u)", tempLen);
|
|
|
|
chassis_tree = proto_item_add_subtree(tf, ett_chassis_id);
|
|
|
|
proto_tree_add_item(chassis_tree, hf_lldp_tlv_type, tvb, offset, 2, FALSE);
|
|
|
|
proto_tree_add_text(chassis_tree, tvb, offset, 2, "%s Invalid Length: %u",
|
|
|
|
decode_boolean_bitfield(tempLen, TLV_INFO_MASK, 16, "", ""), tempLen);
|
|
|
|
/* Get chassis id subtype */
|
|
|
|
proto_tree_add_item(chassis_tree, hf_chassis_id_subtype, tvb, (offset+2), 1, FALSE);
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (check_col(pinfo->cinfo, COL_INFO))
|
|
|
|
col_add_fstr(pinfo->cinfo, COL_INFO, "Chassis Id = %s ", tempStr);
|
|
|
|
|
|
|
|
if (tree)
|
|
|
|
{
|
|
|
|
/* Set chassis tree */
|
|
|
|
tf = proto_tree_add_text(tree, tvb, offset, (tempLen + 2), "Chassis Subtype = %s",
|
|
|
|
val_to_str(tempType, chassis_id_subtypes, "Reserved" ));
|
|
|
|
chassis_tree = proto_item_add_subtree(tf, ett_chassis_id);
|
|
|
|
|
|
|
|
proto_tree_add_item(chassis_tree, hf_lldp_tlv_type, tvb, offset, 2, FALSE);
|
|
|
|
proto_tree_add_item(chassis_tree, hf_lldp_tlv_len, tvb, offset, 2, FALSE);
|
|
|
|
|
|
|
|
/* Get chassis id subtype */
|
|
|
|
proto_tree_add_item(chassis_tree, hf_chassis_id_subtype, tvb, (offset+2), 1, FALSE);
|
|
|
|
|
|
|
|
/* Get chassis id */
|
|
|
|
switch (tempType)
|
|
|
|
{
|
|
|
|
case 4: /* MAC address */
|
|
|
|
strcpy(tempStr,MAC_to_str(mac_addr, 6, ':'));
|
|
|
|
proto_tree_add_text(chassis_tree, tvb, (offset+3), 6, "Chassis Id: %s", tempStr);
|
|
|
|
proto_tree_add_ether_hidden(chassis_tree, hf_chassis_id_mac, tvb, (offset+3), 6, mac_addr);
|
|
|
|
break;
|
|
|
|
case 5: /* Network address */
|
|
|
|
if (tempLen == 5)
|
|
|
|
proto_tree_add_ipv4(chassis_tree, hf_chassis_id_ip4, tvb, (offset+3), 4, ip_addr);
|
|
|
|
else
|
|
|
|
proto_tree_add_ipv6(chassis_tree, hf_chassis_id_ip6, tvb, (offset+3), 16, ip6_addr);
|
|
|
|
break;
|
|
|
|
case 2: /* Interface alias */
|
|
|
|
case 6: /* Interface name */
|
|
|
|
case 7: /* Locally assigned */
|
|
|
|
proto_tree_add_text(chassis_tree, tvb, (offset+3), (tempLen-1), "Chassis Id: %s", tempStr);
|
|
|
|
break;
|
|
|
|
case 1: /* Chassis component */
|
|
|
|
case 3: /* Port component */
|
|
|
|
proto_tree_add_item(chassis_tree, hf_chassis_id, tvb, (offset+3), (tempLen-1), FALSE);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return (tempLen + 2);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Dissect Port Id TLV (Mandatory) */
|
|
|
|
static gint32
|
|
|
|
dissect_lldp_port_id(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, guint32 offset)
|
|
|
|
{
|
|
|
|
guint8 tempType;
|
|
|
|
guint16 tempShort;
|
|
|
|
guint32 tempLen = 0;
|
|
|
|
guint8 tempStr[255];
|
|
|
|
const char *mac_addr;
|
|
|
|
guint32 ip_addr;
|
|
|
|
guint8 ip6_addr[16];
|
|
|
|
|
|
|
|
proto_tree *port_tree = NULL;
|
|
|
|
proto_item *tf = NULL;
|
|
|
|
|
|
|
|
/* Get tlv type */
|
|
|
|
tempShort = tvb_get_ntohs(tvb, offset);
|
|
|
|
tempType = TLV_TYPE(tempShort);
|
|
|
|
if (tempType != PORT_ID_TLV_TYPE)
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
/* Get tlv length and subtype */
|
|
|
|
tempLen = TLV_INFO_LEN(tempShort);
|
|
|
|
tempType = tvb_get_guint8(tvb, (offset+2));
|
|
|
|
|
|
|
|
/* Get port id */
|
|
|
|
switch (tempType)
|
|
|
|
{
|
|
|
|
case 3: /* MAC address */
|
|
|
|
{
|
|
|
|
if (tempLen != 7)
|
|
|
|
return -1; /* Invalid port id */
|
|
|
|
|
|
|
|
mac_addr=tvb_get_ptr(tvb, (offset+3), 6);
|
|
|
|
strcpy(tempStr,MAC_to_str(mac_addr, 6, ':'));
|
|
|
|
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
case 4: /* Network address */
|
|
|
|
{
|
|
|
|
/* Check for IPv4 or IPv6 */
|
|
|
|
if (tempLen == 5)
|
|
|
|
{
|
|
|
|
tvb_memcpy(tvb, (guint8 *)&ip_addr, (offset+3), 4);
|
|
|
|
strcpy(tempStr,ip_to_str((guint8 *)&ip_addr));
|
|
|
|
}
|
|
|
|
else if (tempLen == 17)
|
|
|
|
{
|
|
|
|
tvb_memcpy(tvb, ip6_addr, (offset+3), 16);
|
|
|
|
strcpy(tempStr,ip6_to_str((struct e_in6_addr *)ip6_addr));
|
|
|
|
}
|
|
|
|
else
|
|
|
|
return -1; /* Invalid chassis id */
|
|
|
|
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
default:
|
|
|
|
{
|
|
|
|
tvb_memcpy(tvb, tempStr, (offset+3), (tempLen-1));
|
|
|
|
tempStr[(tempLen-1)] = '\0';
|
|
|
|
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (check_col(pinfo->cinfo, COL_INFO))
|
|
|
|
col_append_fstr(pinfo->cinfo, COL_INFO, "Port Id = %s ", tempStr);
|
|
|
|
|
|
|
|
if (tree)
|
|
|
|
{
|
|
|
|
/* Set port tree */
|
|
|
|
tf = proto_tree_add_text(tree, tvb, offset, (tempLen + 2), "Port Subtype = %s",
|
|
|
|
val_to_str(tempType, port_id_subtypes, "Unknown" ));
|
|
|
|
port_tree = proto_item_add_subtree(tf, ett_port_id);
|
|
|
|
|
|
|
|
proto_tree_add_item(port_tree, hf_lldp_tlv_type, tvb, offset, 2, FALSE);
|
|
|
|
proto_tree_add_item(port_tree, hf_lldp_tlv_len, tvb, offset, 2, FALSE);
|
|
|
|
|
|
|
|
/* Get port id subtype */
|
|
|
|
proto_tree_add_item(port_tree, hf_port_id_subtype, tvb, (offset+2), 1, FALSE);
|
|
|
|
|
|
|
|
/* Get port id */
|
|
|
|
/*proto_tree_add_text(port_tree, tvb, (offset+3), (tempLen-1), "Port Id: %s", tempStr);*/
|
|
|
|
switch (tempType)
|
|
|
|
{
|
|
|
|
case 3: /* MAC address */
|
|
|
|
strcpy(tempStr,MAC_to_str(mac_addr, 6, ':'));
|
|
|
|
proto_tree_add_text(port_tree, tvb, (offset+3), 6, "Port Id: %s", tempStr);
|
|
|
|
proto_tree_add_ether_hidden(port_tree, hf_port_id_mac, tvb, (offset+3), 6, mac_addr);
|
|
|
|
break;
|
|
|
|
case 4: /* Network address */
|
|
|
|
if (tempLen == 5)
|
|
|
|
proto_tree_add_ipv4(port_tree, hf_port_id_ip4, tvb, (offset+3), 4, ip_addr);
|
|
|
|
else
|
|
|
|
proto_tree_add_ipv6(port_tree, hf_port_id_ip6, tvb, (offset+3), 16, ip6_addr);
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
proto_tree_add_text(port_tree, tvb, (offset+3), (tempLen-1), "Port Id: %s", tempStr);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
return (tempLen + 2);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Dissect Time To Live TLV (Mandatory) */
|
|
|
|
static gint32
|
|
|
|
dissect_lldp_time_to_live(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, guint32 offset)
|
|
|
|
{
|
|
|
|
guint8 tempType;
|
|
|
|
guint16 tempShort;
|
|
|
|
guint32 tempLen = 0;
|
|
|
|
|
|
|
|
proto_tree *time_to_live_tree = NULL;
|
|
|
|
proto_item *tf = NULL;
|
|
|
|
|
|
|
|
/* Get tlv type */
|
|
|
|
tempShort = tvb_get_ntohs(tvb, offset);
|
|
|
|
tempType = TLV_TYPE(tempShort);
|
|
|
|
if (tempType != TIME_TO_LIVE_TLV_TYPE)
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
/* Get tlv length and seconds field */
|
|
|
|
tempLen = TLV_INFO_LEN(tempShort);
|
|
|
|
tempShort = tvb_get_ntohs(tvb, (offset+2));
|
|
|
|
|
|
|
|
if (check_col(pinfo->cinfo, COL_INFO))
|
|
|
|
col_append_fstr(pinfo->cinfo, COL_INFO, "TTL = %u ", tempShort);
|
|
|
|
|
|
|
|
if (tree)
|
|
|
|
{
|
|
|
|
/* Set port tree */
|
|
|
|
tf = proto_tree_add_text(tree, tvb, offset, (tempLen + 2), "Time To Live = %u sec", tempShort);
|
|
|
|
time_to_live_tree = proto_item_add_subtree(tf, ett_time_to_live);
|
|
|
|
|
|
|
|
proto_tree_add_item(time_to_live_tree, hf_lldp_tlv_type, tvb, offset, 2, FALSE);
|
|
|
|
proto_tree_add_item(time_to_live_tree, hf_lldp_tlv_len, tvb, offset, 2, FALSE);
|
|
|
|
|
|
|
|
/* Display time to live information */
|
|
|
|
proto_tree_add_item(time_to_live_tree, hf_time_to_live, tvb, (offset+2), 2, FALSE);
|
|
|
|
}
|
|
|
|
|
|
|
|
return (tempLen + 2);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Dissect End of LLDPDU TLV (Mandatory) */
|
|
|
|
static gint32
|
|
|
|
dissect_lldp_end_of_lldpdu(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, guint32 offset)
|
|
|
|
{
|
|
|
|
guint16 tempLen;
|
|
|
|
guint16 tempShort;
|
|
|
|
|
|
|
|
proto_tree *end_of_lldpdu_tree = NULL;
|
|
|
|
proto_item *tf = NULL;
|
|
|
|
|
|
|
|
/* Get tlv type and length */
|
|
|
|
tempShort = tvb_get_ntohs(tvb, offset);
|
|
|
|
|
|
|
|
/* Get tlv length */
|
|
|
|
tempLen = TLV_INFO_LEN(tempShort);
|
|
|
|
|
|
|
|
if (tree)
|
|
|
|
{
|
|
|
|
/* Set port tree */
|
|
|
|
tf = proto_tree_add_text(tree, tvb, offset, (tempLen + 2), "End of LLDPDU");
|
|
|
|
end_of_lldpdu_tree = proto_item_add_subtree(tf, ett_end_of_lldpdu);
|
|
|
|
|
|
|
|
proto_tree_add_item(end_of_lldpdu_tree, hf_lldp_tlv_type, tvb, offset, 2, FALSE);
|
|
|
|
proto_tree_add_item(end_of_lldpdu_tree, hf_lldp_tlv_len, tvb, offset, 2, FALSE);
|
|
|
|
}
|
|
|
|
|
|
|
|
return -1; /* Force the lldp dissector to terminate */
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Dissect Port Description TLV */
|
|
|
|
static gint32
|
|
|
|
dissect_lldp_port_desc(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, guint32 offset)
|
|
|
|
{
|
|
|
|
guint16 tempShort;
|
|
|
|
guint32 tempLen = 0;
|
|
|
|
guint8 tempStr[255];
|
|
|
|
|
|
|
|
proto_tree *port_desc_tree = NULL;
|
|
|
|
proto_item *tf = NULL;
|
|
|
|
|
|
|
|
/* Get tlv type and length */
|
|
|
|
tempShort = tvb_get_ntohs(tvb, offset);
|
|
|
|
|
|
|
|
/* Get tlv length and string field */
|
|
|
|
tempLen = TLV_INFO_LEN(tempShort);
|
|
|
|
tvb_memcpy(tvb, tempStr, (offset+2), tempLen);
|
|
|
|
tempStr[tempLen] = '\0';
|
|
|
|
|
|
|
|
if (tree)
|
|
|
|
{
|
|
|
|
/* Set port tree */
|
|
|
|
tf = proto_tree_add_text(tree, tvb, offset, (tempLen + 2), "Port Description = %s", tempStr);
|
|
|
|
port_desc_tree = proto_item_add_subtree(tf, ett_port_description);
|
|
|
|
|
|
|
|
proto_tree_add_item(port_desc_tree, hf_lldp_tlv_type, tvb, offset, 2, FALSE);
|
|
|
|
proto_tree_add_item(port_desc_tree, hf_lldp_tlv_len, tvb, offset, 2, FALSE);
|
|
|
|
|
|
|
|
/* Display port description information */
|
|
|
|
proto_tree_add_text(port_desc_tree, tvb, (offset+2), tempLen, "Port Description: %s", tempStr);
|
|
|
|
}
|
|
|
|
|
|
|
|
return (tempLen + 2);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Dissect System Name and description TLV */
|
|
|
|
static gint32
|
|
|
|
dissect_lldp_system_name(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, guint32 offset)
|
|
|
|
{
|
|
|
|
guint16 tempShort;
|
|
|
|
guint32 tempLen = 0;
|
|
|
|
guint8 tempStr[255];
|
|
|
|
guint8 tempType;
|
|
|
|
|
|
|
|
proto_tree *system_name_tree = NULL;
|
|
|
|
proto_item *tf = NULL;
|
|
|
|
|
|
|
|
/* Get tlv type and length */
|
|
|
|
tempShort = tvb_get_ntohs(tvb, offset);
|
|
|
|
tempType = TLV_TYPE(tempShort);
|
|
|
|
|
|
|
|
/* Get tlv length and string field */
|
|
|
|
tempLen = TLV_INFO_LEN(tempShort);
|
|
|
|
tvb_memcpy(tvb, tempStr, (offset+2), tempLen);
|
|
|
|
tempStr[tempLen] = '\0';
|
|
|
|
|
|
|
|
if (tree)
|
|
|
|
{
|
|
|
|
/* Set system name tree */
|
|
|
|
if (tempType == SYSTEM_NAME_TLV_TYPE)
|
|
|
|
tf = proto_tree_add_text(tree, tvb, offset, (tempLen + 2), "System Name = %s", tempStr);
|
|
|
|
else
|
|
|
|
tf = proto_tree_add_text(tree, tvb, offset, (tempLen + 2), "System Description");
|
|
|
|
system_name_tree = proto_item_add_subtree(tf, ett_system_name);
|
|
|
|
|
|
|
|
proto_tree_add_item(system_name_tree, hf_lldp_tlv_type, tvb, offset, 2, FALSE);
|
|
|
|
proto_tree_add_item(system_name_tree, hf_lldp_tlv_len, tvb, offset, 2, FALSE);
|
|
|
|
|
|
|
|
/* Display system name information */
|
|
|
|
proto_tree_add_text(system_name_tree, tvb, (offset+2), tempLen, "%s = %s",
|
|
|
|
((tempType == SYSTEM_NAME_TLV_TYPE) ? "System Name" : "System Description"),
|
|
|
|
tempStr);
|
|
|
|
}
|
|
|
|
|
|
|
|
return (tempLen + 2);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Dissect System Capabilities TLV */
|
|
|
|
static gint32
|
|
|
|
dissect_lldp_system_capabilities(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, guint32 offset)
|
|
|
|
{
|
|
|
|
guint16 tempShort;
|
|
|
|
guint32 tempLen = 0;
|
|
|
|
guint16 tempCapability;
|
|
|
|
|
|
|
|
proto_tree *system_capabilities_tree = NULL;
|
|
|
|
proto_tree *capabilities_summary_tree = NULL;
|
|
|
|
proto_tree *capabilities_enabled_tree = NULL;
|
|
|
|
proto_item *tf = NULL;
|
|
|
|
|
|
|
|
/* Get tlv type and length */
|
|
|
|
tempShort = tvb_get_ntohs(tvb, offset);
|
|
|
|
|
|
|
|
/* Get tlv length */
|
|
|
|
tempLen = TLV_INFO_LEN(tempShort);
|
|
|
|
|
|
|
|
/* Get system capabilities */
|
|
|
|
tempCapability = tvb_get_ntohs(tvb, (offset+2));
|
|
|
|
|
|
|
|
if (tree)
|
|
|
|
{
|
|
|
|
/* Set system capabilities tree */
|
|
|
|
tf = proto_tree_add_text(tree, tvb, offset, (tempLen + 2), "Capabilities");
|
|
|
|
system_capabilities_tree = proto_item_add_subtree(tf, ett_system_cap);
|
|
|
|
|
|
|
|
proto_tree_add_item(system_capabilities_tree, hf_lldp_tlv_type, tvb, offset, 2, FALSE);
|
|
|
|
proto_tree_add_item(system_capabilities_tree, hf_lldp_tlv_len, tvb, offset, 2, FALSE);
|
|
|
|
|
|
|
|
/* Display system capability information */
|
|
|
|
tf = proto_tree_add_text(system_capabilities_tree, tvb, (offset+2), 2, "Capabilities: 0x%04x", tempCapability);
|
|
|
|
capabilities_summary_tree = proto_item_add_subtree(tf, ett_system_cap_summary);
|
|
|
|
/* Add capabilities to summary tree */
|
|
|
|
if (tempCapability & SYSTEM_CAPABILITY_OTHER)
|
|
|
|
proto_tree_add_text(capabilities_summary_tree, tvb, (offset+2), 2, "%s",
|
|
|
|
decode_boolean_bitfield(tempCapability, SYSTEM_CAPABILITY_OTHER,
|
|
|
|
16, "Other", ""));
|
|
|
|
if (tempCapability & SYSTEM_CAPABILITY_REPEATER)
|
|
|
|
proto_tree_add_text(capabilities_summary_tree, tvb, (offset+2), 2, "%s",
|
|
|
|
decode_boolean_bitfield(tempCapability, SYSTEM_CAPABILITY_REPEATER,
|
|
|
|
16, "Repeater", ""));
|
|
|
|
if (tempCapability & SYSTEM_CAPABILITY_BRIDGE)
|
|
|
|
proto_tree_add_text(capabilities_summary_tree, tvb, (offset+2), 2, "%s",
|
|
|
|
decode_boolean_bitfield(tempCapability, SYSTEM_CAPABILITY_BRIDGE,
|
|
|
|
16, "Bridge", ""));
|
|
|
|
if (tempCapability & SYSTEM_CAPABILITY_WLAN)
|
|
|
|
proto_tree_add_text(capabilities_summary_tree, tvb, (offset+2), 2, "%s",
|
|
|
|
decode_boolean_bitfield(tempCapability, SYSTEM_CAPABILITY_WLAN,
|
|
|
|
16, "WLAN access point", ""));
|
|
|
|
if (tempCapability & SYSTEM_CAPABILITY_ROUTER)
|
|
|
|
proto_tree_add_text(capabilities_summary_tree, tvb, (offset+2), 2, "%s",
|
|
|
|
decode_boolean_bitfield(tempCapability, SYSTEM_CAPABILITY_ROUTER,
|
|
|
|
16, "Router", ""));
|
|
|
|
if (tempCapability & SYSTEM_CAPABILITY_TELEPHONE)
|
|
|
|
proto_tree_add_text(capabilities_summary_tree, tvb, (offset+2), 2, "%s",
|
|
|
|
decode_boolean_bitfield(tempCapability, SYSTEM_CAPABILITY_TELEPHONE,
|
|
|
|
16, "Telephone", ""));
|
|
|
|
if (tempCapability & SYSTEM_CAPABILITY_DOCSIS)
|
|
|
|
proto_tree_add_text(capabilities_summary_tree, tvb, (offset+2), 2, "%s",
|
|
|
|
decode_boolean_bitfield(tempCapability, SYSTEM_CAPABILITY_DOCSIS,
|
|
|
|
16, "DOCSIS cable device", ""));
|
|
|
|
if (tempCapability & SYSTEM_CAPABILITY_STATION)
|
|
|
|
proto_tree_add_text(capabilities_summary_tree, tvb, (offset+2), 2, "%s",
|
|
|
|
decode_boolean_bitfield(tempCapability, SYSTEM_CAPABILITY_STATION,
|
|
|
|
16, "Station only", ""));
|
|
|
|
|
|
|
|
/* Get enabled summary */
|
|
|
|
tempShort = tvb_get_ntohs(tvb, (offset+4));
|
|
|
|
|
|
|
|
/* Display system capability information */
|
|
|
|
tf = proto_tree_add_text(system_capabilities_tree, tvb, (offset+4), 2, "Enabled Capabilities: 0x%04x", tempShort);
|
|
|
|
capabilities_enabled_tree = proto_item_add_subtree(tf, ett_system_cap_enabled);
|
|
|
|
/* Add capabilities to summary tree */
|
|
|
|
if (tempShort & SYSTEM_CAPABILITY_OTHER)
|
|
|
|
proto_tree_add_text(capabilities_enabled_tree, tvb, (offset+4), 2, "%s",
|
|
|
|
decode_boolean_bitfield(tempShort, SYSTEM_CAPABILITY_OTHER,
|
|
|
|
16, "Other", ""));
|
|
|
|
if (tempShort & SYSTEM_CAPABILITY_REPEATER)
|
|
|
|
proto_tree_add_text(capabilities_enabled_tree, tvb, (offset+4), 2, "%s",
|
|
|
|
decode_boolean_bitfield(tempShort, SYSTEM_CAPABILITY_REPEATER,
|
|
|
|
16, "Repeater", ""));
|
|
|
|
if (tempShort & SYSTEM_CAPABILITY_BRIDGE)
|
|
|
|
proto_tree_add_text(capabilities_enabled_tree, tvb, (offset+4), 2, "%s",
|
|
|
|
decode_boolean_bitfield(tempShort, SYSTEM_CAPABILITY_BRIDGE,
|
|
|
|
16, "Bridge", ""));
|
|
|
|
if (tempShort & SYSTEM_CAPABILITY_WLAN)
|
|
|
|
proto_tree_add_text(capabilities_enabled_tree, tvb, (offset+4), 2, "%s",
|
|
|
|
decode_boolean_bitfield(tempShort, SYSTEM_CAPABILITY_WLAN,
|
|
|
|
16, "WLAN access point", ""));
|
|
|
|
if (tempShort & SYSTEM_CAPABILITY_ROUTER)
|
|
|
|
proto_tree_add_text(capabilities_enabled_tree, tvb, (offset+4), 2, "%s",
|
|
|
|
decode_boolean_bitfield(tempShort, SYSTEM_CAPABILITY_ROUTER,
|
|
|
|
16, "Router", ""));
|
|
|
|
if (tempShort & SYSTEM_CAPABILITY_TELEPHONE)
|
|
|
|
proto_tree_add_text(capabilities_enabled_tree, tvb, (offset+4), 2, "%s",
|
|
|
|
decode_boolean_bitfield(tempShort, SYSTEM_CAPABILITY_TELEPHONE,
|
|
|
|
16, "Telephone", ""));
|
|
|
|
if (tempShort & SYSTEM_CAPABILITY_DOCSIS)
|
|
|
|
proto_tree_add_text(capabilities_enabled_tree, tvb, (offset+4), 2, "%s",
|
|
|
|
decode_boolean_bitfield(tempShort, SYSTEM_CAPABILITY_DOCSIS,
|
|
|
|
16, "DOCSIS cable device", ""));
|
|
|
|
if (tempShort & SYSTEM_CAPABILITY_STATION)
|
|
|
|
proto_tree_add_text(capabilities_enabled_tree, tvb, (offset+4), 2, "%s",
|
|
|
|
decode_boolean_bitfield(tempShort, SYSTEM_CAPABILITY_STATION,
|
|
|
|
16, "Station only", ""));
|
|
|
|
}
|
|
|
|
|
|
|
|
return (tempLen + 2);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Dissect Management Address TLV */
|
|
|
|
static gint32
|
|
|
|
dissect_lldp_management_address(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, guint32 offset)
|
|
|
|
{
|
|
|
|
guint16 tempShort;
|
|
|
|
guint32 tempLen = 0;
|
|
|
|
guint8 tempByte;
|
|
|
|
guint8 stringLen = 0;
|
|
|
|
guint8 tempAddr[129];
|
|
|
|
guint32 tempOffset = offset;
|
|
|
|
guint32 tempLong;
|
|
|
|
|
|
|
|
proto_tree *system_mgm_addr = NULL;
|
|
|
|
proto_item *tf = NULL;
|
|
|
|
|
|
|
|
/* Get tlv type and length */
|
|
|
|
tempShort = tvb_get_ntohs(tvb, tempOffset);
|
|
|
|
|
|
|
|
/* Get tlv length */
|
|
|
|
tempLen = TLV_INFO_LEN(tempShort);
|
|
|
|
|
|
|
|
if (tree)
|
|
|
|
{
|
|
|
|
/* Set system capabilities tree */
|
|
|
|
tf = proto_tree_add_text(tree, tvb, tempOffset, (tempLen + 2), "Management Address");
|
|
|
|
system_mgm_addr = proto_item_add_subtree(tf, ett_management_address);
|
|
|
|
|
|
|
|
proto_tree_add_item(system_mgm_addr, hf_lldp_tlv_type, tvb, tempOffset, 2, FALSE);
|
|
|
|
proto_tree_add_item(system_mgm_addr, hf_lldp_tlv_len, tvb, tempOffset, 2, FALSE);
|
|
|
|
|
|
|
|
tempOffset += 2;
|
|
|
|
|
|
|
|
/* Get management address string length */
|
|
|
|
stringLen = tvb_get_guint8(tvb, tempOffset);
|
|
|
|
proto_tree_add_text(system_mgm_addr, tvb, tempOffset, 1, "Address String Length: %u", stringLen);
|
|
|
|
|
|
|
|
tempOffset++;
|
|
|
|
|
|
|
|
/* Get management address subtype */
|
|
|
|
tempByte = tvb_get_guint8(tvb, tempOffset);
|
|
|
|
proto_tree_add_text(system_mgm_addr, tvb, tempOffset, 1, "Address Subtype: %s (%u)",
|
|
|
|
val_to_str(tempByte, management_addr_values, "Undefined"),
|
|
|
|
tempByte);
|
|
|
|
|
|
|
|
tempOffset++;
|
|
|
|
|
|
|
|
/* Get address */
|
|
|
|
switch (tempByte)
|
|
|
|
{
|
|
|
|
case 1: /* IPv4 */
|
|
|
|
tvb_memcpy(tvb, (guint8 *)&tempLong, tempOffset, 4);
|
|
|
|
proto_tree_add_ipv4(system_mgm_addr, hf_mgn_addr_ipv4, tvb, tempOffset, 4, tempLong);
|
|
|
|
break;
|
|
|
|
case 2: /* IPv6 */
|
|
|
|
tvb_memcpy(tvb, tempAddr, tempOffset, (stringLen-1));
|
|
|
|
proto_tree_add_text(system_mgm_addr, tvb, tempOffset, (stringLen-1),
|
|
|
|
"Address: %s", ip6_to_str((struct e_in6_addr *)tempAddr));
|
|
|
|
proto_tree_add_item_hidden(system_mgm_addr, hf_mgn_addr_hex, tvb, tempOffset, (stringLen-1), FALSE);
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
proto_tree_add_item(system_mgm_addr, hf_mgn_addr_hex, tvb, tempOffset, (stringLen-1), FALSE);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
tempOffset += (stringLen-1);
|
|
|
|
|
|
|
|
/* Get interface numbering subtype */
|
|
|
|
tempByte = tvb_get_guint8(tvb, tempOffset);
|
|
|
|
proto_tree_add_text(system_mgm_addr, tvb, tempOffset, 1, "Interface Subtype: %s (%u)",
|
|
|
|
val_to_str(tempByte, interface_subtype_values, "Undefined"),
|
|
|
|
tempByte);
|
|
|
|
|
|
|
|
tempOffset++;
|
|
|
|
|
|
|
|
/* Get interface number */
|
|
|
|
tempLong = tvb_get_ntohl(tvb, tempOffset);
|
|
|
|
proto_tree_add_text(system_mgm_addr, tvb, tempOffset, 4, "Interface Number: %u", tempLong);
|
|
|
|
|
|
|
|
tempOffset += 4;
|
|
|
|
|
|
|
|
/* Get OID string length */
|
|
|
|
stringLen = tvb_get_guint8(tvb, tempOffset);
|
|
|
|
proto_tree_add_text(system_mgm_addr, tvb, tempOffset, 1, "OID String Length: %u", stringLen);
|
|
|
|
|
|
|
|
if (stringLen > 0)
|
|
|
|
{
|
|
|
|
tempOffset++;
|
|
|
|
|
|
|
|
/* Get OID identifier */
|
|
|
|
proto_tree_add_item(system_mgm_addr, hf_mgn_obj_id, tvb, tempOffset, stringLen, FALSE);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return (tempLen + 2);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Dissect IEEE 802.1 TLVs */
|
|
|
|
static void
|
|
|
|
dissect_ieee_802_1_tlv(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, guint32 offset)
|
|
|
|
{
|
|
|
|
guint8 subType;
|
|
|
|
guint8 tempByte;
|
|
|
|
guint16 tempShort;
|
|
|
|
guint32 tempOffset = offset;
|
|
|
|
guint8 tempStr[255];
|
|
|
|
|
|
|
|
proto_tree *vlan_flags = NULL;
|
|
|
|
proto_item *tf = NULL;
|
|
|
|
|
|
|
|
/* Get subtype */
|
|
|
|
subType = tvb_get_guint8(tvb, tempOffset);
|
|
|
|
|
|
|
|
if (tree)
|
|
|
|
proto_tree_add_item(tree, hf_ieee_802_1_subtype, tvb, tempOffset, 1, FALSE);
|
|
|
|
|
|
|
|
tempOffset++;
|
|
|
|
|
|
|
|
switch (subType)
|
|
|
|
{
|
|
|
|
case 0x01: /* Port VLAN ID */
|
|
|
|
{
|
|
|
|
/* Get port vland id */
|
|
|
|
tempShort = tvb_get_ntohs(tvb, tempOffset);
|
|
|
|
if (tree)
|
|
|
|
proto_tree_add_text(tree, tvb, tempOffset, 2, "Port VLAN Identifier: 0x%04X", tempShort);
|
|
|
|
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
case 0x02: /* Port and Protocol VLAN ID */
|
|
|
|
{
|
|
|
|
/* Get flags */
|
|
|
|
tempByte = tvb_get_guint8(tvb, tempOffset);
|
|
|
|
if (tree)
|
|
|
|
{
|
|
|
|
tf = proto_tree_add_text(tree, tvb, tempOffset, 1, "Flags: 0x%02x", tempByte);
|
|
|
|
vlan_flags = proto_item_add_subtree(tf, ett_port_vlan_flags);
|
|
|
|
|
|
|
|
/* Get supported flag */
|
|
|
|
proto_tree_add_text(vlan_flags, tvb, tempOffset, 1, "%s",
|
|
|
|
decode_boolean_bitfield(tempByte, 0x01, 8, "Port and Protocol VLAN: Supported",
|
|
|
|
"Port and Protocol VLAN: Not Supported"));
|
|
|
|
|
|
|
|
/* Get enabled flag */
|
|
|
|
proto_tree_add_text(vlan_flags, tvb, tempOffset, 1, "%s",
|
|
|
|
decode_boolean_bitfield(tempByte, 0x02, 8, "Port and Protocol VLAN: Enabled",
|
|
|
|
"Port and Protocol VLAN: Not Enabled"));
|
|
|
|
}
|
|
|
|
|
|
|
|
tempOffset++;
|
|
|
|
|
|
|
|
/* Get port and protocol vlan id */
|
|
|
|
tempShort = tvb_get_ntohs(tvb, tempOffset);
|
|
|
|
if (tree)
|
|
|
|
proto_tree_add_text(tree, tvb, tempOffset, 2, "Port and Protocol VLAN Identifier: 0x%04X", tempShort);
|
|
|
|
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
case 0x03: /* VLAN Name */
|
|
|
|
{
|
|
|
|
/* Get vlan id */
|
|
|
|
tempShort = tvb_get_ntohs(tvb, tempOffset);
|
|
|
|
if (tree)
|
|
|
|
proto_tree_add_text(tree, tvb, tempOffset, 2, "VLAN Identifier: 0x%04X", tempShort);
|
|
|
|
|
|
|
|
tempOffset += 2;
|
|
|
|
|
|
|
|
/* Get vlan name length */
|
|
|
|
tempByte = tvb_get_guint8(tvb, tempOffset);
|
|
|
|
if (tree)
|
|
|
|
proto_tree_add_text(tree, tvb, tempOffset, 1, "VLAN Name Length: %u", tempByte);
|
|
|
|
|
|
|
|
tempOffset++;
|
|
|
|
|
|
|
|
if (tempByte > 0)
|
|
|
|
{
|
|
|
|
tvb_memcpy(tvb, tempStr, tempOffset, tempByte);
|
|
|
|
tempStr[tempByte] = '\0';
|
|
|
|
if (tree)
|
|
|
|
proto_tree_add_text(tree, tvb, tempOffset, tempByte, "VLAN Name: %s", tempStr);
|
|
|
|
}
|
|
|
|
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
case 0x04: /* Protocol ID */
|
|
|
|
{
|
|
|
|
/* Get protocal id length */
|
|
|
|
tempByte = tvb_get_guint8(tvb, tempOffset);
|
|
|
|
if (tree)
|
|
|
|
proto_tree_add_text(tree, tvb, tempOffset, 1, "Protocol Identity Length: %u", tempByte);
|
|
|
|
|
|
|
|
tempOffset++;
|
|
|
|
|
|
|
|
if (tempByte > 0)
|
|
|
|
{
|
|
|
|
if (tree)
|
2005-09-15 03:09:40 +00:00
|
|
|
proto_tree_add_text(tree, tvb, tempOffset, tempByte, "Protocol Identity: %s",
|
|
|
|
tvb_bytes_to_str(tvb, tempOffset, tempByte));
|
2005-09-14 19:06:54 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Dissect IEEE 802.3 TLVs */
|
|
|
|
static void
|
|
|
|
dissect_ieee_802_3_tlv(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, guint32 offset)
|
|
|
|
{
|
|
|
|
guint8 subType;
|
|
|
|
guint8 tempByte;
|
|
|
|
guint16 tempShort;
|
|
|
|
guint32 tempLong;
|
|
|
|
guint32 tempOffset = offset;
|
|
|
|
|
|
|
|
proto_tree *mac_phy_flags = NULL;
|
|
|
|
proto_item *tf = NULL;
|
|
|
|
|
|
|
|
/* Get subtype */
|
|
|
|
subType = tvb_get_guint8(tvb, tempOffset);
|
|
|
|
|
|
|
|
if (tree)
|
|
|
|
proto_tree_add_item(tree, hf_ieee_802_3_subtype, tvb, tempOffset, 1, FALSE);
|
|
|
|
|
|
|
|
tempOffset++;
|
|
|
|
|
|
|
|
switch (subType)
|
|
|
|
{
|
|
|
|
case 0x01: /* MAC/PHY Configuration/Status */
|
|
|
|
{
|
|
|
|
/* Get auto-negotiation info */
|
|
|
|
tempByte = tvb_get_guint8(tvb, tempOffset);
|
|
|
|
if (tree)
|
|
|
|
{
|
|
|
|
tf = proto_tree_add_text(tree, tvb, tempOffset, 1, "Auto-Negotiation Support/Status: 0x%02x", tempByte);
|
|
|
|
mac_phy_flags = proto_item_add_subtree(tf, ett_802_3_flags);
|
|
|
|
|
|
|
|
/* Get supported flag */
|
|
|
|
proto_tree_add_text(mac_phy_flags, tvb, tempOffset, 1, "%s",
|
|
|
|
decode_boolean_bitfield(tempByte, 0x01, 8, "Auto-Negotiation: Supported",
|
|
|
|
"Auto-Negotiation: Not Supported"));
|
|
|
|
|
|
|
|
/* Get enabled flag */
|
|
|
|
proto_tree_add_text(mac_phy_flags, tvb, tempOffset, 1, "%s",
|
|
|
|
decode_boolean_bitfield(tempByte, 0x02, 8, "Auto-Negotiation: Enabled",
|
|
|
|
"Auto-Negotiation: Not Enabled"));
|
|
|
|
}
|
|
|
|
|
|
|
|
tempOffset++;
|
|
|
|
|
|
|
|
/* Get pmd auto-negotiation advertised capability */
|
|
|
|
tempShort = tvb_get_ntohs(tvb, tempOffset);
|
|
|
|
if (tree)
|
|
|
|
proto_tree_add_text(tree, tvb, tempOffset, 2, "PMD Auto-Negotiation Advertised Capability: 0x%04X", tempShort);
|
|
|
|
|
|
|
|
tempOffset += 2;
|
|
|
|
|
|
|
|
/* Get operational MAU type */
|
|
|
|
tempShort = tvb_get_ntohs(tvb, tempOffset);
|
|
|
|
if (tree)
|
|
|
|
proto_tree_add_text(tree, tvb, tempOffset, 2, "Operational MAU Type: 0x%04X", tempShort);
|
|
|
|
|
|
|
|
tempOffset += 2;
|
|
|
|
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
case 0x02: /* MDI Power Support */
|
|
|
|
{
|
|
|
|
/* Get MDI power support info */
|
|
|
|
tempByte = tvb_get_guint8(tvb, tempOffset);
|
|
|
|
if (tree)
|
|
|
|
{
|
|
|
|
tf = proto_tree_add_text(tree, tvb, tempOffset, 1, "MDI Power Support: 0x%02x", tempByte);
|
|
|
|
mac_phy_flags = proto_item_add_subtree(tf, ett_802_3_power);
|
|
|
|
|
|
|
|
/* Get port class */
|
|
|
|
proto_tree_add_text(mac_phy_flags, tvb, tempOffset, 1, "%s",
|
|
|
|
decode_boolean_bitfield(tempByte, 0x01, 8, "Port Class: PSE",
|
|
|
|
"Port Class: PD"));
|
|
|
|
|
|
|
|
/* Get PSE MDI power support */
|
|
|
|
proto_tree_add_text(mac_phy_flags, tvb, tempOffset, 1, "%s",
|
|
|
|
decode_boolean_bitfield(tempByte, 0x02, 8, "PSE MDI Power: Supported",
|
|
|
|
"PSE MDI Power: Not Supported"));
|
|
|
|
|
|
|
|
/* Get PSE MDI power state */
|
|
|
|
proto_tree_add_text(mac_phy_flags, tvb, tempOffset, 1, "%s",
|
|
|
|
decode_boolean_bitfield(tempByte, 0x04, 8, "PSE MDI Power Enabled: Yes",
|
|
|
|
"PSE MDI Power Enabled: No"));
|
|
|
|
|
|
|
|
/* Get PSE pairs control ability */
|
|
|
|
proto_tree_add_text(mac_phy_flags, tvb, tempOffset, 1, "%s",
|
|
|
|
decode_boolean_bitfield(tempByte, 0x08, 8, "PSE Pairs Control Ability: Yes",
|
|
|
|
"PSE Pairs Control Ability: No"));
|
|
|
|
}
|
|
|
|
|
|
|
|
tempOffset++;
|
|
|
|
|
|
|
|
/* Get PSE power pair */
|
|
|
|
tempByte = tvb_get_guint8(tvb, tempOffset);
|
|
|
|
if (tree)
|
|
|
|
proto_tree_add_text(tree, tvb, tempOffset, 1, "PSE Power Pair: %u", tempByte);
|
|
|
|
|
|
|
|
tempOffset++;
|
|
|
|
|
|
|
|
/* Get power class */
|
|
|
|
tempByte = tvb_get_guint8(tvb, tempOffset);
|
|
|
|
if (tree)
|
|
|
|
proto_tree_add_text(tree, tvb, tempOffset, 1, "Power Class: %u", tempByte);
|
|
|
|
|
|
|
|
tempOffset++;
|
|
|
|
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
case 0x03: /* Link Aggregation */
|
|
|
|
{
|
|
|
|
/* Get aggregation status */
|
|
|
|
tempByte = tvb_get_guint8(tvb, tempOffset);
|
|
|
|
if (tree)
|
|
|
|
{
|
|
|
|
tf = proto_tree_add_text(tree, tvb, tempOffset, 1, "Aggregation Status: 0x%02x", tempByte);
|
|
|
|
mac_phy_flags = proto_item_add_subtree(tf, ett_802_3_aggregation);
|
|
|
|
|
|
|
|
/* Get aggregation capability */
|
|
|
|
proto_tree_add_text(mac_phy_flags, tvb, tempOffset, 1, "%s",
|
|
|
|
decode_boolean_bitfield(tempByte, 0x01, 8, "Aggregation Capability: Yes",
|
|
|
|
"Aggregation Capability: No"));
|
|
|
|
|
|
|
|
/* Get aggregation status */
|
|
|
|
proto_tree_add_text(mac_phy_flags, tvb, tempOffset, 1, "%s",
|
|
|
|
decode_boolean_bitfield(tempByte, 0x02, 8, "Aggregation Status: Enabled",
|
|
|
|
"Aggregation Status: Not Enabled"));
|
|
|
|
}
|
|
|
|
|
|
|
|
tempOffset++;
|
|
|
|
|
|
|
|
/* Get aggregated port id */
|
|
|
|
tempLong = tvb_get_ntohl(tvb, tempOffset);
|
|
|
|
if (tree)
|
|
|
|
proto_tree_add_text(tree, tvb, tempOffset, 4, "Aggregated Port Id: %u", tempLong);
|
|
|
|
|
|
|
|
tempOffset += 4;
|
|
|
|
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
case 0x04: /* Maximum Frame Size */
|
|
|
|
{
|
|
|
|
/* Get maximum frame size */
|
|
|
|
tempShort = tvb_get_ntohs(tvb, tempOffset);
|
|
|
|
if (tree)
|
|
|
|
proto_tree_add_text(tree, tvb, tempOffset, 2, "Maximum Frame Size: %u", tempShort);
|
|
|
|
|
|
|
|
tempOffset += 2;
|
|
|
|
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Dissect Media TLVs */
|
|
|
|
static void
|
|
|
|
dissect_media_tlv(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, guint32 offset, guint16 tlvLen)
|
|
|
|
{
|
|
|
|
guint32 tempOffset = offset;
|
|
|
|
guint8 subType;
|
|
|
|
guint16 tempShort;
|
|
|
|
guint16 tempVLAN;
|
|
|
|
guint8 tempByte;
|
|
|
|
guint32 tempLong;
|
|
|
|
guint8 tempStr[255];
|
2005-09-15 03:09:40 +00:00
|
|
|
const char *strPtr;
|
2005-09-14 19:06:54 +00:00
|
|
|
guint32 LCI_Length;
|
|
|
|
guint64 temp64bit = 0;
|
|
|
|
|
|
|
|
proto_tree *media_flags = NULL;
|
|
|
|
proto_item *tf = NULL;
|
|
|
|
|
|
|
|
/* Get subtype */
|
|
|
|
subType = tvb_get_guint8(tvb, tempOffset);
|
|
|
|
|
|
|
|
if (tree)
|
|
|
|
proto_tree_add_item(tree, hf_media_tlv_subtype, tvb, tempOffset, 1, FALSE);
|
|
|
|
|
|
|
|
tempOffset++;
|
|
|
|
|
|
|
|
switch (subType)
|
|
|
|
{
|
|
|
|
case 1: /* LLDP-MED Capabilities */
|
|
|
|
{
|
|
|
|
/* Get capabilities */
|
|
|
|
tempShort = tvb_get_ntohs(tvb, tempOffset);
|
|
|
|
if (tree)
|
|
|
|
{
|
|
|
|
tf = proto_tree_add_text(tree, tvb, tempOffset, 2, "Capabilities: 0x%04x", tempShort);
|
|
|
|
media_flags = proto_item_add_subtree(tf, ett_media_capabilities);
|
|
|
|
if (tempShort & MEDIA_CAPABILITY_LLDP)
|
|
|
|
proto_tree_add_text(media_flags, tvb, tempOffset, 2, "%s",
|
|
|
|
decode_boolean_bitfield(tempShort, MEDIA_CAPABILITY_LLDP, 16,
|
|
|
|
"LLDP-MED Capabilities", ""));
|
|
|
|
if (tempShort & MEDIA_CAPABILITY_NETWORK_POLICY)
|
|
|
|
proto_tree_add_text(media_flags, tvb, tempOffset, 2, "%s",
|
|
|
|
decode_boolean_bitfield(tempShort, MEDIA_CAPABILITY_NETWORK_POLICY, 16,
|
|
|
|
"Network Policy", ""));
|
|
|
|
if (tempShort & MEDIA_CAPABILITY_LOCATION_ID)
|
|
|
|
proto_tree_add_text(media_flags, tvb, tempOffset, 2, "%s",
|
|
|
|
decode_boolean_bitfield(tempShort, MEDIA_CAPABILITY_LOCATION_ID, 16,
|
|
|
|
"Location Identification", ""));
|
|
|
|
if (tempShort & MEDIA_CAPABILITY_MDI_PSE)
|
|
|
|
proto_tree_add_text(media_flags, tvb, tempOffset, 2, "%s",
|
|
|
|
decode_boolean_bitfield(tempShort, MEDIA_CAPABILITY_MDI_PSE, 16,
|
|
|
|
"Extended Power via MDI-PSE", ""));
|
|
|
|
if (tempShort & MEDIA_CAPABILITY_MDI_PD)
|
|
|
|
proto_tree_add_text(media_flags, tvb, tempOffset, 2, "%s",
|
|
|
|
decode_boolean_bitfield(tempShort, MEDIA_CAPABILITY_MDI_PD, 16,
|
|
|
|
"Extended Power via MDI-PD", ""));
|
|
|
|
if (tempShort & MEDIA_CAPABILITY_INVENTORY)
|
|
|
|
proto_tree_add_text(media_flags, tvb, tempOffset, 2, "%s",
|
|
|
|
decode_boolean_bitfield(tempShort, MEDIA_CAPABILITY_INVENTORY, 16,
|
|
|
|
"Inventory", ""));
|
|
|
|
}
|
|
|
|
|
|
|
|
tempOffset += 2;
|
|
|
|
|
|
|
|
/* Get Class type */
|
|
|
|
tempByte = tvb_get_guint8(tvb, tempOffset);
|
|
|
|
if (tree)
|
|
|
|
proto_tree_add_text(tree, tvb, tempOffset, 1, "Class Type: %s", val_to_str(tempByte, media_class_values, "Unknown"));
|
|
|
|
|
|
|
|
tempOffset++;
|
|
|
|
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
case 2: /* Network Policy */
|
|
|
|
{
|
|
|
|
/* Get application type */
|
|
|
|
tempByte = tvb_get_guint8(tvb, tempOffset);
|
|
|
|
if (tree)
|
|
|
|
proto_tree_add_text(tree, tvb, tempOffset, 1, "Applicaton Type: %s (%u)",
|
|
|
|
val_to_str(tempByte, media_application_type, "Unknown"), tempByte);
|
|
|
|
|
|
|
|
tempOffset++;
|
|
|
|
|
|
|
|
/* Get flags */
|
|
|
|
tempByte = tvb_get_guint8(tvb, tempOffset);
|
|
|
|
|
|
|
|
/* Unknown policy flag */
|
|
|
|
if (tree)
|
|
|
|
proto_tree_add_text(tree, tvb, tempOffset, 1, "%s",
|
|
|
|
decode_boolean_bitfield(tempByte, 0x80, 8,"Policy: Unknown", "Policy: Defined"));
|
|
|
|
|
|
|
|
/* Tagged flag */
|
|
|
|
if (tree)
|
|
|
|
proto_tree_add_text(tree, tvb, tempOffset, 1, "%s",
|
|
|
|
decode_boolean_bitfield(tempByte, 0x40, 8,"Tagged: Yes", "Tagged: No"));
|
|
|
|
|
|
|
|
/* Get vlan id */
|
|
|
|
tempShort = tvb_get_ntohs(tvb, tempOffset);
|
|
|
|
tempVLAN = (tempShort & 0x1FFE) >> 1;
|
|
|
|
if (tree)
|
|
|
|
proto_tree_add_text(tree, tvb, tempOffset, 2, "%s %u",
|
|
|
|
decode_boolean_bitfield(tempShort, 0x1FFE, 16, "VLAN Id:", "VLAN Id:"), tempVLAN);
|
|
|
|
|
|
|
|
tempOffset++;
|
|
|
|
|
|
|
|
/* Get L2 priority */
|
|
|
|
tempShort = tvb_get_ntohs(tvb, tempOffset);
|
|
|
|
if (tree)
|
|
|
|
proto_tree_add_text(tree, tvb, tempOffset, 2, "%s %u",
|
|
|
|
decode_boolean_bitfield(tempShort, 0x01C0, 16, "L2 Priority:", "L2 Priority:"),
|
|
|
|
((tempShort & 0x01C0) >> 6));
|
|
|
|
|
|
|
|
tempOffset++;
|
|
|
|
|
|
|
|
/* Get DSCP value */
|
|
|
|
tempByte = tvb_get_guint8(tvb, tempOffset);
|
|
|
|
if (tree)
|
|
|
|
proto_tree_add_text(tree, tvb, tempOffset, 1, "%s %u",
|
|
|
|
decode_boolean_bitfield(tempByte, 0x3F, 8, "DSCP Value:", "DSCP Value:"),
|
|
|
|
(tempByte & 0x3F));
|
|
|
|
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
case 3: /* Location Identification */
|
|
|
|
{
|
|
|
|
/* Get location data format */
|
|
|
|
tempByte = tvb_get_guint8(tvb, tempOffset);
|
|
|
|
if (tree)
|
|
|
|
proto_tree_add_text(tree, tvb, tempOffset, 1, "Location Data Format: %s (%u)",
|
|
|
|
val_to_str(tempByte, location_data_format, "Unknown"), tempByte);
|
|
|
|
|
|
|
|
tempOffset++;
|
|
|
|
|
|
|
|
switch (tempByte)
|
|
|
|
{
|
|
|
|
case 1: /* Coordinate-based LCI */
|
|
|
|
{
|
|
|
|
/* Get length of location id field */
|
|
|
|
tempLong = (guint32)(tlvLen - 5);
|
|
|
|
if (tempLong != 16)
|
|
|
|
{
|
|
|
|
if (tree)
|
|
|
|
proto_tree_add_text(tree, tvb, tempOffset, 1, "Location Id Length: %u (Invalid)", tempByte);
|
|
|
|
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Get latitude resolution */
|
|
|
|
tempByte = tvb_get_guint8(tvb, tempOffset);
|
|
|
|
if (tree)
|
|
|
|
proto_tree_add_text(tree, tvb, tempOffset, 1, "%s %u",
|
|
|
|
decode_boolean_bitfield(tempByte, 0xFC, 8, "Latitude Resolution:", "Latitude Resolution:"),
|
|
|
|
((tempByte & 0xFC) >> 2));
|
|
|
|
|
|
|
|
/* Get latitude */
|
2005-09-15 03:09:40 +00:00
|
|
|
temp64bit = tvb_get_ntoh64(tvb, tempOffset);
|
|
|
|
temp64bit = (temp64bit & G_GINT64_CONSTANT(0x03FFFFFFFF000000)) >> 24;
|
2005-09-14 19:06:54 +00:00
|
|
|
get_latitude_or_longitude(0,temp64bit,tempStr);
|
|
|
|
if (tree)
|
2005-09-15 03:09:40 +00:00
|
|
|
proto_tree_add_text(tree, tvb, tempOffset, 5, "Latitude: %s (0x%16" PRIX64 ")",
|
2005-09-14 19:06:54 +00:00
|
|
|
tempStr, temp64bit);
|
|
|
|
|
|
|
|
tempOffset += 5;
|
|
|
|
|
|
|
|
/* Get longitude resolution */
|
2005-09-15 03:09:40 +00:00
|
|
|
tempByte = tvb_get_guint8(tvb, tempOffset);
|
|
|
|
if (tree)
|
|
|
|
proto_tree_add_text(tree, tvb, tempOffset, 1, "%s %u",
|
|
|
|
decode_boolean_bitfield(tempByte, 0xFC, 8, "Longitude Resolution:", "Longitude Resolution:"),
|
|
|
|
((tempByte & 0xFC) >> 2));
|
|
|
|
|
2005-09-14 19:06:54 +00:00
|
|
|
/* Get longitude */
|
2005-09-15 03:09:40 +00:00
|
|
|
temp64bit = tvb_get_ntoh64(tvb, tempOffset);
|
|
|
|
temp64bit = (temp64bit & G_GINT64_CONSTANT(0x03FFFFFFFF000000)) >> 24;
|
2005-09-14 19:06:54 +00:00
|
|
|
get_latitude_or_longitude(1,temp64bit,tempStr);
|
|
|
|
if (tree)
|
2005-09-15 03:09:40 +00:00
|
|
|
proto_tree_add_text(tree, tvb, tempOffset, 5, "Longitude: %s (0x%16" PRIX64 ")",
|
2005-09-14 19:06:54 +00:00
|
|
|
tempStr,temp64bit);
|
|
|
|
|
|
|
|
tempOffset += 5;
|
|
|
|
|
|
|
|
/* Altitude Type */
|
|
|
|
tempByte = tvb_get_guint8(tvb, tempOffset);
|
|
|
|
if (tree)
|
|
|
|
{
|
|
|
|
tf = proto_tree_add_text(tree, tvb, tempOffset, 1, "%s",
|
|
|
|
decode_boolean_bitfield(tempByte, 0xF0, 8, "Altitude Type: ", "Altitude Type: "));
|
|
|
|
|
|
|
|
switch ((tempByte >> 4))
|
|
|
|
{
|
|
|
|
case 1:
|
|
|
|
proto_item_append_text(tf, "Meters (1)");
|
|
|
|
break;
|
|
|
|
case 2:
|
|
|
|
proto_item_append_text(tf, "Floors (2)");
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
proto_item_append_text(tf, " Unknown (%u)", (tempByte >> 4));
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Get Altitude Resolution */
|
|
|
|
tempShort = tvb_get_ntohs(tvb, tempOffset);
|
|
|
|
if (tree)
|
|
|
|
proto_tree_add_text(tree, tvb, tempOffset, 2, "%s %u",
|
|
|
|
decode_boolean_bitfield(tempShort, 0x0FC0, 16, "Altitude Resolution: ", "Altitude Type: "),
|
|
|
|
((tempShort & 0x0FC0) >> 6));
|
|
|
|
|
|
|
|
tempOffset++;
|
|
|
|
|
|
|
|
/* Get Altitude */
|
|
|
|
tempLong = (tvb_get_ntohl(tvb, tempOffset) & 0x03FFFFFFF);
|
|
|
|
if (tree)
|
|
|
|
proto_tree_add_text(tree, tvb, tempOffset, 4, "Altitude: 0x%08X", tempLong);
|
|
|
|
|
|
|
|
tempOffset += 4;
|
|
|
|
|
|
|
|
/* Get datum */
|
|
|
|
tempByte = tvb_get_guint8(tvb, tempOffset);
|
|
|
|
if (tree)
|
|
|
|
proto_tree_add_text(tree, tvb, tempOffset, 1, "Datum: %u", tempByte);
|
|
|
|
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
case 2: /* Civic Address LCI */
|
|
|
|
{
|
|
|
|
/* Get length of location id field */
|
|
|
|
tempLong = (guint32)(tlvLen - 5);
|
|
|
|
|
|
|
|
/* Get LCI length */
|
|
|
|
tempByte = tvb_get_guint8(tvb, tempOffset);
|
|
|
|
if (tempByte > tempLong)
|
|
|
|
{
|
|
|
|
if (tree)
|
|
|
|
proto_tree_add_text(tree, tvb, tempOffset, 1, "LCI Length: %u (Invalid)", tempByte);
|
|
|
|
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (tree)
|
|
|
|
proto_tree_add_text(tree, tvb, tempOffset, 1, "LCI Length: %u", tempByte);
|
|
|
|
|
|
|
|
LCI_Length = (guint32)tempByte;
|
|
|
|
|
|
|
|
tempOffset++;
|
|
|
|
|
|
|
|
/* Get what value */
|
|
|
|
tempByte = tvb_get_guint8(tvb, tempOffset);
|
|
|
|
if (tree)
|
|
|
|
proto_tree_add_text(tree, tvb, tempOffset, 1, "What: %s (%u)",
|
|
|
|
val_to_str(tempByte,civic_address_what_values,"Unknown"),
|
|
|
|
tempByte);
|
|
|
|
|
|
|
|
tempOffset++;
|
|
|
|
LCI_Length--;
|
|
|
|
|
|
|
|
/* Get country code */
|
|
|
|
tvb_memcpy(tvb, tempStr, tempOffset, 2);
|
|
|
|
tempStr[2] = '\0';
|
|
|
|
if (tree)
|
|
|
|
proto_tree_add_text(tree, tvb, tempOffset, 2, "Country: %s", tempStr);
|
|
|
|
|
|
|
|
tempOffset += 2;
|
|
|
|
LCI_Length -= 2;
|
|
|
|
|
|
|
|
while (LCI_Length > 0)
|
|
|
|
{
|
|
|
|
if (LCI_Length < 2)
|
|
|
|
return; /* Not enough bytes left to continue */
|
|
|
|
|
|
|
|
/* Get CA Type */
|
|
|
|
tempByte = tvb_get_guint8(tvb, tempOffset);
|
|
|
|
if (tree)
|
|
|
|
proto_tree_add_text(tree, tvb, tempOffset, 1, "CA Type: %s (%u)",
|
|
|
|
val_to_str(tempByte,civic_address_type_values,"Unknown"),
|
|
|
|
tempByte);
|
|
|
|
|
|
|
|
tempOffset++;
|
|
|
|
LCI_Length--;
|
|
|
|
|
|
|
|
/* Get CA Length */
|
|
|
|
tempByte = tvb_get_guint8(tvb, tempOffset);
|
|
|
|
if (tree)
|
|
|
|
proto_tree_add_text(tree, tvb, tempOffset, 1, "CA Length: %u", tempByte);
|
|
|
|
|
|
|
|
tempOffset++;
|
|
|
|
LCI_Length--;
|
|
|
|
|
|
|
|
/* Make sure the CA value is within the specified length */
|
|
|
|
if (tempByte > LCI_Length)
|
|
|
|
return;
|
|
|
|
|
|
|
|
if (tempByte > 0)
|
|
|
|
{
|
|
|
|
/* Get CA Value */
|
|
|
|
tvb_memcpy(tvb, tempStr, tempOffset, tempByte);
|
|
|
|
tempStr[tempByte] = '\0';
|
|
|
|
if (tree)
|
|
|
|
proto_tree_add_text(tree, tvb, tempOffset, tempByte, "CA Value: %s", tempStr);
|
|
|
|
|
|
|
|
tempOffset += tempByte;
|
|
|
|
LCI_Length -= tempByte;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
case 3: /* ECS ELIN */
|
|
|
|
{
|
|
|
|
tempLong = (guint32)(tlvLen - 5);
|
|
|
|
if (tempLong > 0)
|
|
|
|
{
|
|
|
|
tvb_memcpy(tvb, tempStr, tempOffset, tempLong);
|
|
|
|
tempStr[tempLong] = '\0';
|
|
|
|
if (tree)
|
|
|
|
proto_tree_add_text(tree, tvb, tempOffset, tempLong, "ELIN: %s", tempStr);
|
|
|
|
}
|
|
|
|
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
case 4: /* Extended Power-via-MDI */
|
|
|
|
{
|
|
|
|
/* Get first byte */
|
|
|
|
tempByte = tvb_get_guint8(tvb, tempOffset);
|
|
|
|
|
|
|
|
/* Determine power type */
|
|
|
|
subType = ((tempByte & 0xC0) >> 6);
|
|
|
|
if (tree)
|
|
|
|
proto_tree_add_text(tree, tvb, tempOffset, 1, "%s %s",
|
|
|
|
decode_boolean_bitfield(tempByte, 0xC0, 8, "Power Type:", "Power Type:"),
|
|
|
|
val_to_str(subType, media_power_type, "Unknown"));
|
|
|
|
|
|
|
|
/* Determine power source */
|
|
|
|
switch (subType)
|
|
|
|
{
|
|
|
|
case 0:
|
|
|
|
{
|
|
|
|
subType = ((tempByte & 0x30) >> 4);
|
2005-09-15 03:09:40 +00:00
|
|
|
strPtr = val_to_str(subType, media_power_pse_device, "Reserved");
|
2005-09-14 19:06:54 +00:00
|
|
|
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
case 1:
|
|
|
|
{
|
|
|
|
subType = ((tempByte & 0x30) >> 4);
|
2005-09-15 03:09:40 +00:00
|
|
|
strPtr = val_to_str(subType, media_power_pd_device, "Reserved");
|
2005-09-14 19:06:54 +00:00
|
|
|
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
default:
|
|
|
|
{
|
2005-09-15 03:09:40 +00:00
|
|
|
strPtr = "Unknown";
|
2005-09-14 19:06:54 +00:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (tree)
|
|
|
|
proto_tree_add_text(tree, tvb, tempOffset, 1, "%s %s",
|
|
|
|
decode_boolean_bitfield(tempByte, 0x30, 8, "Power Source:", "Power Source:"),
|
2005-09-15 03:09:40 +00:00
|
|
|
strPtr);
|
2005-09-14 19:06:54 +00:00
|
|
|
|
|
|
|
/* Determine power priority */
|
|
|
|
subType = (tempByte & 0x0F);
|
|
|
|
if (tree)
|
|
|
|
proto_tree_add_text(tree, tvb, tempOffset, 1, "%s %s",
|
|
|
|
decode_boolean_bitfield(tempByte, 0x0F, 8, "Power Priority:", "Power Priority:"),
|
|
|
|
val_to_str(subType, media_power_priority, "Reserved"));
|
|
|
|
|
|
|
|
tempOffset++;
|
|
|
|
|
|
|
|
/* Get power value */
|
|
|
|
tempShort = tvb_get_ntohs(tvb, tempOffset);
|
|
|
|
if (tree)
|
|
|
|
proto_tree_add_text(tree, tvb, tempOffset, 2, "Power Value: %u", tempShort);
|
|
|
|
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
case 5: /* Hardware Revision */
|
|
|
|
{
|
|
|
|
/* Figure out the length of the hardware revision field */
|
|
|
|
tempLong = (guint32)(tlvLen - 4);
|
|
|
|
if (tempLong > 0)
|
|
|
|
{
|
|
|
|
tvb_memcpy(tvb, tempStr, tempOffset, tempLong);
|
|
|
|
tempStr[tempLong] = '\0';
|
|
|
|
if (tree)
|
|
|
|
proto_tree_add_text(tree, tvb, tempOffset, tempLong, "Hardware Revision: %s", tempStr);
|
|
|
|
}
|
|
|
|
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
case 6: /* Firmware Revision */
|
|
|
|
{
|
|
|
|
/* Figure out the length of the firmware revision field */
|
|
|
|
tempLong = (guint32)(tlvLen - 4);
|
|
|
|
if (tempLong > 0)
|
|
|
|
{
|
|
|
|
tvb_memcpy(tvb, tempStr, tempOffset, tempLong);
|
|
|
|
tempStr[tempLong] = '\0';
|
|
|
|
if (tree)
|
|
|
|
proto_tree_add_text(tree, tvb, tempOffset, tempLong, "Firmware Revision: %s", tempStr);
|
|
|
|
}
|
|
|
|
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
case 7: /* Software Revision */
|
|
|
|
{
|
|
|
|
/* Figure out the length of the software revision field */
|
|
|
|
tempLong = (guint32)(tlvLen - 4);
|
|
|
|
if (tempLong > 0)
|
|
|
|
{
|
|
|
|
tvb_memcpy(tvb, tempStr, tempOffset, tempLong);
|
|
|
|
tempStr[tempLong] = '\0';
|
|
|
|
if (tree)
|
|
|
|
proto_tree_add_text(tree, tvb, tempOffset, tempLong, "Software Revision: %s", tempStr);
|
|
|
|
}
|
|
|
|
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
case 8: /* Serial Number */
|
|
|
|
{
|
|
|
|
/* Figure out the length of the serial number field */
|
|
|
|
tempLong = (guint32)(tlvLen - 4);
|
|
|
|
if (tempLong > 0)
|
|
|
|
{
|
|
|
|
tvb_memcpy(tvb, tempStr, tempOffset, tempLong);
|
|
|
|
tempStr[tempLong] = '\0';
|
|
|
|
if (tree)
|
|
|
|
proto_tree_add_text(tree, tvb, tempOffset, tempLong, "Serial Number: %s", tempStr);
|
|
|
|
}
|
|
|
|
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
case 9: /* Manufacturer Name */
|
|
|
|
{
|
|
|
|
/* Figure out the length of the manufacturer name field */
|
|
|
|
tempLong = (guint32)(tlvLen - 4);
|
|
|
|
if (tempLong > 0)
|
|
|
|
{
|
|
|
|
tvb_memcpy(tvb, tempStr, tempOffset, tempLong);
|
|
|
|
tempStr[tempLong] = '\0';
|
|
|
|
if (tree)
|
|
|
|
proto_tree_add_text(tree, tvb, tempOffset, tempLong, "Manufacturer Name: %s", tempStr);
|
|
|
|
}
|
|
|
|
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
case 10: /* Model Name */
|
|
|
|
{
|
|
|
|
/* Figure out the length of the model name field */
|
|
|
|
tempLong = (guint32)(tlvLen - 4);
|
|
|
|
if (tempLong > 0)
|
|
|
|
{
|
|
|
|
tvb_memcpy(tvb, tempStr, tempOffset, tempLong);
|
|
|
|
tempStr[tempLong] = '\0';
|
|
|
|
if (tree)
|
|
|
|
proto_tree_add_text(tree, tvb, tempOffset, tempLong, "Model Name: %s", tempStr);
|
|
|
|
}
|
|
|
|
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
case 11: /* Asset ID */
|
|
|
|
{
|
|
|
|
/* Figure out the length of the asset id field */
|
|
|
|
tempLong = (guint32)(tlvLen - 4);
|
|
|
|
if (tempLong > 0)
|
|
|
|
{
|
|
|
|
tvb_memcpy(tvb, tempStr, tempOffset, tempLong);
|
|
|
|
tempStr[tempLong] = '\0';
|
|
|
|
if (tree)
|
|
|
|
proto_tree_add_text(tree, tvb, tempOffset, tempLong, "Asset ID: %s", tempStr);
|
|
|
|
}
|
|
|
|
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Dissect Organizational Specific TLV */
|
|
|
|
static gint32
|
|
|
|
dissect_organizational_specific_tlv(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, guint32 offset)
|
|
|
|
{
|
|
|
|
guint16 tempLen;
|
|
|
|
guint16 tempShort;
|
|
|
|
guint32 oui;
|
|
|
|
guint8 subType;
|
|
|
|
char tempStr[255];
|
|
|
|
|
|
|
|
proto_tree *org_tlv_tree = NULL;
|
|
|
|
proto_item *tf = NULL;
|
|
|
|
|
|
|
|
/* Get tlv type and length */
|
|
|
|
tempShort = tvb_get_ntohs(tvb, offset);
|
|
|
|
|
|
|
|
/* Get tlv length */
|
|
|
|
tempLen = TLV_INFO_LEN(tempShort);
|
|
|
|
|
|
|
|
/* Get OUI value */
|
|
|
|
oui = tvb_get_ntoh24(tvb, (offset+2));
|
|
|
|
subType = tvb_get_guint8(tvb, (offset+5));
|
|
|
|
|
|
|
|
sprintf(tempStr,"%s - ",val_to_str(oui, tlv_oui_subtype_vals, "Unknown"));
|
|
|
|
switch(oui)
|
|
|
|
{
|
|
|
|
case OUI_IEEE_802_1:
|
|
|
|
strcat(tempStr,val_to_str(subType, ieee_802_1_subtypes, "Unknown"));
|
|
|
|
break;
|
|
|
|
case OUI_IEEE_802_3:
|
|
|
|
strcat(tempStr,val_to_str(subType, ieee_802_3_subtypes, "Unknown"));
|
|
|
|
break;
|
|
|
|
case OUI_MEDIA_ENDPOINT:
|
|
|
|
strcat(tempStr,val_to_str(subType, media_subtypes, "Unknown"));
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
strcat(tempStr,"Unknown");
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (tree)
|
|
|
|
{
|
|
|
|
tf = proto_tree_add_text(tree, tvb, offset, (tempLen + 2), "%s", tempStr);
|
|
|
|
org_tlv_tree = proto_item_add_subtree(tf, ett_org_spc_tlv);
|
|
|
|
|
|
|
|
proto_tree_add_item(org_tlv_tree, hf_lldp_tlv_type, tvb, offset, 2, FALSE);
|
|
|
|
proto_tree_add_item(org_tlv_tree, hf_lldp_tlv_len, tvb, offset, 2, FALSE);
|
|
|
|
|
|
|
|
/* Display organizational unique id */
|
|
|
|
proto_tree_add_uint(org_tlv_tree, hf_org_spc_oui, tvb, (offset+2), 3, oui);
|
|
|
|
}
|
|
|
|
|
|
|
|
switch (oui)
|
|
|
|
{
|
|
|
|
case OUI_IEEE_802_1:
|
|
|
|
dissect_ieee_802_1_tlv(tvb, pinfo, org_tlv_tree, (offset+5));
|
|
|
|
break;
|
|
|
|
case OUI_IEEE_802_3:
|
|
|
|
dissect_ieee_802_3_tlv(tvb, pinfo, org_tlv_tree, (offset+5));
|
|
|
|
break;
|
|
|
|
case OUI_MEDIA_ENDPOINT:
|
|
|
|
dissect_media_tlv(tvb, pinfo, org_tlv_tree, (offset+5), tempLen);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
return (tempLen + 2);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Dissect Unknown TLV */
|
|
|
|
static gint32
|
|
|
|
dissect_lldp_unknown_tlv(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, guint32 offset)
|
|
|
|
{
|
|
|
|
guint16 tempLen;
|
|
|
|
guint16 tempShort;
|
|
|
|
|
|
|
|
proto_tree *unknown_tlv_tree = NULL;
|
|
|
|
proto_item *tf = NULL;
|
|
|
|
|
|
|
|
/* Get tlv type and length */
|
|
|
|
tempShort = tvb_get_ntohs(tvb, offset);
|
|
|
|
|
|
|
|
/* Get tlv length */
|
|
|
|
tempLen = TLV_INFO_LEN(tempShort);
|
|
|
|
|
|
|
|
if (tree)
|
|
|
|
{
|
|
|
|
tf = proto_tree_add_text(tree, tvb, offset, (tempLen + 2), "Unknown TLV");
|
|
|
|
unknown_tlv_tree = proto_item_add_subtree(tf, ett_unknown_tlv);
|
|
|
|
|
|
|
|
proto_tree_add_item(unknown_tlv_tree, hf_lldp_tlv_type, tvb, offset, 2, FALSE);
|
|
|
|
proto_tree_add_item(unknown_tlv_tree, hf_lldp_tlv_len, tvb, offset, 2, FALSE);
|
|
|
|
}
|
|
|
|
|
|
|
|
return (tempLen + 2);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/* Dissect LLDP packets */
|
|
|
|
static void
|
|
|
|
dissect_lldp(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree)
|
|
|
|
{
|
|
|
|
proto_item *ti;
|
|
|
|
proto_tree *lldp_tree = NULL;
|
|
|
|
|
|
|
|
guint32 offset = 0;
|
|
|
|
gint32 rtnValue = 0;
|
|
|
|
guint16 tempShort;
|
|
|
|
guint8 tempType;
|
|
|
|
gboolean reachedEnd = FALSE;
|
|
|
|
|
|
|
|
if (check_col(pinfo->cinfo, COL_PROTOCOL))
|
|
|
|
col_set_str(pinfo->cinfo, COL_PROTOCOL, "LLDP");
|
|
|
|
|
|
|
|
/* Clear the information column on summary display */
|
|
|
|
if (check_col(pinfo->cinfo, COL_INFO)) {
|
|
|
|
col_clear(pinfo->cinfo, COL_INFO);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (tree)
|
|
|
|
{
|
|
|
|
ti = proto_tree_add_item(tree, proto_lldp, tvb, offset, -1, FALSE);
|
|
|
|
lldp_tree = proto_item_add_subtree(ti, ett_lldp);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Get chassis id tlv */
|
|
|
|
rtnValue = dissect_lldp_chassis_id(tvb, pinfo, lldp_tree, offset);
|
|
|
|
if (rtnValue < 0)
|
|
|
|
{
|
|
|
|
if (check_col(pinfo->cinfo, COL_INFO))
|
|
|
|
col_set_str(pinfo->cinfo, COL_INFO, "Invalid Chassis ID TLV");
|
|
|
|
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
offset += rtnValue;
|
|
|
|
|
|
|
|
/* Get port id tlv */
|
|
|
|
rtnValue = dissect_lldp_port_id(tvb, pinfo, lldp_tree, offset);
|
|
|
|
if (rtnValue < 0)
|
|
|
|
{
|
|
|
|
if (check_col(pinfo->cinfo, COL_INFO))
|
|
|
|
col_set_str(pinfo->cinfo, COL_INFO, "Invalid Port ID TLV");
|
|
|
|
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
offset += rtnValue;
|
|
|
|
|
|
|
|
/* Get time to live tlv */
|
|
|
|
rtnValue = dissect_lldp_time_to_live(tvb, pinfo, lldp_tree, offset);
|
|
|
|
if (rtnValue < 0)
|
|
|
|
{
|
|
|
|
if (check_col(pinfo->cinfo, COL_INFO))
|
|
|
|
col_set_str(pinfo->cinfo, COL_INFO, "Invalid Time-to-Live TLV");
|
|
|
|
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
offset += rtnValue;
|
|
|
|
|
|
|
|
/* Dissect optional tlv's until end-of-lldpdu is reached */
|
|
|
|
while (!reachedEnd)
|
|
|
|
{
|
|
|
|
tempShort = tvb_get_ntohs(tvb, offset);
|
|
|
|
tempType = TLV_TYPE(tempShort);
|
|
|
|
|
|
|
|
switch (tempType)
|
|
|
|
{
|
|
|
|
case CHASSIS_ID_TLV_TYPE:
|
|
|
|
rtnValue = dissect_lldp_chassis_id(tvb, pinfo, lldp_tree, offset);
|
|
|
|
rtnValue = -1; /* Duplicate chassis id tlv */
|
|
|
|
if (check_col(pinfo->cinfo, COL_INFO))
|
|
|
|
col_set_str(pinfo->cinfo, COL_INFO, "Duplicate Chassis ID TLV");
|
|
|
|
break;
|
|
|
|
case PORT_ID_TLV_TYPE:
|
|
|
|
rtnValue = dissect_lldp_port_id(tvb, pinfo, lldp_tree, offset);
|
|
|
|
rtnValue = -1; /* Duplicate port id tlv */
|
|
|
|
if (check_col(pinfo->cinfo, COL_INFO))
|
|
|
|
col_set_str(pinfo->cinfo, COL_INFO, "Duplicate Port ID TLV");
|
|
|
|
break;
|
|
|
|
case TIME_TO_LIVE_TLV_TYPE:
|
|
|
|
rtnValue = dissect_lldp_time_to_live(tvb, pinfo, lldp_tree, offset);
|
|
|
|
rtnValue = -1; /* Duplicate time-to-live tlv */
|
|
|
|
if (check_col(pinfo->cinfo, COL_INFO))
|
|
|
|
col_set_str(pinfo->cinfo, COL_INFO, "Duplicate Time-To-Live TLV");
|
|
|
|
break;
|
|
|
|
case END_OF_LLDPDU_TLV_TYPE:
|
|
|
|
rtnValue = dissect_lldp_end_of_lldpdu(tvb, pinfo, lldp_tree, offset);
|
|
|
|
break;
|
|
|
|
case PORT_DESCRIPTION_TLV_TYPE:
|
|
|
|
rtnValue = dissect_lldp_port_desc(tvb, pinfo, lldp_tree, offset);
|
|
|
|
break;
|
|
|
|
case SYSTEM_NAME_TLV_TYPE:
|
|
|
|
case SYSTEM_DESCRIPTION_TLV_TYPE:
|
|
|
|
rtnValue = dissect_lldp_system_name(tvb, pinfo, lldp_tree, offset);
|
|
|
|
break;
|
|
|
|
case SYSTEM_CAPABILITIES_TLV_TYPE:
|
|
|
|
rtnValue = dissect_lldp_system_capabilities(tvb, pinfo, lldp_tree, offset);
|
|
|
|
break;
|
|
|
|
case MANAGEMENT_ADDR_TLV_TYPE:
|
|
|
|
rtnValue = dissect_lldp_management_address(tvb, pinfo, lldp_tree, offset);
|
|
|
|
break;
|
|
|
|
case ORGANIZATION_SPECIFIC_TLV_TYPE:
|
|
|
|
rtnValue = dissect_organizational_specific_tlv(tvb, pinfo, lldp_tree, offset);
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
rtnValue = dissect_lldp_unknown_tlv(tvb, pinfo, lldp_tree, offset);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (rtnValue < 0)
|
|
|
|
reachedEnd = TRUE;
|
|
|
|
else
|
|
|
|
offset += rtnValue;
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Register the protocol with Ethereal */
|
|
|
|
void
|
|
|
|
proto_register_lldp(void)
|
|
|
|
{
|
|
|
|
/* Setup list of header fields */
|
|
|
|
static hf_register_info hf[] = {
|
|
|
|
{ &hf_lldp_tlv_type,
|
|
|
|
{ "TLV Type", "lldp.tlv.type", FT_UINT16, BASE_DEC,
|
|
|
|
VALS(tlv_types), TLV_TYPE_MASK, "", HFILL }
|
|
|
|
},
|
|
|
|
{ &hf_lldp_tlv_len,
|
|
|
|
{ "TLV Length", "lldp.tlv.len", FT_UINT16, BASE_DEC,
|
|
|
|
NULL, TLV_INFO_MASK, "", HFILL }
|
|
|
|
},
|
|
|
|
{ &hf_chassis_id_subtype,
|
|
|
|
{ "Chassis Id Subtype", "lldp.chassis.subtype", FT_UINT8, BASE_DEC,
|
|
|
|
VALS(chassis_id_subtypes), 0, "", HFILL }
|
|
|
|
},
|
|
|
|
{ &hf_chassis_id,
|
|
|
|
{ "Chassis Id", "lldp.chassis.id", FT_BYTES, BASE_HEX,
|
|
|
|
NULL, 0, "", HFILL }
|
|
|
|
},
|
|
|
|
{ &hf_chassis_id_mac,
|
|
|
|
{ "Chassis Id", "lldp.chassis.id.mac", FT_ETHER, BASE_NONE,
|
|
|
|
NULL, 0, "", HFILL }
|
|
|
|
},
|
|
|
|
{ &hf_chassis_id_ip4,
|
|
|
|
{ "Chassis Id", "lldp.chassis.id.ip4", FT_IPv4, BASE_NONE,
|
|
|
|
NULL, 0, "", HFILL }
|
|
|
|
},
|
|
|
|
{ &hf_chassis_id_ip6,
|
|
|
|
{ "Chassis Id", "lldp.chassis.id.ip6", FT_IPv6, BASE_NONE,
|
|
|
|
NULL, 0, "", HFILL }
|
|
|
|
},
|
|
|
|
{ &hf_port_id_subtype,
|
|
|
|
{ "Port Id Subtype", "lldp.port.subtype", FT_UINT8, BASE_DEC,
|
|
|
|
VALS(port_id_subtypes), 0, "", HFILL }
|
|
|
|
},
|
|
|
|
{ &hf_port_id_mac,
|
|
|
|
{ "Port Id", "lldp.port.id.mac", FT_ETHER, BASE_NONE,
|
|
|
|
NULL, 0, "", HFILL }
|
|
|
|
},
|
|
|
|
{ &hf_port_id_ip4,
|
|
|
|
{ "Port Id", "lldp.port.id.ip4", FT_IPv4, BASE_NONE,
|
|
|
|
NULL, 0, "", HFILL }
|
|
|
|
},
|
|
|
|
{ &hf_port_id_ip6,
|
|
|
|
{ "Port Id", "lldp.port.id.ip6", FT_IPv6, BASE_NONE,
|
|
|
|
NULL, 0, "", HFILL }
|
|
|
|
},
|
|
|
|
{ &hf_time_to_live,
|
|
|
|
{ "Seconds", "lldp.time_to_live", FT_UINT16, BASE_DEC,
|
|
|
|
NULL, 0, "", HFILL }
|
|
|
|
},
|
|
|
|
{ &hf_mgn_addr_ipv4,
|
|
|
|
{ "Management Address", "lldp.mgn.addr.ip4", FT_IPv4, BASE_NONE,
|
|
|
|
NULL, 0, "", HFILL }
|
|
|
|
},
|
|
|
|
{ &hf_mgn_addr_hex,
|
|
|
|
{ "Management Address", "lldp.mgn.addr.hex", FT_BYTES, BASE_HEX,
|
|
|
|
NULL, 0, "", HFILL }
|
|
|
|
},
|
|
|
|
{ &hf_mgn_obj_id,
|
|
|
|
{ "Object Identifier", "lldp.mgn.obj.id", FT_BYTES, BASE_HEX,
|
|
|
|
NULL, 0, "", HFILL }
|
|
|
|
},
|
|
|
|
{ & hf_org_spc_oui,
|
|
|
|
{ "Organization Unique Code", "lldp.orgtlv.oui", FT_UINT24, BASE_HEX,
|
|
|
|
VALS(tlv_oui_subtype_vals), 0x0, "", HFILL }
|
|
|
|
},
|
|
|
|
{ &hf_ieee_802_1_subtype,
|
|
|
|
{ "IEEE 802.1 Subtype", "lldp.ieee.802_1.subtype", FT_UINT8, BASE_HEX,
|
|
|
|
VALS(ieee_802_1_subtypes), 0x0, "", HFILL }
|
|
|
|
},
|
|
|
|
{ &hf_ieee_802_3_subtype,
|
|
|
|
{ "IEEE 802.3 Subtype", "lldp.ieee.802_3.subtype", FT_UINT8, BASE_HEX,
|
|
|
|
VALS(ieee_802_3_subtypes), 0x0, "", HFILL }
|
|
|
|
},
|
|
|
|
{ &hf_media_tlv_subtype,
|
|
|
|
{ "Media Subtype", "lldp.media.subtype", FT_UINT8, BASE_HEX,
|
|
|
|
VALS(media_subtypes), 0x0, "", HFILL }
|
|
|
|
},
|
|
|
|
};
|
|
|
|
|
|
|
|
/* Setup protocol subtree array */
|
|
|
|
static gint *ett[] = {
|
|
|
|
&ett_lldp,
|
|
|
|
&ett_chassis_id,
|
|
|
|
&ett_port_id,
|
|
|
|
&ett_time_to_live,
|
|
|
|
&ett_end_of_lldpdu,
|
|
|
|
&ett_port_description,
|
|
|
|
&ett_system_name,
|
|
|
|
&ett_system_cap,
|
|
|
|
&ett_system_cap_summary,
|
|
|
|
&ett_system_cap_enabled,
|
|
|
|
&ett_management_address,
|
|
|
|
&ett_unknown_tlv,
|
|
|
|
&ett_org_spc_tlv,
|
|
|
|
&ett_port_vlan_flags,
|
|
|
|
&ett_802_3_flags,
|
|
|
|
&ett_802_3_power,
|
|
|
|
&ett_802_3_aggregation,
|
|
|
|
&ett_media_capabilities,
|
|
|
|
};
|
|
|
|
|
|
|
|
/* Register the protocol name and description */
|
|
|
|
proto_lldp = proto_register_protocol("Link Layer Discovery Protocol", "LLDP", "lldp");
|
|
|
|
|
|
|
|
/* Required function calls to register the header fields and subtrees used */
|
|
|
|
proto_register_field_array(proto_lldp, hf, array_length(hf));
|
|
|
|
proto_register_subtree_array(ett, array_length(ett));
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
proto_reg_handoff_lldp(void)
|
|
|
|
{
|
|
|
|
dissector_handle_t lldp_handle;
|
|
|
|
|
|
|
|
lldp_handle = create_dissector_handle(dissect_lldp,proto_lldp);
|
|
|
|
dissector_add("ethertype", ETHERNET_TYPE_LLDP, lldp_handle);
|
|
|
|
}
|
|
|
|
|
|
|
|
|