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