Set the svn:eol-style property on all text files to "native", so that
[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$
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/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_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                             tvb_format_stringzpad(tvb, offset + 4, length - 4));
193                 tlv_tree = proto_item_add_subtree(tlvi, ett_cdp_tlv);
194                 proto_tree_add_uint(tlv_tree, hf_cdp_tlvtype, tvb,
195                             offset + TLV_TYPE, 2, type);
196                 proto_tree_add_uint(tlv_tree, hf_cdp_tlvlength, tvb,
197                             offset + TLV_LENGTH, 2, length);
198                 proto_tree_add_text(tlv_tree, tvb, offset + 4,
199                             length - 4, "Device ID: %s",
200                             tvb_format_stringzpad(tvb, offset + 4, length - 4));
201                 offset += length;
202                 break;
203
204             case TYPE_ADDRESS:
205                 /* Addresses */
206                 tlvi = proto_tree_add_text(cdp_tree, tvb, offset,
207                             length, "Addresses");
208                 tlv_tree = proto_item_add_subtree(tlvi, ett_cdp_tlv);
209                 proto_tree_add_uint(tlv_tree, hf_cdp_tlvtype, tvb,
210                             offset + TLV_TYPE, 2, type);
211                 proto_tree_add_uint(tlv_tree, hf_cdp_tlvlength, tvb,
212                             offset + TLV_LENGTH, 2, length);
213                 offset += 4;
214                 length -= 4;
215                 naddresses = tvb_get_ntohl(tvb, offset);
216                 proto_tree_add_text(tlv_tree, tvb, offset, 4,
217                             "Number of addresses: %u", naddresses);
218                 offset += 4;
219                 length -= 4;
220                 while (naddresses != 0) {
221                     addr_length = dissect_address_tlv(tvb, offset, length,
222                                 tlv_tree);
223                     if (addr_length < 0)
224                         break;
225                     offset += addr_length;
226                     length -= addr_length;
227
228                     naddresses--;
229                 }
230                 offset += length;
231                 break;
232
233             case TYPE_PORT_ID:
234                 real_length = length;
235                 if (tvb_get_guint8(tvb, offset + real_length) != 0x00) {
236                     /* The length in the TLV doesn't appear to be the
237                        length of the TLV, as the byte just past it
238                        isn't the first byte of a 2-byte big-endian
239                        small integer; make the length of the TLV the length
240                        in the TLV, plus 4 bytes for the TLV type and length,
241                        minus 1 because that's what makes one capture work. */
242                     real_length = length + 3;
243                 }
244                 tlvi = proto_tree_add_text(cdp_tree, tvb, offset,
245                             real_length, "Port ID: %s",
246                             tvb_format_text(tvb, offset + 4, real_length - 4));
247                 tlv_tree = proto_item_add_subtree(tlvi, ett_cdp_tlv);
248                 proto_tree_add_uint(tlv_tree, hf_cdp_tlvtype, tvb,
249                             offset + TLV_TYPE, 2, type);
250                 proto_tree_add_uint(tlv_tree, hf_cdp_tlvlength, tvb,
251                             offset + TLV_LENGTH, 2, length);
252                 proto_tree_add_text(tlv_tree, tvb, offset + 4,
253                             real_length - 4,
254                             "Sent through Interface: %s",
255                             tvb_format_text(tvb, offset + 4, real_length - 4));
256                 offset += real_length;
257                 break;
258
259             case TYPE_CAPABILITIES:
260                 tlvi = proto_tree_add_text(cdp_tree, tvb, offset,
261                             length, "Capabilities");
262                 tlv_tree = proto_item_add_subtree(tlvi, ett_cdp_tlv);
263                 proto_tree_add_uint(tlv_tree, hf_cdp_tlvtype, tvb,
264                             offset + TLV_TYPE, 2, type);
265                 proto_tree_add_uint(tlv_tree, hf_cdp_tlvlength, tvb,
266                             offset + TLV_LENGTH, 2, length);
267                 offset += 4;
268                 length -= 4;
269                 dissect_capabilities(tvb, offset, length, tlv_tree);
270                 offset += length;
271                 break;
272
273             case TYPE_IOS_VERSION:
274                 tlvi = proto_tree_add_text(cdp_tree, tvb, offset,
275                             length, "Software Version");
276                 tlv_tree = proto_item_add_subtree(tlvi, ett_cdp_tlv);
277                 proto_tree_add_uint(tlv_tree, hf_cdp_tlvtype, tvb,
278                             offset + TLV_TYPE, 2, type);
279                 proto_tree_add_uint(tlv_tree, hf_cdp_tlvlength, tvb,
280                             offset + TLV_LENGTH, 2, length);
281                 add_multi_line_string_to_tree(tlv_tree, tvb, offset + 4,
282                                 length - 4, "Software Version: ");
283                 offset += length;
284                 break;
285
286             case TYPE_PLATFORM:
287                 /* ??? platform */
288                 tlvi = proto_tree_add_text(cdp_tree, tvb,
289                             offset, length, "Platform: %s",
290                             tvb_format_text(tvb, offset + 4, length - 4));
291                 tlv_tree = proto_item_add_subtree(tlvi, ett_cdp_tlv);
292                 proto_tree_add_uint(tlv_tree, hf_cdp_tlvtype, tvb,
293                             offset + TLV_TYPE, 2, type);
294                 proto_tree_add_uint(tlv_tree, hf_cdp_tlvlength, tvb,
295                             offset + TLV_LENGTH, 2, length);
296                 proto_tree_add_text(tlv_tree, tvb, offset + 4,
297                             length - 4, "Platform: %s",
298                             tvb_format_text(tvb, offset + 4, length - 4));
299                 offset += length;
300                 break;
301             case TYPE_IP_PREFIX:
302                 if (length == 8) {
303                     /* if length is 8 then this is default gw not prefix */
304                     tlvi = proto_tree_add_text(cdp_tree, tvb, offset,
305                             length, "ODR Default gateway: %s",
306                             ip_to_str(tvb_get_ptr(tvb, offset+4, 4)));
307                     tlv_tree = proto_item_add_subtree(tlvi, ett_cdp_tlv);
308                     proto_tree_add_uint(tlv_tree, hf_cdp_tlvtype, tvb,
309                             offset + TLV_TYPE, 2, type);
310                     proto_tree_add_uint(tlv_tree, hf_cdp_tlvlength, tvb,
311                             offset + TLV_LENGTH, 2, length);
312                     proto_tree_add_text(tlv_tree, tvb, offset+4, 4,
313                                 "ODR Default gateway = %s",
314                                 ip_to_str(tvb_get_ptr(tvb, offset+4, 4)));
315                     offset += 8;
316                 } else {  
317                     tlvi = proto_tree_add_text(cdp_tree, tvb, offset,
318                             length, "IP Prefixes: %d",length/5);
319
320                     /* the actual number of prefixes is (length-4)/5
321                     but if the variable is not a "float" but "integer"
322                     then length/5=(length-4)/5  :)  */
323
324                     tlv_tree = proto_item_add_subtree(tlvi, ett_cdp_tlv);
325                     proto_tree_add_uint(tlv_tree, hf_cdp_tlvtype, tvb,
326                             offset + TLV_TYPE, 2, type);
327                     proto_tree_add_uint(tlv_tree, hf_cdp_tlvlength, tvb,
328                             offset + TLV_LENGTH, 2, length);
329                     offset += 4;
330                     length -= 4;
331                     while (length > 0) {
332                         proto_tree_add_text(tlv_tree, tvb, offset, 5,
333                                 "IP Prefix = %s/%u",
334                                 ip_to_str(tvb_get_ptr(tvb, offset, 4)),
335                                 tvb_get_guint8(tvb,offset+4));
336                         offset += 5;
337                         length -= 5;
338                     }
339                 }
340                 break;
341             case TYPE_PROTOCOL_HELLO:
342               tlvi = proto_tree_add_text(cdp_tree, tvb,
343                                          offset,length, "Protocol Hello: %s",
344                                          val_to_str(tvb_get_ntohs(tvb, offset+7), type_hello_vals, "Unknown (0x%04x)"));
345               tlv_tree = proto_item_add_subtree(tlvi, ett_cdp_tlv);
346               proto_tree_add_uint(tlv_tree, hf_cdp_tlvtype, tvb,
347                                   offset + TLV_TYPE, 2, type);
348               proto_tree_add_uint(tlv_tree, hf_cdp_tlvlength, tvb,
349                                   offset + TLV_LENGTH, 2, length);
350               proto_tree_add_text(tlv_tree, tvb, offset+4, 3,
351                                   "OUI: 0x%06X (%s)",
352                                   tvb_get_ntoh24(tvb,offset+4),
353                                   val_to_str(tvb_get_ntoh24(tvb,offset+4), oui_vals, "Unknown"));
354               proto_tree_add_text(tlv_tree, tvb, offset+7, 2,
355                                   "Protocol ID: 0x%04X (%s)",
356                                   tvb_get_ntohs(tvb, offset+7),
357                                   val_to_str(tvb_get_ntohs(tvb, offset+7), type_hello_vals, "Unknown"));
358               switch(tvb_get_ntohs(tvb, offset+7)) {
359               case TYPE_HELLO_CLUSTER_MGMT:
360                 /*              proto_tree_add_text(tlv_tree, tvb, offset+9,
361                                     length - 9, "Cluster Management");
362                 */
363                 tvb_memcpy(tvb, (guint8 *)&ip_addr, offset+9, 4);
364                 proto_tree_add_text(tlv_tree, tvb, offset+9, 4,
365                                     "Cluster Master IP: %s",ip_to_str((guint8 *)&ip_addr));
366                 tvb_memcpy(tvb, (guint8 *)&ip_addr, offset+13, 4);
367                 proto_tree_add_text(tlv_tree, tvb, offset+13, 4,
368                                     "UNKNOWN (IP?): 0x%08X (%s)",
369                                     ip_addr, ip_to_str((guint8 *)&ip_addr));
370                 proto_tree_add_text(tlv_tree, tvb, offset+17, 1,
371                                     "Version?: 0x%02X",
372                                     tvb_get_guint8(tvb, offset+17));
373                 proto_tree_add_text(tlv_tree, tvb, offset+18, 1,
374                                     "Sub Version?: 0x%02X",
375                                     tvb_get_guint8(tvb, offset+18));
376                 proto_tree_add_text(tlv_tree, tvb, offset+19, 1,
377                                     "Status?: 0x%02X",
378                                     tvb_get_guint8(tvb, offset+19));
379                 proto_tree_add_text(tlv_tree, tvb, offset+20, 1,
380                                     "UNKNOWN: 0x%02X",
381                                     tvb_get_guint8(tvb, offset+20));
382                 proto_tree_add_text(tlv_tree, tvb, offset+21, 6,
383                                     "Cluster Commander MAC: %s",
384                                     ether_to_str(tvb_get_ptr(tvb, offset+21, 6)));
385                 proto_tree_add_text(tlv_tree, tvb, offset+27, 6,
386                                     "Switch's MAC: %s",
387                                     ether_to_str(tvb_get_ptr(tvb, offset+27, 6)));
388                 proto_tree_add_text(tlv_tree, tvb, offset+33, 1,
389                                     "UNKNOWN: 0x%02X",
390                                     tvb_get_guint8(tvb, offset+33));
391                 proto_tree_add_text(tlv_tree, tvb, offset+34, 2,
392                                     "Management VLAN: %d",
393                                     tvb_get_ntohs(tvb, offset+34));
394                 break;
395               default:
396                 proto_tree_add_text(tlv_tree, tvb, offset + 9,
397                                     length - 9, "Unknown");
398                 break;
399               }
400               offset += length;
401               break;
402             case TYPE_VTP_MGMT_DOMAIN:
403                 tlvi = proto_tree_add_text(cdp_tree, tvb,
404                             offset, length, "VTP Management Domain: %s",
405                             tvb_format_text(tvb, offset + 4, length - 4));
406                 tlv_tree = proto_item_add_subtree(tlvi, ett_cdp_tlv);
407                 proto_tree_add_uint(tlv_tree, hf_cdp_tlvtype, tvb,
408                             offset + TLV_TYPE, 2, type);
409                 proto_tree_add_uint(tlv_tree, hf_cdp_tlvlength, tvb,
410                             offset + TLV_LENGTH, 2, length);
411                 proto_tree_add_text(tlv_tree, tvb, offset + 4,
412                             length - 4, "VTP Management Domain: %s",
413                             tvb_format_text(tvb, offset + 4, length - 4));
414                 offset += length;
415                 break;
416             case TYPE_NATIVE_VLAN:
417                 tlvi = proto_tree_add_text(cdp_tree, tvb,
418                             offset, length, "Native VLAN: %u",
419                                            tvb_get_ntohs(tvb, offset + 4));
420                 tlv_tree = proto_item_add_subtree(tlvi, ett_cdp_tlv);
421                 proto_tree_add_uint(tlv_tree, hf_cdp_tlvtype, tvb,
422                             offset + TLV_TYPE, 2, type);
423                 proto_tree_add_uint(tlv_tree, hf_cdp_tlvlength, tvb,
424                             offset + TLV_LENGTH, 2, length);
425                 proto_tree_add_text(tlv_tree, tvb, offset + 4,
426                             length - 4, "Native VLAN: %u",
427                                     tvb_get_ntohs(tvb, offset + 4));
428                 offset += length;
429                 break;
430             case TYPE_DUPLEX:
431                 tlvi = proto_tree_add_text(cdp_tree, tvb,
432                             offset, length, "Duplex: %s",
433                                            tvb_get_guint8(tvb, offset + 4) ?
434                                            "Full" : "Half" );
435                 tlv_tree = proto_item_add_subtree(tlvi, ett_cdp_tlv);
436                 proto_tree_add_uint(tlv_tree, hf_cdp_tlvtype, tvb,
437                             offset + TLV_TYPE, 2, type);
438                 proto_tree_add_uint(tlv_tree, hf_cdp_tlvlength, tvb,
439                             offset + TLV_LENGTH, 2, length);
440                 proto_tree_add_text(tlv_tree, tvb, offset + 4,
441                             length - 4, "Duplex: %s",
442                                     tvb_get_guint8(tvb, offset + 4) ?
443                                     "Full" : "Half" );
444                 offset += length;
445                 break;
446             case TYPE_VOIP_VLAN_REPLY:
447               tlvi = proto_tree_add_text(cdp_tree, tvb,
448                                          offset, length, "VoIP VLAN Reply");
449               tlv_tree = proto_item_add_subtree(tlvi, ett_cdp_tlv);
450               proto_tree_add_uint(tlv_tree, hf_cdp_tlvtype, tvb,
451                                   offset + TLV_TYPE, 2, type);
452               proto_tree_add_uint(tlv_tree, hf_cdp_tlvlength, tvb,
453                                   offset + TLV_LENGTH, 2, length);
454               proto_tree_add_text(tlv_tree, tvb, offset + 4,
455                                   length - 4, "Data");
456               offset += length;
457               break;
458             case TYPE_VOIP_VLAN_QUERY:
459               tlvi = proto_tree_add_text(cdp_tree, tvb,
460                                          offset, length, "VoIP VLAN Query");
461               tlv_tree = proto_item_add_subtree(tlvi, ett_cdp_tlv);
462               proto_tree_add_uint(tlv_tree, hf_cdp_tlvtype, tvb,
463                                   offset + TLV_TYPE, 2, type);
464               proto_tree_add_uint(tlv_tree, hf_cdp_tlvlength, tvb,
465                                   offset + TLV_LENGTH, 2, length);
466               proto_tree_add_text(tlv_tree, tvb, offset + 4,
467                                   length - 4, "Data");
468               offset += length;
469               break;
470             case TYPE_MTU:
471               tlvi = proto_tree_add_text(cdp_tree, tvb,
472                                          offset, length, "MTU: %u",
473                                          tvb_get_ntohl(tvb,offset + 4));
474               tlv_tree = proto_item_add_subtree(tlvi, ett_cdp_tlv);
475               proto_tree_add_uint(tlv_tree, hf_cdp_tlvtype, tvb,
476                                   offset + TLV_TYPE, 2, type);
477               proto_tree_add_uint(tlv_tree, hf_cdp_tlvlength, tvb,
478                                   offset + TLV_LENGTH, 2, length);
479               proto_tree_add_text(tlv_tree, tvb, offset + 4,
480                                   length - 4, "MTU: %u",
481                                   tvb_get_ntohl(tvb,offset + 4));
482               offset += length;
483               break;
484             case TYPE_TRUST_BITMAP:
485               tlvi = proto_tree_add_text(cdp_tree, tvb,
486                                          offset, length, "Trust Bitmap: 0x%02X",
487                                          tvb_get_guint8(tvb, offset + 4));
488               tlv_tree = proto_item_add_subtree(tlvi, ett_cdp_tlv);
489               proto_tree_add_uint(tlv_tree, hf_cdp_tlvtype, tvb,
490                                   offset + TLV_TYPE, 2, type);
491               proto_tree_add_uint(tlv_tree, hf_cdp_tlvlength, tvb,
492                                   offset + TLV_LENGTH, 2, length);
493               proto_tree_add_text(tlv_tree, tvb, offset + 4,
494                                   length - 4, "Trust Bitmap: %02x",
495                                   tvb_get_guint8(tvb, offset + 4));
496               offset += length;
497               break;
498             case TYPE_UNTRUSTED_COS:
499               tlvi = proto_tree_add_text(cdp_tree, tvb,
500                                          offset, length, "Untrusted port CoS: 0x%02X",
501                                          tvb_get_guint8(tvb, offset + 4));
502               tlv_tree = proto_item_add_subtree(tlvi, ett_cdp_tlv);
503               proto_tree_add_uint(tlv_tree, hf_cdp_tlvtype, tvb,
504                                   offset + TLV_TYPE, 2, type);
505               proto_tree_add_uint(tlv_tree, hf_cdp_tlvlength, tvb,
506                                   offset + TLV_LENGTH, 2, length);
507               proto_tree_add_text(tlv_tree, tvb, offset + 4,
508                                   length - 4, "Untrusted port CoS: %02x",
509                                   tvb_get_guint8(tvb, offset + 4));
510               offset += length;
511               break;
512             case TYPE_SYSTEM_NAME:
513               tlvi = proto_tree_add_text(cdp_tree, tvb,
514                                          offset, length, "System Name: %s",
515                                          tvb_format_text(tvb, offset + 4, length - 4));
516               tlv_tree = proto_item_add_subtree(tlvi, ett_cdp_tlv);
517               proto_tree_add_uint(tlv_tree, hf_cdp_tlvtype, tvb,
518                                   offset + TLV_TYPE, 2, type);
519               proto_tree_add_uint(tlv_tree, hf_cdp_tlvlength, tvb,
520                                   offset + TLV_LENGTH, 2, length);
521               proto_tree_add_text(tlv_tree, tvb, offset + 4,
522                                   length - 4, "System Name: %s",
523                                   tvb_format_text(tvb, offset + 4, length - 4));
524               offset += length;
525               break;
526             case TYPE_SYSTEM_OID:
527               tlvi = proto_tree_add_text(cdp_tree, tvb,
528                                          offset, length, "System Object Identifier");
529               tlv_tree = proto_item_add_subtree(tlvi, ett_cdp_tlv);
530               proto_tree_add_uint(tlv_tree, hf_cdp_tlvtype, tvb,
531                                   offset + TLV_TYPE, 2, type);
532               proto_tree_add_uint(tlv_tree, hf_cdp_tlvlength, tvb,
533                                   offset + TLV_LENGTH, 2, length);
534               proto_tree_add_text(tlv_tree, tvb, offset + 4,
535                                   length - 4, "System Object Identifier: %s",
536                                   tvb_bytes_to_str(tvb, offset + 4, length - 4));
537               offset += length;
538               break;
539             case TYPE_MANAGEMENT_ADDR:
540               tlvi = proto_tree_add_text(cdp_tree, tvb,
541                                          offset, length, "Management Addresses");
542               tlv_tree = proto_item_add_subtree(tlvi, ett_cdp_tlv);
543               proto_tree_add_uint(tlv_tree, hf_cdp_tlvtype, tvb,
544                                   offset + TLV_TYPE, 2, type);
545               proto_tree_add_uint(tlv_tree, hf_cdp_tlvlength, tvb,
546                                   offset + TLV_LENGTH, 2, length);
547               offset += 4;
548               length -= 4;
549               naddresses = tvb_get_ntohl(tvb, offset);
550               proto_tree_add_text(tlv_tree, tvb, offset, 4,
551                                   "Number of addresses: %u", naddresses);
552               offset += 4;
553               length -= 4;
554               while (naddresses != 0) {
555                 addr_length = dissect_address_tlv(tvb, offset, length,
556                                                   tlv_tree);
557                 if (addr_length < 0)
558                   break;
559                 offset += addr_length;
560                 length -= addr_length;
561                 
562                 naddresses--;
563               }
564               offset += length;
565               break;
566             case TYPE_LOCATION:
567               tlvi = proto_tree_add_text(cdp_tree, tvb,
568                                          offset, length, "Location: %s",
569                                          tvb_format_text(tvb, offset + 5, length - 5));
570               tlv_tree = proto_item_add_subtree(tlvi, ett_cdp_tlv);
571               proto_tree_add_uint(tlv_tree, hf_cdp_tlvtype, tvb,
572                                   offset + TLV_TYPE, 2, type);
573               proto_tree_add_uint(tlv_tree, hf_cdp_tlvlength, tvb,
574                                   offset + TLV_LENGTH, 2, length);
575               proto_tree_add_text(tlv_tree, tvb, offset + 4,
576                                   1 , "UNKNOWN: 0x%02X",
577                                   tvb_get_guint8(tvb, offset + 4));
578               proto_tree_add_text(tlv_tree, tvb, offset + 5,
579                                   length - 5, "Location: %s",
580                                   tvb_format_text(tvb, offset + 5, length - 5));
581               offset += length;
582               break;
583             default:
584                 tlvi = proto_tree_add_text(cdp_tree, tvb, offset,
585                             length, "Type: %s, length: %u",
586                             val_to_str(type, type_vals, "Unknown (0x%04x)"),
587                             length);
588                 tlv_tree = proto_item_add_subtree(tlvi, ett_cdp_tlv);
589                 proto_tree_add_uint(tlv_tree, hf_cdp_tlvtype, tvb,
590                             offset + TLV_TYPE, 2, type);
591                 proto_tree_add_uint(tlv_tree, hf_cdp_tlvlength, tvb,
592                             offset + TLV_LENGTH, 2, length);
593                 if (length > 4) {
594                         proto_tree_add_text(tlv_tree, tvb, offset + 4,
595                                         length - 4, "Data");
596                 } else
597                         return;
598                 offset += length;
599             }
600         }
601         call_dissector(data_handle, tvb_new_subset(tvb, offset, -1, -1), pinfo,
602                         cdp_tree);
603     }
604 }
605
606 #define PROTO_TYPE_NLPID        1
607 #define PROTO_TYPE_IEEE_802_2   2
608
609 static const value_string proto_type_vals[] = {
610         { PROTO_TYPE_NLPID,      "NLPID" },
611         { PROTO_TYPE_IEEE_802_2, "802.2" },
612         { 0,                     NULL },
613 };
614
615 static int
616 dissect_address_tlv(tvbuff_t *tvb, int offset, int length, proto_tree *tree)
617 {
618     proto_item *ti;
619     proto_tree *address_tree;
620     guint8 protocol_type;
621     guint8 protocol_length;
622     int nlpid;
623     char *protocol_str;
624     guint16 address_length;
625     char *address_type_str;
626     char *address_str;
627
628     if (length < 1)
629         return -1;
630     ti = proto_tree_add_text(tree, tvb, offset, length, "Truncated address");
631     address_tree = proto_item_add_subtree(ti, ett_cdp_address);
632     protocol_type = tvb_get_guint8(tvb, offset);
633     proto_tree_add_text(address_tree, tvb, offset, 1, "Protocol type: %s",
634         val_to_str(protocol_type, proto_type_vals, "Unknown (0x%02x)"));
635     offset += 1;
636     length -= 1;
637
638     if (length < 1)
639         return -1;
640     protocol_length = tvb_get_guint8(tvb, offset);
641     proto_tree_add_text(address_tree, tvb, offset, 1, "Protocol length: %u",
642                         protocol_length);
643     offset += 1;
644     length -= 1;
645
646     if (length < protocol_length) {
647         if (length != 0) {
648             proto_tree_add_text(address_tree, tvb, offset, length,
649               "Protocol: %s (truncated)",
650               tvb_bytes_to_str(tvb, offset, length));
651         }
652         return -1;
653     }
654     protocol_str = NULL;
655     if (protocol_type == PROTO_TYPE_NLPID && protocol_length == 1) {
656         nlpid = tvb_get_guint8(tvb, offset);
657         protocol_str = val_to_str(nlpid, nlpid_vals, "Unknown (0x%02x)");
658     } else
659         nlpid = -1;
660     if (protocol_str == NULL)
661         protocol_str = tvb_bytes_to_str(tvb, offset, protocol_length);
662     proto_tree_add_text(address_tree, tvb, offset, protocol_length,
663                         "Protocol: %s", protocol_str);
664     offset += protocol_length;
665     length -= protocol_length;
666
667     if (length < 2)
668         return -1;
669     address_length = tvb_get_ntohs(tvb, offset);
670     proto_tree_add_text(address_tree, tvb, offset, 2, "Address length: %u",
671                         address_length);
672     offset += 2;
673     length -= 2;
674
675     if (length < address_length) {
676         if (length != 0) {
677             proto_tree_add_text(address_tree, tvb, offset, length,
678               "Address: %s (truncated)",
679               tvb_bytes_to_str(tvb, offset, length));
680         }
681         return -1;
682     }
683     /* XXX - the Cisco document seems to be saying that, for 802.2-format
684        protocol types, 0xAAAA03 0x000000 0x0800 is IPv6, but 0x0800 is
685        the Ethernet protocol type for IPv4. */
686     length = 2 + protocol_length + 2 + address_length;
687     address_type_str = NULL;
688     address_str = NULL;
689     if (protocol_type == PROTO_TYPE_NLPID && protocol_length == 1) {
690         switch (nlpid) {
691
692         /* XXX - dissect NLPID_ISO8473_CLNP as OSI CLNP address? */
693
694         case NLPID_IP:
695             if (address_length == 4) {
696                 /* The address is an IP address. */
697                 address_type_str = "IP address";
698                 address_str = ip_to_str(tvb_get_ptr(tvb, offset, 4));
699             }
700             break;
701         }
702     }
703     if (address_type_str == NULL)
704         address_type_str = "Address";
705     if (address_str == NULL) {
706         address_str = tvb_bytes_to_str(tvb, offset, address_length);
707     }
708     proto_item_set_text(ti, "%s: %s", address_type_str, address_str);
709     proto_tree_add_text(address_tree, tvb, offset, address_length, "%s: %s",
710       address_type_str, address_str);
711     return 2 + protocol_length + 2 + address_length;
712 }
713
714 static void
715 dissect_capabilities(tvbuff_t *tvb, int offset, int length, proto_tree *tree)
716 {
717     proto_item *ti;
718     proto_tree *capabilities_tree;
719     guint32 capabilities;
720
721     if (length < 4)
722         return;
723     capabilities = tvb_get_ntohl(tvb, offset);
724     ti = proto_tree_add_text(tree, tvb, offset, length, "Capabilities: 0x%08x",
725         capabilities);
726     capabilities_tree = proto_item_add_subtree(ti, ett_cdp_capabilities);
727     proto_tree_add_text(capabilities_tree, tvb, offset, 4,
728         decode_boolean_bitfield(capabilities, 0x01, 4*8,
729             "Is  a Router",
730             "Not a Router"));
731     proto_tree_add_text(capabilities_tree, tvb, offset, 4,
732         decode_boolean_bitfield(capabilities, 0x02, 4*8,
733             "Is  a Transparent Bridge",
734             "Not a Transparent Bridge"));
735     proto_tree_add_text(capabilities_tree, tvb, offset, 4,
736         decode_boolean_bitfield(capabilities, 0x04, 4*8,
737             "Is  a Source Route Bridge",
738             "Not a Source Route Bridge"));
739     proto_tree_add_text(capabilities_tree, tvb, offset, 4,
740         decode_boolean_bitfield(capabilities, 0x08, 4*8,
741             "Is  a Switch",
742             "Not a Switch"));
743     proto_tree_add_text(capabilities_tree, tvb, offset, 4,
744         decode_boolean_bitfield(capabilities, 0x10, 4*8,
745             "Is  a Host",
746             "Not a Host"));
747     proto_tree_add_text(capabilities_tree, tvb, offset, 4,
748         decode_boolean_bitfield(capabilities, 0x20, 4*8,
749             "Is  IGMP capable",
750             "Not IGMP capable"));
751     proto_tree_add_text(capabilities_tree, tvb, offset, 4,
752         decode_boolean_bitfield(capabilities, 0x40, 4*8,
753             "Is  a Repeater",
754             "Not a Repeater"));
755 }
756
757 static void
758 add_multi_line_string_to_tree(proto_tree *tree, tvbuff_t *tvb, gint start,
759   gint len, const gchar *prefix)
760 {
761     int prefix_len;
762     int i;
763     char blanks[64+1];
764     gint next;
765     int line_len;
766     int data_len;
767
768     prefix_len = strlen(prefix);
769     if (prefix_len > 64)
770         prefix_len = 64;
771     for (i = 0; i < prefix_len; i++)
772         blanks[i] = ' ';
773     blanks[i] = '\0';
774     while (len > 0) {
775         line_len = tvb_find_line_end(tvb, start, len, &next, FALSE);
776         data_len = next - start;
777         proto_tree_add_text(tree, tvb, start, data_len, "%s%s", prefix,
778            tvb_format_stringzpad(tvb, start, line_len));
779         start += data_len;
780         len -= data_len;
781         prefix = blanks;
782     }
783 }
784
785 void
786 proto_register_cdp(void)
787 {
788     static hf_register_info hf[] = {
789         { &hf_cdp_version,
790         { "Version",            "cdp.version",  FT_UINT8, BASE_DEC, NULL, 0x0,
791           "", HFILL }},
792
793         { &hf_cdp_ttl,
794         { "TTL",                "cdp.ttl", FT_UINT16, BASE_DEC, NULL, 0x0,
795           "", HFILL }},
796
797         { &hf_cdp_checksum,
798         { "Checksum",           "cdp.checksum", FT_UINT16, BASE_HEX, NULL, 0x0,
799           "", HFILL }},
800
801         { &hf_cdp_tlvtype,
802         { "Type",               "cdp.tlv.type", FT_UINT16, BASE_HEX, VALS(type_vals), 0x0,
803           "", HFILL }},
804
805         { &hf_cdp_tlvlength,
806         { "Length",             "cdp.tlv.len", FT_UINT16, BASE_DEC, NULL, 0x0,
807           "", HFILL }},
808     };
809     static gint *ett[] = {
810         &ett_cdp,
811         &ett_cdp_tlv,
812         &ett_cdp_address,
813         &ett_cdp_capabilities,
814     };
815
816     proto_cdp = proto_register_protocol("Cisco Discovery Protocol",
817                                         "CDP", "cdp");
818     proto_register_field_array(proto_cdp, hf, array_length(hf));
819     proto_register_subtree_array(ett, array_length(ett));
820 }
821
822 void
823 proto_reg_handoff_cdp(void)
824 {
825     dissector_handle_t cdp_handle;
826
827     data_handle = find_dissector("data");
828     cdp_handle = create_dissector_handle(dissect_cdp, proto_cdp);
829     dissector_add("llc.cisco_pid", 0x2000, cdp_handle);
830     dissector_add("chdlctype", 0x2000, cdp_handle);
831     dissector_add("ppp.protocol", 0x0207, cdp_handle);
832 }