Revert "TODO epan/dissectors/asn1/kerberos/packet-kerberos-template.c new GSS flags"
[wireshark-sm.git] / epan / dissectors / packet-iwarp-mpa.c
blob385c8d6fd10706afe7da490ce820efa6ebbfffd9
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 * Wireshark - Network traffic analyzer
8 * By Gerald Combs <gerald@wireshark.org>
9 * Copyright 1998 Gerald Combs
11 * SPDX-License-Identifier: GPL-2.0-or-later
14 /* INCLUDES */
15 #include "config.h"
17 #include <epan/packet.h>
18 #include <epan/unit_strings.h>
19 #include <epan/expert.h>
20 #include <epan/crc32-tvb.h>
21 #include <wsutil/crc32.h>
22 #include "packet-tcp.h"
24 void proto_register_mpa(void);
25 void proto_reg_handoff_mpa(void);
27 /* DEFINES */
29 /* header field byte lengths */
30 #define MPA_REQ_REP_FRAME_HEADER_LEN 20
31 #define MPA_PDLENGTH_LEN 2
32 #define MPA_ULPDU_LENGTH_LEN 2
33 #define MPA_MARKER_LEN 4
34 #define MPA_SMALLEST_FPDU_LEN 8
35 #define MPA_REQ_REP_KEY_LEN 16
36 #define MPA_REQ_REP_FLAG_LEN 1
37 #define MPA_REQ_REP_REV_LEN 1
38 #define MPA_REQ_REP_PDLENGTH_LEN 2
39 #define MPA_MARKER_RSVD_LEN 2
40 #define MPA_MARKER_FPDUPTR_LEN 2
41 #define MPA_CRC_LEN 4
43 /* protocol constants */
44 #define MPA_REQ_REP_FRAME UINT64_C(0x4d50412049442052)
45 #define MPA_ID_REQ_FRAME UINT64_C(0x6571204672616d65)
46 #define MPA_ID_REP_FRAME UINT64_C(0x6570204672616d65)
47 #define MPA_MARKER_INTERVAL 512
48 #define MPA_MAX_PD_LENGTH 512
49 #define MPA_ALIGNMENT 4
50 #define TCP_MAX_SEQ ((uint32_t) 0xffffffff)
52 /* for code readability */
53 #define MPA_REQUEST_FRAME 1
54 #define MPA_REPLY_FRAME 2
55 #define MPA_FPDU 3
56 #define MPA_INITIATOR 0
57 #define MPA_RESPONDER 1
59 /* bitmasks */
60 #define MPA_MARKER_FLAG 0x80
61 #define MPA_CRC_FLAG 0x40
62 #define MPA_REJECT_FLAG 0x20
63 #define MPA_RESERVED_FLAG 0x1F
65 /* GLOBALS */
67 /* initialize the protocol and registered fields */
68 static int proto_iwarp_mpa;
70 static int hf_mpa_req;
71 static int hf_mpa_rep;
72 static int hf_mpa_fpdu;
73 static int hf_mpa_marker;
75 static int hf_mpa_key_req;
76 static int hf_mpa_key_rep;
77 static int hf_mpa_flag_m;
78 static int hf_mpa_flag_c;
79 static int hf_mpa_flag_r;
80 static int hf_mpa_flag_res;
81 static int hf_mpa_rev;
82 static int hf_mpa_pd_length;
83 static int hf_mpa_private_data;
85 static int hf_mpa_ulpdu_length;
86 static int hf_mpa_pad;
87 static int hf_mpa_crc;
88 static int hf_mpa_crc_check;
90 static int hf_mpa_marker_res;
91 static int hf_mpa_marker_fpduptr;
93 /* initialize the subtree pointers */
94 static int ett_mpa;
96 static int ett_mpa_req;
97 static int ett_mpa_rep;
98 static int ett_mpa_fpdu;
99 static int ett_mpa_marker;
101 static expert_field ei_mpa_res_field_not_set0;
102 static expert_field ei_mpa_rev_field_not_set1;
103 static expert_field ei_mpa_reject_bit_responder;
104 static expert_field ei_mpa_bad_length;
106 /* handles of our subdissectors */
107 static dissector_handle_t ddp_rdmap_handle;
109 static const value_string mpa_messages[] = {
110 { MPA_REQUEST_FRAME, "MPA Request Frame" },
111 { MPA_REPLY_FRAME, "MPA Reply Frame" },
112 { MPA_FPDU, "MPA FPDU" },
113 { 0, NULL }
117 * CONNECTION STATE and MARKERS
118 * A MPA endpoint operates in two distinct phases.
119 * The Startup Phase is used to verify correct MPA setup, exchange CRC
120 * and Marker configuration, and optionally pass Private Data between
121 * endpoints prior to completing a DDP connection.
122 * The second distinct phase is Full Operation during which FPDUs are
123 * sent using all the rules that pertain (CRC, Markers, MULPDU,
124 * restrictions etc.).
125 * To keep track of a MPA connection configuration a mpa_state is declared
126 * and maintained per TCP connection, i.e. it is associated to a conversation
127 * between two endpoints.
129 * In some configurations MPA places MARKERs in a FPDU every 512th octet with
130 * respect to the TCP sequence number of the first FPDU. The struct minfo_t
131 * records the source port of a peer that has to insert Markers into its FPDUs
132 * as well as the TCP sequence number of its first FPDU. This information is
133 * necessary to locate the markers within a FPDU afterwards. Itis part of a
134 * mpa_state.
138 * This struct is used to record the source port 'port' and the TCP sequence
139 * number 'seq' of the first FPDU. This information is used to determine the
140 * position of the first Marker within the following FPDUs. The boolean 'valid'
141 * specifies if Markers are inserted by the endpoint running on source port
142 * 'port' or not.
144 struct minfo {
145 uint16_t port;
146 uint32_t seq;
147 bool valid;
149 typedef struct minfo minfo_t;
152 * This struct represents a MPA connection state. It specifies if Markers and
153 * CRC is used for the following FPDUs. It also contains information to
154 * distinguish between the MPA Startup and Full Operation Phase.the connection
155 * parameters negotiated between to MPA endpoints during the MPA Startup Phase
156 * as well as other information for the dissection.
158 * The two MPA endpoints are called Initiator, the sender of the MPA Request,
159 * and Responder, the sender of the MPA Reply.
161 * @full_operation: true if is this state is valid and FLASE otherwise.
162 * @req_frame_num: Frame number of the MPA Request to distinguish this frame
163 * from later FPDUs.
164 * @rep_frame_num: Frame number of the MPA Reply to distinguish this frame
165 * from later FPDUs.
166 * @ini_exp_m_res: true if the Initiator expects the Responder to insert
167 * Markers into his FPDUs sent to Initiator and false otherwise.
168 * @res_exp_m_ini: true if the Responder expects the Initiator to insert
169 * Markers into his FPDUs sent to Responder and false otherwise.
170 * @minfo[2]: Array of minfo_t whichs holds necessary information to
171 * determine the start position of the first Marker within a
172 * a FPDU.
173 * minfo[0] is used for the Initiator endpoint
174 * minfo[1] is used for the Responder endpoint
175 * @crc: true if CRC is used by both endpoints and FLASE otherwise.
176 * @revision: Stores the MPA protocol revision number.
178 struct mpa_state {
179 bool full_operation;
180 unsigned req_frame_num;
181 unsigned rep_frame_num;
182 bool ini_exp_m_res;
183 bool res_exp_m_ini;
184 minfo_t minfo[2];
185 bool crc;
186 int revision;
188 typedef struct mpa_state mpa_state_t;
191 * Returns an initialized MPA connection state or throws an out of
192 * memory exception.
194 static mpa_state_t *
195 init_mpa_state(void)
197 mpa_state_t *state;
199 state = wmem_new0(wmem_file_scope(), mpa_state_t);
200 state->revision = -1;
201 return state;
205 * Returns the state associated with a MPA connection or NULL otherwise.
207 static mpa_state_t *
208 get_mpa_state(conversation_t *conversation)
210 if (conversation) {
211 return (mpa_state_t*) conversation_get_proto_data(conversation,
212 proto_iwarp_mpa);
213 } else {
214 return NULL;
219 * Returns the offset of the first Marker in a FPDU where the beginning of a
220 * FPDU has an offset of 0. It also addresses possible sequence number
221 * overflows.
222 * The endpoint is either the Initiator or the Responder.
224 static uint32_t
225 get_first_marker_offset(mpa_state_t *state, struct tcpinfo *tcpinfo,
226 uint8_t endpoint)
228 uint32_t offset = 0;
230 if (tcpinfo->seq > state->minfo[endpoint].seq) {
231 offset = (tcpinfo->seq - state->minfo[endpoint].seq)
232 % MPA_MARKER_INTERVAL;
235 if (tcpinfo->seq < state->minfo[endpoint].seq) {
236 offset = state->minfo[endpoint].seq
237 + (TCP_MAX_SEQ - tcpinfo->seq) % MPA_MARKER_INTERVAL;
240 return (MPA_MARKER_INTERVAL - offset) % MPA_MARKER_INTERVAL;
244 * Returns the total length of this FPDU under the assumption that a TCP
245 * segment carries only one FPDU.
247 static uint32_t
248 fpdu_total_length(struct tcpinfo *tcpinfo)
250 uint32_t size = 0;
252 if (tcpinfo->seq < tcpinfo->nxtseq) {
253 size = tcpinfo->nxtseq - tcpinfo->seq;
256 if (tcpinfo->seq >= tcpinfo->nxtseq) {
257 size = tcpinfo->nxtseq + (TCP_MAX_SEQ - tcpinfo->seq);
260 return size;
264 * Returns the number of Markers of this MPA FPDU. The endpoint is either the
265 * Initiator or the Responder.
267 static uint32_t
268 number_of_markers(mpa_state_t *state, struct tcpinfo *tcpinfo, uint8_t endpoint)
270 uint32_t size;
271 uint32_t offset;
273 size = fpdu_total_length(tcpinfo);
274 offset = get_first_marker_offset(state, tcpinfo, endpoint);
276 if (offset < size) {
277 return ((size - offset) / MPA_MARKER_INTERVAL)+1;
278 } else {
279 return 0;
284 * Removes any Markers from this FPDU by using memcpy or throws an out of memory
285 * exception.
287 static tvbuff_t *
288 remove_markers(tvbuff_t *tvb, packet_info *pinfo, uint32_t marker_offset,
289 uint32_t num_markers, uint32_t orig_length)
291 uint8_t *mfree_buff = NULL;
292 uint32_t mfree_buff_length, tot_copy, cur_copy;
293 uint32_t source_offset;
294 tvbuff_t *mfree_tvb = NULL;
296 DISSECTOR_ASSERT(num_markers > 0);
297 DISSECTOR_ASSERT(orig_length > MPA_MARKER_LEN * num_markers);
298 DISSECTOR_ASSERT(tvb_captured_length(tvb) == orig_length);
300 /* allocate memory for the marker-free buffer */
301 mfree_buff_length = orig_length - (MPA_MARKER_LEN * num_markers);
302 mfree_buff = (uint8_t *)wmem_alloc(pinfo->pool, mfree_buff_length);
304 tot_copy = 0;
305 source_offset = 0;
306 cur_copy = marker_offset;
307 while (tot_copy < mfree_buff_length) {
308 tvb_memcpy(tvb, mfree_buff+tot_copy, source_offset, cur_copy);
309 tot_copy += cur_copy;
310 source_offset += cur_copy + MPA_MARKER_LEN;
311 cur_copy = MIN(MPA_MARKER_INTERVAL, (mfree_buff_length - tot_copy));
313 mfree_tvb = tvb_new_child_real_data(tvb, mfree_buff, mfree_buff_length,
314 mfree_buff_length);
315 add_new_data_source(pinfo, mfree_tvb, "FPDU without Markers");
317 return mfree_tvb;
320 /* returns true if this TCP segment carries a MPA REQUEST and FLASE otherwise */
321 static bool
322 is_mpa_req(tvbuff_t *tvb, packet_info *pinfo)
324 conversation_t *conversation = NULL;
325 mpa_state_t *state = NULL;
326 uint8_t mcrres;
328 if (tvb_get_ntoh64(tvb, 0) != MPA_REQ_REP_FRAME
329 || tvb_get_ntoh64(tvb, 8) != MPA_ID_REQ_FRAME)
330 return false;
332 conversation = find_or_create_conversation(pinfo);
334 if (!get_mpa_state(conversation)) {
336 /* associate a MPA connection state to this conversation if
337 * there is no MPA state already associated to this connection
339 state = init_mpa_state();
341 /* analyze MPA connection parameter and record them */
342 mcrres = tvb_get_uint8(tvb, 16);
343 state->ini_exp_m_res = mcrres & MPA_MARKER_FLAG;
344 state->crc = mcrres & MPA_CRC_FLAG;
345 state->revision = tvb_get_uint8(tvb, 17);
346 state->req_frame_num = pinfo->num;
347 state->minfo[MPA_INITIATOR].port = pinfo->srcport;
348 state->minfo[MPA_RESPONDER].port = pinfo->destport;
350 conversation_add_proto_data(conversation, proto_iwarp_mpa, state);
352 /* update expert info */
353 if (mcrres & MPA_RESERVED_FLAG)
354 expert_add_info(pinfo, NULL, &ei_mpa_res_field_not_set0);
356 if (state->revision != 1)
357 expert_add_info(pinfo, NULL, &ei_mpa_rev_field_not_set1);
359 return true;
362 /* returns true if this TCP segment carries a MPA REPLY and false otherwise */
363 static bool
364 is_mpa_rep(tvbuff_t *tvb, packet_info *pinfo)
366 conversation_t *conversation = NULL;
367 mpa_state_t *state = NULL;
368 uint8_t mcrres;
370 if (tvb_get_ntoh64(tvb, 0) != MPA_REQ_REP_FRAME
371 || tvb_get_ntoh64(tvb, 8) != MPA_ID_REP_FRAME) {
372 return false;
375 conversation = find_conversation_pinfo(pinfo, 0);
377 if (!conversation) {
378 return false;
381 state = get_mpa_state(conversation);
382 if (!state) {
383 return false;
386 if (!state->full_operation) {
387 /* update state of this conversation */
388 mcrres = tvb_get_uint8(tvb, 16);
389 state->res_exp_m_ini = mcrres & MPA_MARKER_FLAG;
390 state->crc = state->crc | (mcrres & MPA_CRC_FLAG);
391 state->rep_frame_num = pinfo->num;
393 /* enter Full Operation Phase only if the Reject bit is not set */
394 if (!(mcrres & MPA_REJECT_FLAG))
395 state->full_operation = true;
396 else
397 expert_add_info(pinfo, NULL, &ei_mpa_reject_bit_responder);
399 return true;
402 /* returns true if this TCP segment carries a MPA FPDU and false otherwise */
403 static bool
404 is_mpa_fpdu(packet_info *pinfo)
406 conversation_t *conversation = NULL;
407 mpa_state_t *state = NULL;
409 conversation = find_conversation_pinfo(pinfo, 0);
411 if (!conversation) {
412 return false;
415 state = get_mpa_state(conversation);
416 if (!state) {
417 return false;
420 /* make sure all MPA connection parameters have been set */
421 if (!state->full_operation) {
422 return false;
425 if (pinfo->num == state->req_frame_num
426 || pinfo->num == state->rep_frame_num) {
427 return false;
428 } else {
429 return true;
433 /* update packet list pane in the GUI */
434 static void
435 mpa_packetlist(packet_info *pinfo, int message_type)
437 col_set_str(pinfo->cinfo, COL_PROTOCOL, "MPA");
439 col_add_fstr(pinfo->cinfo, COL_INFO,
440 "%d > %d %s", pinfo->srcport, pinfo->destport,
441 val_to_str(message_type, mpa_messages,
442 "Unknown %d"));
445 /* dissects MPA REQUEST or MPA REPLY */
446 static bool
447 dissect_mpa_req_rep(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree,
448 int message_type)
450 proto_tree *mpa_tree = NULL;
451 proto_tree *mpa_header_tree = NULL;
453 proto_item *mpa_item = NULL;
454 proto_item *mpa_header_item = NULL;
456 uint16_t pd_length;
457 uint32_t offset = 0;
459 mpa_packetlist(pinfo, message_type);
461 if (tree) {
462 mpa_item = proto_tree_add_item(tree, proto_iwarp_mpa, tvb, 0,
463 -1, ENC_NA);
464 mpa_tree = proto_item_add_subtree(mpa_item, ett_mpa);
466 if (message_type == MPA_REQUEST_FRAME) {
467 mpa_header_item = proto_tree_add_item(mpa_tree,
468 hf_mpa_req, tvb, offset, -1, ENC_NA);
469 mpa_header_tree = proto_item_add_subtree(
470 mpa_header_item, ett_mpa);
471 proto_tree_add_item(mpa_header_tree, hf_mpa_key_req,
472 tvb, offset, MPA_REQ_REP_KEY_LEN, ENC_NA);
475 if (message_type == MPA_REPLY_FRAME) {
476 mpa_header_item = proto_tree_add_item(mpa_tree,
477 hf_mpa_rep, tvb, offset, -1, ENC_NA);
478 mpa_header_tree = proto_item_add_subtree(
479 mpa_header_item, ett_mpa);
480 proto_tree_add_item(mpa_header_tree, hf_mpa_key_rep,
481 tvb, offset, MPA_REQ_REP_KEY_LEN, ENC_NA);
483 offset += MPA_REQ_REP_KEY_LEN;
485 proto_tree_add_item(mpa_header_tree, hf_mpa_flag_m, tvb,
486 offset, MPA_REQ_REP_FLAG_LEN, ENC_BIG_ENDIAN);
487 proto_tree_add_item(mpa_header_tree, hf_mpa_flag_c, tvb,
488 offset, MPA_REQ_REP_FLAG_LEN, ENC_BIG_ENDIAN);
489 proto_tree_add_item(mpa_header_tree, hf_mpa_flag_r, tvb,
490 offset, MPA_REQ_REP_FLAG_LEN, ENC_BIG_ENDIAN);
491 proto_tree_add_item(mpa_header_tree, hf_mpa_flag_res, tvb,
492 offset, MPA_REQ_REP_FLAG_LEN, ENC_BIG_ENDIAN);
493 offset += MPA_REQ_REP_FLAG_LEN;
495 proto_tree_add_item(mpa_header_tree, hf_mpa_rev, tvb,
496 offset, MPA_REQ_REP_REV_LEN, ENC_BIG_ENDIAN);
497 offset += MPA_REQ_REP_REV_LEN;
499 /* check whether the Private Data Length conforms to RFC 5044 */
500 pd_length = tvb_get_ntohs(tvb, offset);
501 if (pd_length > MPA_MAX_PD_LENGTH) {
502 proto_tree_add_expert_format(tree, pinfo, &ei_mpa_bad_length, tvb, offset, 2,
503 "[PD length field indicates more 512 bytes of Private Data]");
504 return false;
507 proto_tree_add_uint(mpa_header_tree,
508 hf_mpa_pd_length, tvb, offset,
509 MPA_REQ_REP_PDLENGTH_LEN, pd_length);
510 offset += MPA_REQ_REP_PDLENGTH_LEN;
512 if (pd_length) {
513 proto_tree_add_item(mpa_header_tree,
514 hf_mpa_private_data, tvb, offset,
515 pd_length, ENC_NA);
518 return true;
521 /* returns byte length of the padding */
522 static uint8_t
523 fpdu_pad_length(uint16_t ulpdu_length)
526 * The padding guarantees alignment of 4. Since Markers are 4 bytes long
527 * we do need to take them into consideration for computation of pad
528 * length. The padding length depends only on ULPDU (payload) length and
529 * the length of the header field for the ULPDU length.
531 uint32_t length = ulpdu_length + MPA_ULPDU_LENGTH_LEN;
534 * The extra % MPA_ALIGNMENT at the end covers for the case
535 * length % MPA_ALIGNMENT == 0.
537 return (MPA_ALIGNMENT - (length % MPA_ALIGNMENT)) % MPA_ALIGNMENT;
540 /* returns offset for PAD */
541 static uint32_t
542 pad_offset(struct tcpinfo *tcpinfo, uint32_t fpdu_total_len,
543 uint8_t pad_len)
545 if ((tcpinfo->nxtseq - MPA_CRC_LEN - MPA_MARKER_LEN) % MPA_MARKER_INTERVAL
546 == 0) {
547 /* covers the case where a Marker resides between the padding
548 * and CRC.
550 return fpdu_total_len - MPA_CRC_LEN - MPA_MARKER_LEN - pad_len;
551 } else {
552 return fpdu_total_len - MPA_CRC_LEN - pad_len;
556 /* dissects CRC within a FPDU */
557 static void
558 dissect_fpdu_crc(tvbuff_t *tvb, proto_tree *tree, mpa_state_t *state,
559 uint32_t offset, uint32_t length)
561 uint32_t crc = 0;
562 uint32_t sent_crc = 0;
564 if (state->crc) {
566 crc = ~crc32c_tvb_offset_calculate(tvb, 0, length, CRC32C_PRELOAD);
568 sent_crc = tvb_get_ntohl(tvb, offset); /* crc start offset */
570 if (crc == sent_crc) {
571 proto_tree_add_uint_format_value(tree,
572 hf_mpa_crc_check, tvb, offset, MPA_CRC_LEN,
573 sent_crc, "0x%08x (Good CRC32)",
574 sent_crc);
575 } else {
576 proto_tree_add_uint_format_value(tree,
577 hf_mpa_crc_check, tvb, offset, MPA_CRC_LEN,
578 sent_crc,
579 "0x%08x (Bad CRC32, should be 0x%08x)",
580 sent_crc, crc);
582 } else {
583 proto_tree_add_item(tree, hf_mpa_crc, tvb, offset, MPA_CRC_LEN,
584 ENC_BIG_ENDIAN);
588 /* dissects Markers within FPDU */
589 static void
590 dissect_fpdu_markers(tvbuff_t *tvb, proto_tree *tree, mpa_state_t *state,
591 struct tcpinfo *tcpinfo, uint8_t endpoint)
593 proto_tree *mpa_marker_tree;
594 proto_item *mpa_marker_item;
595 uint32_t offset, i;
597 mpa_marker_item = proto_tree_add_item(tree, hf_mpa_marker, tvb,
598 0, -1, ENC_NA);
599 mpa_marker_tree = proto_item_add_subtree(mpa_marker_item, ett_mpa);
601 offset = get_first_marker_offset(state, tcpinfo, endpoint);
603 for (i=0; i<number_of_markers(state, tcpinfo, endpoint); i++) {
604 proto_tree_add_item(mpa_marker_tree, hf_mpa_marker_res, tvb,
605 offset, MPA_MARKER_RSVD_LEN, ENC_BIG_ENDIAN);
606 proto_tree_add_item(mpa_marker_tree,
607 hf_mpa_marker_fpduptr, tvb,
608 offset+MPA_MARKER_RSVD_LEN, MPA_MARKER_FPDUPTR_LEN, ENC_BIG_ENDIAN);
609 offset += MPA_MARKER_INTERVAL;
613 /* returns the expected value of the 16 bits long MPA FPDU ULPDU LENGTH field */
614 static uint16_t
615 expected_ulpdu_length(mpa_state_t *state, struct tcpinfo *tcpinfo,
616 uint8_t endpoint)
618 uint32_t length, pad_length, markers_length;
620 length = fpdu_total_length(tcpinfo);
622 if (length <= MPA_CRC_LEN)
623 return 0;
624 length -= MPA_CRC_LEN;
626 pad_length = (MPA_ALIGNMENT - (length % MPA_ALIGNMENT)) % MPA_ALIGNMENT;
628 if (length <= pad_length)
629 return 0;
630 length -= pad_length;
632 if (state->minfo[endpoint].valid) {
633 markers_length =
634 number_of_markers(state, tcpinfo, endpoint) * MPA_MARKER_LEN;
636 if (length <= markers_length)
637 return 0;
638 length -= markers_length;
641 if (length <= MPA_ULPDU_LENGTH_LEN)
642 return 0;
643 length -= MPA_ULPDU_LENGTH_LEN;
645 return (uint16_t) length;
648 /* dissects MPA FPDU */
649 static uint16_t
650 dissect_mpa_fpdu(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree,
651 mpa_state_t *state, struct tcpinfo *tcpinfo, uint8_t endpoint)
653 proto_item *mpa_item = NULL;
654 proto_item *mpa_header_item = NULL;
656 proto_tree *mpa_tree = NULL;
657 proto_tree *mpa_header_tree = NULL;
659 uint8_t pad_length;
660 uint16_t ulpdu_length, exp_ulpdu_length;
661 uint32_t offset, total_length;
662 uint32_t num_of_m = 0;
665 * Initialize starting offset for this FPDU. Deals with the case that this
666 * FPDU may start with a Marker instead of the ULPDU_LENTH header field.
668 if (state->minfo[endpoint].valid
669 && get_first_marker_offset(state, tcpinfo, endpoint) == 0) {
670 offset = MPA_MARKER_LEN;
671 } else {
672 offset = 0;
675 /* get ULPDU length of this FPDU */
676 ulpdu_length = (uint16_t) tvb_get_ntohs(tvb, offset);
678 if (state->minfo[endpoint].valid) {
679 num_of_m = number_of_markers(state, tcpinfo, endpoint);
684 * Stop FPDU dissection if the read ULPDU_LENGTH field does NOT contain
685 * what is expected.
686 * Reasons for getting a wrong ULPDU_LENGTH can be lost packets (because
687 * libpcap was not able to capture every packet) or lost alignment (the
688 * MPA FPDU header does not start right after TCP header).
689 * We consider the above to be an error since we make the assumption
690 * that exactly one MPA FPDU is contained in one TCP segment and starts
691 * always either with a Marker or the ULPDU_LENGTH header field.
693 pad_length = fpdu_pad_length(ulpdu_length);
694 if (num_of_m > 0) {
695 exp_ulpdu_length = expected_ulpdu_length(state, tcpinfo, endpoint);
696 if (!exp_ulpdu_length || exp_ulpdu_length != (ulpdu_length + pad_length)) {
697 return 0;
701 mpa_packetlist(pinfo, MPA_FPDU);
703 mpa_item = proto_tree_add_item(tree, proto_iwarp_mpa, tvb, 0,
704 -1, ENC_NA);
705 mpa_tree = proto_item_add_subtree(mpa_item, ett_mpa);
707 mpa_header_item = proto_tree_add_item(mpa_tree, hf_mpa_fpdu,
708 tvb, offset, -1, ENC_NA);
709 mpa_header_tree = proto_item_add_subtree(mpa_header_item,
710 ett_mpa);
712 /* ULPDU Length header field */
713 proto_tree_add_uint(mpa_header_tree,
714 hf_mpa_ulpdu_length, tvb, offset,
715 MPA_ULPDU_LENGTH_LEN, ulpdu_length);
717 /* Markers are present in this FPDU */
718 if (num_of_m > 0) {
720 total_length = fpdu_total_length(tcpinfo);
722 if (pad_length > 0) {
723 proto_tree_add_item(mpa_header_tree, hf_mpa_pad,
724 tvb, pad_offset(tcpinfo,
725 total_length,
726 pad_length),
727 pad_length, ENC_NA);
730 dissect_fpdu_crc(tvb, mpa_header_tree, state,
731 total_length-MPA_CRC_LEN, num_of_m * MPA_MARKER_LEN +
732 ulpdu_length + pad_length + MPA_ULPDU_LENGTH_LEN);
734 dissect_fpdu_markers(tvb, mpa_tree, state, tcpinfo, endpoint);
736 } else { /* Markers are not present or not enabled */
738 offset += MPA_ULPDU_LENGTH_LEN + ulpdu_length;
740 if (pad_length > 0) {
741 proto_tree_add_item(mpa_header_tree, hf_mpa_pad, tvb, offset,
742 pad_length, ENC_NA);
743 offset += pad_length;
746 dissect_fpdu_crc(tvb, mpa_header_tree, state, offset,
747 ulpdu_length+pad_length+MPA_ULPDU_LENGTH_LEN);
749 return ulpdu_length;
752 /* Extracted from dissect_warp_mpa, Obtain the TCP seq of the first FPDU */
753 static mpa_state_t*
754 get_state_of_first_fpdu(tvbuff_t *tvb, packet_info *pinfo, struct tcpinfo *tcpinfo, uint8_t *endpoint)
756 conversation_t *conversation = NULL;
757 mpa_state_t *state = NULL;
759 if (tvb_captured_length(tvb) >= MPA_SMALLEST_FPDU_LEN && is_mpa_fpdu(pinfo)) {
760 conversation = find_conversation_pinfo(pinfo, 0);
761 state = get_mpa_state(conversation);
763 if (pinfo->srcport == state->minfo[MPA_INITIATOR].port) {
764 *endpoint = MPA_INITIATOR;
765 } else if (pinfo->srcport == state->minfo[MPA_RESPONDER].port) {
766 *endpoint = MPA_RESPONDER;
767 } else {
768 REPORT_DISSECTOR_BUG("endpoint cannot be determined");
771 /* Markers are used by either the Initiator or the Responder or both. */
772 if ((state->ini_exp_m_res || state->res_exp_m_ini) && *endpoint <= MPA_RESPONDER) {
774 /* find the TCP sequence number of the first FPDU */
775 if (!state->minfo[*endpoint].valid) {
776 state->minfo[*endpoint].seq = tcpinfo->seq;
777 state->minfo[*endpoint].valid = true;
782 return state;
787 * Main dissection routine.
789 static bool
790 dissect_iwarp_mpa(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void *data)
792 tvbuff_t *next_tvb = NULL;
793 mpa_state_t *state = NULL;
794 struct tcpinfo *tcpinfo;
795 uint8_t endpoint = 3;
796 uint16_t ulpdu_length = 0;
798 if (data == NULL)
799 return false;
800 tcpinfo = (struct tcpinfo *)data;
802 /* FPDU */
803 state = get_state_of_first_fpdu(tvb, pinfo, tcpinfo, &endpoint);
804 if (state) {
805 /* dissect FPDU */
806 ulpdu_length = dissect_mpa_fpdu(tvb, pinfo, tree, state, tcpinfo,
807 endpoint);
809 /* an ulpdu_length of 0 should never happen */
810 if (!ulpdu_length)
811 return false;
813 /* removes Markers if any and prepares new tvbuff for next dissector */
814 if (endpoint <= MPA_RESPONDER && state->minfo[endpoint].valid
815 && number_of_markers(state, tcpinfo, endpoint) > 0) {
816 next_tvb = tvb_new_subset_length(remove_markers(tvb, pinfo,
817 get_first_marker_offset(state, tcpinfo, endpoint),
818 number_of_markers(state, tcpinfo, endpoint),
819 fpdu_total_length(tcpinfo)), MPA_ULPDU_LENGTH_LEN,
820 ulpdu_length);
821 } else {
822 next_tvb = tvb_new_subset_length(tvb, MPA_ULPDU_LENGTH_LEN, ulpdu_length);
826 /* call subdissector */
827 if (ddp_rdmap_handle) {
828 call_dissector(ddp_rdmap_handle, next_tvb, pinfo, tree);
829 } else {
830 REPORT_DISSECTOR_BUG("ddp_handle was null");
833 return true;
836 /* MPA REQUEST or MPA REPLY */
837 if (tvb_captured_length(tvb) >= MPA_REQ_REP_FRAME_HEADER_LEN) {
838 if (is_mpa_req(tvb, pinfo))
839 return dissect_mpa_req_rep(tvb, pinfo, tree, MPA_REQUEST_FRAME);
840 else if (is_mpa_rep(tvb, pinfo))
841 return dissect_mpa_req_rep(tvb, pinfo, tree, MPA_REPLY_FRAME);
843 return false;
846 static unsigned
847 iwrap_mpa_pdu_length(packet_info *pinfo _U_, tvbuff_t *tvb,
848 int offset, void *data _U_)
850 uint64_t tag;
851 int remaining = tvb_captured_length_remaining(tvb, offset);
852 unsigned pdu_length = 0;
853 uint16_t PD_Length;
854 mpa_state_t *state = NULL;
855 uint8_t endpoint = 3;
856 uint32_t num_of_m = 0;
857 struct tcpinfo *tcpinfo;
858 int current_offset = offset;
860 tag = tvb_get_ntoh64(tvb, offset);
861 if (tag != MPA_REQ_REP_FRAME) {
862 /* FPDU */
863 uint16_t ULPDU_Length;
864 uint8_t pad_length;
866 tcpinfo = (struct tcpinfo *)data;
868 state = get_state_of_first_fpdu(tvb, pinfo, tcpinfo, &endpoint);
869 if (state) {
870 if (state -> minfo[endpoint] . valid && get_first_marker_offset(state, tcpinfo, endpoint) == 0) {
871 current_offset += MPA_MARKER_LEN;
874 if (state -> minfo[endpoint] . valid) {
875 num_of_m = number_of_markers(state, tcpinfo, endpoint);
879 if (num_of_m > 0) {
880 pdu_length += num_of_m * MPA_MARKER_LEN;
882 ULPDU_Length = tvb_get_ntohs(tvb, current_offset);
883 pad_length = fpdu_pad_length(ULPDU_Length);
885 pdu_length += MPA_ULPDU_LENGTH_LEN;
886 pdu_length += ULPDU_Length;
887 pdu_length += pad_length;
888 pdu_length += MPA_CRC_LEN;
890 return pdu_length;
894 * MPA Request and Reply Frame Format...
897 if (remaining < MPA_REQ_REP_FRAME_HEADER_LEN) {
899 * We need more data.
901 return 0;
904 offset += MPA_REQ_REP_FRAME_HEADER_LEN;
905 offset -= MPA_REQ_REP_PDLENGTH_LEN;
907 PD_Length = tvb_get_ntohs(tvb, offset);
909 pdu_length += MPA_REQ_REP_FRAME_HEADER_LEN;
910 pdu_length += PD_Length;
912 return pdu_length;
915 static int
916 dissect_iwarp_mpa_pdu(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void *data)
918 bool ok;
919 unsigned len;
921 len = iwrap_mpa_pdu_length(pinfo, tvb, 0, data);
922 ok = dissect_iwarp_mpa(tvb, pinfo, tree, data);
923 if (!ok) {
924 return -1;
927 return len;
930 static bool
931 dissect_iwarp_mpa_heur(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void *data)
933 struct tcpinfo *tcpinfo = NULL;
934 bool is_mpa_pdu = false;
936 if (data == NULL)
937 return false;
938 tcpinfo = (struct tcpinfo *)data;
940 /* MPA REQUEST or MPA REPLY */
941 if (tvb_captured_length(tvb) >= MPA_REQ_REP_FRAME_HEADER_LEN) {
942 if (is_mpa_req(tvb, pinfo)) {
943 is_mpa_pdu = true;
944 } else if (is_mpa_rep(tvb, pinfo)) {
945 is_mpa_pdu = true;
948 if (tvb_captured_length(tvb) >= MPA_SMALLEST_FPDU_LEN && is_mpa_fpdu(pinfo)) {
949 is_mpa_pdu = true;
952 if (!is_mpa_pdu) {
953 return false;
956 /* Set the port type for this packet to be iWarp MPA */
957 pinfo->ptype = PT_IWARP_MPA;
959 tcp_dissect_pdus(tvb, pinfo, tree,
960 true, /* proto_desegment*/
961 MPA_SMALLEST_FPDU_LEN,
962 iwrap_mpa_pdu_length,
963 dissect_iwarp_mpa_pdu,
964 tcpinfo);
965 return true;
968 /* registers this protocol with Wireshark */
969 void proto_register_mpa(void)
971 /* setup list of header fields */
972 static hf_register_info hf[] = {
973 { &hf_mpa_req, {
974 "Request frame header", "iwarp_mpa.req",
975 FT_NONE, BASE_NONE, NULL, 0x0,
976 NULL, HFILL } },
977 { &hf_mpa_rep, {
978 "Reply frame header", "iwarp_mpa.rep",
979 FT_NONE, BASE_NONE, NULL, 0x0,
980 NULL, HFILL } },
981 { &hf_mpa_fpdu, {
982 "FPDU", "iwarp_mpa.fpdu",
983 FT_NONE, BASE_NONE, NULL, 0x0,
984 NULL, HFILL } },
985 { &hf_mpa_marker, {
986 "Markers", "iwarp_mpa.markers",
987 FT_NONE, BASE_NONE, NULL, 0x0,
988 NULL, HFILL } },
989 { &hf_mpa_key_req, {
990 "ID Req frame", "iwarp_mpa.key.req",
991 FT_BYTES, BASE_NONE, NULL, 0x0,
992 NULL, HFILL } },
993 { &hf_mpa_key_rep, {
994 "ID Rep frame", "iwarp_mpa.key.rep",
995 FT_BYTES, BASE_NONE, NULL, 0x0,
996 NULL, HFILL } },
997 { &hf_mpa_flag_m, {
998 "Marker flag", "iwarp_mpa.marker_flag",
999 FT_BOOLEAN, 8, NULL, MPA_MARKER_FLAG,
1000 NULL, HFILL } },
1001 { &hf_mpa_flag_c, {
1002 "CRC flag", "iwarp_mpa.crc_flag",
1003 FT_BOOLEAN, 8, NULL, MPA_CRC_FLAG,
1004 NULL, HFILL } },
1005 { &hf_mpa_flag_r, {
1006 "Connection rejected flag",
1007 "iwarp_mpa.rej_flag", FT_BOOLEAN, 8, NULL, MPA_REJECT_FLAG,
1008 NULL, HFILL } },
1009 { &hf_mpa_flag_res, {
1010 "Reserved", "iwarp_mpa.res",
1011 FT_UINT8, BASE_HEX, NULL, MPA_RESERVED_FLAG,
1012 NULL, HFILL } },
1013 { &hf_mpa_rev, {
1014 "Revision", "iwarp_mpa.rev",
1015 FT_UINT8, BASE_DEC, NULL, 0x0,
1016 NULL, HFILL } },
1017 { &hf_mpa_pd_length, {
1018 "Private data length", "iwarp_mpa.pdlength",
1019 FT_UINT16, BASE_DEC|BASE_UNIT_STRING, UNS(&units_byte_bytes), 0x0,
1020 NULL, HFILL } },
1021 { &hf_mpa_private_data, {
1022 "Private data", "iwarp_mpa.privatedata",
1023 FT_BYTES, BASE_NONE, NULL, 0x0,
1024 NULL, HFILL } },
1025 { &hf_mpa_ulpdu_length, {
1026 "ULPDU length", "iwarp_mpa.ulpdulength",
1027 FT_UINT16, BASE_DEC|BASE_UNIT_STRING, UNS(&units_byte_bytes), 0x0,
1028 NULL, HFILL } },
1029 { &hf_mpa_pad, {
1030 "Padding", "iwarp_mpa.pad",
1031 FT_BYTES, BASE_NONE, NULL, 0x0,
1032 NULL, HFILL } },
1033 { &hf_mpa_crc, {
1034 "CRC", "iwarp_mpa.crc",
1035 FT_UINT32, BASE_HEX, NULL, 0x0,
1036 NULL, HFILL } },
1037 { &hf_mpa_crc_check, {
1038 "CRC check", "iwarp_mpa.crc_check",
1039 FT_UINT32, BASE_HEX, NULL, 0x0,
1040 NULL, HFILL } },
1041 { &hf_mpa_marker_res, {
1042 "Reserved", "iwarp_mpa.marker_res",
1043 FT_UINT16, BASE_HEX, NULL, 0x0,
1044 "Marker: Reserved", HFILL } },
1045 { &hf_mpa_marker_fpduptr, {
1046 "FPDU back pointer", "iwarp_mpa.marker_fpduptr",
1047 FT_UINT16, BASE_DEC|BASE_UNIT_STRING, UNS(&units_byte_bytes), 0x0,
1048 "Marker: FPDU Pointer", HFILL } }
1051 /* setup protocol subtree array */
1052 static int *ett[] = {
1053 &ett_mpa,
1054 &ett_mpa_req,
1055 &ett_mpa_rep,
1056 &ett_mpa_fpdu,
1057 &ett_mpa_marker
1060 static ei_register_info ei[] = {
1061 { &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 }},
1062 { &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 }},
1063 { &ei_mpa_reject_bit_responder, { "iwarp_mpa.reject_bit_responder", PI_RESPONSE_CODE, PI_NOTE, "Reject bit set by Responder", EXPFILL }},
1064 { &ei_mpa_bad_length, { "iwarp_mpa.bad_length", PI_MALFORMED, PI_ERROR, "Bad length", EXPFILL }},
1067 expert_module_t* expert_iwarp_mpa;
1069 /* register the protocol name and description */
1070 proto_iwarp_mpa = proto_register_protocol("iWARP Marker Protocol data unit Aligned framing", "IWARP_MPA", "iwarp_mpa");
1072 /* required function calls to register the header fields and subtrees */
1073 proto_register_field_array(proto_iwarp_mpa, hf, array_length(hf));
1074 proto_register_subtree_array(ett, array_length(ett));
1075 expert_iwarp_mpa = expert_register_protocol(proto_iwarp_mpa);
1076 expert_register_field_array(expert_iwarp_mpa, ei, array_length(ei));
1079 void
1080 proto_reg_handoff_mpa(void)
1083 * MPA does not use any specific TCP port so, when not on a specific
1084 * port, try this dissector whenever there is TCP traffic.
1086 heur_dissector_add("tcp", dissect_iwarp_mpa_heur, "IWARP_MPA over TCP", "iwarp_mpa_tcp", proto_iwarp_mpa, HEURISTIC_ENABLE);
1087 ddp_rdmap_handle = find_dissector_add_dependency("iwarp_ddp_rdmap", proto_iwarp_mpa);
1091 * Editor modelines - https://www.wireshark.org/tools/modelines.html
1093 * Local variables:
1094 * c-basic-offset: 8
1095 * tab-width: 8
1096 * indent-tabs-mode: t
1097 * End:
1099 * vi: set shiftwidth=8 tabstop=8 noexpandtab:
1100 * :indentSize=8:tabSize=8:noTabs=false: