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