Get rid of get_ber_last_reated_item() and fix dissection of wIN-TriggerList.
[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
69 static gint ett_cdp = -1;
70 static gint ett_cdp_tlv = -1;
71 static gint ett_cdp_address = -1;
72 static gint ett_cdp_capabilities = -1;
73 static gint ett_cdp_checksum = -1;
74
75 static dissector_handle_t data_handle;
76
77 static int
78 dissect_address_tlv(tvbuff_t *tvb, int offset, int length, proto_tree *tree);
79 static void
80 dissect_capabilities(tvbuff_t *tvb, int offset, int length, proto_tree *tree);
81 static void
82 add_multi_line_string_to_tree(proto_tree *tree, tvbuff_t *tvb, gint start,
83   gint len, const gchar *prefix);
84
85 #define TYPE_DEVICE_ID          0x0001
86 #define TYPE_ADDRESS            0x0002
87 #define TYPE_PORT_ID            0x0003
88 #define TYPE_CAPABILITIES       0x0004
89 #define TYPE_IOS_VERSION        0x0005
90 #define TYPE_PLATFORM           0x0006
91 #define TYPE_IP_PREFIX          0x0007
92 #define TYPE_PROTOCOL_HELLO     0x0008 /* Protocol Hello */
93 #define TYPE_VTP_MGMT_DOMAIN    0x0009 /* VTP Domain, CTPv2 - see second URL */
94 #define TYPE_NATIVE_VLAN        0x000a /* Native VLAN, CTPv2 - see second URL */
95 #define TYPE_DUPLEX             0x000b /* Full/Half Duplex - see second URL */
96 /*                              0x000c */
97 /*                              0x000d */
98 #define TYPE_VOIP_VLAN_REPLY    0x000e /* VoIP VLAN reply */
99 #define TYPE_VOIP_VLAN_QUERY    0x000f /* VoIP VLAN query */
100 #define TYPE_POWER              0x0010 /* Power consumption */
101 #define TYPE_MTU                0x0011 /* MTU */
102 #define TYPE_TRUST_BITMAP       0x0012 /* Trust bitmap */
103 #define TYPE_UNTRUSTED_COS      0x0013 /* Untrusted port CoS */
104 #define TYPE_SYSTEM_NAME        0x0014 /* System Name */
105 #define TYPE_SYSTEM_OID         0x0015 /* System OID */
106 #define TYPE_MANAGEMENT_ADDR    0x0016 /* Management Address(es) */
107 #define TYPE_LOCATION           0x0017 /* Location */
108
109
110 static const value_string type_vals[] = {
111         { TYPE_DEVICE_ID,       "Device ID" },
112         { TYPE_ADDRESS,         "Addresses" },
113         { TYPE_PORT_ID,         "Port ID" },
114         { TYPE_CAPABILITIES,    "Capabilities" },
115         { TYPE_IOS_VERSION,     "Software version" },
116         { TYPE_PLATFORM,        "Platform" },
117         { TYPE_IP_PREFIX,       "IP Prefix/Gateway (used for ODR)" },
118         { TYPE_PROTOCOL_HELLO,  "Protocol Hello" },
119         { TYPE_VTP_MGMT_DOMAIN, "VTP Management Domain" },
120         { TYPE_NATIVE_VLAN,     "Native VLAN" },
121         { TYPE_DUPLEX,          "Duplex" },
122
123         { TYPE_VOIP_VLAN_REPLY, "VoIP VLAN Reply" },
124         { TYPE_VOIP_VLAN_QUERY, "VoIP VLAN Query" },
125         { TYPE_POWER,           "Power consumption" },
126         { TYPE_MTU,             "MTU"},
127         { TYPE_TRUST_BITMAP,    "Trust Bitmap" },
128         { TYPE_UNTRUSTED_COS,   "Untrusted Port CoS" },
129         { TYPE_SYSTEM_NAME,     "System Name" },
130         { TYPE_SYSTEM_OID,      "System Object ID" },
131         { TYPE_MANAGEMENT_ADDR, "Management Address" },
132         { TYPE_LOCATION,        "Location" },
133         { 0,                    NULL }
134 };
135
136 #define TYPE_HELLO_CLUSTER_MGMT    0x0112
137
138 static const value_string type_hello_vals[] = {
139         { TYPE_HELLO_CLUSTER_MGMT,   "Cluster Management" },
140         { 0,                    NULL }
141 };
142
143 static void
144 dissect_cdp(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree)
145 {
146     proto_item *ti, *checksum_item;
147     proto_tree *cdp_tree = NULL, *checksum_tree;
148     int offset = 0;
149     guint16 type;
150     guint16 length, packet_checksum, computed_checksum, data_length;
151     gboolean checksum_good, checksum_bad;
152     proto_item *tlvi;
153     proto_tree *tlv_tree;
154     int real_length;
155     guint32 naddresses;
156     int addr_length;
157     guint32 ip_addr;
158     vec_t cksum_vec[1];
159
160     if (check_col(pinfo->cinfo, COL_PROTOCOL))
161         col_set_str(pinfo->cinfo, COL_PROTOCOL, "CDP");
162     if (check_col(pinfo->cinfo, COL_INFO))
163         col_clear(pinfo->cinfo, COL_INFO);
164
165     if (tree){
166         ti = proto_tree_add_item(tree, proto_cdp, tvb, offset, -1, FALSE);
167         cdp_tree = proto_item_add_subtree(ti, ett_cdp);
168
169         /* CDP header */
170         proto_tree_add_item(cdp_tree, hf_cdp_version, tvb, offset, 1, FALSE);
171         offset += 1;
172
173         proto_tree_add_uint_format_value(cdp_tree, hf_cdp_ttl, tvb, offset, 1,
174                                          tvb_get_guint8(tvb, offset),
175                                          "%u seconds",
176                                          tvb_get_guint8(tvb, offset));
177         offset += 1;
178     } else {
179         offset += 2; /* The version/ttl fields from above */
180     }
181
182     /* Checksum display & verification code */
183     packet_checksum = tvb_get_ntohs(tvb, offset);
184     
185     data_length = tvb_reported_length(tvb);
186     
187     cksum_vec[0].ptr = tvb_get_ptr(tvb, 0, data_length);
188     cksum_vec[0].len = data_length;
189     
190     computed_checksum = in_cksum(cksum_vec, 1);
191     checksum_good = (computed_checksum == 0);
192     checksum_bad = !checksum_good;
193     if (checksum_good) {
194         checksum_item = proto_tree_add_uint_format(cdp_tree,
195         hf_cdp_checksum, tvb, offset, 2, packet_checksum,
196         "Checksum: 0x%04x [correct]", packet_checksum);
197     } else {
198         checksum_item = proto_tree_add_uint_format(cdp_tree,
199          hf_cdp_checksum, tvb, offset, 2, packet_checksum,
200          "Checksum: 0x%04x [incorrect, should be 0x%04x]",
201          packet_checksum,
202          in_cksum_shouldbe(packet_checksum, computed_checksum));
203     }
204     
205     checksum_tree = proto_item_add_subtree(checksum_item, ett_cdp_checksum);
206     checksum_item = proto_tree_add_boolean(checksum_tree, hf_cdp_checksum_good,
207                                            tvb, offset, 2, checksum_good);
208     PROTO_ITEM_SET_GENERATED(checksum_item);
209     checksum_item = proto_tree_add_boolean(checksum_tree, hf_cdp_checksum_bad,
210                                            tvb, offset, 2, checksum_bad);
211     PROTO_ITEM_SET_GENERATED(checksum_item);
212     
213     offset += 2;
214     
215         while (tvb_reported_length_remaining(tvb, offset) != 0) {
216             type = tvb_get_ntohs(tvb, offset + TLV_TYPE);
217             length = tvb_get_ntohs(tvb, offset + TLV_LENGTH);
218             if (length < 4) {
219                     if(tree) {
220                     tlvi = proto_tree_add_text(cdp_tree, tvb, offset, 4,
221                         "TLV with invalid length %u (< 4)",
222                         length);
223                     tlv_tree = proto_item_add_subtree(tlvi, ett_cdp_tlv);
224                     proto_tree_add_uint(tlv_tree, hf_cdp_tlvtype, tvb,
225                                 offset + TLV_TYPE, 2, type);
226                     proto_tree_add_uint(tlv_tree, hf_cdp_tlvlength, tvb,
227                                 offset + TLV_LENGTH, 2, length);
228                     }
229                     offset += 4;
230                     break;
231             }
232
233             switch (type) {
234
235             case TYPE_DEVICE_ID:
236                 /* Device ID */
237
238                 if(check_col(pinfo->cinfo, COL_INFO))
239                     col_append_fstr(pinfo->cinfo, COL_INFO,
240                                     "Device ID: %s  ",
241                                     tvb_format_stringzpad(tvb, offset + 4,
242                                                           length - 4));
243
244                 if(tree){
245                     tlvi = proto_tree_add_text(cdp_tree, tvb, offset,
246                                 length, "Device ID: %s",
247                                 tvb_format_stringzpad(tvb, offset + 4, length - 4));
248                     tlv_tree = proto_item_add_subtree(tlvi, ett_cdp_tlv);
249                     proto_tree_add_uint(tlv_tree, hf_cdp_tlvtype, tvb,
250                                 offset + TLV_TYPE, 2, type);
251                     proto_tree_add_uint(tlv_tree, hf_cdp_tlvlength, tvb,
252                                 offset + TLV_LENGTH, 2, length);
253                     proto_tree_add_text(tlv_tree, tvb, offset + 4,
254                                 length - 4, "Device ID: %s",
255                                 tvb_format_stringzpad(tvb, offset + 4, length - 4));
256                     }
257                     offset += length;
258                     break;
259
260             case TYPE_PORT_ID:
261                 real_length = length;
262                 if (tvb_get_guint8(tvb, offset + real_length) != 0x00) {
263                     /* The length in the TLV doesn't appear to be the
264                        length of the TLV, as the byte just past it
265                        isn't the first byte of a 2-byte big-endian
266                        small integer; make the length of the TLV the length
267                        in the TLV, plus 4 bytes for the TLV type and length,
268                        minus 1 because that's what makes one capture work. */
269                     real_length = length + 3;
270                 }
271
272                 if(check_col(pinfo->cinfo, COL_INFO))
273                     col_append_fstr(pinfo->cinfo, COL_INFO,
274                                     "Port ID: %s  ",
275                                     tvb_format_stringzpad(tvb, offset + 4,
276                                                           length - 4));
277
278                 if(tree){ 
279                 tlvi = proto_tree_add_text(cdp_tree, tvb, offset,
280                             real_length, "Port ID: %s",
281                             tvb_format_text(tvb, offset + 4, real_length - 4));
282                 tlv_tree = proto_item_add_subtree(tlvi, ett_cdp_tlv);
283                 proto_tree_add_uint(tlv_tree, hf_cdp_tlvtype, tvb,
284                             offset + TLV_TYPE, 2, type);
285                 proto_tree_add_uint(tlv_tree, hf_cdp_tlvlength, tvb,
286                             offset + TLV_LENGTH, 2, length);
287                 proto_tree_add_text(tlv_tree, tvb, offset + 4,
288                             real_length - 4,
289                             "Sent through Interface: %s",
290                             tvb_format_text(tvb, offset + 4, real_length - 4));
291                 }
292                 offset += real_length;
293                 break;
294
295             if(tree){
296                 case TYPE_ADDRESS:
297                     /* Addresses */
298                     tlvi = proto_tree_add_text(cdp_tree, tvb, offset,
299                                 length, "Addresses");
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                     offset += 4;
306                     length -= 4;
307                     naddresses = tvb_get_ntohl(tvb, offset);
308                     proto_tree_add_text(tlv_tree, tvb, offset, 4,
309                                 "Number of addresses: %u", naddresses);
310                     offset += 4;
311                     length -= 4;
312                     while (naddresses != 0) {
313                         addr_length = dissect_address_tlv(tvb, offset, length,
314                                     tlv_tree);
315                         if (addr_length < 0)
316                             break;
317                         offset += addr_length;
318                         length -= addr_length;
319     
320                         naddresses--;
321                     }
322                     offset += length;
323                     break;
324
325                 case TYPE_CAPABILITIES:
326                     tlvi = proto_tree_add_text(cdp_tree, tvb, offset,
327                                 length, "Capabilities");
328                     tlv_tree = proto_item_add_subtree(tlvi, ett_cdp_tlv);
329                     proto_tree_add_uint(tlv_tree, hf_cdp_tlvtype, tvb,
330                                 offset + TLV_TYPE, 2, type);
331                     proto_tree_add_uint(tlv_tree, hf_cdp_tlvlength, tvb,
332                                 offset + TLV_LENGTH, 2, length);
333                     offset += 4;
334                     length -= 4;
335                     dissect_capabilities(tvb, offset, length, tlv_tree);
336                     offset += length;
337                     break;
338
339                 case TYPE_IOS_VERSION:
340                     tlvi = proto_tree_add_text(cdp_tree, tvb, offset,
341                                 length, "Software Version");
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                     add_multi_line_string_to_tree(tlv_tree, tvb, offset + 4,
348                                     length - 4, "Software Version: ");
349                     offset += length;
350                     break;
351
352                 case TYPE_PLATFORM:
353                     /* ??? platform */
354                     tlvi = proto_tree_add_text(cdp_tree, tvb,
355                                 offset, length, "Platform: %s",
356                                 tvb_format_text(tvb, offset + 4, length - 4));
357                     tlv_tree = proto_item_add_subtree(tlvi, ett_cdp_tlv);
358                     proto_tree_add_uint(tlv_tree, hf_cdp_tlvtype, tvb,
359                                 offset + TLV_TYPE, 2, type);
360                     proto_tree_add_uint(tlv_tree, hf_cdp_tlvlength, tvb,
361                                 offset + TLV_LENGTH, 2, length);
362                     proto_tree_add_text(tlv_tree, tvb, offset + 4,
363                                 length - 4, "Platform: %s",
364                                 tvb_format_text(tvb, offset + 4, length - 4));
365                     offset += length;
366                     break;
367
368                 case TYPE_IP_PREFIX:
369                     if (length == 8) {
370                         /* if length is 8 then this is default gw not prefix */
371                         tlvi = proto_tree_add_text(cdp_tree, tvb, offset,
372                                 length, "ODR Default gateway: %s",
373                                 ip_to_str(tvb_get_ptr(tvb, offset+4, 4)));
374                         tlv_tree = proto_item_add_subtree(tlvi, ett_cdp_tlv);
375                         proto_tree_add_uint(tlv_tree, hf_cdp_tlvtype, tvb,
376                                 offset + TLV_TYPE, 2, type);
377                         proto_tree_add_uint(tlv_tree, hf_cdp_tlvlength, tvb,
378                                 offset + TLV_LENGTH, 2, length);
379                         proto_tree_add_text(tlv_tree, tvb, offset+4, 4,
380                                     "ODR Default gateway = %s",
381                                     ip_to_str(tvb_get_ptr(tvb, offset+4, 4)));
382                         offset += 8;
383                     } else {  
384                         tlvi = proto_tree_add_text(cdp_tree, tvb, offset,
385                                 length, "IP Prefixes: %d",length/5);
386
387                         /* the actual number of prefixes is (length-4)/5
388                         but if the variable is not a "float" but "integer"
389                         then length/5=(length-4)/5  :)  */
390
391                         tlv_tree = proto_item_add_subtree(tlvi, ett_cdp_tlv);
392                         proto_tree_add_uint(tlv_tree, hf_cdp_tlvtype, tvb,
393                                 offset + TLV_TYPE, 2, type);
394                         proto_tree_add_uint(tlv_tree, hf_cdp_tlvlength, tvb,
395                                 offset + TLV_LENGTH, 2, length);
396                         offset += 4;
397                         length -= 4;
398                         while (length > 0) {
399                             proto_tree_add_text(tlv_tree, tvb, offset, 5,
400                                     "IP Prefix = %s/%u",
401                                     ip_to_str(tvb_get_ptr(tvb, offset, 4)),
402                                     tvb_get_guint8(tvb,offset+4));
403                             offset += 5;
404                             length -= 5;
405                         }
406                     }
407                     break;
408
409                 case TYPE_PROTOCOL_HELLO:
410                   tlvi = proto_tree_add_text(cdp_tree, tvb,
411                                              offset,length, "Protocol Hello: %s",
412                                              val_to_str(tvb_get_ntohs(tvb, offset+7), type_hello_vals, "Unknown (0x%04x)"));
413                   tlv_tree = proto_item_add_subtree(tlvi, ett_cdp_tlv);
414                   proto_tree_add_uint(tlv_tree, hf_cdp_tlvtype, tvb,
415                                       offset + TLV_TYPE, 2, type);
416                   proto_tree_add_uint(tlv_tree, hf_cdp_tlvlength, tvb,
417                                       offset + TLV_LENGTH, 2, length);
418                   proto_tree_add_text(tlv_tree, tvb, offset+4, 3,
419                                       "OUI: 0x%06X (%s)",
420                                       tvb_get_ntoh24(tvb,offset+4),
421                                       val_to_str(tvb_get_ntoh24(tvb,offset+4), oui_vals, "Unknown"));
422                   proto_tree_add_text(tlv_tree, tvb, offset+7, 2,
423                                       "Protocol ID: 0x%04X (%s)",
424                                       tvb_get_ntohs(tvb, offset+7),
425                                       val_to_str(tvb_get_ntohs(tvb, offset+7), type_hello_vals, "Unknown"));
426                   switch(tvb_get_ntohs(tvb, offset+7)) {
427
428                   case TYPE_HELLO_CLUSTER_MGMT:
429                     /*          proto_tree_add_text(tlv_tree, tvb, offset+9,
430                                         length - 9, "Cluster Management");
431                     */
432                     ip_addr = tvb_get_ipv4(tvb, offset+9);
433                     proto_tree_add_text(tlv_tree, tvb, offset+9, 4,
434                                         "Cluster Master IP: %s",ip_to_str((guint8 *)&ip_addr));
435                     ip_addr = tvb_get_ipv4(tvb, offset+13);
436                     proto_tree_add_text(tlv_tree, tvb, offset+13, 4,
437                                         "UNKNOWN (IP?): 0x%08X (%s)",
438                                           ip_addr, ip_to_str((guint8 *)&ip_addr));
439                     proto_tree_add_text(tlv_tree, tvb, offset+17, 1,
440                                         "Version?: 0x%02X",
441                                         tvb_get_guint8(tvb, offset+17));
442                     proto_tree_add_text(tlv_tree, tvb, offset+18, 1,
443                                         "Sub Version?: 0x%02X",
444                                         tvb_get_guint8(tvb, offset+18));
445                     proto_tree_add_text(tlv_tree, tvb, offset+19, 1,
446                                         "Status?: 0x%02X",
447                                         tvb_get_guint8(tvb, offset+19));
448                     proto_tree_add_text(tlv_tree, tvb, offset+20, 1,
449                                         "UNKNOWN: 0x%02X",
450                                         tvb_get_guint8(tvb, offset+20));
451                     proto_tree_add_text(tlv_tree, tvb, offset+21, 6,
452                                         "Cluster Commander MAC: %s",
453                                         ether_to_str(tvb_get_ptr(tvb, offset+21, 6)));
454                     proto_tree_add_text(tlv_tree, tvb, offset+27, 6,
455                                         "Switch's MAC: %s",
456                                         ether_to_str(tvb_get_ptr(tvb, offset+27, 6)));
457                     proto_tree_add_text(tlv_tree, tvb, offset+33, 1,
458                                         "UNKNOWN: 0x%02X",
459                                         tvb_get_guint8(tvb, offset+33));
460                     proto_tree_add_text(tlv_tree, tvb, offset+34, 2,
461                                         "Management VLAN: %d",
462                                         tvb_get_ntohs(tvb, offset+34));
463                     break;
464                   default:
465                     proto_tree_add_text(tlv_tree, tvb, offset + 9,
466                                         length - 9, "Unknown");
467                     break;
468                   }
469                   offset += length;
470                   break;
471
472                  case TYPE_VTP_MGMT_DOMAIN:
473                     tlvi = proto_tree_add_text(cdp_tree, tvb,
474                                 offset, length, "VTP Management Domain: %s",
475                                 tvb_format_text(tvb, offset + 4, length - 4));
476                     tlv_tree = proto_item_add_subtree(tlvi, ett_cdp_tlv);
477                     proto_tree_add_uint(tlv_tree, hf_cdp_tlvtype, tvb,
478                                 offset + TLV_TYPE, 2, type);
479                     proto_tree_add_uint(tlv_tree, hf_cdp_tlvlength, tvb,
480                                 offset + TLV_LENGTH, 2, length);
481                     proto_tree_add_text(tlv_tree, tvb, offset + 4,
482                                 length - 4, "VTP Management Domain: %s",
483                                 tvb_format_text(tvb, offset + 4, length - 4));
484                     offset += length;
485                     break;
486
487                 case TYPE_NATIVE_VLAN:
488                     tlvi = proto_tree_add_text(cdp_tree, tvb,
489                                 offset, length, "Native VLAN: %u",
490                                                tvb_get_ntohs(tvb, offset + 4));
491                     tlv_tree = proto_item_add_subtree(tlvi, ett_cdp_tlv);
492                     proto_tree_add_uint(tlv_tree, hf_cdp_tlvtype, tvb,
493                                 offset + TLV_TYPE, 2, type);
494                     proto_tree_add_uint(tlv_tree, hf_cdp_tlvlength, tvb,
495                                 offset + TLV_LENGTH, 2, length);
496                     proto_tree_add_text(tlv_tree, tvb, offset + 4,
497                                 length - 4, "Native VLAN: %u",
498                                         tvb_get_ntohs(tvb, offset + 4));
499                     offset += length;
500                     break;
501
502                 case TYPE_DUPLEX:
503                     tlvi = proto_tree_add_text(cdp_tree, tvb,
504                                 offset, length, "Duplex: %s",
505                                                tvb_get_guint8(tvb, offset + 4) ?
506                                                "Full" : "Half" );
507                     tlv_tree = proto_item_add_subtree(tlvi, ett_cdp_tlv);
508                     proto_tree_add_uint(tlv_tree, hf_cdp_tlvtype, tvb,
509                                 offset + TLV_TYPE, 2, type);
510                     proto_tree_add_uint(tlv_tree, hf_cdp_tlvlength, tvb,
511                                 offset + TLV_LENGTH, 2, length);
512                     proto_tree_add_text(tlv_tree, tvb, offset + 4,
513                                 length - 4, "Duplex: %s",
514                                         tvb_get_guint8(tvb, offset + 4) ?
515                                         "Full" : "Half" );
516                     offset += length;
517                     break;
518
519                 case TYPE_VOIP_VLAN_REPLY:
520                   if (length >= 7) {
521                     tlvi = proto_tree_add_text(cdp_tree, tvb,
522                                                offset, length, "VoIP VLAN Reply: %u", tvb_get_ntohs(tvb, offset + 5));
523                   } else {
524                     /*
525                      * XXX - what are these?  I've seen them in some captures;
526                      * they have a length of 6, and run up to the end of
527                      * the packet, so if we try to dissect it the same way
528                      * we dissect the 7-byte ones, we report a malformed
529                      * frame.
530                      */
531                     tlvi = proto_tree_add_text(cdp_tree, tvb,
532                                                offset, length, "VoIP VLAN Reply");
533                   }
534                   tlv_tree = proto_item_add_subtree(tlvi, ett_cdp_tlv);
535                   proto_tree_add_uint(tlv_tree, hf_cdp_tlvtype, tvb,
536                                       offset + TLV_TYPE, 2, type);
537                   proto_tree_add_uint(tlv_tree, hf_cdp_tlvlength, tvb,
538                                       offset + TLV_LENGTH, 2, length);
539                   proto_tree_add_text(tlv_tree, tvb, offset + 4,
540                                       1, "Data");
541                   if (length >= 7) {
542                     proto_tree_add_text(tlv_tree, tvb, offset + 5,
543                                         2, "Voice VLAN: %u",
544                                         tvb_get_ntohs(tvb, offset + 5));
545                   }
546                   offset += length;
547                   break;
548
549                 case TYPE_VOIP_VLAN_QUERY:
550                   if (length >= 7) {
551                         tlvi = proto_tree_add_text(cdp_tree, tvb,
552                                                    offset, length, "VoIP VLAN Query: %u", tvb_get_ntohs(tvb, offset + 5));
553                   } else {
554                     /*
555                      * XXX - what are these?  I've seen them in some captures;
556                      * they have a length of 6, and run up to the end of
557                      * the packet, so if we try to dissect it the same way
558                      * we dissect the 7-byte ones, we report a malformed
559                      * frame.
560                      */
561                     tlvi = proto_tree_add_text(cdp_tree, tvb,
562                                                offset, length, "VoIP VLAN Query");
563                   }
564                   tlv_tree = proto_item_add_subtree(tlvi, ett_cdp_tlv);
565                   proto_tree_add_uint(tlv_tree, hf_cdp_tlvtype, tvb,
566                                       offset + TLV_TYPE, 2, type);
567                   proto_tree_add_uint(tlv_tree, hf_cdp_tlvlength, tvb,
568                                       offset + TLV_LENGTH, 2, length);
569                   proto_tree_add_text(tlv_tree, tvb, offset + 4,
570                                       1, "Data");
571                   if (length >= 7) {
572                     proto_tree_add_text(tlv_tree, tvb, offset + 5,
573                                         2, "Voice VLAN: %u",
574                                         tvb_get_ntohs(tvb, offset + 5));
575                   }
576                   offset += length;
577                   break;
578
579                 case TYPE_POWER:
580                     tlvi = proto_tree_add_text(cdp_tree, tvb,
581                                 offset, length, "Power consumption: %u mW",
582                                                tvb_get_ntohs(tvb, offset + 4));
583                     tlv_tree = proto_item_add_subtree(tlvi, ett_cdp_tlv);
584                     proto_tree_add_uint(tlv_tree, hf_cdp_tlvtype, tvb,
585                                 offset + TLV_TYPE, 2, type);
586                     proto_tree_add_uint(tlv_tree, hf_cdp_tlvlength, tvb,
587                                 offset + TLV_LENGTH, 2, length);
588                     proto_tree_add_text(tlv_tree, tvb, offset + 4,
589                                 length - 4, "Power consumption: %u mW",
590                                         tvb_get_ntohs(tvb, offset + 4));
591                     offset += length;
592                     break;
593
594                 case TYPE_MTU:
595                   tlvi = proto_tree_add_text(cdp_tree, tvb,
596                                              offset, length, "MTU: %u",
597                                              tvb_get_ntohl(tvb,offset + 4));
598                   tlv_tree = proto_item_add_subtree(tlvi, ett_cdp_tlv);
599                   proto_tree_add_uint(tlv_tree, hf_cdp_tlvtype, tvb,
600                                       offset + TLV_TYPE, 2, type);
601                   proto_tree_add_uint(tlv_tree, hf_cdp_tlvlength, tvb,
602                                       offset + TLV_LENGTH, 2, length);
603                   proto_tree_add_text(tlv_tree, tvb, offset + 4,
604                                       length - 4, "MTU: %u",
605                                       tvb_get_ntohl(tvb,offset + 4));
606                   offset += length;
607                   break;
608
609                 case TYPE_TRUST_BITMAP:
610                   tlvi = proto_tree_add_text(cdp_tree, tvb,
611                                              offset, length, "Trust Bitmap: 0x%02X",
612                                              tvb_get_guint8(tvb, offset + 4));
613                   tlv_tree = proto_item_add_subtree(tlvi, ett_cdp_tlv);
614                   proto_tree_add_uint(tlv_tree, hf_cdp_tlvtype, tvb,
615                                       offset + TLV_TYPE, 2, type);
616                   proto_tree_add_uint(tlv_tree, hf_cdp_tlvlength, tvb,
617                                       offset + TLV_LENGTH, 2, length);
618                   proto_tree_add_text(tlv_tree, tvb, offset + 4,
619                                       length - 4, "Trust Bitmap: %02x",
620                                       tvb_get_guint8(tvb, offset + 4));
621                   offset += length;
622                   break;
623
624                 case TYPE_UNTRUSTED_COS:
625                   tlvi = proto_tree_add_text(cdp_tree, tvb,
626                                              offset, length, "Untrusted port CoS: 0x%02X",
627                                              tvb_get_guint8(tvb, offset + 4));
628                   tlv_tree = proto_item_add_subtree(tlvi, ett_cdp_tlv);
629                   proto_tree_add_uint(tlv_tree, hf_cdp_tlvtype, tvb,
630                                       offset + TLV_TYPE, 2, type);
631                   proto_tree_add_uint(tlv_tree, hf_cdp_tlvlength, tvb,
632                                       offset + TLV_LENGTH, 2, length);
633                   proto_tree_add_text(tlv_tree, tvb, offset + 4,
634                                       length - 4, "Untrusted port CoS: %02x",
635                                       tvb_get_guint8(tvb, offset + 4));
636                   offset += length;
637                   break;
638
639                 case TYPE_SYSTEM_NAME:
640                   tlvi = proto_tree_add_text(cdp_tree, tvb,
641                                              offset, length, "System Name: %s",
642                                              tvb_format_text(tvb, offset + 4, length - 4));
643                   tlv_tree = proto_item_add_subtree(tlvi, ett_cdp_tlv);
644                   proto_tree_add_uint(tlv_tree, hf_cdp_tlvtype, tvb,
645                                       offset + TLV_TYPE, 2, type);
646                   proto_tree_add_uint(tlv_tree, hf_cdp_tlvlength, tvb,
647                                       offset + TLV_LENGTH, 2, length);
648                   proto_tree_add_text(tlv_tree, tvb, offset + 4,
649                                       length - 4, "System Name: %s",
650                                       tvb_format_text(tvb, offset + 4, length - 4));
651                   offset += length;
652                   break;
653
654                 case TYPE_SYSTEM_OID:
655                   tlvi = proto_tree_add_text(cdp_tree, tvb,
656                                              offset, length, "System Object Identifier");
657                   tlv_tree = proto_item_add_subtree(tlvi, ett_cdp_tlv);
658                   proto_tree_add_uint(tlv_tree, hf_cdp_tlvtype, tvb,
659                                       offset + TLV_TYPE, 2, type);
660                   proto_tree_add_uint(tlv_tree, hf_cdp_tlvlength, tvb,
661                                       offset + TLV_LENGTH, 2, length);
662                   proto_tree_add_text(tlv_tree, tvb, offset + 4,
663                                       length - 4, "System Object Identifier: %s",
664                                       tvb_bytes_to_str(tvb, offset + 4, length - 4));
665                   offset += length;
666                   break;
667
668                 case TYPE_MANAGEMENT_ADDR:
669                   tlvi = proto_tree_add_text(cdp_tree, tvb,
670                                              offset, length, "Management Addresses");
671                   tlv_tree = proto_item_add_subtree(tlvi, ett_cdp_tlv);
672                   proto_tree_add_uint(tlv_tree, hf_cdp_tlvtype, tvb,
673                                       offset + TLV_TYPE, 2, type);
674                   proto_tree_add_uint(tlv_tree, hf_cdp_tlvlength, tvb,
675                                       offset + TLV_LENGTH, 2, length);
676                   offset += 4;
677                   length -= 4;
678                   naddresses = tvb_get_ntohl(tvb, offset);
679                   proto_tree_add_text(tlv_tree, tvb, offset, 4,
680                                       "Number of addresses: %u", naddresses);
681                   offset += 4;
682                   length -= 4;
683                   while (naddresses != 0) {
684                     addr_length = dissect_address_tlv(tvb, offset, length,
685                                                       tlv_tree);
686                     if (addr_length < 0)
687                       break;
688                     offset += addr_length;
689                     length -= addr_length;
690
691                     naddresses--;
692                   }
693                   offset += length;
694                   break;
695
696                 case TYPE_LOCATION:
697                   tlvi = proto_tree_add_text(cdp_tree, tvb,
698                                              offset, length, "Location: %s",
699                                              tvb_format_text(tvb, offset + 5, length - 5));
700                   tlv_tree = proto_item_add_subtree(tlvi, ett_cdp_tlv);
701                   proto_tree_add_uint(tlv_tree, hf_cdp_tlvtype, tvb,
702                                       offset + TLV_TYPE, 2, type);
703                   proto_tree_add_uint(tlv_tree, hf_cdp_tlvlength, tvb,
704                                       offset + TLV_LENGTH, 2, length);
705                   proto_tree_add_text(tlv_tree, tvb, offset + 4,
706                                       1 , "UNKNOWN: 0x%02X",
707                                       tvb_get_guint8(tvb, offset + 4));
708                   proto_tree_add_text(tlv_tree, tvb, offset + 5,
709                                       length - 5, "Location: %s",
710                                       tvb_format_text(tvb, offset + 5, length - 5));
711                   offset += length;
712                   break;
713                   }
714
715                 default:
716                   tlvi = proto_tree_add_text(cdp_tree, tvb, offset,
717                                 length, "Type: %s, length: %u",
718                                 val_to_str(type, type_vals, "Unknown (0x%04x)"),
719                                 length);
720                     tlv_tree = proto_item_add_subtree(tlvi, ett_cdp_tlv);
721                     proto_tree_add_uint(tlv_tree, hf_cdp_tlvtype, tvb,
722                                 offset + TLV_TYPE, 2, type);
723                     proto_tree_add_uint(tlv_tree, hf_cdp_tlvlength, tvb,
724                                 offset + TLV_LENGTH, 2, length);
725                     if (length > 4) {
726                             proto_tree_add_text(tlv_tree, tvb, offset + 4,
727                                             length - 4, "Data");
728                     } else
729                             return;
730                     offset += length;
731             }
732         }
733     call_dissector(data_handle, tvb_new_subset(tvb, offset, -1, -1), pinfo,
734                         cdp_tree);
735 }
736
737 #define PROTO_TYPE_NLPID        1
738 #define PROTO_TYPE_IEEE_802_2   2
739
740 static const value_string proto_type_vals[] = {
741         { PROTO_TYPE_NLPID,      "NLPID" },
742         { PROTO_TYPE_IEEE_802_2, "802.2" },
743         { 0,                     NULL }
744 };
745
746 static int
747 dissect_address_tlv(tvbuff_t *tvb, int offset, int length, proto_tree *tree)
748 {
749     proto_item *ti;
750     proto_tree *address_tree;
751     guint8 protocol_type;
752     guint8 protocol_length;
753     int nlpid;
754     const char *protocol_str;
755     guint16 address_length;
756     const char *address_type_str;
757     char *address_str;
758
759     if (length < 1)
760         return -1;
761     ti = proto_tree_add_text(tree, tvb, offset, length, "Truncated address");
762     address_tree = proto_item_add_subtree(ti, ett_cdp_address);
763     protocol_type = tvb_get_guint8(tvb, offset);
764     proto_tree_add_text(address_tree, tvb, offset, 1, "Protocol type: %s",
765         val_to_str(protocol_type, proto_type_vals, "Unknown (0x%02x)"));
766     offset += 1;
767     length -= 1;
768
769     if (length < 1)
770         return -1;
771     protocol_length = tvb_get_guint8(tvb, offset);
772     proto_tree_add_text(address_tree, tvb, offset, 1, "Protocol length: %u",
773                         protocol_length);
774     offset += 1;
775     length -= 1;
776
777     if (length < protocol_length) {
778         if (length != 0) {
779             proto_tree_add_text(address_tree, tvb, offset, length,
780               "Protocol: %s (truncated)",
781               tvb_bytes_to_str(tvb, offset, length));
782         }
783         return -1;
784     }
785     protocol_str = NULL;
786     if (protocol_type == PROTO_TYPE_NLPID && protocol_length == 1) {
787         nlpid = tvb_get_guint8(tvb, offset);
788         protocol_str = val_to_str(nlpid, nlpid_vals, "Unknown (0x%02x)");
789     } else
790         nlpid = -1;
791     if (protocol_str == NULL)
792         protocol_str = tvb_bytes_to_str(tvb, offset, protocol_length);
793     proto_tree_add_text(address_tree, tvb, offset, protocol_length,
794                         "Protocol: %s", protocol_str);
795     offset += protocol_length;
796     length -= protocol_length;
797
798     if (length < 2)
799         return -1;
800     address_length = tvb_get_ntohs(tvb, offset);
801     proto_tree_add_text(address_tree, tvb, offset, 2, "Address length: %u",
802                         address_length);
803     offset += 2;
804     length -= 2;
805
806     if (length < address_length) {
807         if (length != 0) {
808             proto_tree_add_text(address_tree, tvb, offset, length,
809               "Address: %s (truncated)",
810               tvb_bytes_to_str(tvb, offset, length));
811         }
812         return -1;
813     }
814     /* XXX - the Cisco document seems to be saying that, for 802.2-format
815        protocol types, 0xAAAA03 0x000000 0x0800 is IPv6, but 0x0800 is
816        the Ethernet protocol type for IPv4. */
817     length = 2 + protocol_length + 2 + address_length;
818     address_type_str = NULL;
819     address_str = NULL;
820     if (protocol_type == PROTO_TYPE_NLPID && protocol_length == 1) {
821         switch (nlpid) {
822
823         /* XXX - dissect NLPID_ISO8473_CLNP as OSI CLNP address? */
824
825         case NLPID_IP:
826             if (address_length == 4) {
827                 /* The address is an IP address. */
828                 address_type_str = "IP address";
829                 address_str = ip_to_str(tvb_get_ptr(tvb, offset, 4));
830             }
831             break;
832         }
833     }
834     if (address_type_str == NULL)
835         address_type_str = "Address";
836     if (address_str == NULL) {
837         address_str = tvb_bytes_to_str(tvb, offset, address_length);
838     }
839     proto_item_set_text(ti, "%s: %s", address_type_str, address_str);
840     proto_tree_add_text(address_tree, tvb, offset, address_length, "%s: %s",
841       address_type_str, address_str);
842     return 2 + protocol_length + 2 + address_length;
843 }
844
845 static void
846 dissect_capabilities(tvbuff_t *tvb, int offset, int length, proto_tree *tree)
847 {
848     proto_item *ti;
849     proto_tree *capabilities_tree;
850     guint32 capabilities;
851
852     if (length < 4)
853         return;
854     capabilities = tvb_get_ntohl(tvb, offset);
855     ti = proto_tree_add_text(tree, tvb, offset, length, "Capabilities: 0x%08x",
856         capabilities);
857     capabilities_tree = proto_item_add_subtree(ti, ett_cdp_capabilities);
858     proto_tree_add_text(capabilities_tree, tvb, offset, 4,
859         decode_boolean_bitfield(capabilities, 0x01, 4*8,
860             "Is  a Router",
861             "Not a Router"));
862     proto_tree_add_text(capabilities_tree, tvb, offset, 4,
863         decode_boolean_bitfield(capabilities, 0x02, 4*8,
864             "Is  a Transparent Bridge",
865             "Not a Transparent Bridge"));
866     proto_tree_add_text(capabilities_tree, tvb, offset, 4,
867         decode_boolean_bitfield(capabilities, 0x04, 4*8,
868             "Is  a Source Route Bridge",
869             "Not a Source Route Bridge"));
870     proto_tree_add_text(capabilities_tree, tvb, offset, 4,
871         decode_boolean_bitfield(capabilities, 0x08, 4*8,
872             "Is  a Switch",
873             "Not a Switch"));
874     proto_tree_add_text(capabilities_tree, tvb, offset, 4,
875         decode_boolean_bitfield(capabilities, 0x10, 4*8,
876             "Is  a Host",
877             "Not a Host"));
878     proto_tree_add_text(capabilities_tree, tvb, offset, 4,
879         decode_boolean_bitfield(capabilities, 0x20, 4*8,
880             "Is  IGMP capable",
881             "Not IGMP capable"));
882     proto_tree_add_text(capabilities_tree, tvb, offset, 4,
883         decode_boolean_bitfield(capabilities, 0x40, 4*8,
884             "Is  a Repeater",
885             "Not a Repeater"));
886 }
887
888 static void
889 add_multi_line_string_to_tree(proto_tree *tree, tvbuff_t *tvb, gint start,
890   gint len, const gchar *prefix)
891 {
892     int prefix_len;
893     int i;
894     char blanks[64+1];
895     gint next;
896     int line_len;
897     int data_len;
898
899     prefix_len = strlen(prefix);
900     if (prefix_len > 64)
901         prefix_len = 64;
902     for (i = 0; i < prefix_len; i++)
903         blanks[i] = ' ';
904     blanks[i] = '\0';
905     while (len > 0) {
906         line_len = tvb_find_line_end(tvb, start, len, &next, FALSE);
907         data_len = next - start;
908         proto_tree_add_text(tree, tvb, start, data_len, "%s%s", prefix,
909            tvb_format_stringzpad(tvb, start, line_len));
910         start += data_len;
911         len -= data_len;
912         prefix = blanks;
913     }
914 }
915
916 void
917 proto_register_cdp(void)
918 {
919     static hf_register_info hf[] = {
920         { &hf_cdp_version,
921         { "Version",            "cdp.version",  FT_UINT8, BASE_DEC, NULL, 0x0,
922           "", HFILL }},
923
924         { &hf_cdp_ttl,
925         { "TTL",                "cdp.ttl", FT_UINT16, BASE_DEC, NULL, 0x0,
926           "", HFILL }},
927
928         { &hf_cdp_checksum,
929         { "Checksum",           "cdp.checksum", FT_UINT16, BASE_HEX, NULL, 0x0,
930           "", HFILL }},
931
932         { &hf_cdp_checksum_good,
933           { "Good",       "cdp.checksum_good", FT_BOOLEAN, BASE_NONE, NULL, 0x0,
934             "True: checksum matches packet content; False: doesn't match content or not checked", HFILL }},
935         
936         { &hf_cdp_checksum_bad,
937           { "Bad ",       "cdp.checksum_bad", FT_BOOLEAN, BASE_NONE, NULL, 0x0,
938             "True: checksum doesn't match packet content; False: matches content or not checked", HFILL }},
939         
940         { &hf_cdp_tlvtype,
941         { "Type",               "cdp.tlv.type", FT_UINT16, BASE_HEX, VALS(type_vals), 0x0,
942           "", HFILL }},
943
944         { &hf_cdp_tlvlength,
945         { "Length",             "cdp.tlv.len", FT_UINT16, BASE_DEC, NULL, 0x0,
946           "", HFILL }}
947     };
948     static gint *ett[] = {
949         &ett_cdp,
950         &ett_cdp_tlv,
951         &ett_cdp_address,
952         &ett_cdp_capabilities,
953         &ett_cdp_checksum
954     };
955
956     proto_cdp = proto_register_protocol("Cisco Discovery Protocol",
957                                         "CDP", "cdp");
958     proto_register_field_array(proto_cdp, hf, array_length(hf));
959     proto_register_subtree_array(ett, array_length(ett));
960 }
961
962 void
963 proto_reg_handoff_cdp(void)
964 {
965     dissector_handle_t cdp_handle;
966
967     data_handle = find_dissector("data");
968     cdp_handle = create_dissector_handle(dissect_cdp, proto_cdp);
969     dissector_add("llc.cisco_pid", 0x2000, cdp_handle);
970     dissector_add("chdlctype", 0x2000, cdp_handle);
971     dissector_add("ppp.protocol", 0x0207, cdp_handle);
972 }