2 * Routines for the disassembly of the "Cisco Discovery Protocol"
3 * (c) Copyright Hannes R. Boehm <hannes@boehm.org>
5 * $Id: packet-cdp.c,v 1.44 2002/01/24 09:20:47 guy Exp $
7 * Ethereal - Network traffic analyzer
8 * By Gerald Combs <gerald@ethereal.com>
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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
28 #ifdef HAVE_SYS_TYPES_H
29 #include <sys/types.h>
36 #include <epan/packet.h>
37 #include <epan/strutil.h>
43 * http://www.cisco.com/univercd/cc/td/doc/product/lan/trsrb/frames.htm
45 * for some information on CDP.
48 /* Offsets in TLV structure. */
52 static int proto_cdp = -1;
53 static int hf_cdp_version = -1;
54 static int hf_cdp_checksum = -1;
55 static int hf_cdp_ttl = -1;
56 static int hf_cdp_tlvtype = -1;
57 static int hf_cdp_tlvlength = -1;
59 static gint ett_cdp = -1;
60 static gint ett_cdp_tlv = -1;
61 static gint ett_cdp_address = -1;
62 static gint ett_cdp_capabilities = -1;
64 static dissector_handle_t data_handle;
67 dissect_address_tlv(tvbuff_t *tvb, int offset, int length, proto_tree *tree);
69 dissect_capabilities(tvbuff_t *tvb, int offset, int length, proto_tree *tree);
71 add_multi_line_string_to_tree(proto_tree *tree, tvbuff_t *tvb, gint start,
72 gint len, const gchar *prefix);
74 #define TYPE_DEVICE_ID 0x0001
75 #define TYPE_ADDRESS 0x0002
76 #define TYPE_PORT_ID 0x0003
77 #define TYPE_CAPABILITIES 0x0004
78 #define TYPE_IOS_VERSION 0x0005
79 #define TYPE_PLATFORM 0x0006
80 #define TYPE_IP_PREFIX 0x0007
82 #define TYPE_VTP_MGMT_DOMAIN 0x0009 /* Guessed, from tcpdump */
83 #define TYPE_NATIVE_VLAN 0x000a /* Guessed, from tcpdump */
84 #define TYPE_DUPLEX 0x000b /* Guessed, from tcpdump */
86 static const value_string type_vals[] = {
87 { TYPE_DEVICE_ID, "Device ID" },
88 { TYPE_ADDRESS, "Addresses" },
89 { TYPE_PORT_ID, "Port ID" },
90 { TYPE_CAPABILITIES, "Capabilities" },
91 { TYPE_IOS_VERSION, "Software version" },
92 { TYPE_PLATFORM, "Platform" },
93 { TYPE_IP_PREFIX, "IP Prefix (used for ODR)" },
94 { TYPE_VTP_MGMT_DOMAIN, "VTP Management Domain" },
95 { TYPE_NATIVE_VLAN, "Native VLAN" },
96 { TYPE_DUPLEX, "Duplex" },
101 dissect_cdp(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree)
104 proto_tree *cdp_tree = NULL;
109 proto_tree *tlv_tree;
114 if (check_col(pinfo->cinfo, COL_PROTOCOL))
115 col_set_str(pinfo->cinfo, COL_PROTOCOL, "CDP");
116 if (check_col(pinfo->cinfo, COL_INFO))
117 col_set_str(pinfo->cinfo, COL_INFO, "Cisco Discovery Protocol");
120 ti = proto_tree_add_item(tree, proto_cdp, tvb, offset, -1, FALSE);
121 cdp_tree = proto_item_add_subtree(ti, ett_cdp);
124 proto_tree_add_item(cdp_tree, hf_cdp_version, tvb, offset, 1, FALSE);
126 proto_tree_add_uint_format(cdp_tree, hf_cdp_ttl, tvb, offset, 1,
127 tvb_get_guint8(tvb, offset),
129 tvb_get_guint8(tvb, offset));
131 proto_tree_add_item(cdp_tree, hf_cdp_checksum, tvb, offset, 2, FALSE);
134 while (tvb_reported_length_remaining(tvb, offset) != 0) {
135 type = tvb_get_ntohs(tvb, offset + TLV_TYPE);
136 length = tvb_get_ntohs(tvb, offset + TLV_LENGTH);
138 tlvi = proto_tree_add_text(cdp_tree, tvb, offset, 4,
139 "TLV with invalid length %u (< 4)",
141 tlv_tree = proto_item_add_subtree(tlvi, ett_cdp_tlv);
142 proto_tree_add_uint(tlv_tree, hf_cdp_tlvtype, tvb,
143 offset + TLV_TYPE, 2, type);
144 proto_tree_add_uint(tlv_tree, hf_cdp_tlvlength, tvb,
145 offset + TLV_LENGTH, 2, length);
154 tlvi = proto_tree_add_text(cdp_tree, tvb, offset,
155 length, "Device ID: %.*s",
157 tvb_get_ptr(tvb, offset + 4, length - 4));
158 tlv_tree = proto_item_add_subtree(tlvi, ett_cdp_tlv);
159 proto_tree_add_uint(tlv_tree, hf_cdp_tlvtype, tvb,
160 offset + TLV_TYPE, 2, type);
161 proto_tree_add_uint(tlv_tree, hf_cdp_tlvlength, tvb,
162 offset + TLV_LENGTH, 2, length);
163 proto_tree_add_text(tlv_tree, tvb, offset + 4,
164 length - 4, "Device ID: %.*s",
166 tvb_get_ptr(tvb, offset + 4, length - 4));
172 tlvi = proto_tree_add_text(cdp_tree, tvb, offset,
173 length, "Addresses");
174 tlv_tree = proto_item_add_subtree(tlvi, ett_cdp_tlv);
175 proto_tree_add_uint(tlv_tree, hf_cdp_tlvtype, tvb,
176 offset + TLV_TYPE, 2, type);
177 proto_tree_add_uint(tlv_tree, hf_cdp_tlvlength, tvb,
178 offset + TLV_LENGTH, 2, length);
181 naddresses = tvb_get_ntohl(tvb, offset);
182 proto_tree_add_text(tlv_tree, tvb, offset, 4,
183 "Number of addresses: %u", naddresses);
186 while (naddresses != 0) {
187 addr_length = dissect_address_tlv(tvb, offset, length,
191 offset += addr_length;
192 length -= addr_length;
200 real_length = length;
201 if (tvb_get_guint8(tvb, offset + real_length) != 0x00) {
202 /* The length in the TLV doesn't appear to be the
203 length of the TLV, as the byte just past it
204 isn't the first byte of a 2-byte big-endian
205 small integer; make the length of the TLV the length
206 in the TLV, plus 4 bytes for the TLV type and length,
207 minus 1 because that's what makes one capture work. */
208 real_length = length + 3;
210 tlvi = proto_tree_add_text(cdp_tree, tvb, offset,
211 real_length, "Port ID: %.*s",
213 tvb_get_ptr(tvb, offset + 4, real_length - 4));
214 tlv_tree = proto_item_add_subtree(tlvi, ett_cdp_tlv);
215 proto_tree_add_uint(tlv_tree, hf_cdp_tlvtype, tvb,
216 offset + TLV_TYPE, 2, type);
217 proto_tree_add_uint(tlv_tree, hf_cdp_tlvlength, tvb,
218 offset + TLV_LENGTH, 2, length);
219 proto_tree_add_text(tlv_tree, tvb, offset + 4,
221 "Sent through Interface: %.*s",
223 tvb_get_ptr(tvb, offset + 4, real_length - 4));
224 offset += real_length;
227 case TYPE_CAPABILITIES:
228 tlvi = proto_tree_add_text(cdp_tree, tvb, offset,
229 length, "Capabilities");
230 tlv_tree = proto_item_add_subtree(tlvi, ett_cdp_tlv);
231 proto_tree_add_uint(tlv_tree, hf_cdp_tlvtype, tvb,
232 offset + TLV_TYPE, 2, type);
233 proto_tree_add_uint(tlv_tree, hf_cdp_tlvlength, tvb,
234 offset + TLV_LENGTH, 2, length);
237 dissect_capabilities(tvb, offset, length, tlv_tree);
241 case TYPE_IOS_VERSION:
242 tlvi = proto_tree_add_text(cdp_tree, tvb, offset,
243 length, "Software Version");
244 tlv_tree = proto_item_add_subtree(tlvi, ett_cdp_tlv);
245 proto_tree_add_uint(tlv_tree, hf_cdp_tlvtype, tvb,
246 offset + TLV_TYPE, 2, type);
247 proto_tree_add_uint(tlv_tree, hf_cdp_tlvlength, tvb,
248 offset + TLV_LENGTH, 2, length);
249 add_multi_line_string_to_tree(tlv_tree, tvb, offset + 4,
250 length - 4, "Software Version: ");
256 tlvi = proto_tree_add_text(cdp_tree, tvb,
257 offset, length, "Platform: %.*s",
259 tvb_get_ptr(tvb, offset + 4, length - 4));
260 tlv_tree = proto_item_add_subtree(tlvi, ett_cdp_tlv);
261 proto_tree_add_uint(tlv_tree, hf_cdp_tlvtype, tvb,
262 offset + TLV_TYPE, 2, type);
263 proto_tree_add_uint(tlv_tree, hf_cdp_tlvlength, tvb,
264 offset + TLV_LENGTH, 2, length);
265 proto_tree_add_text(tlv_tree, tvb, offset + 4,
266 length - 4, "Platform: %.*s",
268 tvb_get_ptr(tvb, offset + 4, length - 4));
272 tlvi = proto_tree_add_text(cdp_tree, tvb, offset,
273 length, "IP Prefixes: %d",length/5);
275 /* the actual number of prefixes is (length-4)/5
276 but if the variable is not a "float" but "integer"
277 then length/5=(length-4)/5 :) */
279 tlv_tree = proto_item_add_subtree(tlvi, ett_cdp_tlv);
280 proto_tree_add_uint(tlv_tree, hf_cdp_tlvtype, tvb,
281 offset + TLV_TYPE, 2, type);
282 proto_tree_add_uint(tlv_tree, hf_cdp_tlvlength, tvb,
283 offset + TLV_LENGTH, 2, length);
287 proto_tree_add_text(tlv_tree, tvb, offset, 5,
289 ip_to_str(tvb_get_ptr(tvb, offset, 4)),
290 tvb_get_guint8(tvb,offset+4));
295 case TYPE_VTP_MGMT_DOMAIN:
296 tlvi = proto_tree_add_text(cdp_tree, tvb,
297 offset, length, "VTP Management Domain: %.*s",
299 tvb_get_ptr(tvb, offset + 4, length - 4));
300 tlv_tree = proto_item_add_subtree(tlvi, ett_cdp_tlv);
301 proto_tree_add_uint(tlv_tree, hf_cdp_tlvtype, tvb,
302 offset + TLV_TYPE, 2, type);
303 proto_tree_add_uint(tlv_tree, hf_cdp_tlvlength, tvb,
304 offset + TLV_LENGTH, 2, length);
305 proto_tree_add_text(tlv_tree, tvb, offset + 4,
306 length - 4, "VTP Management Domain: %.*s",
308 tvb_get_ptr(tvb, offset + 4, length - 4));
311 case TYPE_NATIVE_VLAN:
312 tlvi = proto_tree_add_text(cdp_tree, tvb,
313 offset, length, "Native VLAN: %u",
314 tvb_get_ntohs(tvb, offset + 4));
315 tlv_tree = proto_item_add_subtree(tlvi, ett_cdp_tlv);
316 proto_tree_add_uint(tlv_tree, hf_cdp_tlvtype, tvb,
317 offset + TLV_TYPE, 2, type);
318 proto_tree_add_uint(tlv_tree, hf_cdp_tlvlength, tvb,
319 offset + TLV_LENGTH, 2, length);
320 proto_tree_add_text(tlv_tree, tvb, offset + 4,
321 length - 4, "Native VLAN: %u",
322 tvb_get_ntohs(tvb, offset + 4));
326 tlvi = proto_tree_add_text(cdp_tree, tvb,
327 offset, length, "Duplex: %s",
328 tvb_get_guint8(tvb, offset + 4) ?
330 tlv_tree = proto_item_add_subtree(tlvi, ett_cdp_tlv);
331 proto_tree_add_uint(tlv_tree, hf_cdp_tlvtype, tvb,
332 offset + TLV_TYPE, 2, type);
333 proto_tree_add_uint(tlv_tree, hf_cdp_tlvlength, tvb,
334 offset + TLV_LENGTH, 2, length);
335 proto_tree_add_text(tlv_tree, tvb, offset + 4,
336 length - 4, "Duplex: %s",
337 tvb_get_guint8(tvb, offset + 4) ?
342 tlvi = proto_tree_add_text(cdp_tree, tvb, offset,
343 length, "Type: %s, length: %u",
344 val_to_str(type, type_vals, "Unknown (0x%04x)"),
346 tlv_tree = proto_item_add_subtree(tlvi, ett_cdp_tlv);
347 proto_tree_add_uint(tlv_tree, hf_cdp_tlvtype, tvb,
348 offset + TLV_TYPE, 2, type);
349 proto_tree_add_uint(tlv_tree, hf_cdp_tlvlength, tvb,
350 offset + TLV_LENGTH, 2, length);
352 proto_tree_add_text(tlv_tree, tvb, offset + 4,
359 call_dissector(data_handle,tvb_new_subset(tvb, offset,-1,tvb_reported_length_remaining(tvb,offset)), pinfo, cdp_tree);
363 #define PROTO_TYPE_NLPID 1
364 #define PROTO_TYPE_IEEE_802_2 2
366 static const value_string proto_type_vals[] = {
367 { PROTO_TYPE_NLPID, "NLPID" },
368 { PROTO_TYPE_IEEE_802_2, "802.2" },
373 dissect_address_tlv(tvbuff_t *tvb, int offset, int length, proto_tree *tree)
376 proto_tree *address_tree;
377 guint8 protocol_type;
378 guint8 protocol_length;
381 guint16 address_length;
382 char *address_type_str;
387 ti = proto_tree_add_text(tree, tvb, offset, length, "Truncated address");
388 address_tree = proto_item_add_subtree(ti, ett_cdp_address);
389 protocol_type = tvb_get_guint8(tvb, offset);
390 proto_tree_add_text(address_tree, tvb, offset, 1, "Protocol type: %s",
391 val_to_str(protocol_type, proto_type_vals, "Unknown (0x%02x)"));
397 protocol_length = tvb_get_guint8(tvb, offset);
398 proto_tree_add_text(address_tree, tvb, offset, 1, "Protocol length: %u",
403 if (length < protocol_length) {
405 proto_tree_add_text(address_tree, tvb, offset, length,
406 "Protocol: %s (truncated)",
407 tvb_bytes_to_str(tvb, offset, length));
412 if (protocol_type == PROTO_TYPE_NLPID && protocol_length == 1) {
413 nlpid = tvb_get_guint8(tvb, offset);
414 protocol_str = val_to_str(nlpid, nlpid_vals, "Unknown (0x%02x)");
417 if (protocol_str == NULL)
418 protocol_str = tvb_bytes_to_str(tvb, offset, protocol_length);
419 proto_tree_add_text(address_tree, tvb, offset, protocol_length,
420 "Protocol: %s", protocol_str);
421 offset += protocol_length;
422 length -= protocol_length;
426 address_length = tvb_get_ntohs(tvb, offset);
427 proto_tree_add_text(address_tree, tvb, offset, 2, "Address length: %u",
432 if (length < address_length) {
434 proto_tree_add_text(address_tree, tvb, offset, length,
435 "Address: %s (truncated)",
436 tvb_bytes_to_str(tvb, offset, length));
440 /* XXX - the Cisco document seems to be saying that, for 802.2-format
441 protocol types, 0xAAAA03 0x000000 0x0800 is IPv6, but 0x0800 is
442 the Ethernet protocol type for IPv4. */
443 length = 2 + protocol_length + 2 + address_length;
444 address_type_str = NULL;
446 if (protocol_type == PROTO_TYPE_NLPID && protocol_length == 1) {
449 /* XXX - dissect NLPID_ISO8473_CLNP as OSI CLNP address? */
452 if (address_length == 4) {
453 /* The address is an IP address. */
454 address_type_str = "IP address";
455 address_str = ip_to_str(tvb_get_ptr(tvb, offset, 4));
460 if (address_type_str == NULL)
461 address_type_str = "Address";
462 if (address_str == NULL) {
463 address_str = tvb_bytes_to_str(tvb, offset, address_length);
465 proto_item_set_text(ti, "%s: %s", address_type_str, address_str);
466 proto_tree_add_text(address_tree, tvb, offset, address_length, "%s: %s",
467 address_type_str, address_str);
468 return 2 + protocol_length + 2 + address_length;
472 dissect_capabilities(tvbuff_t *tvb, int offset, int length, proto_tree *tree)
475 proto_tree *capabilities_tree;
476 guint32 capabilities;
480 capabilities = tvb_get_ntohl(tvb, offset);
481 ti = proto_tree_add_text(tree, tvb, offset, length, "Capabilities: 0x%08x",
483 capabilities_tree = proto_item_add_subtree(ti, ett_cdp_capabilities);
484 proto_tree_add_text(capabilities_tree, tvb, offset, 4,
485 decode_boolean_bitfield(capabilities, 0x01, 4*8,
486 "Performs level 3 routing",
487 "Doesn't perform level 3 routing"));
488 proto_tree_add_text(capabilities_tree, tvb, offset, 4,
489 decode_boolean_bitfield(capabilities, 0x02, 4*8,
490 "Performs level 2 transparent bridging",
491 "Doesn't perform level 2 transparent bridging"));
492 proto_tree_add_text(capabilities_tree, tvb, offset, 4,
493 decode_boolean_bitfield(capabilities, 0x04, 4*8,
494 "Performs level 2 source-route bridging",
495 "Doesn't perform level 2 source-route bridging"));
496 proto_tree_add_text(capabilities_tree, tvb, offset, 4,
497 decode_boolean_bitfield(capabilities, 0x08, 4*8,
498 "Performs level 2 switching",
499 "Doesn't perform level 2 switching"));
500 proto_tree_add_text(capabilities_tree, tvb, offset, 4,
501 decode_boolean_bitfield(capabilities, 0x10, 4*8,
502 "Sends and receives packets for network-layer protocols",
503 "Doesn't send or receive packets for network-layer protocols"));
504 proto_tree_add_text(capabilities_tree, tvb, offset, 4,
505 decode_boolean_bitfield(capabilities, 0x20, 4*8,
506 "Doesn't forward IGMP Report packets on nonrouter ports",
507 "Forwards IGMP Report packets on nonrouter ports"));
508 proto_tree_add_text(capabilities_tree, tvb, offset, 4,
509 decode_boolean_bitfield(capabilities, 0x40, 4*8,
510 "Provides level 1 functionality",
511 "Doesn't provide level 1 functionality"));
515 add_multi_line_string_to_tree(proto_tree *tree, tvbuff_t *tvb, gint start,
516 gint len, const gchar *prefix)
525 prefix_len = strlen(prefix);
528 for (i = 0; i < prefix_len; i++)
532 line_len = tvb_find_line_end(tvb, start, len, &next);
533 data_len = next - start;
534 proto_tree_add_text(tree, tvb, start, data_len, "%s%.*s", prefix,
535 line_len, tvb_get_ptr(tvb, start, line_len));
543 proto_register_cdp(void)
545 static hf_register_info hf[] = {
547 { "Version", "cdp.version", FT_UINT8, BASE_DEC, NULL, 0x0,
551 { "TTL", "cdp.ttl", FT_UINT16, BASE_DEC, NULL, 0x0,
555 { "Checksum", "cdp.checksum", FT_UINT16, BASE_HEX, NULL, 0x0,
559 { "Type", "cdp.tlv.type", FT_UINT16, BASE_HEX, VALS(type_vals), 0x0,
563 { "Length", "cdp.tlv.len", FT_UINT16, BASE_DEC, NULL, 0x0,
566 static gint *ett[] = {
570 &ett_cdp_capabilities,
573 proto_cdp = proto_register_protocol("Cisco Discovery Protocol",
575 proto_register_field_array(proto_cdp, hf, array_length(hf));
576 proto_register_subtree_array(ett, array_length(ett));
580 proto_reg_handoff_cdp(void)
582 dissector_handle_t cdp_handle;
584 data_handle = find_dissector("data");
585 cdp_handle = create_dissector_handle(dissect_cdp, proto_cdp);
586 dissector_add("llc.cisco_pid", 0x2000, cdp_handle);
587 dissector_add("chdlctype", 0x2000, cdp_handle);