Use ENC_NA as encoding for proto_tree_add_item() calls which directly reference an...
[obnox/wireshark/wip.git] / epan / dissectors / packet-iwarp-mpa.c
1 /* packet-iwarp-mpa.c
2  * Routines for Marker Protocol data unit Aligned framing (MPA) dissection
3  * According to IETF RFC 5044
4  * Copyright 2008, Yves Geissbuehler <yves.geissbuehler@gmx.net>
5  * Copyright 2008, Philip Frey <frey.philip@gmail.com>
6  *
7  * $Id$
8  *
9  * Wireshark - Network traffic analyzer
10  * By Gerald Combs <gerald@wireshark.org>
11  * Copyright 1998 Gerald Combs
12  *
13  * This program is free software; you can redistribute it and/or
14  * modify it under the terms of the GNU General Public License
15  * as published by the Free Software Foundation; either version 2
16  * of the License, or (at your option) any later version.
17  *
18  * This program is distributed in the hope that it will be useful,
19  * but WITHOUT ANY WARRANTY; without even the implied warranty of
20  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
21  * GNU General Public License for more details.
22  *
23  * You should have received a copy of the GNU General Public License
24  * along with this program; if not, write to the Free Software
25  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301,
26  * USA.
27  */
28
29 /* INCLUDES */
30 #ifdef HAVE_CONFIG_H
31 #include "config.h"
32 #endif
33
34 #include <epan/packet.h>
35 #include <epan/emem.h>
36 #include <epan/conversation.h>
37 #include <epan/dissectors/packet-tcp.h>
38 #include <epan/expert.h>
39 #include <wsutil/crc32.h>
40
41 /* DEFINES */
42
43 /* header field byte lengths */
44 #define MPA_REQ_REP_FRAME_HEADER_LEN 20
45 #define MPA_PDLENGTH_LEN 2
46 #define MPA_ULPDU_LENGTH_LEN 2
47 #define MPA_MARKER_LEN 4
48 #define MPA_SMALLEST_FPDU_LEN 8
49 #define MPA_REQ_REP_KEY_LEN 16
50 #define MPA_REQ_REP_FLAG_LEN 1
51 #define MPA_REQ_REP_REV_LEN 1
52 #define MPA_REQ_REP_PDLENGTH_LEN 2
53 #define MPA_MARKER_RSVD_LEN 2
54 #define MPA_MARKER_FPDUPTR_LEN 2
55 #define MPA_CRC_LEN 4
56
57 /* protocol constants */
58 #define MPA_REQ_REP_FRAME G_GINT64_CONSTANT(0x4d50412049442052U)
59 #define MPA_ID_REQ_FRAME G_GINT64_CONSTANT(0x6571204672616d65U)
60 #define MPA_ID_REP_FRAME G_GINT64_CONSTANT(0x6570204672616d65U)
61 #define MPA_MARKER_INTERVAL 512
62 #define MPA_MAX_PD_LENGTH 512
63 #define MPA_ALIGNMENT 4
64 #define TCP_MAX_SEQ ((guint32) 0xffffffff)
65
66 /* for code readability */
67 #define MPA_REQUEST_FRAME 1
68 #define MPA_REPLY_FRAME 2
69 #define MPA_FPDU 3
70 #define MPA_INITIATOR 0
71 #define MPA_RESPONDER 1
72
73 /* bitmasks */
74 #define MPA_MARKER_FLAG 0x80
75 #define MPA_CRC_FLAG 0x40
76 #define MPA_REJECT_FLAG 0x20
77 #define MPA_RESERVED_FLAG 0x1F
78
79 /* GLOBALS */
80
81 /* initialize the protocol and registered fields */
82 static gint proto_iwarp_mpa = -1;
83
84 static gint hf_mpa_req = -1;
85 static gint hf_mpa_rep = -1;
86 static gint hf_mpa_fpdu = -1;
87 static gint hf_mpa_marker = -1;
88
89 static gint hf_mpa_key_req = -1;
90 static gint hf_mpa_key_rep = -1;
91 static gint hf_mpa_flag_m = -1;
92 static gint hf_mpa_flag_c = -1;
93 static gint hf_mpa_flag_r = -1;
94 static gint hf_mpa_flag_res = -1;
95 static gint hf_mpa_rev = -1;
96 static gint hf_mpa_pd_length = -1;
97 static gint hf_mpa_private_data = -1;
98
99 static gint hf_mpa_ulpdu_length = -1;
100 static gint hf_mpa_pad = -1;
101 static gint hf_mpa_crc = -1;
102 static gint hf_mpa_crc_check = -1;
103
104 static gint hf_mpa_marker_res = -1;
105 static gint hf_mpa_marker_fpduptr = -1;
106
107 /* initialize the subtree pointers */
108 static gint ett_mpa = -1;
109
110 static gint ett_mpa_req = -1;
111 static gint ett_mpa_rep = -1;
112 static gint ett_mpa_fpdu = -1;
113 static gint ett_mpa_marker = -1;
114
115 /* handles of our subdissectors */
116 static dissector_handle_t ddp_rdmap_handle = NULL;
117
118 static const value_string mpa_messages[] = {
119                 { MPA_REQUEST_FRAME, "MPA Request Frame" },
120                 { MPA_REPLY_FRAME, "MPA Reply Frame" },
121                 { MPA_FPDU, "MPA FPDU" },
122                 { 0, NULL }
123 };
124
125 /*
126  * CONNECTION STATE and MARKERS
127  * A MPA endpoint operates in two distinct phases.
128  * The Startup Phase is used to verify correct MPA setup, exchange CRC
129  * and Marker configuration, and optionally pass Private Data between
130  * endpoints prior to completing a DDP connection.
131  * The second distinct phase is Full Operation during which FPDUs are
132  * sent using all the rules that pertain (CRC, Markers, MULPDU,
133  * restrictions etc.).
134  * To keep track of a MPA connection configuration a mpa_state is declared
135  * and maintained per TCP connection, i.e. it is associated to a conversation
136  * between two endpoints.
137  *
138  * In some configurations MPA places MARKERs in a FPDU every 512th octet with
139  * respect to the TCP sequence number of the first FPDU. The struct minfo_t
140  * records the source port of a peer that has to insert Markers into its FPDUs
141  * as well as the TCP sequence number of its first FPDU. This information is
142  * necessary to locate the markers within a FPDU afterwards. Itis part of a
143  * mpa_state.
144  */
145
146 /*
147  * This struct is used to record the source port 'port' and the TCP sequence
148  * number 'seq' of the first FPDU. This information is used to determine the
149  * position of the first Marker within the following FPDUs. The boolean 'valid'
150  * specifies if Markers are inserted by the endpoint running on source port
151  * 'port' or not.
152  */
153 struct minfo {
154         guint16 port;
155         guint32 seq;
156         gboolean valid;
157 };
158 typedef struct minfo minfo_t;
159
160 /*
161  * This struct represents a MPA connection state. It specifies if Markers and
162  * CRC is used for the following FPDUs. It also contains information to
163  * distinguish between the MPA Startup and Full Operation Phase.the connection
164  * parameters negotiated between to MPA endpoints during the MPA Startup Phase
165  * as well as other information for the dissection.
166  *
167  * The two MPA endpoints are called Initiator, the sender of the MPA Request,
168  * and Responder, the sender of the MPA Reply.
169  *
170  * @full_operation: TRUE if is this state is valid and FLASE otherwise.
171  * @req_frame_num: Frame number of the MPA Request to distinguish this frame
172  *                 from later FPDUs.
173  * @rep_frame_num: Frame number of the MPA Reply to distinguish this frame
174  *                 from later FPDUs.
175  * @ini_exp_m_res: TRUE if the Initiator expects the Responder to insert
176  *                 Markers into his FPDUs sent to Initiator and FALSE otherwise.
177  * @res_exp_m_ini: TRUE if the Responder expects the Initiator to insert
178  *                 Markers into his FPDUs sent to Responder and FALSE otherwise.
179  * @minfo[2]:      Array of minfo_t whichs holds necessary information to
180  *                 determine the start position of the first Marker within a
181  *                 a FPDU.
182  *                 minfo[0] is used for the Initiator endpoint
183  *                 minfo[1] is used for the Responder endpoint
184  * @crc:           TRUE if CRC is used by both endpoints and FLASE otherwise.
185  * @revision:      Stores the MPA protocol revision number.
186  */
187 struct mpa_state {
188         gboolean full_operation;
189         guint req_frame_num;
190         guint rep_frame_num;
191         gboolean ini_exp_m_res;
192         gboolean res_exp_m_ini;
193         minfo_t minfo[2];
194         gboolean crc;
195         gint revision;
196 };
197 typedef struct mpa_state mpa_state_t;
198
199 /*
200  * Returns an initialized MPA connection state or throws an out of
201  * memory exception.
202  */
203 static mpa_state_t *
204 init_mpa_state(void)
205 {
206         mpa_state_t *state;
207
208         state = (mpa_state_t *) se_alloc0(sizeof(mpa_state_t));
209         state->revision = -1;
210         return state;
211 }
212
213 /*
214  * Returns the state associated with a MPA connection or NULL otherwise.
215  */
216 static mpa_state_t *
217 get_mpa_state(conversation_t *conversation)
218 {
219         if (conversation) {
220                 return (mpa_state_t*) conversation_get_proto_data(conversation,
221                                 proto_iwarp_mpa);
222         } else {
223                 return NULL;
224         }
225 }
226
227 /*
228  * Returns the offset of the first Marker in a FPDU where the beginning of a
229  * FPDU has an offset of 0. It also addresses possible sequence number
230  * overflows.
231  * The endpoint is either the Initiator or the Responder.
232  */
233 static guint32
234 get_first_marker_offset(mpa_state_t *state, struct tcpinfo *tcpinfo,
235                 guint8 endpoint)
236 {
237         guint32 offset = 0;
238
239         if (tcpinfo->seq > state->minfo[endpoint].seq) {
240                 offset = (tcpinfo->seq - state->minfo[endpoint].seq)
241                 % MPA_MARKER_INTERVAL;
242         }
243
244         if (tcpinfo->seq < state->minfo[endpoint].seq) {
245                 offset = state->minfo[endpoint].seq
246                 + (TCP_MAX_SEQ - tcpinfo->seq) % MPA_MARKER_INTERVAL;
247         }
248
249         return (MPA_MARKER_INTERVAL - offset) % MPA_MARKER_INTERVAL;
250 }
251
252 /*
253  * Returns the total length of this FPDU under the assumption that a TCP
254  * segement carries only one FPDU.
255  */
256 static guint32
257 fpdu_total_length(struct tcpinfo *tcpinfo)
258 {
259         guint32 size = 0;
260
261         if (tcpinfo->seq < tcpinfo->nxtseq) {
262                 size = tcpinfo->nxtseq - tcpinfo->seq;
263         }
264
265         if (tcpinfo->seq >= tcpinfo->nxtseq) {
266                 size = tcpinfo->nxtseq + (TCP_MAX_SEQ - tcpinfo->seq);
267         }
268
269         return size;
270 }
271
272 /*
273  * Returns the number of Markers of this MPA FPDU. The endpoint is either the
274  * Initiator or the Responder.
275  */
276 static guint32
277 number_of_markers(mpa_state_t *state, struct tcpinfo *tcpinfo, guint8 endpoint)
278 {
279         guint32 size;
280         guint32 offset;
281
282         size = fpdu_total_length(tcpinfo);
283         offset = get_first_marker_offset(state, tcpinfo, endpoint);
284
285         if (offset < size) {
286                 return ((size - offset) / MPA_MARKER_INTERVAL)+1;
287         } else {
288                 return 0;
289         }
290 }
291
292 /*
293  * Removes any Markers from this FPDU by using memcpy or throws an out of memory
294  * exception.
295  */
296 static tvbuff_t *
297 remove_markers(tvbuff_t *tvb, packet_info *pinfo, guint32 marker_offset,
298                 guint32 num_markers, guint32 orig_length)
299 {
300         guint8 *mfree_buff = NULL;
301         guint32 mfree_buff_length, tot_copy, cur_copy;
302         guint32 source_offset;
303         tvbuff_t *mfree_tvb = NULL;
304
305         DISSECTOR_ASSERT(num_markers > 0);
306         DISSECTOR_ASSERT(orig_length > MPA_MARKER_LEN * num_markers);
307         DISSECTOR_ASSERT(tvb_length(tvb) == orig_length);
308
309         /* allocate memory for the marker-free buffer */
310         mfree_buff_length = orig_length - (MPA_MARKER_LEN * num_markers);
311         mfree_buff = g_malloc(mfree_buff_length);
312
313         tot_copy = 0;
314         source_offset = 0;
315         cur_copy = marker_offset;
316         while (tot_copy < mfree_buff_length) {
317                 tvb_memcpy(tvb, mfree_buff+tot_copy, source_offset, cur_copy);
318                 tot_copy += cur_copy;
319                 source_offset += cur_copy + MPA_MARKER_LEN;
320                 cur_copy = MIN(MPA_MARKER_INTERVAL, (mfree_buff_length - tot_copy));
321         }
322         mfree_tvb = tvb_new_child_real_data(tvb, mfree_buff, mfree_buff_length,
323                                             mfree_buff_length);
324         tvb_set_free_cb(mfree_tvb, g_free);
325         add_new_data_source(pinfo, mfree_tvb, "FPDU without Markers");
326
327         return mfree_tvb;
328 }
329
330 /* returns TRUE if this TCP segment carries a MPA REQUEST and FLASE otherwise */
331 static gboolean
332 is_mpa_req(tvbuff_t *tvb, packet_info *pinfo)
333 {
334         conversation_t *conversation = NULL;
335         mpa_state_t *state = NULL;
336         guint8 mcrres;
337
338         if (tvb_get_ntoh64(tvb, 0) != MPA_REQ_REP_FRAME
339                         || tvb_get_ntoh64(tvb, 8) != MPA_ID_REQ_FRAME)
340                 return FALSE;
341
342         conversation = find_or_create_conversation(pinfo);
343
344         if (!get_mpa_state(conversation)) {
345
346                 /* associate a MPA connection state to this conversation if
347                  * there is no MPA state already associated to this connection
348                  */
349                 state = init_mpa_state();
350
351                 /* anaylize MPA connection parameter and record them */
352                 mcrres = tvb_get_guint8(tvb, 16);
353                 state->ini_exp_m_res = mcrres & MPA_MARKER_FLAG;
354                 state->crc = mcrres & MPA_CRC_FLAG;
355                 state->revision = tvb_get_guint8(tvb, 17);
356                 state->req_frame_num = pinfo->fd->num;
357                 state->minfo[MPA_INITIATOR].port = pinfo->srcport;
358                 state->minfo[MPA_RESPONDER].port = pinfo->destport;
359
360                 conversation_add_proto_data(conversation, proto_iwarp_mpa, state);
361
362                 /* update expert info */
363                 if (mcrres & MPA_RESERVED_FLAG)
364                         expert_add_info_format(pinfo, NULL, PI_REQUEST_CODE, PI_WARN,
365                                         "Res field is NOT set to zero as required by RFC 5044");
366
367                 if (state->revision != 1)
368                         expert_add_info_format(pinfo, NULL, PI_REQUEST_CODE, PI_WARN,
369                                         "Rev field is NOT set to one as required by RFC 5044");
370         }
371         return TRUE;
372 }
373
374 /* returns TRUE if this TCP segment carries a MPA REPLY and FALSE otherwise */
375 static gboolean
376 is_mpa_rep(tvbuff_t *tvb, packet_info *pinfo)
377 {
378         conversation_t *conversation = NULL;
379         mpa_state_t *state = NULL;
380         guint8 mcrres;
381
382         if (tvb_get_ntoh64(tvb, 0) != MPA_REQ_REP_FRAME
383                         || tvb_get_ntoh64(tvb, 8) != MPA_ID_REP_FRAME) {
384                 return FALSE;
385         }
386
387         conversation = find_conversation(pinfo->fd->num, &pinfo->src,
388                         &pinfo->dst, pinfo->ptype, pinfo->srcport,
389                         pinfo->destport, 0);
390
391         if (!conversation) {
392                 return FALSE;
393         }
394
395         state = get_mpa_state(conversation);
396         if (!state) {
397                 return FALSE;
398         }
399
400         if (!state->full_operation) {
401                 /* update state of this conversation */
402                 mcrres = tvb_get_guint8(tvb, 16);
403                 state->res_exp_m_ini = mcrres & MPA_MARKER_FLAG;
404                 state->crc = state->crc | (mcrres & MPA_CRC_FLAG);
405                 state->rep_frame_num = pinfo->fd->num;
406
407                  /* enter Full Operation Phase only if the Reject bit is not set */
408                 if (!(mcrres & MPA_REJECT_FLAG))
409                         state->full_operation = TRUE;
410                 else
411                         expert_add_info_format(pinfo, NULL, PI_RESPONSE_CODE, PI_NOTE,
412                                 "Reject bit set by Responder");
413         }
414         return TRUE;
415 }
416
417 /* returns TRUE if this TCP segment carries a MPA FPDU and FALSE otherwise */
418 static gboolean
419 is_mpa_fpdu(packet_info *pinfo)
420 {
421         conversation_t *conversation = NULL;
422         mpa_state_t *state = NULL;
423
424         conversation = find_conversation(pinfo->fd->num, &pinfo->src,
425                         &pinfo->dst, pinfo->ptype, pinfo->srcport,
426                         pinfo->destport, 0);
427
428         if (!conversation) {
429                 return FALSE;
430         }
431
432         state = get_mpa_state(conversation);
433         if (!state) {
434                 return FALSE;
435         }
436
437         /* make sure all MPA connection parameters have been set */
438         if (!state->full_operation) {
439                 return FALSE;
440         }
441
442         if (pinfo->fd->num == state->req_frame_num
443                         || pinfo->fd->num == state->rep_frame_num) {
444                 return FALSE;
445         } else {
446                 return TRUE;
447         }
448 }
449
450 /* update packet list pane in the GUI */
451 static void
452 mpa_packetlist(packet_info *pinfo, gint message_type)
453 {
454         col_set_str(pinfo->cinfo, COL_PROTOCOL, "MPA");
455
456         if (check_col(pinfo->cinfo, COL_INFO)) {
457                 col_add_fstr(pinfo->cinfo, COL_INFO,
458                                 "%d > %d %s", pinfo->srcport, pinfo->destport,
459                                 val_to_str(message_type, mpa_messages,
460                                                 "Unknown %d"));
461         }
462 }
463
464 /* dissects MPA REQUEST or MPA REPLY */
465 static gboolean
466 dissect_mpa_req_rep(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree,
467                 gint message_type)
468 {
469         proto_tree *mpa_tree = NULL;
470         proto_tree *mpa_header_tree = NULL;
471
472         proto_item *mpa_item = NULL;
473         proto_item *mpa_header_item = NULL;
474
475         proto_item* bad_pd_length_pi = NULL;
476
477         guint16 pd_length;
478         guint32 offset = 0;
479
480         mpa_packetlist(pinfo, message_type);
481
482         if (tree) {
483                 mpa_item = proto_tree_add_item(tree, proto_iwarp_mpa, tvb, 0,
484                                 -1, FALSE);
485                 mpa_tree = proto_item_add_subtree(mpa_item, ett_mpa);
486
487                 if (message_type == MPA_REQUEST_FRAME) {
488                         mpa_header_item = proto_tree_add_item(mpa_tree,
489                                         hf_mpa_req, tvb, offset, -1, ENC_NA);
490                         mpa_header_tree = proto_item_add_subtree(
491                                         mpa_header_item, ett_mpa);
492                         proto_tree_add_item(mpa_header_tree, hf_mpa_key_req,
493                                         tvb, offset, MPA_REQ_REP_KEY_LEN, ENC_NA);
494                 }
495
496                 if (message_type == MPA_REPLY_FRAME) {
497                         mpa_header_item = proto_tree_add_item(mpa_tree,
498                                         hf_mpa_rep, tvb, offset, -1, ENC_NA);
499                         mpa_header_tree = proto_item_add_subtree(
500                                         mpa_header_item, ett_mpa);
501                         proto_tree_add_item(mpa_header_tree, hf_mpa_key_rep,
502                                         tvb, offset, MPA_REQ_REP_KEY_LEN, ENC_NA);
503                 }
504                 offset += MPA_REQ_REP_KEY_LEN;
505
506                 proto_tree_add_item(mpa_header_tree, hf_mpa_flag_m, tvb,
507                                 offset, MPA_REQ_REP_FLAG_LEN, FALSE);
508                 proto_tree_add_item(mpa_header_tree, hf_mpa_flag_c, tvb,
509                                 offset, MPA_REQ_REP_FLAG_LEN, FALSE);
510                 proto_tree_add_item(mpa_header_tree, hf_mpa_flag_r, tvb,
511                                 offset, MPA_REQ_REP_FLAG_LEN, FALSE);
512                 proto_tree_add_item(mpa_header_tree, hf_mpa_flag_res, tvb,
513                                 offset, MPA_REQ_REP_FLAG_LEN, FALSE);
514                 offset += MPA_REQ_REP_FLAG_LEN;
515
516                 proto_tree_add_item(mpa_header_tree, hf_mpa_rev, tvb,
517                                 offset, MPA_REQ_REP_REV_LEN, FALSE);
518                 offset += MPA_REQ_REP_REV_LEN;
519
520                 /* check whether the Private Data Length conforms to RFC 5044 */
521                 pd_length = tvb_get_ntohs(tvb, offset);
522                 if (pd_length > MPA_MAX_PD_LENGTH) {
523                         bad_pd_length_pi = proto_tree_add_text(tree, tvb, offset, 2,
524                                 "[PD length field indicates more 512 bytes of Private Data]");
525                         proto_item_set_expert_flags(bad_pd_length_pi,
526                                 PI_MALFORMED, PI_ERROR);
527                         return FALSE;
528                 }
529
530                 proto_tree_add_uint_format_value(mpa_header_tree,
531                                 hf_mpa_pd_length, tvb, offset,
532                                 MPA_REQ_REP_PDLENGTH_LEN, pd_length, "%u bytes",
533                                 pd_length);
534                 offset += MPA_REQ_REP_PDLENGTH_LEN;
535
536                 if (pd_length) {
537                         proto_tree_add_item(mpa_header_tree,
538                                         hf_mpa_private_data, tvb, offset,
539                                         pd_length, ENC_NA);
540                 }
541         }
542         return TRUE;
543 }
544
545 /* returns byte length of the padding */
546 static guint8
547 fpdu_pad_length(guint16 ulpdu_length)
548 {
549         /*
550          * The padding guarantees alignment of 4. Since Markers are 4 bytes long
551          * we do need to take them into consideration for computation of pad
552          * length. The padding length depends only on ULPDU (payload) length and
553          * the length of the header field for the ULPDU length.
554          */
555         guint32 length = ulpdu_length + MPA_ULPDU_LENGTH_LEN;
556
557         /*
558          * The extra % MPA_ALIGNMENT at the end covers for the case
559          * length % MPA_ALIGNMENT == 0.
560          */
561         return (MPA_ALIGNMENT - (length % MPA_ALIGNMENT)) % MPA_ALIGNMENT;
562 }
563
564 /* returns offset for PAD */
565 static guint32
566 pad_offset(struct tcpinfo *tcpinfo, guint32 fpdu_total_len,
567                 guint8 pad_len)
568 {
569         if ((tcpinfo->nxtseq - MPA_CRC_LEN - MPA_MARKER_LEN) % MPA_MARKER_INTERVAL
570                         == 0) {
571                 /* covers the case where a Marker resides between the padding
572                  * and CRC.
573                  */
574                 return fpdu_total_len - MPA_CRC_LEN - MPA_MARKER_LEN - pad_len;
575         } else {
576                 return fpdu_total_len - MPA_CRC_LEN - pad_len;
577         }
578 }
579
580 /* dissects CRC within a FPDU */
581 static void
582 dissect_fpdu_crc(tvbuff_t *tvb, proto_tree *tree, mpa_state_t *state,
583                 guint32 offset, guint32 length)
584 {
585         guint32 crc = 0;
586         guint32 sent_crc = 0;
587
588         if (state->crc) {
589
590                 crc = ~crc32c_calculate(tvb_get_ptr(tvb, 0, length), length,
591                                 CRC32C_PRELOAD);
592
593                 sent_crc = tvb_get_ntohl(tvb, offset); /* crc start offset */
594
595                 if (crc == sent_crc) {
596                         proto_tree_add_uint_format_value(tree,
597                                         hf_mpa_crc_check, tvb, offset, MPA_CRC_LEN,
598                                         sent_crc, "0x%08x (Good CRC32)",
599                                         sent_crc);
600                 } else {
601                         proto_tree_add_uint_format_value(tree,
602                                         hf_mpa_crc_check, tvb, offset, MPA_CRC_LEN,
603                                         sent_crc,
604                                         "0x%08x (Bad CRC32, should be 0x%08x)",
605                                         sent_crc, crc);
606                 }
607         } else {
608                 proto_tree_add_item(tree, hf_mpa_crc, tvb, offset, MPA_CRC_LEN,
609                                 FALSE);
610         }
611 }
612
613 /* dissects Markers within FPDU */
614 static void
615 dissect_fpdu_markers(tvbuff_t *tvb, proto_tree *tree, mpa_state_t *state,
616                 struct tcpinfo *tcpinfo, guint8 endpoint)
617 {
618         proto_tree *mpa_marker_tree;
619         proto_item *mpa_marker_item;
620         guint16 fpduptr;
621         guint32 offset, i;
622
623         mpa_marker_item = proto_tree_add_item(tree, hf_mpa_marker, tvb,
624                         0, -1, ENC_NA);
625         mpa_marker_tree = proto_item_add_subtree(mpa_marker_item, ett_mpa);
626
627         offset = get_first_marker_offset(state, tcpinfo, endpoint);
628
629         for (i=0; i<number_of_markers(state, tcpinfo, endpoint); i++) {
630                 proto_tree_add_item(mpa_marker_tree, hf_mpa_marker_res, tvb,
631                                 offset, MPA_MARKER_RSVD_LEN, FALSE);
632                 fpduptr = (guint16) tvb_get_ntohs(tvb, offset+MPA_MARKER_RSVD_LEN);
633                 proto_tree_add_uint_format_value(mpa_marker_tree,
634                                 hf_mpa_marker_fpduptr, tvb,
635                                 offset+MPA_MARKER_RSVD_LEN,     MPA_MARKER_FPDUPTR_LEN,
636                                 fpduptr, "%u bytes", fpduptr);
637                 offset += MPA_MARKER_INTERVAL;
638         }
639 }
640
641 /* returns the expected value of the 16 bits long MPA FPDU ULPDU LENGTH field */
642 static guint16
643 expected_ulpdu_length(mpa_state_t *state, struct tcpinfo *tcpinfo,
644                 guint8 endpoint)
645 {
646         guint32 length, pad_length, markers_length;
647
648         length = fpdu_total_length(tcpinfo);
649
650         if (length <= MPA_CRC_LEN)
651                 return 0;
652         length -= MPA_CRC_LEN;
653
654         pad_length = (MPA_ALIGNMENT - (length % MPA_ALIGNMENT)) % MPA_ALIGNMENT;
655
656         if (length <= pad_length)
657                 return 0;
658         length -= pad_length;
659
660         if (state->minfo[endpoint].valid) {
661                 markers_length =
662                         number_of_markers(state, tcpinfo, endpoint) * MPA_MARKER_LEN;
663
664                 if (length <= markers_length)
665                         return 0;
666                 length -= markers_length;
667         }
668
669         if (length <= MPA_ULPDU_LENGTH_LEN)
670                 return 0;
671         length -= MPA_ULPDU_LENGTH_LEN;
672
673         return (guint16) length;
674 }
675
676 /* dissects MPA FPDU */
677 static guint16
678 dissect_mpa_fpdu(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree,
679                 mpa_state_t *state, struct tcpinfo *tcpinfo, guint8 endpoint)
680 {
681         proto_item *mpa_item = NULL;
682         proto_item *mpa_header_item = NULL;
683
684         proto_tree *mpa_tree = NULL;
685         proto_tree *mpa_header_tree = NULL;
686
687         proto_item* bad_ulpdu_length_pi = NULL;
688
689         guint8 pad_length;
690         guint16 ulpdu_length, exp_ulpdu_length;
691         guint32 offset, total_length;
692         guint32 num_of_m = 0;
693
694         /*
695          * Initialize starting offset for this FPDU. Deals with the case that this
696          * FPDU may start with a Marker instead of the ULPDU_LENTH header field.
697          */
698         if (state->minfo[endpoint].valid
699                         && get_first_marker_offset(state, tcpinfo, endpoint) == 0) {
700                 offset = MPA_MARKER_LEN;
701         } else {
702                 offset = 0;
703         }
704
705         /* get ULPDU length of this FPDU */
706         ulpdu_length = (guint16) tvb_get_ntohs(tvb, offset);
707
708         mpa_packetlist(pinfo, MPA_FPDU);
709
710         if (state->minfo[endpoint].valid) {
711                 num_of_m = number_of_markers(state, tcpinfo, endpoint);
712         }
713
714
715         if (tree) {
716
717                 /*
718                  * Stop FPDU dissection if the read ULPDU_LENGTH field does NOT contain
719                  * what is expected.
720                  * Reasons for getting a wrong ULPDU_LENGTH can be lost packets (because
721                  * libpcap was not able to capture every packet) or lost alignment (the
722                  * MPA FPDU header does not start right after TCP header).
723                  * We consider the above to be an error since we make the assumption
724                  * that exactly one MPA FPDU is contained in one TCP segement and starts
725                  * always either with a Marker or the ULPDU_LENGTH header field.
726                  */
727                 exp_ulpdu_length = expected_ulpdu_length(state, tcpinfo, endpoint);
728                 if (!exp_ulpdu_length || exp_ulpdu_length != ulpdu_length) {
729                         bad_ulpdu_length_pi = proto_tree_add_text(tree, tvb, offset,
730                                 MPA_ULPDU_LENGTH_LEN,
731                                 "[ULPDU length field does not contain the expected length]");
732                         proto_item_set_expert_flags(bad_ulpdu_length_pi,
733                                 PI_MALFORMED, PI_ERROR);
734                         return 0;
735                 }
736
737                 mpa_item = proto_tree_add_item(tree, proto_iwarp_mpa, tvb, 0,
738                                 -1, FALSE);
739                 mpa_tree = proto_item_add_subtree(mpa_item, ett_mpa);
740
741                 mpa_header_item = proto_tree_add_item(mpa_tree, hf_mpa_fpdu,
742                                 tvb, offset, -1, ENC_NA);
743                 mpa_header_tree = proto_item_add_subtree(mpa_header_item,
744                                 ett_mpa);
745
746                 /* ULPDU Length header field */
747                 proto_tree_add_uint_format_value(mpa_header_tree,
748                                 hf_mpa_ulpdu_length, tvb, offset,
749                                 MPA_ULPDU_LENGTH_LEN, ulpdu_length, "%u bytes",
750                                 ulpdu_length);
751
752                 pad_length = fpdu_pad_length(ulpdu_length);
753
754                 /* Markers are present in this FPDU */
755                 if (state->minfo[endpoint].valid && num_of_m > 0) {
756
757                         total_length = fpdu_total_length(tcpinfo);
758
759                         if (pad_length > 0) {
760                                 proto_tree_add_item(mpa_header_tree, hf_mpa_pad,
761                                                 tvb, pad_offset(tcpinfo,
762                                                                 total_length,
763                                                                 pad_length),
764                                                                 pad_length, ENC_NA);
765                         }
766
767                         dissect_fpdu_crc(tvb, mpa_header_tree, state,
768                                         total_length-MPA_CRC_LEN, num_of_m * MPA_MARKER_LEN +
769                                         ulpdu_length + pad_length + MPA_ULPDU_LENGTH_LEN);
770
771                         dissect_fpdu_markers(tvb, mpa_tree, state, tcpinfo, endpoint);
772
773                 } else { /* Markers are not present or not enabled */
774
775                         offset += MPA_ULPDU_LENGTH_LEN + ulpdu_length;
776
777                         if (pad_length > 0) {
778                                 proto_tree_add_item(mpa_header_tree, hf_mpa_pad, tvb, offset,
779                                                 pad_length, ENC_NA);
780                                 offset += pad_length;
781                         }
782
783                         dissect_fpdu_crc(tvb, mpa_header_tree, state, offset,
784                                         ulpdu_length+pad_length+MPA_ULPDU_LENGTH_LEN);
785                 }
786         }
787         return ulpdu_length;
788 }
789
790 /*
791  * Main dissection routine.
792  */
793 static gboolean
794 dissect_iwarp_mpa(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree)
795 {
796         tvbuff_t *next_tvb = NULL;
797         conversation_t *conversation = NULL;
798         mpa_state_t *state = NULL;
799         struct tcpinfo *tcpinfo = NULL;
800         guint8 endpoint = 3;
801         guint16 ulpdu_length = 0;
802
803         /* FPDU */
804         if (tvb_length(tvb) >= MPA_SMALLEST_FPDU_LEN && is_mpa_fpdu(pinfo)) {
805
806                 tcpinfo = pinfo->private_data;
807
808                 conversation = find_conversation(pinfo->fd->num, &pinfo->src,
809                                 &pinfo->dst, pinfo->ptype, pinfo->srcport, pinfo->destport, 0);
810
811                 state = get_mpa_state(conversation);
812
813                 if (pinfo->srcport == state->minfo[MPA_INITIATOR].port) {
814                         endpoint = MPA_INITIATOR;
815                 } else if (pinfo->srcport == state->minfo[MPA_RESPONDER].port) {
816                         endpoint = MPA_RESPONDER;
817                 } else {
818                         REPORT_DISSECTOR_BUG("endpoint cannot be determined");
819                 }
820
821                 /* Markers are used by either the Initiator or the Responder or both. */
822                 if ((state->ini_exp_m_res || state->res_exp_m_ini) && endpoint <= MPA_RESPONDER) {
823
824                         /* find the TCP sequence number of the first FPDU */
825                         if (!state->minfo[endpoint].valid) {
826                                 state->minfo[endpoint].seq = tcpinfo->seq;
827                                 state->minfo[endpoint].valid = TRUE;
828                         }
829                 }
830
831                 /* dissect FPDU */
832                 ulpdu_length = dissect_mpa_fpdu(tvb, pinfo, tree, state, tcpinfo,
833                                 endpoint);
834
835                 /* an ulpdu_length of 0 should never happen */
836                 if (!ulpdu_length)
837                         return FALSE;
838
839                 /* removes Markers if any and prepares new tvbuff for next dissector */
840                 if (endpoint <= MPA_RESPONDER && state->minfo[endpoint].valid
841                                 && number_of_markers(state, tcpinfo, endpoint) > 0) {
842                         next_tvb = tvb_new_subset(remove_markers(tvb, pinfo,
843                                         get_first_marker_offset(state, tcpinfo, endpoint),
844                                         number_of_markers(state, tcpinfo, endpoint),
845                                         fpdu_total_length(tcpinfo)), MPA_ULPDU_LENGTH_LEN,
846                                         ulpdu_length, ulpdu_length);
847                 } else {
848                         next_tvb = tvb_new_subset(tvb, MPA_ULPDU_LENGTH_LEN, ulpdu_length,
849                                         ulpdu_length);
850                 }
851
852
853                 /* call subdissector */
854                 if (ddp_rdmap_handle) {
855                         call_dissector(ddp_rdmap_handle, next_tvb, pinfo, tree);
856                 } else {
857                         REPORT_DISSECTOR_BUG("ddp_handle was null");
858                 }
859
860                 return TRUE;
861         }
862
863         /* MPA REQUEST or MPA REPLY */
864         if (tvb_length(tvb) >= MPA_REQ_REP_FRAME_HEADER_LEN) {
865                 if (is_mpa_req(tvb, pinfo))
866                         return dissect_mpa_req_rep(tvb, pinfo, tree, MPA_REQUEST_FRAME);
867                 else if (is_mpa_rep(tvb, pinfo))
868                         return dissect_mpa_req_rep(tvb, pinfo, tree, MPA_REPLY_FRAME);
869         }
870         return FALSE;
871 }
872
873 /* registers this protocol with Wireshark */
874 void proto_register_mpa(void)
875 {
876         /* setup list of header fields */
877         static hf_register_info hf[] = {
878                         { &hf_mpa_req, {
879                                         "Request frame header", "iwarp_mpa.req",
880                                         FT_NONE, BASE_NONE, NULL, 0x0,
881                                         NULL, HFILL     } },
882                         { &hf_mpa_rep, {
883                                         "Reply frame header", "iwarp_mpa.rep",
884                                         FT_NONE, BASE_NONE, NULL, 0x0,
885                                         NULL, HFILL } },
886                         { &hf_mpa_fpdu, {
887                                         "FPDU", "iwarp_mpa.fpdu",
888                                         FT_NONE, BASE_NONE, NULL, 0x0,
889                                         NULL, HFILL } },
890                         { &hf_mpa_marker, {
891                                         "Markers", "iwarp_mpa.markers",
892                                         FT_NONE, BASE_NONE, NULL, 0x0,
893                                         NULL, HFILL } },
894                         { &hf_mpa_key_req, {
895                                         "ID Req frame", "iwarp_mpa.key.req",
896                                         FT_BYTES, BASE_NONE, NULL, 0x0,
897                                         NULL, HFILL } },
898                         { &hf_mpa_key_rep, {
899                                         "ID Rep frame", "iwarp_mpa.key.rep",
900                                         FT_BYTES, BASE_NONE, NULL, 0x0,
901                                         NULL, HFILL } },
902                         { &hf_mpa_flag_m, {
903                                         "Marker flag", "iwarp_mpa.marker_flag",
904                                         FT_BOOLEAN, 8, NULL, MPA_MARKER_FLAG,
905                                         NULL, HFILL } },
906                         { &hf_mpa_flag_c, {
907                                         "CRC flag", "iwarp_mpa.crc_flag",
908                                         FT_BOOLEAN, 8, NULL, MPA_CRC_FLAG,
909                                         NULL, HFILL } },
910                         { &hf_mpa_flag_r, {
911                                         "Connection rejected flag",
912                                         "iwarp_mpa.rej_flag", FT_BOOLEAN, 8, NULL, MPA_REJECT_FLAG,
913                                         NULL, HFILL } },
914                         { &hf_mpa_flag_res, {
915                                         "Reserved", "iwarp_mpa.res",
916                                         FT_UINT8, BASE_HEX, NULL, MPA_RESERVED_FLAG,
917                                         NULL, HFILL } },
918                         { &hf_mpa_rev, {
919                                         "Revision", "iwarp_mpa.rev",
920                                         FT_UINT8, BASE_DEC, NULL, 0x0,
921                                         NULL, HFILL } },
922                         { &hf_mpa_pd_length, {
923                                         "Private data length", "iwarp_mpa.pdlength",
924                                         FT_UINT16, BASE_DEC, NULL, 0x0,
925                                         NULL, HFILL } },
926                         { &hf_mpa_private_data, {
927                                         "Private data", "iwarp_mpa.privatedata",
928                                         FT_BYTES, BASE_NONE, NULL, 0x0,
929                                         NULL, HFILL } },
930                         { &hf_mpa_ulpdu_length, {
931                                         "ULPDU length", "iwarp_mpa.ulpdulength",
932                                         FT_UINT16, BASE_DEC, NULL, 0x0,
933                                         NULL, HFILL } },
934                         { &hf_mpa_pad, {
935                                         "Padding", "iwarp_mpa.pad",
936                                         FT_BYTES, BASE_NONE, NULL, 0x0,
937                                         NULL, HFILL } },
938                         { &hf_mpa_crc, {
939                                         "CRC", "iwarp_mpa.crc",
940                                         FT_UINT32, BASE_HEX, NULL, 0x0,
941                                         NULL, HFILL } },
942                         { &hf_mpa_crc_check, {
943                                         "CRC check", "iwarp_mpa.crc_check",
944                                         FT_UINT32, BASE_HEX, NULL, 0x0,
945                                         NULL, HFILL } },
946                         { &hf_mpa_marker_res, {
947                                         "Reserved", "iwarp_mpa.marker_res",
948                                         FT_UINT16, BASE_HEX, NULL, 0x0,
949                                         "Marker: Reserved", HFILL } },
950                         { &hf_mpa_marker_fpduptr, {
951                                         "FPDU back pointer", "iwarp_mpa.marker_fpduptr",
952                                         FT_UINT16, BASE_DEC, NULL, 0x0,
953                                         "Marker: FPDU Pointer", HFILL } }
954         };
955
956         /* setup protocol subtree array */
957         static gint *ett[] = {
958                         &ett_mpa,
959                         &ett_mpa_req,
960                         &ett_mpa_rep,
961                         &ett_mpa_fpdu,
962                         &ett_mpa_marker
963         };
964
965         /* register the protocol name and description */
966         proto_iwarp_mpa = proto_register_protocol(
967                 "iWARP Marker Protocol data unit Aligned framing",
968                 "IWARP_MPA", "iwarp_mpa");
969
970         /* required function calls to register the header fields and subtrees */
971         proto_register_field_array(proto_iwarp_mpa, hf, array_length(hf));
972         proto_register_subtree_array(ett, array_length(ett));
973
974 }
975
976 void
977 proto_reg_handoff_mpa(void)
978 {
979         /*
980          * MPA does not use any specific TCP port so, when not on a specific
981          * port, try this dissector whenever there is TCP traffic.
982          */
983         heur_dissector_add("tcp", dissect_iwarp_mpa, proto_iwarp_mpa);
984         ddp_rdmap_handle = find_dissector("iwarp_ddp_rdmap");
985 }