MSWSP: fix dissect_mswsp_smb()
[wireshark-wip.git] / epan / dissectors / packet-iwarp-mpa.c
blob71cf53c1bb307082fcdc3a9fdadf330f5aab3d29
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>
7 * $Id$
9 * Wireshark - Network traffic analyzer
10 * By Gerald Combs <gerald@wireshark.org>
11 * Copyright 1998 Gerald Combs
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.
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.
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.
29 /* INCLUDES */
30 #include "config.h"
32 #include <epan/packet.h>
33 #include <epan/wmem/wmem.h>
34 #include <epan/conversation.h>
35 #include <epan/dissectors/packet-tcp.h>
36 #include <epan/expert.h>
37 #include <wsutil/crc32.h>
39 /* DEFINES */
41 /* header field byte lengths */
42 #define MPA_REQ_REP_FRAME_HEADER_LEN 20
43 #define MPA_PDLENGTH_LEN 2
44 #define MPA_ULPDU_LENGTH_LEN 2
45 #define MPA_MARKER_LEN 4
46 #define MPA_SMALLEST_FPDU_LEN 8
47 #define MPA_REQ_REP_KEY_LEN 16
48 #define MPA_REQ_REP_FLAG_LEN 1
49 #define MPA_REQ_REP_REV_LEN 1
50 #define MPA_REQ_REP_PDLENGTH_LEN 2
51 #define MPA_MARKER_RSVD_LEN 2
52 #define MPA_MARKER_FPDUPTR_LEN 2
53 #define MPA_CRC_LEN 4
55 /* protocol constants */
56 #define MPA_REQ_REP_FRAME G_GINT64_CONSTANT(0x4d50412049442052U)
57 #define MPA_ID_REQ_FRAME G_GINT64_CONSTANT(0x6571204672616d65U)
58 #define MPA_ID_REP_FRAME G_GINT64_CONSTANT(0x6570204672616d65U)
59 #define MPA_MARKER_INTERVAL 512
60 #define MPA_MAX_PD_LENGTH 512
61 #define MPA_ALIGNMENT 4
62 #define TCP_MAX_SEQ ((guint32) 0xffffffff)
64 /* for code readability */
65 #define MPA_REQUEST_FRAME 1
66 #define MPA_REPLY_FRAME 2
67 #define MPA_FPDU 3
68 #define MPA_INITIATOR 0
69 #define MPA_RESPONDER 1
71 /* bitmasks */
72 #define MPA_MARKER_FLAG 0x80
73 #define MPA_CRC_FLAG 0x40
74 #define MPA_REJECT_FLAG 0x20
75 #define MPA_RESERVED_FLAG 0x1F
77 /* GLOBALS */
79 /* initialize the protocol and registered fields */
80 static gint proto_iwarp_mpa = -1;
82 static gint hf_mpa_req = -1;
83 static gint hf_mpa_rep = -1;
84 static gint hf_mpa_fpdu = -1;
85 static gint hf_mpa_marker = -1;
87 static gint hf_mpa_key_req = -1;
88 static gint hf_mpa_key_rep = -1;
89 static gint hf_mpa_flag_m = -1;
90 static gint hf_mpa_flag_c = -1;
91 static gint hf_mpa_flag_r = -1;
92 static gint hf_mpa_flag_res = -1;
93 static gint hf_mpa_rev = -1;
94 static gint hf_mpa_pd_length = -1;
95 static gint hf_mpa_private_data = -1;
97 static gint hf_mpa_ulpdu_length = -1;
98 static gint hf_mpa_pad = -1;
99 static gint hf_mpa_crc = -1;
100 static gint hf_mpa_crc_check = -1;
102 static gint hf_mpa_marker_res = -1;
103 static gint hf_mpa_marker_fpduptr = -1;
105 /* initialize the subtree pointers */
106 static gint ett_mpa = -1;
108 static gint ett_mpa_req = -1;
109 static gint ett_mpa_rep = -1;
110 static gint ett_mpa_fpdu = -1;
111 static gint ett_mpa_marker = -1;
113 static expert_field ei_mpa_res_field_not_set0 = EI_INIT;
114 static expert_field ei_mpa_rev_field_not_set1 = EI_INIT;
115 static expert_field ei_mpa_reject_bit_responder = EI_INIT;
116 static expert_field ei_mpa_bad_length = EI_INIT;
118 /* handles of our subdissectors */
119 static dissector_handle_t ddp_rdmap_handle = NULL;
121 static const value_string mpa_messages[] = {
122 { MPA_REQUEST_FRAME, "MPA Request Frame" },
123 { MPA_REPLY_FRAME, "MPA Reply Frame" },
124 { MPA_FPDU, "MPA FPDU" },
125 { 0, NULL }
129 * CONNECTION STATE and MARKERS
130 * A MPA endpoint operates in two distinct phases.
131 * The Startup Phase is used to verify correct MPA setup, exchange CRC
132 * and Marker configuration, and optionally pass Private Data between
133 * endpoints prior to completing a DDP connection.
134 * The second distinct phase is Full Operation during which FPDUs are
135 * sent using all the rules that pertain (CRC, Markers, MULPDU,
136 * restrictions etc.).
137 * To keep track of a MPA connection configuration a mpa_state is declared
138 * and maintained per TCP connection, i.e. it is associated to a conversation
139 * between two endpoints.
141 * In some configurations MPA places MARKERs in a FPDU every 512th octet with
142 * respect to the TCP sequence number of the first FPDU. The struct minfo_t
143 * records the source port of a peer that has to insert Markers into its FPDUs
144 * as well as the TCP sequence number of its first FPDU. This information is
145 * necessary to locate the markers within a FPDU afterwards. Itis part of a
146 * mpa_state.
150 * This struct is used to record the source port 'port' and the TCP sequence
151 * number 'seq' of the first FPDU. This information is used to determine the
152 * position of the first Marker within the following FPDUs. The boolean 'valid'
153 * specifies if Markers are inserted by the endpoint running on source port
154 * 'port' or not.
156 struct minfo {
157 guint16 port;
158 guint32 seq;
159 gboolean valid;
161 typedef struct minfo minfo_t;
164 * This struct represents a MPA connection state. It specifies if Markers and
165 * CRC is used for the following FPDUs. It also contains information to
166 * distinguish between the MPA Startup and Full Operation Phase.the connection
167 * parameters negotiated between to MPA endpoints during the MPA Startup Phase
168 * as well as other information for the dissection.
170 * The two MPA endpoints are called Initiator, the sender of the MPA Request,
171 * and Responder, the sender of the MPA Reply.
173 * @full_operation: TRUE if is this state is valid and FLASE otherwise.
174 * @req_frame_num: Frame number of the MPA Request to distinguish this frame
175 * from later FPDUs.
176 * @rep_frame_num: Frame number of the MPA Reply to distinguish this frame
177 * from later FPDUs.
178 * @ini_exp_m_res: TRUE if the Initiator expects the Responder to insert
179 * Markers into his FPDUs sent to Initiator and FALSE otherwise.
180 * @res_exp_m_ini: TRUE if the Responder expects the Initiator to insert
181 * Markers into his FPDUs sent to Responder and FALSE otherwise.
182 * @minfo[2]: Array of minfo_t whichs holds necessary information to
183 * determine the start position of the first Marker within a
184 * a FPDU.
185 * minfo[0] is used for the Initiator endpoint
186 * minfo[1] is used for the Responder endpoint
187 * @crc: TRUE if CRC is used by both endpoints and FLASE otherwise.
188 * @revision: Stores the MPA protocol revision number.
190 struct mpa_state {
191 gboolean full_operation;
192 guint req_frame_num;
193 guint rep_frame_num;
194 gboolean ini_exp_m_res;
195 gboolean res_exp_m_ini;
196 minfo_t minfo[2];
197 gboolean crc;
198 gint revision;
200 typedef struct mpa_state mpa_state_t;
203 * Returns an initialized MPA connection state or throws an out of
204 * memory exception.
206 static mpa_state_t *
207 init_mpa_state(void)
209 mpa_state_t *state;
211 state = (mpa_state_t *) wmem_alloc0(wmem_file_scope(), sizeof(mpa_state_t));
212 state->revision = -1;
213 return state;
217 * Returns the state associated with a MPA connection or NULL otherwise.
219 static mpa_state_t *
220 get_mpa_state(conversation_t *conversation)
222 if (conversation) {
223 return (mpa_state_t*) conversation_get_proto_data(conversation,
224 proto_iwarp_mpa);
225 } else {
226 return NULL;
231 * Returns the offset of the first Marker in a FPDU where the beginning of a
232 * FPDU has an offset of 0. It also addresses possible sequence number
233 * overflows.
234 * The endpoint is either the Initiator or the Responder.
236 static guint32
237 get_first_marker_offset(mpa_state_t *state, struct tcpinfo *tcpinfo,
238 guint8 endpoint)
240 guint32 offset = 0;
242 if (tcpinfo->seq > state->minfo[endpoint].seq) {
243 offset = (tcpinfo->seq - state->minfo[endpoint].seq)
244 % MPA_MARKER_INTERVAL;
247 if (tcpinfo->seq < state->minfo[endpoint].seq) {
248 offset = state->minfo[endpoint].seq
249 + (TCP_MAX_SEQ - tcpinfo->seq) % MPA_MARKER_INTERVAL;
252 return (MPA_MARKER_INTERVAL - offset) % MPA_MARKER_INTERVAL;
256 * Returns the total length of this FPDU under the assumption that a TCP
257 * segement carries only one FPDU.
259 static guint32
260 fpdu_total_length(struct tcpinfo *tcpinfo)
262 guint32 size = 0;
264 if (tcpinfo->seq < tcpinfo->nxtseq) {
265 size = tcpinfo->nxtseq - tcpinfo->seq;
268 if (tcpinfo->seq >= tcpinfo->nxtseq) {
269 size = tcpinfo->nxtseq + (TCP_MAX_SEQ - tcpinfo->seq);
272 return size;
276 * Returns the number of Markers of this MPA FPDU. The endpoint is either the
277 * Initiator or the Responder.
279 static guint32
280 number_of_markers(mpa_state_t *state, struct tcpinfo *tcpinfo, guint8 endpoint)
282 guint32 size;
283 guint32 offset;
285 size = fpdu_total_length(tcpinfo);
286 offset = get_first_marker_offset(state, tcpinfo, endpoint);
288 if (offset < size) {
289 return ((size - offset) / MPA_MARKER_INTERVAL)+1;
290 } else {
291 return 0;
296 * Removes any Markers from this FPDU by using memcpy or throws an out of memory
297 * exception.
299 static tvbuff_t *
300 remove_markers(tvbuff_t *tvb, packet_info *pinfo, guint32 marker_offset,
301 guint32 num_markers, guint32 orig_length)
303 guint8 *mfree_buff = NULL;
304 guint32 mfree_buff_length, tot_copy, cur_copy;
305 guint32 source_offset;
306 tvbuff_t *mfree_tvb = NULL;
308 DISSECTOR_ASSERT(num_markers > 0);
309 DISSECTOR_ASSERT(orig_length > MPA_MARKER_LEN * num_markers);
310 DISSECTOR_ASSERT(tvb_length(tvb) == orig_length);
312 /* allocate memory for the marker-free buffer */
313 mfree_buff_length = orig_length - (MPA_MARKER_LEN * num_markers);
314 mfree_buff = (guint8 *)wmem_alloc(pinfo->pool, mfree_buff_length);
316 tot_copy = 0;
317 source_offset = 0;
318 cur_copy = marker_offset;
319 while (tot_copy < mfree_buff_length) {
320 tvb_memcpy(tvb, mfree_buff+tot_copy, source_offset, cur_copy);
321 tot_copy += cur_copy;
322 source_offset += cur_copy + MPA_MARKER_LEN;
323 cur_copy = MIN(MPA_MARKER_INTERVAL, (mfree_buff_length - tot_copy));
325 mfree_tvb = tvb_new_child_real_data(tvb, mfree_buff, mfree_buff_length,
326 mfree_buff_length);
327 add_new_data_source(pinfo, mfree_tvb, "FPDU without Markers");
329 return mfree_tvb;
332 /* returns TRUE if this TCP segment carries a MPA REQUEST and FLASE otherwise */
333 static gboolean
334 is_mpa_req(tvbuff_t *tvb, packet_info *pinfo)
336 conversation_t *conversation = NULL;
337 mpa_state_t *state = NULL;
338 guint8 mcrres;
340 if (tvb_get_ntoh64(tvb, 0) != MPA_REQ_REP_FRAME
341 || tvb_get_ntoh64(tvb, 8) != MPA_ID_REQ_FRAME)
342 return FALSE;
344 conversation = find_or_create_conversation(pinfo);
346 if (!get_mpa_state(conversation)) {
348 /* associate a MPA connection state to this conversation if
349 * there is no MPA state already associated to this connection
351 state = init_mpa_state();
353 /* anaylize MPA connection parameter and record them */
354 mcrres = tvb_get_guint8(tvb, 16);
355 state->ini_exp_m_res = mcrres & MPA_MARKER_FLAG;
356 state->crc = mcrres & MPA_CRC_FLAG;
357 state->revision = tvb_get_guint8(tvb, 17);
358 state->req_frame_num = pinfo->fd->num;
359 state->minfo[MPA_INITIATOR].port = pinfo->srcport;
360 state->minfo[MPA_RESPONDER].port = pinfo->destport;
362 conversation_add_proto_data(conversation, proto_iwarp_mpa, state);
364 /* update expert info */
365 if (mcrres & MPA_RESERVED_FLAG)
366 expert_add_info(pinfo, NULL, &ei_mpa_res_field_not_set0);
368 if (state->revision != 1)
369 expert_add_info(pinfo, NULL, &ei_mpa_rev_field_not_set1);
371 return TRUE;
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)
378 conversation_t *conversation = NULL;
379 mpa_state_t *state = NULL;
380 guint8 mcrres;
382 if (tvb_get_ntoh64(tvb, 0) != MPA_REQ_REP_FRAME
383 || tvb_get_ntoh64(tvb, 8) != MPA_ID_REP_FRAME) {
384 return FALSE;
387 conversation = find_conversation(pinfo->fd->num, &pinfo->src,
388 &pinfo->dst, pinfo->ptype, pinfo->srcport,
389 pinfo->destport, 0);
391 if (!conversation) {
392 return FALSE;
395 state = get_mpa_state(conversation);
396 if (!state) {
397 return FALSE;
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;
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(pinfo, NULL, &ei_mpa_reject_bit_responder);
413 return TRUE;
416 /* returns TRUE if this TCP segment carries a MPA FPDU and FALSE otherwise */
417 static gboolean
418 is_mpa_fpdu(packet_info *pinfo)
420 conversation_t *conversation = NULL;
421 mpa_state_t *state = NULL;
423 conversation = find_conversation(pinfo->fd->num, &pinfo->src,
424 &pinfo->dst, pinfo->ptype, pinfo->srcport,
425 pinfo->destport, 0);
427 if (!conversation) {
428 return FALSE;
431 state = get_mpa_state(conversation);
432 if (!state) {
433 return FALSE;
436 /* make sure all MPA connection parameters have been set */
437 if (!state->full_operation) {
438 return FALSE;
441 if (pinfo->fd->num == state->req_frame_num
442 || pinfo->fd->num == state->rep_frame_num) {
443 return FALSE;
444 } else {
445 return TRUE;
449 /* update packet list pane in the GUI */
450 static void
451 mpa_packetlist(packet_info *pinfo, gint message_type)
453 col_set_str(pinfo->cinfo, COL_PROTOCOL, "MPA");
455 col_add_fstr(pinfo->cinfo, COL_INFO,
456 "%d > %d %s", pinfo->srcport, pinfo->destport,
457 val_to_str(message_type, mpa_messages,
458 "Unknown %d"));
461 /* dissects MPA REQUEST or MPA REPLY */
462 static gboolean
463 dissect_mpa_req_rep(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree,
464 gint message_type)
466 proto_tree *mpa_tree = NULL;
467 proto_tree *mpa_header_tree = NULL;
469 proto_item *mpa_item = NULL;
470 proto_item *mpa_header_item = NULL;
472 guint16 pd_length;
473 guint32 offset = 0;
475 mpa_packetlist(pinfo, message_type);
477 if (tree) {
478 mpa_item = proto_tree_add_item(tree, proto_iwarp_mpa, tvb, 0,
479 -1, ENC_NA);
480 mpa_tree = proto_item_add_subtree(mpa_item, ett_mpa);
482 if (message_type == MPA_REQUEST_FRAME) {
483 mpa_header_item = proto_tree_add_item(mpa_tree,
484 hf_mpa_req, tvb, offset, -1, ENC_NA);
485 mpa_header_tree = proto_item_add_subtree(
486 mpa_header_item, ett_mpa);
487 proto_tree_add_item(mpa_header_tree, hf_mpa_key_req,
488 tvb, offset, MPA_REQ_REP_KEY_LEN, ENC_NA);
491 if (message_type == MPA_REPLY_FRAME) {
492 mpa_header_item = proto_tree_add_item(mpa_tree,
493 hf_mpa_rep, tvb, offset, -1, ENC_NA);
494 mpa_header_tree = proto_item_add_subtree(
495 mpa_header_item, ett_mpa);
496 proto_tree_add_item(mpa_header_tree, hf_mpa_key_rep,
497 tvb, offset, MPA_REQ_REP_KEY_LEN, ENC_NA);
499 offset += MPA_REQ_REP_KEY_LEN;
501 proto_tree_add_item(mpa_header_tree, hf_mpa_flag_m, tvb,
502 offset, MPA_REQ_REP_FLAG_LEN, ENC_BIG_ENDIAN);
503 proto_tree_add_item(mpa_header_tree, hf_mpa_flag_c, tvb,
504 offset, MPA_REQ_REP_FLAG_LEN, ENC_BIG_ENDIAN);
505 proto_tree_add_item(mpa_header_tree, hf_mpa_flag_r, tvb,
506 offset, MPA_REQ_REP_FLAG_LEN, ENC_BIG_ENDIAN);
507 proto_tree_add_item(mpa_header_tree, hf_mpa_flag_res, tvb,
508 offset, MPA_REQ_REP_FLAG_LEN, ENC_BIG_ENDIAN);
509 offset += MPA_REQ_REP_FLAG_LEN;
511 proto_tree_add_item(mpa_header_tree, hf_mpa_rev, tvb,
512 offset, MPA_REQ_REP_REV_LEN, ENC_BIG_ENDIAN);
513 offset += MPA_REQ_REP_REV_LEN;
515 /* check whether the Private Data Length conforms to RFC 5044 */
516 pd_length = tvb_get_ntohs(tvb, offset);
517 if (pd_length > MPA_MAX_PD_LENGTH) {
518 proto_tree_add_expert_format(tree, pinfo, &ei_mpa_bad_length, tvb, offset, 2,
519 "[PD length field indicates more 512 bytes of Private Data]");
520 return FALSE;
523 proto_tree_add_uint_format_value(mpa_header_tree,
524 hf_mpa_pd_length, tvb, offset,
525 MPA_REQ_REP_PDLENGTH_LEN, pd_length, "%u bytes",
526 pd_length);
527 offset += MPA_REQ_REP_PDLENGTH_LEN;
529 if (pd_length) {
530 proto_tree_add_item(mpa_header_tree,
531 hf_mpa_private_data, tvb, offset,
532 pd_length, ENC_NA);
535 return TRUE;
538 /* returns byte length of the padding */
539 static guint8
540 fpdu_pad_length(guint16 ulpdu_length)
543 * The padding guarantees alignment of 4. Since Markers are 4 bytes long
544 * we do need to take them into consideration for computation of pad
545 * length. The padding length depends only on ULPDU (payload) length and
546 * the length of the header field for the ULPDU length.
548 guint32 length = ulpdu_length + MPA_ULPDU_LENGTH_LEN;
551 * The extra % MPA_ALIGNMENT at the end covers for the case
552 * length % MPA_ALIGNMENT == 0.
554 return (MPA_ALIGNMENT - (length % MPA_ALIGNMENT)) % MPA_ALIGNMENT;
557 /* returns offset for PAD */
558 static guint32
559 pad_offset(struct tcpinfo *tcpinfo, guint32 fpdu_total_len,
560 guint8 pad_len)
562 if ((tcpinfo->nxtseq - MPA_CRC_LEN - MPA_MARKER_LEN) % MPA_MARKER_INTERVAL
563 == 0) {
564 /* covers the case where a Marker resides between the padding
565 * and CRC.
567 return fpdu_total_len - MPA_CRC_LEN - MPA_MARKER_LEN - pad_len;
568 } else {
569 return fpdu_total_len - MPA_CRC_LEN - pad_len;
573 /* dissects CRC within a FPDU */
574 static void
575 dissect_fpdu_crc(tvbuff_t *tvb, proto_tree *tree, mpa_state_t *state,
576 guint32 offset, guint32 length)
578 guint32 crc = 0;
579 guint32 sent_crc = 0;
581 if (state->crc) {
583 crc = ~crc32c_calculate(tvb_get_ptr(tvb, 0, length), length,
584 CRC32C_PRELOAD);
586 sent_crc = tvb_get_ntohl(tvb, offset); /* crc start offset */
588 if (crc == sent_crc) {
589 proto_tree_add_uint_format_value(tree,
590 hf_mpa_crc_check, tvb, offset, MPA_CRC_LEN,
591 sent_crc, "0x%08x (Good CRC32)",
592 sent_crc);
593 } else {
594 proto_tree_add_uint_format_value(tree,
595 hf_mpa_crc_check, tvb, offset, MPA_CRC_LEN,
596 sent_crc,
597 "0x%08x (Bad CRC32, should be 0x%08x)",
598 sent_crc, crc);
600 } else {
601 proto_tree_add_item(tree, hf_mpa_crc, tvb, offset, MPA_CRC_LEN,
602 ENC_BIG_ENDIAN);
606 /* dissects Markers within FPDU */
607 static void
608 dissect_fpdu_markers(tvbuff_t *tvb, proto_tree *tree, mpa_state_t *state,
609 struct tcpinfo *tcpinfo, guint8 endpoint)
611 proto_tree *mpa_marker_tree;
612 proto_item *mpa_marker_item;
613 guint16 fpduptr;
614 guint32 offset, i;
616 mpa_marker_item = proto_tree_add_item(tree, hf_mpa_marker, tvb,
617 0, -1, ENC_NA);
618 mpa_marker_tree = proto_item_add_subtree(mpa_marker_item, ett_mpa);
620 offset = get_first_marker_offset(state, tcpinfo, endpoint);
622 for (i=0; i<number_of_markers(state, tcpinfo, endpoint); i++) {
623 proto_tree_add_item(mpa_marker_tree, hf_mpa_marker_res, tvb,
624 offset, MPA_MARKER_RSVD_LEN, ENC_BIG_ENDIAN);
625 fpduptr = (guint16) tvb_get_ntohs(tvb, offset+MPA_MARKER_RSVD_LEN);
626 proto_tree_add_uint_format_value(mpa_marker_tree,
627 hf_mpa_marker_fpduptr, tvb,
628 offset+MPA_MARKER_RSVD_LEN, MPA_MARKER_FPDUPTR_LEN,
629 fpduptr, "%u bytes", fpduptr);
630 offset += MPA_MARKER_INTERVAL;
634 /* returns the expected value of the 16 bits long MPA FPDU ULPDU LENGTH field */
635 static guint16
636 expected_ulpdu_length(mpa_state_t *state, struct tcpinfo *tcpinfo,
637 guint8 endpoint)
639 guint32 length, pad_length, markers_length;
641 length = fpdu_total_length(tcpinfo);
643 if (length <= MPA_CRC_LEN)
644 return 0;
645 length -= MPA_CRC_LEN;
647 pad_length = (MPA_ALIGNMENT - (length % MPA_ALIGNMENT)) % MPA_ALIGNMENT;
649 if (length <= pad_length)
650 return 0;
651 length -= pad_length;
653 if (state->minfo[endpoint].valid) {
654 markers_length =
655 number_of_markers(state, tcpinfo, endpoint) * MPA_MARKER_LEN;
657 if (length <= markers_length)
658 return 0;
659 length -= markers_length;
662 if (length <= MPA_ULPDU_LENGTH_LEN)
663 return 0;
664 length -= MPA_ULPDU_LENGTH_LEN;
666 return (guint16) length;
669 /* dissects MPA FPDU */
670 static guint16
671 dissect_mpa_fpdu(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree,
672 mpa_state_t *state, struct tcpinfo *tcpinfo, guint8 endpoint)
674 proto_item *mpa_item = NULL;
675 proto_item *mpa_header_item = NULL;
677 proto_tree *mpa_tree = NULL;
678 proto_tree *mpa_header_tree = NULL;
680 guint8 pad_length;
681 guint16 ulpdu_length, exp_ulpdu_length;
682 guint32 offset, total_length;
683 guint32 num_of_m = 0;
686 * Initialize starting offset for this FPDU. Deals with the case that this
687 * FPDU may start with a Marker instead of the ULPDU_LENTH header field.
689 if (state->minfo[endpoint].valid
690 && get_first_marker_offset(state, tcpinfo, endpoint) == 0) {
691 offset = MPA_MARKER_LEN;
692 } else {
693 offset = 0;
696 /* get ULPDU length of this FPDU */
697 ulpdu_length = (guint16) tvb_get_ntohs(tvb, offset);
699 mpa_packetlist(pinfo, MPA_FPDU);
701 if (state->minfo[endpoint].valid) {
702 num_of_m = number_of_markers(state, tcpinfo, endpoint);
706 if (tree) {
709 * Stop FPDU dissection if the read ULPDU_LENGTH field does NOT contain
710 * what is expected.
711 * Reasons for getting a wrong ULPDU_LENGTH can be lost packets (because
712 * libpcap was not able to capture every packet) or lost alignment (the
713 * MPA FPDU header does not start right after TCP header).
714 * We consider the above to be an error since we make the assumption
715 * that exactly one MPA FPDU is contained in one TCP segement and starts
716 * always either with a Marker or the ULPDU_LENGTH header field.
718 exp_ulpdu_length = expected_ulpdu_length(state, tcpinfo, endpoint);
719 if (!exp_ulpdu_length || exp_ulpdu_length != ulpdu_length) {
720 proto_tree_add_expert_format(tree, pinfo, &ei_mpa_bad_length, tvb, offset,
721 MPA_ULPDU_LENGTH_LEN,
722 "[ULPDU length field does not contain the expected length]");
723 return 0;
726 mpa_item = proto_tree_add_item(tree, proto_iwarp_mpa, tvb, 0,
727 -1, ENC_NA);
728 mpa_tree = proto_item_add_subtree(mpa_item, ett_mpa);
730 mpa_header_item = proto_tree_add_item(mpa_tree, hf_mpa_fpdu,
731 tvb, offset, -1, ENC_NA);
732 mpa_header_tree = proto_item_add_subtree(mpa_header_item,
733 ett_mpa);
735 /* ULPDU Length header field */
736 proto_tree_add_uint_format_value(mpa_header_tree,
737 hf_mpa_ulpdu_length, tvb, offset,
738 MPA_ULPDU_LENGTH_LEN, ulpdu_length, "%u bytes",
739 ulpdu_length);
741 pad_length = fpdu_pad_length(ulpdu_length);
743 /* Markers are present in this FPDU */
744 if (state->minfo[endpoint].valid && num_of_m > 0) {
746 total_length = fpdu_total_length(tcpinfo);
748 if (pad_length > 0) {
749 proto_tree_add_item(mpa_header_tree, hf_mpa_pad,
750 tvb, pad_offset(tcpinfo,
751 total_length,
752 pad_length),
753 pad_length, ENC_NA);
756 dissect_fpdu_crc(tvb, mpa_header_tree, state,
757 total_length-MPA_CRC_LEN, num_of_m * MPA_MARKER_LEN +
758 ulpdu_length + pad_length + MPA_ULPDU_LENGTH_LEN);
760 dissect_fpdu_markers(tvb, mpa_tree, state, tcpinfo, endpoint);
762 } else { /* Markers are not present or not enabled */
764 offset += MPA_ULPDU_LENGTH_LEN + ulpdu_length;
766 if (pad_length > 0) {
767 proto_tree_add_item(mpa_header_tree, hf_mpa_pad, tvb, offset,
768 pad_length, ENC_NA);
769 offset += pad_length;
772 dissect_fpdu_crc(tvb, mpa_header_tree, state, offset,
773 ulpdu_length+pad_length+MPA_ULPDU_LENGTH_LEN);
776 return ulpdu_length;
780 * Main dissection routine.
782 static gboolean
783 dissect_iwarp_mpa(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void *data)
785 tvbuff_t *next_tvb = NULL;
786 conversation_t *conversation = NULL;
787 mpa_state_t *state = NULL;
788 struct tcpinfo *tcpinfo = (struct tcpinfo *)data;
789 guint8 endpoint = 3;
790 guint16 ulpdu_length = 0;
792 /* FPDU */
793 if (tvb_length(tvb) >= MPA_SMALLEST_FPDU_LEN && is_mpa_fpdu(pinfo)) {
795 conversation = find_conversation(pinfo->fd->num, &pinfo->src,
796 &pinfo->dst, pinfo->ptype, pinfo->srcport, pinfo->destport, 0);
798 state = get_mpa_state(conversation);
800 if (pinfo->srcport == state->minfo[MPA_INITIATOR].port) {
801 endpoint = MPA_INITIATOR;
802 } else if (pinfo->srcport == state->minfo[MPA_RESPONDER].port) {
803 endpoint = MPA_RESPONDER;
804 } else {
805 REPORT_DISSECTOR_BUG("endpoint cannot be determined");
808 /* Markers are used by either the Initiator or the Responder or both. */
809 if ((state->ini_exp_m_res || state->res_exp_m_ini) && endpoint <= MPA_RESPONDER) {
811 /* find the TCP sequence number of the first FPDU */
812 if (!state->minfo[endpoint].valid) {
813 state->minfo[endpoint].seq = tcpinfo->seq;
814 state->minfo[endpoint].valid = TRUE;
818 /* dissect FPDU */
819 ulpdu_length = dissect_mpa_fpdu(tvb, pinfo, tree, state, tcpinfo,
820 endpoint);
822 /* an ulpdu_length of 0 should never happen */
823 if (!ulpdu_length)
824 return FALSE;
826 /* removes Markers if any and prepares new tvbuff for next dissector */
827 if (endpoint <= MPA_RESPONDER && state->minfo[endpoint].valid
828 && number_of_markers(state, tcpinfo, endpoint) > 0) {
829 next_tvb = tvb_new_subset(remove_markers(tvb, pinfo,
830 get_first_marker_offset(state, tcpinfo, endpoint),
831 number_of_markers(state, tcpinfo, endpoint),
832 fpdu_total_length(tcpinfo)), MPA_ULPDU_LENGTH_LEN,
833 ulpdu_length, ulpdu_length);
834 } else {
835 next_tvb = tvb_new_subset(tvb, MPA_ULPDU_LENGTH_LEN, ulpdu_length,
836 ulpdu_length);
840 /* call subdissector */
841 if (ddp_rdmap_handle) {
842 call_dissector(ddp_rdmap_handle, next_tvb, pinfo, tree);
843 } else {
844 REPORT_DISSECTOR_BUG("ddp_handle was null");
847 return TRUE;
850 /* MPA REQUEST or MPA REPLY */
851 if (tvb_length(tvb) >= MPA_REQ_REP_FRAME_HEADER_LEN) {
852 if (is_mpa_req(tvb, pinfo))
853 return dissect_mpa_req_rep(tvb, pinfo, tree, MPA_REQUEST_FRAME);
854 else if (is_mpa_rep(tvb, pinfo))
855 return dissect_mpa_req_rep(tvb, pinfo, tree, MPA_REPLY_FRAME);
857 return FALSE;
860 /* registers this protocol with Wireshark */
861 void proto_register_mpa(void)
863 /* setup list of header fields */
864 static hf_register_info hf[] = {
865 { &hf_mpa_req, {
866 "Request frame header", "iwarp_mpa.req",
867 FT_NONE, BASE_NONE, NULL, 0x0,
868 NULL, HFILL } },
869 { &hf_mpa_rep, {
870 "Reply frame header", "iwarp_mpa.rep",
871 FT_NONE, BASE_NONE, NULL, 0x0,
872 NULL, HFILL } },
873 { &hf_mpa_fpdu, {
874 "FPDU", "iwarp_mpa.fpdu",
875 FT_NONE, BASE_NONE, NULL, 0x0,
876 NULL, HFILL } },
877 { &hf_mpa_marker, {
878 "Markers", "iwarp_mpa.markers",
879 FT_NONE, BASE_NONE, NULL, 0x0,
880 NULL, HFILL } },
881 { &hf_mpa_key_req, {
882 "ID Req frame", "iwarp_mpa.key.req",
883 FT_BYTES, BASE_NONE, NULL, 0x0,
884 NULL, HFILL } },
885 { &hf_mpa_key_rep, {
886 "ID Rep frame", "iwarp_mpa.key.rep",
887 FT_BYTES, BASE_NONE, NULL, 0x0,
888 NULL, HFILL } },
889 { &hf_mpa_flag_m, {
890 "Marker flag", "iwarp_mpa.marker_flag",
891 FT_BOOLEAN, 8, NULL, MPA_MARKER_FLAG,
892 NULL, HFILL } },
893 { &hf_mpa_flag_c, {
894 "CRC flag", "iwarp_mpa.crc_flag",
895 FT_BOOLEAN, 8, NULL, MPA_CRC_FLAG,
896 NULL, HFILL } },
897 { &hf_mpa_flag_r, {
898 "Connection rejected flag",
899 "iwarp_mpa.rej_flag", FT_BOOLEAN, 8, NULL, MPA_REJECT_FLAG,
900 NULL, HFILL } },
901 { &hf_mpa_flag_res, {
902 "Reserved", "iwarp_mpa.res",
903 FT_UINT8, BASE_HEX, NULL, MPA_RESERVED_FLAG,
904 NULL, HFILL } },
905 { &hf_mpa_rev, {
906 "Revision", "iwarp_mpa.rev",
907 FT_UINT8, BASE_DEC, NULL, 0x0,
908 NULL, HFILL } },
909 { &hf_mpa_pd_length, {
910 "Private data length", "iwarp_mpa.pdlength",
911 FT_UINT16, BASE_DEC, NULL, 0x0,
912 NULL, HFILL } },
913 { &hf_mpa_private_data, {
914 "Private data", "iwarp_mpa.privatedata",
915 FT_BYTES, BASE_NONE, NULL, 0x0,
916 NULL, HFILL } },
917 { &hf_mpa_ulpdu_length, {
918 "ULPDU length", "iwarp_mpa.ulpdulength",
919 FT_UINT16, BASE_DEC, NULL, 0x0,
920 NULL, HFILL } },
921 { &hf_mpa_pad, {
922 "Padding", "iwarp_mpa.pad",
923 FT_BYTES, BASE_NONE, NULL, 0x0,
924 NULL, HFILL } },
925 { &hf_mpa_crc, {
926 "CRC", "iwarp_mpa.crc",
927 FT_UINT32, BASE_HEX, NULL, 0x0,
928 NULL, HFILL } },
929 { &hf_mpa_crc_check, {
930 "CRC check", "iwarp_mpa.crc_check",
931 FT_UINT32, BASE_HEX, NULL, 0x0,
932 NULL, HFILL } },
933 { &hf_mpa_marker_res, {
934 "Reserved", "iwarp_mpa.marker_res",
935 FT_UINT16, BASE_HEX, NULL, 0x0,
936 "Marker: Reserved", HFILL } },
937 { &hf_mpa_marker_fpduptr, {
938 "FPDU back pointer", "iwarp_mpa.marker_fpduptr",
939 FT_UINT16, BASE_DEC, NULL, 0x0,
940 "Marker: FPDU Pointer", HFILL } }
943 /* setup protocol subtree array */
944 static gint *ett[] = {
945 &ett_mpa,
946 &ett_mpa_req,
947 &ett_mpa_rep,
948 &ett_mpa_fpdu,
949 &ett_mpa_marker
952 static ei_register_info ei[] = {
953 { &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 }},
954 { &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 }},
955 { &ei_mpa_reject_bit_responder, { "iwarp_mpa.reject_bit_responder", PI_RESPONSE_CODE, PI_NOTE, "Reject bit set by Responder", EXPFILL }},
956 { &ei_mpa_bad_length, { "iwarp_mpa.bad_length", PI_MALFORMED, PI_ERROR, "Bad length", EXPFILL }},
959 expert_module_t* expert_iwarp_mpa;
961 /* register the protocol name and description */
962 proto_iwarp_mpa = proto_register_protocol(
963 "iWARP Marker Protocol data unit Aligned framing",
964 "IWARP_MPA", "iwarp_mpa");
966 /* required function calls to register the header fields and subtrees */
967 proto_register_field_array(proto_iwarp_mpa, hf, array_length(hf));
968 proto_register_subtree_array(ett, array_length(ett));
969 expert_iwarp_mpa = expert_register_protocol(proto_iwarp_mpa);
970 expert_register_field_array(expert_iwarp_mpa, ei, array_length(ei));
973 void
974 proto_reg_handoff_mpa(void)
977 * MPA does not use any specific TCP port so, when not on a specific
978 * port, try this dissector whenever there is TCP traffic.
980 heur_dissector_add("tcp", dissect_iwarp_mpa, proto_iwarp_mpa);
981 ddp_rdmap_handle = find_dissector("iwarp_ddp_rdmap");