HACK: pinfo->private_data points to smb_info again
[wireshark-wip.git] / epan / dissectors / packet-cdp.c
blob0325a39f22ba1a6232e5d7d8f382f68fcd44626c
1 /* packet-cdp.c
2 * Routines for the disassembly of the "Cisco Discovery Protocol"
3 * (c) Copyright Hannes R. Boehm <hannes@boehm.org>
5 * $Id$
7 * Wireshark - Network traffic analyzer
8 * By Gerald Combs <gerald@wireshark.org>
9 * Copyright 1998 Gerald Combs
11 * This program is free software; you can redistribute it and/or
12 * modify it under the terms of the GNU General Public License
13 * as published by the Free Software Foundation; either version 2
14 * of the License, or (at your option) any later version.
16 * This program is distributed in the hope that it will be useful,
17 * but WITHOUT ANY WARRANTY; without even the implied warranty of
18 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19 * GNU General Public License for more details.
21 * You should have received a copy of the GNU General Public License
22 * along with this program; if not, write to the Free Software
23 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
26 #include "config.h"
28 #include <string.h>
30 #include <glib.h>
31 #include <epan/packet.h>
32 #include <epan/to_str.h>
33 #include <epan/in_cksum.h>
34 #include <epan/wmem/wmem.h>
36 #include <epan/oui.h>
37 #include <epan/nlpid.h>
41 * See
43 * http://www.cisco.com/univercd/cc/td/doc/product/lan/trsrb/frames.htm#xtocid12
45 * for some information on CDP.
47 * See
49 * http://www.cisco.com/en/US/products/hw/switches/ps663/products_tech_note09186a0080094713.shtml#cdp
51 * for some more information on CDP version 2.
54 void proto_register_cdp(void);
55 void proto_reg_handoff_cdp(void);
57 /* Offsets in TLV structure. */
58 #define TLV_TYPE 0
59 #define TLV_LENGTH 2
61 static int proto_cdp = -1;
62 static int hf_cdp_version = -1;
63 static int hf_cdp_checksum = -1;
64 static int hf_cdp_checksum_good = -1;
65 static int hf_cdp_checksum_bad = -1;
66 static int hf_cdp_ttl = -1;
67 static int hf_cdp_tlvtype = -1;
68 static int hf_cdp_tlvlength = -1;
69 static int hf_cdp_deviceid = -1;
70 static int hf_cdp_platform = -1;
71 static int hf_cdp_portid = -1;
72 static int hf_cdp_capabilities = -1;
73 static int hf_cdp_capabilities_router = -1;
74 static int hf_cdp_capabilities_trans_bridge = -1;
75 static int hf_cdp_capabilities_src_bridge = -1;
76 static int hf_cdp_capabilities_switch = -1;
77 static int hf_cdp_capabilities_host = -1;
78 static int hf_cdp_capabilities_igmp_capable = -1;
79 static int hf_cdp_capabilities_repeater = -1;
80 static int hf_cdp_spare_poe_tlv = -1;
81 static int hf_cdp_spare_poe_tlv_poe = -1;
82 static int hf_cdp_spare_poe_tlv_spare_pair_arch = -1;
83 static int hf_cdp_spare_poe_tlv_req_spare_pair_poe = -1;
84 static int hf_cdp_spare_poe_tlv_pse_spare_pair_poe = -1;
86 static gint ett_cdp = -1;
87 static gint ett_cdp_tlv = -1;
88 static gint ett_cdp_nrgyz_tlv = -1;
89 static gint ett_cdp_address = -1;
90 static gint ett_cdp_capabilities = -1;
91 static gint ett_cdp_spare_poe_tlv = -1;
92 static gint ett_cdp_checksum = -1;
94 static dissector_handle_t data_handle;
96 static int
97 dissect_address_tlv(tvbuff_t *tvb, int offset, int length, proto_tree *tree);
98 static void
99 dissect_capabilities(tvbuff_t *tvb, int offset, int length, proto_tree *tree);
100 static void
101 dissect_nrgyz_tlv(tvbuff_t *tvb, int offset, guint16 length, guint16 num,
102 proto_tree *tree);
103 static void
104 dissect_spare_poe_tlv(tvbuff_t *tvb, int offset, int length, proto_tree *tree);
105 static void
106 add_multi_line_string_to_tree(proto_tree *tree, tvbuff_t *tvb, gint start,
107 gint len, const gchar *prefix);
109 #define TYPE_DEVICE_ID 0x0001
110 #define TYPE_ADDRESS 0x0002
111 #define TYPE_PORT_ID 0x0003
112 #define TYPE_CAPABILITIES 0x0004
113 #define TYPE_IOS_VERSION 0x0005
114 #define TYPE_PLATFORM 0x0006
115 #define TYPE_IP_PREFIX 0x0007
116 #define TYPE_PROTOCOL_HELLO 0x0008 /* Protocol Hello */
117 #define TYPE_VTP_MGMT_DOMAIN 0x0009 /* VTP Domain, CTPv2 - see second URL */
118 #define TYPE_NATIVE_VLAN 0x000a /* Native VLAN, CTPv2 - see second URL */
119 #define TYPE_DUPLEX 0x000b /* Full/Half Duplex - see second URL */
120 /* 0x000c */
121 /* 0x000d */
122 #define TYPE_VOIP_VLAN_REPLY 0x000e /* VoIP VLAN reply */
123 #define TYPE_VOIP_VLAN_QUERY 0x000f /* VoIP VLAN query */
124 #define TYPE_POWER 0x0010 /* Power consumption */
125 #define TYPE_MTU 0x0011 /* MTU */
126 #define TYPE_TRUST_BITMAP 0x0012 /* Trust bitmap */
127 #define TYPE_UNTRUSTED_COS 0x0013 /* Untrusted port CoS */
128 #define TYPE_SYSTEM_NAME 0x0014 /* System Name */
129 #define TYPE_SYSTEM_OID 0x0015 /* System OID */
130 #define TYPE_MANAGEMENT_ADDR 0x0016 /* Management Address(es) */
131 #define TYPE_LOCATION 0x0017 /* Location */
132 #define TYPE_EXT_PORT_ID 0x0018 /* External Port-ID */
133 #define TYPE_POWER_REQUESTED 0x0019 /* Power Requested */
134 #define TYPE_POWER_AVAILABLE 0x001a /* Power Available */
135 #define TYPE_PORT_UNIDIR 0x001b /* Port Unidirectional */
136 #define TYPE_NRGYZ 0x001d /* EnergyWise over CDP */
137 #define TYPE_SPARE_POE 0x001f /* Spare Pair PoE */
139 static const value_string type_vals[] = {
140 { TYPE_DEVICE_ID, "Device ID" },
141 { TYPE_ADDRESS, "Addresses" },
142 { TYPE_PORT_ID, "Port ID" },
143 { TYPE_CAPABILITIES, "Capabilities" },
144 { TYPE_IOS_VERSION, "Software version" },
145 { TYPE_PLATFORM, "Platform" },
146 { TYPE_IP_PREFIX, "IP Prefix/Gateway (used for ODR)" },
147 { TYPE_PROTOCOL_HELLO, "Protocol Hello" },
148 { TYPE_VTP_MGMT_DOMAIN, "VTP Management Domain" },
149 { TYPE_NATIVE_VLAN, "Native VLAN" },
150 { TYPE_DUPLEX, "Duplex" },
151 { TYPE_VOIP_VLAN_REPLY, "VoIP VLAN Reply" },
152 { TYPE_VOIP_VLAN_QUERY, "VoIP VLAN Query" },
153 { TYPE_POWER, "Power consumption" },
154 { TYPE_MTU, "MTU"},
155 { TYPE_TRUST_BITMAP, "Trust Bitmap" },
156 { TYPE_UNTRUSTED_COS, "Untrusted Port CoS" },
157 { TYPE_SYSTEM_NAME, "System Name" },
158 { TYPE_SYSTEM_OID, "System Object ID" },
159 { TYPE_MANAGEMENT_ADDR, "Management Address" },
160 { TYPE_LOCATION, "Location" },
161 { TYPE_EXT_PORT_ID, "External Port-ID" },
162 { TYPE_POWER_REQUESTED, "Power Requested" },
163 { TYPE_POWER_AVAILABLE, "Power Available" },
164 { TYPE_PORT_UNIDIR, "Port Unidirectional" },
165 { TYPE_NRGYZ, "EnergyWise" },
166 { TYPE_SPARE_POE, "Spare PoE" },
167 { 0, NULL }
170 #define TYPE_HELLO_CLUSTER_MGMT 0x0112
172 static const value_string type_hello_vals[] = {
173 { TYPE_HELLO_CLUSTER_MGMT, "Cluster Management" },
174 { 0, NULL }
177 #define TYPE_NRGYZ_ROLE 0x00000007
178 #define TYPE_NRGYZ_DOMAIN 0x00000008
179 #define TYPE_NRGYZ_NAME 0x00000009
180 #define TYPE_NRGYZ_REPLYTO 0x00000017
182 static const value_string type_nrgyz_vals[] = {
183 { TYPE_NRGYZ_ROLE, "Role" },
184 { TYPE_NRGYZ_DOMAIN, "Domain" },
185 { TYPE_NRGYZ_NAME, "Name" },
186 { TYPE_NRGYZ_REPLYTO, "Reply To" },
187 { 0, NULL }
190 static void
191 dissect_cdp(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree)
193 proto_item *ti, *checksum_item;
194 proto_tree *cdp_tree = NULL, *checksum_tree;
195 int offset = 0;
196 guint16 type;
197 guint16 length, packet_checksum, computed_checksum, data_length;
198 gboolean checksum_good, checksum_bad;
199 proto_item *tlvi = NULL;
200 proto_tree *tlv_tree = NULL;
201 int real_length;
202 guint32 naddresses;
203 guint32 power_avail_len, power_avail;
204 guint32 power_req_len, power_req;
205 int addr_length;
206 guint32 ip_addr;
207 vec_t cksum_vec[1];
209 col_set_str(pinfo->cinfo, COL_PROTOCOL, "CDP");
210 col_clear(pinfo->cinfo, COL_INFO);
212 if (tree) {
213 ti = proto_tree_add_item(tree, proto_cdp, tvb, offset, -1, ENC_NA);
214 cdp_tree = proto_item_add_subtree(ti, ett_cdp);
216 /* CDP header */
217 proto_tree_add_item(cdp_tree, hf_cdp_version, tvb, offset, 1, ENC_BIG_ENDIAN);
218 offset += 1;
220 proto_tree_add_uint_format_value(cdp_tree, hf_cdp_ttl, tvb, offset, 1,
221 tvb_get_guint8(tvb, offset),
222 "%u seconds",
223 tvb_get_guint8(tvb, offset));
224 offset += 1;
225 } else {
226 offset += 2; /* The version/ttl fields from above */
229 /* Checksum display & verification code */
230 packet_checksum = tvb_get_ntohs(tvb, offset);
232 data_length = tvb_reported_length(tvb);
234 /* CDP doesn't adhere to RFC 1071 section 2. (B). It incorrectly assumes
235 * checksums are calculated on a big endian platform, therefore i.s.o.
236 * padding odd sized data with a zero byte _at the end_ it sets the last
237 * big endian _word_ to contain the last network _octet_. This byteswap
238 * has to be done on the last octet of network data before feeding it to
239 * the Internet checksum routine.
240 * CDP checksumming code has a bug in the addition of this last _word_
241 * as a signed number into the long word intermediate checksum. When
242 * reducing this long to word size checksum an off-by-one error can be
243 * made. This off-by-one error is compensated for in the last _word_ of
244 * the network data.
246 if (data_length & 1) {
247 guint8 *padded_buffer;
248 /* Allocate new buffer */
249 padded_buffer = (guint8 *)wmem_alloc(wmem_packet_scope(), data_length+1);
250 tvb_memcpy(tvb, padded_buffer, 0, data_length);
251 /* Swap bytes in last word */
252 padded_buffer[data_length] = padded_buffer[data_length-1];
253 padded_buffer[data_length-1] = 0;
254 /* Compensate off-by-one error */
255 if (padded_buffer[data_length] & 0x80) {
256 padded_buffer[data_length]--;
257 padded_buffer[data_length-1]--;
259 /* Setup checksum routine data buffer */
260 cksum_vec[0].ptr = padded_buffer;
261 cksum_vec[0].len = data_length+1;
262 } else {
263 /* Setup checksum routine data buffer */
264 cksum_vec[0].ptr = tvb_get_ptr(tvb, 0, data_length);
265 cksum_vec[0].len = data_length;
268 computed_checksum = in_cksum(cksum_vec, 1);
269 checksum_good = (computed_checksum == 0);
270 checksum_bad = !checksum_good;
271 if (checksum_good) {
272 checksum_item = proto_tree_add_uint_format_value(cdp_tree,
273 hf_cdp_checksum, tvb, offset, 2, packet_checksum,
274 "0x%04x [correct]", packet_checksum);
275 } else {
276 checksum_item = proto_tree_add_uint_format_value(cdp_tree,
277 hf_cdp_checksum, tvb, offset, 2, packet_checksum,
278 "0x%04x [incorrect, should be 0x%04x]",
279 packet_checksum,
280 in_cksum_shouldbe(packet_checksum, computed_checksum));
283 checksum_tree = proto_item_add_subtree(checksum_item, ett_cdp_checksum);
284 checksum_item = proto_tree_add_boolean(checksum_tree, hf_cdp_checksum_good,
285 tvb, offset, 2, checksum_good);
286 PROTO_ITEM_SET_GENERATED(checksum_item);
287 checksum_item = proto_tree_add_boolean(checksum_tree, hf_cdp_checksum_bad,
288 tvb, offset, 2, checksum_bad);
289 PROTO_ITEM_SET_GENERATED(checksum_item);
291 offset += 2;
293 while (tvb_reported_length_remaining(tvb, offset) != 0) {
294 type = tvb_get_ntohs(tvb, offset + TLV_TYPE);
295 length = tvb_get_ntohs(tvb, offset + TLV_LENGTH);
296 if (length < 4) {
297 if (tree) {
298 tlvi = proto_tree_add_text(cdp_tree, tvb, offset, 4,
299 "TLV with invalid length %u (< 4)",
300 length);
301 tlv_tree = proto_item_add_subtree(tlvi, ett_cdp_tlv);
302 proto_tree_add_item(tlv_tree, hf_cdp_tlvtype, tvb, offset + TLV_TYPE, 2, ENC_BIG_ENDIAN);
303 proto_tree_add_item(tlv_tree, hf_cdp_tlvlength, tvb, offset + TLV_LENGTH, 2, ENC_BIG_ENDIAN);
305 offset += 4;
306 break;
309 switch (type) {
311 case TYPE_DEVICE_ID:
312 /* Device ID */
314 col_append_fstr(pinfo->cinfo, COL_INFO,
315 "Device ID: %s ",
316 tvb_format_stringzpad(tvb, offset + 4, length - 4));
318 if (tree) {
319 tlvi = proto_tree_add_text(cdp_tree, tvb, offset,
320 length, "Device ID: %s",
321 tvb_format_stringzpad(tvb, offset + 4, length - 4));
322 tlv_tree = proto_item_add_subtree(tlvi, ett_cdp_tlv);
323 proto_tree_add_item(tlv_tree, hf_cdp_tlvtype, tvb, offset + TLV_TYPE, 2, ENC_BIG_ENDIAN);
324 proto_tree_add_item(tlv_tree, hf_cdp_tlvlength, tvb, offset + TLV_LENGTH, 2, ENC_BIG_ENDIAN);
325 proto_tree_add_item(tlv_tree, hf_cdp_deviceid, tvb, offset + 4, length - 4, ENC_ASCII|ENC_NA);
327 offset += length;
328 break;
330 case TYPE_PORT_ID:
331 real_length = length;
332 if (tvb_get_guint8(tvb, offset + real_length) != 0x00) {
333 /* The length in the TLV doesn't appear to be the
334 length of the TLV, as the byte just past it
335 isn't the first byte of a 2-byte big-endian
336 small integer; make the length of the TLV the length
337 in the TLV, plus 4 bytes for the TLV type and length,
338 minus 1 because that's what makes one capture work. */
339 real_length = length + 3;
342 col_append_fstr(pinfo->cinfo, COL_INFO,
343 "Port ID: %s ",
344 tvb_format_stringzpad(tvb, offset + 4,
345 length - 4));
347 if (tree) {
348 tlvi = proto_tree_add_text(cdp_tree, tvb, offset,
349 real_length, "Port ID: %s",
350 tvb_format_text(tvb, offset + 4, real_length - 4));
351 tlv_tree = proto_item_add_subtree(tlvi, ett_cdp_tlv);
352 proto_tree_add_item(tlv_tree, hf_cdp_tlvtype, tvb, offset + TLV_TYPE, 2, ENC_BIG_ENDIAN);
353 proto_tree_add_item(tlv_tree, hf_cdp_tlvlength, tvb, offset + TLV_LENGTH, 2, ENC_BIG_ENDIAN);
354 proto_tree_add_item(tlv_tree, hf_cdp_portid, tvb, offset + 4, real_length - 4, ENC_ASCII|ENC_NA);
356 offset += real_length;
357 break;
359 case TYPE_ADDRESS:
360 /* Addresses */
361 if (tree) {
362 tlvi = proto_tree_add_text(cdp_tree, tvb, offset,
363 length, "Addresses");
364 tlv_tree = proto_item_add_subtree(tlvi, ett_cdp_tlv);
365 proto_tree_add_item(tlv_tree, hf_cdp_tlvtype, tvb, offset + TLV_TYPE, 2, ENC_BIG_ENDIAN);
366 proto_tree_add_item(tlv_tree, hf_cdp_tlvlength, tvb, offset + TLV_LENGTH, 2, ENC_BIG_ENDIAN);
368 offset += 4;
369 length -= 4;
370 naddresses = tvb_get_ntohl(tvb, offset);
371 if (tree) {
372 proto_tree_add_text(tlv_tree, tvb, offset, 4,
373 "Number of addresses: %u", naddresses);
375 offset += 4;
376 length -= 4;
377 while (naddresses != 0) {
378 addr_length = dissect_address_tlv(tvb, offset, length,
379 tlv_tree);
380 if (addr_length < 0)
381 break;
382 offset += addr_length;
383 length -= addr_length;
385 naddresses--;
387 offset += length;
388 break;
390 case TYPE_CAPABILITIES:
391 if (tree) {
392 tlvi = proto_tree_add_text(cdp_tree, tvb, offset,
393 length, "Capabilities");
394 tlv_tree = proto_item_add_subtree(tlvi, ett_cdp_tlv);
395 proto_tree_add_item(tlv_tree, hf_cdp_tlvtype, tvb, offset + TLV_TYPE, 2, ENC_BIG_ENDIAN);
396 proto_tree_add_item(tlv_tree, hf_cdp_tlvlength, tvb, offset + TLV_LENGTH, 2, ENC_BIG_ENDIAN);
398 offset += 4;
399 length -= 4;
400 dissect_capabilities(tvb, offset, length, tlv_tree);
401 offset += length;
402 break;
404 case TYPE_IOS_VERSION:
405 if (tree) {
406 tlvi = proto_tree_add_text(cdp_tree, tvb, offset,
407 length, "Software Version");
408 tlv_tree = proto_item_add_subtree(tlvi, ett_cdp_tlv);
409 proto_tree_add_item(tlv_tree, hf_cdp_tlvtype, tvb, offset + TLV_TYPE, 2, ENC_BIG_ENDIAN);
410 proto_tree_add_item(tlv_tree, hf_cdp_tlvlength, tvb, offset + TLV_LENGTH, 2, ENC_BIG_ENDIAN);
411 add_multi_line_string_to_tree(tlv_tree, tvb, offset + 4,
412 length - 4, "Software Version: ");
414 offset += length;
415 break;
417 case TYPE_PLATFORM:
418 /* ??? platform */
419 if (tree) {
420 tlvi = proto_tree_add_text(cdp_tree, tvb,
421 offset, length, "Platform: %s",
422 tvb_format_text(tvb, offset + 4, length - 4));
423 tlv_tree = proto_item_add_subtree(tlvi, ett_cdp_tlv);
424 proto_tree_add_item(tlv_tree, hf_cdp_tlvtype, tvb, offset + TLV_TYPE, 2, ENC_BIG_ENDIAN);
425 proto_tree_add_item(tlv_tree, hf_cdp_tlvlength, tvb, offset + TLV_LENGTH, 2, ENC_BIG_ENDIAN);
426 proto_tree_add_item(tlv_tree, hf_cdp_platform, tvb, offset + 4, length - 4, ENC_ASCII|ENC_NA);
428 offset += length;
429 break;
431 case TYPE_IP_PREFIX:
432 if (length == 8) {
433 /* if length is 8 then this is default gw not prefix */
434 if (tree) {
435 tlvi = proto_tree_add_text(cdp_tree, tvb, offset,
436 length, "ODR Default gateway: %s",
437 tvb_ip_to_str(tvb, offset+4));
438 tlv_tree = proto_item_add_subtree(tlvi, ett_cdp_tlv);
439 proto_tree_add_item(tlv_tree, hf_cdp_tlvtype, tvb, offset + TLV_TYPE, 2, ENC_BIG_ENDIAN);
440 proto_tree_add_item(tlv_tree, hf_cdp_tlvlength, tvb, offset + TLV_LENGTH, 2, ENC_BIG_ENDIAN);
441 proto_tree_add_text(tlv_tree, tvb, offset+4, 4,
442 "ODR Default gateway = %s",
443 tvb_ip_to_str(tvb, offset+4));
445 offset += 8;
446 } else {
447 if (tree) {
448 tlvi = proto_tree_add_text(cdp_tree, tvb, offset,
449 length, "IP Prefixes: %d",length/5);
451 /* the actual number of prefixes is (length-4)/5
452 but if the variable is not a "float" but "integer"
453 then length/5=(length-4)/5 :) */
455 tlv_tree = proto_item_add_subtree(tlvi, ett_cdp_tlv);
456 proto_tree_add_item(tlv_tree, hf_cdp_tlvtype, tvb, offset + TLV_TYPE, 2, ENC_BIG_ENDIAN);
457 proto_tree_add_item(tlv_tree, hf_cdp_tlvlength, tvb, offset + TLV_LENGTH, 2, ENC_BIG_ENDIAN);
459 offset += 4;
460 length -= 4;
461 while (length > 0) {
462 if (tree) {
463 proto_tree_add_text(tlv_tree, tvb, offset, 5,
464 "IP Prefix = %s/%u",
465 tvb_ip_to_str(tvb, offset),
466 tvb_get_guint8(tvb,offset+4));
468 offset += 5;
469 length -= 5;
472 break;
474 case TYPE_PROTOCOL_HELLO:
475 if (tree) {
476 tlvi = proto_tree_add_text(cdp_tree, tvb,
477 offset,length, "Protocol Hello: %s",
478 val_to_str(tvb_get_ntohs(tvb, offset+7), type_hello_vals, "Unknown (0x%04x)"));
479 tlv_tree = proto_item_add_subtree(tlvi, ett_cdp_tlv);
480 proto_tree_add_item(tlv_tree, hf_cdp_tlvtype, tvb, offset + TLV_TYPE, 2, ENC_BIG_ENDIAN);
481 proto_tree_add_item(tlv_tree, hf_cdp_tlvlength, tvb, offset + TLV_LENGTH, 2, ENC_BIG_ENDIAN);
482 proto_tree_add_text(tlv_tree, tvb, offset+4, 3,
483 "OUI: 0x%06X (%s)",
484 tvb_get_ntoh24(tvb,offset+4),
485 val_to_str_const(tvb_get_ntoh24(tvb,offset+4), oui_vals, "Unknown"));
486 proto_tree_add_text(tlv_tree, tvb, offset+7, 2,
487 "Protocol ID: 0x%04X (%s)",
488 tvb_get_ntohs(tvb, offset+7),
489 val_to_str_const(tvb_get_ntohs(tvb, offset+7), type_hello_vals, "Unknown"));
491 switch(tvb_get_ntohs(tvb, offset+7)) {
493 case TYPE_HELLO_CLUSTER_MGMT:
494 /* proto_tree_add_text(tlv_tree, tvb, offset+9,
495 length - 9, "Cluster Management");
497 ip_addr = tvb_get_ipv4(tvb, offset+9);
498 proto_tree_add_text(tlv_tree, tvb, offset+9, 4,
499 "Cluster Master IP: %s",ip_to_str((guint8 *)&ip_addr));
500 ip_addr = tvb_get_ipv4(tvb, offset+13);
501 proto_tree_add_text(tlv_tree, tvb, offset+13, 4,
502 "UNKNOWN (IP?): 0x%08X (%s)",
503 ip_addr, ip_to_str((guint8 *)&ip_addr));
504 proto_tree_add_text(tlv_tree, tvb, offset+17, 1,
505 "Version?: 0x%02X",
506 tvb_get_guint8(tvb, offset+17));
507 proto_tree_add_text(tlv_tree, tvb, offset+18, 1,
508 "Sub Version?: 0x%02X",
509 tvb_get_guint8(tvb, offset+18));
510 proto_tree_add_text(tlv_tree, tvb, offset+19, 1,
511 "Status?: 0x%02X",
512 tvb_get_guint8(tvb, offset+19));
513 proto_tree_add_text(tlv_tree, tvb, offset+20, 1,
514 "UNKNOWN: 0x%02X",
515 tvb_get_guint8(tvb, offset+20));
516 proto_tree_add_text(tlv_tree, tvb, offset+21, 6,
517 "Cluster Commander MAC: %s",
518 tvb_ether_to_str(tvb, offset+21));
519 proto_tree_add_text(tlv_tree, tvb, offset+27, 6,
520 "Switch's MAC: %s",
521 tvb_ether_to_str(tvb, offset+27));
522 proto_tree_add_text(tlv_tree, tvb, offset+33, 1,
523 "UNKNOWN: 0x%02X",
524 tvb_get_guint8(tvb, offset+33));
525 proto_tree_add_text(tlv_tree, tvb, offset+34, 2,
526 "Management VLAN: %d",
527 tvb_get_ntohs(tvb, offset+34));
528 break;
529 default:
530 proto_tree_add_text(tlv_tree, tvb, offset + 9,
531 length - 9, "Unknown");
532 break;
535 offset += length;
536 break;
538 case TYPE_VTP_MGMT_DOMAIN:
539 if (tree) {
540 tlvi = proto_tree_add_text(cdp_tree, tvb,
541 offset, length, "VTP Management Domain: %s",
542 tvb_format_text(tvb, offset + 4, length - 4));
543 tlv_tree = proto_item_add_subtree(tlvi, ett_cdp_tlv);
544 proto_tree_add_item(tlv_tree, hf_cdp_tlvtype, tvb, offset + TLV_TYPE, 2, ENC_BIG_ENDIAN);
545 proto_tree_add_item(tlv_tree, hf_cdp_tlvlength, tvb, offset + TLV_LENGTH, 2, ENC_BIG_ENDIAN);
546 proto_tree_add_text(tlv_tree, tvb, offset + 4,
547 length - 4, "VTP Management Domain: %s",
548 tvb_format_text(tvb, offset + 4, length - 4));
550 offset += length;
551 break;
553 case TYPE_NATIVE_VLAN:
554 if (tree) {
555 tlvi = proto_tree_add_text(cdp_tree, tvb,
556 offset, length, "Native VLAN: %u",
557 tvb_get_ntohs(tvb, offset + 4));
558 tlv_tree = proto_item_add_subtree(tlvi, ett_cdp_tlv);
559 proto_tree_add_item(tlv_tree, hf_cdp_tlvtype, tvb, offset + TLV_TYPE, 2, ENC_BIG_ENDIAN);
560 proto_tree_add_item(tlv_tree, hf_cdp_tlvlength, tvb, offset + TLV_LENGTH, 2, ENC_BIG_ENDIAN);
561 proto_tree_add_text(tlv_tree, tvb, offset + 4,
562 length - 4, "Native VLAN: %u",
563 tvb_get_ntohs(tvb, offset + 4));
565 offset += length;
566 break;
568 case TYPE_DUPLEX:
569 if (tree) {
570 tlvi = proto_tree_add_text(cdp_tree, tvb,
571 offset, length, "Duplex: %s",
572 tvb_get_guint8(tvb, offset + 4) ?
573 "Full" : "Half" );
574 tlv_tree = proto_item_add_subtree(tlvi, ett_cdp_tlv);
575 proto_tree_add_item(tlv_tree, hf_cdp_tlvtype, tvb, offset + TLV_TYPE, 2, ENC_BIG_ENDIAN);
576 proto_tree_add_item(tlv_tree, hf_cdp_tlvlength, tvb, offset + TLV_LENGTH, 2, ENC_BIG_ENDIAN);
577 proto_tree_add_text(tlv_tree, tvb, offset + 4,
578 length - 4, "Duplex: %s",
579 tvb_get_guint8(tvb, offset + 4) ?
580 "Full" : "Half" );
582 offset += length;
583 break;
585 case TYPE_VOIP_VLAN_REPLY:
586 if (tree) {
587 if (length >= 7) {
588 tlvi = proto_tree_add_text(cdp_tree, tvb, offset, length,
589 "VoIP VLAN Reply: %u", tvb_get_ntohs(tvb, offset + 5));
590 } else {
592 * XXX - what are these? I've seen them in some captures;
593 * they have a length of 6, and run up to the end of
594 * the packet, so if we try to dissect it the same way
595 * we dissect the 7-byte ones, we report a malformed
596 * frame.
598 tlvi = proto_tree_add_text(cdp_tree, tvb,
599 offset, length, "VoIP VLAN Reply");
601 tlv_tree = proto_item_add_subtree(tlvi, ett_cdp_tlv);
602 proto_tree_add_item(tlv_tree, hf_cdp_tlvtype, tvb, offset + TLV_TYPE, 2, ENC_BIG_ENDIAN);
603 proto_tree_add_item(tlv_tree, hf_cdp_tlvlength, tvb, offset + TLV_LENGTH, 2, ENC_BIG_ENDIAN);
604 proto_tree_add_text(tlv_tree, tvb, offset + 4,
605 1, "Data");
606 if (length >= 7) {
607 proto_tree_add_text(tlv_tree, tvb, offset + 5,
608 2, "Voice VLAN: %u",
609 tvb_get_ntohs(tvb, offset + 5));
612 offset += length;
613 break;
615 case TYPE_VOIP_VLAN_QUERY:
616 if (tree) {
617 if (length >= 7) {
618 tlvi = proto_tree_add_text(cdp_tree, tvb,
619 offset, length, "VoIP VLAN Query: %u", tvb_get_ntohs(tvb, offset + 5));
620 } else {
622 * XXX - what are these? I've seen them in some captures;
623 * they have a length of 6, and run up to the end of
624 * the packet, so if we try to dissect it the same way
625 * we dissect the 7-byte ones, we report a malformed
626 * frame.
628 tlvi = proto_tree_add_text(cdp_tree, tvb,
629 offset, length, "VoIP VLAN Query");
631 tlv_tree = proto_item_add_subtree(tlvi, ett_cdp_tlv);
632 proto_tree_add_item(tlv_tree, hf_cdp_tlvtype, tvb, offset + TLV_TYPE, 2, ENC_BIG_ENDIAN);
633 proto_tree_add_item(tlv_tree, hf_cdp_tlvlength, tvb, offset + TLV_LENGTH, 2, ENC_BIG_ENDIAN);
634 proto_tree_add_text(tlv_tree, tvb, offset + 4,
635 1, "Data");
636 if (length >= 7) {
637 proto_tree_add_text(tlv_tree, tvb, offset + 5,
638 2, "Voice VLAN: %u",
639 tvb_get_ntohs(tvb, offset + 5));
642 offset += length;
643 break;
645 case TYPE_POWER:
646 if (tree) {
647 tlvi = proto_tree_add_text(cdp_tree, tvb,
648 offset, length, "Power Consumption: %u mW",
649 tvb_get_ntohs(tvb, offset + 4));
650 tlv_tree = proto_item_add_subtree(tlvi, ett_cdp_tlv);
651 proto_tree_add_item(tlv_tree, hf_cdp_tlvtype, tvb, offset + TLV_TYPE, 2, ENC_BIG_ENDIAN);
652 proto_tree_add_item(tlv_tree, hf_cdp_tlvlength, tvb, offset + TLV_LENGTH, 2, ENC_BIG_ENDIAN);
653 proto_tree_add_text(tlv_tree, tvb, offset + 4,
654 length - 4, "Power Consumption: %u mW",
655 tvb_get_ntohs(tvb, offset + 4));
657 offset += length;
658 break;
660 case TYPE_MTU:
661 if (tree) {
662 tlvi = proto_tree_add_text(cdp_tree, tvb,
663 offset, length, "MTU: %u",
664 tvb_get_ntohl(tvb,offset + 4));
665 tlv_tree = proto_item_add_subtree(tlvi, ett_cdp_tlv);
666 proto_tree_add_item(tlv_tree, hf_cdp_tlvtype, tvb, offset + TLV_TYPE, 2, ENC_BIG_ENDIAN);
667 proto_tree_add_item(tlv_tree, hf_cdp_tlvlength, tvb, offset + TLV_LENGTH, 2, ENC_BIG_ENDIAN);
668 proto_tree_add_text(tlv_tree, tvb, offset + 4,
669 length - 4, "MTU: %u",
670 tvb_get_ntohl(tvb,offset + 4));
672 offset += length;
673 break;
675 case TYPE_TRUST_BITMAP:
676 if (tree) {
677 tlvi = proto_tree_add_text(cdp_tree, tvb,
678 offset, length, "Trust Bitmap: 0x%02X",
679 tvb_get_guint8(tvb, offset + 4));
680 tlv_tree = proto_item_add_subtree(tlvi, ett_cdp_tlv);
681 proto_tree_add_item(tlv_tree, hf_cdp_tlvtype, tvb, offset + TLV_TYPE, 2, ENC_BIG_ENDIAN);
682 proto_tree_add_item(tlv_tree, hf_cdp_tlvlength, tvb, offset + TLV_LENGTH, 2, ENC_BIG_ENDIAN);
683 proto_tree_add_text(tlv_tree, tvb, offset + 4,
684 length - 4, "Trust Bitmap: %02x",
685 tvb_get_guint8(tvb, offset + 4));
687 offset += length;
688 break;
690 case TYPE_UNTRUSTED_COS:
691 if (tree) {
692 tlvi = proto_tree_add_text(cdp_tree, tvb,
693 offset, length, "Untrusted port CoS: 0x%02X",
694 tvb_get_guint8(tvb, offset + 4));
695 tlv_tree = proto_item_add_subtree(tlvi, ett_cdp_tlv);
696 proto_tree_add_item(tlv_tree, hf_cdp_tlvtype, tvb, offset + TLV_TYPE, 2, ENC_BIG_ENDIAN);
697 proto_tree_add_item(tlv_tree, hf_cdp_tlvlength, tvb, offset + TLV_LENGTH, 2, ENC_BIG_ENDIAN);
698 proto_tree_add_text(tlv_tree, tvb, offset + 4,
699 length - 4, "Untrusted port CoS: %02x",
700 tvb_get_guint8(tvb, offset + 4));
702 offset += length;
703 break;
705 case TYPE_SYSTEM_NAME:
706 if (tree) {
707 tlvi = proto_tree_add_text(cdp_tree, tvb,
708 offset, length, "System Name: %s",
709 tvb_format_text(tvb, offset + 4, length - 4));
710 tlv_tree = proto_item_add_subtree(tlvi, ett_cdp_tlv);
711 proto_tree_add_item(tlv_tree, hf_cdp_tlvtype, tvb, offset + TLV_TYPE, 2, ENC_BIG_ENDIAN);
712 proto_tree_add_item(tlv_tree, hf_cdp_tlvlength, tvb, offset + TLV_LENGTH, 2, ENC_BIG_ENDIAN);
713 proto_tree_add_text(tlv_tree, tvb, offset + 4,
714 length - 4, "System Name: %s",
715 tvb_format_text(tvb, offset + 4, length - 4));
717 offset += length;
718 break;
720 case TYPE_SYSTEM_OID:
721 if (tree) {
722 tlvi = proto_tree_add_text(cdp_tree, tvb,
723 offset, length, "System Object Identifier");
724 tlv_tree = proto_item_add_subtree(tlvi, ett_cdp_tlv);
725 proto_tree_add_item(tlv_tree, hf_cdp_tlvtype, tvb, offset + TLV_TYPE, 2, ENC_BIG_ENDIAN);
726 proto_tree_add_item(tlv_tree, hf_cdp_tlvlength, tvb, offset + TLV_LENGTH, 2, ENC_BIG_ENDIAN);
727 proto_tree_add_text(tlv_tree, tvb, offset + 4,
728 length - 4, "System Object Identifier: %s",
729 tvb_bytes_to_str(tvb, offset + 4, length - 4));
731 offset += length;
732 break;
734 case TYPE_MANAGEMENT_ADDR:
735 if (tree) {
736 tlvi = proto_tree_add_text(cdp_tree, tvb,
737 offset, length, "Management Addresses");
738 tlv_tree = proto_item_add_subtree(tlvi, ett_cdp_tlv);
739 proto_tree_add_item(tlv_tree, hf_cdp_tlvtype, tvb, offset + TLV_TYPE, 2, ENC_BIG_ENDIAN);
740 proto_tree_add_item(tlv_tree, hf_cdp_tlvlength, tvb, offset + TLV_LENGTH, 2, ENC_BIG_ENDIAN);
742 offset += 4;
743 length -= 4;
744 naddresses = tvb_get_ntohl(tvb, offset);
745 if (tree) {
746 proto_tree_add_text(tlv_tree, tvb, offset, 4,
747 "Number of addresses: %u", naddresses);
749 offset += 4;
750 length -= 4;
751 while (naddresses != 0) {
752 addr_length = dissect_address_tlv(tvb, offset, length,
753 tlv_tree);
754 if (addr_length < 0)
755 break;
756 offset += addr_length;
757 length -= addr_length;
759 naddresses--;
761 offset += length;
762 break;
764 case TYPE_LOCATION:
765 if (tree) {
766 tlvi = proto_tree_add_text(cdp_tree, tvb,
767 offset, length, "Location: %s",
768 tvb_format_text(tvb, offset + 5, length - 5));
769 tlv_tree = proto_item_add_subtree(tlvi, ett_cdp_tlv);
770 proto_tree_add_item(tlv_tree, hf_cdp_tlvtype, tvb, offset + TLV_TYPE, 2, ENC_BIG_ENDIAN);
771 proto_tree_add_item(tlv_tree, hf_cdp_tlvlength, tvb, offset + TLV_LENGTH, 2, ENC_BIG_ENDIAN);
772 proto_tree_add_text(tlv_tree, tvb, offset + 4,
773 1 , "UNKNOWN: 0x%02X",
774 tvb_get_guint8(tvb, offset + 4));
775 proto_tree_add_text(tlv_tree, tvb, offset + 5,
776 length - 5, "Location: %s",
777 tvb_format_text(tvb, offset + 5, length - 5));
779 offset += length;
780 break;
782 case TYPE_POWER_REQUESTED:
783 if (tree) {
784 tlvi = proto_tree_add_text(cdp_tree, tvb,
785 offset, length, "Power Request: ");
786 tlv_tree = proto_item_add_subtree(tlvi, ett_cdp_tlv);
787 proto_tree_add_item(tlv_tree, hf_cdp_tlvtype, tvb, offset + TLV_TYPE, 2, ENC_BIG_ENDIAN);
788 proto_tree_add_item(tlv_tree, hf_cdp_tlvlength, tvb, offset + TLV_LENGTH, 2, ENC_BIG_ENDIAN);
789 proto_tree_add_text(tlv_tree, tvb, offset + 4,
790 2, "Request-ID: %u",
791 tvb_get_ntohs(tvb, offset + 4));
792 proto_tree_add_text(tlv_tree, tvb, offset + 6,
793 2, "Management-ID: %u",
794 tvb_get_ntohs(tvb, offset + 6));
796 power_req_len = (tvb_get_ntohs(tvb, offset + TLV_LENGTH)) - 8;
797 /* Move offset to where the list of Power Request Values Exist */
798 offset += 8;
799 while(power_req_len) {
800 if (power_req_len > 4) {
801 power_req = tvb_get_ntohl(tvb, offset);
802 if (tree) {
803 proto_tree_add_text(tlv_tree, tvb, offset,
804 4, "Power Requested: %u mW", power_req);
805 proto_item_append_text(tlvi, "%u mW, ", power_req);
807 power_req_len -= 4;
808 offset += 4;
809 } else {
810 if (power_req_len == 4) {
811 power_req = tvb_get_ntohl(tvb, offset);
812 if (tree) {
813 proto_tree_add_text(tlv_tree, tvb, offset,
814 4, "Power Requested: %u mW", power_req);
815 proto_item_append_text(tlvi, "%u mW", power_req);
818 offset += power_req_len;
819 break;
822 break;
824 case TYPE_POWER_AVAILABLE:
825 if (tree) {
826 tlvi = proto_tree_add_text(cdp_tree, tvb,
827 offset, length, "Power Available: ");
828 tlv_tree = proto_item_add_subtree(tlvi, ett_cdp_tlv);
829 proto_tree_add_item(tlv_tree, hf_cdp_tlvtype, tvb, offset + TLV_TYPE, 2, ENC_BIG_ENDIAN);
830 proto_tree_add_item(tlv_tree, hf_cdp_tlvlength, tvb, offset + TLV_LENGTH, 2, ENC_BIG_ENDIAN);
831 proto_tree_add_text(tlv_tree, tvb, offset + 4,
832 2, "Request-ID: %u",
833 tvb_get_ntohs(tvb, offset + 4));
834 proto_tree_add_text(tlv_tree, tvb, offset + 6,
835 2, "Management-ID: %u",
836 tvb_get_ntohs(tvb, offset + 6));
838 power_avail_len = (tvb_get_ntohs(tvb, offset + TLV_LENGTH)) - 8;
839 /* Move offset to where the list of Power Available Values Exist */
840 offset += 8;
841 while(power_avail_len) {
842 if (power_avail_len >= 4) {
843 power_avail = tvb_get_ntohl(tvb, offset);
844 if (tree) {
845 proto_tree_add_text(tlv_tree, tvb, offset,
846 4, "Power Available: %u mW", power_avail);
847 proto_item_append_text(tlvi, "%u mW, ", power_avail);
849 power_avail_len -= 4;
850 offset += 4;
851 } else {
852 offset += power_avail_len;
853 break;
856 break;
858 case TYPE_NRGYZ:
859 if (tree) {
860 tlvi = proto_tree_add_text(cdp_tree, tvb,
861 offset, length, "EnergyWise");
862 tlv_tree = proto_item_add_subtree(tlvi, ett_cdp_tlv);
863 proto_tree_add_item(tlv_tree, hf_cdp_tlvtype, tvb, offset + TLV_TYPE, 2, ENC_BIG_ENDIAN);
864 proto_tree_add_item(tlv_tree, hf_cdp_tlvlength, tvb, offset + TLV_LENGTH, 2, ENC_BIG_ENDIAN);
865 proto_tree_add_text(tlv_tree, tvb, offset + 4,
866 20, "Encrypted Data");
867 proto_tree_add_text(tlv_tree, tvb, offset + 24,
868 4, "Unknown (Seen Sequence?): %u",
869 tvb_get_ntohl(tvb, offset + 24));
870 proto_tree_add_text(tlv_tree, tvb, offset + 28,
871 4, "Sequence Number: %u",
872 tvb_get_ntohl(tvb, offset + 28));
873 proto_tree_add_text(tlv_tree, tvb, offset + 32,
874 16, "Model Number: %s",
875 tvb_format_stringzpad(tvb, offset + 32, 16));
876 proto_tree_add_text(tlv_tree, tvb, offset + 48,
877 2, "Unknown Pad: %x",
878 tvb_get_ntohs(tvb, offset + 48));
879 proto_tree_add_text(tlv_tree, tvb, offset + 50,
880 3, "Hardware Version ID: %s",
881 tvb_format_stringzpad(tvb, offset + 50, 3));
882 proto_tree_add_text(tlv_tree, tvb, offset + 53,
883 11, "System Serial Number: %s",
884 tvb_format_stringzpad(tvb, offset + 53, 11));
885 proto_tree_add_text(tlv_tree, tvb, offset + 64,
886 8, "Unknown Values");
887 proto_tree_add_text(tlv_tree, tvb, offset + 72,
888 2, "Length of TLV table: %u",
889 tvb_get_ntohs(tvb, offset + 72));
890 proto_tree_add_text(tlv_tree, tvb, offset + 74,
891 2, "Number of TLVs in table: %u",
892 tvb_get_ntohs(tvb, offset + 74));
894 proto_tree_add_text(tlv_tree, tvb,
895 offset + 76, length - 76,
896 "EnergyWise TLV Table");
898 dissect_nrgyz_tlv(tvb, offset + 76,
899 tvb_get_ntohs(tvb, offset + 72),
900 tvb_get_ntohs(tvb, offset + 74),
901 tlv_tree);
905 offset += length;
906 break;
908 case TYPE_SPARE_POE:
909 if (tree) {
910 tlvi = proto_tree_add_text(cdp_tree, tvb, offset, length,
911 "Spare Pair PoE");
912 tlv_tree = proto_item_add_subtree(tlvi, ett_cdp_tlv);
914 proto_tree_add_item(tlv_tree, hf_cdp_tlvtype, tvb, offset + TLV_TYPE, 2, ENC_BIG_ENDIAN);
915 proto_tree_add_item(tlv_tree, hf_cdp_tlvlength, tvb, offset + TLV_LENGTH, 2, ENC_BIG_ENDIAN);
917 offset += 4;
918 length -= 4;
919 dissect_spare_poe_tlv(tvb, offset, length, tlv_tree);
920 offset += length;
921 break;
923 default:
924 if (tree) {
925 tlvi = proto_tree_add_text(cdp_tree, tvb, offset,
926 length, "Type: %s, length: %u",
927 val_to_str(type, type_vals, "Unknown (0x%04x)"),
928 length);
929 tlv_tree = proto_item_add_subtree(tlvi, ett_cdp_tlv);
930 proto_tree_add_item(tlv_tree, hf_cdp_tlvtype, tvb, offset + TLV_TYPE, 2, ENC_BIG_ENDIAN);
931 proto_tree_add_item(tlv_tree, hf_cdp_tlvlength, tvb, offset + TLV_LENGTH, 2, ENC_BIG_ENDIAN);
932 if (length > 4) {
933 proto_tree_add_text(tlv_tree, tvb, offset + 4,
934 length - 4, "Data");
935 } else {
936 return;
939 offset += length;
942 call_dissector(data_handle, tvb_new_subset_remaining(tvb, offset), pinfo, cdp_tree);
945 #define PROTO_TYPE_NLPID 1
946 #define PROTO_TYPE_IEEE_802_2 2
948 static const value_string proto_type_vals[] = {
949 { PROTO_TYPE_NLPID, "NLPID" },
950 { PROTO_TYPE_IEEE_802_2, "802.2" },
951 { 0, NULL }
954 static int
955 dissect_address_tlv(tvbuff_t *tvb, int offset, int length, proto_tree *tree)
957 proto_item *ti;
958 proto_tree *address_tree;
959 guint8 protocol_type;
960 guint8 protocol_length;
961 int nlpid;
962 const char *protocol_str;
963 guint16 address_length;
964 const char *address_type_str;
965 const char *address_str;
967 if (length < 1)
968 return -1;
969 ti = proto_tree_add_text(tree, tvb, offset, length, "Truncated address");
970 address_tree = proto_item_add_subtree(ti, ett_cdp_address);
971 protocol_type = tvb_get_guint8(tvb, offset);
972 proto_tree_add_text(address_tree, tvb, offset, 1, "Protocol type: %s",
973 val_to_str(protocol_type, proto_type_vals, "Unknown (0x%02x)"));
974 offset += 1;
975 length -= 1;
977 if (length < 1)
978 return -1;
979 protocol_length = tvb_get_guint8(tvb, offset);
980 proto_tree_add_text(address_tree, tvb, offset, 1, "Protocol length: %u",
981 protocol_length);
982 offset += 1;
983 length -= 1;
985 if (length < protocol_length) {
986 if (length != 0) {
987 proto_tree_add_text(address_tree, tvb, offset, length,
988 "Protocol: %s (truncated)",
989 tvb_bytes_to_str(tvb, offset, length));
991 return -1;
993 protocol_str = NULL;
994 if ((protocol_type == PROTO_TYPE_NLPID) && (protocol_length == 1)) {
995 nlpid = tvb_get_guint8(tvb, offset);
996 protocol_str = val_to_str(nlpid, nlpid_vals, "Unknown (0x%02x)");
997 } else
998 nlpid = -1;
999 if (protocol_str == NULL)
1000 protocol_str = tvb_bytes_to_str(tvb, offset, protocol_length);
1001 proto_tree_add_text(address_tree, tvb, offset, protocol_length,
1002 "Protocol: %s", protocol_str);
1003 offset += protocol_length;
1004 length -= protocol_length;
1006 if (length < 2)
1007 return -1;
1008 address_length = tvb_get_ntohs(tvb, offset);
1009 proto_tree_add_text(address_tree, tvb, offset, 2, "Address length: %u",
1010 address_length);
1011 offset += 2;
1012 length -= 2;
1014 if (length < address_length) {
1015 if (length != 0) {
1016 proto_tree_add_text(address_tree, tvb, offset, length,
1017 "Address: %s (truncated)",
1018 tvb_bytes_to_str(tvb, offset, length));
1020 return -1;
1022 /* XXX - the Cisco document seems to be saying that, for 802.2-format
1023 protocol types, 0xAAAA03 0x000000 0x0800 is IPv6, but 0x0800 is
1024 the Ethernet protocol type for IPv4. */
1025 address_type_str = NULL;
1026 address_str = NULL;
1027 if ((protocol_type == PROTO_TYPE_NLPID) && (protocol_length == 1)) {
1028 switch (nlpid) {
1030 /* XXX - dissect NLPID_ISO8473_CLNP as OSI CLNP address? */
1032 case NLPID_IP:
1033 if (address_length == 4) {
1034 /* The address is an IP address. */
1035 address_type_str = "IP address";
1036 address_str = tvb_ip_to_str(tvb, offset);
1038 break;
1041 if (address_type_str == NULL)
1042 address_type_str = "Address";
1043 if (address_str == NULL) {
1044 address_str = tvb_bytes_to_str(tvb, offset, address_length);
1046 proto_item_set_text(ti, "%s: %s", address_type_str, address_str);
1047 proto_tree_add_text(address_tree, tvb, offset, address_length, "%s: %s",
1048 address_type_str, address_str);
1049 return 2 + protocol_length + 2 + address_length;
1052 static void
1053 dissect_capabilities(tvbuff_t *tvb, int offset, int length, proto_tree *tree)
1055 proto_item *ti;
1056 proto_tree *capabilities_tree;
1058 if (length < 4)
1059 return;
1060 ti = proto_tree_add_item(tree, hf_cdp_capabilities, tvb, offset, 4, ENC_BIG_ENDIAN);
1061 capabilities_tree = proto_item_add_subtree(ti, ett_cdp_capabilities);
1062 proto_tree_add_item(capabilities_tree, hf_cdp_capabilities_router, tvb, offset, 4, ENC_BIG_ENDIAN);
1063 proto_tree_add_item(capabilities_tree, hf_cdp_capabilities_trans_bridge, tvb, offset, 4, ENC_BIG_ENDIAN);
1064 proto_tree_add_item(capabilities_tree, hf_cdp_capabilities_src_bridge, tvb, offset, 4, ENC_BIG_ENDIAN);
1065 proto_tree_add_item(capabilities_tree, hf_cdp_capabilities_switch, tvb, offset, 4, ENC_BIG_ENDIAN);
1066 proto_tree_add_item(capabilities_tree, hf_cdp_capabilities_host, tvb, offset, 4, ENC_BIG_ENDIAN);
1067 proto_tree_add_item(capabilities_tree, hf_cdp_capabilities_igmp_capable, tvb, offset, 4, ENC_BIG_ENDIAN);
1068 proto_tree_add_item(capabilities_tree, hf_cdp_capabilities_repeater, tvb, offset, 4, ENC_BIG_ENDIAN);
1071 static void
1072 dissect_nrgyz_tlv(tvbuff_t *tvb, int offset, guint16 length, guint16 num,
1073 proto_tree *tree)
1075 guint32 tlvt, tlvl, ip_addr;
1076 proto_item *it = NULL;
1077 proto_tree *etree = NULL;
1078 char const *ttext = NULL;
1080 while (num-- && (length >= 8)) {
1081 tlvt = tvb_get_ntohl(tvb, offset);
1082 tlvl = tvb_get_ntohl(tvb, offset + 4);
1084 if (length < tlvl)
1085 break;
1086 length -= tlvl;
1088 if (tlvl < 8) {
1089 proto_tree_add_text(tree, tvb, offset, 8,
1090 "TLV with invalid length %u (< 8)",
1091 tlvl);
1092 offset += 8;
1093 break;
1095 else {
1096 ttext = val_to_str(tlvt, type_nrgyz_vals, "Unknown (0x%04x)");
1097 switch (tlvt) {
1098 case TYPE_NRGYZ_ROLE:
1099 case TYPE_NRGYZ_DOMAIN:
1100 case TYPE_NRGYZ_NAME:
1101 it = proto_tree_add_text(tree, tvb, offset,
1102 tlvl, "EnergyWise %s: %s", ttext,
1103 tvb_format_stringzpad(tvb, offset + 8, tlvl - 8)
1105 break;
1106 case TYPE_NRGYZ_REPLYTO:
1107 ip_addr = tvb_get_ipv4(tvb, offset + 12);
1108 it = proto_tree_add_text(tree, tvb, offset,
1109 tlvl, "EnergyWise %s: %s port %u",
1110 ttext,
1111 ip_to_str((guint8 *)&ip_addr),
1112 tvb_get_ntohs(tvb, offset + 10)
1114 break;
1115 default:
1116 it = proto_tree_add_text(tree, tvb, offset,
1117 tlvl, "EnergyWise %s TLV", ttext);
1119 etree = proto_item_add_subtree(it, ett_cdp_nrgyz_tlv);
1120 proto_tree_add_text(etree, tvb, offset, 4,
1121 "TLV Type: %x (%s)", tlvt, ttext);
1122 proto_tree_add_text(etree, tvb, offset + 4, 4,
1123 "TLV Length: %u", tlvl);
1124 switch (tlvt) {
1125 case TYPE_NRGYZ_ROLE:
1126 case TYPE_NRGYZ_DOMAIN:
1127 case TYPE_NRGYZ_NAME:
1128 proto_tree_add_text(etree, tvb, offset + 8,
1129 tlvl - 8, "%s %s", ttext,
1130 tvb_format_stringzpad(tvb, offset + 8, tlvl - 8)
1132 break;
1133 case TYPE_NRGYZ_REPLYTO:
1134 ip_addr = tvb_get_ipv4(tvb, offset + 12);
1135 proto_tree_add_text(etree, tvb, offset + 8, 2,
1136 "Unknown Field");
1137 proto_tree_add_text(etree, tvb, offset + 10, 2,
1138 "Port %d",
1139 tvb_get_ntohs(tvb, offset + 10)
1141 proto_tree_add_text(etree, tvb, offset + 12, 4,
1142 "IP Address %s",
1143 ip_to_str((guint8 *)&ip_addr)
1145 proto_tree_add_text(etree, tvb, offset + 16, 2,
1146 "Unknown Field (Backup server Port?)");
1147 proto_tree_add_text(etree, tvb, offset + 18, 4,
1148 "Unknown Field (Backup Server IP?)");
1149 break;
1150 default:
1151 if (tlvl > 8) {
1152 proto_tree_add_text(etree, tvb, offset + 8,
1153 tlvl - 8, "Data");
1156 offset += tlvl;
1159 if (length) {
1160 proto_tree_add_text(tree, tvb, offset, length,
1161 "Invalid garbage at end");
1165 static void
1166 dissect_spare_poe_tlv(tvbuff_t *tvb, int offset, int length,
1167 proto_tree *tree)
1169 proto_item *ti;
1170 proto_tree *tlv_tree;
1172 if (length == 0) {
1173 return;
1176 ti = proto_tree_add_item(tree, hf_cdp_spare_poe_tlv, tvb, offset, 1, ENC_BIG_ENDIAN);
1177 tlv_tree = proto_item_add_subtree(ti, ett_cdp_spare_poe_tlv);
1178 proto_tree_add_item(tlv_tree, hf_cdp_spare_poe_tlv_poe, tvb, offset, 1, ENC_BIG_ENDIAN);
1179 proto_tree_add_item(tlv_tree, hf_cdp_spare_poe_tlv_spare_pair_arch, tvb, offset, 1, ENC_BIG_ENDIAN);
1180 proto_tree_add_item(tlv_tree, hf_cdp_spare_poe_tlv_req_spare_pair_poe, tvb, offset, 1, ENC_BIG_ENDIAN);
1181 proto_tree_add_item(tlv_tree, hf_cdp_spare_poe_tlv_pse_spare_pair_poe, tvb, offset, 1, ENC_BIG_ENDIAN);
1184 static void
1185 add_multi_line_string_to_tree(proto_tree *tree, tvbuff_t *tvb, gint start,
1186 gint len, const gchar *prefix)
1188 int prefix_len;
1189 int i;
1190 char blanks[64+1];
1191 gint next;
1192 int line_len;
1193 int data_len;
1195 prefix_len = (int)strlen(prefix);
1196 if (prefix_len > 64)
1197 prefix_len = 64;
1198 for (i = 0; i < prefix_len; i++)
1199 blanks[i] = ' ';
1200 blanks[i] = '\0';
1201 while (len > 0) {
1202 line_len = tvb_find_line_end(tvb, start, len, &next, FALSE);
1203 data_len = next - start;
1204 proto_tree_add_text(tree, tvb, start, data_len, "%s%s", prefix,
1205 tvb_format_stringzpad(tvb, start, line_len));
1206 start += data_len;
1207 len -= data_len;
1208 prefix = blanks;
1212 void
1213 proto_register_cdp(void)
1215 static hf_register_info hf[] = {
1216 { &hf_cdp_version,
1217 { "Version", "cdp.version", FT_UINT8, BASE_DEC, NULL, 0x0,
1218 NULL, HFILL }},
1220 { &hf_cdp_ttl,
1221 { "TTL", "cdp.ttl", FT_UINT16, BASE_DEC, NULL, 0x0,
1222 NULL, HFILL }},
1224 { &hf_cdp_checksum,
1225 { "Checksum", "cdp.checksum", FT_UINT16, BASE_HEX, NULL, 0x0,
1226 NULL, HFILL }},
1228 { &hf_cdp_checksum_good,
1229 { "Good", "cdp.checksum_good", FT_BOOLEAN, BASE_NONE, NULL, 0x0,
1230 "True: checksum matches packet content; False: doesn't match content or not checked", HFILL }},
1232 { &hf_cdp_checksum_bad,
1233 { "Bad", "cdp.checksum_bad", FT_BOOLEAN, BASE_NONE, NULL, 0x0,
1234 "True: checksum doesn't match packet content; False: matches content or not checked", HFILL }},
1236 { &hf_cdp_tlvtype,
1237 { "Type", "cdp.tlv.type", FT_UINT16, BASE_HEX, VALS(type_vals), 0x0,
1238 NULL, HFILL }},
1240 { &hf_cdp_tlvlength,
1241 { "Length", "cdp.tlv.len", FT_UINT16, BASE_DEC, NULL, 0x0,
1242 NULL, HFILL }},
1244 { &hf_cdp_deviceid,
1245 {"Device ID", "cdp.deviceid", FT_STRING, BASE_NONE,
1246 NULL, 0, NULL, HFILL }},
1248 { &hf_cdp_platform,
1249 {"Platform", "cdp.platform", FT_STRING, BASE_NONE,
1250 NULL, 0, NULL, HFILL }},
1252 { &hf_cdp_portid,
1253 {"Sent through Interface", "cdp.portid", FT_STRING, BASE_NONE,
1254 NULL, 0, NULL, HFILL }},
1256 { &hf_cdp_capabilities,
1257 {"Capabilities", "cdp.capabilities", FT_UINT32, BASE_HEX,
1258 NULL, 0, NULL, HFILL }},
1260 { &hf_cdp_capabilities_router,
1261 {"Router", "cdp.capabilities.router", FT_BOOLEAN, 32,
1262 TFS(&tfs_yes_no), 0x01, NULL, HFILL }},
1264 { &hf_cdp_capabilities_trans_bridge,
1265 {"Transparent Bridge", "cdp.capabilities.trans_bridge", FT_BOOLEAN, 32,
1266 TFS(&tfs_yes_no), 0x02, NULL, HFILL }},
1268 { &hf_cdp_capabilities_src_bridge,
1269 {"Source Route Bridge", "cdp.capabilities.src_bridge", FT_BOOLEAN, 32,
1270 TFS(&tfs_yes_no), 0x04, NULL, HFILL }},
1272 { &hf_cdp_capabilities_switch,
1273 {"Switch", "cdp.capabilities.switch", FT_BOOLEAN, 32,
1274 TFS(&tfs_yes_no), 0x08, NULL, HFILL }},
1276 { &hf_cdp_capabilities_host,
1277 {"Host", "cdp.capabilities.host", FT_BOOLEAN, 32,
1278 TFS(&tfs_yes_no), 0x10, NULL, HFILL }},
1280 { &hf_cdp_capabilities_igmp_capable,
1281 {"IGMP capable", "cdp.capabilities.igmp_capable", FT_BOOLEAN, 32,
1282 TFS(&tfs_yes_no), 0x20, NULL, HFILL }},
1284 { &hf_cdp_capabilities_repeater,
1285 {"Repeater", "cdp.capabilities.repeater", FT_BOOLEAN, 32,
1286 TFS(&tfs_yes_no), 0x40, NULL, HFILL }},
1288 { &hf_cdp_spare_poe_tlv,
1289 { "Spare Pair PoE", "cdp.spare_poe_tlv", FT_UINT8, BASE_HEX,
1290 NULL, 0x0, NULL, HFILL }
1293 { &hf_cdp_spare_poe_tlv_poe,
1294 { "PSE Four-Wire PoE", "cdp.spare_poe_tlv.poe", FT_BOOLEAN, 8,
1295 TFS(&tfs_supported_not_supported), 0x01, NULL, HFILL }
1298 { &hf_cdp_spare_poe_tlv_spare_pair_arch,
1299 { "PD Spare Pair Architecture", "cdp.spare_poe_tlv.spare_pair_arch", FT_BOOLEAN, 8,
1300 TFS(&tfs_shared_independent), 0x02, NULL, HFILL }
1303 { &hf_cdp_spare_poe_tlv_req_spare_pair_poe,
1304 { "PD Request Spare Pair PoE", "cdp.spare_poe_tlv.req_spare_pair_poe", FT_BOOLEAN, 8,
1305 TFS(&tfs_on_off), 0x04, NULL, HFILL }
1308 { &hf_cdp_spare_poe_tlv_pse_spare_pair_poe,
1309 { "PSE Spare Pair PoE", "cdp.spare_poe_tlv.pse_spare_pair_poe", FT_BOOLEAN, 8,
1310 TFS(&tfs_on_off), 0x08, NULL, HFILL }
1314 static gint *ett[] = {
1315 &ett_cdp,
1316 &ett_cdp_tlv,
1317 &ett_cdp_nrgyz_tlv,
1318 &ett_cdp_address,
1319 &ett_cdp_capabilities,
1320 &ett_cdp_checksum,
1321 &ett_cdp_spare_poe_tlv
1324 proto_cdp = proto_register_protocol("Cisco Discovery Protocol",
1325 "CDP", "cdp");
1326 proto_register_field_array(proto_cdp, hf, array_length(hf));
1327 proto_register_subtree_array(ett, array_length(ett));
1330 void
1331 proto_reg_handoff_cdp(void)
1333 dissector_handle_t cdp_handle;
1335 data_handle = find_dissector("data");
1336 cdp_handle = create_dissector_handle(dissect_cdp, proto_cdp);
1337 dissector_add_uint("llc.cisco_pid", 0x2000, cdp_handle);
1338 dissector_add_uint("chdlc.protocol", 0x2000, cdp_handle);
1339 dissector_add_uint("ppp.protocol", 0x0207, cdp_handle);