Tvbuffify the BOOTP/DHCP dissector.
[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.29 2001/01/03 06:55:27 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
80 static const value_string type_vals[] = {
81         { TYPE_DEVICE_ID,    "Device ID" },
82         { TYPE_ADDRESS,      "Addresses" },
83         { TYPE_PORT_ID,      "Port ID" },
84         { TYPE_CAPABILITIES, "Capabilities" },
85         { TYPE_IOS_VERSION,  "Software version" },
86         { TYPE_PLATFORM,     "Platform" },
87         { 0,                 NULL },
88 };
89         
90 static void 
91 dissect_cdp(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree)
92 {
93     proto_item *ti; 
94     proto_tree *cdp_tree = NULL;
95     int offset = 0;
96     guint16 type;
97     guint16 length;
98     proto_item *tlvi;
99     proto_tree *tlv_tree;
100     int real_length;
101     guint32 naddresses;
102     int addr_length;
103
104     CHECK_DISPLAY_AS_DATA(proto_cdp, tvb, pinfo, tree);
105
106     pinfo->current_proto = "CDP";
107
108     if (check_col(pinfo->fd, COL_PROTOCOL))
109         col_set_str(pinfo->fd, COL_PROTOCOL, "CDP");
110     if (check_col(pinfo->fd, COL_INFO))
111         col_set_str(pinfo->fd, COL_INFO, "Cisco Discovery Protocol"); 
112
113     if (tree){
114         ti = proto_tree_add_item(tree, proto_cdp, tvb, offset,
115                                  tvb_length_remaining(tvb, offset), FALSE);
116         cdp_tree = proto_item_add_subtree(ti, ett_cdp);
117         
118         /* CDP header */
119         proto_tree_add_item(cdp_tree, hf_cdp_version, tvb, offset, 1, FALSE);
120         offset += 1;
121         proto_tree_add_uint_format(cdp_tree, hf_cdp_ttl, tvb, offset, 1,
122                                    tvb_get_guint8(tvb, offset),
123                                    "TTL: %u seconds",
124                                    tvb_get_guint8(tvb, offset));
125         offset += 1;
126         proto_tree_add_item(cdp_tree, hf_cdp_checksum, tvb, offset, 2, FALSE);
127         offset += 2;
128
129         while (tvb_reported_length_remaining(tvb, offset) != 0) {
130             type = tvb_get_ntohs(tvb, offset + TLV_TYPE);
131             length = tvb_get_ntohs(tvb, offset + TLV_LENGTH);
132
133             switch (type) {
134
135             case TYPE_DEVICE_ID:
136                 /* Device ID */
137                 tlvi = proto_tree_add_text(cdp_tree, tvb, offset,
138                             length, "Device ID: %.*s",
139                             length - 4,
140                             tvb_get_ptr(tvb, offset + 4, length - 4));
141                 tlv_tree = proto_item_add_subtree(tlvi, ett_cdp_tlv);
142                 proto_tree_add_uint(tlv_tree, hf_cdp_tlvtype, tvb,
143                             offset + TLV_TYPE, 2, type);
144                 proto_tree_add_uint(tlv_tree, hf_cdp_tlvlength, tvb,
145                             offset + TLV_LENGTH, 2, length);
146                 proto_tree_add_text(tlv_tree, tvb, offset + 4,
147                             length - 4, "Device ID: %.*s",
148                             length - 4,
149                             tvb_get_ptr(tvb, offset + 4, length - 4));
150                 offset += length;
151                 break;
152
153             case TYPE_ADDRESS:
154                 /* Addresses */
155                 tlvi = proto_tree_add_text(cdp_tree, tvb, offset,
156                             length, "Addresses");
157                 tlv_tree = proto_item_add_subtree(tlvi, ett_cdp_tlv);
158                 proto_tree_add_uint(tlv_tree, hf_cdp_tlvtype, tvb,
159                             offset + TLV_TYPE, 2, type);
160                 proto_tree_add_uint(tlv_tree, hf_cdp_tlvlength, tvb,
161                             offset + TLV_LENGTH, 2, length);
162                 offset += 4;
163                 length -= 4;
164                 naddresses = tvb_get_ntohl(tvb, offset);
165                 proto_tree_add_text(tlv_tree, tvb, offset, 4,
166                             "Number of addresses: %u", naddresses);
167                 offset += 4;
168                 length -= 4;
169                 while (naddresses != 0) {
170                     addr_length = dissect_address_tlv(tvb, offset, length,
171                                 tlv_tree);
172                     if (addr_length < 0)
173                         break;
174                     offset += addr_length;
175                     length -= addr_length;
176
177                     naddresses--;
178                 }
179                 offset += length;
180                 break;
181
182             case TYPE_PORT_ID:
183                 real_length = length;
184                 if (tvb_get_guint8(tvb, offset + real_length) != 0x00) {
185                     /* The length in the TLV doesn't appear to be the
186                        length of the TLV, as the byte just past it
187                        isn't the first byte of a 2-byte big-endian
188                        small integer; make the length of the TLV the length
189                        in the TLV, plus 4 bytes for the TLV type and length,
190                        minus 1 because that's what makes one capture work. */
191                     real_length = length + 3;
192                 }
193                 tlvi = proto_tree_add_text(cdp_tree, tvb, offset,
194                             real_length, "Port ID: %.*s",
195                             real_length - 4,
196                             tvb_get_ptr(tvb, offset + 4, real_length - 4));
197                 tlv_tree = proto_item_add_subtree(tlvi, ett_cdp_tlv);
198                 proto_tree_add_uint(tlv_tree, hf_cdp_tlvtype, tvb,
199                             offset + TLV_TYPE, 2, type);
200                 proto_tree_add_uint(tlv_tree, hf_cdp_tlvlength, tvb,
201                             offset + TLV_LENGTH, 2, length);
202                 proto_tree_add_text(tlv_tree, tvb, offset + 4,
203                             real_length - 4,
204                             "Sent through Interface: %.*s",
205                             real_length - 4,
206                             tvb_get_ptr(tvb, offset + 4, real_length - 4));
207                 offset += real_length;
208                 break;
209
210             case TYPE_CAPABILITIES:
211                 tlvi = proto_tree_add_text(cdp_tree, tvb, offset,
212                             length, "Capabilities");
213                 tlv_tree = proto_item_add_subtree(tlvi, ett_cdp_tlv);
214                 proto_tree_add_uint(tlv_tree, hf_cdp_tlvtype, tvb,
215                             offset + TLV_TYPE, 2, type);
216                 proto_tree_add_uint(tlv_tree, hf_cdp_tlvlength, tvb,
217                             offset + TLV_LENGTH, 2, length);
218                 offset += 4;
219                 length -= 4;
220                 dissect_capabilities(tvb, offset, length, tlv_tree);
221                 offset += length;
222                 break;
223
224             case TYPE_IOS_VERSION:
225                 tlvi = proto_tree_add_text(cdp_tree, tvb, offset,
226                             length, "Software Version");
227                 tlv_tree = proto_item_add_subtree(tlvi, ett_cdp_tlv);
228                 proto_tree_add_uint(tlv_tree, hf_cdp_tlvtype, tvb,
229                             offset + TLV_TYPE, 2, type);
230                 proto_tree_add_uint(tlv_tree, hf_cdp_tlvlength, tvb,
231                             offset + TLV_LENGTH, 2, length);
232                 add_multi_line_string_to_tree(tlv_tree, tvb, offset + 4,
233                                 length - 4, "Software Version: ");
234                 offset += length;
235                 break;
236
237             case TYPE_PLATFORM:
238                 /* ??? platform */
239                 tlvi = proto_tree_add_text(cdp_tree, tvb,
240                             offset, length, "Platform: %.*s",
241                             length - 4,
242                             tvb_get_ptr(tvb, offset + 4, length - 4));
243                 tlv_tree = proto_item_add_subtree(tlvi, ett_cdp_tlv);
244                 proto_tree_add_uint(tlv_tree, hf_cdp_tlvtype, tvb,
245                             offset + TLV_TYPE, 2, type);
246                 proto_tree_add_uint(tlv_tree, hf_cdp_tlvlength, tvb,
247                             offset + TLV_LENGTH, 2, length);
248                 proto_tree_add_text(tlv_tree, tvb, offset + 4,
249                             length - 4, "Platform: %.*s",
250                             length - 4,
251                             tvb_get_ptr(tvb, offset + 4, length - 4));
252                 offset += length;
253                 break;
254
255             default:
256                 tlvi = proto_tree_add_text(cdp_tree, tvb, offset,
257                             length, "Type: %s, length: %u",
258                             val_to_str(type, type_vals, "Unknown (0x%04x)"),
259                             length);
260                 tlv_tree = proto_item_add_subtree(tlvi, ett_cdp_tlv);
261                 proto_tree_add_uint(tlv_tree, hf_cdp_tlvtype, tvb,
262                             offset + TLV_TYPE, 2, type);
263                 proto_tree_add_uint(tlv_tree, hf_cdp_tlvlength, tvb,
264                             offset + TLV_LENGTH, 2, length);
265                 if (length > 4) {
266                         proto_tree_add_text(tlv_tree, tvb, offset + 4,
267                                         length - 4, "Data");
268                 } else
269                         return;
270                 offset += length;
271             }
272         }
273         dissect_data(tvb, offset, pinfo, cdp_tree);
274     }
275 }
276
277 #define PROTO_TYPE_NLPID        1
278 #define PROTO_TYPE_IEEE_802_2   2
279
280 static const value_string proto_type_vals[] = {
281         { PROTO_TYPE_NLPID,      "NLPID" },
282         { PROTO_TYPE_IEEE_802_2, "802.2" },
283         { 0,                     NULL },
284 };
285
286 static int
287 dissect_address_tlv(tvbuff_t *tvb, int offset, int length, proto_tree *tree)
288 {
289     proto_item *ti;
290     proto_tree *address_tree;
291     guint8 protocol_type;
292     guint8 protocol_length;
293     int nlpid;
294     char *protocol_str;
295     guint16 address_length;
296     char *address_type_str;
297     char *address_str;
298
299     if (length < 1)
300         return -1;
301     ti = proto_tree_add_notext(tree, tvb, offset, length);
302     address_tree = proto_item_add_subtree(ti, ett_cdp_address);
303     protocol_type = tvb_get_guint8(tvb, offset);
304     proto_tree_add_text(address_tree, tvb, offset, 1, "Protocol type: %s",
305         val_to_str(protocol_type, proto_type_vals, "Unknown (0x%02x)"));
306     offset += 1;
307     length -= 1;
308
309     if (length < 1) {
310         proto_item_set_text(ti, "Truncated address");
311         return -1;
312     }
313     protocol_length = tvb_get_guint8(tvb, offset);
314     proto_tree_add_text(address_tree, tvb, offset, 1, "Protocol length: %u",
315                         protocol_length);
316     offset += 1;
317     length -= 1;
318
319     if (length < protocol_length) {
320         proto_item_set_text(ti, "Truncated address");
321         if (length != 0) {
322             proto_tree_add_text(address_tree, tvb, offset, length,
323               "Protocol: %s (truncated)",
324               tvb_bytes_to_str(tvb, offset, length));
325         }
326         return -1;
327     }
328     protocol_str = NULL;
329     if (protocol_type == PROTO_TYPE_NLPID && protocol_length == 1) {
330         nlpid = tvb_get_guint8(tvb, offset);
331         protocol_str = val_to_str(nlpid, nlpid_vals, "Unknown (0x%02x)");
332     } else
333         nlpid = -1;
334     if (protocol_str == NULL)
335         protocol_str = tvb_bytes_to_str(tvb, offset, protocol_length);
336     proto_tree_add_text(address_tree, tvb, offset, protocol_length,
337                         "Protocol: %s", protocol_str);
338     offset += protocol_length;
339     length -= protocol_length;
340
341     if (length < 2) {
342         proto_item_set_text(ti, "Truncated address");
343         return -1;
344     }
345     address_length = tvb_get_ntohs(tvb, offset);
346     proto_tree_add_text(address_tree, tvb, offset, 2, "Address length: %u",
347                         address_length);
348     offset += 2;
349     length -= 2;
350
351     if (length < address_length) {
352         proto_item_set_text(ti, "Truncated address");
353         if (length != 0) {
354             proto_tree_add_text(address_tree, tvb, offset, length,
355               "Address: %s (truncated)",
356               tvb_bytes_to_str(tvb, offset, length));
357         }
358         return -1;
359     }
360     /* XXX - the Cisco document seems to be saying that, for 802.2-format
361        protocol types, 0xAAAA03 0x000000 0x0800 is IPv6, but 0x0800 is
362        the Ethernet protocol type for IPv4. */
363     length = 2 + protocol_length + 2 + address_length;
364     address_type_str = NULL;
365     address_str = NULL;
366     if (protocol_type == PROTO_TYPE_NLPID && protocol_length == 1) {
367         switch (nlpid) {
368
369         /* XXX - dissect NLPID_ISO8473_CLNP as OSI CLNP address? */
370
371         case NLPID_IP:
372             if (address_length == 4) {
373                 /* The address is an IP address. */
374                 address_type_str = "IP address";
375                 address_str = ip_to_str(tvb_get_ptr(tvb, offset, 4));
376             }
377             break;
378         }
379     }
380     if (address_type_str == NULL)
381         address_type_str = "Address";
382     if (address_str == NULL) {
383         address_str = tvb_bytes_to_str(tvb, offset, address_length);
384     }
385     proto_item_set_text(ti, "%s: %s", address_type_str, address_str);
386     proto_tree_add_text(address_tree, tvb, offset, address_length, "%s: %s",
387       address_type_str, address_str);
388     return 2 + protocol_length + 2 + address_length;
389 }
390
391 static void
392 dissect_capabilities(tvbuff_t *tvb, int offset, int length, proto_tree *tree)
393 {
394     proto_item *ti;
395     proto_tree *capabilities_tree;
396     guint32 capabilities;
397
398     if (length < 4)
399         return;
400     capabilities = tvb_get_ntohl(tvb, offset);
401     ti = proto_tree_add_text(tree, tvb, offset, length, "Capabilities: 0x%08x",
402         capabilities);
403     capabilities_tree = proto_item_add_subtree(ti, ett_cdp_capabilities);
404     proto_tree_add_text(capabilities_tree, tvb, offset, 4,
405         decode_boolean_bitfield(capabilities, 0x01, 4*8,
406             "Performs level 3 routing",
407             "Doesn't perform level 3 routing"));
408     proto_tree_add_text(capabilities_tree, tvb, offset, 4,
409         decode_boolean_bitfield(capabilities, 0x02, 4*8,
410             "Performs level 2 transparent bridging",
411             "Doesn't perform level 2 transparent bridging"));
412     proto_tree_add_text(capabilities_tree, tvb, offset, 4,
413         decode_boolean_bitfield(capabilities, 0x04, 4*8,
414             "Performs level 2 source-route bridging",
415             "Doesn't perform level 2 source-route bridging"));
416     proto_tree_add_text(capabilities_tree, tvb, offset, 4,
417         decode_boolean_bitfield(capabilities, 0x08, 4*8,
418             "Performs level 2 switching",
419             "Doesn't perform level 2 switching"));
420     proto_tree_add_text(capabilities_tree, tvb, offset, 4,
421         decode_boolean_bitfield(capabilities, 0x10, 4*8,
422             "Sends and receives packets for network-layer protocols",
423             "Doesn't send or receive packets for network-layer protocols"));
424     proto_tree_add_text(capabilities_tree, tvb, offset, 4,
425         decode_boolean_bitfield(capabilities, 0x20, 4*8,
426             "Doesn't forward IGMP Report packets on nonrouter ports",
427             "Forwards IGMP Report packets on nonrouter ports"));
428     proto_tree_add_text(capabilities_tree, tvb, offset, 4,
429         decode_boolean_bitfield(capabilities, 0x40, 4*8,
430             "Provides level 1 functionality",
431             "Doesn't provide level 1 functionality"));
432 }
433
434 static void
435 add_multi_line_string_to_tree(proto_tree *tree, tvbuff_t *tvb, gint start,
436   gint len, const gchar *prefix)
437 {
438     int prefix_len;
439     int i;
440     char blanks[64+1];
441     gint next;
442     int line_len;
443     int data_len;
444
445     prefix_len = strlen(prefix);
446     if (prefix_len > 64)
447         prefix_len = 64;
448     for (i = 0; i < prefix_len; i++)
449         blanks[i] = ' ';
450     blanks[i] = '\0';
451     while (len > 0) {
452         line_len = tvb_find_line_end(tvb, start, len, &next);
453         data_len = next - start;
454         proto_tree_add_text(tree, tvb, start, data_len, "%s%.*s", prefix,
455            line_len, tvb_get_ptr(tvb, start, line_len));
456         start += data_len;
457         len -= data_len;
458         prefix = blanks;
459     }
460 }
461
462 void
463 proto_register_cdp(void)
464 {
465     static hf_register_info hf[] = {
466         { &hf_cdp_version,
467         { "Version",            "cdp.version",  FT_UINT8, BASE_DEC, NULL, 0x0,
468           "" }},
469
470         { &hf_cdp_ttl,
471         { "TTL",                "cdp.ttl", FT_UINT16, BASE_DEC, NULL, 0x0,
472           "" }},
473
474         { &hf_cdp_checksum,
475         { "Checksum",           "cdp.checksum", FT_UINT16, BASE_HEX, NULL, 0x0,
476           "" }},
477
478         { &hf_cdp_tlvtype,
479         { "Type",               "cdp.tlv.type", FT_UINT16, BASE_HEX, VALS(type_vals), 0x0,
480           "" }},
481
482         { &hf_cdp_tlvlength,
483         { "Length",             "cdp.tlv.len", FT_UINT16, BASE_DEC, NULL, 0x0,
484           "" }},
485     };
486     static gint *ett[] = {
487         &ett_cdp,
488         &ett_cdp_tlv,
489         &ett_cdp_address,
490         &ett_cdp_capabilities,
491     };
492
493     proto_cdp = proto_register_protocol("Cisco Discovery Protocol",
494                                         "CDP", "cdp");
495     proto_register_field_array(proto_cdp, hf, array_length(hf));
496     proto_register_subtree_array(ett, array_length(ett));
497 }
498
499 void
500 proto_reg_handoff_cdp(void)
501 {
502     dissector_add("llc.cisco_pid", 0x2000, dissect_cdp);
503 }