Get rid of a debugging fprintf.
[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.23 2000/05/31 05:06:56 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, NullTVB, offset, END_OF_FRAME, FALSE);
111         cdp_tree = proto_item_add_subtree(ti, ett_cdp);
112         
113         /* CDP header */
114         proto_tree_add_uint(cdp_tree, hf_cdp_version, NullTVB, offset, 1, pd[offset]);
115         offset += 1;
116         proto_tree_add_uint_format(cdp_tree, hf_cdp_ttl, NullTVB, offset, 1,
117                                    pntohs(&pd[offset]),
118                                    "TTL: %u seconds", pd[offset]);
119         offset += 1;
120         proto_tree_add_uint_format(cdp_tree, hf_cdp_flags, NullTVB, 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, NullTVB, 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_uint(tlv_tree, hf_cdp_tlvtype, NullTVB,
140                                     offset + TLV_TYPE, 2, type);
141                                 proto_tree_add_uint(tlv_tree, hf_cdp_tlvlength, NullTVB,
142                                     offset + TLV_LENGTH, 2, length);
143                                 proto_tree_add_text(tlv_tree, NullTVB, 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, NullTVB, offset,
151                                     length, "Addresses");
152                                 tlv_tree = proto_item_add_subtree(tlvi,
153                                     ett_cdp_tlv);
154                                 proto_tree_add_uint(tlv_tree, hf_cdp_tlvtype, NullTVB,
155                                     offset + TLV_TYPE, 2, type);
156                                 proto_tree_add_uint(tlv_tree, hf_cdp_tlvlength, NullTVB,
157                                     offset + TLV_LENGTH, 2, length);
158                                 offset += 4;
159                                 length -= 4;
160                                 naddresses = pntohl(&pd[offset]);
161                                 proto_tree_add_text(tlv_tree, NullTVB, offset, 4,
162                                     "Number of addresses: %u", naddresses);
163                                 offset += 4;
164                                 length -= 4;
165                                 while (naddresses != 0) {
166                                     addr_length = dissect_address_tlv(pd,
167                                         offset, length, tlv_tree);
168                                     if (addr_length < 0)
169                                         break;
170                                     offset += addr_length;
171                                     length -= addr_length;
172
173                                     naddresses--;
174                                 }
175                                 offset += length;
176                                 break;
177                         case TYPE_PORT_ID:
178                                 real_length = length;
179                                 if (pd[offset + real_length] != 0x00) {
180                                     /* The length in the TLV doesn't
181                                        appear to be the length of the
182                                        TLV, as the byte just past it
183                                        isn't the first byte of a 2-byte
184                                        big-endian small integer; make
185                                        the length of the TLV the length
186                                        in the TLV, plus 4 bytes for the
187                                        TLV type and length, minus 1
188                                        because that's what makes one
189                                        capture work. */
190                                     real_length = length + 3;
191                                 }
192                                 tlvi = proto_tree_add_text(cdp_tree, NullTVB, offset,
193                                     real_length, "Port ID: %s",
194                                     &pd[offset+4]);
195                                 tlv_tree = proto_item_add_subtree(tlvi,
196                                     ett_cdp_tlv);
197                                 proto_tree_add_uint(tlv_tree, hf_cdp_tlvtype, NullTVB,
198                                     offset + TLV_TYPE, 2, type);
199                                 proto_tree_add_uint(tlv_tree, hf_cdp_tlvlength, NullTVB,
200                                     offset + TLV_LENGTH, 2, length);
201                                 proto_tree_add_text(tlv_tree, NullTVB, offset + 4,
202                                     real_length - 4,
203                                     "Sent through Interface: %s",
204                                     &pd[offset+4]);
205                                 offset += real_length;
206                                 break;
207                         case TYPE_CAPABILITIES:
208                                 tlvi = proto_tree_add_text(cdp_tree, NullTVB, offset,
209                                     length, "Capabilities");
210                                 tlv_tree = proto_item_add_subtree(tlvi,
211                                     ett_cdp_tlv);
212                                 proto_tree_add_uint(tlv_tree, hf_cdp_tlvtype, NullTVB,
213                                     offset + TLV_TYPE, 2, type);
214                                 proto_tree_add_uint(tlv_tree, hf_cdp_tlvlength, NullTVB,
215                                     offset + TLV_LENGTH, 2, length);
216                                 offset += 4;
217                                 length -= 4;
218                                 dissect_capabilities(pd, offset, length,
219                                     tlv_tree);
220                                 offset += length;
221                                 break;
222                         case TYPE_IOS_VERSION:
223                                 tlvi = proto_tree_add_text(cdp_tree, NullTVB, offset,
224                                     length, "Software Version");
225                                 tlv_tree = proto_item_add_subtree(tlvi,
226                                     ett_cdp_tlv);
227                                 proto_tree_add_uint(tlv_tree, hf_cdp_tlvtype, NullTVB,
228                                     offset + TLV_TYPE, 2, type);
229                                 proto_tree_add_uint(tlv_tree, hf_cdp_tlvlength, NullTVB,
230                                     offset + TLV_LENGTH, 2, length);
231                                 add_multi_line_string_to_tree(tlv_tree,
232                                     offset + 4, length - 4, "Software Version: ",
233                                     &pd[offset+4] );
234                                 offset += length;
235                                 break;
236                         case TYPE_PLATFORM:
237                                 /* ??? platform */
238                                 stringmem = malloc(length);
239                                 memset(stringmem, '\0', length);
240                                 memcpy(stringmem, &pd[offset+4], length - 4 );
241                                 tlvi = proto_tree_add_text(cdp_tree, NullTVB,
242                                     offset, length, "Platform: %s",
243                                     stringmem);
244                                 tlv_tree = proto_item_add_subtree(tlvi,
245                                     ett_cdp_tlv);
246                                 proto_tree_add_uint(tlv_tree, hf_cdp_tlvtype, NullTVB,
247                                     offset + TLV_TYPE, 2, type);
248                                 proto_tree_add_uint(tlv_tree, hf_cdp_tlvlength, NullTVB,
249                                     offset + TLV_LENGTH, 2, length);
250                                 proto_tree_add_text(tlv_tree, NullTVB, offset + 4,
251                                     length - 4, "Platform: %s", stringmem);
252                                 free(stringmem);
253                                 offset+=length;
254                                 break;
255                         default:
256                                 tlvi = proto_tree_add_text(cdp_tree, NullTVB, offset,
257                                     length, "Type: %s, length: %u",
258                                     type_str, length);
259                                 tlv_tree = proto_item_add_subtree(tlvi,
260                                     ett_cdp_tlv);
261                                 proto_tree_add_uint(tlv_tree, hf_cdp_tlvtype, NullTVB,
262                                     offset + TLV_TYPE, 2, type);
263                                 proto_tree_add_uint(tlv_tree, hf_cdp_tlvlength, NullTVB,
264                                     offset + TLV_LENGTH, 2, length);
265                                 if (length > 4) {
266                                         proto_tree_add_text(tlv_tree, NullTVB,
267                                             offset + 4, length - 4, "Data");
268                                 } else
269                                         return;
270                                 offset+=length;
271                 }
272         }
273         dissect_data(pd, offset, fd, 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(const u_char *pd, 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, NullTVB, offset, length);
302     address_tree = proto_item_add_subtree(ti, ett_cdp_address);
303     protocol_type = pd[offset];
304     proto_tree_add_text(address_tree, NullTVB, 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 = pd[offset];
314     proto_tree_add_text(address_tree, NullTVB, 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, NullTVB, offset, length,
323               "Protocol: %s (truncated)", bytes_to_str(&pd[offset], length));
324         }
325         return -1;
326     }
327     protocol_str = NULL;
328     if (protocol_type == PROTO_TYPE_NLPID && protocol_length == 1) {
329         nlpid = pd[offset];
330         protocol_str = val_to_str(nlpid, nlpid_vals, "Unknown (0x%02x)");
331     } else
332         nlpid = -1;
333     if (protocol_str == NULL)
334         protocol_str = bytes_to_str(&pd[offset], protocol_length);
335     proto_tree_add_text(address_tree, NullTVB, offset, protocol_length,
336                         "Protocol: %s", protocol_str);
337     offset += protocol_length;
338     length -= protocol_length;
339
340     if (length < 2) {
341         proto_item_set_text(ti, "Truncated address");
342         return -1;
343     }
344     address_length = pntohs(&pd[offset]);
345     proto_tree_add_text(address_tree, NullTVB, offset, 2, "Address length: %u",
346                         address_length);
347     offset += 2;
348     length -= 2;
349
350     if (length < address_length) {
351         proto_item_set_text(ti, "Truncated address");
352         if (length != 0) {
353             proto_tree_add_text(address_tree, NullTVB, offset, length,
354               "Address: %s (truncated)", bytes_to_str(&pd[offset], length));
355         }
356         return -1;
357     }
358     /* XXX - the Cisco document seems to be saying that, for 802.2-format
359        protocol types, 0xAAAA03 0x000000 0x0800 is IPv6, but 0x0800 is
360        the Ethernet protocol type for IPv4. */
361     length = 2 + protocol_length + 2 + address_length;
362     address_type_str = NULL;
363     address_str = NULL;
364     if (protocol_type == PROTO_TYPE_NLPID && protocol_length == 1) {
365         switch (nlpid) {
366
367         /* XXX - dissect NLPID_ISO8473_CLNP as OSI CLNP address? */
368
369         case NLPID_IP:
370             if (address_length == 4) {
371                 /* The address is an IP address. */
372                 address_type_str = "IP address";
373                 address_str = ip_to_str(&pd[offset]);
374             }
375             break;
376         }
377     }
378     if (address_type_str == NULL)
379         address_type_str = "Address";
380     if (address_str == NULL) {
381         address_str = bytes_to_str(&pd[offset], address_length);
382     }
383     proto_item_set_text(ti, "%s: %s", address_type_str, address_str);
384     proto_tree_add_text(address_tree, NullTVB, offset, address_length, "%s: %s",
385       address_type_str, address_str);
386     return 2 + protocol_length + 2 + address_length;
387 }
388
389 static void
390 dissect_capabilities(const u_char *pd, int offset, int length, proto_tree *tree)
391 {
392     proto_item *ti;
393     proto_tree *capabilities_tree;
394     guint32 capabilities;
395
396     if (length < 4)
397         return;
398     capabilities = pntohl(&pd[offset]);
399     ti = proto_tree_add_text(tree, NullTVB, offset, length, "Capabilities: 0x%08x",
400         capabilities);
401     capabilities_tree = proto_item_add_subtree(ti, ett_cdp_capabilities);
402     proto_tree_add_text(capabilities_tree, NullTVB, offset, 4,
403         decode_boolean_bitfield(capabilities, 0x01, 4*8,
404             "Performs level 3 routing",
405             "Doesn't perform level 3 routing"));
406     proto_tree_add_text(capabilities_tree, NullTVB, offset, 4,
407         decode_boolean_bitfield(capabilities, 0x02, 4*8,
408             "Performs level 2 transparent bridging",
409             "Doesn't perform level 2 transparent bridging"));
410     proto_tree_add_text(capabilities_tree, NullTVB, offset, 4,
411         decode_boolean_bitfield(capabilities, 0x04, 4*8,
412             "Performs level 2 source-route bridging",
413             "Doesn't perform level 2 source-route bridging"));
414     proto_tree_add_text(capabilities_tree, NullTVB, offset, 4,
415         decode_boolean_bitfield(capabilities, 0x08, 4*8,
416             "Performs level 2 switching",
417             "Doesn't perform level 2 switching"));
418     proto_tree_add_text(capabilities_tree, NullTVB, offset, 4,
419         decode_boolean_bitfield(capabilities, 0x10, 4*8,
420             "Sends and receives packets for network-layer protocols",
421             "Doesn't send or receive packets for network-layer protocols"));
422     proto_tree_add_text(capabilities_tree, NullTVB, offset, 4,
423         decode_boolean_bitfield(capabilities, 0x20, 4*8,
424             "Doesn't forward IGMP Report packets on nonrouter ports",
425             "Forwards IGMP Report packets on nonrouter ports"));
426     proto_tree_add_text(capabilities_tree, NullTVB, offset, 4,
427         decode_boolean_bitfield(capabilities, 0x40, 4*8,
428             "Provides level 1 functionality",
429             "Doesn't provide level 1 functionality"));
430 }
431
432 static void
433 add_multi_line_string_to_tree(proto_tree *tree, gint start, gint len,
434   const gchar *prefix, const gchar *string)
435 {
436     int prefix_len;
437     int i;
438     char blanks[64+1];
439     const gchar *p, *q;
440     int line_len;
441     int data_len;
442
443     prefix_len = strlen(prefix);
444     if (prefix_len > 64)
445         prefix_len = 64;
446     for (i = 0; i < prefix_len; i++)
447         blanks[i] = ' ';
448     blanks[i] = '\0';
449     p = string;
450     for (;;) {
451         q = strchr(p, '\n');
452         if (q != NULL) {
453             line_len = q - p;
454             data_len = line_len + 1;
455         } else {
456             line_len = strlen(p);
457             data_len = line_len;
458         }
459         proto_tree_add_text(tree, NullTVB, start, data_len, "%s%.*s", prefix,
460            line_len, p);
461         if (q == NULL)
462             break;
463         p += data_len;
464         start += data_len;
465         prefix = blanks;
466     }
467 }
468
469 void
470 proto_register_cdp(void)
471 {
472         static hf_register_info hf[] = {
473                 { &hf_cdp_version,
474                 { "Version",            "cdp.version",  FT_UINT8, BASE_DEC, NULL, 0x0,
475                         "" }},
476
477                 { &hf_cdp_flags,
478                 { "Flags",              "cdp.flags", FT_UINT8, BASE_HEX, NULL, 0x0,
479                         "" }},
480
481                 { &hf_cdp_ttl,
482                 { "TTL",                "cdp.ttl", FT_UINT16, BASE_DEC, NULL, 0x0,
483                         "" }},
484
485                 { &hf_cdp_tlvtype,
486                 { "Type",               "cdp.tlv.type", FT_UINT16, BASE_HEX, VALS(type_vals), 0x0,
487                         "" }},
488
489                 { &hf_cdp_tlvlength,
490                 { "Length",             "cdp.tlv.len", FT_UINT16, BASE_DEC, NULL, 0x0,
491                         "" }},
492         };
493         static gint *ett[] = {
494                 &ett_cdp,
495                 &ett_cdp_tlv,
496                 &ett_cdp_address,
497                 &ett_cdp_capabilities,
498         };
499
500         proto_cdp = proto_register_protocol("Cisco Discovery Protocol", "cdp");
501         proto_register_field_array(proto_cdp, hf, array_length(hf));
502         proto_register_subtree_array(ett, array_length(ett));
503 }