Make more infiniband subdissectors heuristic.
[metze/wireshark/wip.git] / epan / dissectors / packet-fcoib.c
1 /*
2  * packet-fcoib.c
3  * Routines for FCoIB dissection - Fibre Channel over Infiniband
4  * Copyright (c) 2010 Mellanox Technologies Ltd. (slavak@mellanox.co.il)
5  *
6  * Wireshark - Network traffic analyzer
7  * By Gerald Combs <gerald@wireshark.org>
8  * Copyright 1998 Gerald Combs
9  *
10  * Based on packet-fcoe.c, Copyright (c) 2006 Nuova Systems, Inc. (jre@nuovasystems.com)
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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
25  */
26
27 #include "config.h"
28
29 #include <stdlib.h>
30 #include <errno.h>
31
32 #include <epan/packet.h>
33 #include <epan/prefs.h>
34 #include <epan/crc32-tvb.h>
35 #include <epan/expert.h>
36 #include <epan/addr_resolv.h>
37 #include "packet-infiniband.h"
38 #include "packet-fc.h"
39
40 void proto_register_fcoib(void);
41 void proto_reg_handoff_fcoib(void);
42
43 #define FCOIB_HEADER_LEN   16        /* header: encap. header, SOF, and padding */
44 #define FCOIB_TRAILER_LEN   8        /* trailer: FC-CRC, EOF and padding */
45 #define FCOIB_VER_OFFSET    2        /* offset of ver field (in bytes) inside FCoIB Encap. header */
46
47 typedef enum {
48     FCOIB_EOFn    = 0x41,
49     FCOIB_EOFt    = 0x42,
50     FCOIB_EOFrt   = 0x44,
51     FCOIB_EOFdt   = 0x46,
52     FCOIB_EOFni   = 0x49,
53     FCOIB_EOFdti  = 0x4E,
54     FCOIB_EOFrti  = 0x4F,
55     FCOIB_EOFa    = 0x50
56 } fcoib_eof_t;
57
58 typedef enum {
59     FCOIB_SOFf    = 0x28,
60     FCOIB_SOFi4   = 0x29,
61     FCOIB_SOFi2   = 0x2D,
62     FCOIB_SOFi3   = 0x2E,
63     FCOIB_SOFn4   = 0x31,
64     FCOIB_SOFn2   = 0x35,
65     FCOIB_SOFn3   = 0x36,
66     FCOIB_SOFc4   = 0x39
67 } fcoib_sof_t;
68
69 static const value_string fcoib_eof_vals[] = {
70     {FCOIB_EOFn,    "EOFn" },
71     {FCOIB_EOFt,    "EOFt" },
72     {FCOIB_EOFrt,   "EOFrt" },
73     {FCOIB_EOFdt,   "EOFdt" },
74     {FCOIB_EOFni,   "EOFni" },
75     {FCOIB_EOFdti,  "EOFdti" },
76     {FCOIB_EOFrti,  "EOFrti" },
77     {FCOIB_EOFa,    "EOFa" },
78     {0, NULL}
79 };
80
81 static const value_string fcoib_sof_vals[] = {
82     {FCOIB_SOFf,    "SOFf" },
83     {FCOIB_SOFi4,   "SOFi4" },
84     {FCOIB_SOFi2,   "SOFi2" },
85     {FCOIB_SOFi3,   "SOFi3" },
86     {FCOIB_SOFn4,   "SOFn4" },
87     {FCOIB_SOFn2,   "SOFn2" },
88     {FCOIB_SOFn3,   "SOFn3" },
89     {FCOIB_SOFc4,   "SOFc4" },
90     {0, NULL}
91 };
92
93 static int proto_fcoib          = -1;
94 static int hf_fcoib_ver         = -1;
95 static int hf_fcoib_sig         = -1;
96 static int hf_fcoib_sof         = -1;
97 static int hf_fcoib_eof         = -1;
98 static int hf_fcoib_crc         = -1;
99 static int hf_fcoib_crc_status  = -1;
100
101 static int ett_fcoib            = -1;
102
103 static expert_field ei_fcoib_crc = EI_INIT;
104
105 static dissector_handle_t fc_handle;
106
107 /* global preferences */
108 static gboolean    gPREF_HEUR_EN = TRUE;
109 static gboolean    gPREF_MAN_EN  = FALSE;
110 static gint        gPREF_TYPE[2] = {0};
111 static const char *gPREF_ID[2]   = {NULL};
112 static guint       gPREF_QP[2]   = {0};
113
114 /* source/destination addresses from preferences menu (parsed from gPREF_TYPE[?], gPREF_ID[?]) */
115 static address  manual_addr[2];
116 static void    *manual_addr_data[2];
117
118 static const enum_val_t pref_address_types[] = {
119     {"lid", "LID", 0},
120     {"gid", "GID", 1},
121     {NULL, NULL, -1}
122 };
123
124 /* checks if a packet matches the source/destination manually-configured in preferences */
125 static gboolean
126 manual_addr_match(packet_info *pinfo) {
127     if (gPREF_MAN_EN) {
128         /* If the manual settings are enabled see if this fits - in which case we can skip
129            the following checks entirely and go straight to dissecting */
130         if (    (addresses_equal(&pinfo->src, &manual_addr[0]) &&
131                  addresses_equal(&pinfo->dst, &manual_addr[1]) &&
132                  (pinfo->srcport == 0xffffffff /* is unknown */ || pinfo->srcport == gPREF_QP[0]) &&
133                  (pinfo->destport == 0xffffffff /* is unknown */ || pinfo->destport == gPREF_QP[1]))    ||
134                 (addresses_equal(&pinfo->src, &manual_addr[1]) &&
135                  addresses_equal(&pinfo->dst, &manual_addr[0]) &&
136                  (pinfo->srcport == 0xffffffff /* is unknown */ || pinfo->srcport == gPREF_QP[1]) &&
137                  (pinfo->destport == 0xffffffff /* is unknown */ || pinfo->destport == gPREF_QP[0]))    )
138             return TRUE;
139     }
140
141     return FALSE;
142 }
143
144 static gboolean
145 dissect_fcoib(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void *data _U_)
146 {
147     gint        crc_offset;
148     gint        eof_offset;
149     gint        sof_offset;
150     gint        frame_len;
151     guint       version;
152     const char *ver;
153     gint        bytes_remaining;
154     guint8      sof          = 0;
155     guint8      eof          = 0;
156     guint8      sig          = 0;
157     const char *eof_str;
158     const char *sof_str;
159     const char *crc_msg;
160     const char *len_msg;
161     proto_item *ti;
162     proto_tree *fcoib_tree;
163     tvbuff_t   *next_tvb;
164     gboolean    crc_exists;
165     guint32     crc_computed = 0;
166     guint32     crc          = 0;
167     gboolean    packet_match_manual;
168     fc_data_t   fc_data;
169
170     frame_len = tvb_reported_length_remaining(tvb, 0) -
171       FCOIB_HEADER_LEN - FCOIB_TRAILER_LEN;
172     crc_offset = FCOIB_HEADER_LEN + frame_len;
173     eof_offset = crc_offset + 4;
174     sof_offset = FCOIB_HEADER_LEN - 1;
175
176     if (frame_len <= 0)
177         return FALSE;   /* this packet isn't even long enough to contain the header+trailer w/o FC payload! */
178
179     packet_match_manual = manual_addr_match(pinfo);
180
181     if (!packet_match_manual && !gPREF_HEUR_EN)
182         return FALSE;   /* user doesn't want us trying to automatically identify FCoIB packets */
183
184     /* we start off with some basic heuristics checks to make sure this could be a FCoIB packet */
185
186     if (tvb_bytes_exist(tvb, 0, 1))
187         sig = tvb_get_guint8(tvb, 0) >> 6;
188     if (tvb_bytes_exist(tvb, eof_offset, 1))
189         eof = tvb_get_guint8(tvb, eof_offset);
190     if (tvb_bytes_exist(tvb, sof_offset, 1))
191         sof = tvb_get_guint8(tvb, sof_offset);
192
193     if (!packet_match_manual) {
194         if (sig != 1)
195             return FALSE;   /* the sig field in the FCoIB Encap. header MUST be 2'b01*/
196         if (!tvb_bytes_exist(tvb, eof_offset + 1, 3) || tvb_get_ntoh24(tvb, eof_offset + 1) != 0)
197             return FALSE;   /* 3 bytes of RESERVED field immediately after eEOF MUST be 0 */
198         if (!try_val_to_str(sof, fcoib_sof_vals))
199             return FALSE;   /* invalid value for SOF */
200         if (!try_val_to_str(eof, fcoib_eof_vals))
201             return FALSE;   /* invalid value for EOF */
202     }
203
204
205     col_set_str(pinfo->cinfo, COL_PROTOCOL, "FCoIB");
206     bytes_remaining = tvb_captured_length_remaining(tvb, FCOIB_HEADER_LEN);
207     if (bytes_remaining > frame_len)
208         bytes_remaining = frame_len;        /* backing length */
209     next_tvb = tvb_new_subset(tvb, FCOIB_HEADER_LEN, bytes_remaining, frame_len);
210
211     /*
212      * Only version 0 is defined at this point.
213      * Don't print the version in the short summary if it is zero.
214      */
215     ver = "";
216     version = tvb_get_guint8(tvb, 0 + FCOIB_VER_OFFSET) >> 4;
217     if (version != 0)
218         ver = wmem_strdup_printf(wmem_packet_scope(), ver, "ver %d ", version);
219
220     eof_str = "none";
221     if (tvb_bytes_exist(tvb, eof_offset, 1)) {
222         eof_str = val_to_str(eof, fcoib_eof_vals, "0x%x");
223     }
224
225     sof_str = "none";
226     if (tvb_bytes_exist(tvb, sof_offset, 1)) {
227         sof_str = val_to_str(sof, fcoib_sof_vals, "0x%x");
228     }
229
230     /*
231      * Check the CRC.
232      */
233     crc_msg = "";
234     crc_exists = tvb_bytes_exist(tvb, crc_offset, 4);
235     if (crc_exists) {
236         crc = tvb_get_ntohl(tvb, crc_offset);
237         crc_computed = crc32_802_tvb(next_tvb, frame_len);
238         if (crc != crc_computed) {
239             crc_msg = " [bad FC CRC]";
240         }
241     }
242     len_msg = "";
243     if ((frame_len % 4) != 0 || frame_len < 24) {
244         len_msg = " [invalid length]";
245     }
246
247     ti = proto_tree_add_protocol_format(tree, proto_fcoib, tvb, 0,
248                                         FCOIB_HEADER_LEN,
249                                         "FCoIB %s(%s/%s) %d bytes%s%s", ver,
250                                         sof_str, eof_str,
251                                         frame_len, crc_msg,
252                                         len_msg);
253
254     /* Dissect the FCoIB Encapsulation header */
255
256     fcoib_tree = proto_item_add_subtree(ti, ett_fcoib);
257     proto_tree_add_uint(fcoib_tree, hf_fcoib_sig, tvb, 0, 1, sig);
258     proto_tree_add_uint(fcoib_tree, hf_fcoib_ver, tvb, FCOIB_VER_OFFSET, 1, version);
259     proto_tree_add_uint(fcoib_tree, hf_fcoib_sof, tvb, sof_offset, 1, sof);
260
261     /*
262      * Create the CRC information.
263      */
264     if (crc_exists) {
265         proto_tree_add_checksum(fcoib_tree, tvb, crc_offset, hf_fcoib_crc, hf_fcoib_crc_status, &ei_fcoib_crc, pinfo, crc_computed, ENC_BIG_ENDIAN, PROTO_CHECKSUM_VERIFY);
266         proto_tree_set_appendix(fcoib_tree, tvb, crc_offset,
267                                 tvb_captured_length_remaining (tvb, crc_offset));
268     } else {
269         proto_tree_add_checksum(fcoib_tree, tvb, crc_offset, hf_fcoib_crc, hf_fcoib_crc_status, &ei_fcoib_crc, pinfo, 0, ENC_BIG_ENDIAN, PROTO_CHECKSUM_NOT_PRESENT);
270     }
271
272     /*
273      * Interpret the EOF.
274      */
275     if (tvb_bytes_exist(tvb, eof_offset, 1)) {
276         proto_tree_add_item(fcoib_tree, hf_fcoib_eof, tvb, eof_offset, 1, ENC_BIG_ENDIAN);
277     }
278
279     /* Set the SOF/EOF flags in the packet_info header */
280     fc_data.sof_eof = 0;
281     if (sof == FCOIB_SOFi3 || sof == FCOIB_SOFi2 || sof == FCOIB_SOFi4) {
282         fc_data.sof_eof = FC_DATA_SOF_FIRST_FRAME;
283     } else if (sof == FCOIB_SOFf) {
284         fc_data.sof_eof = FC_DATA_SOF_SOFF;
285     }
286
287     if (eof != FCOIB_EOFn) {
288         fc_data.sof_eof |= FC_DATA_EOF_LAST_FRAME;
289     } else if (eof != FCOIB_EOFt) {
290         fc_data.sof_eof |= FC_DATA_EOF_INVALID;
291     }
292
293     /* Call the FC Dissector if this is carrying an FC frame */
294     fc_data.ethertype = 0;
295
296     if (fc_handle) {
297         call_dissector_with_data(fc_handle, next_tvb, pinfo, tree, &fc_data);
298     } else {
299         call_data_dissector(next_tvb, pinfo, tree);
300     }
301
302     return TRUE;
303 }
304
305 void
306 proto_register_fcoib(void)
307 {
308     module_t *fcoib_module;
309
310     /* Setup list of header fields  See Section 1.6.1 for details*/
311     static hf_register_info hf[] = {
312         { &hf_fcoib_sof,
313           {"SOF", "fcoib.sof", FT_UINT8, BASE_HEX, VALS(fcoib_sof_vals), 0,
314            NULL, HFILL}},
315         { &hf_fcoib_eof,
316           {"EOF", "fcoib.eof", FT_UINT8, BASE_HEX, VALS(fcoib_eof_vals), 0,
317            NULL, HFILL}},
318         { &hf_fcoib_sig,
319           {"Signature", "fcoib.sig", FT_UINT8, BASE_HEX, NULL, 0, NULL, HFILL}},
320         { &hf_fcoib_ver,
321           {"Version", "fcoib.ver", FT_UINT32, BASE_DEC, NULL, 0, NULL, HFILL}},
322         { &hf_fcoib_crc,
323           {"CRC", "fcoib.crc", FT_UINT32, BASE_HEX, NULL, 0, NULL, HFILL}},
324         { &hf_fcoib_crc_status,
325           {"CRC Status", "fcoib.crc.status", FT_UINT8, BASE_NONE, VALS(proto_checksum_vals), 0x0,
326            NULL, HFILL }},
327     };
328     static gint *ett[] = {
329         &ett_fcoib,
330     };
331
332     static ei_register_info ei[] = {
333         { &ei_fcoib_crc, { "fcoib.crc.bad", PI_CHECKSUM, PI_ERROR, "Bad checksum", EXPFILL }},
334     };
335
336     expert_module_t* expert_fcoib;
337
338     /* Register the protocol name and description */
339     proto_fcoib = proto_register_protocol("Fibre Channel over Infiniband",
340         "FCoIB", "fcoib");
341
342     /* Required function calls to register the header fields and
343      * subtrees used */
344     proto_register_field_array(proto_fcoib, hf, array_length(hf));
345     proto_register_subtree_array(ett, array_length(ett));
346     expert_fcoib = expert_register_protocol(proto_fcoib);
347     expert_register_field_array(expert_fcoib, ei, array_length(ei));
348
349     fcoib_module = prefs_register_protocol(proto_fcoib, proto_reg_handoff_fcoib);
350
351     prefs_register_bool_preference(fcoib_module, "heur_en", "Enable heuristic identification of FCoIB packets",
352         "When this option is enabled Wireshark will attempt to identify FCoIB packets automatically "
353         "based on some common features (may generate false positives)",
354         &gPREF_HEUR_EN);
355
356     prefs_register_bool_preference(fcoib_module, "manual_en", "Enable manual settings",
357         "Enables dissecting packets between the manually configured source/destination as FCoIB traffic",
358         &gPREF_MAN_EN);
359
360     prefs_register_static_text_preference(fcoib_module, "addr_a", "Address A",
361         "Side A of the manually-configured connection");
362     prefs_register_enum_preference(fcoib_module, "addr_a_type", "Address Type",
363         "Type of address specified", &gPREF_TYPE[0], pref_address_types, FALSE);
364     prefs_register_string_preference(fcoib_module, "addr_a_id", "ID",
365         "LID/GID of address A", &gPREF_ID[0]);
366     prefs_register_uint_preference(fcoib_module, "addr_a_qp", "QP Number",
367         "QP Number for address A", 10, &gPREF_QP[0]);
368
369     prefs_register_static_text_preference(fcoib_module, "addr_b", "Address B",
370         "Side B of the manually-configured connection");
371     prefs_register_enum_preference(fcoib_module, "addr_b_type", "Address Type",
372         "Type of address specified", &gPREF_TYPE[1], pref_address_types, FALSE);
373     prefs_register_string_preference(fcoib_module, "addr_b_id", "ID",
374         "LID/GID of address B", &gPREF_ID[1]);
375     prefs_register_uint_preference(fcoib_module, "addr_b_qp", "QP Number",
376         "QP Number for address B", 10, &gPREF_QP[1]);
377 }
378
379 void
380 proto_reg_handoff_fcoib(void)
381 {
382     static gboolean initialized = FALSE;
383
384     if (!initialized) {
385         heur_dissector_add("infiniband.payload", dissect_fcoib, "Fibre Channel over Infiniband", "fc_infiniband", proto_fcoib, HEURISTIC_ENABLE);
386
387         fc_handle = find_dissector_add_dependency("fc", proto_fcoib);
388
389         initialized = TRUE;
390     }
391
392     if (gPREF_MAN_EN) {
393         /* the manual setting is enabled, so parse the settings into the address type */
394         gboolean error_occured = FALSE;
395         char *not_parsed;
396         int i;
397
398         for (i = 0; i < 2; i++) {
399             if (gPREF_ID[i][0] == '\0') {
400                 error_occured = TRUE;
401             } else if (gPREF_TYPE[i] == 0) {   /* LID */
402                 errno = 0;  /* reset any previous error indicators */
403                 *((guint16*)manual_addr_data[i]) = (guint16)strtoul(gPREF_ID[i], &not_parsed, 0);
404                 if (errno || *not_parsed != '\0') {
405                     error_occured = TRUE;
406                 } else {
407                     set_address(&manual_addr[i], AT_IB, sizeof(guint16), manual_addr_data[i]);
408                 }
409             } else {    /* GID */
410                 if (!str_to_ip6( gPREF_ID[i], manual_addr_data[i])) {
411                     error_occured = TRUE;
412                 } else {
413                     set_address(&manual_addr[i], AT_IB, GID_SIZE, manual_addr_data[i]);
414                 }
415             }
416
417             if (error_occured) {
418                 /* an invalid id was specified - disable manual settings until it's fixed */
419                 gPREF_MAN_EN = FALSE;
420                 break;
421             }
422         }
423
424     }
425 }
426
427 /*
428  * Editor modelines  -  http://www.wireshark.org/tools/modelines.html
429  *
430  * Local variables:
431  * c-basic-offset: 4
432  * tab-width: 8
433  * indent-tabs-mode: nil
434  * End:
435  *
436  * vi: set shiftwidth=4 tabstop=8 expandtab:
437  * :indentSize=4:tabSize=8:noTabs=true:
438  */