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