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