0x2000, for CDP, doesn't appear to be an Ethernet type - it's not
[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.18 2000/01/13 00:41:09 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 "nlpid.h"
39
40 /*
41  * See
42  *
43  *      http://www.cisco.com/univercd/cc/td/doc/product/lan/trsrb/frames.htm
44  *
45  * for some information on CDP.
46  */
47
48 /* Offsets in TLV structure. */
49 #define TLV_TYPE        0
50 #define TLV_LENGTH      2
51
52 static int proto_cdp = -1;
53 static int hf_cdp_version = -1;
54 static int hf_cdp_flags = -1;
55 static int hf_cdp_ttl = -1;
56 static int hf_cdp_tlvtype = -1;
57 static int hf_cdp_tlvlength = -1;
58
59 static gint ett_cdp = -1;
60 static gint ett_cdp_tlv = -1;
61 static gint ett_cdp_address = -1;
62 static gint ett_cdp_capabilities = -1;
63
64 static int
65 dissect_address_tlv(const u_char *pd, int offset, int length, proto_tree *tree);
66 static void
67 dissect_capabilities(const u_char *pd, int offset, int length, proto_tree *tree);
68 static void
69 add_multi_line_string_to_tree(proto_tree *tree, gint start, gint len,
70   const gchar *prefix, const gchar *string);
71
72 #define TYPE_DEVICE_ID          0x0001
73 #define TYPE_ADDRESS            0x0002
74 #define TYPE_PORT_ID            0x0003
75 #define TYPE_CAPABILITIES       0x0004
76 #define TYPE_IOS_VERSION        0x0005
77 #define TYPE_PLATFORM           0x0006
78
79 static const value_string type_vals[] = {
80         { TYPE_DEVICE_ID,    "Device ID" },
81         { TYPE_ADDRESS,      "Addresses" },
82         { TYPE_PORT_ID,      "Port ID" },
83         { TYPE_CAPABILITIES, "Capabilities" },
84         { TYPE_IOS_VERSION,  "Software version" },
85         { TYPE_PLATFORM,     "Platform" },
86         { 0,                 NULL },
87 };
88         
89 void 
90 dissect_cdp(const u_char *pd, int offset, frame_data *fd, proto_tree *tree)
91 {
92     proto_item *ti; 
93     proto_tree *cdp_tree = NULL;
94     guint16 type;
95     guint16 length;
96     char *type_str;
97     char *stringmem;
98     proto_item *tlvi;
99     proto_tree *tlv_tree;
100     int real_length;
101     guint32 naddresses;
102     int addr_length;
103
104     if (check_col(fd, COL_PROTOCOL))
105         col_add_str(fd, COL_PROTOCOL, "CDP");
106     if (check_col(fd, COL_INFO))
107         col_add_str(fd, COL_INFO, "Cisco Discovery Protocol"); 
108
109     if(tree){
110         ti = proto_tree_add_item(tree, proto_cdp, offset, END_OF_FRAME, NULL);
111         cdp_tree = proto_item_add_subtree(ti, ett_cdp);
112         
113         /* CDP header */
114         proto_tree_add_item(cdp_tree, hf_cdp_version, offset, 1, pd[offset]);
115         offset += 1;
116         proto_tree_add_item_format(cdp_tree, hf_cdp_ttl, offset, 1,
117                                    pntohs(&pd[offset]),
118                                    "TTL: %u seconds", pd[offset]);
119         offset += 1;
120         proto_tree_add_item_format(cdp_tree, hf_cdp_flags, offset, 2,
121                                    pd[offset], 
122                                    "Checksum: 0x%04x", pntohs(&pd[offset]));
123         offset += 2;
124
125         while( IS_DATA_IN_FRAME(offset) ){
126                 type = pntohs(&pd[offset + TLV_TYPE]);
127                 length = pntohs(&pd[offset + TLV_LENGTH]);
128                 type_str = val_to_str(type, type_vals,
129                     "Unknown (0x%04x)");
130
131                 switch( type ){
132                         case TYPE_DEVICE_ID:
133                                 /* Device ID */
134                                 tlvi = proto_tree_add_text(cdp_tree, offset,
135                                     length, "Device ID: %s",
136                                     &pd[offset+4]);
137                                 tlv_tree = proto_item_add_subtree(tlvi,
138                                     ett_cdp_tlv);
139                                 proto_tree_add_item(tlv_tree, hf_cdp_tlvtype,
140                                     offset + TLV_TYPE, 2, type);
141                                 proto_tree_add_item(tlv_tree, hf_cdp_tlvlength,
142                                     offset + TLV_LENGTH, 2, length);
143                                 proto_tree_add_text(tlv_tree, offset + 4,
144                                     length - 4, "Device ID: %s",
145                                     &pd[offset+4]);
146                                 offset+=length;
147                                 break;
148                         case TYPE_ADDRESS:
149                                 /* Addresses */
150                                 tlvi = proto_tree_add_text(cdp_tree, offset,
151                                     4, "Address",
152                                     type, length);
153                                 tlv_tree = proto_item_add_subtree(tlvi,
154                                     ett_cdp_tlv);
155                                 proto_tree_add_item(tlv_tree, hf_cdp_tlvtype,
156                                     offset + TLV_TYPE, 2, type);
157                                 proto_tree_add_item(tlv_tree, hf_cdp_tlvlength,
158                                     offset + TLV_LENGTH, 2, length);
159                                 offset += 4;
160                                 length -= 4;
161                                 naddresses = pntohl(&pd[offset]);
162                                 proto_tree_add_text(tlv_tree, offset, 4,
163                                     "Number of addresses: %u", naddresses);
164                                 offset += 4;
165                                 length -= 4;
166                                 while (naddresses != 0) {
167                                     addr_length = dissect_address_tlv(pd,
168                                         offset, length, tlv_tree);
169                                     if (addr_length < 0)
170                                         break;
171                                     offset += addr_length;
172                                     length -= addr_length;
173
174                                     naddresses--;
175                                 }
176                                 offset += length;
177                                 break;
178                         case TYPE_PORT_ID:
179                                 real_length = length;
180                                 if (pd[offset + real_length] != 0x00) {
181                                     /* The length in the TLV doesn't
182                                        appear to be the length of the
183                                        TLV, as the byte just past it
184                                        isn't the first byte of a 2-byte
185                                        big-endian small integer; make
186                                        the length of the TLV the length
187                                        in the TLV, plus 4 bytes for the
188                                        TLV type and length, minus 1
189                                        because that's what makes one
190                                        capture work. */
191                                     real_length = length + 3;
192                                 }
193                                 tlvi = proto_tree_add_text(cdp_tree, offset,
194                                     real_length, "Port ID: %s",
195                                     &pd[offset+4]);
196                                 tlv_tree = proto_item_add_subtree(tlvi,
197                                     ett_cdp_tlv);
198                                 proto_tree_add_item(tlv_tree, hf_cdp_tlvtype,
199                                     offset + TLV_TYPE, 2, type);
200                                 proto_tree_add_item(tlv_tree, hf_cdp_tlvlength,
201                                     offset + TLV_LENGTH, 2, length);
202                                 proto_tree_add_text(tlv_tree, offset + 4,
203                                     real_length - 4,
204                                     "Sent through Interface: %s",
205                                     &pd[offset+4]);
206                                 offset += real_length;
207                                 break;
208                         case TYPE_CAPABILITIES:
209                                 tlvi = proto_tree_add_text(cdp_tree, offset,
210                                     length, "Capabilities");
211                                 tlv_tree = proto_item_add_subtree(tlvi,
212                                     ett_cdp_tlv);
213                                 proto_tree_add_item(tlv_tree, hf_cdp_tlvtype,
214                                     offset + TLV_TYPE, 2, type);
215                                 proto_tree_add_item(tlv_tree, hf_cdp_tlvlength,
216                                     offset + TLV_LENGTH, 2, length);
217                                 offset += 4;
218                                 length -= 4;
219                                 dissect_capabilities(pd, offset, length,
220                                     tlv_tree);
221                                 offset += length;
222                                 break;
223                         case TYPE_IOS_VERSION:
224                                 tlvi = proto_tree_add_text(cdp_tree, offset,
225                                     length, "Software Version");
226                                 tlv_tree = proto_item_add_subtree(tlvi,
227                                     ett_cdp_tlv);
228                                 proto_tree_add_item(tlv_tree, hf_cdp_tlvtype,
229                                     offset + TLV_TYPE, 2, type);
230                                 proto_tree_add_item(tlv_tree, hf_cdp_tlvlength,
231                                     offset + TLV_LENGTH, 2, length);
232                                 add_multi_line_string_to_tree(tlv_tree,
233                                     offset + 4, length - 4, "Software Version: ",
234                                     &pd[offset+4] );
235                                 offset += length;
236                                 break;
237                         case TYPE_PLATFORM:
238                                 /* ??? platform */
239                                 stringmem = malloc(length);
240                                 memset(stringmem, '\0', length);
241                                 memcpy(stringmem, &pd[offset+4], length - 4 );
242                                 tlvi = proto_tree_add_text(cdp_tree,
243                                     offset, length, "Platform: %s",
244                                     stringmem);
245                                 tlv_tree = proto_item_add_subtree(tlvi,
246                                     ett_cdp_tlv);
247                                 proto_tree_add_item(tlv_tree, hf_cdp_tlvtype,
248                                     offset + TLV_TYPE, 2, type);
249                                 proto_tree_add_item(tlv_tree, hf_cdp_tlvlength,
250                                     offset + TLV_LENGTH, 2, length);
251                                 proto_tree_add_text(tlv_tree, offset + 4,
252                                     length - 4, "Platform: %s", stringmem);
253                                 free(stringmem);
254                                 offset+=length;
255                                 break;
256                         default:
257                                 tlvi = proto_tree_add_text(cdp_tree, offset,
258                                     length, "Type: %s, length: %u",
259                                     type_str, length);
260                                 tlv_tree = proto_item_add_subtree(tlvi,
261                                     ett_cdp_tlv);
262                                 proto_tree_add_item(tlv_tree, hf_cdp_tlvtype,
263                                     offset + TLV_TYPE, 2, type);
264                                 proto_tree_add_item(tlv_tree, hf_cdp_tlvlength,
265                                     offset + TLV_LENGTH, 2, length);
266                                 if (length > 4) {
267                                         proto_tree_add_text(tlv_tree,
268                                             offset + 4, length - 4, "Data");
269                                 } else
270                                         return;
271                                 offset+=length;
272                 }
273         }
274         dissect_data(pd, offset, fd, cdp_tree);
275     }
276 }
277
278 #define PROTO_TYPE_NLPID        1
279 #define PROTO_TYPE_IEEE_802_2   2
280
281 static const value_string proto_type_vals[] = {
282         { PROTO_TYPE_NLPID,      "NLPID" },
283         { PROTO_TYPE_IEEE_802_2, "802.2" },
284         { 0,                     NULL },
285 };
286
287 static int
288 dissect_address_tlv(const u_char *pd, int offset, int length, proto_tree *tree)
289 {
290     proto_item *ti;
291     proto_tree *address_tree;
292     guint8 protocol_type;
293     guint8 protocol_length;
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     protocol_type = pd[offset];
302     if (length < 2) {
303         ti = proto_tree_add_text(tree, offset, length, "Truncated address");
304         address_tree = proto_item_add_subtree(ti, ett_cdp_address);
305         proto_tree_add_text(address_tree, offset, 1, "Protocol type: %s",
306           val_to_str(protocol_type, proto_type_vals, "Unknown (0x%02x)"));
307         return -1;
308     }
309     protocol_length = pd[offset + 1];
310     if (length < 2 + protocol_length) {
311         ti = proto_tree_add_text(tree, offset, length, "Truncated address");
312         address_tree = proto_item_add_subtree(ti, ett_cdp_address);
313         proto_tree_add_text(address_tree, offset, 1, "Protocol type: %s",
314           val_to_str(protocol_type, proto_type_vals, "Unknown (0x%02x)"));
315         offset += 1;
316         proto_tree_add_text(address_tree, offset, 1, "Protocol length: %u",
317           protocol_length);
318         return -1;
319     }
320
321     if (length < 2 + protocol_length) {
322         ti = proto_tree_add_text(tree, offset, length, "Truncated address");
323         address_tree = proto_item_add_subtree(ti, ett_cdp_address);
324         proto_tree_add_text(address_tree, offset, 1, "Protocol type: %s",
325           val_to_str(protocol_type, proto_type_vals, "Unknown (0x%02x)"));
326         offset += 1;
327         length -= 1;
328         proto_tree_add_text(address_tree, offset, 1, "Protocol length: %u",
329           protocol_length);
330         offset += 1;
331         length -= 1;
332         if (length != 0) {
333             proto_tree_add_text(address_tree, offset, length,
334               "Protocol: %s (truncated)", bytes_to_str(&pd[offset], length));
335         }
336         return -1;
337     }
338
339     protocol_str = NULL;
340     if (protocol_type == PROTO_TYPE_NLPID && protocol_length == 1)
341         protocol_str = val_to_str(pd[offset + 2], nlpid_vals, "Unknown (0x%02x)");
342     if (protocol_str == NULL)
343         protocol_str = bytes_to_str(&pd[offset], protocol_length);
344
345     if (length < 2 + protocol_length + 2) {
346         ti = proto_tree_add_text(tree, offset, length, "Truncated address");
347         address_tree = proto_item_add_subtree(ti, ett_cdp_address);
348         proto_tree_add_text(address_tree, offset, 1, "Protocol type: %s",
349           val_to_str(protocol_type, proto_type_vals, "Unknown (0x%02x)"));
350         offset += 1;
351         length -= 1;
352         proto_tree_add_text(address_tree, offset, 1, "Protocol length: %u",
353           protocol_length);
354         offset += 1;
355         length -= 1;
356         proto_tree_add_text(address_tree, offset, protocol_length,
357           "Protocol: %s", protocol_str);
358         return -1;
359     }
360     address_length = pntohs(&pd[offset + 2 + protocol_length]);
361
362     if (length < 2 + protocol_length + 2 + address_length) {
363         ti = proto_tree_add_text(tree, offset, length, "Truncated address");
364         address_tree = proto_item_add_subtree(ti, ett_cdp_address);
365         proto_tree_add_text(address_tree, offset, 1, "Protocol type: %s",
366           val_to_str(protocol_type, proto_type_vals, "Unknown (0x%02x)"));
367         offset += 1;
368         length -= 1;
369         proto_tree_add_text(address_tree, offset, 1, "Protocol length: %u",
370           protocol_length);
371         offset += 1;
372         length -= 1;
373         proto_tree_add_text(address_tree, offset, protocol_length,
374           "Protocol: %s", protocol_str);
375         offset += protocol_length;
376         length -= protocol_length;
377         proto_tree_add_text(address_tree, offset, 2, "Address length: %u",
378           address_length);
379         offset += 2;
380         length -= 2;
381         if (length != 0) {
382             proto_tree_add_text(address_tree, offset, length,
383               "Address: %s (truncated)", bytes_to_str(&pd[offset], length));
384         }
385         return -1;
386     }
387
388     /* XXX - the Cisco document seems to be saying that, for 802.2-format
389        protocol types, 0xAAAA03 0x000000 0x0800 is IPv6, but 0x0800 is
390        the Ethernet protocol type for IPv4. */
391     length = 2 + protocol_length + 2 + address_length;
392     address_type_str = NULL;
393     address_str = NULL;
394     if (protocol_type == PROTO_TYPE_NLPID && protocol_length == 1) {
395         switch (pd[offset + 2]) {
396
397         /* XXX - dissect NLPID_ISO8473_CLNP as OSI CLNP address? */
398
399         case NLPID_IP:
400             if (address_length == 4) {
401                 /* The address is an IP address. */
402                 address_type_str = "IP address";
403                 address_str = ip_to_str(&pd[offset + 2 + protocol_length + 2]);
404             }
405             break;
406         }
407     }
408     if (address_type_str == NULL)
409         address_type_str = "Address";
410     if (address_str == NULL) {
411         address_str = bytes_to_str(&pd[offset + 2 + protocol_length + 2],
412           address_length);
413     }
414     ti = proto_tree_add_text(tree, offset, length, "%s: %s",
415       address_type_str, address_str);
416     address_tree = proto_item_add_subtree(ti, ett_cdp_address);
417     proto_tree_add_text(address_tree, offset, 1, "Protocol type: %s",
418       val_to_str(protocol_type, proto_type_vals, "Unknown (0x%02x)"));
419     offset += 1;
420     length -= 1;
421     proto_tree_add_text(address_tree, offset, 1, "Protocol length: %u",
422       protocol_length);
423     offset += 1;
424     length -= 1;
425     proto_tree_add_text(address_tree, offset, protocol_length,
426       "Protocol: %s", protocol_str);
427     offset += protocol_length;
428     length -= protocol_length;
429     proto_tree_add_text(address_tree, offset, 2, "Address length: %u",
430       address_length);
431     offset += 2;
432     length -= 2;
433     proto_tree_add_text(address_tree, offset, address_length, "%s: %s",
434       address_type_str, address_str);
435     return 2 + protocol_length + 2 + address_length;
436 }
437
438 static void
439 dissect_capabilities(const u_char *pd, int offset, int length, proto_tree *tree)
440 {
441     proto_item *ti;
442     proto_tree *capabilities_tree;
443     guint32 capabilities;
444
445     if (length < 4)
446         return;
447     capabilities = pntohl(&pd[offset]);
448     ti = proto_tree_add_text(tree, offset, length, "Capabilities: 0x%08x",
449         capabilities);
450     capabilities_tree = proto_item_add_subtree(ti, ett_cdp_capabilities);
451     proto_tree_add_text(capabilities_tree, offset, 4,
452         decode_boolean_bitfield(capabilities, 0x01, 4*8,
453             "Performs level 3 routing",
454             "Doesn't perform level 3 routing"));
455     proto_tree_add_text(capabilities_tree, offset, 4,
456         decode_boolean_bitfield(capabilities, 0x02, 4*8,
457             "Performs level 2 transparent bridging",
458             "Doesn't perform level 2 transparent bridging"));
459     proto_tree_add_text(capabilities_tree, offset, 4,
460         decode_boolean_bitfield(capabilities, 0x04, 4*8,
461             "Performs level 2 source-route bridging",
462             "Doesn't perform level 2 source-route bridging"));
463     proto_tree_add_text(capabilities_tree, offset, 4,
464         decode_boolean_bitfield(capabilities, 0x08, 4*8,
465             "Performs level 2 switching",
466             "Doesn't perform level 2 switching"));
467     proto_tree_add_text(capabilities_tree, offset, 4,
468         decode_boolean_bitfield(capabilities, 0x10, 4*8,
469             "Sends and receives packets for network-layer protocols",
470             "Doesn't send or receive packets for network-layer protocols"));
471     proto_tree_add_text(capabilities_tree, offset, 4,
472         decode_boolean_bitfield(capabilities, 0x20, 4*8,
473             "Doesn't forward IGMP Report packets on nonrouter ports",
474             "Forwards IGMP Report packets on nonrouter ports"));
475     proto_tree_add_text(capabilities_tree, offset, 4,
476         decode_boolean_bitfield(capabilities, 0x40, 4*8,
477             "Provides level 1 functionality",
478             "Doesn't provide level 1 functionality"));
479 }
480
481 static void
482 add_multi_line_string_to_tree(proto_tree *tree, gint start, gint len,
483   const gchar *prefix, const gchar *string)
484 {
485     int prefix_len;
486     int i;
487     char blanks[64+1];
488     const gchar *p, *q;
489     int line_len;
490     int data_len;
491
492     prefix_len = strlen(prefix);
493     if (prefix_len > 64)
494         prefix_len = 64;
495     for (i = 0; i < prefix_len; i++)
496         blanks[i] = ' ';
497     blanks[i] = '\0';
498     p = string;
499     for (;;) {
500         q = strchr(p, '\n');
501         if (q != NULL) {
502             line_len = q - p;
503             data_len = line_len + 1;
504         } else {
505             line_len = strlen(p);
506             data_len = line_len;
507         }
508         proto_tree_add_text(tree, start, data_len, "%s%.*s", prefix,
509            line_len, p);
510         if (q == NULL)
511             break;
512         p += data_len;
513         start += data_len;
514         prefix = blanks;
515     }
516 }
517
518 void
519 proto_register_cdp(void)
520 {
521         static hf_register_info hf[] = {
522                 { &hf_cdp_version,
523                 { "Version",            "cdp.version",  FT_UINT8, BASE_DEC, NULL, 0x0,
524                         "" }},
525
526                 { &hf_cdp_flags,
527                 { "Flags",              "cdp.flags", FT_UINT8, BASE_HEX, NULL, 0x0,
528                         "" }},
529
530                 { &hf_cdp_ttl,
531                 { "TTL",                "cdp.ttl", FT_UINT16, BASE_DEC, NULL, 0x0,
532                         "" }},
533
534                 { &hf_cdp_tlvtype,
535                 { "Type",               "cdp.tlv.type", FT_UINT16, BASE_HEX, VALS(type_vals), 0x0,
536                         "" }},
537
538                 { &hf_cdp_tlvlength,
539                 { "Length",             "cdp.tlv.len", FT_UINT16, BASE_DEC, NULL, 0x0,
540                         "" }},
541         };
542         static gint *ett[] = {
543                 &ett_cdp,
544                 &ett_cdp_tlv,
545                 &ett_cdp_address,
546                 &ett_cdp_capabilities,
547         };
548
549         proto_cdp = proto_register_protocol("Cisco Discovery Protocol", "cdp");
550         proto_register_field_array(proto_cdp, hf, array_length(hf));
551         proto_register_subtree_array(ett, array_length(ett));
552 }