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.35 2001/01/25 06:14:13 guy Exp $
7 * Ethereal - Network traffic analyzer
8 * By Gerald Combs <gerald@zing.org>
9 * Copyright 1998 Gerald Combs
12 * This program is free software; you can redistribute it and/or
13 * modify it under the terms of the GNU General Public License
14 * as published by the Free Software Foundation; either version 2
15 * of the License, or (at your option) any later version.
17 * This program is distributed in the hope that it will be useful,
18 * but WITHOUT ANY WARRANTY; without even the implied warranty of
19 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20 * GNU General Public License for more details.
22 * You should have received a copy of the GNU General Public License
23 * along with this program; if not, write to the Free Software
24 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
29 #ifdef HAVE_SYS_TYPES_H
30 #include <sys/types.h>
44 * http://www.cisco.com/univercd/cc/td/doc/product/lan/trsrb/frames.htm
46 * for some information on CDP.
49 /* Offsets in TLV structure. */
53 static int proto_cdp = -1;
54 static int hf_cdp_version = -1;
55 static int hf_cdp_checksum = -1;
56 static int hf_cdp_ttl = -1;
57 static int hf_cdp_tlvtype = -1;
58 static int hf_cdp_tlvlength = -1;
60 static gint ett_cdp = -1;
61 static gint ett_cdp_tlv = -1;
62 static gint ett_cdp_address = -1;
63 static gint ett_cdp_capabilities = -1;
66 dissect_address_tlv(tvbuff_t *tvb, int offset, int length, proto_tree *tree);
68 dissect_capabilities(tvbuff_t *tvb, int offset, int length, proto_tree *tree);
70 add_multi_line_string_to_tree(proto_tree *tree, tvbuff_t *tvb, gint start,
71 gint len, const gchar *prefix);
73 #define TYPE_DEVICE_ID 0x0001
74 #define TYPE_ADDRESS 0x0002
75 #define TYPE_PORT_ID 0x0003
76 #define TYPE_CAPABILITIES 0x0004
77 #define TYPE_IOS_VERSION 0x0005
78 #define TYPE_PLATFORM 0x0006
79 #define TYPE_IP_PREFIX 0x0007
81 #define TYPE_VTP_MGMT_DOMAIN 0x0009 /* Guessed, from tcpdump */
82 #define TYPE_NATIVE_VLAN 0x000a /* Guessed, from tcpdump */
83 #define TYPE_DUPLEX 0x000b /* Guessed, from tcpdump */
85 static const value_string type_vals[] = {
86 { TYPE_DEVICE_ID, "Device ID" },
87 { TYPE_ADDRESS, "Addresses" },
88 { TYPE_PORT_ID, "Port ID" },
89 { TYPE_CAPABILITIES, "Capabilities" },
90 { TYPE_IOS_VERSION, "Software version" },
91 { TYPE_PLATFORM, "Platform" },
92 { TYPE_IP_PREFIX, "IP Prefix (used for ODR)" },
93 { TYPE_VTP_MGMT_DOMAIN, "VTP Management Domain" },
94 { TYPE_NATIVE_VLAN, "Native VLAN" },
95 { TYPE_DUPLEX, "Duplex" },
100 dissect_cdp(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree)
103 proto_tree *cdp_tree = NULL;
108 proto_tree *tlv_tree;
113 if (check_col(pinfo->fd, COL_PROTOCOL))
114 col_set_str(pinfo->fd, COL_PROTOCOL, "CDP");
115 if (check_col(pinfo->fd, COL_INFO))
116 col_set_str(pinfo->fd, COL_INFO, "Cisco Discovery Protocol");
119 ti = proto_tree_add_item(tree, proto_cdp, tvb, offset,
120 tvb_length_remaining(tvb, offset), 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);
142 tlvi = proto_tree_add_text(cdp_tree, tvb, offset,
143 length, "Device ID: %.*s",
145 tvb_get_ptr(tvb, offset + 4, length - 4));
146 tlv_tree = proto_item_add_subtree(tlvi, ett_cdp_tlv);
147 proto_tree_add_uint(tlv_tree, hf_cdp_tlvtype, tvb,
148 offset + TLV_TYPE, 2, type);
149 proto_tree_add_uint(tlv_tree, hf_cdp_tlvlength, tvb,
150 offset + TLV_LENGTH, 2, length);
151 proto_tree_add_text(tlv_tree, tvb, offset + 4,
152 length - 4, "Device ID: %.*s",
154 tvb_get_ptr(tvb, offset + 4, length - 4));
160 tlvi = proto_tree_add_text(cdp_tree, tvb, offset,
161 length, "Addresses");
162 tlv_tree = proto_item_add_subtree(tlvi, ett_cdp_tlv);
163 proto_tree_add_uint(tlv_tree, hf_cdp_tlvtype, tvb,
164 offset + TLV_TYPE, 2, type);
165 proto_tree_add_uint(tlv_tree, hf_cdp_tlvlength, tvb,
166 offset + TLV_LENGTH, 2, length);
169 naddresses = tvb_get_ntohl(tvb, offset);
170 proto_tree_add_text(tlv_tree, tvb, offset, 4,
171 "Number of addresses: %u", naddresses);
174 while (naddresses != 0) {
175 addr_length = dissect_address_tlv(tvb, offset, length,
179 offset += addr_length;
180 length -= addr_length;
188 real_length = length;
189 if (tvb_get_guint8(tvb, offset + real_length) != 0x00) {
190 /* The length in the TLV doesn't appear to be the
191 length of the TLV, as the byte just past it
192 isn't the first byte of a 2-byte big-endian
193 small integer; make the length of the TLV the length
194 in the TLV, plus 4 bytes for the TLV type and length,
195 minus 1 because that's what makes one capture work. */
196 real_length = length + 3;
198 tlvi = proto_tree_add_text(cdp_tree, tvb, offset,
199 real_length, "Port ID: %.*s",
201 tvb_get_ptr(tvb, offset + 4, real_length - 4));
202 tlv_tree = proto_item_add_subtree(tlvi, ett_cdp_tlv);
203 proto_tree_add_uint(tlv_tree, hf_cdp_tlvtype, tvb,
204 offset + TLV_TYPE, 2, type);
205 proto_tree_add_uint(tlv_tree, hf_cdp_tlvlength, tvb,
206 offset + TLV_LENGTH, 2, length);
207 proto_tree_add_text(tlv_tree, tvb, offset + 4,
209 "Sent through Interface: %.*s",
211 tvb_get_ptr(tvb, offset + 4, real_length - 4));
212 offset += real_length;
215 case TYPE_CAPABILITIES:
216 tlvi = proto_tree_add_text(cdp_tree, tvb, offset,
217 length, "Capabilities");
218 tlv_tree = proto_item_add_subtree(tlvi, ett_cdp_tlv);
219 proto_tree_add_uint(tlv_tree, hf_cdp_tlvtype, tvb,
220 offset + TLV_TYPE, 2, type);
221 proto_tree_add_uint(tlv_tree, hf_cdp_tlvlength, tvb,
222 offset + TLV_LENGTH, 2, length);
225 dissect_capabilities(tvb, offset, length, tlv_tree);
229 case TYPE_IOS_VERSION:
230 tlvi = proto_tree_add_text(cdp_tree, tvb, offset,
231 length, "Software Version");
232 tlv_tree = proto_item_add_subtree(tlvi, ett_cdp_tlv);
233 proto_tree_add_uint(tlv_tree, hf_cdp_tlvtype, tvb,
234 offset + TLV_TYPE, 2, type);
235 proto_tree_add_uint(tlv_tree, hf_cdp_tlvlength, tvb,
236 offset + TLV_LENGTH, 2, length);
237 add_multi_line_string_to_tree(tlv_tree, tvb, offset + 4,
238 length - 4, "Software Version: ");
244 tlvi = proto_tree_add_text(cdp_tree, tvb,
245 offset, length, "Platform: %.*s",
247 tvb_get_ptr(tvb, offset + 4, length - 4));
248 tlv_tree = proto_item_add_subtree(tlvi, ett_cdp_tlv);
249 proto_tree_add_uint(tlv_tree, hf_cdp_tlvtype, tvb,
250 offset + TLV_TYPE, 2, type);
251 proto_tree_add_uint(tlv_tree, hf_cdp_tlvlength, tvb,
252 offset + TLV_LENGTH, 2, length);
253 proto_tree_add_text(tlv_tree, tvb, offset + 4,
254 length - 4, "Platform: %.*s",
256 tvb_get_ptr(tvb, offset + 4, length - 4));
260 tlvi = proto_tree_add_text(cdp_tree, tvb, offset,
261 length, "IP Prefixes: %d",length/5);
263 /* the actual number of prefixes is (length-4)/5
264 but if the variable is not a "float" but "integer"
265 then length/5=(length-4)/5 :) */
267 tlv_tree = proto_item_add_subtree(tlvi, ett_cdp_tlv);
268 proto_tree_add_uint(tlv_tree, hf_cdp_tlvtype, tvb,
269 offset + TLV_TYPE, 2, type);
270 proto_tree_add_uint(tlv_tree, hf_cdp_tlvlength, tvb,
271 offset + TLV_LENGTH, 2, length);
275 proto_tree_add_text(tlv_tree, tvb, offset, 5,
277 ip_to_str(tvb_get_ptr(tvb, offset, 4)),
278 tvb_get_guint8(tvb,offset+4));
283 case TYPE_VTP_MGMT_DOMAIN:
284 tlvi = proto_tree_add_text(cdp_tree, tvb,
285 offset, length, "VTP Management Domain: %.*s",
287 tvb_get_ptr(tvb, offset + 4, length - 4));
288 tlv_tree = proto_item_add_subtree(tlvi, ett_cdp_tlv);
289 proto_tree_add_uint(tlv_tree, hf_cdp_tlvtype, tvb,
290 offset + TLV_TYPE, 2, type);
291 proto_tree_add_uint(tlv_tree, hf_cdp_tlvlength, tvb,
292 offset + TLV_LENGTH, 2, length);
293 proto_tree_add_text(tlv_tree, tvb, offset + 4,
294 length - 4, "VTP Management Domain: %.*s",
296 tvb_get_ptr(tvb, offset + 4, length - 4));
299 case TYPE_NATIVE_VLAN:
300 tlvi = proto_tree_add_text(cdp_tree, tvb,
301 offset, length, "Native VLAN: %u",
302 tvb_get_ntohs(tvb, offset + 4));
303 tlv_tree = proto_item_add_subtree(tlvi, ett_cdp_tlv);
304 proto_tree_add_uint(tlv_tree, hf_cdp_tlvtype, tvb,
305 offset + TLV_TYPE, 2, type);
306 proto_tree_add_uint(tlv_tree, hf_cdp_tlvlength, tvb,
307 offset + TLV_LENGTH, 2, length);
308 proto_tree_add_text(tlv_tree, tvb, offset + 4,
309 length - 4, "Native VLAN: %u",
310 tvb_get_ntohs(tvb, offset + 4));
314 tlvi = proto_tree_add_text(cdp_tree, tvb,
315 offset, length, "Duplex: %s",
316 tvb_get_guint8(tvb, offset + 4) ?
318 tlv_tree = proto_item_add_subtree(tlvi, ett_cdp_tlv);
319 proto_tree_add_uint(tlv_tree, hf_cdp_tlvtype, tvb,
320 offset + TLV_TYPE, 2, type);
321 proto_tree_add_uint(tlv_tree, hf_cdp_tlvlength, tvb,
322 offset + TLV_LENGTH, 2, length);
323 proto_tree_add_text(tlv_tree, tvb, offset + 4,
324 length - 4, "Duplex: %s",
325 tvb_get_guint8(tvb, offset + 4) ?
330 tlvi = proto_tree_add_text(cdp_tree, tvb, offset,
331 length, "Type: %s, length: %u",
332 val_to_str(type, type_vals, "Unknown (0x%04x)"),
334 tlv_tree = proto_item_add_subtree(tlvi, ett_cdp_tlv);
335 proto_tree_add_uint(tlv_tree, hf_cdp_tlvtype, tvb,
336 offset + TLV_TYPE, 2, type);
337 proto_tree_add_uint(tlv_tree, hf_cdp_tlvlength, tvb,
338 offset + TLV_LENGTH, 2, length);
340 proto_tree_add_text(tlv_tree, tvb, offset + 4,
347 dissect_data(tvb, offset, pinfo, cdp_tree);
351 #define PROTO_TYPE_NLPID 1
352 #define PROTO_TYPE_IEEE_802_2 2
354 static const value_string proto_type_vals[] = {
355 { PROTO_TYPE_NLPID, "NLPID" },
356 { PROTO_TYPE_IEEE_802_2, "802.2" },
361 dissect_address_tlv(tvbuff_t *tvb, int offset, int length, proto_tree *tree)
364 proto_tree *address_tree;
365 guint8 protocol_type;
366 guint8 protocol_length;
369 guint16 address_length;
370 char *address_type_str;
375 ti = proto_tree_add_notext(tree, tvb, offset, length);
376 address_tree = proto_item_add_subtree(ti, ett_cdp_address);
377 protocol_type = tvb_get_guint8(tvb, offset);
378 proto_tree_add_text(address_tree, tvb, offset, 1, "Protocol type: %s",
379 val_to_str(protocol_type, proto_type_vals, "Unknown (0x%02x)"));
384 proto_item_set_text(ti, "Truncated address");
387 protocol_length = tvb_get_guint8(tvb, offset);
388 proto_tree_add_text(address_tree, tvb, offset, 1, "Protocol length: %u",
393 if (length < protocol_length) {
394 proto_item_set_text(ti, "Truncated address");
396 proto_tree_add_text(address_tree, tvb, offset, length,
397 "Protocol: %s (truncated)",
398 tvb_bytes_to_str(tvb, offset, length));
403 if (protocol_type == PROTO_TYPE_NLPID && protocol_length == 1) {
404 nlpid = tvb_get_guint8(tvb, offset);
405 protocol_str = val_to_str(nlpid, nlpid_vals, "Unknown (0x%02x)");
408 if (protocol_str == NULL)
409 protocol_str = tvb_bytes_to_str(tvb, offset, protocol_length);
410 proto_tree_add_text(address_tree, tvb, offset, protocol_length,
411 "Protocol: %s", protocol_str);
412 offset += protocol_length;
413 length -= protocol_length;
416 proto_item_set_text(ti, "Truncated address");
419 address_length = tvb_get_ntohs(tvb, offset);
420 proto_tree_add_text(address_tree, tvb, offset, 2, "Address length: %u",
425 if (length < address_length) {
426 proto_item_set_text(ti, "Truncated address");
428 proto_tree_add_text(address_tree, tvb, offset, length,
429 "Address: %s (truncated)",
430 tvb_bytes_to_str(tvb, offset, length));
434 /* XXX - the Cisco document seems to be saying that, for 802.2-format
435 protocol types, 0xAAAA03 0x000000 0x0800 is IPv6, but 0x0800 is
436 the Ethernet protocol type for IPv4. */
437 length = 2 + protocol_length + 2 + address_length;
438 address_type_str = NULL;
440 if (protocol_type == PROTO_TYPE_NLPID && protocol_length == 1) {
443 /* XXX - dissect NLPID_ISO8473_CLNP as OSI CLNP address? */
446 if (address_length == 4) {
447 /* The address is an IP address. */
448 address_type_str = "IP address";
449 address_str = ip_to_str(tvb_get_ptr(tvb, offset, 4));
454 if (address_type_str == NULL)
455 address_type_str = "Address";
456 if (address_str == NULL) {
457 address_str = tvb_bytes_to_str(tvb, offset, address_length);
459 proto_item_set_text(ti, "%s: %s", address_type_str, address_str);
460 proto_tree_add_text(address_tree, tvb, offset, address_length, "%s: %s",
461 address_type_str, address_str);
462 return 2 + protocol_length + 2 + address_length;
466 dissect_capabilities(tvbuff_t *tvb, int offset, int length, proto_tree *tree)
469 proto_tree *capabilities_tree;
470 guint32 capabilities;
474 capabilities = tvb_get_ntohl(tvb, offset);
475 ti = proto_tree_add_text(tree, tvb, offset, length, "Capabilities: 0x%08x",
477 capabilities_tree = proto_item_add_subtree(ti, ett_cdp_capabilities);
478 proto_tree_add_text(capabilities_tree, tvb, offset, 4,
479 decode_boolean_bitfield(capabilities, 0x01, 4*8,
480 "Performs level 3 routing",
481 "Doesn't perform level 3 routing"));
482 proto_tree_add_text(capabilities_tree, tvb, offset, 4,
483 decode_boolean_bitfield(capabilities, 0x02, 4*8,
484 "Performs level 2 transparent bridging",
485 "Doesn't perform level 2 transparent bridging"));
486 proto_tree_add_text(capabilities_tree, tvb, offset, 4,
487 decode_boolean_bitfield(capabilities, 0x04, 4*8,
488 "Performs level 2 source-route bridging",
489 "Doesn't perform level 2 source-route bridging"));
490 proto_tree_add_text(capabilities_tree, tvb, offset, 4,
491 decode_boolean_bitfield(capabilities, 0x08, 4*8,
492 "Performs level 2 switching",
493 "Doesn't perform level 2 switching"));
494 proto_tree_add_text(capabilities_tree, tvb, offset, 4,
495 decode_boolean_bitfield(capabilities, 0x10, 4*8,
496 "Sends and receives packets for network-layer protocols",
497 "Doesn't send or receive packets for network-layer protocols"));
498 proto_tree_add_text(capabilities_tree, tvb, offset, 4,
499 decode_boolean_bitfield(capabilities, 0x20, 4*8,
500 "Doesn't forward IGMP Report packets on nonrouter ports",
501 "Forwards IGMP Report packets on nonrouter ports"));
502 proto_tree_add_text(capabilities_tree, tvb, offset, 4,
503 decode_boolean_bitfield(capabilities, 0x40, 4*8,
504 "Provides level 1 functionality",
505 "Doesn't provide level 1 functionality"));
509 add_multi_line_string_to_tree(proto_tree *tree, tvbuff_t *tvb, gint start,
510 gint len, const gchar *prefix)
519 prefix_len = strlen(prefix);
522 for (i = 0; i < prefix_len; i++)
526 line_len = tvb_find_line_end(tvb, start, len, &next);
527 data_len = next - start;
528 proto_tree_add_text(tree, tvb, start, data_len, "%s%.*s", prefix,
529 line_len, tvb_get_ptr(tvb, start, line_len));
537 proto_register_cdp(void)
539 static hf_register_info hf[] = {
541 { "Version", "cdp.version", FT_UINT8, BASE_DEC, NULL, 0x0,
545 { "TTL", "cdp.ttl", FT_UINT16, BASE_DEC, NULL, 0x0,
549 { "Checksum", "cdp.checksum", FT_UINT16, BASE_HEX, NULL, 0x0,
553 { "Type", "cdp.tlv.type", FT_UINT16, BASE_HEX, VALS(type_vals), 0x0,
557 { "Length", "cdp.tlv.len", FT_UINT16, BASE_DEC, NULL, 0x0,
560 static gint *ett[] = {
564 &ett_cdp_capabilities,
567 proto_cdp = proto_register_protocol("Cisco Discovery Protocol",
569 proto_register_field_array(proto_cdp, hf, array_length(hf));
570 proto_register_subtree_array(ett, array_length(ett));
574 proto_reg_handoff_cdp(void)
576 dissector_add("llc.cisco_pid", 0x2000, dissect_cdp, proto_cdp);
577 dissector_add("fr.cisco", 0x2000, dissect_cdp, proto_cdp);