Boolean fields are just like other fields - if you use the field name
[obnox/wireshark/wip.git] / packet-cdp.c
1 /* packet-cdp.c
2  * Routines for the disassembly of the "Cisco Discovery Protocol"
3  * (c) Copyright Hannes R. Boehm <hannes@boehm.org>
4  *
5  * $Id: packet-cdp.c,v 1.35 2001/01/25 06:14:13 guy Exp $
6  *
7  * Ethereal - Network traffic analyzer
8  * By Gerald Combs <gerald@zing.org>
9  * Copyright 1998 Gerald Combs
10  * 
11  * 
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.
16  * 
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.
21  * 
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.
25  */
26  
27 #include "config.h"
28
29 #ifdef HAVE_SYS_TYPES_H
30 #include <sys/types.h>
31 #endif
32
33 #include <stdlib.h>
34 #include <string.h>
35
36 #include <glib.h>
37 #include "packet.h"
38 #include "strutil.h"
39 #include "nlpid.h"
40
41 /*
42  * See
43  *
44  *      http://www.cisco.com/univercd/cc/td/doc/product/lan/trsrb/frames.htm
45  *
46  * for some information on CDP.
47  */
48
49 /* Offsets in TLV structure. */
50 #define TLV_TYPE        0
51 #define TLV_LENGTH      2
52
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;
59
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;
64
65 static int
66 dissect_address_tlv(tvbuff_t *tvb, int offset, int length, proto_tree *tree);
67 static void
68 dissect_capabilities(tvbuff_t *tvb, int offset, int length, proto_tree *tree);
69 static void
70 add_multi_line_string_to_tree(proto_tree *tree, tvbuff_t *tvb, gint start,
71   gint len, const gchar *prefix);
72
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
80
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 */
84
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" },
96         { 0,                    NULL },
97 };
98         
99 static void 
100 dissect_cdp(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree)
101 {
102     proto_item *ti; 
103     proto_tree *cdp_tree = NULL;
104     int offset = 0;
105     guint16 type;
106     guint16 length;
107     proto_item *tlvi;
108     proto_tree *tlv_tree;
109     int real_length;
110     guint32 naddresses;
111     int addr_length;
112
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"); 
117
118     if (tree){
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);
122         
123         /* CDP header */
124         proto_tree_add_item(cdp_tree, hf_cdp_version, tvb, offset, 1, FALSE);
125         offset += 1;
126         proto_tree_add_uint_format(cdp_tree, hf_cdp_ttl, tvb, offset, 1,
127                                    tvb_get_guint8(tvb, offset),
128                                    "TTL: %u seconds",
129                                    tvb_get_guint8(tvb, offset));
130         offset += 1;
131         proto_tree_add_item(cdp_tree, hf_cdp_checksum, tvb, offset, 2, FALSE);
132         offset += 2;
133
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);
137
138             switch (type) {
139
140             case TYPE_DEVICE_ID:
141                 /* Device ID */
142                 tlvi = proto_tree_add_text(cdp_tree, tvb, offset,
143                             length, "Device ID: %.*s",
144                             length - 4,
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",
153                             length - 4,
154                             tvb_get_ptr(tvb, offset + 4, length - 4));
155                 offset += length;
156                 break;
157
158             case TYPE_ADDRESS:
159                 /* Addresses */
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);
167                 offset += 4;
168                 length -= 4;
169                 naddresses = tvb_get_ntohl(tvb, offset);
170                 proto_tree_add_text(tlv_tree, tvb, offset, 4,
171                             "Number of addresses: %u", naddresses);
172                 offset += 4;
173                 length -= 4;
174                 while (naddresses != 0) {
175                     addr_length = dissect_address_tlv(tvb, offset, length,
176                                 tlv_tree);
177                     if (addr_length < 0)
178                         break;
179                     offset += addr_length;
180                     length -= addr_length;
181
182                     naddresses--;
183                 }
184                 offset += length;
185                 break;
186
187             case TYPE_PORT_ID:
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;
197                 }
198                 tlvi = proto_tree_add_text(cdp_tree, tvb, offset,
199                             real_length, "Port ID: %.*s",
200                             real_length - 4,
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,
208                             real_length - 4,
209                             "Sent through Interface: %.*s",
210                             real_length - 4,
211                             tvb_get_ptr(tvb, offset + 4, real_length - 4));
212                 offset += real_length;
213                 break;
214
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);
223                 offset += 4;
224                 length -= 4;
225                 dissect_capabilities(tvb, offset, length, tlv_tree);
226                 offset += length;
227                 break;
228
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: ");
239                 offset += length;
240                 break;
241
242             case TYPE_PLATFORM:
243                 /* ??? platform */
244                 tlvi = proto_tree_add_text(cdp_tree, tvb,
245                             offset, length, "Platform: %.*s",
246                             length - 4,
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",
255                             length - 4,
256                             tvb_get_ptr(tvb, offset + 4, length - 4));
257                 offset += length;
258                 break;
259             case TYPE_IP_PREFIX:
260                 tlvi = proto_tree_add_text(cdp_tree, tvb, offset,
261                             length, "IP Prefixes: %d",length/5);
262
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  :)  */
266                             
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);
272                 offset += 4;
273                 length -= 4;
274                 while (length > 0) {
275                         proto_tree_add_text(tlv_tree, tvb, offset, 5,
276                                 "IP Prefix = %s/%u",
277                                 ip_to_str(tvb_get_ptr(tvb, offset, 4)),
278                                 tvb_get_guint8(tvb,offset+4));
279                         offset += 5;
280                         length -= 5;
281                 }
282                 break;
283             case TYPE_VTP_MGMT_DOMAIN:
284                 tlvi = proto_tree_add_text(cdp_tree, tvb,
285                             offset, length, "VTP Management Domain: %.*s",
286                             length - 4,
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",
295                             length - 4,
296                             tvb_get_ptr(tvb, offset + 4, length - 4));
297                 offset += length;
298                 break;
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));
311                 offset += length;
312                 break;
313             case TYPE_DUPLEX:
314                 tlvi = proto_tree_add_text(cdp_tree, tvb,
315                             offset, length, "Duplex: %s",
316                                            tvb_get_guint8(tvb, offset + 4) ?
317                                            "Full" : "Half" );
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) ?
326                                     "Full" : "Half" );
327                 offset += length;
328                 break;
329             default:
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)"),
333                             length);
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);
339                 if (length > 4) {
340                         proto_tree_add_text(tlv_tree, tvb, offset + 4,
341                                         length - 4, "Data");
342                 } else
343                         return;
344                 offset += length;
345             }
346         }
347         dissect_data(tvb, offset, pinfo, cdp_tree);
348     }
349 }
350
351 #define PROTO_TYPE_NLPID        1
352 #define PROTO_TYPE_IEEE_802_2   2
353
354 static const value_string proto_type_vals[] = {
355         { PROTO_TYPE_NLPID,      "NLPID" },
356         { PROTO_TYPE_IEEE_802_2, "802.2" },
357         { 0,                     NULL },
358 };
359
360 static int
361 dissect_address_tlv(tvbuff_t *tvb, int offset, int length, proto_tree *tree)
362 {
363     proto_item *ti;
364     proto_tree *address_tree;
365     guint8 protocol_type;
366     guint8 protocol_length;
367     int nlpid;
368     char *protocol_str;
369     guint16 address_length;
370     char *address_type_str;
371     char *address_str;
372
373     if (length < 1)
374         return -1;
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)"));
380     offset += 1;
381     length -= 1;
382
383     if (length < 1) {
384         proto_item_set_text(ti, "Truncated address");
385         return -1;
386     }
387     protocol_length = tvb_get_guint8(tvb, offset);
388     proto_tree_add_text(address_tree, tvb, offset, 1, "Protocol length: %u",
389                         protocol_length);
390     offset += 1;
391     length -= 1;
392
393     if (length < protocol_length) {
394         proto_item_set_text(ti, "Truncated address");
395         if (length != 0) {
396             proto_tree_add_text(address_tree, tvb, offset, length,
397               "Protocol: %s (truncated)",
398               tvb_bytes_to_str(tvb, offset, length));
399         }
400         return -1;
401     }
402     protocol_str = NULL;
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)");
406     } else
407         nlpid = -1;
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;
414
415     if (length < 2) {
416         proto_item_set_text(ti, "Truncated address");
417         return -1;
418     }
419     address_length = tvb_get_ntohs(tvb, offset);
420     proto_tree_add_text(address_tree, tvb, offset, 2, "Address length: %u",
421                         address_length);
422     offset += 2;
423     length -= 2;
424
425     if (length < address_length) {
426         proto_item_set_text(ti, "Truncated address");
427         if (length != 0) {
428             proto_tree_add_text(address_tree, tvb, offset, length,
429               "Address: %s (truncated)",
430               tvb_bytes_to_str(tvb, offset, length));
431         }
432         return -1;
433     }
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;
439     address_str = NULL;
440     if (protocol_type == PROTO_TYPE_NLPID && protocol_length == 1) {
441         switch (nlpid) {
442
443         /* XXX - dissect NLPID_ISO8473_CLNP as OSI CLNP address? */
444
445         case NLPID_IP:
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));
450             }
451             break;
452         }
453     }
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);
458     }
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;
463 }
464
465 static void
466 dissect_capabilities(tvbuff_t *tvb, int offset, int length, proto_tree *tree)
467 {
468     proto_item *ti;
469     proto_tree *capabilities_tree;
470     guint32 capabilities;
471
472     if (length < 4)
473         return;
474     capabilities = tvb_get_ntohl(tvb, offset);
475     ti = proto_tree_add_text(tree, tvb, offset, length, "Capabilities: 0x%08x",
476         capabilities);
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"));
506 }
507
508 static void
509 add_multi_line_string_to_tree(proto_tree *tree, tvbuff_t *tvb, gint start,
510   gint len, const gchar *prefix)
511 {
512     int prefix_len;
513     int i;
514     char blanks[64+1];
515     gint next;
516     int line_len;
517     int data_len;
518
519     prefix_len = strlen(prefix);
520     if (prefix_len > 64)
521         prefix_len = 64;
522     for (i = 0; i < prefix_len; i++)
523         blanks[i] = ' ';
524     blanks[i] = '\0';
525     while (len > 0) {
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));
530         start += data_len;
531         len -= data_len;
532         prefix = blanks;
533     }
534 }
535
536 void
537 proto_register_cdp(void)
538 {
539     static hf_register_info hf[] = {
540         { &hf_cdp_version,
541         { "Version",            "cdp.version",  FT_UINT8, BASE_DEC, NULL, 0x0,
542           "" }},
543
544         { &hf_cdp_ttl,
545         { "TTL",                "cdp.ttl", FT_UINT16, BASE_DEC, NULL, 0x0,
546           "" }},
547
548         { &hf_cdp_checksum,
549         { "Checksum",           "cdp.checksum", FT_UINT16, BASE_HEX, NULL, 0x0,
550           "" }},
551
552         { &hf_cdp_tlvtype,
553         { "Type",               "cdp.tlv.type", FT_UINT16, BASE_HEX, VALS(type_vals), 0x0,
554           "" }},
555
556         { &hf_cdp_tlvlength,
557         { "Length",             "cdp.tlv.len", FT_UINT16, BASE_DEC, NULL, 0x0,
558           "" }},
559     };
560     static gint *ett[] = {
561         &ett_cdp,
562         &ett_cdp_tlv,
563         &ett_cdp_address,
564         &ett_cdp_capabilities,
565     };
566
567     proto_cdp = proto_register_protocol("Cisco Discovery Protocol",
568                                         "CDP", "cdp");
569     proto_register_field_array(proto_cdp, hf, array_length(hf));
570     proto_register_subtree_array(ett, array_length(ett));
571 }
572
573 void
574 proto_reg_handoff_cdp(void)
575 {
576     dissector_add("llc.cisco_pid", 0x2000, dissect_cdp, proto_cdp);
577     dissector_add("fr.cisco", 0x2000, dissect_cdp, proto_cdp);
578 }