Beginnings of README.macos
[obnox/wireshark/wip.git] / packet-cdp.c
1 /* packet-cdp.c
2  * Routines for the disassembly of the "Cisco Discovery Protocol"
3  * (c) Copyright Hannes R. Boehm <hannes@boehm.org>
4  *
5  * $Id: packet-cdp.c,v 1.52 2004/03/13 09:35:41 guy Exp $
6  *
7  * Ethereal - Network traffic analyzer
8  * By Gerald Combs <gerald@ethereal.com>
9  * Copyright 1998 Gerald Combs
10  *
11  * This program is free software; you can redistribute it and/or
12  * modify it under the terms of the GNU General Public License
13  * as published by the Free Software Foundation; either version 2
14  * of the License, or (at your option) any later version.
15  *
16  * This program is distributed in the hope that it will be useful,
17  * but WITHOUT ANY WARRANTY; without even the implied warranty of
18  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
19  * GNU General Public License for more details.
20  *
21  * You should have received a copy of the GNU General Public License
22  * along with this program; if not, write to the Free Software
23  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
24  */
25
26 #include "config.h"
27
28 #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 "oui.h"
36 #include "nlpid.h"
37
38
39 /*
40  * See
41  *
42  *      http://www.cisco.com/univercd/cc/td/doc/product/lan/trsrb/frames.htm
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
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 /*                                     Somewhere in here there's a Power Draw TLV */
91 /*                              0x000c */
92 /*                              0x000d */
93 #define TYPE_VOIP_VLAN_REPLY    0x000e /* VoIP VLAN reply */
94 #define TYPE_VOIP_VLAN_QUERY    0x000f /* VoIP VLAN query */
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 (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_MTU,             "MTU"},
119         { TYPE_TRUST_BITMAP,    "Trust Bitmap" },
120         { TYPE_UNTRUSTED_COS,   "Untrusted Port CoS" },
121         { TYPE_SYSTEM_NAME,     "System Name" },
122         { TYPE_SYSTEM_OID,      "System Object ID" },
123         { TYPE_MANAGEMENT_ADDR, "Management Address" },
124         { TYPE_LOCATION,        "Location" },
125         { 0,                    NULL },
126 };
127
128 #define TYPE_HELLO_CLUSTER_MGMT    0x0112
129
130 static const value_string type_hello_vals[] = {
131         { TYPE_HELLO_CLUSTER_MGMT,   "Cluster Management" },
132         { 0,                    NULL },
133 };
134
135 static void
136 dissect_cdp(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree)
137 {
138     proto_item *ti;
139     proto_tree *cdp_tree = NULL;
140     int offset = 0;
141     guint16 type;
142     guint16 length;
143     proto_item *tlvi;
144     proto_tree *tlv_tree;
145     int real_length;
146     guint32 naddresses;
147     int addr_length;
148     guint32 ip_addr;
149
150     if (check_col(pinfo->cinfo, COL_PROTOCOL))
151         col_set_str(pinfo->cinfo, COL_PROTOCOL, "CDP");
152     if (check_col(pinfo->cinfo, COL_INFO))
153         col_set_str(pinfo->cinfo, COL_INFO, "Cisco Discovery Protocol");
154
155     if (tree){
156         ti = proto_tree_add_item(tree, proto_cdp, tvb, offset, -1, FALSE);
157         cdp_tree = proto_item_add_subtree(ti, ett_cdp);
158
159         /* CDP header */
160         proto_tree_add_item(cdp_tree, hf_cdp_version, tvb, offset, 1, FALSE);
161         offset += 1;
162         proto_tree_add_uint_format(cdp_tree, hf_cdp_ttl, tvb, offset, 1,
163                                    tvb_get_guint8(tvb, offset),
164                                    "TTL: %u seconds",
165                                    tvb_get_guint8(tvb, offset));
166         offset += 1;
167         proto_tree_add_item(cdp_tree, hf_cdp_checksum, tvb, offset, 2, FALSE);
168         offset += 2;
169
170         while (tvb_reported_length_remaining(tvb, offset) != 0) {
171             type = tvb_get_ntohs(tvb, offset + TLV_TYPE);
172             length = tvb_get_ntohs(tvb, offset + TLV_LENGTH);
173             if (length < 4) {
174                 tlvi = proto_tree_add_text(cdp_tree, tvb, offset, 4,
175                     "TLV with invalid length %u (< 4)",
176                     length);
177                 tlv_tree = proto_item_add_subtree(tlvi, ett_cdp_tlv);
178                 proto_tree_add_uint(tlv_tree, hf_cdp_tlvtype, tvb,
179                             offset + TLV_TYPE, 2, type);
180                 proto_tree_add_uint(tlv_tree, hf_cdp_tlvlength, tvb,
181                             offset + TLV_LENGTH, 2, length);
182                 offset += 4;
183                 break;
184             }
185
186             switch (type) {
187
188             case TYPE_DEVICE_ID:
189                 /* Device ID */
190                 tlvi = proto_tree_add_text(cdp_tree, tvb, offset,
191                             length, "Device ID: %.*s",
192                             length - 4,
193                             tvb_get_ptr(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                             length - 4,
202                             tvb_get_ptr(tvb, offset + 4, length - 4));
203                 offset += length;
204                 break;
205
206             case TYPE_ADDRESS:
207                 /* Addresses */
208                 tlvi = proto_tree_add_text(cdp_tree, tvb, offset,
209                             length, "Addresses");
210                 tlv_tree = proto_item_add_subtree(tlvi, ett_cdp_tlv);
211                 proto_tree_add_uint(tlv_tree, hf_cdp_tlvtype, tvb,
212                             offset + TLV_TYPE, 2, type);
213                 proto_tree_add_uint(tlv_tree, hf_cdp_tlvlength, tvb,
214                             offset + TLV_LENGTH, 2, length);
215                 offset += 4;
216                 length -= 4;
217                 naddresses = tvb_get_ntohl(tvb, offset);
218                 proto_tree_add_text(tlv_tree, tvb, offset, 4,
219                             "Number of addresses: %u", naddresses);
220                 offset += 4;
221                 length -= 4;
222                 while (naddresses != 0) {
223                     addr_length = dissect_address_tlv(tvb, offset, length,
224                                 tlv_tree);
225                     if (addr_length < 0)
226                         break;
227                     offset += addr_length;
228                     length -= addr_length;
229
230                     naddresses--;
231                 }
232                 offset += length;
233                 break;
234
235             case TYPE_PORT_ID:
236                 real_length = length;
237                 if (tvb_get_guint8(tvb, offset + real_length) != 0x00) {
238                     /* The length in the TLV doesn't appear to be the
239                        length of the TLV, as the byte just past it
240                        isn't the first byte of a 2-byte big-endian
241                        small integer; make the length of the TLV the length
242                        in the TLV, plus 4 bytes for the TLV type and length,
243                        minus 1 because that's what makes one capture work. */
244                     real_length = length + 3;
245                 }
246                 tlvi = proto_tree_add_text(cdp_tree, tvb, offset,
247                             real_length, "Port ID: %.*s",
248                             real_length - 4,
249                             tvb_get_ptr(tvb, offset + 4, real_length - 4));
250                 tlv_tree = proto_item_add_subtree(tlvi, ett_cdp_tlv);
251                 proto_tree_add_uint(tlv_tree, hf_cdp_tlvtype, tvb,
252                             offset + TLV_TYPE, 2, type);
253                 proto_tree_add_uint(tlv_tree, hf_cdp_tlvlength, tvb,
254                             offset + TLV_LENGTH, 2, length);
255                 proto_tree_add_text(tlv_tree, tvb, offset + 4,
256                             real_length - 4,
257                             "Sent through Interface: %.*s",
258                             real_length - 4,
259                             tvb_get_ptr(tvb, offset + 4, real_length - 4));
260                 offset += real_length;
261                 break;
262
263             case TYPE_CAPABILITIES:
264                 tlvi = proto_tree_add_text(cdp_tree, tvb, offset,
265                             length, "Capabilities");
266                 tlv_tree = proto_item_add_subtree(tlvi, ett_cdp_tlv);
267                 proto_tree_add_uint(tlv_tree, hf_cdp_tlvtype, tvb,
268                             offset + TLV_TYPE, 2, type);
269                 proto_tree_add_uint(tlv_tree, hf_cdp_tlvlength, tvb,
270                             offset + TLV_LENGTH, 2, length);
271                 offset += 4;
272                 length -= 4;
273                 dissect_capabilities(tvb, offset, length, tlv_tree);
274                 offset += length;
275                 break;
276
277             case TYPE_IOS_VERSION:
278                 tlvi = proto_tree_add_text(cdp_tree, tvb, offset,
279                             length, "Software Version");
280                 tlv_tree = proto_item_add_subtree(tlvi, ett_cdp_tlv);
281                 proto_tree_add_uint(tlv_tree, hf_cdp_tlvtype, tvb,
282                             offset + TLV_TYPE, 2, type);
283                 proto_tree_add_uint(tlv_tree, hf_cdp_tlvlength, tvb,
284                             offset + TLV_LENGTH, 2, length);
285                 add_multi_line_string_to_tree(tlv_tree, tvb, offset + 4,
286                                 length - 4, "Software Version: ");
287                 offset += length;
288                 break;
289
290             case TYPE_PLATFORM:
291                 /* ??? platform */
292                 tlvi = proto_tree_add_text(cdp_tree, tvb,
293                             offset, length, "Platform: %.*s",
294                             length - 4,
295                             tvb_get_ptr(tvb, offset + 4, length - 4));
296                 tlv_tree = proto_item_add_subtree(tlvi, ett_cdp_tlv);
297                 proto_tree_add_uint(tlv_tree, hf_cdp_tlvtype, tvb,
298                             offset + TLV_TYPE, 2, type);
299                 proto_tree_add_uint(tlv_tree, hf_cdp_tlvlength, tvb,
300                             offset + TLV_LENGTH, 2, length);
301                 proto_tree_add_text(tlv_tree, tvb, offset + 4,
302                             length - 4, "Platform: %.*s",
303                             length - 4,
304                             tvb_get_ptr(tvb, offset + 4, length - 4));
305                 offset += length;
306                 break;
307             case TYPE_IP_PREFIX:
308                 tlvi = proto_tree_add_text(cdp_tree, tvb, offset,
309                             length, "IP Prefixes: %d",length/5);
310
311                             /* the actual number of prefixes is (length-4)/5
312                             but if the variable is not a "float" but "integer"
313                             then length/5=(length-4)/5  :)  */
314
315                 tlv_tree = proto_item_add_subtree(tlvi, ett_cdp_tlv);
316                 proto_tree_add_uint(tlv_tree, hf_cdp_tlvtype, tvb,
317                             offset + TLV_TYPE, 2, type);
318                 proto_tree_add_uint(tlv_tree, hf_cdp_tlvlength, tvb,
319                             offset + TLV_LENGTH, 2, length);
320                 offset += 4;
321                 length -= 4;
322                 while (length > 0) {
323                         proto_tree_add_text(tlv_tree, tvb, offset, 5,
324                                 "IP Prefix = %s/%u",
325                                 ip_to_str(tvb_get_ptr(tvb, offset, 4)),
326                                 tvb_get_guint8(tvb,offset+4));
327                         offset += 5;
328                         length -= 5;
329                 }
330                 break;
331             case TYPE_PROTOCOL_HELLO:
332               tlvi = proto_tree_add_text(cdp_tree, tvb,
333                                          offset,length, "Protocol Hello: %s",
334                                          val_to_str(tvb_get_ntohs(tvb, offset+7), type_hello_vals, "Unknown (0x%04x)"));
335               tlv_tree = proto_item_add_subtree(tlvi, ett_cdp_tlv);
336               proto_tree_add_uint(tlv_tree, hf_cdp_tlvtype, tvb,
337                                   offset + TLV_TYPE, 2, type);
338               proto_tree_add_uint(tlv_tree, hf_cdp_tlvlength, tvb,
339                                   offset + TLV_LENGTH, 2, length);
340               proto_tree_add_text(tlv_tree, tvb, offset+4, 3,
341                                   "OUI: 0x%06X (%s)",
342                                   tvb_get_ntoh24(tvb,offset+4),
343                                   val_to_str(tvb_get_ntoh24(tvb,offset+4), oui_vals, "Unknown"));
344               proto_tree_add_text(tlv_tree, tvb, offset+7, 2,
345                                   "Protocol ID: 0x%04X (%s)",
346                                   tvb_get_ntohs(tvb, offset+7),
347                                   val_to_str(tvb_get_ntohs(tvb, offset+7), type_hello_vals, "Unknown"));
348               switch(tvb_get_ntohs(tvb, offset+7)) {
349               case TYPE_HELLO_CLUSTER_MGMT:
350                 /*              proto_tree_add_text(tlv_tree, tvb, offset+9,
351                                     length - 9, "Cluster Management");
352                 */
353                 tvb_memcpy(tvb, (guint8 *)&ip_addr, offset+9, 4);
354                 proto_tree_add_text(tlv_tree, tvb, offset+9, 4,
355                                     "Cluster Master IP: %s",ip_to_str((guint8 *)&ip_addr));
356                 tvb_memcpy(tvb, (guint8 *)&ip_addr, offset+13, 4);
357                 proto_tree_add_text(tlv_tree, tvb, offset+13, 4,
358                                     "UNKNOWN (IP?): 0x%08X (%s)",
359                                     ip_addr, ip_to_str((guint8 *)&ip_addr));
360                 proto_tree_add_text(tlv_tree, tvb, offset+17, 1,
361                                     "Version?: 0x%02X",
362                                     tvb_get_guint8(tvb, offset+17));
363                 proto_tree_add_text(tlv_tree, tvb, offset+18, 1,
364                                     "Sub Version?: 0x%02X",
365                                     tvb_get_guint8(tvb, offset+18));
366                 proto_tree_add_text(tlv_tree, tvb, offset+19, 1,
367                                     "Status?: 0x%02X",
368                                     tvb_get_guint8(tvb, offset+19));
369                 proto_tree_add_text(tlv_tree, tvb, offset+20, 1,
370                                     "UNKNOWN: 0x%02X",
371                                     tvb_get_guint8(tvb, offset+20));
372                 proto_tree_add_text(tlv_tree, tvb, offset+21, 6,
373                                     "Cluster Commander MAC: %s",
374                                     ether_to_str(tvb_get_ptr(tvb, offset+21, 6)));
375                 proto_tree_add_text(tlv_tree, tvb, offset+27, 6,
376                                     "Switchs' MAC: %s",
377                                     ether_to_str(tvb_get_ptr(tvb, offset+27, 6)));
378                 proto_tree_add_text(tlv_tree, tvb, offset+33, 1,
379                                     "UNKNOWN: 0x%02X",
380                                     tvb_get_guint8(tvb, offset+33));
381                 proto_tree_add_text(tlv_tree, tvb, offset+34, 2,
382                                     "Management VLAN: %d",
383                                     tvb_get_ntohs(tvb, offset+34));
384                 break;
385               default:
386                 proto_tree_add_text(tlv_tree, tvb, offset + 9,
387                                     length - 9, "Unknown");
388                 break;
389               }
390               offset += length;
391               break;
392             case TYPE_VTP_MGMT_DOMAIN:
393                 tlvi = proto_tree_add_text(cdp_tree, tvb,
394                             offset, length, "VTP Management Domain: %.*s",
395                             length - 4,
396                             tvb_get_ptr(tvb, offset + 4, length - 4));
397                 tlv_tree = proto_item_add_subtree(tlvi, ett_cdp_tlv);
398                 proto_tree_add_uint(tlv_tree, hf_cdp_tlvtype, tvb,
399                             offset + TLV_TYPE, 2, type);
400                 proto_tree_add_uint(tlv_tree, hf_cdp_tlvlength, tvb,
401                             offset + TLV_LENGTH, 2, length);
402                 proto_tree_add_text(tlv_tree, tvb, offset + 4,
403                             length - 4, "VTP Management Domain: %.*s",
404                             length - 4,
405                             tvb_get_ptr(tvb, offset + 4, length - 4));
406                 offset += length;
407                 break;
408             case TYPE_NATIVE_VLAN:
409                 tlvi = proto_tree_add_text(cdp_tree, tvb,
410                             offset, length, "Native VLAN: %u",
411                                            tvb_get_ntohs(tvb, offset + 4));
412                 tlv_tree = proto_item_add_subtree(tlvi, ett_cdp_tlv);
413                 proto_tree_add_uint(tlv_tree, hf_cdp_tlvtype, tvb,
414                             offset + TLV_TYPE, 2, type);
415                 proto_tree_add_uint(tlv_tree, hf_cdp_tlvlength, tvb,
416                             offset + TLV_LENGTH, 2, length);
417                 proto_tree_add_text(tlv_tree, tvb, offset + 4,
418                             length - 4, "Native VLAN: %u",
419                                     tvb_get_ntohs(tvb, offset + 4));
420                 offset += length;
421                 break;
422             case TYPE_DUPLEX:
423                 tlvi = proto_tree_add_text(cdp_tree, tvb,
424                             offset, length, "Duplex: %s",
425                                            tvb_get_guint8(tvb, offset + 4) ?
426                                            "Full" : "Half" );
427                 tlv_tree = proto_item_add_subtree(tlvi, ett_cdp_tlv);
428                 proto_tree_add_uint(tlv_tree, hf_cdp_tlvtype, tvb,
429                             offset + TLV_TYPE, 2, type);
430                 proto_tree_add_uint(tlv_tree, hf_cdp_tlvlength, tvb,
431                             offset + TLV_LENGTH, 2, length);
432                 proto_tree_add_text(tlv_tree, tvb, offset + 4,
433                             length - 4, "Duplex: %s",
434                                     tvb_get_guint8(tvb, offset + 4) ?
435                                     "Full" : "Half" );
436                 offset += length;
437                 break;
438             case TYPE_VOIP_VLAN_REPLY:
439               tlvi = proto_tree_add_text(cdp_tree, tvb,
440                                          offset, length, "VoIP VLAN Reply");
441               tlv_tree = proto_item_add_subtree(tlvi, ett_cdp_tlv);
442               proto_tree_add_uint(tlv_tree, hf_cdp_tlvtype, tvb,
443                                   offset + TLV_TYPE, 2, type);
444               proto_tree_add_uint(tlv_tree, hf_cdp_tlvlength, tvb,
445                                   offset + TLV_LENGTH, 2, length);
446               proto_tree_add_text(tlv_tree, tvb, offset + 4,
447                                   length - 4, "Data");
448               offset += length;
449               break;
450             case TYPE_VOIP_VLAN_QUERY:
451               tlvi = proto_tree_add_text(cdp_tree, tvb,
452                                          offset, length, "VoIP VLAN Query");
453               tlv_tree = proto_item_add_subtree(tlvi, ett_cdp_tlv);
454               proto_tree_add_uint(tlv_tree, hf_cdp_tlvtype, tvb,
455                                   offset + TLV_TYPE, 2, type);
456               proto_tree_add_uint(tlv_tree, hf_cdp_tlvlength, tvb,
457                                   offset + TLV_LENGTH, 2, length);
458               proto_tree_add_text(tlv_tree, tvb, offset + 4,
459                                   length - 4, "Data");
460               offset += length;
461               break;
462             case TYPE_MTU:
463               tlvi = proto_tree_add_text(cdp_tree, tvb,
464                                          offset, length, "MTU: %u",
465                                          tvb_get_ntohl(tvb,offset + 4));
466               tlv_tree = proto_item_add_subtree(tlvi, ett_cdp_tlv);
467               proto_tree_add_uint(tlv_tree, hf_cdp_tlvtype, tvb,
468                                   offset + TLV_TYPE, 2, type);
469               proto_tree_add_uint(tlv_tree, hf_cdp_tlvlength, tvb,
470                                   offset + TLV_LENGTH, 2, length);
471               proto_tree_add_text(tlv_tree, tvb, offset + 4,
472                                   length - 4, "MTU: %u",
473                                   tvb_get_ntohl(tvb,offset + 4));
474               offset += length;
475               break;
476             case TYPE_TRUST_BITMAP:
477               tlvi = proto_tree_add_text(cdp_tree, tvb,
478                                          offset, length, "Trust Bitmap: 0x%02X",
479                                          tvb_get_guint8(tvb, offset + 4));
480               tlv_tree = proto_item_add_subtree(tlvi, ett_cdp_tlv);
481               proto_tree_add_uint(tlv_tree, hf_cdp_tlvtype, tvb,
482                                   offset + TLV_TYPE, 2, type);
483               proto_tree_add_uint(tlv_tree, hf_cdp_tlvlength, tvb,
484                                   offset + TLV_LENGTH, 2, length);
485               proto_tree_add_text(tlv_tree, tvb, offset + 4,
486                                   length - 4, "Trust Bitmap: %02x",
487                                   tvb_get_guint8(tvb, offset + 4));
488               offset += length;
489               break;
490             case TYPE_UNTRUSTED_COS:
491               tlvi = proto_tree_add_text(cdp_tree, tvb,
492                                          offset, length, "Untrusted port CoS: 0x%02X",
493                                          tvb_get_guint8(tvb, offset + 4));
494               tlv_tree = proto_item_add_subtree(tlvi, ett_cdp_tlv);
495               proto_tree_add_uint(tlv_tree, hf_cdp_tlvtype, tvb,
496                                   offset + TLV_TYPE, 2, type);
497               proto_tree_add_uint(tlv_tree, hf_cdp_tlvlength, tvb,
498                                   offset + TLV_LENGTH, 2, length);
499               proto_tree_add_text(tlv_tree, tvb, offset + 4,
500                                   length - 4, "Untrusted port CoS: %02x",
501                                   tvb_get_guint8(tvb, offset + 4));
502               offset += length;
503               break;
504             case TYPE_SYSTEM_NAME:
505               tlvi = proto_tree_add_text(cdp_tree, tvb,
506                                          offset, length, "System Name: %.*s",
507                                          length - 4,
508                                          tvb_get_ptr(tvb, offset + 4, length - 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, "System Name: %.*s",
516                                   length - 4,
517                                   tvb_get_ptr(tvb, offset + 4, length - 4));
518               offset += length;
519               break;
520             case TYPE_SYSTEM_OID:
521               tlvi = proto_tree_add_text(cdp_tree, tvb,
522                                          offset, length, "System Object Identifier");
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, "System Object Identifier: %s",
530                                   tvb_bytes_to_str(tvb, offset + 4, length - 4));
531               offset += length;
532               break;
533             case TYPE_MANAGEMENT_ADDR:
534               tlvi = proto_tree_add_text(cdp_tree, tvb,
535                                          offset, length, "Management Addresses");
536               tlv_tree = proto_item_add_subtree(tlvi, ett_cdp_tlv);
537               proto_tree_add_uint(tlv_tree, hf_cdp_tlvtype, tvb,
538                                   offset + TLV_TYPE, 2, type);
539               proto_tree_add_uint(tlv_tree, hf_cdp_tlvlength, tvb,
540                                   offset + TLV_LENGTH, 2, length);
541               offset += 4;
542               length -= 4;
543               naddresses = tvb_get_ntohl(tvb, offset);
544               proto_tree_add_text(tlv_tree, tvb, offset, 4,
545                                   "Number of addresses: %u", naddresses);
546               offset += 4;
547               length -= 4;
548               while (naddresses != 0) {
549                 addr_length = dissect_address_tlv(tvb, offset, length,
550                                                   tlv_tree);
551                 if (addr_length < 0)
552                   break;
553                 offset += addr_length;
554                 length -= addr_length;
555                 
556                 naddresses--;
557               }
558               offset += length;
559               break;
560             case TYPE_LOCATION:
561               tlvi = proto_tree_add_text(cdp_tree, tvb,
562                                          offset, length, "Location: %.*s",
563                                          length - 5,
564                                          tvb_get_ptr(tvb, offset + 5, length - 5));
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                                   1 , "UNKNOWN: 0x%02X",
572                                   tvb_get_guint8(tvb, offset + 4));
573               proto_tree_add_text(tlv_tree, tvb, offset + 5,
574                                   length - 5, "Location: %.*s",
575                                   length - 5,
576                                   tvb_get_ptr(tvb, offset + 5, length - 5));
577               offset += length;
578               break;
579             default:
580                 tlvi = proto_tree_add_text(cdp_tree, tvb, offset,
581                             length, "Type: %s, length: %u",
582                             val_to_str(type, type_vals, "Unknown (0x%04x)"),
583                             length);
584                 tlv_tree = proto_item_add_subtree(tlvi, ett_cdp_tlv);
585                 proto_tree_add_uint(tlv_tree, hf_cdp_tlvtype, tvb,
586                             offset + TLV_TYPE, 2, type);
587                 proto_tree_add_uint(tlv_tree, hf_cdp_tlvlength, tvb,
588                             offset + TLV_LENGTH, 2, length);
589                 if (length > 4) {
590                         proto_tree_add_text(tlv_tree, tvb, offset + 4,
591                                         length - 4, "Data");
592                 } else
593                         return;
594                 offset += length;
595             }
596         }
597         call_dissector(data_handle, tvb_new_subset(tvb, offset, -1, -1), pinfo,
598                         cdp_tree);
599     }
600 }
601
602 #define PROTO_TYPE_NLPID        1
603 #define PROTO_TYPE_IEEE_802_2   2
604
605 static const value_string proto_type_vals[] = {
606         { PROTO_TYPE_NLPID,      "NLPID" },
607         { PROTO_TYPE_IEEE_802_2, "802.2" },
608         { 0,                     NULL },
609 };
610
611 static int
612 dissect_address_tlv(tvbuff_t *tvb, int offset, int length, proto_tree *tree)
613 {
614     proto_item *ti;
615     proto_tree *address_tree;
616     guint8 protocol_type;
617     guint8 protocol_length;
618     int nlpid;
619     char *protocol_str;
620     guint16 address_length;
621     char *address_type_str;
622     char *address_str;
623
624     if (length < 1)
625         return -1;
626     ti = proto_tree_add_text(tree, tvb, offset, length, "Truncated address");
627     address_tree = proto_item_add_subtree(ti, ett_cdp_address);
628     protocol_type = tvb_get_guint8(tvb, offset);
629     proto_tree_add_text(address_tree, tvb, offset, 1, "Protocol type: %s",
630         val_to_str(protocol_type, proto_type_vals, "Unknown (0x%02x)"));
631     offset += 1;
632     length -= 1;
633
634     if (length < 1)
635         return -1;
636     protocol_length = tvb_get_guint8(tvb, offset);
637     proto_tree_add_text(address_tree, tvb, offset, 1, "Protocol length: %u",
638                         protocol_length);
639     offset += 1;
640     length -= 1;
641
642     if (length < protocol_length) {
643         if (length != 0) {
644             proto_tree_add_text(address_tree, tvb, offset, length,
645               "Protocol: %s (truncated)",
646               tvb_bytes_to_str(tvb, offset, length));
647         }
648         return -1;
649     }
650     protocol_str = NULL;
651     if (protocol_type == PROTO_TYPE_NLPID && protocol_length == 1) {
652         nlpid = tvb_get_guint8(tvb, offset);
653         protocol_str = val_to_str(nlpid, nlpid_vals, "Unknown (0x%02x)");
654     } else
655         nlpid = -1;
656     if (protocol_str == NULL)
657         protocol_str = tvb_bytes_to_str(tvb, offset, protocol_length);
658     proto_tree_add_text(address_tree, tvb, offset, protocol_length,
659                         "Protocol: %s", protocol_str);
660     offset += protocol_length;
661     length -= protocol_length;
662
663     if (length < 2)
664         return -1;
665     address_length = tvb_get_ntohs(tvb, offset);
666     proto_tree_add_text(address_tree, tvb, offset, 2, "Address length: %u",
667                         address_length);
668     offset += 2;
669     length -= 2;
670
671     if (length < address_length) {
672         if (length != 0) {
673             proto_tree_add_text(address_tree, tvb, offset, length,
674               "Address: %s (truncated)",
675               tvb_bytes_to_str(tvb, offset, length));
676         }
677         return -1;
678     }
679     /* XXX - the Cisco document seems to be saying that, for 802.2-format
680        protocol types, 0xAAAA03 0x000000 0x0800 is IPv6, but 0x0800 is
681        the Ethernet protocol type for IPv4. */
682     length = 2 + protocol_length + 2 + address_length;
683     address_type_str = NULL;
684     address_str = NULL;
685     if (protocol_type == PROTO_TYPE_NLPID && protocol_length == 1) {
686         switch (nlpid) {
687
688         /* XXX - dissect NLPID_ISO8473_CLNP as OSI CLNP address? */
689
690         case NLPID_IP:
691             if (address_length == 4) {
692                 /* The address is an IP address. */
693                 address_type_str = "IP address";
694                 address_str = ip_to_str(tvb_get_ptr(tvb, offset, 4));
695             }
696             break;
697         }
698     }
699     if (address_type_str == NULL)
700         address_type_str = "Address";
701     if (address_str == NULL) {
702         address_str = tvb_bytes_to_str(tvb, offset, address_length);
703     }
704     proto_item_set_text(ti, "%s: %s", address_type_str, address_str);
705     proto_tree_add_text(address_tree, tvb, offset, address_length, "%s: %s",
706       address_type_str, address_str);
707     return 2 + protocol_length + 2 + address_length;
708 }
709
710 static void
711 dissect_capabilities(tvbuff_t *tvb, int offset, int length, proto_tree *tree)
712 {
713     proto_item *ti;
714     proto_tree *capabilities_tree;
715     guint32 capabilities;
716
717     if (length < 4)
718         return;
719     capabilities = tvb_get_ntohl(tvb, offset);
720     ti = proto_tree_add_text(tree, tvb, offset, length, "Capabilities: 0x%08x",
721         capabilities);
722     capabilities_tree = proto_item_add_subtree(ti, ett_cdp_capabilities);
723     proto_tree_add_text(capabilities_tree, tvb, offset, 4,
724         decode_boolean_bitfield(capabilities, 0x01, 4*8,
725             "Is  a Router",
726             "Not a Router"));
727     proto_tree_add_text(capabilities_tree, tvb, offset, 4,
728         decode_boolean_bitfield(capabilities, 0x02, 4*8,
729             "Is  a Transparent Bridge",
730             "Not a Transparent Bridge"));
731     proto_tree_add_text(capabilities_tree, tvb, offset, 4,
732         decode_boolean_bitfield(capabilities, 0x04, 4*8,
733             "Is  a Source Route Bridge",
734             "Not a Source Route Bridge"));
735     proto_tree_add_text(capabilities_tree, tvb, offset, 4,
736         decode_boolean_bitfield(capabilities, 0x08, 4*8,
737             "Is  a Switch",
738             "Not a Switch"));
739     proto_tree_add_text(capabilities_tree, tvb, offset, 4,
740         decode_boolean_bitfield(capabilities, 0x10, 4*8,
741             "Is  a Host",
742             "Not a Host"));
743     proto_tree_add_text(capabilities_tree, tvb, offset, 4,
744         decode_boolean_bitfield(capabilities, 0x20, 4*8,
745             "Is  IGMP capable",
746             "Not IGMP capable"));
747     proto_tree_add_text(capabilities_tree, tvb, offset, 4,
748         decode_boolean_bitfield(capabilities, 0x40, 4*8,
749             "Is  a Repeater",
750             "Not a Repeater"));
751 }
752
753 static void
754 add_multi_line_string_to_tree(proto_tree *tree, tvbuff_t *tvb, gint start,
755   gint len, const gchar *prefix)
756 {
757     int prefix_len;
758     int i;
759     char blanks[64+1];
760     gint next;
761     int line_len;
762     int data_len;
763
764     prefix_len = strlen(prefix);
765     if (prefix_len > 64)
766         prefix_len = 64;
767     for (i = 0; i < prefix_len; i++)
768         blanks[i] = ' ';
769     blanks[i] = '\0';
770     while (len > 0) {
771         line_len = tvb_find_line_end(tvb, start, len, &next, FALSE);
772         data_len = next - start;
773         proto_tree_add_text(tree, tvb, start, data_len, "%s%.*s", prefix,
774            line_len, tvb_get_ptr(tvb, start, line_len));
775         start += data_len;
776         len -= data_len;
777         prefix = blanks;
778     }
779 }
780
781 void
782 proto_register_cdp(void)
783 {
784     static hf_register_info hf[] = {
785         { &hf_cdp_version,
786         { "Version",            "cdp.version",  FT_UINT8, BASE_DEC, NULL, 0x0,
787           "", HFILL }},
788
789         { &hf_cdp_ttl,
790         { "TTL",                "cdp.ttl", FT_UINT16, BASE_DEC, NULL, 0x0,
791           "", HFILL }},
792
793         { &hf_cdp_checksum,
794         { "Checksum",           "cdp.checksum", FT_UINT16, BASE_HEX, NULL, 0x0,
795           "", HFILL }},
796
797         { &hf_cdp_tlvtype,
798         { "Type",               "cdp.tlv.type", FT_UINT16, BASE_HEX, VALS(type_vals), 0x0,
799           "", HFILL }},
800
801         { &hf_cdp_tlvlength,
802         { "Length",             "cdp.tlv.len", FT_UINT16, BASE_DEC, NULL, 0x0,
803           "", HFILL }},
804     };
805     static gint *ett[] = {
806         &ett_cdp,
807         &ett_cdp_tlv,
808         &ett_cdp_address,
809         &ett_cdp_capabilities,
810     };
811
812     proto_cdp = proto_register_protocol("Cisco Discovery Protocol",
813                                         "CDP", "cdp");
814     proto_register_field_array(proto_cdp, hf, array_length(hf));
815     proto_register_subtree_array(ett, array_length(ett));
816 }
817
818 void
819 proto_reg_handoff_cdp(void)
820 {
821     dissector_handle_t cdp_handle;
822
823     data_handle = find_dissector("data");
824     cdp_handle = create_dissector_handle(dissect_cdp, proto_cdp);
825     dissector_add("llc.cisco_pid", 0x2000, cdp_handle);
826     dissector_add("chdlctype", 0x2000, cdp_handle);
827     dissector_add("ppp.protocol", 0x0207, cdp_handle);
828 }