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