Fix the Frame Relay dissector to call subdissectors regardless of
[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.30 2001/01/05 19:14:05 guy Exp $
6  *
7  * Ethereal - Network traffic analyzer
8  * By Gerald Combs <gerald@zing.org>
9  * Copyright 1998 Gerald Combs
10  * 
11  * 
12  * This program is free software; you can redistribute it and/or
13  * modify it under the terms of the GNU General Public License
14  * as published by the Free Software Foundation; either version 2
15  * of the License, or (at your option) any later version.
16  * 
17  * This program is distributed in the hope that it will be useful,
18  * but WITHOUT ANY WARRANTY; without even the implied warranty of
19  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
20  * GNU General Public License for more details.
21  * 
22  * You should have received a copy of the GNU General Public License
23  * along with this program; if not, write to the Free Software
24  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
25  */
26  
27 #include "config.h"
28
29 #ifdef HAVE_SYS_TYPES_H
30 #include <sys/types.h>
31 #endif
32
33 #include <stdlib.h>
34 #include <string.h>
35
36 #include <glib.h>
37 #include "packet.h"
38 #include "strutil.h"
39 #include "nlpid.h"
40
41 /*
42  * See
43  *
44  *      http://www.cisco.com/univercd/cc/td/doc/product/lan/trsrb/frames.htm
45  *
46  * for some information on CDP.
47  */
48
49 /* Offsets in TLV structure. */
50 #define TLV_TYPE        0
51 #define TLV_LENGTH      2
52
53 static int proto_cdp = -1;
54 static int hf_cdp_version = -1;
55 static int hf_cdp_checksum = -1;
56 static int hf_cdp_ttl = -1;
57 static int hf_cdp_tlvtype = -1;
58 static int hf_cdp_tlvlength = -1;
59
60 static gint ett_cdp = -1;
61 static gint ett_cdp_tlv = -1;
62 static gint ett_cdp_address = -1;
63 static gint ett_cdp_capabilities = -1;
64
65 static int
66 dissect_address_tlv(tvbuff_t *tvb, int offset, int length, proto_tree *tree);
67 static void
68 dissect_capabilities(tvbuff_t *tvb, int offset, int length, proto_tree *tree);
69 static void
70 add_multi_line_string_to_tree(proto_tree *tree, tvbuff_t *tvb, gint start,
71   gint len, const gchar *prefix);
72
73 #define TYPE_DEVICE_ID          0x0001
74 #define TYPE_ADDRESS            0x0002
75 #define TYPE_PORT_ID            0x0003
76 #define TYPE_CAPABILITIES       0x0004
77 #define TYPE_IOS_VERSION        0x0005
78 #define TYPE_PLATFORM           0x0006
79 #define TYPE_IP_PREFIX          0x0007
80
81 static const value_string type_vals[] = {
82         { TYPE_DEVICE_ID,    "Device ID" },
83         { TYPE_ADDRESS,      "Addresses" },
84         { TYPE_PORT_ID,      "Port ID" },
85         { TYPE_CAPABILITIES, "Capabilities" },
86         { TYPE_IOS_VERSION,  "Software version" },
87         { TYPE_PLATFORM,     "Platform" },
88         { TYPE_IP_PREFIX,    "IP Prefix (used for ODR)" },
89         { 0,                 NULL },
90 };
91         
92 static void 
93 dissect_cdp(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree)
94 {
95     proto_item *ti; 
96     proto_tree *cdp_tree = NULL;
97     int offset = 0;
98     guint16 type;
99     guint16 length;
100     proto_item *tlvi;
101     proto_tree *tlv_tree;
102     int real_length;
103     guint32 naddresses;
104     int addr_length;
105
106     CHECK_DISPLAY_AS_DATA(proto_cdp, tvb, pinfo, tree);
107
108     pinfo->current_proto = "CDP";
109
110     if (check_col(pinfo->fd, COL_PROTOCOL))
111         col_set_str(pinfo->fd, COL_PROTOCOL, "CDP");
112     if (check_col(pinfo->fd, COL_INFO))
113         col_set_str(pinfo->fd, COL_INFO, "Cisco Discovery Protocol"); 
114
115     if (tree){
116         ti = proto_tree_add_item(tree, proto_cdp, tvb, offset,
117                                  tvb_length_remaining(tvb, offset), FALSE);
118         cdp_tree = proto_item_add_subtree(ti, ett_cdp);
119         
120         /* CDP header */
121         proto_tree_add_item(cdp_tree, hf_cdp_version, tvb, offset, 1, FALSE);
122         offset += 1;
123         proto_tree_add_uint_format(cdp_tree, hf_cdp_ttl, tvb, offset, 1,
124                                    tvb_get_guint8(tvb, offset),
125                                    "TTL: %u seconds",
126                                    tvb_get_guint8(tvb, offset));
127         offset += 1;
128         proto_tree_add_item(cdp_tree, hf_cdp_checksum, tvb, offset, 2, FALSE);
129         offset += 2;
130
131         while (tvb_reported_length_remaining(tvb, offset) != 0) {
132             type = tvb_get_ntohs(tvb, offset + TLV_TYPE);
133             length = tvb_get_ntohs(tvb, offset + TLV_LENGTH);
134
135             switch (type) {
136
137             case TYPE_DEVICE_ID:
138                 /* Device ID */
139                 tlvi = proto_tree_add_text(cdp_tree, tvb, offset,
140                             length, "Device ID: %.*s",
141                             length - 4,
142                             tvb_get_ptr(tvb, offset + 4, length - 4));
143                 tlv_tree = proto_item_add_subtree(tlvi, ett_cdp_tlv);
144                 proto_tree_add_uint(tlv_tree, hf_cdp_tlvtype, tvb,
145                             offset + TLV_TYPE, 2, type);
146                 proto_tree_add_uint(tlv_tree, hf_cdp_tlvlength, tvb,
147                             offset + TLV_LENGTH, 2, length);
148                 proto_tree_add_text(tlv_tree, tvb, offset + 4,
149                             length - 4, "Device ID: %.*s",
150                             length - 4,
151                             tvb_get_ptr(tvb, offset + 4, length - 4));
152                 offset += length;
153                 break;
154
155             case TYPE_ADDRESS:
156                 /* Addresses */
157                 tlvi = proto_tree_add_text(cdp_tree, tvb, offset,
158                             length, "Addresses");
159                 tlv_tree = proto_item_add_subtree(tlvi, ett_cdp_tlv);
160                 proto_tree_add_uint(tlv_tree, hf_cdp_tlvtype, tvb,
161                             offset + TLV_TYPE, 2, type);
162                 proto_tree_add_uint(tlv_tree, hf_cdp_tlvlength, tvb,
163                             offset + TLV_LENGTH, 2, length);
164                 offset += 4;
165                 length -= 4;
166                 naddresses = tvb_get_ntohl(tvb, offset);
167                 proto_tree_add_text(tlv_tree, tvb, offset, 4,
168                             "Number of addresses: %u", naddresses);
169                 offset += 4;
170                 length -= 4;
171                 while (naddresses != 0) {
172                     addr_length = dissect_address_tlv(tvb, offset, length,
173                                 tlv_tree);
174                     if (addr_length < 0)
175                         break;
176                     offset += addr_length;
177                     length -= addr_length;
178
179                     naddresses--;
180                 }
181                 offset += length;
182                 break;
183
184             case TYPE_PORT_ID:
185                 real_length = length;
186                 if (tvb_get_guint8(tvb, offset + real_length) != 0x00) {
187                     /* The length in the TLV doesn't appear to be the
188                        length of the TLV, as the byte just past it
189                        isn't the first byte of a 2-byte big-endian
190                        small integer; make the length of the TLV the length
191                        in the TLV, plus 4 bytes for the TLV type and length,
192                        minus 1 because that's what makes one capture work. */
193                     real_length = length + 3;
194                 }
195                 tlvi = proto_tree_add_text(cdp_tree, tvb, offset,
196                             real_length, "Port ID: %.*s",
197                             real_length - 4,
198                             tvb_get_ptr(tvb, offset + 4, real_length - 4));
199                 tlv_tree = proto_item_add_subtree(tlvi, ett_cdp_tlv);
200                 proto_tree_add_uint(tlv_tree, hf_cdp_tlvtype, tvb,
201                             offset + TLV_TYPE, 2, type);
202                 proto_tree_add_uint(tlv_tree, hf_cdp_tlvlength, tvb,
203                             offset + TLV_LENGTH, 2, length);
204                 proto_tree_add_text(tlv_tree, tvb, offset + 4,
205                             real_length - 4,
206                             "Sent through Interface: %.*s",
207                             real_length - 4,
208                             tvb_get_ptr(tvb, offset + 4, real_length - 4));
209                 offset += real_length;
210                 break;
211
212             case TYPE_CAPABILITIES:
213                 tlvi = proto_tree_add_text(cdp_tree, tvb, offset,
214                             length, "Capabilities");
215                 tlv_tree = proto_item_add_subtree(tlvi, ett_cdp_tlv);
216                 proto_tree_add_uint(tlv_tree, hf_cdp_tlvtype, tvb,
217                             offset + TLV_TYPE, 2, type);
218                 proto_tree_add_uint(tlv_tree, hf_cdp_tlvlength, tvb,
219                             offset + TLV_LENGTH, 2, length);
220                 offset += 4;
221                 length -= 4;
222                 dissect_capabilities(tvb, offset, length, tlv_tree);
223                 offset += length;
224                 break;
225
226             case TYPE_IOS_VERSION:
227                 tlvi = proto_tree_add_text(cdp_tree, tvb, offset,
228                             length, "Software Version");
229                 tlv_tree = proto_item_add_subtree(tlvi, ett_cdp_tlv);
230                 proto_tree_add_uint(tlv_tree, hf_cdp_tlvtype, tvb,
231                             offset + TLV_TYPE, 2, type);
232                 proto_tree_add_uint(tlv_tree, hf_cdp_tlvlength, tvb,
233                             offset + TLV_LENGTH, 2, length);
234                 add_multi_line_string_to_tree(tlv_tree, tvb, offset + 4,
235                                 length - 4, "Software Version: ");
236                 offset += length;
237                 break;
238
239             case TYPE_PLATFORM:
240                 /* ??? platform */
241                 tlvi = proto_tree_add_text(cdp_tree, tvb,
242                             offset, length, "Platform: %.*s",
243                             length - 4,
244                             tvb_get_ptr(tvb, offset + 4, length - 4));
245                 tlv_tree = proto_item_add_subtree(tlvi, ett_cdp_tlv);
246                 proto_tree_add_uint(tlv_tree, hf_cdp_tlvtype, tvb,
247                             offset + TLV_TYPE, 2, type);
248                 proto_tree_add_uint(tlv_tree, hf_cdp_tlvlength, tvb,
249                             offset + TLV_LENGTH, 2, length);
250                 proto_tree_add_text(tlv_tree, tvb, offset + 4,
251                             length - 4, "Platform: %.*s",
252                             length - 4,
253                             tvb_get_ptr(tvb, offset + 4, length - 4));
254                 offset += length;
255                 break;
256             case TYPE_IP_PREFIX:
257                 tlvi = proto_tree_add_text(cdp_tree, tvb, offset,
258                             length, "IP Prefixes: %d",length/5);
259
260                             /* the actual number of prefixes is (length-4)/5
261                             but if the variable is not a "float" but "integer"
262                             then length/5=(length-4)/5  :)  */
263                             
264                 tlv_tree = proto_item_add_subtree(tlvi, ett_cdp_tlv);
265                 proto_tree_add_uint(tlv_tree, hf_cdp_tlvtype, tvb,
266                             offset + TLV_TYPE, 2, type);  
267                 proto_tree_add_uint(tlv_tree, hf_cdp_tlvlength, tvb,
268                             offset + TLV_LENGTH, 2, length);
269                 offset += 4;
270                 length -= 4;
271                 while (length > 0) {
272                         proto_tree_add_text(tlv_tree, tvb, offset, 5,
273                                 "IP Prefix = %s/%u",
274                                 ip_to_str(tvb_get_ptr(tvb, offset, 4)),
275                                 tvb_get_guint8(tvb,offset+4));
276                         offset += 5;
277                         length -= 5;
278                 }
279                 break;
280             default:
281                 tlvi = proto_tree_add_text(cdp_tree, tvb, offset,
282                             length, "Type: %s, length: %u",
283                             val_to_str(type, type_vals, "Unknown (0x%04x)"),
284                             length);
285                 tlv_tree = proto_item_add_subtree(tlvi, ett_cdp_tlv);
286                 proto_tree_add_uint(tlv_tree, hf_cdp_tlvtype, tvb,
287                             offset + TLV_TYPE, 2, type);
288                 proto_tree_add_uint(tlv_tree, hf_cdp_tlvlength, tvb,
289                             offset + TLV_LENGTH, 2, length);
290                 if (length > 4) {
291                         proto_tree_add_text(tlv_tree, tvb, offset + 4,
292                                         length - 4, "Data");
293                 } else
294                         return;
295                 offset += length;
296             }
297         }
298         dissect_data(tvb, offset, pinfo, cdp_tree);
299     }
300 }
301
302 #define PROTO_TYPE_NLPID        1
303 #define PROTO_TYPE_IEEE_802_2   2
304
305 static const value_string proto_type_vals[] = {
306         { PROTO_TYPE_NLPID,      "NLPID" },
307         { PROTO_TYPE_IEEE_802_2, "802.2" },
308         { 0,                     NULL },
309 };
310
311 static int
312 dissect_address_tlv(tvbuff_t *tvb, int offset, int length, proto_tree *tree)
313 {
314     proto_item *ti;
315     proto_tree *address_tree;
316     guint8 protocol_type;
317     guint8 protocol_length;
318     int nlpid;
319     char *protocol_str;
320     guint16 address_length;
321     char *address_type_str;
322     char *address_str;
323
324     if (length < 1)
325         return -1;
326     ti = proto_tree_add_notext(tree, tvb, offset, length);
327     address_tree = proto_item_add_subtree(ti, ett_cdp_address);
328     protocol_type = tvb_get_guint8(tvb, offset);
329     proto_tree_add_text(address_tree, tvb, offset, 1, "Protocol type: %s",
330         val_to_str(protocol_type, proto_type_vals, "Unknown (0x%02x)"));
331     offset += 1;
332     length -= 1;
333
334     if (length < 1) {
335         proto_item_set_text(ti, "Truncated address");
336         return -1;
337     }
338     protocol_length = tvb_get_guint8(tvb, offset);
339     proto_tree_add_text(address_tree, tvb, offset, 1, "Protocol length: %u",
340                         protocol_length);
341     offset += 1;
342     length -= 1;
343
344     if (length < protocol_length) {
345         proto_item_set_text(ti, "Truncated address");
346         if (length != 0) {
347             proto_tree_add_text(address_tree, tvb, offset, length,
348               "Protocol: %s (truncated)",
349               tvb_bytes_to_str(tvb, offset, length));
350         }
351         return -1;
352     }
353     protocol_str = NULL;
354     if (protocol_type == PROTO_TYPE_NLPID && protocol_length == 1) {
355         nlpid = tvb_get_guint8(tvb, offset);
356         protocol_str = val_to_str(nlpid, nlpid_vals, "Unknown (0x%02x)");
357     } else
358         nlpid = -1;
359     if (protocol_str == NULL)
360         protocol_str = tvb_bytes_to_str(tvb, offset, protocol_length);
361     proto_tree_add_text(address_tree, tvb, offset, protocol_length,
362                         "Protocol: %s", protocol_str);
363     offset += protocol_length;
364     length -= protocol_length;
365
366     if (length < 2) {
367         proto_item_set_text(ti, "Truncated address");
368         return -1;
369     }
370     address_length = tvb_get_ntohs(tvb, offset);
371     proto_tree_add_text(address_tree, tvb, offset, 2, "Address length: %u",
372                         address_length);
373     offset += 2;
374     length -= 2;
375
376     if (length < address_length) {
377         proto_item_set_text(ti, "Truncated address");
378         if (length != 0) {
379             proto_tree_add_text(address_tree, tvb, offset, length,
380               "Address: %s (truncated)",
381               tvb_bytes_to_str(tvb, offset, length));
382         }
383         return -1;
384     }
385     /* XXX - the Cisco document seems to be saying that, for 802.2-format
386        protocol types, 0xAAAA03 0x000000 0x0800 is IPv6, but 0x0800 is
387        the Ethernet protocol type for IPv4. */
388     length = 2 + protocol_length + 2 + address_length;
389     address_type_str = NULL;
390     address_str = NULL;
391     if (protocol_type == PROTO_TYPE_NLPID && protocol_length == 1) {
392         switch (nlpid) {
393
394         /* XXX - dissect NLPID_ISO8473_CLNP as OSI CLNP address? */
395
396         case NLPID_IP:
397             if (address_length == 4) {
398                 /* The address is an IP address. */
399                 address_type_str = "IP address";
400                 address_str = ip_to_str(tvb_get_ptr(tvb, offset, 4));
401             }
402             break;
403         }
404     }
405     if (address_type_str == NULL)
406         address_type_str = "Address";
407     if (address_str == NULL) {
408         address_str = tvb_bytes_to_str(tvb, offset, address_length);
409     }
410     proto_item_set_text(ti, "%s: %s", address_type_str, address_str);
411     proto_tree_add_text(address_tree, tvb, offset, address_length, "%s: %s",
412       address_type_str, address_str);
413     return 2 + protocol_length + 2 + address_length;
414 }
415
416 static void
417 dissect_capabilities(tvbuff_t *tvb, int offset, int length, proto_tree *tree)
418 {
419     proto_item *ti;
420     proto_tree *capabilities_tree;
421     guint32 capabilities;
422
423     if (length < 4)
424         return;
425     capabilities = tvb_get_ntohl(tvb, offset);
426     ti = proto_tree_add_text(tree, tvb, offset, length, "Capabilities: 0x%08x",
427         capabilities);
428     capabilities_tree = proto_item_add_subtree(ti, ett_cdp_capabilities);
429     proto_tree_add_text(capabilities_tree, tvb, offset, 4,
430         decode_boolean_bitfield(capabilities, 0x01, 4*8,
431             "Performs level 3 routing",
432             "Doesn't perform level 3 routing"));
433     proto_tree_add_text(capabilities_tree, tvb, offset, 4,
434         decode_boolean_bitfield(capabilities, 0x02, 4*8,
435             "Performs level 2 transparent bridging",
436             "Doesn't perform level 2 transparent bridging"));
437     proto_tree_add_text(capabilities_tree, tvb, offset, 4,
438         decode_boolean_bitfield(capabilities, 0x04, 4*8,
439             "Performs level 2 source-route bridging",
440             "Doesn't perform level 2 source-route bridging"));
441     proto_tree_add_text(capabilities_tree, tvb, offset, 4,
442         decode_boolean_bitfield(capabilities, 0x08, 4*8,
443             "Performs level 2 switching",
444             "Doesn't perform level 2 switching"));
445     proto_tree_add_text(capabilities_tree, tvb, offset, 4,
446         decode_boolean_bitfield(capabilities, 0x10, 4*8,
447             "Sends and receives packets for network-layer protocols",
448             "Doesn't send or receive packets for network-layer protocols"));
449     proto_tree_add_text(capabilities_tree, tvb, offset, 4,
450         decode_boolean_bitfield(capabilities, 0x20, 4*8,
451             "Doesn't forward IGMP Report packets on nonrouter ports",
452             "Forwards IGMP Report packets on nonrouter ports"));
453     proto_tree_add_text(capabilities_tree, tvb, offset, 4,
454         decode_boolean_bitfield(capabilities, 0x40, 4*8,
455             "Provides level 1 functionality",
456             "Doesn't provide level 1 functionality"));
457 }
458
459 static void
460 add_multi_line_string_to_tree(proto_tree *tree, tvbuff_t *tvb, gint start,
461   gint len, const gchar *prefix)
462 {
463     int prefix_len;
464     int i;
465     char blanks[64+1];
466     gint next;
467     int line_len;
468     int data_len;
469
470     prefix_len = strlen(prefix);
471     if (prefix_len > 64)
472         prefix_len = 64;
473     for (i = 0; i < prefix_len; i++)
474         blanks[i] = ' ';
475     blanks[i] = '\0';
476     while (len > 0) {
477         line_len = tvb_find_line_end(tvb, start, len, &next);
478         data_len = next - start;
479         proto_tree_add_text(tree, tvb, start, data_len, "%s%.*s", prefix,
480            line_len, tvb_get_ptr(tvb, start, line_len));
481         start += data_len;
482         len -= data_len;
483         prefix = blanks;
484     }
485 }
486
487 void
488 proto_register_cdp(void)
489 {
490     static hf_register_info hf[] = {
491         { &hf_cdp_version,
492         { "Version",            "cdp.version",  FT_UINT8, BASE_DEC, NULL, 0x0,
493           "" }},
494
495         { &hf_cdp_ttl,
496         { "TTL",                "cdp.ttl", FT_UINT16, BASE_DEC, NULL, 0x0,
497           "" }},
498
499         { &hf_cdp_checksum,
500         { "Checksum",           "cdp.checksum", FT_UINT16, BASE_HEX, NULL, 0x0,
501           "" }},
502
503         { &hf_cdp_tlvtype,
504         { "Type",               "cdp.tlv.type", FT_UINT16, BASE_HEX, VALS(type_vals), 0x0,
505           "" }},
506
507         { &hf_cdp_tlvlength,
508         { "Length",             "cdp.tlv.len", FT_UINT16, BASE_DEC, NULL, 0x0,
509           "" }},
510     };
511     static gint *ett[] = {
512         &ett_cdp,
513         &ett_cdp_tlv,
514         &ett_cdp_address,
515         &ett_cdp_capabilities,
516     };
517
518     proto_cdp = proto_register_protocol("Cisco Discovery Protocol",
519                                         "CDP", "cdp");
520     proto_register_field_array(proto_cdp, hf, array_length(hf));
521     proto_register_subtree_array(ett, array_length(ett));
522 }
523
524 void
525 proto_reg_handoff_cdp(void)
526 {
527     dissector_add("llc.cisco_pid", 0x2000, dissect_cdp);
528 }