/src/wireshark/epan/dissectors/packet-iser.c
Line | Count | Source |
1 | | /* packet-iser.c |
2 | | * Routines for iSCSI RDMA Extensions dissection |
3 | | * Copyright 2014, Mellanox Technologies Ltd. |
4 | | * Code by Yan Burman. |
5 | | * |
6 | | * Wireshark - Network traffic analyzer |
7 | | * By Gerald Combs <gerald@wireshark.org> |
8 | | * Copyright 1998 Gerald Combs |
9 | | * |
10 | | * SPDX-License-Identifier: GPL-2.0-or-later |
11 | | */ |
12 | | |
13 | | #include "config.h" |
14 | | |
15 | | #include <stdlib.h> |
16 | | |
17 | | #include <epan/packet.h> |
18 | | #include <epan/prefs.h> |
19 | | #include <epan/conversation.h> |
20 | | #include <epan/addr_resolv.h> |
21 | | |
22 | | #include "packet-infiniband.h" |
23 | | |
24 | 14 | #define ISER_WSV 0x08 |
25 | 14 | #define ISER_RSV 0x04 |
26 | 14 | #define ISER_REJ 0x01 |
27 | | |
28 | 0 | #define ISER_ISCSI_CTRL 0x10 |
29 | 0 | #define ISER_HELLO 0x20 |
30 | 0 | #define ISER_HELLORPLY 0x30 |
31 | | |
32 | 14 | #define ISER_OPCODE_MASK 0xf0 |
33 | 0 | #define ISER_SPECIFIC_MASK 0x0f |
34 | | |
35 | 337 | #define ISER_HDR_SZ (1 + 3 + 4 + 8 + 4 + 8) |
36 | 337 | #define ISCSI_HDR_SZ 48 |
37 | | |
38 | 337 | #define ISER_ISCSI_HDR_SZ (ISER_HDR_SZ + ISCSI_HDR_SZ) |
39 | | |
40 | 0 | #define SID_ULP_MASK 0x00000000FF000000 |
41 | 0 | #define SID_PROTO_MASK 0x0000000000FF0000 |
42 | 0 | #define SID_PORT_MASK 0x000000000000FFFF |
43 | | |
44 | 0 | #define SID_ULP 0x01 |
45 | 0 | #define SID_PROTO_TCP 0x06 |
46 | 14 | #define TCP_PORT_ISER_RANGE "3260" |
47 | | |
48 | 0 | #define SID_MASK (SID_ULP_MASK | SID_PROTO_MASK) |
49 | 0 | #define SID_ULP_TCP ((SID_ULP << 3 * 8) | (SID_PROTO_TCP << 2 * 8)) |
50 | | |
51 | | void proto_reg_handoff_iser(void); |
52 | | void proto_register_iser(void); |
53 | | |
54 | | static dissector_handle_t iser_handle; |
55 | | |
56 | | static int proto_iser; |
57 | | static dissector_handle_t iscsi_handler; |
58 | | |
59 | | static int proto_ib; |
60 | | |
61 | | /* iSER Header */ |
62 | | static int hf_iser_flags; |
63 | | static int hf_iser_opcode_f; |
64 | | static int hf_iser_RSV_f; |
65 | | static int hf_iser_WSV_f; |
66 | | static int hf_iser_REJ_f; |
67 | | static int hf_iser_write_stag; |
68 | | static int hf_iser_write_va; |
69 | | static int hf_iser_read_stag; |
70 | | static int hf_iser_read_va; |
71 | | static int hf_iser_ird; |
72 | | static int hf_iser_ord; |
73 | | |
74 | | /* Initialize the subtree pointers */ |
75 | | static int ett_iser; |
76 | | static int ett_iser_flags; |
77 | | |
78 | | /* global preferences */ |
79 | | static range_t *gPORT_RANGE; |
80 | | |
81 | | static const value_string iser_flags_opcode[] = { |
82 | | { ISER_ISCSI_CTRL >> 4, "iSCSI Control-Type PDU"}, |
83 | | { ISER_HELLO >> 4, "Hello Message"}, |
84 | | { ISER_HELLORPLY >> 4, "HelloReply Message"}, |
85 | | {0, NULL}, |
86 | | }; |
87 | | |
88 | | static int * const flags_fields[] = { |
89 | | &hf_iser_opcode_f, |
90 | | &hf_iser_WSV_f, |
91 | | &hf_iser_RSV_f, |
92 | | NULL |
93 | | }; |
94 | | static int * const hello_flags_fields[] = { |
95 | | &hf_iser_opcode_f, |
96 | | NULL |
97 | | }; |
98 | | static int * const hellorply_flags_fields[] = { |
99 | | &hf_iser_opcode_f, |
100 | | &hf_iser_REJ_f, |
101 | | NULL |
102 | | }; |
103 | | |
104 | | static int dissect_packet(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void* data _U_) |
105 | 0 | { |
106 | 0 | tvbuff_t *next_tvb; |
107 | | /* Set up structures needed to add the protocol subtree and manage it */ |
108 | 0 | proto_item *ti; |
109 | 0 | proto_tree *iser_tree; |
110 | 0 | unsigned offset = 0; |
111 | 0 | uint8_t flags, vers, opcode; |
112 | |
|
113 | 0 | if (tvb_reported_length(tvb) < ISER_ISCSI_HDR_SZ) |
114 | 0 | return 0; |
115 | | |
116 | 0 | flags = tvb_get_uint8(tvb, 0); |
117 | 0 | opcode = flags & ISER_OPCODE_MASK; |
118 | | |
119 | | /* Check if the opcode is valid */ |
120 | 0 | switch (opcode) { |
121 | 0 | case ISER_ISCSI_CTRL: |
122 | 0 | switch (flags & ISER_SPECIFIC_MASK) { |
123 | 0 | case 0: |
124 | 0 | case ISER_WSV: |
125 | 0 | case ISER_RSV: |
126 | 0 | case ISER_RSV|ISER_WSV: |
127 | 0 | break; |
128 | | |
129 | 0 | default: |
130 | 0 | return 0; |
131 | 0 | } |
132 | 0 | break; |
133 | | |
134 | 0 | case ISER_HELLO: |
135 | 0 | case ISER_HELLORPLY: |
136 | 0 | vers = tvb_get_uint8(tvb, 1); |
137 | 0 | if ((vers & 0xf) != 10) |
138 | 0 | return 0; |
139 | 0 | if (((vers >> 4) & 0x0f) != 10) |
140 | 0 | return 0; |
141 | 0 | break; |
142 | | |
143 | 0 | default: |
144 | 0 | return 0; |
145 | 0 | } |
146 | | |
147 | 0 | col_set_str(pinfo->cinfo, COL_PROTOCOL, "iSER"); |
148 | | /* Clear out stuff in the info column */ |
149 | 0 | col_clear(pinfo->cinfo, COL_INFO); |
150 | | |
151 | | /* Set info only for hello, since for iscsi, the iscsi dissector will */ |
152 | 0 | switch (opcode) { |
153 | 0 | case ISER_HELLO: |
154 | 0 | col_set_str(pinfo->cinfo, COL_INFO, "iSER Hello"); |
155 | 0 | break; |
156 | | |
157 | 0 | case ISER_HELLORPLY: |
158 | 0 | col_set_str(pinfo->cinfo, COL_INFO, "iSER HelloRply"); |
159 | 0 | break; |
160 | 0 | } |
161 | | |
162 | 0 | if (tree) { |
163 | | /* create display subtree for the protocol */ |
164 | 0 | ti = proto_tree_add_item(tree, proto_iser, tvb, 0, ISER_HDR_SZ, ENC_NA); |
165 | |
|
166 | 0 | iser_tree = proto_item_add_subtree(ti, ett_iser); |
167 | |
|
168 | 0 | switch (opcode) { |
169 | 0 | case ISER_ISCSI_CTRL: |
170 | 0 | proto_tree_add_bitmask(iser_tree, tvb, offset, hf_iser_flags, |
171 | 0 | ett_iser_flags, flags_fields, ENC_LITTLE_ENDIAN); |
172 | 0 | offset += 4; |
173 | 0 | proto_tree_add_item(iser_tree, hf_iser_write_stag, tvb, |
174 | 0 | offset, 4, ENC_BIG_ENDIAN); |
175 | 0 | offset += 4; |
176 | 0 | proto_tree_add_item(iser_tree, hf_iser_write_va, tvb, |
177 | 0 | offset, 8, ENC_BIG_ENDIAN); |
178 | 0 | offset += 8; |
179 | 0 | proto_tree_add_item(iser_tree, hf_iser_read_stag, tvb, |
180 | 0 | offset, 4, ENC_BIG_ENDIAN); |
181 | 0 | offset += 4; |
182 | 0 | proto_tree_add_item(iser_tree, hf_iser_read_va, tvb, |
183 | 0 | offset, 8, ENC_BIG_ENDIAN); |
184 | 0 | break; |
185 | | |
186 | 0 | case ISER_HELLO: |
187 | 0 | proto_tree_add_bitmask(iser_tree, tvb, offset, hf_iser_flags, |
188 | 0 | ett_iser_flags, hello_flags_fields, ENC_LITTLE_ENDIAN); |
189 | 0 | offset += 2; |
190 | 0 | proto_tree_add_item(iser_tree, hf_iser_ird, tvb, |
191 | 0 | offset, 2, ENC_BIG_ENDIAN); |
192 | 0 | break; |
193 | | |
194 | 0 | case ISER_HELLORPLY: |
195 | 0 | proto_tree_add_bitmask(iser_tree, tvb, offset, hf_iser_flags, |
196 | 0 | ett_iser_flags, hellorply_flags_fields, ENC_LITTLE_ENDIAN); |
197 | 0 | offset += 2; |
198 | 0 | proto_tree_add_item(iser_tree, hf_iser_ord, tvb, |
199 | 0 | offset, 2, ENC_BIG_ENDIAN); |
200 | 0 | break; |
201 | 0 | } |
202 | 0 | } |
203 | | |
204 | 0 | if (opcode == ISER_ISCSI_CTRL) { |
205 | 0 | next_tvb = tvb_new_subset_remaining(tvb, ISER_HDR_SZ); |
206 | 0 | call_dissector(iscsi_handler, next_tvb, pinfo, tree); |
207 | 0 | } |
208 | |
|
209 | 0 | return ISER_HDR_SZ; |
210 | 0 | } |
211 | | |
212 | | static bool |
213 | | dissect_iser(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, |
214 | | void *data _U_) |
215 | 337 | { |
216 | 337 | conversation_t *conv; |
217 | 337 | conversation_infiniband_data *convo_data = NULL; |
218 | | |
219 | 337 | if (tvb_reported_length(tvb) < ISER_ISCSI_HDR_SZ) |
220 | 281 | return false; |
221 | | |
222 | | /* first try to find a conversation between the two current hosts. in most cases this |
223 | | will not work since we do not have the source QP. this WILL succeed when we're still |
224 | | in the process of CM negotiations */ |
225 | 56 | conv = find_conversation(pinfo->num, &pinfo->src, &pinfo->dst, |
226 | 56 | CONVERSATION_IBQP, pinfo->srcport, pinfo->destport, 0); |
227 | | |
228 | 56 | if (!conv) { |
229 | | /* if not, try to find an established RC channel. recall Infiniband conversations are |
230 | | registered with one side of the channel. since the packet is only guaranteed to |
231 | | contain the qpn of the destination, we'll use this */ |
232 | 0 | conv = find_conversation(pinfo->num, &pinfo->dst, &pinfo->dst, |
233 | 0 | CONVERSATION_IBQP, pinfo->destport, pinfo->destport, NO_ADDR_B|NO_PORT_B); |
234 | |
|
235 | 0 | if (!conv) |
236 | 0 | return false; /* nothing to do with no conversation context */ |
237 | 0 | } |
238 | | |
239 | 56 | convo_data = (conversation_infiniband_data *)conversation_get_proto_data(conv, proto_ib); |
240 | | |
241 | 56 | if (!convo_data) |
242 | 56 | return false; |
243 | | |
244 | 0 | if ((convo_data->service_id & SID_MASK) != SID_ULP_TCP) |
245 | 0 | return false; /* the service id doesn't match that of TCP ULP - nothing for us to do here */ |
246 | | |
247 | 0 | if (!(value_is_in_range(gPORT_RANGE, (uint32_t)(convo_data->service_id & SID_PORT_MASK)))) |
248 | 0 | return false; /* the port doesn't match that of iSER - nothing for us to do here */ |
249 | | |
250 | 0 | dissect_packet(tvb, pinfo, tree, data); |
251 | 0 | return true; |
252 | 0 | } |
253 | | |
254 | | void |
255 | | proto_register_iser(void) |
256 | 14 | { |
257 | 14 | module_t *iser_module; |
258 | 14 | static hf_register_info hf[] = { |
259 | 14 | { &hf_iser_flags, |
260 | 14 | { "Flags", "iser.flags", |
261 | 14 | FT_UINT8, BASE_HEX, NULL, 0x0, NULL, HFILL} |
262 | 14 | }, |
263 | 14 | { &hf_iser_opcode_f, |
264 | 14 | { "Opcode", "iser.flags.opcode", |
265 | 14 | FT_UINT8, BASE_HEX, VALS(iser_flags_opcode), |
266 | 14 | ISER_OPCODE_MASK, NULL, HFILL} |
267 | 14 | }, |
268 | 14 | { &hf_iser_RSV_f, |
269 | 14 | { "RSV", "iser.flags.rsv", |
270 | 14 | FT_BOOLEAN, 8, NULL, ISER_RSV, "Read STag Valid", HFILL} |
271 | 14 | }, |
272 | 14 | { &hf_iser_WSV_f, |
273 | 14 | { "WSV", "iser.flags.wsv", |
274 | 14 | FT_BOOLEAN, 8, NULL, ISER_WSV, "Write STag Valid", HFILL} |
275 | 14 | }, |
276 | 14 | { &hf_iser_REJ_f, |
277 | 14 | { "REJ", "iser.flags.rej", |
278 | 14 | FT_BOOLEAN, 8, NULL, ISER_REJ, "Target reject connection", HFILL} |
279 | 14 | }, |
280 | 14 | { &hf_iser_write_stag, |
281 | 14 | { "Write STag", "iser.write_stag", |
282 | 14 | FT_UINT32, BASE_HEX, NULL, 0x0, NULL, HFILL} |
283 | 14 | }, |
284 | 14 | { &hf_iser_write_va, |
285 | 14 | { "Write Base Offset", "iser.write_base_offset", |
286 | 14 | FT_UINT64, BASE_HEX, NULL, 0x0, NULL, HFILL} |
287 | 14 | }, |
288 | 14 | { &hf_iser_read_stag, |
289 | 14 | { "Read STag", "iser.read_stag", |
290 | 14 | FT_UINT32, BASE_HEX, NULL, 0x0, NULL, HFILL} |
291 | 14 | }, |
292 | 14 | { &hf_iser_read_va, |
293 | 14 | { "Read Base Offset", "iser.read_base_offset", |
294 | 14 | FT_UINT64, BASE_HEX, NULL, 0x0, NULL, HFILL} |
295 | 14 | }, |
296 | 14 | { &hf_iser_ird, |
297 | 14 | { "iSER-IRD", "iser.ird", |
298 | 14 | FT_UINT16, BASE_HEX, NULL, 0x0, NULL, HFILL} |
299 | 14 | }, |
300 | 14 | { &hf_iser_ord, |
301 | 14 | { "iSER-ORD", "iser.ord", |
302 | 14 | FT_UINT16, BASE_HEX, NULL, 0x0, NULL, HFILL} |
303 | 14 | } |
304 | 14 | }; |
305 | | |
306 | 14 | static int *ett[] = { |
307 | 14 | &ett_iser, |
308 | 14 | &ett_iser_flags |
309 | 14 | }; |
310 | | |
311 | 14 | proto_iser = proto_register_protocol ( |
312 | 14 | "iSCSI Extensions for RDMA", /* name */ |
313 | 14 | "iSER", /* short name */ |
314 | 14 | "iser" /* abbrev */ |
315 | 14 | ); |
316 | | |
317 | 14 | proto_register_field_array(proto_iser, hf, array_length(hf)); |
318 | 14 | proto_register_subtree_array(ett, array_length(ett)); |
319 | | |
320 | | /* Register preferences */ |
321 | 14 | iser_module = prefs_register_protocol(proto_iser, proto_reg_handoff_iser); |
322 | | |
323 | 14 | prefs_register_static_text_preference(iser_module, "use_decode_as", |
324 | 14 | "Heuristic matching preferences removed. Use Infiniband protocol preferences or Decode As.", |
325 | 14 | "Simple heuristics can still be enable (may generate false positives) through Infiniband protocol preferences." |
326 | 14 | "To force iSER dissection use Decode As"); |
327 | | |
328 | 14 | prefs_register_obsolete_preference(iser_module, "manual_en"); |
329 | | |
330 | 14 | prefs_register_obsolete_preference(iser_module, "addr_a"); |
331 | 14 | prefs_register_obsolete_preference(iser_module, "addr_a_type"); |
332 | 14 | prefs_register_obsolete_preference(iser_module, "addr_a_id"); |
333 | 14 | prefs_register_obsolete_preference(iser_module, "addr_a_qp"); |
334 | | |
335 | 14 | prefs_register_obsolete_preference(iser_module, "addr_b"); |
336 | 14 | prefs_register_obsolete_preference(iser_module, "addr_b_type"); |
337 | 14 | prefs_register_obsolete_preference(iser_module, "addr_b_id"); |
338 | 14 | prefs_register_obsolete_preference(iser_module, "addr_b_qp"); |
339 | | |
340 | 14 | range_convert_str(wmem_epan_scope(), &gPORT_RANGE, TCP_PORT_ISER_RANGE, MAX_TCP_PORT); |
341 | 14 | prefs_register_range_preference(iser_module, |
342 | 14 | "target_ports", |
343 | 14 | "Target Ports Range", |
344 | 14 | "Range of iSER target ports" |
345 | 14 | "(default " TCP_PORT_ISER_RANGE ")", |
346 | 14 | &gPORT_RANGE, MAX_TCP_PORT); |
347 | | |
348 | 14 | iser_handle = register_dissector("iser", dissect_packet, proto_iser ); |
349 | 14 | } |
350 | | |
351 | | void |
352 | | proto_reg_handoff_iser(void) |
353 | 14 | { |
354 | 14 | heur_dissector_add("infiniband.payload", dissect_iser, "iSER Infiniband", "iser_infiniband", proto_iser, HEURISTIC_ENABLE); |
355 | 14 | heur_dissector_add("infiniband.mad.cm.private", dissect_iser, "iSER in PrivateData of CM packets", "iser_ib_private", proto_iser, HEURISTIC_ENABLE); |
356 | | |
357 | 14 | dissector_add_for_decode_as("infiniband", iser_handle); |
358 | | |
359 | 14 | iscsi_handler = find_dissector_add_dependency("iscsi", proto_iser); |
360 | 14 | proto_ib = proto_get_id_by_filter_name( "infiniband" ); |
361 | 14 | } |
362 | | |
363 | | /* |
364 | | * Editor modelines - https://www.wireshark.org/tools/modelines.html |
365 | | * |
366 | | * Local variables: |
367 | | * c-basic-offset: 4 |
368 | | * tab-width: 8 |
369 | | * indent-tabs-mode: nil |
370 | | * End: |
371 | | * |
372 | | * vi: set shiftwidth=4 tabstop=8 expandtab: |
373 | | * :indentSize=4:tabSize=8:noTabs=true: |
374 | | */ |