Document the new Copy Profile button.
[obnox/wireshark/wip.git] / epan / dissectors / 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$
6  *
7  * Wireshark - Network traffic analyzer
8  * By Gerald Combs <gerald@wireshark.org>
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., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
24  */
25
26 #ifdef HAVE_CONFIG_H
27 # include "config.h"
28 #endif
29
30 #include <stdlib.h>
31 #include <string.h>
32
33 #include <glib.h>
34 #include <epan/packet.h>
35 #include <epan/strutil.h>
36 #include <epan/in_cksum.h>
37
38 #include <epan/oui.h>
39 #include <epan/nlpid.h>
40
41
42 /*
43  * See
44  *
45  *      http://www.cisco.com/univercd/cc/td/doc/product/lan/trsrb/frames.htm#xtocid12
46  *
47  * for some information on CDP.
48  *
49  * See
50  *
51  *      http://www.cisco.com/en/US/products/hw/switches/ps663/products_tech_note09186a0080094713.shtml#cdp
52  *
53  * for some more information on CDP version 2.
54  */
55
56 /* Offsets in TLV structure. */
57 #define TLV_TYPE        0
58 #define TLV_LENGTH      2
59
60 static int proto_cdp = -1;
61 static int hf_cdp_version = -1;
62 static int hf_cdp_checksum = -1;
63 static int hf_cdp_checksum_good = -1;
64 static int hf_cdp_checksum_bad = -1;
65 static int hf_cdp_ttl = -1;
66 static int hf_cdp_tlvtype = -1;
67 static int hf_cdp_tlvlength = -1;
68 static int hf_cdp_deviceid = -1;
69 static int hf_cdp_platform = -1;
70 static int hf_cdp_portid = -1;
71
72 static gint ett_cdp = -1;
73 static gint ett_cdp_tlv = -1;
74 static gint ett_cdp_nrgyz_tlv = -1;
75 static gint ett_cdp_address = -1;
76 static gint ett_cdp_capabilities = -1;
77 static gint ett_cdp_checksum = -1;
78
79 static dissector_handle_t data_handle;
80
81 static int
82 dissect_address_tlv(tvbuff_t *tvb, int offset, int length, proto_tree *tree);
83 static void
84 dissect_capabilities(tvbuff_t *tvb, int offset, int length, proto_tree *tree);
85 static void
86 dissect_nrgyz_tlv(tvbuff_t *tvb, int offset, guint16 length, guint16 num, 
87   proto_tree *tree);
88 static void
89 add_multi_line_string_to_tree(proto_tree *tree, tvbuff_t *tvb, gint start,
90   gint len, const gchar *prefix);
91
92 #define TYPE_DEVICE_ID          0x0001
93 #define TYPE_ADDRESS            0x0002
94 #define TYPE_PORT_ID            0x0003
95 #define TYPE_CAPABILITIES       0x0004
96 #define TYPE_IOS_VERSION        0x0005
97 #define TYPE_PLATFORM           0x0006
98 #define TYPE_IP_PREFIX          0x0007
99 #define TYPE_PROTOCOL_HELLO     0x0008 /* Protocol Hello */
100 #define TYPE_VTP_MGMT_DOMAIN    0x0009 /* VTP Domain, CTPv2 - see second URL */
101 #define TYPE_NATIVE_VLAN        0x000a /* Native VLAN, CTPv2 - see second URL */
102 #define TYPE_DUPLEX             0x000b /* Full/Half Duplex - see second URL */
103 /*                              0x000c */
104 /*                              0x000d */
105 #define TYPE_VOIP_VLAN_REPLY    0x000e /* VoIP VLAN reply */
106 #define TYPE_VOIP_VLAN_QUERY    0x000f /* VoIP VLAN query */
107 #define TYPE_POWER              0x0010 /* Power consumption */
108 #define TYPE_MTU                0x0011 /* MTU */
109 #define TYPE_TRUST_BITMAP       0x0012 /* Trust bitmap */
110 #define TYPE_UNTRUSTED_COS      0x0013 /* Untrusted port CoS */
111 #define TYPE_SYSTEM_NAME        0x0014 /* System Name */
112 #define TYPE_SYSTEM_OID         0x0015 /* System OID */
113 #define TYPE_MANAGEMENT_ADDR    0x0016 /* Management Address(es) */
114 #define TYPE_LOCATION           0x0017 /* Location */
115 #define TYPE_EXT_PORT_ID        0x0018 /* External Port-ID */
116 #define TYPE_POWER_REQUESTED    0x0019 /* Power Requested */
117 #define TYPE_POWER_AVAILABLE    0x001a /* Power Available */
118 #define TYPE_PORT_UNIDIR        0x001b /* Port Unidirectional */
119 #define TYPE_NRGYZ              0x001d /* EnergyWise over CDP */
120
121 static const value_string type_vals[] = {
122         { TYPE_DEVICE_ID,       "Device ID" },
123         { TYPE_ADDRESS,         "Addresses" },
124         { TYPE_PORT_ID,         "Port ID" },
125         { TYPE_CAPABILITIES,    "Capabilities" },
126         { TYPE_IOS_VERSION,     "Software version" },
127         { TYPE_PLATFORM,        "Platform" },
128         { TYPE_IP_PREFIX,       "IP Prefix/Gateway (used for ODR)" },
129         { TYPE_PROTOCOL_HELLO,  "Protocol Hello" },
130         { TYPE_VTP_MGMT_DOMAIN, "VTP Management Domain" },
131         { TYPE_NATIVE_VLAN,     "Native VLAN" },
132         { TYPE_DUPLEX,          "Duplex" },
133         { TYPE_VOIP_VLAN_REPLY, "VoIP VLAN Reply" },
134         { TYPE_VOIP_VLAN_QUERY, "VoIP VLAN Query" },
135         { TYPE_POWER,           "Power consumption" },
136         { TYPE_MTU,             "MTU"},
137         { TYPE_TRUST_BITMAP,    "Trust Bitmap" },
138         { TYPE_UNTRUSTED_COS,   "Untrusted Port CoS" },
139         { TYPE_SYSTEM_NAME,     "System Name" },
140         { TYPE_SYSTEM_OID,      "System Object ID" },
141         { TYPE_MANAGEMENT_ADDR, "Management Address" },
142         { TYPE_LOCATION,        "Location" },
143         { TYPE_EXT_PORT_ID,     "External Port-ID" },
144         { TYPE_POWER_REQUESTED, "Power Requested" },
145         { TYPE_POWER_AVAILABLE, "Power Available" },
146         { TYPE_PORT_UNIDIR,     "Port Unidirectional" },
147         { TYPE_NRGYZ,           "EnergyWise" },
148         { 0,                    NULL }
149 };
150
151 #define TYPE_HELLO_CLUSTER_MGMT    0x0112
152
153 static const value_string type_hello_vals[] = {
154         { TYPE_HELLO_CLUSTER_MGMT,   "Cluster Management" },
155         { 0,                    NULL }
156 };
157
158 #define TYPE_NRGYZ_ROLE            0x00000007
159 #define TYPE_NRGYZ_DOMAIN          0x00000008
160 #define TYPE_NRGYZ_NAME            0x00000009
161 #define TYPE_NRGYZ_REPLYTO         0x00000017
162
163 static const value_string type_nrgyz_vals[] = {
164         { TYPE_NRGYZ_ROLE,           "Role" },
165         { TYPE_NRGYZ_DOMAIN,         "Domain" },
166         { TYPE_NRGYZ_NAME,           "Name" },
167         { TYPE_NRGYZ_REPLYTO,        "Reply To" },
168         { 0,                         NULL }
169 };
170
171 static void
172 dissect_cdp(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree)
173 {
174     proto_item *ti, *checksum_item;
175     proto_tree *cdp_tree = NULL, *checksum_tree;
176     int offset = 0;
177     guint16 type;
178     guint16 length, packet_checksum, computed_checksum, data_length;
179     gboolean checksum_good, checksum_bad;
180     proto_item *tlvi = NULL;
181     proto_tree *tlv_tree = NULL;
182     int real_length;
183     guint32 naddresses;
184     guint32 power_avail_len, power_avail;
185     guint32 power_req_len, power_req;
186     int addr_length;
187     guint32 ip_addr;
188     vec_t cksum_vec[1];
189
190     col_set_str(pinfo->cinfo, COL_PROTOCOL, "CDP");
191     col_clear(pinfo->cinfo, COL_INFO);
192
193     if (tree) {
194         ti = proto_tree_add_item(tree, proto_cdp, tvb, offset, -1, FALSE);
195         cdp_tree = proto_item_add_subtree(ti, ett_cdp);
196
197         /* CDP header */
198         proto_tree_add_item(cdp_tree, hf_cdp_version, tvb, offset, 1, FALSE);
199         offset += 1;
200
201         proto_tree_add_uint_format_value(cdp_tree, hf_cdp_ttl, tvb, offset, 1,
202                                          tvb_get_guint8(tvb, offset),
203                                          "%u seconds",
204                                          tvb_get_guint8(tvb, offset));
205         offset += 1;
206     } else {
207         offset += 2; /* The version/ttl fields from above */
208     }
209
210     /* Checksum display & verification code */
211     packet_checksum = tvb_get_ntohs(tvb, offset);
212     
213     data_length = tvb_reported_length(tvb);
214
215     /* CDP doesn't adhere to RFC 1071 section 2. (B). It incorrectly assumes
216      * checksums are calculated on a big endian platform, therefore i.s.o.
217      * padding odd sized data with a zero byte _at the end_ it sets the last
218      * big endian _word_ to contain the last network _octet_. This byteswap
219      * has to be done on the last octet of network data before feeding it to
220      * the Internet checksum routine.
221      * CDP checksumming code has a bug in the addition of this last _word_
222      * as a signed number into the long word intermediate checksum. When
223      * reducing this long to word size checksum an off-by-one error can be
224      * made. This off-by-one error is compensated for in the last _word_ of
225      * the network data.
226      */
227     if (data_length & 1) {
228         guint8 *padded_buffer;
229         /* Allocate new buffer */
230         padded_buffer = ep_alloc(data_length+1);
231         tvb_memcpy(tvb, padded_buffer, 0, data_length);
232         /* Swap bytes in last word */
233         padded_buffer[data_length] = padded_buffer[data_length-1];
234         padded_buffer[data_length-1] = 0;
235         /* Compensate off-by-one error */
236         if (padded_buffer[data_length] & 0x80) {
237           padded_buffer[data_length]--;
238           padded_buffer[data_length-1]--;
239         }
240         /* Setup checksum routine data buffer */
241         cksum_vec[0].ptr = padded_buffer;
242         cksum_vec[0].len = data_length+1;
243     } else {
244         /* Setup checksum routine data buffer */
245         cksum_vec[0].ptr = tvb_get_ptr(tvb, 0, data_length);
246         cksum_vec[0].len = data_length;
247     }
248
249     computed_checksum = in_cksum(cksum_vec, 1);
250     checksum_good = (computed_checksum == 0);
251     checksum_bad = !checksum_good;
252     if (checksum_good) {
253         checksum_item = proto_tree_add_uint_format(cdp_tree,
254         hf_cdp_checksum, tvb, offset, 2, packet_checksum,
255         "Checksum: 0x%04x [correct]", packet_checksum);
256     } else {
257         checksum_item = proto_tree_add_uint_format(cdp_tree,
258          hf_cdp_checksum, tvb, offset, 2, packet_checksum,
259          "Checksum: 0x%04x [incorrect, should be 0x%04x]",
260          packet_checksum,
261          in_cksum_shouldbe(packet_checksum, computed_checksum));
262     }
263     
264     checksum_tree = proto_item_add_subtree(checksum_item, ett_cdp_checksum);
265     checksum_item = proto_tree_add_boolean(checksum_tree, hf_cdp_checksum_good,
266                                            tvb, offset, 2, checksum_good);
267     PROTO_ITEM_SET_GENERATED(checksum_item);
268     checksum_item = proto_tree_add_boolean(checksum_tree, hf_cdp_checksum_bad,
269                                            tvb, offset, 2, checksum_bad);
270     PROTO_ITEM_SET_GENERATED(checksum_item);
271     
272     offset += 2;
273     
274     while (tvb_reported_length_remaining(tvb, offset) != 0) {
275       type = tvb_get_ntohs(tvb, offset + TLV_TYPE);
276       length = tvb_get_ntohs(tvb, offset + TLV_LENGTH);
277       if (length < 4) {
278         if (tree) {
279           tlvi = proto_tree_add_text(cdp_tree, tvb, offset, 4,
280                                      "TLV with invalid length %u (< 4)",
281                                      length);
282           tlv_tree = proto_item_add_subtree(tlvi, ett_cdp_tlv);
283           proto_tree_add_item(tlv_tree, hf_cdp_tlvtype, tvb, offset + TLV_TYPE, 2, FALSE);
284           proto_tree_add_item(tlv_tree, hf_cdp_tlvlength, tvb, offset + TLV_LENGTH, 2, FALSE);
285         }
286         offset += 4;
287         break;
288       }
289
290       switch (type) {
291
292       case TYPE_DEVICE_ID:
293                   /* Device ID */
294
295                   col_append_fstr(pinfo->cinfo, COL_INFO,
296                           "Device ID: %s  ",
297                           tvb_format_stringzpad(tvb, offset + 4, length - 4));
298
299         if (tree) {
300           tlvi = proto_tree_add_text(cdp_tree, tvb, offset,
301                                      length, "Device ID: %s",
302                                      tvb_format_stringzpad(tvb, offset + 4, length - 4));
303           tlv_tree = proto_item_add_subtree(tlvi, ett_cdp_tlv);
304           proto_tree_add_item(tlv_tree, hf_cdp_tlvtype, tvb, offset + TLV_TYPE, 2, FALSE);
305           proto_tree_add_item(tlv_tree, hf_cdp_tlvlength, tvb, offset + TLV_LENGTH, 2, FALSE);
306           proto_tree_add_item(tlv_tree, hf_cdp_deviceid, tvb, offset + 4, length - 4, FALSE);
307         }
308         offset += length;
309         break;
310
311       case TYPE_PORT_ID:
312         real_length = length;
313         if (tvb_get_guint8(tvb, offset + real_length) != 0x00) {
314           /* The length in the TLV doesn't appear to be the
315              length of the TLV, as the byte just past it
316              isn't the first byte of a 2-byte big-endian
317              small integer; make the length of the TLV the length
318              in the TLV, plus 4 bytes for the TLV type and length,
319              minus 1 because that's what makes one capture work. */
320           real_length = length + 3;
321         }
322
323     col_append_fstr(pinfo->cinfo, COL_INFO,
324                   "Port ID: %s  ",
325                   tvb_format_stringzpad(tvb, offset + 4,
326                                         length - 4));
327
328         if (tree) { 
329           tlvi = proto_tree_add_text(cdp_tree, tvb, offset,
330                                      real_length, "Port ID: %s",
331                                      tvb_format_text(tvb, offset + 4, real_length - 4));
332           tlv_tree = proto_item_add_subtree(tlvi, ett_cdp_tlv);
333           proto_tree_add_item(tlv_tree, hf_cdp_tlvtype, tvb, offset + TLV_TYPE, 2, FALSE);
334           proto_tree_add_item(tlv_tree, hf_cdp_tlvlength, tvb, offset + TLV_LENGTH, 2, FALSE);
335           proto_tree_add_item(tlv_tree, hf_cdp_portid, tvb, offset + 4, real_length - 4, FALSE);
336         }
337         offset += real_length;
338         break;
339
340       case TYPE_ADDRESS:
341         /* Addresses */
342         if (tree) {
343           tlvi = proto_tree_add_text(cdp_tree, tvb, offset,
344                                      length, "Addresses");
345           tlv_tree = proto_item_add_subtree(tlvi, ett_cdp_tlv);
346           proto_tree_add_item(tlv_tree, hf_cdp_tlvtype, tvb, offset + TLV_TYPE, 2, FALSE);
347           proto_tree_add_item(tlv_tree, hf_cdp_tlvlength, tvb, offset + TLV_LENGTH, 2, FALSE);
348         }
349         offset += 4;
350         length -= 4;
351         naddresses = tvb_get_ntohl(tvb, offset);
352         if (tree) {
353           proto_tree_add_text(tlv_tree, tvb, offset, 4,
354                               "Number of addresses: %u", naddresses);
355         }
356         offset += 4;
357         length -= 4;
358         while (naddresses != 0) {
359           addr_length = dissect_address_tlv(tvb, offset, length,
360                                             tlv_tree);
361           if (addr_length < 0)
362             break;
363           offset += addr_length;
364           length -= addr_length;
365                 
366           naddresses--;
367         }
368         offset += length;
369         break;
370               
371       case TYPE_CAPABILITIES:
372         if (tree) {
373           tlvi = proto_tree_add_text(cdp_tree, tvb, offset,
374                                      length, "Capabilities");
375           tlv_tree = proto_item_add_subtree(tlvi, ett_cdp_tlv);
376           proto_tree_add_item(tlv_tree, hf_cdp_tlvtype, tvb, offset + TLV_TYPE, 2, FALSE);
377           proto_tree_add_item(tlv_tree, hf_cdp_tlvlength, tvb, offset + TLV_LENGTH, 2, FALSE);
378         }
379         offset += 4;
380         length -= 4;
381         dissect_capabilities(tvb, offset, length, tlv_tree);
382         offset += length;
383         break;
384               
385       case TYPE_IOS_VERSION:
386         if (tree) {
387           tlvi = proto_tree_add_text(cdp_tree, tvb, offset,
388                                      length, "Software Version");
389           tlv_tree = proto_item_add_subtree(tlvi, ett_cdp_tlv);
390           proto_tree_add_item(tlv_tree, hf_cdp_tlvtype, tvb, offset + TLV_TYPE, 2, FALSE);
391           proto_tree_add_item(tlv_tree, hf_cdp_tlvlength, tvb, offset + TLV_LENGTH, 2, FALSE);
392           add_multi_line_string_to_tree(tlv_tree, tvb, offset + 4,
393                                         length - 4, "Software Version: ");
394         }
395         offset += length;
396         break;
397
398       case TYPE_PLATFORM:
399         /* ??? platform */
400         if (tree) {
401           tlvi = proto_tree_add_text(cdp_tree, tvb,
402                                      offset, length, "Platform: %s",
403                                      tvb_format_text(tvb, offset + 4, length - 4));
404           tlv_tree = proto_item_add_subtree(tlvi, ett_cdp_tlv);
405           proto_tree_add_item(tlv_tree, hf_cdp_tlvtype, tvb, offset + TLV_TYPE, 2, FALSE);
406           proto_tree_add_item(tlv_tree, hf_cdp_tlvlength, tvb, offset + TLV_LENGTH, 2, FALSE);
407           proto_tree_add_item(tlv_tree, hf_cdp_platform, tvb, offset + 4, length - 4, FALSE);
408         }
409         offset += length;
410         break;
411
412       case TYPE_IP_PREFIX:
413         if (length == 8) {
414           /* if length is 8 then this is default gw not prefix */
415           if (tree) {
416             tlvi = proto_tree_add_text(cdp_tree, tvb, offset,
417                                        length, "ODR Default gateway: %s",
418                                        ip_to_str(tvb_get_ptr(tvb, offset+4, 4)));
419             tlv_tree = proto_item_add_subtree(tlvi, ett_cdp_tlv);
420                 proto_tree_add_item(tlv_tree, hf_cdp_tlvtype, tvb, offset + TLV_TYPE, 2, FALSE);
421             proto_tree_add_item(tlv_tree, hf_cdp_tlvlength, tvb, offset + TLV_LENGTH, 2, FALSE);
422             proto_tree_add_text(tlv_tree, tvb, offset+4, 4,
423                                 "ODR Default gateway = %s",
424                                 ip_to_str(tvb_get_ptr(tvb, offset+4, 4)));
425           }
426           offset += 8;
427         } else {  
428           if (tree) {
429             tlvi = proto_tree_add_text(cdp_tree, tvb, offset,
430                                        length, "IP Prefixes: %d",length/5);
431
432             /* the actual number of prefixes is (length-4)/5
433                but if the variable is not a "float" but "integer"
434                then length/5=(length-4)/5  :)  */
435
436             tlv_tree = proto_item_add_subtree(tlvi, ett_cdp_tlv);
437                 proto_tree_add_item(tlv_tree, hf_cdp_tlvtype, tvb, offset + TLV_TYPE, 2, FALSE);
438             proto_tree_add_item(tlv_tree, hf_cdp_tlvlength, tvb, offset + TLV_LENGTH, 2, FALSE);
439           }
440           offset += 4;
441           length -= 4;
442           while (length > 0) {
443             if (tree) {
444               proto_tree_add_text(tlv_tree, tvb, offset, 5,
445                                   "IP Prefix = %s/%u",
446                                   ip_to_str(tvb_get_ptr(tvb, offset, 4)),
447                                   tvb_get_guint8(tvb,offset+4));
448             }
449             offset += 5;
450             length -= 5;
451           }
452         }
453         break;
454
455       case TYPE_PROTOCOL_HELLO:
456         if (tree) {
457           tlvi = proto_tree_add_text(cdp_tree, tvb,
458                                      offset,length, "Protocol Hello: %s",
459                                      val_to_str(tvb_get_ntohs(tvb, offset+7), type_hello_vals, "Unknown (0x%04x)"));
460           tlv_tree = proto_item_add_subtree(tlvi, ett_cdp_tlv);
461           proto_tree_add_item(tlv_tree, hf_cdp_tlvtype, tvb, offset + TLV_TYPE, 2, FALSE);
462           proto_tree_add_item(tlv_tree, hf_cdp_tlvlength, tvb, offset + TLV_LENGTH, 2, FALSE);
463           proto_tree_add_text(tlv_tree, tvb, offset+4, 3,
464                               "OUI: 0x%06X (%s)",
465                               tvb_get_ntoh24(tvb,offset+4),
466                               val_to_str(tvb_get_ntoh24(tvb,offset+4), oui_vals, "Unknown"));
467           proto_tree_add_text(tlv_tree, tvb, offset+7, 2,
468                               "Protocol ID: 0x%04X (%s)",
469                               tvb_get_ntohs(tvb, offset+7),
470                               val_to_str(tvb_get_ntohs(tvb, offset+7), type_hello_vals, "Unknown"));
471
472           switch(tvb_get_ntohs(tvb, offset+7)) {
473
474           case TYPE_HELLO_CLUSTER_MGMT:
475             /*          proto_tree_add_text(tlv_tree, tvb, offset+9,
476                         length - 9, "Cluster Management");
477             */
478             ip_addr = tvb_get_ipv4(tvb, offset+9);
479             proto_tree_add_text(tlv_tree, tvb, offset+9, 4,
480                                 "Cluster Master IP: %s",ip_to_str((guint8 *)&ip_addr));
481             ip_addr = tvb_get_ipv4(tvb, offset+13);
482             proto_tree_add_text(tlv_tree, tvb, offset+13, 4,
483                                 "UNKNOWN (IP?): 0x%08X (%s)",
484                                 ip_addr, ip_to_str((guint8 *)&ip_addr));
485             proto_tree_add_text(tlv_tree, tvb, offset+17, 1,
486                                 "Version?: 0x%02X",
487                                 tvb_get_guint8(tvb, offset+17));
488             proto_tree_add_text(tlv_tree, tvb, offset+18, 1,
489                                 "Sub Version?: 0x%02X",
490                                 tvb_get_guint8(tvb, offset+18));
491             proto_tree_add_text(tlv_tree, tvb, offset+19, 1,
492                                 "Status?: 0x%02X",
493                                 tvb_get_guint8(tvb, offset+19));
494             proto_tree_add_text(tlv_tree, tvb, offset+20, 1,
495                                 "UNKNOWN: 0x%02X",
496                                 tvb_get_guint8(tvb, offset+20));
497             proto_tree_add_text(tlv_tree, tvb, offset+21, 6,
498                                 "Cluster Commander MAC: %s",
499                                 ether_to_str(tvb_get_ptr(tvb, offset+21, 6)));
500             proto_tree_add_text(tlv_tree, tvb, offset+27, 6,
501                                 "Switch's MAC: %s",
502                                 ether_to_str(tvb_get_ptr(tvb, offset+27, 6)));
503             proto_tree_add_text(tlv_tree, tvb, offset+33, 1,
504                                 "UNKNOWN: 0x%02X",
505                                 tvb_get_guint8(tvb, offset+33));
506             proto_tree_add_text(tlv_tree, tvb, offset+34, 2,
507                                 "Management VLAN: %d",
508                                 tvb_get_ntohs(tvb, offset+34));
509             break;
510           default:
511             proto_tree_add_text(tlv_tree, tvb, offset + 9,
512                                 length - 9, "Unknown");
513             break;
514           }
515         }
516         offset += length;
517         break;
518
519       case TYPE_VTP_MGMT_DOMAIN:
520         if (tree) {
521           tlvi = proto_tree_add_text(cdp_tree, tvb,
522                                      offset, length, "VTP Management Domain: %s",
523                                      tvb_format_text(tvb, offset + 4, length - 4));
524           tlv_tree = proto_item_add_subtree(tlvi, ett_cdp_tlv);
525           proto_tree_add_item(tlv_tree, hf_cdp_tlvtype, tvb, offset + TLV_TYPE, 2, FALSE);
526           proto_tree_add_item(tlv_tree, hf_cdp_tlvlength, tvb, offset + TLV_LENGTH, 2, FALSE);
527           proto_tree_add_text(tlv_tree, tvb, offset + 4,
528                               length - 4, "VTP Management Domain: %s",
529                               tvb_format_text(tvb, offset + 4, length - 4));
530         }
531         offset += length;
532         break;
533
534       case TYPE_NATIVE_VLAN:
535         if (tree) {
536           tlvi = proto_tree_add_text(cdp_tree, tvb,
537                                      offset, length, "Native VLAN: %u",
538                                      tvb_get_ntohs(tvb, offset + 4));
539           tlv_tree = proto_item_add_subtree(tlvi, ett_cdp_tlv);
540           proto_tree_add_item(tlv_tree, hf_cdp_tlvtype, tvb, offset + TLV_TYPE, 2, FALSE);
541           proto_tree_add_item(tlv_tree, hf_cdp_tlvlength, tvb, offset + TLV_LENGTH, 2, FALSE);
542           proto_tree_add_text(tlv_tree, tvb, offset + 4,
543                               length - 4, "Native VLAN: %u",
544                               tvb_get_ntohs(tvb, offset + 4));
545         }
546         offset += length;
547         break;
548
549       case TYPE_DUPLEX:
550         if (tree) {
551           tlvi = proto_tree_add_text(cdp_tree, tvb,
552                                      offset, length, "Duplex: %s",
553                                      tvb_get_guint8(tvb, offset + 4) ?
554                                      "Full" : "Half" );
555           tlv_tree = proto_item_add_subtree(tlvi, ett_cdp_tlv);
556           proto_tree_add_item(tlv_tree, hf_cdp_tlvtype, tvb, offset + TLV_TYPE, 2, FALSE);
557           proto_tree_add_item(tlv_tree, hf_cdp_tlvlength, tvb, offset + TLV_LENGTH, 2, FALSE);
558           proto_tree_add_text(tlv_tree, tvb, offset + 4,
559                               length - 4, "Duplex: %s",
560                               tvb_get_guint8(tvb, offset + 4) ?
561                               "Full" : "Half" );
562         }
563         offset += length;
564         break;
565
566       case TYPE_VOIP_VLAN_REPLY:
567         if (tree) {
568           if (length >= 7) {
569             tlvi = proto_tree_add_text(cdp_tree, tvb, offset, length, 
570                                        "VoIP VLAN Reply: %u", tvb_get_ntohs(tvb, offset + 5));
571           } else {
572             /*
573              * XXX - what are these?  I've seen them in some captures;
574              * they have a length of 6, and run up to the end of
575              * the packet, so if we try to dissect it the same way
576              * we dissect the 7-byte ones, we report a malformed
577              * frame.
578              */
579             tlvi = proto_tree_add_text(cdp_tree, tvb,
580                                        offset, length, "VoIP VLAN Reply");
581           }
582           tlv_tree = proto_item_add_subtree(tlvi, ett_cdp_tlv);
583           proto_tree_add_item(tlv_tree, hf_cdp_tlvtype, tvb, offset + TLV_TYPE, 2, FALSE);
584           proto_tree_add_item(tlv_tree, hf_cdp_tlvlength, tvb, offset + TLV_LENGTH, 2, FALSE);
585           proto_tree_add_text(tlv_tree, tvb, offset + 4,
586                               1, "Data");
587           if (length >= 7) {
588             proto_tree_add_text(tlv_tree, tvb, offset + 5,
589                                 2, "Voice VLAN: %u",
590                                 tvb_get_ntohs(tvb, offset + 5));
591           }
592         }
593         offset += length;
594         break;
595
596       case TYPE_VOIP_VLAN_QUERY:
597         if (tree) {
598           if (length >= 7) {
599             tlvi = proto_tree_add_text(cdp_tree, tvb,
600                                        offset, length, "VoIP VLAN Query: %u", tvb_get_ntohs(tvb, offset + 5));
601           } else {
602             /*
603              * XXX - what are these?  I've seen them in some captures;
604              * they have a length of 6, and run up to the end of
605              * the packet, so if we try to dissect it the same way
606              * we dissect the 7-byte ones, we report a malformed
607              * frame.
608              */
609             tlvi = proto_tree_add_text(cdp_tree, tvb,
610                                        offset, length, "VoIP VLAN Query");
611           }
612           tlv_tree = proto_item_add_subtree(tlvi, ett_cdp_tlv);
613           proto_tree_add_item(tlv_tree, hf_cdp_tlvtype, tvb, offset + TLV_TYPE, 2, FALSE);
614           proto_tree_add_item(tlv_tree, hf_cdp_tlvlength, tvb, offset + TLV_LENGTH, 2, FALSE);
615           proto_tree_add_text(tlv_tree, tvb, offset + 4,
616                               1, "Data");
617           if (length >= 7) {
618             proto_tree_add_text(tlv_tree, tvb, offset + 5,
619                                 2, "Voice VLAN: %u",
620                                 tvb_get_ntohs(tvb, offset + 5));
621           }
622         }
623         offset += length;
624         break;
625
626       case TYPE_POWER:
627         if (tree) {
628           tlvi = proto_tree_add_text(cdp_tree, tvb,
629                                      offset, length, "Power Consumption: %u mW",
630                                      tvb_get_ntohs(tvb, offset + 4));
631           tlv_tree = proto_item_add_subtree(tlvi, ett_cdp_tlv);
632           proto_tree_add_item(tlv_tree, hf_cdp_tlvtype, tvb, offset + TLV_TYPE, 2, FALSE);
633           proto_tree_add_item(tlv_tree, hf_cdp_tlvlength, tvb, offset + TLV_LENGTH, 2, FALSE);
634           proto_tree_add_text(tlv_tree, tvb, offset + 4,
635                               length - 4, "Power Consumption: %u mW",
636                               tvb_get_ntohs(tvb, offset + 4));
637         }
638         offset += length;
639         break;
640
641       case TYPE_MTU:
642         if (tree) {
643           tlvi = proto_tree_add_text(cdp_tree, tvb,
644                                      offset, length, "MTU: %u",
645                                      tvb_get_ntohl(tvb,offset + 4));
646           tlv_tree = proto_item_add_subtree(tlvi, ett_cdp_tlv);
647           proto_tree_add_item(tlv_tree, hf_cdp_tlvtype, tvb, offset + TLV_TYPE, 2, FALSE);
648           proto_tree_add_item(tlv_tree, hf_cdp_tlvlength, tvb, offset + TLV_LENGTH, 2, FALSE);
649           proto_tree_add_text(tlv_tree, tvb, offset + 4,
650                               length - 4, "MTU: %u",
651                               tvb_get_ntohl(tvb,offset + 4));
652         }
653         offset += length;
654         break;
655
656       case TYPE_TRUST_BITMAP:
657         if (tree) {
658           tlvi = proto_tree_add_text(cdp_tree, tvb,
659                                      offset, length, "Trust Bitmap: 0x%02X",
660                                      tvb_get_guint8(tvb, offset + 4));
661           tlv_tree = proto_item_add_subtree(tlvi, ett_cdp_tlv);
662           proto_tree_add_item(tlv_tree, hf_cdp_tlvtype, tvb, offset + TLV_TYPE, 2, FALSE);
663           proto_tree_add_item(tlv_tree, hf_cdp_tlvlength, tvb, offset + TLV_LENGTH, 2, FALSE);
664           proto_tree_add_text(tlv_tree, tvb, offset + 4,
665                               length - 4, "Trust Bitmap: %02x",
666                               tvb_get_guint8(tvb, offset + 4));
667         }
668         offset += length;
669         break;
670
671       case TYPE_UNTRUSTED_COS:
672         if (tree) {
673           tlvi = proto_tree_add_text(cdp_tree, tvb,
674                                      offset, length, "Untrusted port CoS: 0x%02X",
675                                      tvb_get_guint8(tvb, offset + 4));
676           tlv_tree = proto_item_add_subtree(tlvi, ett_cdp_tlv);
677           proto_tree_add_item(tlv_tree, hf_cdp_tlvtype, tvb, offset + TLV_TYPE, 2, FALSE);
678           proto_tree_add_item(tlv_tree, hf_cdp_tlvlength, tvb, offset + TLV_LENGTH, 2, FALSE);
679           proto_tree_add_text(tlv_tree, tvb, offset + 4,
680                               length - 4, "Untrusted port CoS: %02x",
681                               tvb_get_guint8(tvb, offset + 4));
682         }
683         offset += length;
684         break;
685
686       case TYPE_SYSTEM_NAME:
687         if (tree) {
688           tlvi = proto_tree_add_text(cdp_tree, tvb,
689                                      offset, length, "System Name: %s",
690                                      tvb_format_text(tvb, offset + 4, length - 4));
691           tlv_tree = proto_item_add_subtree(tlvi, ett_cdp_tlv);
692           proto_tree_add_item(tlv_tree, hf_cdp_tlvtype, tvb, offset + TLV_TYPE, 2, FALSE);
693           proto_tree_add_item(tlv_tree, hf_cdp_tlvlength, tvb, offset + TLV_LENGTH, 2, FALSE);
694           proto_tree_add_text(tlv_tree, tvb, offset + 4,
695                               length - 4, "System Name: %s",
696                               tvb_format_text(tvb, offset + 4, length - 4));
697         }
698         offset += length;
699         break;
700
701       case TYPE_SYSTEM_OID:
702         if (tree) {
703           tlvi = proto_tree_add_text(cdp_tree, tvb,
704                                      offset, length, "System Object Identifier");
705           tlv_tree = proto_item_add_subtree(tlvi, ett_cdp_tlv);
706           proto_tree_add_item(tlv_tree, hf_cdp_tlvtype, tvb, offset + TLV_TYPE, 2, FALSE);
707           proto_tree_add_item(tlv_tree, hf_cdp_tlvlength, tvb, offset + TLV_LENGTH, 2, FALSE);
708           proto_tree_add_text(tlv_tree, tvb, offset + 4,
709                               length - 4, "System Object Identifier: %s",
710                               tvb_bytes_to_str(tvb, offset + 4, length - 4));
711         }
712         offset += length;
713         break;
714
715       case TYPE_MANAGEMENT_ADDR:
716         if (tree) {
717           tlvi = proto_tree_add_text(cdp_tree, tvb,
718                                      offset, length, "Management Addresses");
719           tlv_tree = proto_item_add_subtree(tlvi, ett_cdp_tlv);
720           proto_tree_add_item(tlv_tree, hf_cdp_tlvtype, tvb, offset + TLV_TYPE, 2, FALSE);
721           proto_tree_add_item(tlv_tree, hf_cdp_tlvlength, tvb, offset + TLV_LENGTH, 2, FALSE);
722         }
723         offset += 4;
724         length -= 4;
725         naddresses = tvb_get_ntohl(tvb, offset);
726         if (tree) {
727           proto_tree_add_text(tlv_tree, tvb, offset, 4,
728                               "Number of addresses: %u", naddresses);
729         }
730         offset += 4;
731         length -= 4;
732         while (naddresses != 0) {
733           addr_length = dissect_address_tlv(tvb, offset, length,
734                                             tlv_tree);
735           if (addr_length < 0)
736             break;
737           offset += addr_length;
738           length -= addr_length;
739                 
740           naddresses--;
741         }
742         offset += length;
743         break;
744               
745       case TYPE_LOCATION:
746         if (tree) {
747           tlvi = proto_tree_add_text(cdp_tree, tvb,
748                                      offset, length, "Location: %s",
749                                      tvb_format_text(tvb, offset + 5, length - 5));
750           tlv_tree = proto_item_add_subtree(tlvi, ett_cdp_tlv);
751           proto_tree_add_item(tlv_tree, hf_cdp_tlvtype, tvb, offset + TLV_TYPE, 2, FALSE);
752           proto_tree_add_item(tlv_tree, hf_cdp_tlvlength, tvb, offset + TLV_LENGTH, 2, FALSE);
753           proto_tree_add_text(tlv_tree, tvb, offset + 4,
754                               1 , "UNKNOWN: 0x%02X",
755                               tvb_get_guint8(tvb, offset + 4));
756           proto_tree_add_text(tlv_tree, tvb, offset + 5,
757                               length - 5, "Location: %s",
758                               tvb_format_text(tvb, offset + 5, length - 5));
759         }
760         offset += length;
761         break;
762               
763       case TYPE_POWER_REQUESTED:
764         if (tree) {
765           tlvi = proto_tree_add_text(cdp_tree, tvb,
766                                      offset, length, "Power Request: ");
767           tlv_tree = proto_item_add_subtree(tlvi, ett_cdp_tlv);
768           proto_tree_add_item(tlv_tree, hf_cdp_tlvtype, tvb, offset + TLV_TYPE, 2, FALSE);
769           proto_tree_add_item(tlv_tree, hf_cdp_tlvlength, tvb, offset + TLV_LENGTH, 2, FALSE);
770           proto_tree_add_text(tlv_tree, tvb, offset + 4,
771                               2, "Request-ID: %u",
772                               tvb_get_ntohs(tvb, offset + 4));
773           proto_tree_add_text(tlv_tree, tvb, offset + 6,
774                               2, "Management-ID: %u",
775                               tvb_get_ntohs(tvb, offset + 6));
776         }
777         power_req_len = (tvb_get_ntohs(tvb, offset + TLV_LENGTH)) - 8;
778         /* Move offset to where the list of Power Request Values Exist */
779         offset += 8;
780         while(power_req_len) {
781           if (power_req_len > 4) {
782             power_req = tvb_get_ntohl(tvb, offset);
783             if (tree) {
784               proto_tree_add_text(tlv_tree, tvb, offset,
785                                   4, "Power Requested: %u mW", power_req);
786               proto_item_append_text(tlvi, "%u mW, ", power_req);
787             }
788             power_req_len -= 4;
789             offset += 4;
790           } else {
791             if (power_req_len == 4) {
792               power_req = tvb_get_ntohl(tvb, offset);
793               if (tree) {
794                 proto_tree_add_text(tlv_tree, tvb, offset,
795                                     4, "Power Requested: %u mW", power_req);
796                 proto_item_append_text(tlvi, "%u mW", power_req);
797               }
798             }
799             offset += power_req_len;
800             break;
801           }
802         }
803         break;
804               
805       case TYPE_POWER_AVAILABLE:
806         if (tree) {
807           tlvi = proto_tree_add_text(cdp_tree, tvb,
808                                      offset, length, "Power Available: ");
809           tlv_tree = proto_item_add_subtree(tlvi, ett_cdp_tlv);
810           proto_tree_add_item(tlv_tree, hf_cdp_tlvtype, tvb, offset + TLV_TYPE, 2, FALSE);
811           proto_tree_add_item(tlv_tree, hf_cdp_tlvlength, tvb, offset + TLV_LENGTH, 2, FALSE);
812           proto_tree_add_text(tlv_tree, tvb, offset + 4,
813                               2, "Request-ID: %u",
814                               tvb_get_ntohs(tvb, offset + 4));
815           proto_tree_add_text(tlv_tree, tvb, offset + 6,
816                               2, "Management-ID: %u",
817                               tvb_get_ntohs(tvb, offset + 6));
818         }
819         power_avail_len = (tvb_get_ntohs(tvb, offset + TLV_LENGTH)) - 8;
820         /* Move offset to where the list of Power Available Values Exist */
821         offset += 8;
822         while(power_avail_len) {
823           if (power_avail_len >= 4) {
824             power_avail = tvb_get_ntohl(tvb, offset);
825             if (tree) {
826               proto_tree_add_text(tlv_tree, tvb, offset,
827                                   4, "Power Available: %u mW", power_avail);
828               proto_item_append_text(tlvi, "%u mW, ", power_avail);
829             }
830             power_avail_len -= 4;
831             offset += 4;
832           } else {
833             offset += power_avail_len;
834             break;
835           }
836         }
837         break;
838
839       case TYPE_NRGYZ:
840         if (tree) {
841           tlvi = proto_tree_add_text(cdp_tree, tvb,
842                                      offset, length, "EnergyWise");
843           tlv_tree = proto_item_add_subtree(tlvi, ett_cdp_tlv);
844           proto_tree_add_item(tlv_tree, hf_cdp_tlvtype, tvb, offset + TLV_TYPE, 2, FALSE);
845           proto_tree_add_item(tlv_tree, hf_cdp_tlvlength, tvb, offset + TLV_LENGTH, 2, FALSE);
846           proto_tree_add_text(tlv_tree, tvb, offset + 4,
847                               20, "Encrypted Data");
848           proto_tree_add_text(tlv_tree, tvb, offset + 24,
849                               4, "Unknown (Seen Sequence?): %u",
850                               tvb_get_ntohl(tvb, offset + 24));
851           proto_tree_add_text(tlv_tree, tvb, offset + 28,
852                               4, "Sequence Number: %u",
853                               tvb_get_ntohl(tvb, offset + 28));
854           proto_tree_add_text(tlv_tree, tvb, offset + 32,
855                               16, "Model Number: %s",
856                               tvb_format_stringzpad(tvb, offset + 32, 16));
857           proto_tree_add_text(tlv_tree, tvb, offset + 48,
858                               2, "Unknown Pad: %x",
859                               tvb_get_ntohs(tvb, offset + 48));
860           proto_tree_add_text(tlv_tree, tvb, offset + 50,
861                               3, "Hardware Version ID: %s",
862                               tvb_format_stringzpad(tvb, offset + 50, 3));
863           proto_tree_add_text(tlv_tree, tvb, offset + 53,
864                               11, "System Serial Number: %s",
865                               tvb_format_stringzpad(tvb, offset + 53, 11));
866           proto_tree_add_text(tlv_tree, tvb, offset + 64,
867                               8, "Unknown Values");
868           proto_tree_add_text(tlv_tree, tvb, offset + 72,
869                               2, "Length of TLV table: %u",
870                               tvb_get_ntohs(tvb, offset + 72));
871           proto_tree_add_text(tlv_tree, tvb, offset + 74,
872                               2, "Number of TLVs in table: %u",
873                               tvb_get_ntohs(tvb, offset + 74));
874 /*
875           proto_tree_add_text(tlv_tree, tvb,
876                               offset + 76, length - 76,
877                               "EnergyWise TLV Table");
878 */
879           dissect_nrgyz_tlv(tvb, offset + 76,
880                             tvb_get_ntohs(tvb, offset + 72),
881                             tvb_get_ntohs(tvb, offset + 74),
882                             tlv_tree);
883
884
885         }
886         offset += length;
887         break;
888
889       default:
890         if (tree) {
891           tlvi = proto_tree_add_text(cdp_tree, tvb, offset,
892                                      length, "Type: %s, length: %u",
893                                      val_to_str(type, type_vals, "Unknown (0x%04x)"),
894                                      length);
895           tlv_tree = proto_item_add_subtree(tlvi, ett_cdp_tlv);
896           proto_tree_add_item(tlv_tree, hf_cdp_tlvtype, tvb, offset + TLV_TYPE, 2, FALSE);
897           proto_tree_add_item(tlv_tree, hf_cdp_tlvlength, tvb, offset + TLV_LENGTH, 2, FALSE);
898           if (length > 4) {
899             proto_tree_add_text(tlv_tree, tvb, offset + 4,
900                                 length - 4, "Data");
901           } else {
902             return;
903           }
904         }
905         offset += length;
906       }
907     }
908     call_dissector(data_handle, tvb_new_subset_remaining(tvb, offset), pinfo,
909                    cdp_tree);
910 }
911
912 #define PROTO_TYPE_NLPID        1
913 #define PROTO_TYPE_IEEE_802_2   2
914
915 static const value_string proto_type_vals[] = {
916         { PROTO_TYPE_NLPID,      "NLPID" },
917         { PROTO_TYPE_IEEE_802_2, "802.2" },
918         { 0,                     NULL }
919 };
920
921 static int
922 dissect_address_tlv(tvbuff_t *tvb, int offset, int length, proto_tree *tree)
923 {
924     proto_item *ti;
925     proto_tree *address_tree;
926     guint8 protocol_type;
927     guint8 protocol_length;
928     int nlpid;
929     const char *protocol_str;
930     guint16 address_length;
931     const char *address_type_str;
932     const char *address_str;
933
934     if (length < 1)
935         return -1;
936     ti = proto_tree_add_text(tree, tvb, offset, length, "Truncated address");
937     address_tree = proto_item_add_subtree(ti, ett_cdp_address);
938     protocol_type = tvb_get_guint8(tvb, offset);
939     proto_tree_add_text(address_tree, tvb, offset, 1, "Protocol type: %s",
940         val_to_str(protocol_type, proto_type_vals, "Unknown (0x%02x)"));
941     offset += 1;
942     length -= 1;
943
944     if (length < 1)
945         return -1;
946     protocol_length = tvb_get_guint8(tvb, offset);
947     proto_tree_add_text(address_tree, tvb, offset, 1, "Protocol length: %u",
948                         protocol_length);
949     offset += 1;
950     length -= 1;
951
952     if (length < protocol_length) {
953         if (length != 0) {
954             proto_tree_add_text(address_tree, tvb, offset, length,
955               "Protocol: %s (truncated)",
956               tvb_bytes_to_str(tvb, offset, length));
957         }
958         return -1;
959     }
960     protocol_str = NULL;
961     if (protocol_type == PROTO_TYPE_NLPID && protocol_length == 1) {
962         nlpid = tvb_get_guint8(tvb, offset);
963         protocol_str = val_to_str(nlpid, nlpid_vals, "Unknown (0x%02x)");
964     } else
965         nlpid = -1;
966     if (protocol_str == NULL)
967         protocol_str = tvb_bytes_to_str(tvb, offset, protocol_length);
968     proto_tree_add_text(address_tree, tvb, offset, protocol_length,
969                         "Protocol: %s", protocol_str);
970     offset += protocol_length;
971     length -= protocol_length;
972
973     if (length < 2)
974         return -1;
975     address_length = tvb_get_ntohs(tvb, offset);
976     proto_tree_add_text(address_tree, tvb, offset, 2, "Address length: %u",
977                         address_length);
978     offset += 2;
979     length -= 2;
980
981     if (length < address_length) {
982         if (length != 0) {
983             proto_tree_add_text(address_tree, tvb, offset, length,
984               "Address: %s (truncated)",
985               tvb_bytes_to_str(tvb, offset, length));
986         }
987         return -1;
988     }
989     /* XXX - the Cisco document seems to be saying that, for 802.2-format
990        protocol types, 0xAAAA03 0x000000 0x0800 is IPv6, but 0x0800 is
991        the Ethernet protocol type for IPv4. */
992     length = 2 + protocol_length + 2 + address_length;
993     address_type_str = NULL;
994     address_str = NULL;
995     if (protocol_type == PROTO_TYPE_NLPID && protocol_length == 1) {
996         switch (nlpid) {
997
998         /* XXX - dissect NLPID_ISO8473_CLNP as OSI CLNP address? */
999
1000         case NLPID_IP:
1001             if (address_length == 4) {
1002                 /* The address is an IP address. */
1003                 address_type_str = "IP address";
1004                 address_str = ip_to_str(tvb_get_ptr(tvb, offset, 4));
1005             }
1006             break;
1007         }
1008     }
1009     if (address_type_str == NULL)
1010         address_type_str = "Address";
1011     if (address_str == NULL) {
1012         address_str = tvb_bytes_to_str(tvb, offset, address_length);
1013     }
1014     proto_item_set_text(ti, "%s: %s", address_type_str, address_str);
1015     proto_tree_add_text(address_tree, tvb, offset, address_length, "%s: %s",
1016       address_type_str, address_str);
1017     return 2 + protocol_length + 2 + address_length;
1018 }
1019
1020 static void
1021 dissect_capabilities(tvbuff_t *tvb, int offset, int length, proto_tree *tree)
1022 {
1023     proto_item *ti;
1024     proto_tree *capabilities_tree;
1025     guint32 capabilities;
1026
1027     if (length < 4)
1028         return;
1029     capabilities = tvb_get_ntohl(tvb, offset);
1030     ti = proto_tree_add_text(tree, tvb, offset, length, "Capabilities: 0x%08x",
1031         capabilities);
1032     capabilities_tree = proto_item_add_subtree(ti, ett_cdp_capabilities);
1033     proto_tree_add_text(capabilities_tree, tvb, offset, 4, "%s",
1034         decode_boolean_bitfield(capabilities, 0x01, 4*8,
1035             "Is  a Router",
1036             "Not a Router"));
1037     proto_tree_add_text(capabilities_tree, tvb, offset, 4, "%s",
1038         decode_boolean_bitfield(capabilities, 0x02, 4*8,
1039             "Is  a Transparent Bridge",
1040             "Not a Transparent Bridge"));
1041     proto_tree_add_text(capabilities_tree, tvb, offset, 4, "%s",
1042         decode_boolean_bitfield(capabilities, 0x04, 4*8,
1043             "Is  a Source Route Bridge",
1044             "Not a Source Route Bridge"));
1045     proto_tree_add_text(capabilities_tree, tvb, offset, 4, "%s",
1046         decode_boolean_bitfield(capabilities, 0x08, 4*8,
1047             "Is  a Switch",
1048             "Not a Switch"));
1049     proto_tree_add_text(capabilities_tree, tvb, offset, 4, "%s",
1050         decode_boolean_bitfield(capabilities, 0x10, 4*8,
1051             "Is  a Host",
1052             "Not a Host"));
1053     proto_tree_add_text(capabilities_tree, tvb, offset, 4, "%s",
1054         decode_boolean_bitfield(capabilities, 0x20, 4*8,
1055             "Is  IGMP capable",
1056             "Not IGMP capable"));
1057     proto_tree_add_text(capabilities_tree, tvb, offset, 4, "%s",
1058         decode_boolean_bitfield(capabilities, 0x40, 4*8,
1059             "Is  a Repeater",
1060             "Not a Repeater"));
1061 }
1062
1063 static void
1064 dissect_nrgyz_tlv(tvbuff_t *tvb, int offset, guint16 length, guint16 num,
1065                   proto_tree *tree) 
1066 {
1067     guint32 tlvt, tlvl, ip_addr;
1068     proto_item *it = NULL;
1069     proto_tree *etree = NULL;
1070     char const *ttext = NULL;
1071
1072     while (num-- && length >= 8) {
1073         tlvt = tvb_get_ntohl(tvb, offset);
1074         tlvl = tvb_get_ntohl(tvb, offset + 4);
1075
1076         if (length < tlvl) break;
1077         length -= tlvl;
1078
1079         if (tlvl < 8) {
1080                 proto_tree_add_text(tree, tvb, offset, 8,
1081                                     "TLV with invalid length %u (< 8)",
1082                                     tlvl);
1083                 offset += 8;
1084                 break;
1085         }
1086         else {
1087             ttext = val_to_str(tlvt, type_nrgyz_vals, "Unknown (0x%04x)");
1088             switch (tlvt) {
1089             case TYPE_NRGYZ_ROLE:
1090             case TYPE_NRGYZ_DOMAIN:
1091             case TYPE_NRGYZ_NAME:
1092                 it = proto_tree_add_text(tree, tvb, offset,
1093                                          tlvl, "EnergyWise %s: %s", ttext,
1094                                          tvb_format_stringzpad(tvb,
1095                                                          offset + 8, tlvl - 8)
1096                                          );
1097                 break;
1098             case TYPE_NRGYZ_REPLYTO:
1099                 ip_addr = tvb_get_ipv4(tvb, offset + 12);
1100                 it = proto_tree_add_text(tree, tvb, offset,
1101                                          tlvl, "EnergyWise %s: %s port %u",
1102                                          ttext,
1103                                          ip_to_str((guint8 *)&ip_addr),
1104                                          tvb_get_ntohs(tvb, offset + 10)
1105                                          );
1106                 break;
1107             default:
1108                 it = proto_tree_add_text(tree, tvb, offset,
1109                                          tlvl, "EnergyWise %s TLV", ttext);
1110             }
1111             etree = proto_item_add_subtree(it, ett_cdp_nrgyz_tlv);
1112             proto_tree_add_text(etree, tvb, offset, 4,
1113                                 "TLV Type: %x (%s)", tlvt, ttext);
1114             proto_tree_add_text(etree, tvb, offset + 4, 4,
1115                                 "TLV Length: %u", tlvl);
1116             switch (tlvt) {
1117             case TYPE_NRGYZ_ROLE:
1118             case TYPE_NRGYZ_DOMAIN:
1119             case TYPE_NRGYZ_NAME:
1120                 proto_tree_add_text(etree, tvb, offset + 8,
1121                                     tlvl - 8, "%s %s", ttext,
1122                                     tvb_format_stringzpad(tvb,
1123                                                           offset + 8, tlvl - 8)
1124                                     );
1125                 break;
1126             case TYPE_NRGYZ_REPLYTO:
1127                 ip_addr = tvb_get_ipv4(tvb, offset + 12);
1128                 proto_tree_add_text(etree, tvb, offset + 8, 2,
1129                                     "Unknown Field");
1130                 proto_tree_add_text(etree, tvb, offset + 10, 2,
1131                                     "Port %d",
1132                                     tvb_get_ntohs(tvb, offset + 10)
1133                                     );
1134                 proto_tree_add_text(etree, tvb, offset + 12, 4,
1135                                     "IP Address %s",
1136                                     ip_to_str((guint8 *)&ip_addr)
1137                                     );
1138                 proto_tree_add_text(etree, tvb, offset + 16, 2,
1139                                     "Unknown Field (Backup server Port?)");
1140                 proto_tree_add_text(etree, tvb, offset + 18, 4,
1141                                     "Unknown Field (Backup Server IP?)");
1142                 break;
1143             default:
1144                 if (tlvl > 8) {
1145                     proto_tree_add_text(etree, tvb, offset + 8,
1146                                         tlvl - 8, "Data");
1147                 }
1148             }
1149             offset += tlvl;
1150         }
1151     }
1152     if (length) {
1153         proto_tree_add_text(tree, tvb, offset, length,
1154                             "Invalid garbage at end");
1155     }
1156 }
1157
1158 static void
1159 add_multi_line_string_to_tree(proto_tree *tree, tvbuff_t *tvb, gint start,
1160   gint len, const gchar *prefix)
1161 {
1162     int prefix_len;
1163     int i;
1164     char blanks[64+1];
1165     gint next;
1166     int line_len;
1167     int data_len;
1168
1169     prefix_len = (int)strlen(prefix);
1170     if (prefix_len > 64)
1171         prefix_len = 64;
1172     for (i = 0; i < prefix_len; i++)
1173         blanks[i] = ' ';
1174     blanks[i] = '\0';
1175     while (len > 0) {
1176         line_len = tvb_find_line_end(tvb, start, len, &next, FALSE);
1177         data_len = next - start;
1178         proto_tree_add_text(tree, tvb, start, data_len, "%s%s", prefix,
1179            tvb_format_stringzpad(tvb, start, line_len));
1180         start += data_len;
1181         len -= data_len;
1182         prefix = blanks;
1183     }
1184 }
1185
1186 void
1187 proto_register_cdp(void)
1188 {
1189     static hf_register_info hf[] = {
1190         { &hf_cdp_version,
1191         { "Version",            "cdp.version",  FT_UINT8, BASE_DEC, NULL, 0x0,
1192           NULL, HFILL }},
1193
1194         { &hf_cdp_ttl,
1195         { "TTL",                "cdp.ttl", FT_UINT16, BASE_DEC, NULL, 0x0,
1196           NULL, HFILL }},
1197
1198         { &hf_cdp_checksum,
1199         { "Checksum",           "cdp.checksum", FT_UINT16, BASE_HEX, NULL, 0x0,
1200           NULL, HFILL }},
1201
1202         { &hf_cdp_checksum_good,
1203           { "Good",       "cdp.checksum_good", FT_BOOLEAN, BASE_NONE, NULL, 0x0,
1204             "True: checksum matches packet content; False: doesn't match content or not checked", HFILL }},
1205         
1206         { &hf_cdp_checksum_bad,
1207           { "Bad",       "cdp.checksum_bad", FT_BOOLEAN, BASE_NONE, NULL, 0x0,
1208             "True: checksum doesn't match packet content; False: matches content or not checked", HFILL }},
1209         
1210         { &hf_cdp_tlvtype,
1211         { "Type",               "cdp.tlv.type", FT_UINT16, BASE_HEX, VALS(type_vals), 0x0,
1212           NULL, HFILL }},
1213
1214         { &hf_cdp_tlvlength,
1215         { "Length",             "cdp.tlv.len", FT_UINT16, BASE_DEC, NULL, 0x0,
1216           NULL, HFILL }},
1217
1218         { &hf_cdp_deviceid, 
1219         {"Device ID", "cdp.deviceid", FT_STRING, BASE_NONE,
1220          NULL, 0, NULL, HFILL }},
1221
1222         { &hf_cdp_platform, 
1223         {"Platform", "cdp.platform", FT_STRING, BASE_NONE,
1224          NULL, 0, NULL, HFILL }},
1225
1226         { &hf_cdp_portid, 
1227         {"Sent through Interface", "cdp.portid", FT_STRING, BASE_NONE,
1228                 NULL, 0, NULL, HFILL }}
1229     };
1230     static gint *ett[] = {
1231         &ett_cdp,
1232         &ett_cdp_tlv,
1233         &ett_cdp_nrgyz_tlv,
1234         &ett_cdp_address,
1235         &ett_cdp_capabilities,
1236         &ett_cdp_checksum
1237     };
1238
1239     proto_cdp = proto_register_protocol("Cisco Discovery Protocol",
1240                                         "CDP", "cdp");
1241     proto_register_field_array(proto_cdp, hf, array_length(hf));
1242     proto_register_subtree_array(ett, array_length(ett));
1243 }
1244
1245 void
1246 proto_reg_handoff_cdp(void)
1247 {
1248     dissector_handle_t cdp_handle;
1249
1250     data_handle = find_dissector("data");
1251     cdp_handle = create_dissector_handle(dissect_cdp, proto_cdp);
1252     dissector_add("llc.cisco_pid", 0x2000, cdp_handle);
1253     dissector_add("chdlctype", 0x2000, cdp_handle);
1254     dissector_add("ppp.protocol", 0x0207, cdp_handle);
1255 }