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.18 2000/01/13 00:41:09 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>
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_flags = -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;
65 dissect_address_tlv(const u_char *pd, int offset, int length, proto_tree *tree);
67 dissect_capabilities(const u_char *pd, int offset, int length, proto_tree *tree);
69 add_multi_line_string_to_tree(proto_tree *tree, gint start, gint len,
70 const gchar *prefix, const gchar *string);
72 #define TYPE_DEVICE_ID 0x0001
73 #define TYPE_ADDRESS 0x0002
74 #define TYPE_PORT_ID 0x0003
75 #define TYPE_CAPABILITIES 0x0004
76 #define TYPE_IOS_VERSION 0x0005
77 #define TYPE_PLATFORM 0x0006
79 static const value_string type_vals[] = {
80 { TYPE_DEVICE_ID, "Device ID" },
81 { TYPE_ADDRESS, "Addresses" },
82 { TYPE_PORT_ID, "Port ID" },
83 { TYPE_CAPABILITIES, "Capabilities" },
84 { TYPE_IOS_VERSION, "Software version" },
85 { TYPE_PLATFORM, "Platform" },
90 dissect_cdp(const u_char *pd, int offset, frame_data *fd, proto_tree *tree)
93 proto_tree *cdp_tree = NULL;
104 if (check_col(fd, COL_PROTOCOL))
105 col_add_str(fd, COL_PROTOCOL, "CDP");
106 if (check_col(fd, COL_INFO))
107 col_add_str(fd, COL_INFO, "Cisco Discovery Protocol");
110 ti = proto_tree_add_item(tree, proto_cdp, offset, END_OF_FRAME, NULL);
111 cdp_tree = proto_item_add_subtree(ti, ett_cdp);
114 proto_tree_add_item(cdp_tree, hf_cdp_version, offset, 1, pd[offset]);
116 proto_tree_add_item_format(cdp_tree, hf_cdp_ttl, offset, 1,
118 "TTL: %u seconds", pd[offset]);
120 proto_tree_add_item_format(cdp_tree, hf_cdp_flags, offset, 2,
122 "Checksum: 0x%04x", pntohs(&pd[offset]));
125 while( IS_DATA_IN_FRAME(offset) ){
126 type = pntohs(&pd[offset + TLV_TYPE]);
127 length = pntohs(&pd[offset + TLV_LENGTH]);
128 type_str = val_to_str(type, type_vals,
134 tlvi = proto_tree_add_text(cdp_tree, offset,
135 length, "Device ID: %s",
137 tlv_tree = proto_item_add_subtree(tlvi,
139 proto_tree_add_item(tlv_tree, hf_cdp_tlvtype,
140 offset + TLV_TYPE, 2, type);
141 proto_tree_add_item(tlv_tree, hf_cdp_tlvlength,
142 offset + TLV_LENGTH, 2, length);
143 proto_tree_add_text(tlv_tree, offset + 4,
144 length - 4, "Device ID: %s",
150 tlvi = proto_tree_add_text(cdp_tree, offset,
153 tlv_tree = proto_item_add_subtree(tlvi,
155 proto_tree_add_item(tlv_tree, hf_cdp_tlvtype,
156 offset + TLV_TYPE, 2, type);
157 proto_tree_add_item(tlv_tree, hf_cdp_tlvlength,
158 offset + TLV_LENGTH, 2, length);
161 naddresses = pntohl(&pd[offset]);
162 proto_tree_add_text(tlv_tree, offset, 4,
163 "Number of addresses: %u", naddresses);
166 while (naddresses != 0) {
167 addr_length = dissect_address_tlv(pd,
168 offset, length, tlv_tree);
171 offset += addr_length;
172 length -= addr_length;
179 real_length = length;
180 if (pd[offset + real_length] != 0x00) {
181 /* The length in the TLV doesn't
182 appear to be the length of the
183 TLV, as the byte just past it
184 isn't the first byte of a 2-byte
185 big-endian small integer; make
186 the length of the TLV the length
187 in the TLV, plus 4 bytes for the
188 TLV type and length, minus 1
189 because that's what makes one
191 real_length = length + 3;
193 tlvi = proto_tree_add_text(cdp_tree, offset,
194 real_length, "Port ID: %s",
196 tlv_tree = proto_item_add_subtree(tlvi,
198 proto_tree_add_item(tlv_tree, hf_cdp_tlvtype,
199 offset + TLV_TYPE, 2, type);
200 proto_tree_add_item(tlv_tree, hf_cdp_tlvlength,
201 offset + TLV_LENGTH, 2, length);
202 proto_tree_add_text(tlv_tree, offset + 4,
204 "Sent through Interface: %s",
206 offset += real_length;
208 case TYPE_CAPABILITIES:
209 tlvi = proto_tree_add_text(cdp_tree, offset,
210 length, "Capabilities");
211 tlv_tree = proto_item_add_subtree(tlvi,
213 proto_tree_add_item(tlv_tree, hf_cdp_tlvtype,
214 offset + TLV_TYPE, 2, type);
215 proto_tree_add_item(tlv_tree, hf_cdp_tlvlength,
216 offset + TLV_LENGTH, 2, length);
219 dissect_capabilities(pd, offset, length,
223 case TYPE_IOS_VERSION:
224 tlvi = proto_tree_add_text(cdp_tree, offset,
225 length, "Software Version");
226 tlv_tree = proto_item_add_subtree(tlvi,
228 proto_tree_add_item(tlv_tree, hf_cdp_tlvtype,
229 offset + TLV_TYPE, 2, type);
230 proto_tree_add_item(tlv_tree, hf_cdp_tlvlength,
231 offset + TLV_LENGTH, 2, length);
232 add_multi_line_string_to_tree(tlv_tree,
233 offset + 4, length - 4, "Software Version: ",
239 stringmem = malloc(length);
240 memset(stringmem, '\0', length);
241 memcpy(stringmem, &pd[offset+4], length - 4 );
242 tlvi = proto_tree_add_text(cdp_tree,
243 offset, length, "Platform: %s",
245 tlv_tree = proto_item_add_subtree(tlvi,
247 proto_tree_add_item(tlv_tree, hf_cdp_tlvtype,
248 offset + TLV_TYPE, 2, type);
249 proto_tree_add_item(tlv_tree, hf_cdp_tlvlength,
250 offset + TLV_LENGTH, 2, length);
251 proto_tree_add_text(tlv_tree, offset + 4,
252 length - 4, "Platform: %s", stringmem);
257 tlvi = proto_tree_add_text(cdp_tree, offset,
258 length, "Type: %s, length: %u",
260 tlv_tree = proto_item_add_subtree(tlvi,
262 proto_tree_add_item(tlv_tree, hf_cdp_tlvtype,
263 offset + TLV_TYPE, 2, type);
264 proto_tree_add_item(tlv_tree, hf_cdp_tlvlength,
265 offset + TLV_LENGTH, 2, length);
267 proto_tree_add_text(tlv_tree,
268 offset + 4, length - 4, "Data");
274 dissect_data(pd, offset, fd, cdp_tree);
278 #define PROTO_TYPE_NLPID 1
279 #define PROTO_TYPE_IEEE_802_2 2
281 static const value_string proto_type_vals[] = {
282 { PROTO_TYPE_NLPID, "NLPID" },
283 { PROTO_TYPE_IEEE_802_2, "802.2" },
288 dissect_address_tlv(const u_char *pd, int offset, int length, proto_tree *tree)
291 proto_tree *address_tree;
292 guint8 protocol_type;
293 guint8 protocol_length;
295 guint16 address_length;
296 char *address_type_str;
301 protocol_type = pd[offset];
303 ti = proto_tree_add_text(tree, offset, length, "Truncated address");
304 address_tree = proto_item_add_subtree(ti, ett_cdp_address);
305 proto_tree_add_text(address_tree, offset, 1, "Protocol type: %s",
306 val_to_str(protocol_type, proto_type_vals, "Unknown (0x%02x)"));
309 protocol_length = pd[offset + 1];
310 if (length < 2 + protocol_length) {
311 ti = proto_tree_add_text(tree, offset, length, "Truncated address");
312 address_tree = proto_item_add_subtree(ti, ett_cdp_address);
313 proto_tree_add_text(address_tree, offset, 1, "Protocol type: %s",
314 val_to_str(protocol_type, proto_type_vals, "Unknown (0x%02x)"));
316 proto_tree_add_text(address_tree, offset, 1, "Protocol length: %u",
321 if (length < 2 + protocol_length) {
322 ti = proto_tree_add_text(tree, offset, length, "Truncated address");
323 address_tree = proto_item_add_subtree(ti, ett_cdp_address);
324 proto_tree_add_text(address_tree, offset, 1, "Protocol type: %s",
325 val_to_str(protocol_type, proto_type_vals, "Unknown (0x%02x)"));
328 proto_tree_add_text(address_tree, offset, 1, "Protocol length: %u",
333 proto_tree_add_text(address_tree, offset, length,
334 "Protocol: %s (truncated)", bytes_to_str(&pd[offset], length));
340 if (protocol_type == PROTO_TYPE_NLPID && protocol_length == 1)
341 protocol_str = val_to_str(pd[offset + 2], nlpid_vals, "Unknown (0x%02x)");
342 if (protocol_str == NULL)
343 protocol_str = bytes_to_str(&pd[offset], protocol_length);
345 if (length < 2 + protocol_length + 2) {
346 ti = proto_tree_add_text(tree, offset, length, "Truncated address");
347 address_tree = proto_item_add_subtree(ti, ett_cdp_address);
348 proto_tree_add_text(address_tree, offset, 1, "Protocol type: %s",
349 val_to_str(protocol_type, proto_type_vals, "Unknown (0x%02x)"));
352 proto_tree_add_text(address_tree, offset, 1, "Protocol length: %u",
356 proto_tree_add_text(address_tree, offset, protocol_length,
357 "Protocol: %s", protocol_str);
360 address_length = pntohs(&pd[offset + 2 + protocol_length]);
362 if (length < 2 + protocol_length + 2 + address_length) {
363 ti = proto_tree_add_text(tree, offset, length, "Truncated address");
364 address_tree = proto_item_add_subtree(ti, ett_cdp_address);
365 proto_tree_add_text(address_tree, offset, 1, "Protocol type: %s",
366 val_to_str(protocol_type, proto_type_vals, "Unknown (0x%02x)"));
369 proto_tree_add_text(address_tree, offset, 1, "Protocol length: %u",
373 proto_tree_add_text(address_tree, offset, protocol_length,
374 "Protocol: %s", protocol_str);
375 offset += protocol_length;
376 length -= protocol_length;
377 proto_tree_add_text(address_tree, offset, 2, "Address length: %u",
382 proto_tree_add_text(address_tree, offset, length,
383 "Address: %s (truncated)", bytes_to_str(&pd[offset], length));
388 /* XXX - the Cisco document seems to be saying that, for 802.2-format
389 protocol types, 0xAAAA03 0x000000 0x0800 is IPv6, but 0x0800 is
390 the Ethernet protocol type for IPv4. */
391 length = 2 + protocol_length + 2 + address_length;
392 address_type_str = NULL;
394 if (protocol_type == PROTO_TYPE_NLPID && protocol_length == 1) {
395 switch (pd[offset + 2]) {
397 /* XXX - dissect NLPID_ISO8473_CLNP as OSI CLNP address? */
400 if (address_length == 4) {
401 /* The address is an IP address. */
402 address_type_str = "IP address";
403 address_str = ip_to_str(&pd[offset + 2 + protocol_length + 2]);
408 if (address_type_str == NULL)
409 address_type_str = "Address";
410 if (address_str == NULL) {
411 address_str = bytes_to_str(&pd[offset + 2 + protocol_length + 2],
414 ti = proto_tree_add_text(tree, offset, length, "%s: %s",
415 address_type_str, address_str);
416 address_tree = proto_item_add_subtree(ti, ett_cdp_address);
417 proto_tree_add_text(address_tree, offset, 1, "Protocol type: %s",
418 val_to_str(protocol_type, proto_type_vals, "Unknown (0x%02x)"));
421 proto_tree_add_text(address_tree, offset, 1, "Protocol length: %u",
425 proto_tree_add_text(address_tree, offset, protocol_length,
426 "Protocol: %s", protocol_str);
427 offset += protocol_length;
428 length -= protocol_length;
429 proto_tree_add_text(address_tree, offset, 2, "Address length: %u",
433 proto_tree_add_text(address_tree, offset, address_length, "%s: %s",
434 address_type_str, address_str);
435 return 2 + protocol_length + 2 + address_length;
439 dissect_capabilities(const u_char *pd, int offset, int length, proto_tree *tree)
442 proto_tree *capabilities_tree;
443 guint32 capabilities;
447 capabilities = pntohl(&pd[offset]);
448 ti = proto_tree_add_text(tree, offset, length, "Capabilities: 0x%08x",
450 capabilities_tree = proto_item_add_subtree(ti, ett_cdp_capabilities);
451 proto_tree_add_text(capabilities_tree, offset, 4,
452 decode_boolean_bitfield(capabilities, 0x01, 4*8,
453 "Performs level 3 routing",
454 "Doesn't perform level 3 routing"));
455 proto_tree_add_text(capabilities_tree, offset, 4,
456 decode_boolean_bitfield(capabilities, 0x02, 4*8,
457 "Performs level 2 transparent bridging",
458 "Doesn't perform level 2 transparent bridging"));
459 proto_tree_add_text(capabilities_tree, offset, 4,
460 decode_boolean_bitfield(capabilities, 0x04, 4*8,
461 "Performs level 2 source-route bridging",
462 "Doesn't perform level 2 source-route bridging"));
463 proto_tree_add_text(capabilities_tree, offset, 4,
464 decode_boolean_bitfield(capabilities, 0x08, 4*8,
465 "Performs level 2 switching",
466 "Doesn't perform level 2 switching"));
467 proto_tree_add_text(capabilities_tree, offset, 4,
468 decode_boolean_bitfield(capabilities, 0x10, 4*8,
469 "Sends and receives packets for network-layer protocols",
470 "Doesn't send or receive packets for network-layer protocols"));
471 proto_tree_add_text(capabilities_tree, offset, 4,
472 decode_boolean_bitfield(capabilities, 0x20, 4*8,
473 "Doesn't forward IGMP Report packets on nonrouter ports",
474 "Forwards IGMP Report packets on nonrouter ports"));
475 proto_tree_add_text(capabilities_tree, offset, 4,
476 decode_boolean_bitfield(capabilities, 0x40, 4*8,
477 "Provides level 1 functionality",
478 "Doesn't provide level 1 functionality"));
482 add_multi_line_string_to_tree(proto_tree *tree, gint start, gint len,
483 const gchar *prefix, const gchar *string)
492 prefix_len = strlen(prefix);
495 for (i = 0; i < prefix_len; i++)
503 data_len = line_len + 1;
505 line_len = strlen(p);
508 proto_tree_add_text(tree, start, data_len, "%s%.*s", prefix,
519 proto_register_cdp(void)
521 static hf_register_info hf[] = {
523 { "Version", "cdp.version", FT_UINT8, BASE_DEC, NULL, 0x0,
527 { "Flags", "cdp.flags", FT_UINT8, BASE_HEX, NULL, 0x0,
531 { "TTL", "cdp.ttl", FT_UINT16, BASE_DEC, NULL, 0x0,
535 { "Type", "cdp.tlv.type", FT_UINT16, BASE_HEX, VALS(type_vals), 0x0,
539 { "Length", "cdp.tlv.len", FT_UINT16, BASE_DEC, NULL, 0x0,
542 static gint *ett[] = {
546 &ett_cdp_capabilities,
549 proto_cdp = proto_register_protocol("Cisco Discovery Protocol", "cdp");
550 proto_register_field_array(proto_cdp, hf, array_length(hf));
551 proto_register_subtree_array(ett, array_length(ett));