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.48 2002/08/18 15:30:38 gerald 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.
32 #include <epan/packet.h>
33 #include <epan/strutil.h>
39 * http://www.cisco.com/univercd/cc/td/doc/product/lan/trsrb/frames.htm
41 * for some information on CDP.
44 /* Offsets in TLV structure. */
48 static int proto_cdp = -1;
49 static int hf_cdp_version = -1;
50 static int hf_cdp_checksum = -1;
51 static int hf_cdp_ttl = -1;
52 static int hf_cdp_tlvtype = -1;
53 static int hf_cdp_tlvlength = -1;
55 static gint ett_cdp = -1;
56 static gint ett_cdp_tlv = -1;
57 static gint ett_cdp_address = -1;
58 static gint ett_cdp_capabilities = -1;
60 static dissector_handle_t data_handle;
63 dissect_address_tlv(tvbuff_t *tvb, int offset, int length, proto_tree *tree);
65 dissect_capabilities(tvbuff_t *tvb, int offset, int length, proto_tree *tree);
67 add_multi_line_string_to_tree(proto_tree *tree, tvbuff_t *tvb, gint start,
68 gint len, const gchar *prefix);
70 #define TYPE_DEVICE_ID 0x0001
71 #define TYPE_ADDRESS 0x0002
72 #define TYPE_PORT_ID 0x0003
73 #define TYPE_CAPABILITIES 0x0004
74 #define TYPE_IOS_VERSION 0x0005
75 #define TYPE_PLATFORM 0x0006
76 #define TYPE_IP_PREFIX 0x0007
78 #define TYPE_VTP_MGMT_DOMAIN 0x0009 /* Guessed, from tcpdump */
79 #define TYPE_NATIVE_VLAN 0x000a /* Guessed, from tcpdump */
80 #define TYPE_DUPLEX 0x000b /* Guessed, from tcpdump */
82 static const value_string type_vals[] = {
83 { TYPE_DEVICE_ID, "Device ID" },
84 { TYPE_ADDRESS, "Addresses" },
85 { TYPE_PORT_ID, "Port ID" },
86 { TYPE_CAPABILITIES, "Capabilities" },
87 { TYPE_IOS_VERSION, "Software version" },
88 { TYPE_PLATFORM, "Platform" },
89 { TYPE_IP_PREFIX, "IP Prefix (used for ODR)" },
90 { TYPE_VTP_MGMT_DOMAIN, "VTP Management Domain" },
91 { TYPE_NATIVE_VLAN, "Native VLAN" },
92 { TYPE_DUPLEX, "Duplex" },
97 dissect_cdp(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree)
100 proto_tree *cdp_tree = NULL;
105 proto_tree *tlv_tree;
110 if (check_col(pinfo->cinfo, COL_PROTOCOL))
111 col_set_str(pinfo->cinfo, COL_PROTOCOL, "CDP");
112 if (check_col(pinfo->cinfo, COL_INFO))
113 col_set_str(pinfo->cinfo, COL_INFO, "Cisco Discovery Protocol");
116 ti = proto_tree_add_item(tree, proto_cdp, tvb, offset, -1, FALSE);
117 cdp_tree = proto_item_add_subtree(ti, ett_cdp);
120 proto_tree_add_item(cdp_tree, hf_cdp_version, tvb, offset, 1, FALSE);
122 proto_tree_add_uint_format(cdp_tree, hf_cdp_ttl, tvb, offset, 1,
123 tvb_get_guint8(tvb, offset),
125 tvb_get_guint8(tvb, offset));
127 proto_tree_add_item(cdp_tree, hf_cdp_checksum, tvb, offset, 2, FALSE);
130 while (tvb_reported_length_remaining(tvb, offset) != 0) {
131 type = tvb_get_ntohs(tvb, offset + TLV_TYPE);
132 length = tvb_get_ntohs(tvb, offset + TLV_LENGTH);
134 tlvi = proto_tree_add_text(cdp_tree, tvb, offset, 4,
135 "TLV with invalid length %u (< 4)",
137 tlv_tree = proto_item_add_subtree(tlvi, ett_cdp_tlv);
138 proto_tree_add_uint(tlv_tree, hf_cdp_tlvtype, tvb,
139 offset + TLV_TYPE, 2, type);
140 proto_tree_add_uint(tlv_tree, hf_cdp_tlvlength, tvb,
141 offset + TLV_LENGTH, 2, length);
150 tlvi = proto_tree_add_text(cdp_tree, tvb, offset,
151 length, "Device ID: %.*s",
153 tvb_get_ptr(tvb, offset + 4, length - 4));
154 tlv_tree = proto_item_add_subtree(tlvi, ett_cdp_tlv);
155 proto_tree_add_uint(tlv_tree, hf_cdp_tlvtype, tvb,
156 offset + TLV_TYPE, 2, type);
157 proto_tree_add_uint(tlv_tree, hf_cdp_tlvlength, tvb,
158 offset + TLV_LENGTH, 2, length);
159 proto_tree_add_text(tlv_tree, tvb, offset + 4,
160 length - 4, "Device ID: %.*s",
162 tvb_get_ptr(tvb, offset + 4, length - 4));
168 tlvi = proto_tree_add_text(cdp_tree, tvb, offset,
169 length, "Addresses");
170 tlv_tree = proto_item_add_subtree(tlvi, ett_cdp_tlv);
171 proto_tree_add_uint(tlv_tree, hf_cdp_tlvtype, tvb,
172 offset + TLV_TYPE, 2, type);
173 proto_tree_add_uint(tlv_tree, hf_cdp_tlvlength, tvb,
174 offset + TLV_LENGTH, 2, length);
177 naddresses = tvb_get_ntohl(tvb, offset);
178 proto_tree_add_text(tlv_tree, tvb, offset, 4,
179 "Number of addresses: %u", naddresses);
182 while (naddresses != 0) {
183 addr_length = dissect_address_tlv(tvb, offset, length,
187 offset += addr_length;
188 length -= addr_length;
196 real_length = length;
197 if (tvb_get_guint8(tvb, offset + real_length) != 0x00) {
198 /* The length in the TLV doesn't appear to be the
199 length of the TLV, as the byte just past it
200 isn't the first byte of a 2-byte big-endian
201 small integer; make the length of the TLV the length
202 in the TLV, plus 4 bytes for the TLV type and length,
203 minus 1 because that's what makes one capture work. */
204 real_length = length + 3;
206 tlvi = proto_tree_add_text(cdp_tree, tvb, offset,
207 real_length, "Port ID: %.*s",
209 tvb_get_ptr(tvb, offset + 4, real_length - 4));
210 tlv_tree = proto_item_add_subtree(tlvi, ett_cdp_tlv);
211 proto_tree_add_uint(tlv_tree, hf_cdp_tlvtype, tvb,
212 offset + TLV_TYPE, 2, type);
213 proto_tree_add_uint(tlv_tree, hf_cdp_tlvlength, tvb,
214 offset + TLV_LENGTH, 2, length);
215 proto_tree_add_text(tlv_tree, tvb, offset + 4,
217 "Sent through Interface: %.*s",
219 tvb_get_ptr(tvb, offset + 4, real_length - 4));
220 offset += real_length;
223 case TYPE_CAPABILITIES:
224 tlvi = proto_tree_add_text(cdp_tree, tvb, offset,
225 length, "Capabilities");
226 tlv_tree = proto_item_add_subtree(tlvi, ett_cdp_tlv);
227 proto_tree_add_uint(tlv_tree, hf_cdp_tlvtype, tvb,
228 offset + TLV_TYPE, 2, type);
229 proto_tree_add_uint(tlv_tree, hf_cdp_tlvlength, tvb,
230 offset + TLV_LENGTH, 2, length);
233 dissect_capabilities(tvb, offset, length, tlv_tree);
237 case TYPE_IOS_VERSION:
238 tlvi = proto_tree_add_text(cdp_tree, tvb, offset,
239 length, "Software Version");
240 tlv_tree = proto_item_add_subtree(tlvi, ett_cdp_tlv);
241 proto_tree_add_uint(tlv_tree, hf_cdp_tlvtype, tvb,
242 offset + TLV_TYPE, 2, type);
243 proto_tree_add_uint(tlv_tree, hf_cdp_tlvlength, tvb,
244 offset + TLV_LENGTH, 2, length);
245 add_multi_line_string_to_tree(tlv_tree, tvb, offset + 4,
246 length - 4, "Software Version: ");
252 tlvi = proto_tree_add_text(cdp_tree, tvb,
253 offset, length, "Platform: %.*s",
255 tvb_get_ptr(tvb, offset + 4, length - 4));
256 tlv_tree = proto_item_add_subtree(tlvi, ett_cdp_tlv);
257 proto_tree_add_uint(tlv_tree, hf_cdp_tlvtype, tvb,
258 offset + TLV_TYPE, 2, type);
259 proto_tree_add_uint(tlv_tree, hf_cdp_tlvlength, tvb,
260 offset + TLV_LENGTH, 2, length);
261 proto_tree_add_text(tlv_tree, tvb, offset + 4,
262 length - 4, "Platform: %.*s",
264 tvb_get_ptr(tvb, offset + 4, length - 4));
268 tlvi = proto_tree_add_text(cdp_tree, tvb, offset,
269 length, "IP Prefixes: %d",length/5);
271 /* the actual number of prefixes is (length-4)/5
272 but if the variable is not a "float" but "integer"
273 then length/5=(length-4)/5 :) */
275 tlv_tree = proto_item_add_subtree(tlvi, ett_cdp_tlv);
276 proto_tree_add_uint(tlv_tree, hf_cdp_tlvtype, tvb,
277 offset + TLV_TYPE, 2, type);
278 proto_tree_add_uint(tlv_tree, hf_cdp_tlvlength, tvb,
279 offset + TLV_LENGTH, 2, length);
283 proto_tree_add_text(tlv_tree, tvb, offset, 5,
285 ip_to_str(tvb_get_ptr(tvb, offset, 4)),
286 tvb_get_guint8(tvb,offset+4));
291 case TYPE_VTP_MGMT_DOMAIN:
292 tlvi = proto_tree_add_text(cdp_tree, tvb,
293 offset, length, "VTP Management Domain: %.*s",
295 tvb_get_ptr(tvb, offset + 4, length - 4));
296 tlv_tree = proto_item_add_subtree(tlvi, ett_cdp_tlv);
297 proto_tree_add_uint(tlv_tree, hf_cdp_tlvtype, tvb,
298 offset + TLV_TYPE, 2, type);
299 proto_tree_add_uint(tlv_tree, hf_cdp_tlvlength, tvb,
300 offset + TLV_LENGTH, 2, length);
301 proto_tree_add_text(tlv_tree, tvb, offset + 4,
302 length - 4, "VTP Management Domain: %.*s",
304 tvb_get_ptr(tvb, offset + 4, length - 4));
307 case TYPE_NATIVE_VLAN:
308 tlvi = proto_tree_add_text(cdp_tree, tvb,
309 offset, length, "Native VLAN: %u",
310 tvb_get_ntohs(tvb, offset + 4));
311 tlv_tree = proto_item_add_subtree(tlvi, ett_cdp_tlv);
312 proto_tree_add_uint(tlv_tree, hf_cdp_tlvtype, tvb,
313 offset + TLV_TYPE, 2, type);
314 proto_tree_add_uint(tlv_tree, hf_cdp_tlvlength, tvb,
315 offset + TLV_LENGTH, 2, length);
316 proto_tree_add_text(tlv_tree, tvb, offset + 4,
317 length - 4, "Native VLAN: %u",
318 tvb_get_ntohs(tvb, offset + 4));
322 tlvi = proto_tree_add_text(cdp_tree, tvb,
323 offset, length, "Duplex: %s",
324 tvb_get_guint8(tvb, offset + 4) ?
326 tlv_tree = proto_item_add_subtree(tlvi, ett_cdp_tlv);
327 proto_tree_add_uint(tlv_tree, hf_cdp_tlvtype, tvb,
328 offset + TLV_TYPE, 2, type);
329 proto_tree_add_uint(tlv_tree, hf_cdp_tlvlength, tvb,
330 offset + TLV_LENGTH, 2, length);
331 proto_tree_add_text(tlv_tree, tvb, offset + 4,
332 length - 4, "Duplex: %s",
333 tvb_get_guint8(tvb, offset + 4) ?
338 tlvi = proto_tree_add_text(cdp_tree, tvb, offset,
339 length, "Type: %s, length: %u",
340 val_to_str(type, type_vals, "Unknown (0x%04x)"),
342 tlv_tree = proto_item_add_subtree(tlvi, ett_cdp_tlv);
343 proto_tree_add_uint(tlv_tree, hf_cdp_tlvtype, tvb,
344 offset + TLV_TYPE, 2, type);
345 proto_tree_add_uint(tlv_tree, hf_cdp_tlvlength, tvb,
346 offset + TLV_LENGTH, 2, length);
348 proto_tree_add_text(tlv_tree, tvb, offset + 4,
355 call_dissector(data_handle, tvb_new_subset(tvb, offset, -1, -1), pinfo,
360 #define PROTO_TYPE_NLPID 1
361 #define PROTO_TYPE_IEEE_802_2 2
363 static const value_string proto_type_vals[] = {
364 { PROTO_TYPE_NLPID, "NLPID" },
365 { PROTO_TYPE_IEEE_802_2, "802.2" },
370 dissect_address_tlv(tvbuff_t *tvb, int offset, int length, proto_tree *tree)
373 proto_tree *address_tree;
374 guint8 protocol_type;
375 guint8 protocol_length;
378 guint16 address_length;
379 char *address_type_str;
384 ti = proto_tree_add_text(tree, tvb, offset, length, "Truncated address");
385 address_tree = proto_item_add_subtree(ti, ett_cdp_address);
386 protocol_type = tvb_get_guint8(tvb, offset);
387 proto_tree_add_text(address_tree, tvb, offset, 1, "Protocol type: %s",
388 val_to_str(protocol_type, proto_type_vals, "Unknown (0x%02x)"));
394 protocol_length = tvb_get_guint8(tvb, offset);
395 proto_tree_add_text(address_tree, tvb, offset, 1, "Protocol length: %u",
400 if (length < protocol_length) {
402 proto_tree_add_text(address_tree, tvb, offset, length,
403 "Protocol: %s (truncated)",
404 tvb_bytes_to_str(tvb, offset, length));
409 if (protocol_type == PROTO_TYPE_NLPID && protocol_length == 1) {
410 nlpid = tvb_get_guint8(tvb, offset);
411 protocol_str = val_to_str(nlpid, nlpid_vals, "Unknown (0x%02x)");
414 if (protocol_str == NULL)
415 protocol_str = tvb_bytes_to_str(tvb, offset, protocol_length);
416 proto_tree_add_text(address_tree, tvb, offset, protocol_length,
417 "Protocol: %s", protocol_str);
418 offset += protocol_length;
419 length -= protocol_length;
423 address_length = tvb_get_ntohs(tvb, offset);
424 proto_tree_add_text(address_tree, tvb, offset, 2, "Address length: %u",
429 if (length < address_length) {
431 proto_tree_add_text(address_tree, tvb, offset, length,
432 "Address: %s (truncated)",
433 tvb_bytes_to_str(tvb, offset, length));
437 /* XXX - the Cisco document seems to be saying that, for 802.2-format
438 protocol types, 0xAAAA03 0x000000 0x0800 is IPv6, but 0x0800 is
439 the Ethernet protocol type for IPv4. */
440 length = 2 + protocol_length + 2 + address_length;
441 address_type_str = NULL;
443 if (protocol_type == PROTO_TYPE_NLPID && protocol_length == 1) {
446 /* XXX - dissect NLPID_ISO8473_CLNP as OSI CLNP address? */
449 if (address_length == 4) {
450 /* The address is an IP address. */
451 address_type_str = "IP address";
452 address_str = ip_to_str(tvb_get_ptr(tvb, offset, 4));
457 if (address_type_str == NULL)
458 address_type_str = "Address";
459 if (address_str == NULL) {
460 address_str = tvb_bytes_to_str(tvb, offset, address_length);
462 proto_item_set_text(ti, "%s: %s", address_type_str, address_str);
463 proto_tree_add_text(address_tree, tvb, offset, address_length, "%s: %s",
464 address_type_str, address_str);
465 return 2 + protocol_length + 2 + address_length;
469 dissect_capabilities(tvbuff_t *tvb, int offset, int length, proto_tree *tree)
472 proto_tree *capabilities_tree;
473 guint32 capabilities;
477 capabilities = tvb_get_ntohl(tvb, offset);
478 ti = proto_tree_add_text(tree, tvb, offset, length, "Capabilities: 0x%08x",
480 capabilities_tree = proto_item_add_subtree(ti, ett_cdp_capabilities);
481 proto_tree_add_text(capabilities_tree, tvb, offset, 4,
482 decode_boolean_bitfield(capabilities, 0x01, 4*8,
483 "Performs level 3 routing",
484 "Doesn't perform level 3 routing"));
485 proto_tree_add_text(capabilities_tree, tvb, offset, 4,
486 decode_boolean_bitfield(capabilities, 0x02, 4*8,
487 "Performs level 2 transparent bridging",
488 "Doesn't perform level 2 transparent bridging"));
489 proto_tree_add_text(capabilities_tree, tvb, offset, 4,
490 decode_boolean_bitfield(capabilities, 0x04, 4*8,
491 "Performs level 2 source-route bridging",
492 "Doesn't perform level 2 source-route bridging"));
493 proto_tree_add_text(capabilities_tree, tvb, offset, 4,
494 decode_boolean_bitfield(capabilities, 0x08, 4*8,
495 "Performs level 2 switching",
496 "Doesn't perform level 2 switching"));
497 proto_tree_add_text(capabilities_tree, tvb, offset, 4,
498 decode_boolean_bitfield(capabilities, 0x10, 4*8,
499 "Sends and receives packets for network-layer protocols",
500 "Doesn't send or receive packets for network-layer protocols"));
501 proto_tree_add_text(capabilities_tree, tvb, offset, 4,
502 decode_boolean_bitfield(capabilities, 0x20, 4*8,
503 "Doesn't forward IGMP Report packets on nonrouter ports",
504 "Forwards IGMP Report packets on nonrouter ports"));
505 proto_tree_add_text(capabilities_tree, tvb, offset, 4,
506 decode_boolean_bitfield(capabilities, 0x40, 4*8,
507 "Provides level 1 functionality",
508 "Doesn't provide level 1 functionality"));
512 add_multi_line_string_to_tree(proto_tree *tree, tvbuff_t *tvb, gint start,
513 gint len, const gchar *prefix)
522 prefix_len = strlen(prefix);
525 for (i = 0; i < prefix_len; i++)
529 line_len = tvb_find_line_end(tvb, start, len, &next, FALSE);
530 data_len = next - start;
531 proto_tree_add_text(tree, tvb, start, data_len, "%s%.*s", prefix,
532 line_len, tvb_get_ptr(tvb, start, line_len));
540 proto_register_cdp(void)
542 static hf_register_info hf[] = {
544 { "Version", "cdp.version", FT_UINT8, BASE_DEC, NULL, 0x0,
548 { "TTL", "cdp.ttl", FT_UINT16, BASE_DEC, NULL, 0x0,
552 { "Checksum", "cdp.checksum", FT_UINT16, BASE_HEX, NULL, 0x0,
556 { "Type", "cdp.tlv.type", FT_UINT16, BASE_HEX, VALS(type_vals), 0x0,
560 { "Length", "cdp.tlv.len", FT_UINT16, BASE_DEC, NULL, 0x0,
563 static gint *ett[] = {
567 &ett_cdp_capabilities,
570 proto_cdp = proto_register_protocol("Cisco Discovery Protocol",
572 proto_register_field_array(proto_cdp, hf, array_length(hf));
573 proto_register_subtree_array(ett, array_length(ett));
577 proto_reg_handoff_cdp(void)
579 dissector_handle_t cdp_handle;
581 data_handle = find_dissector("data");
582 cdp_handle = create_dissector_handle(dissect_cdp, proto_cdp);
583 dissector_add("llc.cisco_pid", 0x2000, cdp_handle);
584 dissector_add("chdlctype", 0x2000, cdp_handle);
585 dissector_add("ppp.protocol", 0x0207, cdp_handle);