/src/wireshark/epan/dissectors/packet-iwarp-mpa.c
Line | Count | Source |
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/unit_strings.h> |
19 | | #include <epan/tfs.h> |
20 | | #include <epan/expert.h> |
21 | | #include <epan/crc32-tvb.h> |
22 | | #include <wsutil/crc32.h> |
23 | | #include "packet-tcp.h" |
24 | | |
25 | | void proto_register_mpa(void); |
26 | | void proto_reg_handoff_mpa(void); |
27 | | |
28 | | /* DEFINES */ |
29 | | |
30 | | /* header field byte lengths */ |
31 | 1.71k | #define MPA_REQ_REP_FRAME_HEADER_LEN 20 |
32 | | #define MPA_PDLENGTH_LEN 2 |
33 | 0 | #define MPA_ULPDU_LENGTH_LEN 2 |
34 | 0 | #define MPA_MARKER_LEN 4 |
35 | 3.43k | #define MPA_SMALLEST_FPDU_LEN 8 |
36 | 0 | #define MPA_REQ_REP_KEY_LEN 16 |
37 | 0 | #define MPA_REQ_REP_FLAG_LEN 1 |
38 | 0 | #define MPA_REQ_REP_REV_LEN 1 |
39 | 0 | #define MPA_REQ_REP_PDLENGTH_LEN 2 |
40 | 0 | #define MPA_MARKER_RSVD_LEN 2 |
41 | 0 | #define MPA_MARKER_FPDUPTR_LEN 2 |
42 | 0 | #define MPA_CRC_LEN 4 |
43 | | |
44 | | /* protocol constants */ |
45 | 2.34k | #define MPA_REQ_REP_FRAME UINT64_C(0x4d50412049442052) |
46 | 2 | #define MPA_ID_REQ_FRAME UINT64_C(0x6571204672616d65) |
47 | 2 | #define MPA_ID_REP_FRAME UINT64_C(0x6570204672616d65) |
48 | 0 | #define MPA_MARKER_INTERVAL 512 |
49 | 0 | #define MPA_MAX_PD_LENGTH 512 |
50 | 0 | #define MPA_ALIGNMENT 4 |
51 | 0 | #define TCP_MAX_SEQ ((uint32_t) 0xffffffff) |
52 | | |
53 | | /* for code readability */ |
54 | 0 | #define MPA_REQUEST_FRAME 1 |
55 | 0 | #define MPA_REPLY_FRAME 2 |
56 | 0 | #define MPA_FPDU 3 |
57 | 0 | #define MPA_INITIATOR 0 |
58 | 0 | #define MPA_RESPONDER 1 |
59 | | |
60 | | /* bitmasks */ |
61 | 14 | #define MPA_MARKER_FLAG 0x80 |
62 | 14 | #define MPA_CRC_FLAG 0x40 |
63 | 14 | #define MPA_REJECT_FLAG 0x20 |
64 | 14 | #define MPA_ENHANCED_RDMA 0x10 |
65 | 14 | #define MPA_RESERVED_FLAG 0x0F |
66 | | |
67 | 14 | #define MPA_ENHANCED_CONTROL_A 0x80 |
68 | 14 | #define MPA_ENHANCED_CONTROL_B 0x40 |
69 | 14 | #define MPA_ENHANCED_IRD_MASK 0x3FFF |
70 | 14 | #define MPA_ENHANCED_CONTROL_C 0x80 |
71 | 14 | #define MPA_ENHANCED_CONTROL_D 0x40 |
72 | 14 | #define MPA_ENHANCED_ORD_MASK 0x3FFF |
73 | | |
74 | | /* GLOBALS */ |
75 | | |
76 | | /* initialize the protocol and registered fields */ |
77 | | static int proto_iwarp_mpa; |
78 | | static dissector_handle_t iwarp_mpa_handle; |
79 | | |
80 | | static int hf_mpa_req; |
81 | | static int hf_mpa_rep; |
82 | | static int hf_mpa_fpdu; |
83 | | static int hf_mpa_marker; |
84 | | |
85 | | static int hf_mpa_key_req; |
86 | | static int hf_mpa_key_rep; |
87 | | static int hf_mpa_flag_m; |
88 | | static int hf_mpa_flag_c; |
89 | | static int hf_mpa_flag_r; |
90 | | static int hf_mpa_flag_s; |
91 | | static int hf_mpa_flag_res; |
92 | | static int hf_mpa_rev; |
93 | | static int hf_mpa_pd_length; |
94 | | static int hf_mpa_private_data; |
95 | | static int hf_mpa_enhanced; |
96 | | static int hf_mpa_enhanced_a; |
97 | | static int hf_mpa_enhanced_b; |
98 | | static int hf_mpa_enhanced_ird; |
99 | | static int hf_mpa_enhanced_c; |
100 | | static int hf_mpa_enhanced_d; |
101 | | static int hf_mpa_enhanced_ord; |
102 | | |
103 | | static int hf_mpa_legacy; |
104 | | static int hf_mpa_legacy_broken; |
105 | | static int hf_mpa_legacy_ird; |
106 | | static int hf_mpa_legacy_ord; |
107 | | |
108 | | static int hf_mpa_ulpdu_length; |
109 | | static int hf_mpa_pad; |
110 | | static int hf_mpa_crc; |
111 | | static int hf_mpa_crc_check; |
112 | | |
113 | | static int hf_mpa_marker_res; |
114 | | static int hf_mpa_marker_fpduptr; |
115 | | |
116 | | /* initialize the subtree pointers */ |
117 | | static int ett_mpa; |
118 | | |
119 | | static int ett_mpa_req; |
120 | | static int ett_mpa_rep; |
121 | | static int ett_mpa_fpdu; |
122 | | static int ett_mpa_marker; |
123 | | static int ett_mpa_enhanced; |
124 | | static int ett_mpa_legacy; |
125 | | |
126 | | static expert_field ei_mpa_res_field_not_set0; |
127 | | static expert_field ei_mpa_rev_field_not_set1; |
128 | | static expert_field ei_mpa_reject_bit_responder; |
129 | | static expert_field ei_mpa_bad_length; |
130 | | |
131 | | /* handles of our subdissectors */ |
132 | | static dissector_handle_t ddp_rdmap_handle; |
133 | | |
134 | | static const value_string mpa_messages[] = { |
135 | | { MPA_REQUEST_FRAME, "MPA Request Frame" }, |
136 | | { MPA_REPLY_FRAME, "MPA Reply Frame" }, |
137 | | { MPA_FPDU, "MPA FPDU" }, |
138 | | { 0, NULL } |
139 | | }; |
140 | | |
141 | | static const true_false_string tfs_mpa_enhanced_a = { |
142 | | "Peer-To-Peer Connection Model", |
143 | | "Client-To-Server Connection Model", |
144 | | }; |
145 | | |
146 | | static const true_false_string tfs_mpa_enhanced_b = { |
147 | | "Zero-Length FULPDU (Send) RTR Indication", |
148 | | "No Zero-Length FULPDU (Send) RTR Indication", |
149 | | }; |
150 | | |
151 | | static const true_false_string tfs_mpa_enhanced_c = { |
152 | | "Zero-Length RDMA Write RTR Indication", |
153 | | "No Zero-Length RDMA Write RTR Indication", |
154 | | }; |
155 | | |
156 | | static const true_false_string tfs_mpa_enhanced_d = { |
157 | | "Zero-Length RDMA Read RTR Indication", |
158 | | "No Zero-Length RDMA Read RTR Indication", |
159 | | }; |
160 | | |
161 | | /* |
162 | | * CONNECTION STATE and MARKERS |
163 | | * A MPA endpoint operates in two distinct phases. |
164 | | * The Startup Phase is used to verify correct MPA setup, exchange CRC |
165 | | * and Marker configuration, and optionally pass Private Data between |
166 | | * endpoints prior to completing a DDP connection. |
167 | | * The second distinct phase is Full Operation during which FPDUs are |
168 | | * sent using all the rules that pertain (CRC, Markers, MULPDU, |
169 | | * restrictions etc.). |
170 | | * To keep track of a MPA connection configuration a mpa_state is declared |
171 | | * and maintained per TCP connection, i.e. it is associated to a conversation |
172 | | * between two endpoints. |
173 | | * |
174 | | * In some configurations MPA places MARKERs in a FPDU every 512th octet with |
175 | | * respect to the TCP sequence number of the first FPDU. The struct minfo_t |
176 | | * records the source port of a peer that has to insert Markers into its FPDUs |
177 | | * as well as the TCP sequence number of its first FPDU. This information is |
178 | | * necessary to locate the markers within a FPDU afterwards. Itis part of a |
179 | | * mpa_state. |
180 | | */ |
181 | | |
182 | | /* |
183 | | * This struct is used to record the source port 'port' and the TCP sequence |
184 | | * number 'seq' of the first FPDU. This information is used to determine the |
185 | | * position of the first Marker within the following FPDUs. The boolean 'valid' |
186 | | * specifies if Markers are inserted by the endpoint running on source port |
187 | | * 'port' or not. |
188 | | */ |
189 | | struct minfo { |
190 | | uint16_t port; |
191 | | uint32_t seq; |
192 | | bool valid; |
193 | | }; |
194 | | typedef struct minfo minfo_t; |
195 | | |
196 | | /* |
197 | | * This struct represents a MPA connection state. It specifies if Markers and |
198 | | * CRC is used for the following FPDUs. It also contains information to |
199 | | * distinguish between the MPA Startup and Full Operation Phase.the connection |
200 | | * parameters negotiated between to MPA endpoints during the MPA Startup Phase |
201 | | * as well as other information for the dissection. |
202 | | * |
203 | | * The two MPA endpoints are called Initiator, the sender of the MPA Request, |
204 | | * and Responder, the sender of the MPA Reply. |
205 | | * |
206 | | * @full_operation: true if is this state is valid and FALSE otherwise. |
207 | | * @req_frame_num: Frame number of the MPA Request to distinguish this frame |
208 | | * from later FPDUs. |
209 | | * @rep_frame_num: Frame number of the MPA Reply to distinguish this frame |
210 | | * from later FPDUs. |
211 | | * @ini_exp_m_res: true if the Initiator expects the Responder to insert |
212 | | * Markers into his FPDUs sent to Initiator and false otherwise. |
213 | | * @res_exp_m_ini: true if the Responder expects the Initiator to insert |
214 | | * Markers into his FPDUs sent to Responder and false otherwise. |
215 | | * @minfo[2]: Array of minfo_t whichs holds necessary information to |
216 | | * determine the start position of the first Marker within a |
217 | | * a FPDU. |
218 | | * minfo[0] is used for the Initiator endpoint |
219 | | * minfo[1] is used for the Responder endpoint |
220 | | * @crc: true if CRC is used by both endpoints and FALSE otherwise. |
221 | | * @revision: Stores the MPA protocol revision number. |
222 | | */ |
223 | | struct mpa_state { |
224 | | bool full_operation; |
225 | | unsigned req_frame_num; |
226 | | unsigned rep_frame_num; |
227 | | bool ini_exp_m_res; |
228 | | bool res_exp_m_ini; |
229 | | minfo_t minfo[2]; |
230 | | bool crc; |
231 | | int revision; |
232 | | }; |
233 | | typedef struct mpa_state mpa_state_t; |
234 | | |
235 | | /* |
236 | | * Returns an initialized MPA connection state or throws an out of |
237 | | * memory exception. |
238 | | */ |
239 | | static mpa_state_t * |
240 | | init_mpa_state(void) |
241 | 0 | { |
242 | 0 | mpa_state_t *state; |
243 | |
|
244 | 0 | state = wmem_new0(wmem_file_scope(), mpa_state_t); |
245 | 0 | state->revision = -1; |
246 | 0 | return state; |
247 | 0 | } |
248 | | |
249 | | /* |
250 | | * Returns the state associated with a MPA connection or NULL otherwise. |
251 | | */ |
252 | | static mpa_state_t * |
253 | | get_mpa_state(conversation_t *conversation) |
254 | 1.57k | { |
255 | 1.57k | if (conversation) { |
256 | 1.57k | return (mpa_state_t*) conversation_get_proto_data(conversation, |
257 | 1.57k | proto_iwarp_mpa); |
258 | 1.57k | } else { |
259 | 0 | return NULL; |
260 | 0 | } |
261 | 1.57k | } |
262 | | |
263 | | /* |
264 | | * Returns the offset of the first Marker in a FPDU where the beginning of a |
265 | | * FPDU has an offset of 0. It also addresses possible sequence number |
266 | | * overflows. |
267 | | * The endpoint is either the Initiator or the Responder. |
268 | | */ |
269 | | static uint32_t |
270 | | get_first_marker_offset(mpa_state_t *state, struct tcpinfo *tcpinfo, |
271 | | uint8_t endpoint) |
272 | 0 | { |
273 | 0 | uint32_t offset = 0; |
274 | |
|
275 | 0 | if (tcpinfo->seq > state->minfo[endpoint].seq) { |
276 | 0 | offset = (tcpinfo->seq - state->minfo[endpoint].seq) |
277 | 0 | % MPA_MARKER_INTERVAL; |
278 | 0 | } |
279 | |
|
280 | 0 | if (tcpinfo->seq < state->minfo[endpoint].seq) { |
281 | 0 | offset = state->minfo[endpoint].seq |
282 | 0 | + (TCP_MAX_SEQ - tcpinfo->seq) % MPA_MARKER_INTERVAL; |
283 | 0 | } |
284 | |
|
285 | 0 | return (MPA_MARKER_INTERVAL - offset) % MPA_MARKER_INTERVAL; |
286 | 0 | } |
287 | | |
288 | | /* |
289 | | * Returns the total length of this FPDU under the assumption that a TCP |
290 | | * segment carries only one FPDU. |
291 | | */ |
292 | | static uint32_t |
293 | | fpdu_total_length(struct tcpinfo *tcpinfo) |
294 | 0 | { |
295 | 0 | uint32_t size = 0; |
296 | |
|
297 | 0 | if (tcpinfo->seq < tcpinfo->nxtseq) { |
298 | 0 | size = tcpinfo->nxtseq - tcpinfo->seq; |
299 | 0 | } |
300 | |
|
301 | 0 | if (tcpinfo->seq >= tcpinfo->nxtseq) { |
302 | 0 | size = tcpinfo->nxtseq + (TCP_MAX_SEQ - tcpinfo->seq); |
303 | 0 | } |
304 | |
|
305 | 0 | return size; |
306 | 0 | } |
307 | | |
308 | | /* |
309 | | * Returns the number of Markers of this MPA FPDU. The endpoint is either the |
310 | | * Initiator or the Responder. |
311 | | */ |
312 | | static uint32_t |
313 | | number_of_markers(mpa_state_t *state, struct tcpinfo *tcpinfo, uint8_t endpoint) |
314 | 0 | { |
315 | 0 | uint32_t size; |
316 | 0 | uint32_t offset; |
317 | |
|
318 | 0 | size = fpdu_total_length(tcpinfo); |
319 | 0 | offset = get_first_marker_offset(state, tcpinfo, endpoint); |
320 | |
|
321 | 0 | if (offset < size) { |
322 | 0 | return ((size - offset) / MPA_MARKER_INTERVAL)+1; |
323 | 0 | } else { |
324 | 0 | return 0; |
325 | 0 | } |
326 | 0 | } |
327 | | |
328 | | /* |
329 | | * Removes any Markers from this FPDU by using memcpy or throws an out of memory |
330 | | * exception. |
331 | | */ |
332 | | static tvbuff_t * |
333 | | remove_markers(tvbuff_t *tvb, packet_info *pinfo, uint32_t marker_offset, |
334 | | uint32_t num_markers, uint32_t orig_length) |
335 | 0 | { |
336 | 0 | uint8_t *mfree_buff = NULL; |
337 | 0 | uint32_t mfree_buff_length, tot_copy, cur_copy; |
338 | 0 | uint32_t source_offset; |
339 | 0 | tvbuff_t *mfree_tvb = NULL; |
340 | |
|
341 | 0 | DISSECTOR_ASSERT(num_markers > 0); |
342 | 0 | DISSECTOR_ASSERT(orig_length > MPA_MARKER_LEN * num_markers); |
343 | 0 | DISSECTOR_ASSERT(tvb_captured_length(tvb) == orig_length); |
344 | | |
345 | | /* allocate memory for the marker-free buffer */ |
346 | 0 | mfree_buff_length = orig_length - (MPA_MARKER_LEN * num_markers); |
347 | 0 | mfree_buff = (uint8_t *)wmem_alloc(pinfo->pool, mfree_buff_length); |
348 | |
|
349 | 0 | tot_copy = 0; |
350 | 0 | source_offset = 0; |
351 | 0 | cur_copy = marker_offset; |
352 | 0 | while (tot_copy < mfree_buff_length) { |
353 | 0 | tvb_memcpy(tvb, mfree_buff+tot_copy, source_offset, cur_copy); |
354 | 0 | tot_copy += cur_copy; |
355 | 0 | source_offset += cur_copy + MPA_MARKER_LEN; |
356 | 0 | cur_copy = MIN(MPA_MARKER_INTERVAL, (mfree_buff_length - tot_copy)); |
357 | 0 | } |
358 | 0 | mfree_tvb = tvb_new_child_real_data(tvb, mfree_buff, mfree_buff_length, |
359 | 0 | mfree_buff_length); |
360 | 0 | add_new_data_source(pinfo, mfree_tvb, "FPDU without Markers"); |
361 | |
|
362 | 0 | return mfree_tvb; |
363 | 0 | } |
364 | | |
365 | | /* returns true if this TCP segment carries a MPA REQUEST and FALSE otherwise */ |
366 | | static bool |
367 | | is_mpa_req(tvbuff_t *tvb, packet_info *pinfo) |
368 | 1.17k | { |
369 | 1.17k | conversation_t *conversation = NULL; |
370 | 1.17k | mpa_state_t *state = NULL; |
371 | 1.17k | uint8_t mcrres; |
372 | | |
373 | 1.17k | if (tvb_get_ntoh64(tvb, 0) != MPA_REQ_REP_FRAME |
374 | 2 | || tvb_get_ntoh64(tvb, 8) != MPA_ID_REQ_FRAME) |
375 | 1.17k | return false; |
376 | | |
377 | 0 | conversation = find_or_create_conversation(pinfo); |
378 | |
|
379 | 0 | if (!get_mpa_state(conversation)) { |
380 | | |
381 | | /* associate a MPA connection state to this conversation if |
382 | | * there is no MPA state already associated to this connection |
383 | | */ |
384 | 0 | state = init_mpa_state(); |
385 | | |
386 | | /* analyze MPA connection parameter and record them */ |
387 | 0 | mcrres = tvb_get_uint8(tvb, 16); |
388 | 0 | state->ini_exp_m_res = mcrres & MPA_MARKER_FLAG; |
389 | 0 | state->crc = mcrres & MPA_CRC_FLAG; |
390 | 0 | state->revision = tvb_get_uint8(tvb, 17); |
391 | 0 | state->req_frame_num = pinfo->num; |
392 | 0 | state->minfo[MPA_INITIATOR].port = pinfo->srcport; |
393 | 0 | state->minfo[MPA_RESPONDER].port = pinfo->destport; |
394 | |
|
395 | 0 | conversation_add_proto_data(conversation, proto_iwarp_mpa, state); |
396 | | |
397 | | /* update expert info */ |
398 | 0 | if (mcrres & MPA_RESERVED_FLAG) |
399 | 0 | expert_add_info(pinfo, NULL, &ei_mpa_res_field_not_set0); |
400 | |
|
401 | 0 | if (state->revision != 1) |
402 | 0 | expert_add_info(pinfo, NULL, &ei_mpa_rev_field_not_set1); |
403 | 0 | } |
404 | 0 | return true; |
405 | 1.17k | } |
406 | | |
407 | | /* returns true if this TCP segment carries a MPA REPLY and false otherwise */ |
408 | | static bool |
409 | | is_mpa_rep(tvbuff_t *tvb, packet_info *pinfo) |
410 | 1.17k | { |
411 | 1.17k | conversation_t *conversation = NULL; |
412 | 1.17k | mpa_state_t *state = NULL; |
413 | 1.17k | uint8_t mcrres; |
414 | | |
415 | 1.17k | if (tvb_get_ntoh64(tvb, 0) != MPA_REQ_REP_FRAME |
416 | 1.17k | || tvb_get_ntoh64(tvb, 8) != MPA_ID_REP_FRAME) { |
417 | 1.17k | return false; |
418 | 1.17k | } |
419 | | |
420 | 0 | conversation = find_conversation_pinfo(pinfo, 0); |
421 | |
|
422 | 0 | if (!conversation) { |
423 | 0 | return false; |
424 | 0 | } |
425 | | |
426 | 0 | state = get_mpa_state(conversation); |
427 | 0 | if (!state) { |
428 | 0 | return false; |
429 | 0 | } |
430 | | |
431 | 0 | if (!state->full_operation) { |
432 | | /* update state of this conversation */ |
433 | 0 | mcrres = tvb_get_uint8(tvb, 16); |
434 | 0 | state->res_exp_m_ini = mcrres & MPA_MARKER_FLAG; |
435 | 0 | state->crc = state->crc | (mcrres & MPA_CRC_FLAG); |
436 | 0 | state->rep_frame_num = pinfo->num; |
437 | | |
438 | | /* enter Full Operation Phase only if the Reject bit is not set */ |
439 | 0 | if (!(mcrres & MPA_REJECT_FLAG)) |
440 | 0 | state->full_operation = true; |
441 | 0 | else |
442 | 0 | expert_add_info(pinfo, NULL, &ei_mpa_reject_bit_responder); |
443 | 0 | } |
444 | 0 | return true; |
445 | 0 | } |
446 | | |
447 | | /* returns true if this TCP segment carries a MPA FPDU and false otherwise */ |
448 | | static bool |
449 | | is_mpa_fpdu(packet_info *pinfo) |
450 | 1.57k | { |
451 | 1.57k | conversation_t *conversation = NULL; |
452 | 1.57k | mpa_state_t *state = NULL; |
453 | | |
454 | 1.57k | conversation = find_conversation_pinfo(pinfo, 0); |
455 | | |
456 | 1.57k | if (!conversation) { |
457 | 0 | return false; |
458 | 0 | } |
459 | | |
460 | 1.57k | state = get_mpa_state(conversation); |
461 | 1.57k | if (!state) { |
462 | 1.57k | return false; |
463 | 1.57k | } |
464 | | |
465 | | /* make sure all MPA connection parameters have been set */ |
466 | 0 | if (!state->full_operation) { |
467 | 0 | return false; |
468 | 0 | } |
469 | | |
470 | 0 | if (pinfo->num == state->req_frame_num |
471 | 0 | || pinfo->num == state->rep_frame_num) { |
472 | 0 | return false; |
473 | 0 | } else { |
474 | 0 | return true; |
475 | 0 | } |
476 | 0 | } |
477 | | |
478 | | /* update packet list pane in the GUI */ |
479 | | static void |
480 | | mpa_packetlist(packet_info *pinfo, int message_type) |
481 | 0 | { |
482 | 0 | col_set_str(pinfo->cinfo, COL_PROTOCOL, "MPA"); |
483 | |
|
484 | 0 | col_add_fstr(pinfo->cinfo, COL_INFO, |
485 | 0 | "%d > %d %s", pinfo->srcport, pinfo->destport, |
486 | 0 | val_to_str(pinfo->pool, message_type, mpa_messages, |
487 | 0 | "Unknown %d")); |
488 | 0 | } |
489 | | |
490 | | /* dissects MPA REQUEST or MPA REPLY */ |
491 | | static bool |
492 | | dissect_mpa_req_rep(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, |
493 | | int message_type) |
494 | 0 | { |
495 | 0 | proto_tree *mpa_tree = NULL; |
496 | 0 | proto_tree *mpa_header_tree = NULL; |
497 | |
|
498 | 0 | proto_item *mpa_item = NULL; |
499 | 0 | proto_item *mpa_header_item = NULL; |
500 | |
|
501 | 0 | uint8_t flags = 0; |
502 | 0 | uint16_t pd_length; |
503 | 0 | uint32_t offset = 0; |
504 | |
|
505 | 0 | mpa_packetlist(pinfo, message_type); |
506 | |
|
507 | 0 | if (tree) { |
508 | 0 | mpa_item = proto_tree_add_item(tree, proto_iwarp_mpa, tvb, 0, |
509 | 0 | -1, ENC_NA); |
510 | 0 | mpa_tree = proto_item_add_subtree(mpa_item, ett_mpa); |
511 | |
|
512 | 0 | if (message_type == MPA_REQUEST_FRAME) { |
513 | 0 | mpa_header_item = proto_tree_add_item(mpa_tree, |
514 | 0 | hf_mpa_req, tvb, offset, -1, ENC_NA); |
515 | 0 | mpa_header_tree = proto_item_add_subtree( |
516 | 0 | mpa_header_item, ett_mpa_req); |
517 | 0 | proto_tree_add_item(mpa_header_tree, hf_mpa_key_req, |
518 | 0 | tvb, offset, MPA_REQ_REP_KEY_LEN, ENC_NA); |
519 | 0 | } |
520 | |
|
521 | 0 | if (message_type == MPA_REPLY_FRAME) { |
522 | 0 | mpa_header_item = proto_tree_add_item(mpa_tree, |
523 | 0 | hf_mpa_rep, tvb, offset, -1, ENC_NA); |
524 | 0 | mpa_header_tree = proto_item_add_subtree( |
525 | 0 | mpa_header_item, ett_mpa_rep); |
526 | 0 | proto_tree_add_item(mpa_header_tree, hf_mpa_key_rep, |
527 | 0 | tvb, offset, MPA_REQ_REP_KEY_LEN, ENC_NA); |
528 | 0 | } |
529 | 0 | offset += MPA_REQ_REP_KEY_LEN; |
530 | |
|
531 | 0 | flags = tvb_get_uint8(tvb, offset); |
532 | 0 | proto_tree_add_item(mpa_header_tree, hf_mpa_flag_m, tvb, |
533 | 0 | offset, MPA_REQ_REP_FLAG_LEN, ENC_BIG_ENDIAN); |
534 | 0 | proto_tree_add_item(mpa_header_tree, hf_mpa_flag_c, tvb, |
535 | 0 | offset, MPA_REQ_REP_FLAG_LEN, ENC_BIG_ENDIAN); |
536 | 0 | proto_tree_add_item(mpa_header_tree, hf_mpa_flag_r, tvb, |
537 | 0 | offset, MPA_REQ_REP_FLAG_LEN, ENC_BIG_ENDIAN); |
538 | 0 | proto_tree_add_item(mpa_header_tree, hf_mpa_flag_s, tvb, |
539 | 0 | offset, MPA_REQ_REP_FLAG_LEN, ENC_BIG_ENDIAN); |
540 | 0 | proto_tree_add_item(mpa_header_tree, hf_mpa_flag_res, tvb, |
541 | 0 | offset, MPA_REQ_REP_FLAG_LEN, ENC_BIG_ENDIAN); |
542 | 0 | offset += MPA_REQ_REP_FLAG_LEN; |
543 | |
|
544 | 0 | proto_tree_add_item(mpa_header_tree, hf_mpa_rev, tvb, |
545 | 0 | offset, MPA_REQ_REP_REV_LEN, ENC_BIG_ENDIAN); |
546 | 0 | offset += MPA_REQ_REP_REV_LEN; |
547 | | |
548 | | /* check whether the Private Data Length conforms to RFC 5044 */ |
549 | 0 | pd_length = tvb_get_ntohs(tvb, offset); |
550 | 0 | if (pd_length > MPA_MAX_PD_LENGTH) { |
551 | 0 | proto_tree_add_expert_format(tree, pinfo, &ei_mpa_bad_length, tvb, offset, 2, |
552 | 0 | "[PD length field indicates more 512 bytes of Private Data]"); |
553 | 0 | return false; |
554 | 0 | } |
555 | | |
556 | 0 | if (pd_length >= 4 && flags & MPA_ENHANCED_RDMA) { |
557 | 0 | pd_length -= 4; |
558 | 0 | } else { |
559 | 0 | flags &= ~MPA_ENHANCED_RDMA; |
560 | 0 | } |
561 | |
|
562 | 0 | proto_tree_add_uint(mpa_header_tree, |
563 | 0 | hf_mpa_pd_length, tvb, offset, |
564 | 0 | MPA_REQ_REP_PDLENGTH_LEN, pd_length); |
565 | 0 | offset += MPA_REQ_REP_PDLENGTH_LEN; |
566 | |
|
567 | 0 | if (flags & MPA_ENHANCED_RDMA) { |
568 | 0 | proto_item *enhanced_item = NULL; |
569 | 0 | proto_tree *enhanced_tree = NULL; |
570 | |
|
571 | 0 | enhanced_item = proto_tree_add_item(mpa_header_tree, |
572 | 0 | hf_mpa_enhanced, |
573 | 0 | tvb, |
574 | 0 | offset, 4, |
575 | 0 | ENC_NA); |
576 | 0 | enhanced_tree = proto_item_add_subtree( |
577 | 0 | enhanced_item, ett_mpa_enhanced); |
578 | |
|
579 | 0 | proto_tree_add_item(enhanced_tree, hf_mpa_enhanced_a, |
580 | 0 | tvb, offset, 1, ENC_BIG_ENDIAN); |
581 | 0 | proto_tree_add_item(enhanced_tree, hf_mpa_enhanced_b, |
582 | 0 | tvb, offset, 1, ENC_BIG_ENDIAN); |
583 | 0 | proto_tree_add_item(enhanced_tree, hf_mpa_enhanced_ird, |
584 | 0 | tvb, offset, 2, ENC_BIG_ENDIAN); |
585 | 0 | offset += 2; |
586 | |
|
587 | 0 | proto_tree_add_item(enhanced_tree, hf_mpa_enhanced_c, |
588 | 0 | tvb, offset, 1, ENC_BIG_ENDIAN); |
589 | 0 | proto_tree_add_item(enhanced_tree, hf_mpa_enhanced_d, |
590 | 0 | tvb, offset, 1, ENC_BIG_ENDIAN); |
591 | 0 | proto_tree_add_item(enhanced_tree, hf_mpa_enhanced_ord, |
592 | 0 | tvb, offset, 2, ENC_BIG_ENDIAN); |
593 | 0 | offset += 2; |
594 | 0 | } |
595 | |
|
596 | 0 | if (pd_length) { |
597 | 0 | proto_tree_add_item(mpa_header_tree, |
598 | 0 | hf_mpa_private_data, tvb, offset, |
599 | 0 | pd_length, ENC_NA); |
600 | 0 | } |
601 | | |
602 | | /* |
603 | | * Display the legacy IRD/ORD negotiation from |
604 | | * [MS-SMBD] 6 Appendix A: RDMA Provider IRD/ORD Negotiation |
605 | | */ |
606 | 0 | if (pd_length == 8 && |
607 | 0 | tvb_get_ntohl(tvb, offset+0) <= 0x3FFF && |
608 | 0 | tvb_get_ntohl(tvb, offset+4) <= 0x3FFF) |
609 | 0 | { |
610 | 0 | proto_item *legacy_item = NULL; |
611 | 0 | proto_tree *legacy_tree = NULL; |
612 | |
|
613 | 0 | legacy_item = proto_tree_add_item(mpa_header_tree, |
614 | 0 | hf_mpa_legacy, |
615 | 0 | tvb, |
616 | 0 | offset, 8, |
617 | 0 | ENC_NA); |
618 | 0 | legacy_tree = proto_item_add_subtree(legacy_item, |
619 | 0 | ett_mpa_legacy); |
620 | 0 | proto_tree_add_item(legacy_tree, hf_mpa_legacy_ird, |
621 | 0 | tvb, offset+0, 4, ENC_BIG_ENDIAN); |
622 | 0 | proto_tree_add_item(legacy_tree, hf_mpa_legacy_ord, |
623 | 0 | tvb, offset+4, 4, ENC_BIG_ENDIAN); |
624 | 0 | } |
625 | | /* |
626 | | * Broken version using little endian |
627 | | */ |
628 | 0 | if (pd_length == 8 && |
629 | 0 | tvb_get_letohl(tvb, offset+0) <= 0x3FFF && |
630 | 0 | tvb_get_letohl(tvb, offset+4) <= 0x3FFF) |
631 | 0 | { |
632 | 0 | proto_item *legacy_item = NULL; |
633 | 0 | proto_tree *legacy_tree = NULL; |
634 | |
|
635 | 0 | legacy_item = proto_tree_add_item(mpa_header_tree, |
636 | 0 | hf_mpa_legacy_broken, |
637 | 0 | tvb, |
638 | 0 | offset, 8, |
639 | 0 | ENC_NA); |
640 | 0 | legacy_tree = proto_item_add_subtree(legacy_item, |
641 | 0 | ett_mpa_legacy); |
642 | 0 | proto_tree_add_item(legacy_tree, hf_mpa_legacy_ird, |
643 | 0 | tvb, offset+0, 4, ENC_LITTLE_ENDIAN); |
644 | 0 | proto_tree_add_item(legacy_tree, hf_mpa_legacy_ord, |
645 | 0 | tvb, offset+4, 4, ENC_LITTLE_ENDIAN); |
646 | 0 | } |
647 | 0 | } |
648 | 0 | return true; |
649 | 0 | } |
650 | | |
651 | | /* returns byte length of the padding */ |
652 | | static uint8_t |
653 | | fpdu_pad_length(uint16_t ulpdu_length) |
654 | 0 | { |
655 | | /* |
656 | | * The padding guarantees alignment of 4. Since Markers are 4 bytes long |
657 | | * we do need to take them into consideration for computation of pad |
658 | | * length. The padding length depends only on ULPDU (payload) length and |
659 | | * the length of the header field for the ULPDU length. |
660 | | */ |
661 | 0 | uint32_t length = ulpdu_length + MPA_ULPDU_LENGTH_LEN; |
662 | | |
663 | | /* |
664 | | * The extra % MPA_ALIGNMENT at the end covers for the case |
665 | | * length % MPA_ALIGNMENT == 0. |
666 | | */ |
667 | 0 | return (MPA_ALIGNMENT - (length % MPA_ALIGNMENT)) % MPA_ALIGNMENT; |
668 | 0 | } |
669 | | |
670 | | /* returns offset for PAD */ |
671 | | static uint32_t |
672 | | pad_offset(struct tcpinfo *tcpinfo, uint32_t fpdu_total_len, |
673 | | uint8_t pad_len) |
674 | 0 | { |
675 | 0 | if ((tcpinfo->nxtseq - MPA_CRC_LEN - MPA_MARKER_LEN) % MPA_MARKER_INTERVAL |
676 | 0 | == 0) { |
677 | | /* covers the case where a Marker resides between the padding |
678 | | * and CRC. |
679 | | */ |
680 | 0 | return fpdu_total_len - MPA_CRC_LEN - MPA_MARKER_LEN - pad_len; |
681 | 0 | } else { |
682 | 0 | return fpdu_total_len - MPA_CRC_LEN - pad_len; |
683 | 0 | } |
684 | 0 | } |
685 | | |
686 | | /* dissects CRC within a FPDU */ |
687 | | static void |
688 | | dissect_fpdu_crc(tvbuff_t *tvb, proto_tree *tree, mpa_state_t *state, |
689 | | uint32_t offset, uint32_t length) |
690 | 0 | { |
691 | 0 | uint32_t crc = 0; |
692 | 0 | uint32_t sent_crc = 0; |
693 | |
|
694 | 0 | if (state->crc) { |
695 | |
|
696 | 0 | crc = ~crc32c_tvb_offset_calculate(tvb, 0, length, CRC32C_PRELOAD); |
697 | |
|
698 | 0 | sent_crc = tvb_get_ntohl(tvb, offset); /* crc start offset */ |
699 | |
|
700 | 0 | if (crc == sent_crc) { |
701 | 0 | proto_tree_add_uint_format_value(tree, |
702 | 0 | hf_mpa_crc_check, tvb, offset, MPA_CRC_LEN, |
703 | 0 | sent_crc, "0x%08x (Good CRC32)", |
704 | 0 | sent_crc); |
705 | 0 | } else { |
706 | 0 | proto_tree_add_uint_format_value(tree, |
707 | 0 | hf_mpa_crc_check, tvb, offset, MPA_CRC_LEN, |
708 | 0 | sent_crc, |
709 | 0 | "0x%08x (Bad CRC32, should be 0x%08x)", |
710 | 0 | sent_crc, crc); |
711 | 0 | } |
712 | 0 | } else { |
713 | 0 | proto_tree_add_item(tree, hf_mpa_crc, tvb, offset, MPA_CRC_LEN, |
714 | 0 | ENC_BIG_ENDIAN); |
715 | 0 | } |
716 | 0 | } |
717 | | |
718 | | /* dissects Markers within FPDU */ |
719 | | static void |
720 | | dissect_fpdu_markers(tvbuff_t *tvb, proto_tree *tree, mpa_state_t *state, |
721 | | struct tcpinfo *tcpinfo, uint8_t endpoint) |
722 | 0 | { |
723 | 0 | proto_tree *mpa_marker_tree; |
724 | 0 | proto_item *mpa_marker_item; |
725 | 0 | uint32_t offset, i; |
726 | |
|
727 | 0 | mpa_marker_item = proto_tree_add_item(tree, hf_mpa_marker, tvb, |
728 | 0 | 0, -1, ENC_NA); |
729 | 0 | mpa_marker_tree = proto_item_add_subtree(mpa_marker_item, ett_mpa_marker); |
730 | |
|
731 | 0 | offset = get_first_marker_offset(state, tcpinfo, endpoint); |
732 | |
|
733 | 0 | for (i=0; i<number_of_markers(state, tcpinfo, endpoint); i++) { |
734 | 0 | proto_tree_add_item(mpa_marker_tree, hf_mpa_marker_res, tvb, |
735 | 0 | offset, MPA_MARKER_RSVD_LEN, ENC_BIG_ENDIAN); |
736 | 0 | proto_tree_add_item(mpa_marker_tree, |
737 | 0 | hf_mpa_marker_fpduptr, tvb, |
738 | 0 | offset+MPA_MARKER_RSVD_LEN, MPA_MARKER_FPDUPTR_LEN, ENC_BIG_ENDIAN); |
739 | 0 | offset += MPA_MARKER_INTERVAL; |
740 | 0 | } |
741 | 0 | } |
742 | | |
743 | | /* returns the expected value of the 16 bits long MPA FPDU ULPDU LENGTH field */ |
744 | | static uint16_t |
745 | | expected_ulpdu_length(mpa_state_t *state, struct tcpinfo *tcpinfo, |
746 | | uint8_t endpoint) |
747 | 0 | { |
748 | 0 | uint32_t length, pad_length, markers_length; |
749 | |
|
750 | 0 | length = fpdu_total_length(tcpinfo); |
751 | |
|
752 | 0 | if (length <= MPA_CRC_LEN) |
753 | 0 | return 0; |
754 | 0 | length -= MPA_CRC_LEN; |
755 | |
|
756 | 0 | pad_length = (MPA_ALIGNMENT - (length % MPA_ALIGNMENT)) % MPA_ALIGNMENT; |
757 | |
|
758 | 0 | if (length <= pad_length) |
759 | 0 | return 0; |
760 | 0 | length -= pad_length; |
761 | |
|
762 | 0 | if (state->minfo[endpoint].valid) { |
763 | 0 | markers_length = |
764 | 0 | number_of_markers(state, tcpinfo, endpoint) * MPA_MARKER_LEN; |
765 | |
|
766 | 0 | if (length <= markers_length) |
767 | 0 | return 0; |
768 | 0 | length -= markers_length; |
769 | 0 | } |
770 | | |
771 | 0 | if (length <= MPA_ULPDU_LENGTH_LEN) |
772 | 0 | return 0; |
773 | 0 | length -= MPA_ULPDU_LENGTH_LEN; |
774 | |
|
775 | 0 | return (uint16_t) length; |
776 | 0 | } |
777 | | |
778 | | /* dissects MPA FPDU */ |
779 | | static uint16_t |
780 | | dissect_mpa_fpdu(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, |
781 | | mpa_state_t *state, struct tcpinfo *tcpinfo, uint8_t endpoint) |
782 | 0 | { |
783 | 0 | proto_item *mpa_item = NULL; |
784 | 0 | proto_item *mpa_header_item = NULL; |
785 | |
|
786 | 0 | proto_tree *mpa_tree = NULL; |
787 | 0 | proto_tree *mpa_header_tree = NULL; |
788 | |
|
789 | 0 | uint8_t pad_length; |
790 | 0 | uint16_t ulpdu_length, exp_ulpdu_length; |
791 | 0 | uint32_t offset, total_length; |
792 | 0 | uint32_t num_of_m = 0; |
793 | | |
794 | | /* |
795 | | * Initialize starting offset for this FPDU. Deals with the case that this |
796 | | * FPDU may start with a Marker instead of the ULPDU_LENGTH header field. |
797 | | */ |
798 | 0 | if (state->minfo[endpoint].valid |
799 | 0 | && get_first_marker_offset(state, tcpinfo, endpoint) == 0) { |
800 | 0 | offset = MPA_MARKER_LEN; |
801 | 0 | } else { |
802 | 0 | offset = 0; |
803 | 0 | } |
804 | | |
805 | | /* get ULPDU length of this FPDU */ |
806 | 0 | ulpdu_length = (uint16_t) tvb_get_ntohs(tvb, offset); |
807 | |
|
808 | 0 | if (state->minfo[endpoint].valid) { |
809 | 0 | num_of_m = number_of_markers(state, tcpinfo, endpoint); |
810 | 0 | } |
811 | | |
812 | | |
813 | | /* |
814 | | * Stop FPDU dissection if the read ULPDU_LENGTH field does NOT contain |
815 | | * what is expected. |
816 | | * Reasons for getting a wrong ULPDU_LENGTH can be lost packets (because |
817 | | * libpcap was not able to capture every packet) or lost alignment (the |
818 | | * MPA FPDU header does not start right after TCP header). |
819 | | * We consider the above to be an error since we make the assumption |
820 | | * that exactly one MPA FPDU is contained in one TCP segment and starts |
821 | | * always either with a Marker or the ULPDU_LENGTH header field. |
822 | | */ |
823 | 0 | pad_length = fpdu_pad_length(ulpdu_length); |
824 | 0 | if (num_of_m > 0) { |
825 | 0 | exp_ulpdu_length = expected_ulpdu_length(state, tcpinfo, endpoint); |
826 | 0 | if (!exp_ulpdu_length || exp_ulpdu_length != (ulpdu_length + pad_length)) { |
827 | 0 | return 0; |
828 | 0 | } |
829 | 0 | } |
830 | | |
831 | 0 | mpa_packetlist(pinfo, MPA_FPDU); |
832 | |
|
833 | 0 | mpa_item = proto_tree_add_item(tree, proto_iwarp_mpa, tvb, 0, |
834 | 0 | -1, ENC_NA); |
835 | 0 | mpa_tree = proto_item_add_subtree(mpa_item, ett_mpa); |
836 | |
|
837 | 0 | mpa_header_item = proto_tree_add_item(mpa_tree, hf_mpa_fpdu, |
838 | 0 | tvb, offset, -1, ENC_NA); |
839 | 0 | mpa_header_tree = proto_item_add_subtree(mpa_header_item, |
840 | 0 | ett_mpa_fpdu); |
841 | | |
842 | | /* ULPDU Length header field */ |
843 | 0 | proto_tree_add_uint(mpa_header_tree, |
844 | 0 | hf_mpa_ulpdu_length, tvb, offset, |
845 | 0 | MPA_ULPDU_LENGTH_LEN, ulpdu_length); |
846 | | |
847 | | /* Markers are present in this FPDU */ |
848 | 0 | if (num_of_m > 0) { |
849 | |
|
850 | 0 | total_length = fpdu_total_length(tcpinfo); |
851 | |
|
852 | 0 | if (pad_length > 0) { |
853 | 0 | proto_tree_add_item(mpa_header_tree, hf_mpa_pad, |
854 | 0 | tvb, pad_offset(tcpinfo, |
855 | 0 | total_length, |
856 | 0 | pad_length), |
857 | 0 | pad_length, ENC_NA); |
858 | 0 | } |
859 | |
|
860 | 0 | dissect_fpdu_crc(tvb, mpa_header_tree, state, |
861 | 0 | total_length-MPA_CRC_LEN, num_of_m * MPA_MARKER_LEN + |
862 | 0 | ulpdu_length + pad_length + MPA_ULPDU_LENGTH_LEN); |
863 | |
|
864 | 0 | dissect_fpdu_markers(tvb, mpa_tree, state, tcpinfo, endpoint); |
865 | |
|
866 | 0 | } else { /* Markers are not present or not enabled */ |
867 | |
|
868 | 0 | offset += MPA_ULPDU_LENGTH_LEN + ulpdu_length; |
869 | |
|
870 | 0 | if (pad_length > 0) { |
871 | 0 | proto_tree_add_item(mpa_header_tree, hf_mpa_pad, tvb, offset, |
872 | 0 | pad_length, ENC_NA); |
873 | 0 | offset += pad_length; |
874 | 0 | } |
875 | |
|
876 | 0 | dissect_fpdu_crc(tvb, mpa_header_tree, state, offset, |
877 | 0 | ulpdu_length+pad_length+MPA_ULPDU_LENGTH_LEN); |
878 | 0 | } |
879 | 0 | return ulpdu_length; |
880 | 0 | } |
881 | | |
882 | | /* Extracted from dissect_warp_mpa, Obtain the TCP seq of the first FPDU */ |
883 | | static mpa_state_t* |
884 | | get_state_of_first_fpdu(tvbuff_t *tvb, packet_info *pinfo, struct tcpinfo *tcpinfo, uint8_t *endpoint) |
885 | 0 | { |
886 | 0 | conversation_t *conversation = NULL; |
887 | 0 | mpa_state_t *state = NULL; |
888 | |
|
889 | 0 | if (tvb_captured_length(tvb) >= MPA_SMALLEST_FPDU_LEN && is_mpa_fpdu(pinfo)) { |
890 | 0 | conversation = find_conversation_pinfo(pinfo, 0); |
891 | 0 | state = get_mpa_state(conversation); |
892 | |
|
893 | 0 | if (pinfo->srcport == state->minfo[MPA_INITIATOR].port) { |
894 | 0 | *endpoint = MPA_INITIATOR; |
895 | 0 | } else if (pinfo->srcport == state->minfo[MPA_RESPONDER].port) { |
896 | 0 | *endpoint = MPA_RESPONDER; |
897 | 0 | } else { |
898 | 0 | REPORT_DISSECTOR_BUG("endpoint cannot be determined"); |
899 | 0 | } |
900 | | |
901 | | /* Markers are used by either the Initiator or the Responder or both. */ |
902 | 0 | if ((state->ini_exp_m_res || state->res_exp_m_ini) && *endpoint <= MPA_RESPONDER) { |
903 | | |
904 | | /* find the TCP sequence number of the first FPDU */ |
905 | 0 | if (!state->minfo[*endpoint].valid) { |
906 | 0 | state->minfo[*endpoint].seq = tcpinfo->seq; |
907 | 0 | state->minfo[*endpoint].valid = true; |
908 | 0 | } |
909 | 0 | } |
910 | 0 | } |
911 | |
|
912 | 0 | return state; |
913 | 0 | } |
914 | | |
915 | | |
916 | | /* |
917 | | * Main dissection routine. |
918 | | */ |
919 | | static bool |
920 | | dissect_iwarp_mpa(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void *data) |
921 | 0 | { |
922 | 0 | tvbuff_t *next_tvb = NULL; |
923 | 0 | mpa_state_t *state = NULL; |
924 | 0 | struct tcpinfo *tcpinfo; |
925 | 0 | uint8_t endpoint = 3; |
926 | 0 | uint16_t ulpdu_length = 0; |
927 | |
|
928 | 0 | if (data == NULL) |
929 | 0 | return false; |
930 | 0 | tcpinfo = (struct tcpinfo *)data; |
931 | | |
932 | | /* FPDU */ |
933 | 0 | state = get_state_of_first_fpdu(tvb, pinfo, tcpinfo, &endpoint); |
934 | 0 | if (state) { |
935 | | /* dissect FPDU */ |
936 | 0 | ulpdu_length = dissect_mpa_fpdu(tvb, pinfo, tree, state, tcpinfo, |
937 | 0 | endpoint); |
938 | | |
939 | | /* an ulpdu_length of 0 should never happen */ |
940 | 0 | if (!ulpdu_length) |
941 | 0 | return false; |
942 | | |
943 | | /* removes Markers if any and prepares new tvbuff for next dissector */ |
944 | 0 | if (endpoint <= MPA_RESPONDER && state->minfo[endpoint].valid |
945 | 0 | && number_of_markers(state, tcpinfo, endpoint) > 0) { |
946 | 0 | next_tvb = tvb_new_subset_length(remove_markers(tvb, pinfo, |
947 | 0 | get_first_marker_offset(state, tcpinfo, endpoint), |
948 | 0 | number_of_markers(state, tcpinfo, endpoint), |
949 | 0 | fpdu_total_length(tcpinfo)), MPA_ULPDU_LENGTH_LEN, |
950 | 0 | ulpdu_length); |
951 | 0 | } else { |
952 | 0 | next_tvb = tvb_new_subset_length(tvb, MPA_ULPDU_LENGTH_LEN, ulpdu_length); |
953 | 0 | } |
954 | | |
955 | | |
956 | | /* call subdissector */ |
957 | 0 | if (ddp_rdmap_handle) { |
958 | 0 | call_dissector(ddp_rdmap_handle, next_tvb, pinfo, tree); |
959 | 0 | } else { |
960 | 0 | REPORT_DISSECTOR_BUG("ddp_handle was null"); |
961 | 0 | } |
962 | |
|
963 | 0 | return true; |
964 | 0 | } |
965 | | |
966 | | /* MPA REQUEST or MPA REPLY */ |
967 | 0 | if (tvb_captured_length(tvb) >= MPA_REQ_REP_FRAME_HEADER_LEN) { |
968 | 0 | if (is_mpa_req(tvb, pinfo)) |
969 | 0 | return dissect_mpa_req_rep(tvb, pinfo, tree, MPA_REQUEST_FRAME); |
970 | 0 | else if (is_mpa_rep(tvb, pinfo)) |
971 | 0 | return dissect_mpa_req_rep(tvb, pinfo, tree, MPA_REPLY_FRAME); |
972 | 0 | } |
973 | 0 | return false; |
974 | 0 | } |
975 | | |
976 | | static unsigned |
977 | | iwrap_mpa_pdu_length(packet_info *pinfo _U_, tvbuff_t *tvb, |
978 | | int offset, void *data _U_) |
979 | 0 | { |
980 | 0 | uint64_t tag; |
981 | 0 | int remaining = tvb_captured_length_remaining(tvb, offset); |
982 | 0 | unsigned pdu_length = 0; |
983 | 0 | uint16_t PD_Length; |
984 | 0 | mpa_state_t *state = NULL; |
985 | 0 | uint8_t endpoint = 3; |
986 | 0 | uint32_t num_of_m = 0; |
987 | 0 | struct tcpinfo *tcpinfo; |
988 | 0 | int current_offset = offset; |
989 | |
|
990 | 0 | tag = tvb_get_ntoh64(tvb, offset); |
991 | 0 | if (tag != MPA_REQ_REP_FRAME) { |
992 | | /* FPDU */ |
993 | 0 | uint16_t ULPDU_Length; |
994 | 0 | uint8_t pad_length; |
995 | |
|
996 | 0 | tcpinfo = (struct tcpinfo *)data; |
997 | |
|
998 | 0 | state = get_state_of_first_fpdu(tvb, pinfo, tcpinfo, &endpoint); |
999 | 0 | if (state) { |
1000 | 0 | if (state -> minfo[endpoint] . valid && get_first_marker_offset(state, tcpinfo, endpoint) == 0) { |
1001 | 0 | current_offset += MPA_MARKER_LEN; |
1002 | 0 | } |
1003 | |
|
1004 | 0 | if (state -> minfo[endpoint] . valid) { |
1005 | 0 | num_of_m = number_of_markers(state, tcpinfo, endpoint); |
1006 | 0 | } |
1007 | 0 | } |
1008 | |
|
1009 | 0 | if (num_of_m > 0) { |
1010 | 0 | pdu_length += num_of_m * MPA_MARKER_LEN; |
1011 | 0 | } |
1012 | 0 | ULPDU_Length = tvb_get_ntohs(tvb, current_offset); |
1013 | 0 | pad_length = fpdu_pad_length(ULPDU_Length); |
1014 | |
|
1015 | 0 | pdu_length += MPA_ULPDU_LENGTH_LEN; |
1016 | 0 | pdu_length += ULPDU_Length; |
1017 | 0 | pdu_length += pad_length; |
1018 | 0 | pdu_length += MPA_CRC_LEN; |
1019 | |
|
1020 | 0 | return pdu_length; |
1021 | 0 | } |
1022 | | |
1023 | | /* |
1024 | | * MPA Request and Reply Frame Format... |
1025 | | */ |
1026 | | |
1027 | 0 | if (remaining < MPA_REQ_REP_FRAME_HEADER_LEN) { |
1028 | | /* |
1029 | | * We need more data. |
1030 | | */ |
1031 | 0 | return 0; |
1032 | 0 | } |
1033 | | |
1034 | 0 | offset += MPA_REQ_REP_FRAME_HEADER_LEN; |
1035 | 0 | offset -= MPA_REQ_REP_PDLENGTH_LEN; |
1036 | |
|
1037 | 0 | PD_Length = tvb_get_ntohs(tvb, offset); |
1038 | |
|
1039 | 0 | pdu_length += MPA_REQ_REP_FRAME_HEADER_LEN; |
1040 | 0 | pdu_length += PD_Length; |
1041 | |
|
1042 | 0 | return pdu_length; |
1043 | 0 | } |
1044 | | |
1045 | | static int |
1046 | | dissect_iwarp_mpa_pdu(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void *data) |
1047 | 0 | { |
1048 | 0 | bool ok; |
1049 | 0 | unsigned len; |
1050 | |
|
1051 | 0 | len = iwrap_mpa_pdu_length(pinfo, tvb, 0, data); |
1052 | 0 | ok = dissect_iwarp_mpa(tvb, pinfo, tree, data); |
1053 | 0 | if (!ok) { |
1054 | 0 | return -1; |
1055 | 0 | } |
1056 | | |
1057 | 0 | return len; |
1058 | 0 | } |
1059 | | |
1060 | | static int |
1061 | | dissect_iwarp_mpa_decode(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void *data) |
1062 | 0 | { |
1063 | 0 | conversation_t *conversation = NULL; |
1064 | 0 | struct tcpinfo *tcpinfo = NULL; |
1065 | 0 | mpa_state_t *state = NULL; |
1066 | 0 | bool is_mpa_req_rep = false; |
1067 | 0 | port_type saved_ptype = pinfo->ptype; |
1068 | |
|
1069 | 0 | if (data == NULL) |
1070 | 0 | return 0; |
1071 | 0 | tcpinfo = (struct tcpinfo *)data; |
1072 | | |
1073 | | /* Set the port type for this packet to be iWarp MPA */ |
1074 | 0 | pinfo->ptype = PT_IWARP_MPA; |
1075 | | |
1076 | | /* MPA REQUEST or MPA REPLY */ |
1077 | 0 | if (tvb_captured_length(tvb) >= MPA_REQ_REP_FRAME_HEADER_LEN) { |
1078 | 0 | if (is_mpa_req(tvb, pinfo)) { |
1079 | 0 | is_mpa_req_rep = true; |
1080 | 0 | } else if (is_mpa_rep(tvb, pinfo)) { |
1081 | 0 | is_mpa_req_rep = true; |
1082 | 0 | } |
1083 | 0 | } |
1084 | |
|
1085 | 0 | if (is_mpa_req_rep) { |
1086 | | /* |
1087 | | * The MPA REQ and REP are in the |
1088 | | * capture so just decode it. |
1089 | | */ |
1090 | 0 | goto decode; |
1091 | 0 | } |
1092 | | |
1093 | 0 | conversation = find_or_create_conversation(pinfo); |
1094 | 0 | state = get_mpa_state(conversation); |
1095 | 0 | if (state) { |
1096 | | /* |
1097 | | * The MPA REQ and REP are in the |
1098 | | * capture or we were below before... |
1099 | | */ |
1100 | 0 | goto decode; |
1101 | 0 | } |
1102 | | |
1103 | | /* |
1104 | | * The start of the iwarp tcp connection |
1105 | | * is not captured, so we have to fake |
1106 | | * the state for a connection without |
1107 | | * the marker bits negotiated |
1108 | | */ |
1109 | 0 | state = init_mpa_state(); |
1110 | 0 | state->minfo[MPA_INITIATOR].port = pinfo->srcport; |
1111 | 0 | state->minfo[MPA_RESPONDER].port = pinfo->destport; |
1112 | 0 | state->full_operation = true; |
1113 | |
|
1114 | 0 | conversation_add_proto_data(conversation, proto_iwarp_mpa, state); |
1115 | |
|
1116 | 0 | decode: |
1117 | 0 | tcp_dissect_pdus(tvb, pinfo, tree, |
1118 | 0 | true, /* proto_desegment*/ |
1119 | 0 | MPA_SMALLEST_FPDU_LEN, |
1120 | 0 | iwrap_mpa_pdu_length, |
1121 | 0 | dissect_iwarp_mpa_pdu, |
1122 | 0 | tcpinfo); |
1123 | 0 | pinfo->ptype = saved_ptype; |
1124 | 0 | return tvb_captured_length(tvb); |
1125 | 0 | } |
1126 | | |
1127 | | static bool |
1128 | | dissect_iwarp_mpa_heur(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void *data) |
1129 | 1.71k | { |
1130 | 1.71k | bool is_mpa_pdu = false; |
1131 | | |
1132 | 1.71k | if (data == NULL) |
1133 | 0 | return false; |
1134 | | |
1135 | | /* MPA REQUEST or MPA REPLY */ |
1136 | 1.71k | if (tvb_captured_length(tvb) >= MPA_REQ_REP_FRAME_HEADER_LEN) { |
1137 | 1.17k | if (is_mpa_req(tvb, pinfo)) { |
1138 | 0 | is_mpa_pdu = true; |
1139 | 1.17k | } else if (is_mpa_rep(tvb, pinfo)) { |
1140 | 0 | is_mpa_pdu = true; |
1141 | 0 | } |
1142 | 1.17k | } |
1143 | 1.71k | if (tvb_captured_length(tvb) >= MPA_SMALLEST_FPDU_LEN && is_mpa_fpdu(pinfo)) { |
1144 | 0 | is_mpa_pdu = true; |
1145 | 0 | } |
1146 | | |
1147 | 1.71k | if (!is_mpa_pdu) { |
1148 | 1.71k | return false; |
1149 | 1.71k | } |
1150 | | |
1151 | 0 | return dissect_iwarp_mpa_decode(tvb, pinfo, tree, data) > 0; |
1152 | 1.71k | } |
1153 | | |
1154 | | /* registers this protocol with Wireshark */ |
1155 | | void proto_register_mpa(void) |
1156 | 14 | { |
1157 | | /* setup list of header fields */ |
1158 | 14 | static hf_register_info hf[] = { |
1159 | 14 | { &hf_mpa_req, { |
1160 | 14 | "Request frame header", "iwarp_mpa.req", |
1161 | 14 | FT_NONE, BASE_NONE, NULL, 0x0, |
1162 | 14 | NULL, HFILL } }, |
1163 | 14 | { &hf_mpa_rep, { |
1164 | 14 | "Reply frame header", "iwarp_mpa.rep", |
1165 | 14 | FT_NONE, BASE_NONE, NULL, 0x0, |
1166 | 14 | NULL, HFILL } }, |
1167 | 14 | { &hf_mpa_fpdu, { |
1168 | 14 | "FPDU", "iwarp_mpa.fpdu", |
1169 | 14 | FT_NONE, BASE_NONE, NULL, 0x0, |
1170 | 14 | NULL, HFILL } }, |
1171 | 14 | { &hf_mpa_marker, { |
1172 | 14 | "Markers", "iwarp_mpa.markers", |
1173 | 14 | FT_NONE, BASE_NONE, NULL, 0x0, |
1174 | 14 | NULL, HFILL } }, |
1175 | 14 | { &hf_mpa_key_req, { |
1176 | 14 | "ID Req frame", "iwarp_mpa.key.req", |
1177 | 14 | FT_BYTES, BASE_NONE, NULL, 0x0, |
1178 | 14 | NULL, HFILL } }, |
1179 | 14 | { &hf_mpa_key_rep, { |
1180 | 14 | "ID Rep frame", "iwarp_mpa.key.rep", |
1181 | 14 | FT_BYTES, BASE_NONE, NULL, 0x0, |
1182 | 14 | NULL, HFILL } }, |
1183 | 14 | { &hf_mpa_flag_m, { |
1184 | 14 | "Marker flag", "iwarp_mpa.marker_flag", |
1185 | 14 | FT_BOOLEAN, 8, NULL, MPA_MARKER_FLAG, |
1186 | 14 | NULL, HFILL } }, |
1187 | 14 | { &hf_mpa_flag_c, { |
1188 | 14 | "CRC flag", "iwarp_mpa.crc_flag", |
1189 | 14 | FT_BOOLEAN, 8, NULL, MPA_CRC_FLAG, |
1190 | 14 | NULL, HFILL } }, |
1191 | 14 | { &hf_mpa_flag_r, { |
1192 | 14 | "Connection rejected flag", |
1193 | 14 | "iwarp_mpa.rej_flag", FT_BOOLEAN, 8, NULL, MPA_REJECT_FLAG, |
1194 | 14 | NULL, HFILL } }, |
1195 | 14 | { &hf_mpa_flag_s, { |
1196 | 14 | "Enhanced RDMA Connection", |
1197 | 14 | "iwarp_mpa.enhanced_flag", FT_BOOLEAN, 8, NULL, MPA_ENHANCED_RDMA, |
1198 | 14 | NULL, HFILL } }, |
1199 | 14 | { &hf_mpa_flag_res, { |
1200 | 14 | "Reserved", "iwarp_mpa.res", |
1201 | 14 | FT_UINT8, BASE_HEX, NULL, MPA_RESERVED_FLAG, |
1202 | 14 | NULL, HFILL } }, |
1203 | 14 | { &hf_mpa_rev, { |
1204 | 14 | "Revision", "iwarp_mpa.rev", |
1205 | 14 | FT_UINT8, BASE_DEC, NULL, 0x0, |
1206 | 14 | NULL, HFILL } }, |
1207 | 14 | { &hf_mpa_enhanced, { |
1208 | 14 | "Enhanced Negotiation", "iwarp_mpa.enhanced", |
1209 | 14 | FT_NONE, BASE_NONE, NULL, 0x0, |
1210 | 14 | NULL, HFILL } }, |
1211 | 14 | { &hf_mpa_enhanced_a, { |
1212 | 14 | "Control Flag A", |
1213 | 14 | "iwarp_mpa.enhanced.a", FT_BOOLEAN, 8, |
1214 | 14 | TFS(&tfs_mpa_enhanced_a), MPA_ENHANCED_CONTROL_A, |
1215 | 14 | NULL, HFILL } }, |
1216 | 14 | { &hf_mpa_enhanced_b, { |
1217 | 14 | "Control Flag B", |
1218 | 14 | "iwarp_mpa.enhanced.b", FT_BOOLEAN, 8, |
1219 | 14 | TFS(&tfs_mpa_enhanced_b), MPA_ENHANCED_CONTROL_B, |
1220 | 14 | NULL, HFILL } }, |
1221 | 14 | { &hf_mpa_enhanced_ird, { |
1222 | 14 | "Inbound RDMA Read Queue Depth", "iwarp_mpa.enhanced.ird", |
1223 | 14 | FT_UINT16, BASE_DEC, NULL, MPA_ENHANCED_IRD_MASK, |
1224 | 14 | NULL, HFILL } }, |
1225 | 14 | { &hf_mpa_enhanced_c, { |
1226 | 14 | "Control Flag C", |
1227 | 14 | "iwarp_mpa.enhanced.c", FT_BOOLEAN, 8, |
1228 | 14 | TFS(&tfs_mpa_enhanced_c), MPA_ENHANCED_CONTROL_C, |
1229 | 14 | NULL, HFILL } }, |
1230 | 14 | { &hf_mpa_enhanced_d, { |
1231 | 14 | "Control Flag D", |
1232 | 14 | "iwarp_mpa.enhanced.D", FT_BOOLEAN, 8, |
1233 | 14 | TFS(&tfs_mpa_enhanced_d), MPA_ENHANCED_CONTROL_D, |
1234 | 14 | NULL, HFILL } }, |
1235 | 14 | { &hf_mpa_enhanced_ord, { |
1236 | 14 | "Outbound RDMA Read Queue Depth", "iwarp_mpa.enhanced.ord", |
1237 | 14 | FT_UINT16, BASE_DEC, NULL, MPA_ENHANCED_ORD_MASK, |
1238 | 14 | NULL, HFILL } }, |
1239 | 14 | { &hf_mpa_legacy, { |
1240 | 14 | "Legacy IRD/ORD Negotiation", "iwarp_mpa.legacy", |
1241 | 14 | FT_NONE, BASE_NONE, NULL, 0x0, |
1242 | 14 | NULL, HFILL } }, |
1243 | 14 | { &hf_mpa_legacy_broken, { |
1244 | 14 | "Broken Little-Endian Legacy IRD/ORD Negotiation", "iwarp_mpa.broken_le_legacy", |
1245 | 14 | FT_NONE, BASE_NONE, NULL, 0x0, |
1246 | 14 | NULL, HFILL } }, |
1247 | 14 | { &hf_mpa_legacy_ird, { |
1248 | 14 | "Legacy Inbound RDMA Read Queue Depth", "iwarp_mpa.legacy.ird", |
1249 | 14 | FT_UINT32, BASE_DEC, NULL, 0, |
1250 | 14 | NULL, HFILL } }, |
1251 | 14 | { &hf_mpa_legacy_ord, { |
1252 | 14 | "Legacy Outbound RDMA Read Queue Depth", "iwarp_mpa.legacy.ord", |
1253 | 14 | FT_UINT32, BASE_DEC, NULL, 0, |
1254 | 14 | NULL, HFILL } }, |
1255 | 14 | { &hf_mpa_pd_length, { |
1256 | 14 | "Private data length", "iwarp_mpa.pdlength", |
1257 | 14 | FT_UINT16, BASE_DEC|BASE_UNIT_STRING, UNS(&units_byte_bytes), 0x0, |
1258 | 14 | NULL, HFILL } }, |
1259 | 14 | { &hf_mpa_private_data, { |
1260 | 14 | "Private data", "iwarp_mpa.privatedata", |
1261 | 14 | FT_BYTES, BASE_NONE, NULL, 0x0, |
1262 | 14 | NULL, HFILL } }, |
1263 | 14 | { &hf_mpa_ulpdu_length, { |
1264 | 14 | "ULPDU length", "iwarp_mpa.ulpdulength", |
1265 | 14 | FT_UINT16, BASE_DEC|BASE_UNIT_STRING, UNS(&units_byte_bytes), 0x0, |
1266 | 14 | NULL, HFILL } }, |
1267 | 14 | { &hf_mpa_pad, { |
1268 | 14 | "Padding", "iwarp_mpa.pad", |
1269 | 14 | FT_BYTES, BASE_NONE, NULL, 0x0, |
1270 | 14 | NULL, HFILL } }, |
1271 | 14 | { &hf_mpa_crc, { |
1272 | 14 | "CRC", "iwarp_mpa.crc", |
1273 | 14 | FT_UINT32, BASE_HEX, NULL, 0x0, |
1274 | 14 | NULL, HFILL } }, |
1275 | 14 | { &hf_mpa_crc_check, { |
1276 | 14 | "CRC check", "iwarp_mpa.crc_check", |
1277 | 14 | FT_UINT32, BASE_HEX, NULL, 0x0, |
1278 | 14 | NULL, HFILL } }, |
1279 | 14 | { &hf_mpa_marker_res, { |
1280 | 14 | "Reserved", "iwarp_mpa.marker_res", |
1281 | 14 | FT_UINT16, BASE_HEX, NULL, 0x0, |
1282 | 14 | "Marker: Reserved", HFILL } }, |
1283 | 14 | { &hf_mpa_marker_fpduptr, { |
1284 | 14 | "FPDU back pointer", "iwarp_mpa.marker_fpduptr", |
1285 | 14 | FT_UINT16, BASE_DEC|BASE_UNIT_STRING, UNS(&units_byte_bytes), 0x0, |
1286 | 14 | "Marker: FPDU Pointer", HFILL } } |
1287 | 14 | }; |
1288 | | |
1289 | | /* setup protocol subtree array */ |
1290 | 14 | static int *ett[] = { |
1291 | 14 | &ett_mpa, |
1292 | 14 | &ett_mpa_req, |
1293 | 14 | &ett_mpa_rep, |
1294 | 14 | &ett_mpa_fpdu, |
1295 | 14 | &ett_mpa_marker, |
1296 | 14 | &ett_mpa_enhanced, |
1297 | 14 | &ett_mpa_legacy, |
1298 | 14 | }; |
1299 | | |
1300 | 14 | static ei_register_info ei[] = { |
1301 | 14 | { &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 }}, |
1302 | 14 | { &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 }}, |
1303 | 14 | { &ei_mpa_reject_bit_responder, { "iwarp_mpa.reject_bit_responder", PI_RESPONSE_CODE, PI_NOTE, "Reject bit set by Responder", EXPFILL }}, |
1304 | 14 | { &ei_mpa_bad_length, { "iwarp_mpa.bad_length", PI_MALFORMED, PI_ERROR, "Bad length", EXPFILL }}, |
1305 | 14 | }; |
1306 | | |
1307 | 14 | expert_module_t* expert_iwarp_mpa; |
1308 | | |
1309 | | /* register the protocol name and description */ |
1310 | 14 | proto_iwarp_mpa = proto_register_protocol("iWARP Marker Protocol data unit Aligned framing", "IWARP_MPA", "iwarp_mpa"); |
1311 | | |
1312 | 14 | iwarp_mpa_handle = register_dissector("iwarp_mpa", dissect_iwarp_mpa_decode, proto_iwarp_mpa); |
1313 | | |
1314 | | /* required function calls to register the header fields and subtrees */ |
1315 | 14 | proto_register_field_array(proto_iwarp_mpa, hf, array_length(hf)); |
1316 | 14 | proto_register_subtree_array(ett, array_length(ett)); |
1317 | 14 | expert_iwarp_mpa = expert_register_protocol(proto_iwarp_mpa); |
1318 | 14 | expert_register_field_array(expert_iwarp_mpa, ei, array_length(ei)); |
1319 | 14 | } |
1320 | | |
1321 | | void |
1322 | | proto_reg_handoff_mpa(void) |
1323 | 14 | { |
1324 | | /* |
1325 | | * MPA does not use any specific TCP port so, when not on a specific |
1326 | | * port, try this dissector whenever there is TCP traffic. |
1327 | | */ |
1328 | 14 | heur_dissector_add("tcp", dissect_iwarp_mpa_heur, "IWARP_MPA over TCP", "iwarp_mpa_tcp", proto_iwarp_mpa, HEURISTIC_ENABLE); |
1329 | 14 | dissector_add_for_decode_as("tcp.port", iwarp_mpa_handle); |
1330 | 14 | ddp_rdmap_handle = find_dissector_add_dependency("iwarp_ddp_rdmap", proto_iwarp_mpa); |
1331 | 14 | } |
1332 | | |
1333 | | /* |
1334 | | * Editor modelines - https://www.wireshark.org/tools/modelines.html |
1335 | | * |
1336 | | * Local variables: |
1337 | | * c-basic-offset: 8 |
1338 | | * tab-width: 8 |
1339 | | * indent-tabs-mode: t |
1340 | | * End: |
1341 | | * |
1342 | | * vi: set shiftwidth=8 tabstop=8 noexpandtab: |
1343 | | * :indentSize=8:tabSize=8:noTabs=false: |
1344 | | */ |