Squelch an MSVC++ complaint.
[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.39 2001/08/28 08:28:14 guy Exp $
6  *
7  * Ethereal - Network traffic analyzer
8  * By Gerald Combs <gerald@ethereal.com>
9  * Copyright 1998 Gerald Combs
10  * 
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.
15  * 
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.
20  * 
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.
24  */
25  
26 #include "config.h"
27
28 #ifdef HAVE_SYS_TYPES_H
29 #include <sys/types.h>
30 #endif
31
32 #include <stdlib.h>
33 #include <string.h>
34
35 #include <glib.h>
36 #include "packet.h"
37 #include "strutil.h"
38 #include "nlpid.h"
39
40 /*
41  * See
42  *
43  *      http://www.cisco.com/univercd/cc/td/doc/product/lan/trsrb/frames.htm
44  *
45  * for some information on CDP.
46  */
47
48 /* Offsets in TLV structure. */
49 #define TLV_TYPE        0
50 #define TLV_LENGTH      2
51
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;
58
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;
63
64 static int
65 dissect_address_tlv(tvbuff_t *tvb, int offset, int length, proto_tree *tree);
66 static void
67 dissect_capabilities(tvbuff_t *tvb, int offset, int length, proto_tree *tree);
68 static void
69 add_multi_line_string_to_tree(proto_tree *tree, tvbuff_t *tvb, gint start,
70   gint len, const gchar *prefix);
71
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
78 #define TYPE_IP_PREFIX          0x0007
79
80 #define TYPE_VTP_MGMT_DOMAIN    0x0009 /* Guessed, from tcpdump */
81 #define TYPE_NATIVE_VLAN        0x000a /* Guessed, from tcpdump */
82 #define TYPE_DUPLEX             0x000b /* Guessed, from tcpdump */
83
84 static const value_string type_vals[] = {
85         { TYPE_DEVICE_ID,       "Device ID" },
86         { TYPE_ADDRESS,         "Addresses" },
87         { TYPE_PORT_ID,         "Port ID" },
88         { TYPE_CAPABILITIES,    "Capabilities" },
89         { TYPE_IOS_VERSION,     "Software version" },
90         { TYPE_PLATFORM,        "Platform" },
91         { TYPE_IP_PREFIX,       "IP Prefix (used for ODR)" },
92         { TYPE_VTP_MGMT_DOMAIN, "VTP Management Domain" },
93         { TYPE_NATIVE_VLAN,     "Native VLAN" },
94         { TYPE_DUPLEX,          "Duplex" },
95         { 0,                    NULL },
96 };
97         
98 static void 
99 dissect_cdp(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree)
100 {
101     proto_item *ti; 
102     proto_tree *cdp_tree = NULL;
103     int offset = 0;
104     guint16 type;
105     guint16 length;
106     proto_item *tlvi;
107     proto_tree *tlv_tree;
108     int real_length;
109     guint32 naddresses;
110     int addr_length;
111
112     if (check_col(pinfo->fd, COL_PROTOCOL))
113         col_set_str(pinfo->fd, COL_PROTOCOL, "CDP");
114     if (check_col(pinfo->fd, COL_INFO))
115         col_set_str(pinfo->fd, COL_INFO, "Cisco Discovery Protocol"); 
116
117     if (tree){
118         ti = proto_tree_add_item(tree, proto_cdp, tvb, offset,
119                                  tvb_length_remaining(tvb, offset), FALSE);
120         cdp_tree = proto_item_add_subtree(ti, ett_cdp);
121         
122         /* CDP header */
123         proto_tree_add_item(cdp_tree, hf_cdp_version, tvb, offset, 1, FALSE);
124         offset += 1;
125         proto_tree_add_uint_format(cdp_tree, hf_cdp_ttl, tvb, offset, 1,
126                                    tvb_get_guint8(tvb, offset),
127                                    "TTL: %u seconds",
128                                    tvb_get_guint8(tvb, offset));
129         offset += 1;
130         proto_tree_add_item(cdp_tree, hf_cdp_checksum, tvb, offset, 2, FALSE);
131         offset += 2;
132
133         while (tvb_reported_length_remaining(tvb, offset) != 0) {
134             type = tvb_get_ntohs(tvb, offset + TLV_TYPE);
135             length = tvb_get_ntohs(tvb, offset + TLV_LENGTH);
136             if (length < 4) {
137                 tlvi = proto_tree_add_text(cdp_tree, tvb, offset, 4,
138                     "TLV with invalid length %u (< 4)",
139                     length);
140                 tlv_tree = proto_item_add_subtree(tlvi, ett_cdp_tlv);
141                 proto_tree_add_uint(tlv_tree, hf_cdp_tlvtype, tvb,
142                             offset + TLV_TYPE, 2, type);
143                 proto_tree_add_uint(tlv_tree, hf_cdp_tlvlength, tvb,
144                             offset + TLV_LENGTH, 2, length);
145                 offset += 4;
146                 break;
147             }
148
149             switch (type) {
150
151             case TYPE_DEVICE_ID:
152                 /* Device ID */
153                 tlvi = proto_tree_add_text(cdp_tree, tvb, offset,
154                             length, "Device ID: %.*s",
155                             length - 4,
156                             tvb_get_ptr(tvb, offset + 4, length - 4));
157                 tlv_tree = proto_item_add_subtree(tlvi, ett_cdp_tlv);
158                 proto_tree_add_uint(tlv_tree, hf_cdp_tlvtype, tvb,
159                             offset + TLV_TYPE, 2, type);
160                 proto_tree_add_uint(tlv_tree, hf_cdp_tlvlength, tvb,
161                             offset + TLV_LENGTH, 2, length);
162                 proto_tree_add_text(tlv_tree, tvb, offset + 4,
163                             length - 4, "Device ID: %.*s",
164                             length - 4,
165                             tvb_get_ptr(tvb, offset + 4, length - 4));
166                 offset += length;
167                 break;
168
169             case TYPE_ADDRESS:
170                 /* Addresses */
171                 tlvi = proto_tree_add_text(cdp_tree, tvb, offset,
172                             length, "Addresses");
173                 tlv_tree = proto_item_add_subtree(tlvi, ett_cdp_tlv);
174                 proto_tree_add_uint(tlv_tree, hf_cdp_tlvtype, tvb,
175                             offset + TLV_TYPE, 2, type);
176                 proto_tree_add_uint(tlv_tree, hf_cdp_tlvlength, tvb,
177                             offset + TLV_LENGTH, 2, length);
178                 offset += 4;
179                 length -= 4;
180                 naddresses = tvb_get_ntohl(tvb, offset);
181                 proto_tree_add_text(tlv_tree, tvb, offset, 4,
182                             "Number of addresses: %u", naddresses);
183                 offset += 4;
184                 length -= 4;
185                 while (naddresses != 0) {
186                     addr_length = dissect_address_tlv(tvb, offset, length,
187                                 tlv_tree);
188                     if (addr_length < 0)
189                         break;
190                     offset += addr_length;
191                     length -= addr_length;
192
193                     naddresses--;
194                 }
195                 offset += length;
196                 break;
197
198             case TYPE_PORT_ID:
199                 real_length = length;
200                 if (tvb_get_guint8(tvb, offset + real_length) != 0x00) {
201                     /* The length in the TLV doesn't appear to be the
202                        length of the TLV, as the byte just past it
203                        isn't the first byte of a 2-byte big-endian
204                        small integer; make the length of the TLV the length
205                        in the TLV, plus 4 bytes for the TLV type and length,
206                        minus 1 because that's what makes one capture work. */
207                     real_length = length + 3;
208                 }
209                 tlvi = proto_tree_add_text(cdp_tree, tvb, offset,
210                             real_length, "Port ID: %.*s",
211                             real_length - 4,
212                             tvb_get_ptr(tvb, offset + 4, real_length - 4));
213                 tlv_tree = proto_item_add_subtree(tlvi, ett_cdp_tlv);
214                 proto_tree_add_uint(tlv_tree, hf_cdp_tlvtype, tvb,
215                             offset + TLV_TYPE, 2, type);
216                 proto_tree_add_uint(tlv_tree, hf_cdp_tlvlength, tvb,
217                             offset + TLV_LENGTH, 2, length);
218                 proto_tree_add_text(tlv_tree, tvb, offset + 4,
219                             real_length - 4,
220                             "Sent through Interface: %.*s",
221                             real_length - 4,
222                             tvb_get_ptr(tvb, offset + 4, real_length - 4));
223                 offset += real_length;
224                 break;
225
226             case TYPE_CAPABILITIES:
227                 tlvi = proto_tree_add_text(cdp_tree, tvb, offset,
228                             length, "Capabilities");
229                 tlv_tree = proto_item_add_subtree(tlvi, ett_cdp_tlv);
230                 proto_tree_add_uint(tlv_tree, hf_cdp_tlvtype, tvb,
231                             offset + TLV_TYPE, 2, type);
232                 proto_tree_add_uint(tlv_tree, hf_cdp_tlvlength, tvb,
233                             offset + TLV_LENGTH, 2, length);
234                 offset += 4;
235                 length -= 4;
236                 dissect_capabilities(tvb, offset, length, tlv_tree);
237                 offset += length;
238                 break;
239
240             case TYPE_IOS_VERSION:
241                 tlvi = proto_tree_add_text(cdp_tree, tvb, offset,
242                             length, "Software Version");
243                 tlv_tree = proto_item_add_subtree(tlvi, ett_cdp_tlv);
244                 proto_tree_add_uint(tlv_tree, hf_cdp_tlvtype, tvb,
245                             offset + TLV_TYPE, 2, type);
246                 proto_tree_add_uint(tlv_tree, hf_cdp_tlvlength, tvb,
247                             offset + TLV_LENGTH, 2, length);
248                 add_multi_line_string_to_tree(tlv_tree, tvb, offset + 4,
249                                 length - 4, "Software Version: ");
250                 offset += length;
251                 break;
252
253             case TYPE_PLATFORM:
254                 /* ??? platform */
255                 tlvi = proto_tree_add_text(cdp_tree, tvb,
256                             offset, length, "Platform: %.*s",
257                             length - 4,
258                             tvb_get_ptr(tvb, offset + 4, length - 4));
259                 tlv_tree = proto_item_add_subtree(tlvi, ett_cdp_tlv);
260                 proto_tree_add_uint(tlv_tree, hf_cdp_tlvtype, tvb,
261                             offset + TLV_TYPE, 2, type);
262                 proto_tree_add_uint(tlv_tree, hf_cdp_tlvlength, tvb,
263                             offset + TLV_LENGTH, 2, length);
264                 proto_tree_add_text(tlv_tree, tvb, offset + 4,
265                             length - 4, "Platform: %.*s",
266                             length - 4,
267                             tvb_get_ptr(tvb, offset + 4, length - 4));
268                 offset += length;
269                 break;
270             case TYPE_IP_PREFIX:
271                 tlvi = proto_tree_add_text(cdp_tree, tvb, offset,
272                             length, "IP Prefixes: %d",length/5);
273
274                             /* the actual number of prefixes is (length-4)/5
275                             but if the variable is not a "float" but "integer"
276                             then length/5=(length-4)/5  :)  */
277                             
278                 tlv_tree = proto_item_add_subtree(tlvi, ett_cdp_tlv);
279                 proto_tree_add_uint(tlv_tree, hf_cdp_tlvtype, tvb,
280                             offset + TLV_TYPE, 2, type);  
281                 proto_tree_add_uint(tlv_tree, hf_cdp_tlvlength, tvb,
282                             offset + TLV_LENGTH, 2, length);
283                 offset += 4;
284                 length -= 4;
285                 while (length > 0) {
286                         proto_tree_add_text(tlv_tree, tvb, offset, 5,
287                                 "IP Prefix = %s/%u",
288                                 ip_to_str(tvb_get_ptr(tvb, offset, 4)),
289                                 tvb_get_guint8(tvb,offset+4));
290                         offset += 5;
291                         length -= 5;
292                 }
293                 break;
294             case TYPE_VTP_MGMT_DOMAIN:
295                 tlvi = proto_tree_add_text(cdp_tree, tvb,
296                             offset, length, "VTP Management Domain: %.*s",
297                             length - 4,
298                             tvb_get_ptr(tvb, offset + 4, length - 4));
299                 tlv_tree = proto_item_add_subtree(tlvi, ett_cdp_tlv);
300                 proto_tree_add_uint(tlv_tree, hf_cdp_tlvtype, tvb,
301                             offset + TLV_TYPE, 2, type);
302                 proto_tree_add_uint(tlv_tree, hf_cdp_tlvlength, tvb,
303                             offset + TLV_LENGTH, 2, length);
304                 proto_tree_add_text(tlv_tree, tvb, offset + 4,
305                             length - 4, "VTP Management Domain: %.*s",
306                             length - 4,
307                             tvb_get_ptr(tvb, offset + 4, length - 4));
308                 offset += length;
309                 break;
310             case TYPE_NATIVE_VLAN:
311                 tlvi = proto_tree_add_text(cdp_tree, tvb,
312                             offset, length, "Native VLAN: %u",
313                                            tvb_get_ntohs(tvb, offset + 4));
314                 tlv_tree = proto_item_add_subtree(tlvi, ett_cdp_tlv);
315                 proto_tree_add_uint(tlv_tree, hf_cdp_tlvtype, tvb,
316                             offset + TLV_TYPE, 2, type);
317                 proto_tree_add_uint(tlv_tree, hf_cdp_tlvlength, tvb,
318                             offset + TLV_LENGTH, 2, length);
319                 proto_tree_add_text(tlv_tree, tvb, offset + 4,
320                             length - 4, "Native VLAN: %u",
321                                     tvb_get_ntohs(tvb, offset + 4));
322                 offset += length;
323                 break;
324             case TYPE_DUPLEX:
325                 tlvi = proto_tree_add_text(cdp_tree, tvb,
326                             offset, length, "Duplex: %s",
327                                            tvb_get_guint8(tvb, offset + 4) ?
328                                            "Full" : "Half" );
329                 tlv_tree = proto_item_add_subtree(tlvi, ett_cdp_tlv);
330                 proto_tree_add_uint(tlv_tree, hf_cdp_tlvtype, tvb,
331                             offset + TLV_TYPE, 2, type);
332                 proto_tree_add_uint(tlv_tree, hf_cdp_tlvlength, tvb,
333                             offset + TLV_LENGTH, 2, length);
334                 proto_tree_add_text(tlv_tree, tvb, offset + 4,
335                             length - 4, "Duplex: %s",
336                                     tvb_get_guint8(tvb, offset + 4) ?
337                                     "Full" : "Half" );
338                 offset += length;
339                 break;
340             default:
341                 tlvi = proto_tree_add_text(cdp_tree, tvb, offset,
342                             length, "Type: %s, length: %u",
343                             val_to_str(type, type_vals, "Unknown (0x%04x)"),
344                             length);
345                 tlv_tree = proto_item_add_subtree(tlvi, ett_cdp_tlv);
346                 proto_tree_add_uint(tlv_tree, hf_cdp_tlvtype, tvb,
347                             offset + TLV_TYPE, 2, type);
348                 proto_tree_add_uint(tlv_tree, hf_cdp_tlvlength, tvb,
349                             offset + TLV_LENGTH, 2, length);
350                 if (length > 4) {
351                         proto_tree_add_text(tlv_tree, tvb, offset + 4,
352                                         length - 4, "Data");
353                 } else
354                         return;
355                 offset += length;
356             }
357         }
358         dissect_data(tvb, offset, pinfo, cdp_tree);
359     }
360 }
361
362 #define PROTO_TYPE_NLPID        1
363 #define PROTO_TYPE_IEEE_802_2   2
364
365 static const value_string proto_type_vals[] = {
366         { PROTO_TYPE_NLPID,      "NLPID" },
367         { PROTO_TYPE_IEEE_802_2, "802.2" },
368         { 0,                     NULL },
369 };
370
371 static int
372 dissect_address_tlv(tvbuff_t *tvb, int offset, int length, proto_tree *tree)
373 {
374     proto_item *ti;
375     proto_tree *address_tree;
376     guint8 protocol_type;
377     guint8 protocol_length;
378     int nlpid;
379     char *protocol_str;
380     guint16 address_length;
381     char *address_type_str;
382     char *address_str;
383
384     if (length < 1)
385         return -1;
386     ti = proto_tree_add_text(tree, tvb, offset, length, "Truncated address");
387     address_tree = proto_item_add_subtree(ti, ett_cdp_address);
388     protocol_type = tvb_get_guint8(tvb, offset);
389     proto_tree_add_text(address_tree, tvb, offset, 1, "Protocol type: %s",
390         val_to_str(protocol_type, proto_type_vals, "Unknown (0x%02x)"));
391     offset += 1;
392     length -= 1;
393
394     if (length < 1)
395         return -1;
396     protocol_length = tvb_get_guint8(tvb, offset);
397     proto_tree_add_text(address_tree, tvb, offset, 1, "Protocol length: %u",
398                         protocol_length);
399     offset += 1;
400     length -= 1;
401
402     if (length < protocol_length) {
403         if (length != 0) {
404             proto_tree_add_text(address_tree, tvb, offset, length,
405               "Protocol: %s (truncated)",
406               tvb_bytes_to_str(tvb, offset, length));
407         }
408         return -1;
409     }
410     protocol_str = NULL;
411     if (protocol_type == PROTO_TYPE_NLPID && protocol_length == 1) {
412         nlpid = tvb_get_guint8(tvb, offset);
413         protocol_str = val_to_str(nlpid, nlpid_vals, "Unknown (0x%02x)");
414     } else
415         nlpid = -1;
416     if (protocol_str == NULL)
417         protocol_str = tvb_bytes_to_str(tvb, offset, protocol_length);
418     proto_tree_add_text(address_tree, tvb, offset, protocol_length,
419                         "Protocol: %s", protocol_str);
420     offset += protocol_length;
421     length -= protocol_length;
422
423     if (length < 2)
424         return -1;
425     address_length = tvb_get_ntohs(tvb, offset);
426     proto_tree_add_text(address_tree, tvb, offset, 2, "Address length: %u",
427                         address_length);
428     offset += 2;
429     length -= 2;
430
431     if (length < address_length) {
432         if (length != 0) {
433             proto_tree_add_text(address_tree, tvb, offset, length,
434               "Address: %s (truncated)",
435               tvb_bytes_to_str(tvb, offset, length));
436         }
437         return -1;
438     }
439     /* XXX - the Cisco document seems to be saying that, for 802.2-format
440        protocol types, 0xAAAA03 0x000000 0x0800 is IPv6, but 0x0800 is
441        the Ethernet protocol type for IPv4. */
442     length = 2 + protocol_length + 2 + address_length;
443     address_type_str = NULL;
444     address_str = NULL;
445     if (protocol_type == PROTO_TYPE_NLPID && protocol_length == 1) {
446         switch (nlpid) {
447
448         /* XXX - dissect NLPID_ISO8473_CLNP as OSI CLNP address? */
449
450         case NLPID_IP:
451             if (address_length == 4) {
452                 /* The address is an IP address. */
453                 address_type_str = "IP address";
454                 address_str = ip_to_str(tvb_get_ptr(tvb, offset, 4));
455             }
456             break;
457         }
458     }
459     if (address_type_str == NULL)
460         address_type_str = "Address";
461     if (address_str == NULL) {
462         address_str = tvb_bytes_to_str(tvb, offset, address_length);
463     }
464     proto_item_set_text(ti, "%s: %s", address_type_str, address_str);
465     proto_tree_add_text(address_tree, tvb, offset, address_length, "%s: %s",
466       address_type_str, address_str);
467     return 2 + protocol_length + 2 + address_length;
468 }
469
470 static void
471 dissect_capabilities(tvbuff_t *tvb, int offset, int length, proto_tree *tree)
472 {
473     proto_item *ti;
474     proto_tree *capabilities_tree;
475     guint32 capabilities;
476
477     if (length < 4)
478         return;
479     capabilities = tvb_get_ntohl(tvb, offset);
480     ti = proto_tree_add_text(tree, tvb, offset, length, "Capabilities: 0x%08x",
481         capabilities);
482     capabilities_tree = proto_item_add_subtree(ti, ett_cdp_capabilities);
483     proto_tree_add_text(capabilities_tree, tvb, offset, 4,
484         decode_boolean_bitfield(capabilities, 0x01, 4*8,
485             "Performs level 3 routing",
486             "Doesn't perform level 3 routing"));
487     proto_tree_add_text(capabilities_tree, tvb, offset, 4,
488         decode_boolean_bitfield(capabilities, 0x02, 4*8,
489             "Performs level 2 transparent bridging",
490             "Doesn't perform level 2 transparent bridging"));
491     proto_tree_add_text(capabilities_tree, tvb, offset, 4,
492         decode_boolean_bitfield(capabilities, 0x04, 4*8,
493             "Performs level 2 source-route bridging",
494             "Doesn't perform level 2 source-route bridging"));
495     proto_tree_add_text(capabilities_tree, tvb, offset, 4,
496         decode_boolean_bitfield(capabilities, 0x08, 4*8,
497             "Performs level 2 switching",
498             "Doesn't perform level 2 switching"));
499     proto_tree_add_text(capabilities_tree, tvb, offset, 4,
500         decode_boolean_bitfield(capabilities, 0x10, 4*8,
501             "Sends and receives packets for network-layer protocols",
502             "Doesn't send or receive packets for network-layer protocols"));
503     proto_tree_add_text(capabilities_tree, tvb, offset, 4,
504         decode_boolean_bitfield(capabilities, 0x20, 4*8,
505             "Doesn't forward IGMP Report packets on nonrouter ports",
506             "Forwards IGMP Report packets on nonrouter ports"));
507     proto_tree_add_text(capabilities_tree, tvb, offset, 4,
508         decode_boolean_bitfield(capabilities, 0x40, 4*8,
509             "Provides level 1 functionality",
510             "Doesn't provide level 1 functionality"));
511 }
512
513 static void
514 add_multi_line_string_to_tree(proto_tree *tree, tvbuff_t *tvb, gint start,
515   gint len, const gchar *prefix)
516 {
517     int prefix_len;
518     int i;
519     char blanks[64+1];
520     gint next;
521     int line_len;
522     int data_len;
523
524     prefix_len = strlen(prefix);
525     if (prefix_len > 64)
526         prefix_len = 64;
527     for (i = 0; i < prefix_len; i++)
528         blanks[i] = ' ';
529     blanks[i] = '\0';
530     while (len > 0) {
531         line_len = tvb_find_line_end(tvb, start, len, &next);
532         data_len = next - start;
533         proto_tree_add_text(tree, tvb, start, data_len, "%s%.*s", prefix,
534            line_len, tvb_get_ptr(tvb, start, line_len));
535         start += data_len;
536         len -= data_len;
537         prefix = blanks;
538     }
539 }
540
541 void
542 proto_register_cdp(void)
543 {
544     static hf_register_info hf[] = {
545         { &hf_cdp_version,
546         { "Version",            "cdp.version",  FT_UINT8, BASE_DEC, NULL, 0x0,
547           "", HFILL }},
548
549         { &hf_cdp_ttl,
550         { "TTL",                "cdp.ttl", FT_UINT16, BASE_DEC, NULL, 0x0,
551           "", HFILL }},
552
553         { &hf_cdp_checksum,
554         { "Checksum",           "cdp.checksum", FT_UINT16, BASE_HEX, NULL, 0x0,
555           "", HFILL }},
556
557         { &hf_cdp_tlvtype,
558         { "Type",               "cdp.tlv.type", FT_UINT16, BASE_HEX, VALS(type_vals), 0x0,
559           "", HFILL }},
560
561         { &hf_cdp_tlvlength,
562         { "Length",             "cdp.tlv.len", FT_UINT16, BASE_DEC, NULL, 0x0,
563           "", HFILL }},
564     };
565     static gint *ett[] = {
566         &ett_cdp,
567         &ett_cdp_tlv,
568         &ett_cdp_address,
569         &ett_cdp_capabilities,
570     };
571
572     proto_cdp = proto_register_protocol("Cisco Discovery Protocol",
573                                         "CDP", "cdp");
574     proto_register_field_array(proto_cdp, hf, array_length(hf));
575     proto_register_subtree_array(ett, array_length(ett));
576 }
577
578 void
579 proto_reg_handoff_cdp(void)
580 {
581     dissector_add("llc.cisco_pid", 0x2000, dissect_cdp, proto_cdp);
582     dissector_add("chdlctype", 0x2000, dissect_cdp, proto_cdp);
583 }