/src/wireshark/epan/dissectors/packet-nbd.c
Line | Count | Source (jump to first uncovered line) |
1 | | /* packet-nbd.c |
2 | | * Routines for Network Block Device (NBD) dissection. |
3 | | * |
4 | | * https://github.com/NetworkBlockDevice/nbd/blob/master/doc/proto.md |
5 | | * |
6 | | * Ronnie sahlberg 2006 |
7 | | * |
8 | | * Wireshark - Network traffic analyzer |
9 | | * By Gerald Combs <gerald@wireshark.org> |
10 | | * Copyright 1998 Gerald Combs |
11 | | * |
12 | | * SPDX-License-Identifier: GPL-2.0-or-later |
13 | | */ |
14 | | |
15 | | #include "config.h" |
16 | | |
17 | | #include <epan/packet.h> |
18 | | #include <epan/prefs.h> |
19 | | #include <epan/expert.h> |
20 | | #include <epan/unit_strings.h> |
21 | | #include <epan/tfs.h> |
22 | | #include <wsutil/array.h> |
23 | | #include "packet-tcp.h" |
24 | | #include "packet-tls-utils.h" |
25 | | |
26 | | void proto_register_nbd(void); |
27 | | void proto_reg_handoff_nbd(void); |
28 | | |
29 | | static int proto_nbd; |
30 | | static int hf_nbd_hnd_magic; |
31 | | static int hf_nbd_hnd_flags; |
32 | | static int hf_nbd_hnd_flags_fixed_new; |
33 | | static int hf_nbd_hnd_flags_no_zeroes; |
34 | | static int hf_nbd_hnd_opt; |
35 | | static int hf_nbd_hnd_reply; |
36 | | static int hf_nbd_cli_flags; |
37 | | static int hf_nbd_cli_flags_fixed_new; |
38 | | static int hf_nbd_cli_flags_no_zeroes; |
39 | | static int hf_nbd_magic; |
40 | | static int hf_nbd_cmd_flags; |
41 | | static int hf_nbd_cmd_flags_fua; |
42 | | static int hf_nbd_cmd_flags_no_hole; |
43 | | static int hf_nbd_cmd_flags_df; |
44 | | static int hf_nbd_cmd_flags_req_one; |
45 | | static int hf_nbd_cmd_flags_fast_zero; |
46 | | static int hf_nbd_cmd_flags_payload_len; |
47 | | static int hf_nbd_reply_flags; |
48 | | static int hf_nbd_reply_flags_done; |
49 | | static int hf_nbd_export_size; |
50 | | static int hf_nbd_trans_flags; |
51 | | static int hf_nbd_trans_flags_has_flags; |
52 | | static int hf_nbd_trans_flags_read_only; |
53 | | static int hf_nbd_trans_flags_flush; |
54 | | static int hf_nbd_trans_flags_fua; |
55 | | static int hf_nbd_trans_flags_rotational; |
56 | | static int hf_nbd_trans_flags_trim; |
57 | | static int hf_nbd_trans_flags_write_zeroes; |
58 | | static int hf_nbd_trans_flags_df; |
59 | | static int hf_nbd_trans_flags_multi_conn; |
60 | | static int hf_nbd_trans_flags_resize; |
61 | | static int hf_nbd_trans_flags_cache; |
62 | | static int hf_nbd_trans_flags_fast_zero; |
63 | | static int hf_nbd_trans_flags_block_status_payload; |
64 | | static int hf_nbd_reserved; |
65 | | static int hf_nbd_type; |
66 | | static int hf_nbd_reply_type; |
67 | | static int hf_nbd_error; |
68 | | static int hf_nbd_handle; |
69 | | static int hf_nbd_from; |
70 | | static int hf_nbd_len; |
71 | | static int hf_nbd_response_in; |
72 | | static int hf_nbd_response_to; |
73 | | static int hf_nbd_time; |
74 | | static int hf_nbd_export_name_len; |
75 | | static int hf_nbd_export_name; |
76 | | static int hf_nbd_info_num; |
77 | | static int hf_nbd_info; |
78 | | static int hf_nbd_query_num; |
79 | | static int hf_nbd_query; |
80 | | static int hf_nbd_export_description; |
81 | | static int hf_nbd_block_size_min; |
82 | | static int hf_nbd_block_size_prefer; |
83 | | static int hf_nbd_payload_size_max; |
84 | | static int hf_nbd_meta_context_id; |
85 | | static int hf_nbd_meta_context_name; |
86 | | static int hf_nbd_error_msg_len; |
87 | | static int hf_nbd_error_msg; |
88 | | static int hf_nbd_data; |
89 | | static int hf_nbd_hole_size; |
90 | | static int hf_nbd_status_flags; |
91 | | |
92 | | static int ett_nbd; |
93 | | static int ett_nbd_hnd_flags; |
94 | | static int ett_nbd_cli_flags; |
95 | | static int ett_nbd_cmd_flags; |
96 | | static int ett_nbd_reply_flags; |
97 | | static int ett_nbd_trans_flags; |
98 | | |
99 | | static expert_field ei_nbd_hnd_reply_error; |
100 | | static expert_field ei_nbd_unexpected_data; |
101 | | |
102 | | static dissector_handle_t nbd_handle; |
103 | | static dissector_handle_t tls_handle; |
104 | | |
105 | | static bool nbd_desegment = true; |
106 | | |
107 | | static void apply_nbd_prefs(void); |
108 | | |
109 | 14 | #define NBD_TCP_PORTS "10809" /* IANA-registered */ |
110 | | |
111 | | static range_t *nbd_port_range; |
112 | | |
113 | | typedef struct _nbd_transaction_t { |
114 | | uint32_t req_frame; |
115 | | uint32_t rep_frame; |
116 | | nstime_t req_time; |
117 | | uint32_t datalen; |
118 | | uint16_t type; |
119 | | } nbd_transaction_t; |
120 | | typedef struct _nbd_option_t { |
121 | | uint32_t req_frame; |
122 | | uint32_t rep_frame; |
123 | | nstime_t req_time; |
124 | | uint32_t opt; |
125 | | } nbd_option_t; |
126 | | typedef struct _nbd_conv_info_t { |
127 | | bool no_zeroes; |
128 | | wmem_tree_t *state; |
129 | | wmem_tree_t *opts; /* indexed by packet# (per spec, client MUST not send |
130 | | a new option until reply received for previous */ |
131 | | wmem_tree_t *unacked_pdus; /* indexed by handle, which wraps quite frequently */ |
132 | | wmem_tree_t *acked_pdus; /* indexed by packet# and handle */ |
133 | | } nbd_conv_info_t; |
134 | | |
135 | | typedef enum _nbd_state_e { |
136 | | STATE_UNK = 0, |
137 | | STATE_HND_INIT, |
138 | | STATE_HND_OPT, |
139 | | STATE_HND_DONE |
140 | | } nbd_state_e; |
141 | | |
142 | 3 | #define NBD_HND_INIT_MAGIC 0x4e42444d41474943 // "NBDMAGIC" |
143 | 5 | #define NBD_HND_OPT_MAGIC 0x49484156454F5054 // "IHAVEOPT" |
144 | 19 | #define NBD_HND_REPLY_MAGIC 0x03e889045565a9 |
145 | 14 | #define NBD_HND_OLD_MAGIC 0x00420281861253 |
146 | | |
147 | 0 | #define NBD_REQUEST_MAGIC 0x25609513 |
148 | 0 | #define NBD_RESPONSE_MAGIC 0x67446698 |
149 | 0 | #define NBD_STRUCTURED_REPLY_MAGIC 0x668e33ef |
150 | | |
151 | 6 | #define NBD_OPT_EXPORT_NAME 1 |
152 | | #define NBD_OPT_ABORT 2 |
153 | | #define NBD_OPT_LIST 3 |
154 | | #define NBD_OPT_PEEK_EXPORT 4 |
155 | 46 | #define NBD_OPT_STARTTLS 5 |
156 | 0 | #define NBD_OPT_INFO 6 |
157 | 0 | #define NBD_OPT_GO 7 |
158 | | #define NBD_OPT_STRUCTURED_REPLY 8 |
159 | 0 | #define NBD_OPT_LIST_META_CONTEXT 9 |
160 | 0 | #define NBD_OPT_SET_META_CONTEXT 10 |
161 | | #define NBD_OPT_EXTENDED_HEADERS 11 |
162 | | |
163 | | static const value_string nbd_opt_vals[] = { |
164 | | {NBD_OPT_EXPORT_NAME, "Export Name"}, |
165 | | {NBD_OPT_ABORT, "Abort"}, |
166 | | {NBD_OPT_LIST, "List"}, |
167 | | {NBD_OPT_PEEK_EXPORT, "Peek Export"}, // Withdrawn |
168 | | {NBD_OPT_STARTTLS, "STARTTLS"}, |
169 | | {NBD_OPT_INFO, "Info"}, |
170 | | {NBD_OPT_GO, "Go"}, |
171 | | {NBD_OPT_STRUCTURED_REPLY, "Structured Reply"}, |
172 | | {NBD_OPT_LIST_META_CONTEXT, "List Metadata Contexts"}, |
173 | | {NBD_OPT_SET_META_CONTEXT, "Set Metadata Contexts"}, |
174 | | {NBD_OPT_EXTENDED_HEADERS, "Extended Headers"}, |
175 | | {0, NULL} |
176 | | }; |
177 | | |
178 | 0 | #define NBD_INFO_EXPORT 0 |
179 | 0 | #define NBD_INFO_NAME 1 |
180 | 0 | #define NBD_INFO_DESCRIPTION 2 |
181 | 0 | #define NBD_INFO_BLOCK_SIZE 3 |
182 | | |
183 | | static const value_string nbd_info_vals[] = { |
184 | | {NBD_INFO_EXPORT, "Export"}, |
185 | | {NBD_INFO_NAME, "Name"}, |
186 | | {NBD_INFO_DESCRIPTION, "Description"}, |
187 | | {NBD_INFO_BLOCK_SIZE, "Block Size"}, |
188 | | {0, NULL} |
189 | | }; |
190 | | |
191 | 0 | #define NBD_REP_ACK 1 |
192 | 0 | #define NBD_REP_SERVER 2 |
193 | 0 | #define NBD_REP_INFO 3 |
194 | 0 | #define NBD_REP_META_CONTEXT 4 |
195 | | #define NBD_REP_ERR_UNSUP UINT32_C((1U << 31) + 1) |
196 | | #define NBD_REP_ERR_POLICY UINT32_C((1U << 31) + 2) |
197 | | #define NBD_REP_ERR_INVALID UINT32_C((1U << 31) + 3) |
198 | | #define NBD_REP_ERR_PLATFORM UINT32_C((1U << 31) + 4) |
199 | | #define NBD_REP_ERR_TLS_REQD UINT32_C((1U << 31) + 5) |
200 | | #define NBD_REP_ERR_UNKNOWN UINT32_C((1U << 31) + 6) |
201 | | #define NBD_REP_ERR_SHUTDOWN UINT32_C((1U << 31) + 7) |
202 | | #define NBD_REP_ERR_BLOCK_SIZE_REQD UINT32_C((1U << 31) + 8) |
203 | | #define NBD_REP_ERR_TOO_BIG UINT32_C((1U << 31) + 9) |
204 | | #define NBD_REP_ERR_EXT_HEADER_REQD UINT32_C((1U << 31) + 10) |
205 | | |
206 | | static const value_string nbd_hnd_reply_vals[] = { |
207 | | {NBD_REP_ACK, "ACK"}, |
208 | | {NBD_REP_SERVER, "Server"}, |
209 | | {NBD_REP_INFO, "Information"}, |
210 | | {NBD_REP_META_CONTEXT, "Metadata Context"}, |
211 | | {NBD_REP_ERR_UNSUP, "Unknown option"}, |
212 | | {NBD_REP_ERR_POLICY, "Forbidden by policy"}, |
213 | | {NBD_REP_ERR_INVALID, "Syntactically or semantically invalid"}, |
214 | | {NBD_REP_ERR_PLATFORM, "Unsupported by platform or as compiled"}, |
215 | | {NBD_REP_ERR_TLS_REQD, "TLS required"}, |
216 | | {NBD_REP_ERR_UNKNOWN, "Export not available"}, |
217 | | {NBD_REP_ERR_SHUTDOWN, "Server shutdown in process"}, |
218 | | {NBD_REP_ERR_BLOCK_SIZE_REQD, "Export requires negotiating non-default block size support"}, |
219 | | {NBD_REP_ERR_TOO_BIG, "Request or reply too large to process"}, |
220 | | {NBD_REP_ERR_EXT_HEADER_REQD, "Export requires negotiating extended header support"}, |
221 | | {0, NULL} |
222 | | }; |
223 | | |
224 | 0 | #define NBD_CMD_READ 0 |
225 | 0 | #define NBD_CMD_WRITE 1 |
226 | | #define NBD_CMD_DISC 2 |
227 | | #define NBD_CMD_FLUSH 3 |
228 | 0 | #define NBD_CMD_TRIM 4 |
229 | 0 | #define NBD_CMD_CACHE 5 |
230 | 0 | #define NBD_CMD_WRITE_ZEROES 6 |
231 | 0 | #define NBD_CMD_BLOCK_STATUS 7 |
232 | | #define NBD_CMD_RESIZE 8 |
233 | | |
234 | | static const value_string nbd_type_vals[] = { |
235 | | {NBD_CMD_READ, "Read"}, |
236 | | {NBD_CMD_WRITE, "Write"}, |
237 | | {NBD_CMD_DISC, "Disconnect"}, |
238 | | {NBD_CMD_FLUSH, "Flush"}, |
239 | | {NBD_CMD_TRIM, "Trim"}, |
240 | | {NBD_CMD_CACHE, "Cache"}, |
241 | | {NBD_CMD_WRITE_ZEROES, "Write Zeroes"}, |
242 | | {NBD_CMD_BLOCK_STATUS, "Block Status"}, |
243 | | {NBD_CMD_RESIZE, "Resize"}, |
244 | | {0, NULL} |
245 | | }; |
246 | | |
247 | | #define NBD_REPLY_NONE 0 |
248 | 0 | #define NBD_REPLY_OFFSET_DATA 1 |
249 | 0 | #define NBD_REPLY_OFFSET_HOLE 2 |
250 | 0 | #define NBD_REPLY_BLOCK_STATUS 5 |
251 | | #define NBD_REPLY_BLOCK_STATUS_EXT 6 |
252 | 0 | #define NBD_REPLY_ERROR 32769 |
253 | 0 | #define NBD_REPLY_ERROR_OFFSET 32770 |
254 | | |
255 | | static const value_string nbd_reply_type_vals[] = { |
256 | | {NBD_REPLY_NONE, "NBD_REPLY_NONE"}, |
257 | | {NBD_REPLY_OFFSET_DATA, "NBD_REPLY_OFFSET_DATA"}, |
258 | | {NBD_REPLY_OFFSET_HOLE, "NBD_REPLY_OFFSET_HOLE"}, |
259 | | {NBD_REPLY_BLOCK_STATUS, "NBD_REPLY_BLOCK_STATUS"}, |
260 | | {NBD_REPLY_BLOCK_STATUS_EXT, "NBD_REPLY_BLOCK_STATUS_EXT"}, |
261 | | {NBD_REPLY_ERROR, "NBD_REPLY_ERROR"}, |
262 | | {NBD_REPLY_ERROR_OFFSET, "NBD_REPLY_ERROR_OFFSET"}, |
263 | | {0, NULL} |
264 | | }; |
265 | | |
266 | | #define NBD_SUCCESS 0 |
267 | | #define NBD_EPERM 1 |
268 | | #define NBD_EIO 5 |
269 | | #define NBD_ENOMEM 12 |
270 | | #define NBD_EINVAL 22 |
271 | | #define NBD_ENOSPC 28 |
272 | | #define NBD_EOVERFLOW 75 |
273 | | #define NBD_ENOTSUP 95 |
274 | | #define NBD_ESHUTDOWN 108 |
275 | | |
276 | | static const value_string nbd_error_vals[] = { |
277 | | {NBD_SUCCESS, "Success"}, |
278 | | {NBD_EPERM, "Operation not permitted"}, |
279 | | {NBD_EIO, "Input/output error"}, |
280 | | {NBD_ENOMEM, "Cannot allocate memory"}, |
281 | | {NBD_EINVAL, "Invalid argument"}, |
282 | | {NBD_ENOSPC, "No space left on device"}, |
283 | | {NBD_EOVERFLOW, "Value too large"}, |
284 | | {NBD_ENOTSUP, "Operation not supported"}, |
285 | | {NBD_ESHUTDOWN, "Server is in the process of being shut down"}, |
286 | | {0, NULL} |
287 | | }; |
288 | | |
289 | 28 | #define NBD_FLAG_NO_ZEROES 0x0002 |
290 | | |
291 | | static bool |
292 | | nbd_from_server(packet_info *pinfo) |
293 | 182 | { |
294 | 182 | if (value_is_in_range(nbd_port_range, pinfo->srcport)) { |
295 | 0 | return true; |
296 | 182 | } else if (value_is_in_range(nbd_port_range, pinfo->destport)) { |
297 | 0 | return false; |
298 | 0 | } |
299 | 182 | return false; |
300 | 182 | } |
301 | | |
302 | | static nbd_conv_info_t* |
303 | | get_nbd_conv_info(packet_info *pinfo) |
304 | 130 | { |
305 | 130 | conversation_t *conversation; |
306 | 130 | nbd_conv_info_t *nbd_info; |
307 | | |
308 | 130 | conversation = find_or_create_conversation(pinfo); |
309 | | |
310 | | /* |
311 | | * Do we already have a state structure for this conv |
312 | | */ |
313 | 130 | nbd_info = (nbd_conv_info_t *)conversation_get_proto_data(conversation, proto_nbd); |
314 | | |
315 | 130 | if (!nbd_info) { |
316 | | /* No. Attach that information to the conversation, and add |
317 | | * it to the list of information structures. |
318 | | */ |
319 | 12 | nbd_info = wmem_new(wmem_file_scope(), nbd_conv_info_t); |
320 | 12 | nbd_info->no_zeroes = false; |
321 | 12 | nbd_info->state = wmem_tree_new(wmem_file_scope()); |
322 | 12 | nbd_info->opts = wmem_tree_new(wmem_file_scope()); |
323 | 12 | nbd_info->unacked_pdus = wmem_tree_new(wmem_file_scope()); |
324 | 12 | nbd_info->acked_pdus = wmem_tree_new(wmem_file_scope()); |
325 | | |
326 | 12 | conversation_add_proto_data(conversation, proto_nbd, nbd_info); |
327 | 12 | } |
328 | 130 | return nbd_info; |
329 | 130 | } |
330 | | |
331 | | static void |
332 | | nbd_set_state(packet_info *pinfo, nbd_state_e state) |
333 | 13 | { |
334 | 13 | nbd_conv_info_t *nbd_info; |
335 | 13 | nbd_state_e current_state; |
336 | | |
337 | 13 | nbd_info = get_nbd_conv_info(pinfo); |
338 | 13 | current_state = (nbd_state_e)GPOINTER_TO_UINT(wmem_tree_lookup32_le(nbd_info->state, pinfo->num)); |
339 | 13 | if (current_state != state) { |
340 | 10 | wmem_tree_insert32(nbd_info->state, pinfo->num, GUINT_TO_POINTER(state)); |
341 | 10 | } |
342 | 13 | } |
343 | | |
344 | | /* This function will try to determine the complete size of a PDU |
345 | | * based on the information in the header. |
346 | | */ |
347 | | static unsigned |
348 | | get_nbd_tcp_pdu_len(packet_info *pinfo, tvbuff_t *tvb, int offset, void *data _U_) |
349 | 0 | { |
350 | 0 | uint32_t magic, type, packet; |
351 | 0 | conversation_t *conversation; |
352 | 0 | nbd_conv_info_t *nbd_info; |
353 | 0 | nbd_transaction_t *nbd_trans=NULL; |
354 | 0 | wmem_tree_key_t hkey[3]; |
355 | 0 | uint32_t handle[2]; |
356 | |
|
357 | 0 | magic=tvb_get_ntohl(tvb, offset); |
358 | |
|
359 | 0 | switch(magic){ |
360 | 0 | case NBD_REQUEST_MAGIC: |
361 | 0 | type=tvb_get_ntohs(tvb, offset+6); |
362 | 0 | switch(type){ |
363 | 0 | case NBD_CMD_WRITE: |
364 | 0 | return tvb_get_ntohl(tvb, offset+24)+28; |
365 | 0 | default: |
366 | | /* |
367 | | * NB: Length field should always be present (and zero) |
368 | | * for other types too. |
369 | | */ |
370 | 0 | return 28; |
371 | 0 | } |
372 | 0 | case NBD_RESPONSE_MAGIC: |
373 | | /* |
374 | | * Do we have a conversation for this connection? |
375 | | */ |
376 | 0 | conversation = find_conversation_pinfo(pinfo, 0); |
377 | 0 | if (conversation == NULL) { |
378 | | /* No, so just return the rest of the current packet */ |
379 | 0 | return tvb_captured_length(tvb); |
380 | 0 | } |
381 | | /* |
382 | | * Do we have a state structure for this conv |
383 | | */ |
384 | 0 | nbd_info = (nbd_conv_info_t *)conversation_get_proto_data(conversation, proto_nbd); |
385 | 0 | if (!nbd_info) { |
386 | | /* No, so just return the rest of the current packet */ |
387 | 0 | return tvb_captured_length(tvb); |
388 | 0 | } |
389 | 0 | if(!pinfo->fd->visited){ |
390 | | /* |
391 | | * Do we have a state structure for this transaction |
392 | | */ |
393 | 0 | handle[0]=tvb_get_ntohl(tvb, offset+8); |
394 | 0 | handle[1]=tvb_get_ntohl(tvb, offset+12); |
395 | 0 | hkey[0].length=2; |
396 | 0 | hkey[0].key=handle; |
397 | 0 | hkey[1].length=0; |
398 | 0 | nbd_trans=(nbd_transaction_t *)wmem_tree_lookup32_array(nbd_info->unacked_pdus, hkey); |
399 | 0 | if(!nbd_trans){ |
400 | | /* No, so just return the rest of the current packet */ |
401 | 0 | return tvb_captured_length(tvb); |
402 | 0 | } |
403 | 0 | } else { |
404 | | /* |
405 | | * Do we have a state structure for this transaction |
406 | | */ |
407 | 0 | handle[0]=tvb_get_ntohl(tvb, offset+8); |
408 | 0 | handle[1]=tvb_get_ntohl(tvb, offset+12); |
409 | 0 | packet=pinfo->num; |
410 | 0 | hkey[0].length=1; |
411 | 0 | hkey[0].key=&packet; |
412 | 0 | hkey[1].length=2; |
413 | 0 | hkey[1].key=handle; |
414 | 0 | hkey[2].length=0; |
415 | 0 | nbd_trans=(nbd_transaction_t *)wmem_tree_lookup32_array(nbd_info->acked_pdus, hkey); |
416 | 0 | if(!nbd_trans){ |
417 | | /* No, so just return the rest of the current packet */ |
418 | 0 | return tvb_captured_length(tvb); |
419 | 0 | } |
420 | 0 | } |
421 | | /* If this is a read response we must add the datalen to |
422 | | * the pdu size |
423 | | */ |
424 | 0 | if(nbd_trans->type==NBD_CMD_READ){ |
425 | 0 | return 16+nbd_trans->datalen; |
426 | 0 | } else { |
427 | 0 | return 16; |
428 | 0 | } |
429 | 0 | case NBD_STRUCTURED_REPLY_MAGIC: |
430 | 0 | return tvb_get_ntohl(tvb, offset+16)+20; |
431 | 0 | default: |
432 | 0 | break; |
433 | 0 | } |
434 | | |
435 | | /* Did not really look like a NBD packet after all */ |
436 | 0 | return 0; |
437 | 0 | } |
438 | | |
439 | | static int * const nbd_cmd_flags[] = { |
440 | | &hf_nbd_cmd_flags_fua, |
441 | | &hf_nbd_cmd_flags_no_hole, |
442 | | &hf_nbd_cmd_flags_df, |
443 | | &hf_nbd_cmd_flags_req_one, |
444 | | &hf_nbd_cmd_flags_fast_zero, |
445 | | &hf_nbd_cmd_flags_payload_len, |
446 | | NULL, |
447 | | }; |
448 | | |
449 | | static int * const nbd_reply_flags[] = { |
450 | | &hf_nbd_reply_flags_done, |
451 | | NULL, |
452 | | }; |
453 | | |
454 | | static int * const nbd_trans_flags[] = { |
455 | | &hf_nbd_trans_flags_has_flags, |
456 | | &hf_nbd_trans_flags_read_only, |
457 | | &hf_nbd_trans_flags_flush, |
458 | | &hf_nbd_trans_flags_fua, |
459 | | &hf_nbd_trans_flags_rotational, |
460 | | &hf_nbd_trans_flags_trim, |
461 | | &hf_nbd_trans_flags_write_zeroes, |
462 | | &hf_nbd_trans_flags_df, |
463 | | &hf_nbd_trans_flags_multi_conn, |
464 | | &hf_nbd_trans_flags_resize, |
465 | | &hf_nbd_trans_flags_cache, |
466 | | &hf_nbd_trans_flags_fast_zero, |
467 | | &hf_nbd_trans_flags_block_status_payload, |
468 | | NULL, |
469 | | }; |
470 | | |
471 | | static int |
472 | | dissect_nbd_structured_reply(tvbuff_t *tvb, packet_info *pinfo _U_, proto_tree *tree, unsigned type) |
473 | 0 | { |
474 | 0 | proto_item *item; |
475 | 0 | int offset = 0; |
476 | 0 | uint32_t len; |
477 | |
|
478 | 0 | switch (type) { |
479 | 0 | case NBD_REPLY_OFFSET_DATA: |
480 | 0 | proto_tree_add_item(tree, hf_nbd_from, tvb, offset, 8, ENC_BIG_ENDIAN); |
481 | 0 | offset += 8; |
482 | |
|
483 | 0 | proto_tree_add_item(tree, hf_nbd_data, tvb, offset, -1, ENC_NA); |
484 | 0 | offset = tvb_reported_length(tvb); |
485 | 0 | break; |
486 | | |
487 | 0 | case NBD_REPLY_OFFSET_HOLE: |
488 | 0 | proto_tree_add_item(tree, hf_nbd_from, tvb, offset, 8, ENC_BIG_ENDIAN); |
489 | 0 | offset += 8; |
490 | |
|
491 | 0 | proto_tree_add_item(tree, hf_nbd_hole_size, tvb, offset, 4, ENC_NA); |
492 | 0 | offset = tvb_reported_length(tvb); |
493 | 0 | break; |
494 | | |
495 | 0 | case NBD_REPLY_BLOCK_STATUS: |
496 | 0 | proto_tree_add_item(tree, hf_nbd_meta_context_id, tvb, offset, 4, ENC_BIG_ENDIAN); |
497 | 0 | offset += 4; |
498 | 0 | while (tvb_reported_length_remaining(tvb, offset)) { |
499 | 0 | proto_tree_add_item(tree, hf_nbd_len, tvb, offset, 4, ENC_BIG_ENDIAN); |
500 | 0 | offset += 4; |
501 | 0 | proto_tree_add_item(tree, hf_nbd_status_flags, tvb, offset, 4, ENC_BIG_ENDIAN); |
502 | 0 | offset += 4; |
503 | 0 | } |
504 | 0 | break; |
505 | | |
506 | 0 | case NBD_REPLY_ERROR: |
507 | 0 | proto_tree_add_item(tree, hf_nbd_error, tvb, offset, 4, ENC_BIG_ENDIAN); |
508 | 0 | offset += 4; |
509 | 0 | proto_tree_add_item_ret_uint(tree, hf_nbd_error_msg_len, tvb, offset, 2, ENC_BIG_ENDIAN, &len); |
510 | 0 | offset += 2; |
511 | 0 | proto_tree_add_item(tree, hf_nbd_error_msg, tvb, offset, len, ENC_UTF_8); |
512 | 0 | break; |
513 | | |
514 | 0 | case NBD_REPLY_ERROR_OFFSET: |
515 | 0 | proto_tree_add_item(tree, hf_nbd_error, tvb, offset, 4, ENC_BIG_ENDIAN); |
516 | 0 | offset += 4; |
517 | 0 | proto_tree_add_item_ret_uint(tree, hf_nbd_error_msg_len, tvb, offset, 2, ENC_BIG_ENDIAN, &len); |
518 | 0 | offset += 2; |
519 | 0 | proto_tree_add_item(tree, hf_nbd_error_msg, tvb, offset, len, ENC_UTF_8); |
520 | 0 | offset += len; |
521 | 0 | proto_tree_add_item(tree, hf_nbd_from, tvb, offset, 8, ENC_BIG_ENDIAN); |
522 | 0 | offset += 8; |
523 | 0 | } |
524 | | |
525 | 0 | if (tvb_reported_length_remaining(tvb, offset)) { |
526 | 0 | item = proto_tree_add_item(tree, hf_nbd_data, tvb, offset, -1, ENC_NA); |
527 | 0 | expert_add_info(pinfo, item, &ei_nbd_unexpected_data); |
528 | 0 | } |
529 | |
|
530 | 0 | return tvb_reported_length(tvb); |
531 | 0 | } |
532 | | |
533 | | static int |
534 | | dissect_nbd_tcp_pdu(tvbuff_t *tvb, packet_info *pinfo, proto_tree *parent_tree, void* data _U_) |
535 | 0 | { |
536 | 0 | uint32_t magic, error, packet, data_len, type; |
537 | 0 | uint32_t handle[2]; |
538 | 0 | uint64_t from; |
539 | 0 | int offset=0; |
540 | 0 | proto_tree *tree=NULL; |
541 | 0 | proto_item *item=NULL; |
542 | 0 | nbd_conv_info_t *nbd_info; |
543 | 0 | nbd_transaction_t *nbd_trans=NULL; |
544 | 0 | wmem_tree_key_t hkey[3]; |
545 | |
|
546 | 0 | col_set_str(pinfo->cinfo, COL_PROTOCOL, "NBD"); |
547 | |
|
548 | 0 | col_clear(pinfo->cinfo, COL_INFO); |
549 | |
|
550 | 0 | item = proto_tree_add_item(parent_tree, proto_nbd, tvb, 0, -1, ENC_NA); |
551 | 0 | tree = proto_item_add_subtree(item, ett_nbd); |
552 | | |
553 | |
|
554 | 0 | magic=tvb_get_ntohl(tvb, offset); |
555 | 0 | proto_tree_add_item(tree, hf_nbd_magic, tvb, offset, 4, ENC_BIG_ENDIAN); |
556 | 0 | offset+=4; |
557 | | |
558 | | |
559 | | /* grab what we need to do the request/response matching */ |
560 | 0 | switch(magic){ |
561 | 0 | case NBD_REQUEST_MAGIC: |
562 | 0 | case NBD_RESPONSE_MAGIC: |
563 | 0 | case NBD_STRUCTURED_REPLY_MAGIC: |
564 | 0 | handle[0]=tvb_get_ntohl(tvb, offset+4); |
565 | 0 | handle[1]=tvb_get_ntohl(tvb, offset+8); |
566 | 0 | break; |
567 | 0 | default: |
568 | 0 | return 4; |
569 | 0 | } |
570 | | |
571 | 0 | nbd_info = get_nbd_conv_info(pinfo); |
572 | 0 | if(!pinfo->fd->visited){ |
573 | 0 | switch (magic) { |
574 | 0 | case NBD_REQUEST_MAGIC: |
575 | | /* This is a request */ |
576 | 0 | nbd_trans=wmem_new(wmem_file_scope(), nbd_transaction_t); |
577 | 0 | nbd_trans->req_frame=pinfo->num; |
578 | 0 | nbd_trans->rep_frame=0; |
579 | 0 | nbd_trans->req_time=pinfo->abs_ts; |
580 | 0 | nbd_trans->type=tvb_get_ntohl(tvb, offset); |
581 | 0 | nbd_trans->datalen=tvb_get_ntohl(tvb, offset+20); |
582 | |
|
583 | 0 | hkey[0].length=2; |
584 | 0 | hkey[0].key=handle; |
585 | 0 | hkey[1].length=0; |
586 | |
|
587 | 0 | wmem_tree_insert32_array(nbd_info->unacked_pdus, hkey, (void *)nbd_trans); |
588 | 0 | break; |
589 | | |
590 | 0 | case NBD_RESPONSE_MAGIC: |
591 | 0 | case NBD_STRUCTURED_REPLY_MAGIC: |
592 | | /* There MAY be multiple structured reply chunk to the |
593 | | * same request (with the same cookie/handle), instead |
594 | | * of TCP segmentation. In that case the later ones |
595 | | * will replace the older ones for matching. |
596 | | */ |
597 | 0 | hkey[0].length=2; |
598 | 0 | hkey[0].key=handle; |
599 | 0 | hkey[1].length=0; |
600 | |
|
601 | 0 | nbd_trans=(nbd_transaction_t *)wmem_tree_lookup32_array(nbd_info->unacked_pdus, hkey); |
602 | 0 | if(nbd_trans){ |
603 | 0 | nbd_trans->rep_frame=pinfo->num; |
604 | |
|
605 | 0 | hkey[0].length=1; |
606 | 0 | hkey[0].key=&nbd_trans->rep_frame; |
607 | 0 | hkey[1].length=2; |
608 | 0 | hkey[1].key=handle; |
609 | 0 | hkey[2].length=0; |
610 | 0 | wmem_tree_insert32_array(nbd_info->acked_pdus, hkey, (void *)nbd_trans); |
611 | 0 | hkey[0].length=1; |
612 | 0 | hkey[0].key=&nbd_trans->req_frame; |
613 | 0 | hkey[1].length=2; |
614 | 0 | hkey[1].key=handle; |
615 | 0 | hkey[2].length=0; |
616 | 0 | wmem_tree_insert32_array(nbd_info->acked_pdus, hkey, (void *)nbd_trans); |
617 | 0 | } |
618 | 0 | break; |
619 | 0 | default: |
620 | 0 | ws_assert_not_reached(); |
621 | 0 | } |
622 | 0 | } else { |
623 | 0 | packet=pinfo->num; |
624 | 0 | hkey[0].length=1; |
625 | 0 | hkey[0].key=&packet; |
626 | 0 | hkey[1].length=2; |
627 | 0 | hkey[1].key=handle; |
628 | 0 | hkey[2].length=0; |
629 | |
|
630 | 0 | nbd_trans=(nbd_transaction_t *)wmem_tree_lookup32_array(nbd_info->acked_pdus, hkey); |
631 | 0 | } |
632 | | /* The bloody handles are reused !!! even though they are 64 bits. |
633 | | * So we must verify we got the "correct" one |
634 | | */ |
635 | 0 | if( (magic==NBD_RESPONSE_MAGIC || magic==NBD_STRUCTURED_REPLY_MAGIC) |
636 | 0 | && (nbd_trans) |
637 | 0 | && (pinfo->num<nbd_trans->req_frame) ){ |
638 | | /* must have been the wrong one */ |
639 | 0 | nbd_trans=NULL; |
640 | 0 | } |
641 | |
|
642 | 0 | if(!nbd_trans){ |
643 | | /* create a "fake" nbd_trans structure */ |
644 | 0 | nbd_trans=wmem_new(pinfo->pool, nbd_transaction_t); |
645 | 0 | nbd_trans->req_frame=0; |
646 | 0 | nbd_trans->rep_frame=0; |
647 | 0 | nbd_trans->req_time=pinfo->abs_ts; |
648 | 0 | if (magic == NBD_REQUEST_MAGIC) { |
649 | 0 | nbd_trans->type=tvb_get_ntohl(tvb, offset); |
650 | 0 | nbd_trans->datalen=tvb_get_ntohl(tvb, offset+20); |
651 | 0 | } else { |
652 | 0 | nbd_trans->type=0xffff; |
653 | 0 | nbd_trans->datalen=0; |
654 | 0 | } |
655 | 0 | } |
656 | | |
657 | | /* print state tracking in the tree */ |
658 | 0 | switch (magic) { |
659 | 0 | case NBD_REQUEST_MAGIC: |
660 | | /* This is a request */ |
661 | 0 | if(nbd_trans->rep_frame){ |
662 | 0 | proto_item *it; |
663 | |
|
664 | 0 | it=proto_tree_add_uint(tree, hf_nbd_response_in, tvb, 0, 0, nbd_trans->rep_frame); |
665 | 0 | proto_item_set_generated(it); |
666 | 0 | } |
667 | 0 | break; |
668 | 0 | case NBD_RESPONSE_MAGIC: |
669 | 0 | case NBD_STRUCTURED_REPLY_MAGIC: |
670 | | /* This is a reply */ |
671 | 0 | if(nbd_trans->req_frame){ |
672 | 0 | proto_item *it; |
673 | 0 | nstime_t ns; |
674 | |
|
675 | 0 | it=proto_tree_add_uint(tree, hf_nbd_response_to, tvb, 0, 0, nbd_trans->req_frame); |
676 | 0 | proto_item_set_generated(it); |
677 | |
|
678 | 0 | nstime_delta(&ns, &pinfo->abs_ts, &nbd_trans->req_time); |
679 | 0 | it=proto_tree_add_time(tree, hf_nbd_time, tvb, 0, 0, &ns); |
680 | 0 | proto_item_set_generated(it); |
681 | 0 | } |
682 | 0 | } |
683 | | |
684 | | |
685 | 0 | switch(magic){ |
686 | 0 | case NBD_REQUEST_MAGIC: |
687 | 0 | proto_tree_add_bitmask(tree, tvb, offset, hf_nbd_cmd_flags, |
688 | 0 | ett_nbd_cmd_flags, nbd_cmd_flags, ENC_BIG_ENDIAN); |
689 | 0 | offset+=2; |
690 | |
|
691 | 0 | proto_tree_add_item(tree, hf_nbd_type, tvb, offset, 2, ENC_BIG_ENDIAN); |
692 | 0 | offset+=2; |
693 | |
|
694 | 0 | proto_tree_add_item(tree, hf_nbd_handle, tvb, offset, 8, ENC_BIG_ENDIAN); |
695 | 0 | offset+=8; |
696 | |
|
697 | 0 | from=tvb_get_ntoh64(tvb, offset); |
698 | 0 | proto_tree_add_item(tree, hf_nbd_from, tvb, offset, 8, ENC_BIG_ENDIAN); |
699 | 0 | offset+=8; |
700 | |
|
701 | 0 | proto_tree_add_item(tree, hf_nbd_len, tvb, offset, 4, ENC_BIG_ENDIAN); |
702 | 0 | offset+=4; |
703 | |
|
704 | 0 | col_add_fstr(pinfo->cinfo, COL_INFO, "%s Request", val_to_str(nbd_trans->type, nbd_type_vals, "Unknown (%d)")); |
705 | 0 | switch(nbd_trans->type){ |
706 | 0 | case NBD_CMD_WRITE: |
707 | 0 | case NBD_CMD_READ: |
708 | 0 | case NBD_CMD_TRIM: |
709 | 0 | case NBD_CMD_CACHE: |
710 | 0 | case NBD_CMD_WRITE_ZEROES: |
711 | 0 | case NBD_CMD_BLOCK_STATUS: |
712 | 0 | col_append_fstr(pinfo->cinfo, COL_INFO, " Offset:0x%" PRIx64 " Length:%d", from, nbd_trans->datalen); |
713 | 0 | break; |
714 | 0 | } |
715 | | |
716 | 0 | if(nbd_trans->type==NBD_CMD_WRITE){ |
717 | 0 | proto_tree_add_item(tree, hf_nbd_data, tvb, offset, nbd_trans->datalen, ENC_NA); |
718 | 0 | } |
719 | 0 | break; |
720 | 0 | case NBD_RESPONSE_MAGIC: |
721 | 0 | item=proto_tree_add_uint(tree, hf_nbd_type, tvb, 0, 0, nbd_trans->type); |
722 | 0 | proto_item_set_generated(item); |
723 | |
|
724 | 0 | error=tvb_get_ntohl(tvb, offset); |
725 | 0 | proto_tree_add_item(tree, hf_nbd_error, tvb, offset, 4, ENC_BIG_ENDIAN); |
726 | 0 | offset+=4; |
727 | |
|
728 | 0 | proto_tree_add_item(tree, hf_nbd_handle, tvb, offset, 8, ENC_BIG_ENDIAN); |
729 | 0 | offset+=8; |
730 | |
|
731 | 0 | col_add_fstr(pinfo->cinfo, COL_INFO, "%s Response %s", val_to_str(nbd_trans->type, nbd_type_vals, "Unknown (%d)"), val_to_str(error, nbd_error_vals, "Unknown error (%d)")); |
732 | |
|
733 | 0 | if(nbd_trans->type==NBD_CMD_READ){ |
734 | 0 | proto_tree_add_item(tree, hf_nbd_data, tvb, offset, nbd_trans->datalen, ENC_NA); |
735 | 0 | } |
736 | 0 | break; |
737 | 0 | case NBD_STRUCTURED_REPLY_MAGIC: |
738 | | /* structured reply flags */ |
739 | 0 | proto_tree_add_bitmask(tree, tvb, offset, hf_nbd_reply_flags, |
740 | 0 | ett_nbd_reply_flags, nbd_reply_flags, ENC_BIG_ENDIAN); |
741 | 0 | offset+=2; |
742 | 0 | item = proto_tree_add_item_ret_uint(tree, hf_nbd_reply_type, tvb, offset, 2, ENC_BIG_ENDIAN, &type); |
743 | 0 | if (type & 0x8000) { |
744 | 0 | expert_add_info(pinfo, item, &ei_nbd_hnd_reply_error); |
745 | 0 | } |
746 | 0 | offset+=2; |
747 | |
|
748 | 0 | proto_tree_add_item(tree, hf_nbd_handle, tvb, offset, 8, ENC_BIG_ENDIAN); |
749 | 0 | offset+=8; |
750 | |
|
751 | 0 | proto_tree_add_item_ret_uint(tree, hf_nbd_len, tvb, offset, 4, ENC_BIG_ENDIAN, &data_len); |
752 | 0 | offset+=4; |
753 | |
|
754 | 0 | dissect_nbd_structured_reply(tvb_new_subset_length(tvb, offset, data_len), pinfo, tree, type); |
755 | | /*offset += data_len; */ |
756 | 0 | } |
757 | | |
758 | 0 | return tvb_captured_length(tvb); |
759 | 0 | } |
760 | | |
761 | | static int |
762 | | dissect_nbd_transmission(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void *data _U_) |
763 | 91 | { |
764 | 91 | uint32_t magic, type; |
765 | 91 | unsigned pdu_fixed; |
766 | | |
767 | | /* We want 8 to test the type */ |
768 | 91 | if (tvb_captured_length(tvb) < 8) { |
769 | 0 | return 0; |
770 | 0 | } |
771 | | |
772 | 91 | magic = tvb_get_ntohl(tvb, 0); |
773 | 91 | type = tvb_get_ntohs(tvb, 6); |
774 | | |
775 | 91 | switch(magic){ |
776 | 0 | case NBD_REQUEST_MAGIC: |
777 | | /* verify type */ |
778 | 0 | if (!try_val_to_str(type, nbd_type_vals)) { |
779 | 0 | return 0; |
780 | 0 | } |
781 | 0 | pdu_fixed = 28; |
782 | 0 | break; |
783 | 0 | case NBD_RESPONSE_MAGIC: |
784 | 0 | pdu_fixed = 16; |
785 | 0 | break; |
786 | 0 | case NBD_STRUCTURED_REPLY_MAGIC: |
787 | | /* verify type */ |
788 | 0 | if (!try_val_to_str(type, nbd_reply_type_vals)) { |
789 | 0 | return 0; |
790 | 0 | } |
791 | 0 | pdu_fixed = 20; |
792 | 0 | break; |
793 | 91 | default: |
794 | 91 | return 0; |
795 | 91 | } |
796 | | |
797 | 0 | nbd_set_state(pinfo, STATE_HND_DONE); |
798 | 0 | tcp_dissect_pdus(tvb, pinfo, tree, nbd_desegment, pdu_fixed, get_nbd_tcp_pdu_len, dissect_nbd_tcp_pdu, data); |
799 | 0 | return tvb_captured_length(tvb); |
800 | 91 | } |
801 | | |
802 | | static unsigned |
803 | | get_nbd_opt_len(packet_info *pinfo _U_, tvbuff_t *tvb, int offset, void *data _U_) |
804 | 3 | { |
805 | 3 | unsigned pdu_len = tvb_get_uint32(tvb, offset + 12, ENC_BIG_ENDIAN); |
806 | | |
807 | 3 | return 16 + pdu_len; |
808 | 3 | } |
809 | | |
810 | | static int |
811 | | dissect_nbd_opt_pdu(tvbuff_t *tvb, packet_info *pinfo, proto_tree *parent_tree, void* data _U_) |
812 | 3 | { |
813 | 3 | proto_item *item; |
814 | 3 | proto_tree *tree; |
815 | 3 | int offset = 0; |
816 | 3 | uint32_t opt, data_len, name_len, info_num; |
817 | 3 | nbd_conv_info_t *nbd_info; |
818 | 3 | nbd_option_t *nbd_opt; |
819 | 3 | const uint8_t *export_name; |
820 | | |
821 | 3 | item = proto_tree_add_item(parent_tree, proto_nbd, tvb, 0, -1, ENC_NA); |
822 | 3 | tree = proto_item_add_subtree(item, ett_nbd); |
823 | | |
824 | 3 | proto_tree_add_item(tree, hf_nbd_hnd_magic, tvb, offset, 8, ENC_BIG_ENDIAN); |
825 | 3 | offset += 8; |
826 | | |
827 | 3 | nbd_info = get_nbd_conv_info(pinfo); |
828 | 3 | if (!PINFO_FD_VISITED(pinfo)) { |
829 | 3 | nbd_opt = wmem_new(wmem_file_scope(), nbd_option_t); |
830 | 3 | nbd_opt->req_frame=pinfo->num; |
831 | 3 | nbd_opt->rep_frame=0; |
832 | 3 | nbd_opt->req_time=pinfo->abs_ts; |
833 | 3 | nbd_opt->opt=tvb_get_ntohl(tvb, offset); |
834 | | |
835 | 3 | wmem_tree_insert32(nbd_info->opts, pinfo->num, (void *)nbd_opt); |
836 | 3 | } else { |
837 | 0 | nbd_opt = (nbd_option_t*)wmem_tree_lookup32(nbd_info->opts, pinfo->num); |
838 | 0 | if (nbd_opt && nbd_opt->rep_frame) { |
839 | 0 | item = proto_tree_add_uint(tree, hf_nbd_response_in, tvb, 0, 0, nbd_opt->rep_frame); |
840 | 0 | proto_item_set_generated(item); |
841 | 0 | } |
842 | 0 | } |
843 | | |
844 | 3 | proto_tree_add_item_ret_uint(tree, hf_nbd_hnd_opt, tvb, offset, 4, ENC_BIG_ENDIAN, &opt); |
845 | 3 | col_append_sep_str(pinfo->cinfo, COL_INFO, NULL, val_to_str(opt, nbd_opt_vals, "Unknown (%d)")); |
846 | | |
847 | 3 | offset += 4; |
848 | | |
849 | 3 | proto_tree_add_item_ret_uint(tree, hf_nbd_len, tvb, offset, 4, ENC_BIG_ENDIAN, &data_len); |
850 | 3 | offset += 4; |
851 | | |
852 | 3 | if (data_len) { |
853 | 2 | switch (opt) { |
854 | 0 | case NBD_OPT_EXPORT_NAME: |
855 | 0 | proto_tree_add_item_ret_string(tree, hf_nbd_export_name, tvb, offset, data_len, ENC_UTF_8, pinfo->pool, &export_name); |
856 | 0 | col_append_sep_str(pinfo->cinfo, COL_INFO, ":", export_name); |
857 | 0 | break; |
858 | 0 | case NBD_OPT_INFO: |
859 | 0 | case NBD_OPT_GO: |
860 | 0 | proto_tree_add_item_ret_uint(tree, hf_nbd_export_name_len, tvb, offset, 4, ENC_BIG_ENDIAN, &name_len); |
861 | 0 | offset += 4; |
862 | 0 | proto_tree_add_item(tree, hf_nbd_export_name, tvb, offset, name_len, ENC_UTF_8); |
863 | 0 | offset += name_len; |
864 | 0 | proto_tree_add_item_ret_uint(tree, hf_nbd_info_num, tvb, offset, 2, ENC_BIG_ENDIAN, &info_num); |
865 | 0 | offset += 2; |
866 | 0 | for (unsigned i = 0; i < info_num; ++i) { |
867 | 0 | proto_tree_add_item(tree, hf_nbd_info, tvb, offset, 2, ENC_BIG_ENDIAN); |
868 | 0 | offset += 2; |
869 | 0 | } |
870 | 0 | break; |
871 | 0 | case NBD_OPT_LIST_META_CONTEXT: |
872 | 0 | case NBD_OPT_SET_META_CONTEXT: |
873 | 0 | proto_tree_add_item_ret_uint(tree, hf_nbd_export_name_len, tvb, offset, 4, ENC_BIG_ENDIAN, &name_len); |
874 | 0 | offset += 4; |
875 | 0 | proto_tree_add_item(tree, hf_nbd_export_name, tvb, offset, name_len, ENC_UTF_8); |
876 | 0 | offset += name_len; |
877 | 0 | proto_tree_add_item_ret_uint(tree, hf_nbd_query_num, tvb, offset, 4, ENC_BIG_ENDIAN, &info_num); |
878 | 0 | offset += 4; |
879 | 0 | for (unsigned i = 0; i < info_num; ++i) { |
880 | 0 | proto_tree_add_item_ret_length(tree, hf_nbd_query, tvb, offset, 2, ENC_BIG_ENDIAN, &name_len); |
881 | 0 | offset += name_len; |
882 | 0 | } |
883 | 0 | break; |
884 | 2 | default: |
885 | 2 | proto_tree_add_item(tree, hf_nbd_data, tvb, offset, data_len, ENC_NA); |
886 | 2 | } |
887 | 2 | } |
888 | | |
889 | 1 | return tvb_captured_length(tvb); |
890 | 3 | } |
891 | | |
892 | | static unsigned |
893 | | get_nbd_opt_reply_len(packet_info *pinfo _U_, tvbuff_t *tvb, int offset, void *data _U_) |
894 | 24 | { |
895 | 24 | unsigned pdu_len = tvb_get_uint32(tvb, offset + 16, ENC_BIG_ENDIAN); |
896 | | |
897 | 24 | return 20 + pdu_len; |
898 | 24 | } |
899 | | |
900 | | static int |
901 | | dissect_nbd_opt_reply(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, unsigned type) |
902 | 23 | { |
903 | 23 | proto_item *item; |
904 | 23 | int offset = 0; |
905 | 23 | uint32_t name_len, info_type; |
906 | | |
907 | 23 | switch (type) { |
908 | 0 | case NBD_REP_SERVER: |
909 | 0 | proto_tree_add_item_ret_uint(tree, hf_nbd_export_name_len, tvb, offset, 4, ENC_BIG_ENDIAN, &name_len); |
910 | 0 | offset += 4; |
911 | 0 | proto_tree_add_item(tree, hf_nbd_export_name, tvb, offset, name_len, ENC_UTF_8); |
912 | 0 | offset += name_len; |
913 | 0 | break; |
914 | 0 | case NBD_REP_INFO: |
915 | 0 | proto_tree_add_item_ret_uint(tree, hf_nbd_info, tvb, offset, 2, ENC_BIG_ENDIAN, &info_type); |
916 | 0 | offset += 2; |
917 | 0 | switch (info_type) { |
918 | 0 | case NBD_INFO_EXPORT: |
919 | 0 | proto_tree_add_item(tree, hf_nbd_export_size, tvb, offset, 8, ENC_BIG_ENDIAN); |
920 | 0 | offset += 8; |
921 | |
|
922 | 0 | proto_tree_add_bitmask(tree, tvb, offset, hf_nbd_trans_flags, |
923 | 0 | ett_nbd_trans_flags, nbd_trans_flags, ENC_BIG_ENDIAN); |
924 | 0 | offset += 2; |
925 | 0 | break; |
926 | 0 | case NBD_INFO_NAME: |
927 | 0 | proto_tree_add_item(tree, hf_nbd_export_name, tvb, offset, tvb_reported_length_remaining(tvb, offset), ENC_UTF_8); |
928 | 0 | offset = tvb_reported_length(tvb); |
929 | 0 | break; |
930 | 0 | case NBD_INFO_DESCRIPTION: |
931 | 0 | proto_tree_add_item(tree, hf_nbd_export_description, tvb, offset, tvb_reported_length_remaining(tvb, offset), ENC_UTF_8); |
932 | 0 | offset = tvb_reported_length(tvb); |
933 | 0 | break; |
934 | 0 | case NBD_INFO_BLOCK_SIZE: |
935 | 0 | proto_tree_add_item(tree, hf_nbd_block_size_min, tvb, offset, 4, ENC_BIG_ENDIAN); |
936 | 0 | offset += 4; |
937 | 0 | proto_tree_add_item(tree, hf_nbd_block_size_prefer, tvb, offset, 4, ENC_BIG_ENDIAN); |
938 | 0 | offset += 4; |
939 | 0 | proto_tree_add_item(tree, hf_nbd_payload_size_max, tvb, offset, 4, ENC_BIG_ENDIAN); |
940 | 0 | offset += 4; |
941 | 0 | } |
942 | 0 | break; |
943 | 0 | case NBD_REP_META_CONTEXT: |
944 | 0 | proto_tree_add_item(tree, hf_nbd_meta_context_id, tvb, offset, 4, ENC_BIG_ENDIAN); |
945 | 0 | offset += 4; |
946 | 0 | proto_tree_add_item(tree, hf_nbd_meta_context_name, tvb, offset, tvb_reported_length_remaining(tvb, offset), ENC_UTF_8); |
947 | 0 | offset = tvb_reported_length(tvb); |
948 | 23 | } |
949 | | |
950 | 23 | if (tvb_reported_length_remaining(tvb, offset)) { |
951 | 11 | if (type & UINT32_C(1 << 31)) { |
952 | 2 | proto_tree_add_item(tree, hf_nbd_error_msg, tvb, offset, -1, ENC_UTF_8); |
953 | 9 | } else { |
954 | 9 | item = proto_tree_add_item(tree, hf_nbd_data, tvb, offset, -1, ENC_NA); |
955 | 9 | expert_add_info(pinfo, item, &ei_nbd_unexpected_data); |
956 | 9 | } |
957 | 11 | } |
958 | | |
959 | 23 | return tvb_reported_length(tvb); |
960 | 23 | } |
961 | | |
962 | | static int |
963 | | dissect_nbd_opt_reply_pdu(tvbuff_t *tvb, packet_info *pinfo, proto_tree *parent_tree, void* data _U_) |
964 | 23 | { |
965 | 23 | proto_item *item, *gen_item; |
966 | 23 | proto_tree *tree; |
967 | 23 | int offset = 0; |
968 | 23 | uint32_t opt, reply, data_len; |
969 | 23 | nbd_conv_info_t *nbd_info; |
970 | 23 | nbd_option_t *nbd_opt; |
971 | | |
972 | 23 | item = proto_tree_add_item(parent_tree, proto_nbd, tvb, 0, -1, ENC_NA); |
973 | 23 | tree = proto_item_add_subtree(item, ett_nbd); |
974 | | |
975 | 23 | item = proto_tree_add_item(tree, hf_nbd_hnd_magic, tvb, offset, 8, ENC_BIG_ENDIAN); |
976 | 23 | offset += 8; |
977 | | |
978 | 23 | nbd_info = get_nbd_conv_info(pinfo); |
979 | 23 | nbd_opt = (nbd_option_t*)wmem_tree_lookup32_le(nbd_info->opts, pinfo->num); |
980 | | |
981 | 23 | proto_tree_add_item_ret_uint(tree, hf_nbd_hnd_opt, tvb, offset, 4, ENC_BIG_ENDIAN, &opt); |
982 | 23 | col_append_sep_str(pinfo->cinfo, COL_INFO, NULL, val_to_str(opt, nbd_opt_vals, "Unknown (%d)")); |
983 | 23 | offset += 4; |
984 | | |
985 | 23 | if (nbd_opt && nbd_opt->opt == opt) { |
986 | 0 | nbd_opt->rep_frame = pinfo->num; |
987 | |
|
988 | 0 | gen_item = proto_tree_add_uint(tree, hf_nbd_response_to, tvb, 0, 0, nbd_opt->req_frame); |
989 | 0 | proto_item_set_generated(gen_item); |
990 | 0 | proto_tree_move_item(tree, item, gen_item); |
991 | 0 | item = gen_item; |
992 | |
|
993 | 0 | nstime_t ns; |
994 | 0 | nstime_delta(&ns, &pinfo->abs_ts, &nbd_opt->req_time); |
995 | 0 | gen_item = proto_tree_add_time(tree, hf_nbd_time, tvb, 0, 0, &ns); |
996 | 0 | proto_item_set_generated(gen_item); |
997 | 0 | proto_tree_move_item(tree, item, gen_item); |
998 | 0 | } |
999 | | |
1000 | 23 | item = proto_tree_add_item_ret_uint(tree, hf_nbd_hnd_reply, tvb, offset, 4, ENC_BIG_ENDIAN, &reply); |
1001 | 23 | col_append_sep_str(pinfo->cinfo, COL_INFO, NULL, val_to_str(reply, nbd_hnd_reply_vals, "Unknown (%d)")); |
1002 | 23 | if (reply & UINT64_C(0x80000000)) { |
1003 | 3 | expert_add_info(pinfo, item, &ei_nbd_hnd_reply_error); |
1004 | 3 | } |
1005 | 23 | if (opt == NBD_OPT_STARTTLS && reply == NBD_REP_ACK) { |
1006 | 0 | ssl_starttls_ack(tls_handle, pinfo, nbd_handle); |
1007 | 0 | } |
1008 | | |
1009 | 23 | offset += 4; |
1010 | | |
1011 | 23 | proto_tree_add_item_ret_uint(tree, hf_nbd_len, tvb, offset, 4, ENC_BIG_ENDIAN, &data_len); |
1012 | 23 | offset += 4; |
1013 | | |
1014 | 23 | dissect_nbd_opt_reply(tvb_new_subset_length(tvb, offset, data_len), pinfo, tree, reply); |
1015 | 23 | return tvb_captured_length(tvb); |
1016 | 23 | } |
1017 | | |
1018 | | static unsigned |
1019 | | get_nbd_export_len(packet_info *pinfo, tvbuff_t *tvb _U_, int offset _U_, void *data _U_) |
1020 | 0 | { |
1021 | 0 | nbd_conv_info_t *nbd_info; |
1022 | |
|
1023 | 0 | nbd_info = get_nbd_conv_info(pinfo); |
1024 | | /* There might, or might not, be 124 bytes of zeroes, depending on |
1025 | | * what was negotiated in the flags. */ |
1026 | 0 | return 10 + (nbd_info->no_zeroes ? 0 : 124); |
1027 | 0 | } |
1028 | | |
1029 | | static int |
1030 | | dissect_nbd_export_pdu(tvbuff_t *tvb, packet_info *pinfo, proto_tree *parent_tree, void* data _U_) |
1031 | 0 | { |
1032 | 0 | proto_item *item; |
1033 | 0 | proto_tree *tree; |
1034 | 0 | int offset = 0; |
1035 | 0 | nbd_conv_info_t *nbd_info; |
1036 | 0 | nbd_option_t *nbd_opt; |
1037 | |
|
1038 | 0 | item = proto_tree_add_item(parent_tree, proto_nbd, tvb, 0, -1, ENC_NA); |
1039 | 0 | tree = proto_item_add_subtree(item, ett_nbd); |
1040 | |
|
1041 | 0 | nbd_info = get_nbd_conv_info(pinfo); |
1042 | 0 | nbd_opt = (nbd_option_t*)wmem_tree_lookup32_le(nbd_info->opts, pinfo->num); |
1043 | |
|
1044 | 0 | if (nbd_opt && nbd_opt->opt == NBD_OPT_EXPORT_NAME) { |
1045 | 0 | nbd_opt->rep_frame = pinfo->num; |
1046 | |
|
1047 | 0 | item = proto_tree_add_uint(tree, hf_nbd_response_to, tvb, 0, 0, nbd_opt->req_frame); |
1048 | 0 | proto_item_set_generated(item); |
1049 | |
|
1050 | 0 | nstime_t ns; |
1051 | 0 | nstime_delta(&ns, &pinfo->abs_ts, &nbd_opt->req_time); |
1052 | 0 | item = proto_tree_add_time(tree, hf_nbd_time, tvb, 0, 0, &ns); |
1053 | 0 | proto_item_set_generated(item); |
1054 | |
|
1055 | 0 | item = proto_tree_add_uint(tree, hf_nbd_hnd_opt, tvb, 0, 0, nbd_opt->opt); |
1056 | 0 | proto_item_set_generated(item); |
1057 | 0 | } |
1058 | |
|
1059 | 0 | proto_tree_add_item(tree, hf_nbd_export_size, tvb, offset, 8, ENC_BIG_ENDIAN); |
1060 | 0 | offset += 8; |
1061 | |
|
1062 | 0 | proto_tree_add_bitmask(tree, tvb, offset, hf_nbd_trans_flags, |
1063 | 0 | ett_nbd_trans_flags, nbd_trans_flags, ENC_BIG_ENDIAN); |
1064 | 0 | col_set_str(pinfo->cinfo, COL_INFO, "Transmission Flags"); |
1065 | 0 | offset += 2; |
1066 | |
|
1067 | 0 | if (tvb_captured_length_remaining(tvb, offset)) { |
1068 | 0 | proto_tree_add_item(tree, hf_nbd_reserved, tvb, offset, -1, ENC_NA); |
1069 | 0 | } |
1070 | |
|
1071 | 0 | return tvb_captured_length(tvb); |
1072 | 0 | } |
1073 | | |
1074 | | /* These flags have the same offset, but one is a 16 bit bitmask |
1075 | | * and one is a 32 bit bitmask, which might matter for future |
1076 | | * expansion. |
1077 | | */ |
1078 | | static int * const nbd_hnd_flags[] = { |
1079 | | &hf_nbd_hnd_flags_fixed_new, |
1080 | | &hf_nbd_hnd_flags_no_zeroes, |
1081 | | NULL, |
1082 | | }; |
1083 | | |
1084 | | static int * const nbd_cli_flags[] = { |
1085 | | &hf_nbd_cli_flags_fixed_new, |
1086 | | &hf_nbd_cli_flags_no_zeroes, |
1087 | | NULL, |
1088 | | }; |
1089 | | |
1090 | | static unsigned |
1091 | | get_nbd_old_len(packet_info *pinfo _U_, tvbuff_t *tvb _U_, int offset _U_, void *data _U_) |
1092 | 6 | { |
1093 | 6 | return 144; // 8 + 8 + 4 + 124 |
1094 | 6 | } |
1095 | | |
1096 | | static int |
1097 | | dissect_nbd_old_pdu(tvbuff_t *tvb, packet_info *pinfo, proto_tree *parent_tree, void* data _U_) |
1098 | 6 | { |
1099 | 6 | proto_item *item; |
1100 | 6 | proto_tree *tree; |
1101 | 6 | int offset = 0; |
1102 | | |
1103 | 6 | col_set_str(pinfo->cinfo, COL_INFO, "Oldstyle Handshake"); |
1104 | | |
1105 | 6 | item = proto_tree_add_item(parent_tree, proto_nbd, tvb, 0, -1, ENC_NA); |
1106 | 6 | tree = proto_item_add_subtree(item, ett_nbd); |
1107 | | |
1108 | 6 | proto_tree_add_item(tree, hf_nbd_hnd_magic, tvb, offset, 8, ENC_BIG_ENDIAN); |
1109 | 6 | offset += 8; |
1110 | | |
1111 | 6 | proto_tree_add_item(tree, hf_nbd_export_size, tvb, offset, 8, ENC_BIG_ENDIAN); |
1112 | 6 | offset += 8; |
1113 | | |
1114 | 6 | proto_tree_add_bitmask(tree, tvb, offset, hf_nbd_hnd_flags, |
1115 | 6 | ett_nbd_hnd_flags, nbd_hnd_flags, ENC_BIG_ENDIAN); |
1116 | 6 | offset += 2; |
1117 | | |
1118 | 6 | proto_tree_add_bitmask(tree, tvb, offset, hf_nbd_trans_flags, |
1119 | 6 | ett_nbd_trans_flags, nbd_trans_flags, ENC_BIG_ENDIAN); |
1120 | 6 | offset += 2; |
1121 | | |
1122 | 6 | proto_tree_add_item(tree, hf_nbd_reserved, tvb, offset, 124, ENC_NA); |
1123 | | |
1124 | 6 | return tvb_captured_length(tvb); |
1125 | 6 | } |
1126 | | |
1127 | | static int |
1128 | | dissect_nbd_hnd(tvbuff_t *tvb, packet_info *pinfo, proto_tree *parent_tree, void *data _U_) |
1129 | 91 | { |
1130 | 91 | proto_item *item; |
1131 | 91 | proto_tree *tree; |
1132 | 91 | int offset = 0; |
1133 | 91 | uint64_t magic; |
1134 | | //nbd_conv_info_t *nbd_info; |
1135 | | |
1136 | 91 | nbd_state_e new_state; |
1137 | | |
1138 | | //nbd_info = get_nbd_conv_info(pinfo); |
1139 | 91 | bool from_server = nbd_from_server(pinfo); |
1140 | | |
1141 | | /* We want 8 to test the magic number */ |
1142 | 91 | if (tvb_captured_length_remaining(tvb, offset) < 8) { |
1143 | 0 | return 0; |
1144 | 0 | } |
1145 | | |
1146 | 91 | magic = tvb_get_uint64(tvb, offset, ENC_BIG_ENDIAN); |
1147 | | |
1148 | 91 | switch (magic) { |
1149 | 2 | case NBD_HND_INIT_MAGIC: |
1150 | 2 | item = proto_tree_add_item(parent_tree, proto_nbd, tvb, 0, 8, ENC_NA); |
1151 | 2 | tree = proto_item_add_subtree(item, ett_nbd); |
1152 | | |
1153 | 2 | proto_tree_add_item(tree, hf_nbd_hnd_magic, tvb, offset, 8, ENC_BIG_ENDIAN); |
1154 | 2 | col_set_str(pinfo->cinfo, COL_INFO, "Handshake Start"); |
1155 | 2 | nbd_set_state(pinfo, STATE_HND_INIT); |
1156 | 2 | break; |
1157 | 2 | case NBD_HND_OPT_MAGIC: |
1158 | | /* Unfortunately the server and client use the same OPT_MAGIC, |
1159 | | * and they mean something different about what is expected next. |
1160 | | */ |
1161 | 2 | new_state = from_server ? STATE_HND_INIT : STATE_HND_OPT; |
1162 | 2 | nbd_set_state(pinfo, new_state); |
1163 | 2 | if (from_server) { |
1164 | 0 | item = proto_tree_add_item(parent_tree, proto_nbd, tvb, 0, 8, ENC_NA); |
1165 | 0 | tree = proto_item_add_subtree(item, ett_nbd); |
1166 | |
|
1167 | 0 | proto_tree_add_item(tree, hf_nbd_hnd_magic, tvb, offset, 8, ENC_BIG_ENDIAN); |
1168 | 0 | col_set_str(pinfo->cinfo, COL_INFO, "Newstyle Handshake"); |
1169 | 2 | } else { |
1170 | 2 | tcp_dissect_pdus(tvb, pinfo, parent_tree, nbd_desegment, 16, get_nbd_opt_len, dissect_nbd_opt_pdu, data); |
1171 | 2 | } |
1172 | 2 | break; |
1173 | 9 | case NBD_HND_REPLY_MAGIC: |
1174 | 9 | nbd_set_state(pinfo, STATE_HND_OPT); |
1175 | 9 | tcp_dissect_pdus(tvb, pinfo, parent_tree, nbd_desegment, 20, get_nbd_opt_reply_len, dissect_nbd_opt_reply_pdu, data); |
1176 | 9 | break; |
1177 | 2 | case NBD_HND_OLD_MAGIC: |
1178 | 2 | tcp_dissect_pdus(tvb, pinfo, parent_tree, nbd_desegment, 20, get_nbd_old_len, dissect_nbd_old_pdu, data); |
1179 | 2 | break; |
1180 | 76 | default: |
1181 | 76 | return 0; |
1182 | 91 | } |
1183 | | |
1184 | 11 | return tvb_captured_length(tvb); |
1185 | 91 | } |
1186 | | |
1187 | | static int |
1188 | | dissect_nbd(tvbuff_t *tvb, packet_info *pinfo, proto_tree *parent_tree, void* data _U_) |
1189 | 91 | { |
1190 | 91 | int offset=0; |
1191 | 91 | proto_tree *tree=NULL; |
1192 | 91 | proto_item *item=NULL; |
1193 | 91 | nbd_conv_info_t *nbd_info; |
1194 | | |
1195 | 91 | nbd_state_e current_state; |
1196 | | |
1197 | 91 | col_set_str(pinfo->cinfo, COL_PROTOCOL, "NBD"); |
1198 | | |
1199 | 91 | col_clear(pinfo->cinfo, COL_INFO); |
1200 | | |
1201 | 91 | bool from_server = nbd_from_server(pinfo); |
1202 | 91 | nbd_info = get_nbd_conv_info(pinfo); |
1203 | 91 | current_state = (nbd_state_e)GPOINTER_TO_UINT(wmem_tree_lookup32_le(nbd_info->state, pinfo->num)); |
1204 | 91 | nbd_option_t *nbd_opt; |
1205 | 91 | nbd_opt = (nbd_option_t*)wmem_tree_lookup32_le(nbd_info->opts, pinfo->num); |
1206 | | |
1207 | | /* NBD has 8 byte magic numbers for the handshake phase, and 4 byte |
1208 | | * magic numbers for the transmission phase. A few handshake messages |
1209 | | * are not preceded by magic numbers in that direction (and one magic |
1210 | | * number is used for different messages types in the two directions.) |
1211 | | */ |
1212 | | |
1213 | 91 | if (!dissect_nbd_transmission(tvb, pinfo, parent_tree, data)) { |
1214 | 91 | if (!dissect_nbd_hnd(tvb, pinfo, parent_tree, data)) { |
1215 | | |
1216 | 76 | item = proto_tree_add_item(parent_tree, proto_nbd, tvb, 0, -1, ENC_NA); |
1217 | 76 | tree = proto_item_add_subtree(item, ett_nbd); |
1218 | | |
1219 | 76 | if (current_state == STATE_HND_INIT) { |
1220 | 0 | uint64_t flags; |
1221 | 0 | if (from_server) { |
1222 | 0 | proto_tree_add_bitmask(tree, tvb, offset, hf_nbd_hnd_flags, |
1223 | 0 | ett_nbd_hnd_flags, nbd_hnd_flags, ENC_BIG_ENDIAN); |
1224 | 0 | col_set_str(pinfo->cinfo, COL_INFO, "Handshake Flags"); |
1225 | 0 | } else { |
1226 | 0 | proto_tree_add_bitmask_ret_uint64(tree, tvb, offset, hf_nbd_cli_flags, |
1227 | 0 | ett_nbd_hnd_flags, nbd_cli_flags, ENC_BIG_ENDIAN, &flags); |
1228 | 0 | col_set_str(pinfo->cinfo, COL_INFO, "Client Flags"); |
1229 | 0 | if (flags & NBD_FLAG_NO_ZEROES) { |
1230 | 0 | nbd_info->no_zeroes = true; |
1231 | 0 | } |
1232 | 0 | } |
1233 | 76 | } else if (current_state == STATE_HND_OPT && nbd_opt && nbd_opt->opt == NBD_OPT_EXPORT_NAME) { |
1234 | 0 | tcp_dissect_pdus(tvb, pinfo, tree, nbd_desegment, 10, get_nbd_export_len, dissect_nbd_export_pdu, data); |
1235 | 0 | } |
1236 | 76 | } |
1237 | 91 | } |
1238 | 91 | return tvb_captured_length(tvb); |
1239 | 91 | } |
1240 | | |
1241 | | static bool |
1242 | | dissect_nbd_tcp_heur(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void *data) |
1243 | 2.25k | { |
1244 | 2.25k | uint32_t magic, type; |
1245 | 2.25k | uint64_t magic64; |
1246 | 2.25k | conversation_t *conversation; |
1247 | 2.25k | conversation = find_or_create_conversation(pinfo); |
1248 | | |
1249 | | /* We need at least this much to tell whether this is NBD or not */ |
1250 | 2.25k | if(tvb_captured_length(tvb)<4){ |
1251 | 70 | return false; |
1252 | 70 | } |
1253 | | |
1254 | | /* Check if it looks like NBD */ |
1255 | 2.18k | magic=tvb_get_ntohl(tvb, 0); |
1256 | 2.18k | switch(magic){ |
1257 | 0 | case NBD_REQUEST_MAGIC: |
1258 | | /* requests are 28 bytes or more */ |
1259 | 0 | if(tvb_captured_length(tvb)<28){ |
1260 | 0 | return false; |
1261 | 0 | } |
1262 | | /* verify type */ |
1263 | 0 | type=tvb_get_ntohs(tvb, 6); |
1264 | 0 | if (!try_val_to_str(type, nbd_type_vals)) { |
1265 | 0 | return false; |
1266 | 0 | } |
1267 | 0 | conversation_set_dissector(conversation, nbd_handle); |
1268 | 0 | tcp_dissect_pdus(tvb, pinfo, tree, nbd_desegment, 28, get_nbd_tcp_pdu_len, dissect_nbd_tcp_pdu, data); |
1269 | 0 | return true; |
1270 | 0 | case NBD_RESPONSE_MAGIC: |
1271 | | /* responses are 16 bytes or more */ |
1272 | 0 | if(tvb_captured_length(tvb)<16){ |
1273 | 0 | return false; |
1274 | 0 | } |
1275 | 0 | conversation_set_dissector(conversation, nbd_handle); |
1276 | 0 | tcp_dissect_pdus(tvb, pinfo, tree, nbd_desegment, 16, get_nbd_tcp_pdu_len, dissect_nbd_tcp_pdu, data); |
1277 | 0 | return true; |
1278 | 0 | case NBD_STRUCTURED_REPLY_MAGIC: |
1279 | | /* structured replies are 20 bytes or more, |
1280 | | * and the length is in bytes 17-20. */ |
1281 | 0 | if(tvb_captured_length(tvb)<20){ |
1282 | 0 | return false; |
1283 | 0 | } |
1284 | 0 | conversation_set_dissector(conversation, nbd_handle); |
1285 | 0 | tcp_dissect_pdus(tvb, pinfo, tree, nbd_desegment, 20, get_nbd_tcp_pdu_len, dissect_nbd_tcp_pdu, data); |
1286 | 2.18k | default: |
1287 | 2.18k | break; |
1288 | 2.18k | } |
1289 | | |
1290 | 2.18k | if (tvb_captured_length(tvb) < 8){ |
1291 | 83 | return false; |
1292 | 83 | } |
1293 | 2.10k | magic64 = tvb_get_uint64(tvb, 0, ENC_BIG_ENDIAN); |
1294 | 2.10k | switch (magic64) { |
1295 | 1 | case NBD_HND_INIT_MAGIC: |
1296 | 3 | case NBD_HND_OPT_MAGIC: |
1297 | 10 | case NBD_HND_REPLY_MAGIC: |
1298 | 12 | case NBD_HND_OLD_MAGIC: |
1299 | 12 | conversation_set_dissector(conversation, nbd_handle); |
1300 | 12 | dissect_nbd(tvb, pinfo, tree, data); |
1301 | 12 | return true; |
1302 | 2.09k | default: |
1303 | 2.09k | break; |
1304 | 2.10k | } |
1305 | | |
1306 | 2.09k | return false; |
1307 | 2.10k | } |
1308 | | |
1309 | | void proto_register_nbd(void) |
1310 | 14 | { |
1311 | 14 | static hf_register_info hf[] = { |
1312 | 14 | { &hf_nbd_hnd_magic, |
1313 | 14 | { "Magic", "nbd.hnd.magic", FT_UINT64, BASE_HEX, |
1314 | 14 | NULL, 0x0, NULL, HFILL }}, |
1315 | 14 | { &hf_nbd_hnd_flags, |
1316 | 14 | { "Handshake Flags", "nbd.hnd.flags", FT_UINT16, BASE_HEX, |
1317 | 14 | NULL, 0x0, NULL, HFILL }}, |
1318 | 14 | { &hf_nbd_hnd_flags_fixed_new, |
1319 | 14 | { "Fixed Newstyle", "nbd.hnd.flags.fixed_new", FT_BOOLEAN, 16, |
1320 | 14 | TFS(&tfs_set_notset), 0x0001, NULL, HFILL }}, |
1321 | 14 | { &hf_nbd_hnd_flags_no_zeroes, |
1322 | 14 | { "No Zeroes", "nbd.hnd.flags.no_zeroes", FT_BOOLEAN, 16, |
1323 | 14 | TFS(&tfs_set_notset), NBD_FLAG_NO_ZEROES, NULL, HFILL }}, |
1324 | 14 | { &hf_nbd_cli_flags, |
1325 | 14 | { "Client Flags", "nbd.cli.flags", FT_UINT32, BASE_HEX, |
1326 | 14 | NULL, 0x0, NULL, HFILL }}, |
1327 | 14 | { &hf_nbd_cli_flags_fixed_new, |
1328 | 14 | { "Fixed Newstyle", "nbd.cli.flags.fixed_new", FT_BOOLEAN, 32, |
1329 | 14 | TFS(&tfs_set_notset), 0x00000001, NULL, HFILL }}, |
1330 | 14 | { &hf_nbd_cli_flags_no_zeroes, |
1331 | 14 | { "No Zeroes", "nbd.cli.flags.no_zeroes", FT_BOOLEAN, 32, |
1332 | 14 | TFS(&tfs_set_notset), NBD_FLAG_NO_ZEROES, NULL, HFILL }}, |
1333 | 14 | { &hf_nbd_hnd_opt, |
1334 | 14 | { "Option", "nbd.hnd.opt", FT_UINT32, BASE_HEX, |
1335 | 14 | VALS(nbd_opt_vals), 0x0, NULL, HFILL }}, |
1336 | 14 | { &hf_nbd_hnd_reply, |
1337 | 14 | { "Reply", "nbd.hnd.reply", FT_UINT32, BASE_HEX, |
1338 | 14 | VALS(nbd_hnd_reply_vals), 0x0, NULL, HFILL }}, |
1339 | 14 | { &hf_nbd_magic, |
1340 | 14 | { "Magic", "nbd.magic", FT_UINT32, BASE_HEX, |
1341 | 14 | NULL, 0x0, NULL, HFILL }}, |
1342 | 14 | { &hf_nbd_cmd_flags, |
1343 | 14 | { "Command Flags", "nbd.cmd.flags", FT_UINT16, BASE_HEX, |
1344 | 14 | NULL, 0x0, NULL, HFILL }}, |
1345 | 14 | { &hf_nbd_cmd_flags_fua, |
1346 | 14 | { "Forced Unit Access", "nbd.cmd.flags.fua", FT_BOOLEAN, 16, |
1347 | 14 | TFS(&tfs_set_notset), 0x0001, NULL, HFILL }}, |
1348 | 14 | { &hf_nbd_cmd_flags_no_hole, |
1349 | 14 | { "No Hole", "nbd.cmd.flags.no_hole", FT_BOOLEAN, 16, |
1350 | 14 | TFS(&tfs_set_notset), 0x0002, NULL, HFILL }}, |
1351 | 14 | { &hf_nbd_cmd_flags_df, |
1352 | 14 | { "Don't Fragment", "nbd.cmd.flags.df", FT_BOOLEAN, 16, |
1353 | 14 | TFS(&tfs_set_notset), 0x0004, NULL, HFILL }}, |
1354 | 14 | { &hf_nbd_cmd_flags_req_one, |
1355 | 14 | { "Request One", "nbd.cmd.flags.req_one", FT_BOOLEAN, 16, |
1356 | 14 | TFS(&tfs_set_notset), 0x0008, NULL, HFILL }}, |
1357 | 14 | { &hf_nbd_cmd_flags_fast_zero, |
1358 | 14 | { "Fast Zero", "nbd.cmd.flags.fast_zero", FT_BOOLEAN, 16, |
1359 | 14 | TFS(&tfs_set_notset), 0x0010, NULL, HFILL }}, |
1360 | 14 | { &hf_nbd_cmd_flags_payload_len, |
1361 | 14 | { "Payload Len", "nbd.cmd.flags.payload_len", FT_BOOLEAN, 16, |
1362 | 14 | TFS(&tfs_set_notset), 0x0020, NULL, HFILL }}, |
1363 | 14 | { &hf_nbd_reply_flags, |
1364 | 14 | { "Reply Flags", "nbd.reply.flags", FT_UINT16, BASE_HEX, |
1365 | 14 | NULL, 0x0, NULL, HFILL }}, |
1366 | 14 | { &hf_nbd_reply_flags_done, |
1367 | 14 | { "Done", "nbd.reply.flags.done", FT_BOOLEAN, 16, |
1368 | 14 | TFS(&tfs_set_notset), 0x0001, NULL, HFILL }}, |
1369 | 14 | { &hf_nbd_export_size, |
1370 | 14 | { "Export Size", "nbd.export.size", FT_UINT64, BASE_DEC|BASE_UNIT_STRING, |
1371 | 14 | UNS(&units_byte_bytes), 0x0, NULL, HFILL }}, |
1372 | 14 | { &hf_nbd_trans_flags, |
1373 | 14 | { "Transmission Flags", "nbd.export.trans.flags", FT_UINT16, BASE_HEX, |
1374 | 14 | NULL, 0x0, NULL, HFILL }}, |
1375 | 14 | { &hf_nbd_trans_flags_has_flags, |
1376 | 14 | { "Has Flags", "nbd.trans.flags.has_flags", FT_BOOLEAN, 16, |
1377 | 14 | TFS(&tfs_set_notset), 0x0001, NULL, HFILL }}, |
1378 | 14 | { &hf_nbd_trans_flags_read_only, |
1379 | 14 | { "Read Only", "nbd.trans.flags.read_only", FT_BOOLEAN, 16, |
1380 | 14 | TFS(&tfs_set_notset), 0x0002, NULL, HFILL }}, |
1381 | 14 | { &hf_nbd_trans_flags_flush, |
1382 | 14 | { "Flush", "nbd.trans.flags.flush", FT_BOOLEAN, 16, |
1383 | 14 | TFS(&tfs_supported_not_supported), 0x0004, NULL, HFILL }}, |
1384 | 14 | { &hf_nbd_trans_flags_fua, |
1385 | 14 | { "Forced Unit Access", "nbd.trans.flags.fua", FT_BOOLEAN, 16, |
1386 | 14 | TFS(&tfs_supported_not_supported), 0x0008, NULL, HFILL }}, |
1387 | 14 | { &hf_nbd_trans_flags_rotational, |
1388 | 14 | { "Rotational", "nbd.trans.flags.rotational", FT_BOOLEAN, 16, |
1389 | 14 | TFS(&tfs_set_notset), 0x0010, NULL, HFILL }}, |
1390 | 14 | { &hf_nbd_trans_flags_trim, |
1391 | 14 | { "Trim", "nbd.trans.flags.trim", FT_BOOLEAN, 16, |
1392 | 14 | TFS(&tfs_supported_not_supported), 0x0020, NULL, HFILL }}, |
1393 | 14 | { &hf_nbd_trans_flags_write_zeroes, |
1394 | 14 | { "Write Zeroes", "nbd.trans.flags.write_zeroes", FT_BOOLEAN, 16, |
1395 | 14 | TFS(&tfs_supported_not_supported), 0x0040, NULL, HFILL }}, |
1396 | 14 | { &hf_nbd_trans_flags_df, |
1397 | 14 | { "Don't Fragment", "nbd.trans.flags.df", FT_BOOLEAN, 16, |
1398 | 14 | TFS(&tfs_supported_not_supported), 0x0080, NULL, HFILL }}, |
1399 | 14 | { &hf_nbd_trans_flags_multi_conn, |
1400 | 14 | { "Multiple Connections", "nbd.trans.flags.multi_conn", FT_BOOLEAN, 16, |
1401 | 14 | TFS(&tfs_supported_not_supported), 0x0100, NULL, HFILL }}, |
1402 | 14 | { &hf_nbd_trans_flags_resize, |
1403 | 14 | { "Resize", "nbd.trans.flags.resize", FT_BOOLEAN, 16, |
1404 | 14 | TFS(&tfs_supported_not_supported), 0x0200, NULL, HFILL }}, |
1405 | 14 | { &hf_nbd_trans_flags_cache, |
1406 | 14 | { "Cache", "nbd.trans.flags.cache", FT_BOOLEAN, 16, |
1407 | 14 | TFS(&tfs_supported_not_supported), 0x0400, NULL, HFILL }}, |
1408 | 14 | { &hf_nbd_trans_flags_fast_zero, |
1409 | 14 | { "Fast Zeroes", "nbd.trans.flags.fast_zero", FT_BOOLEAN, 16, |
1410 | 14 | TFS(&tfs_supported_not_supported), 0x0800, NULL, HFILL }}, |
1411 | 14 | { &hf_nbd_trans_flags_block_status_payload, |
1412 | 14 | { "Block Status Payload", "nbd.trans.flags.block_status_payload", FT_BOOLEAN, 16, |
1413 | 14 | TFS(&tfs_supported_not_supported), 0x1000, NULL, HFILL }}, |
1414 | 14 | { &hf_nbd_reserved, |
1415 | 14 | { "Reserved (Zeroes)", "nbd.reserved", FT_BYTES, BASE_NONE, |
1416 | 14 | NULL, 0x0, NULL, HFILL }}, |
1417 | 14 | { &hf_nbd_type, |
1418 | 14 | { "Type", "nbd.type", FT_UINT16, BASE_DEC, |
1419 | 14 | VALS(nbd_type_vals), 0x0, NULL, HFILL }}, |
1420 | 14 | { &hf_nbd_reply_type, |
1421 | 14 | { "Reply Type", "nbd.reply.type", FT_UINT16, BASE_DEC, |
1422 | 14 | VALS(nbd_reply_type_vals), 0x0, NULL, HFILL }}, |
1423 | 14 | { &hf_nbd_error, |
1424 | 14 | { "Error", "nbd.error", FT_UINT32, BASE_DEC, |
1425 | 14 | VALS(nbd_error_vals), 0x0, NULL, HFILL }}, |
1426 | 14 | { &hf_nbd_len, |
1427 | 14 | { "Length", "nbd.len", FT_UINT32, BASE_DEC, |
1428 | 14 | NULL, 0x0, NULL, HFILL }}, |
1429 | 14 | { &hf_nbd_handle, |
1430 | 14 | { "Handle", "nbd.handle", FT_UINT64, BASE_HEX, |
1431 | 14 | NULL, 0x0, NULL, HFILL }}, |
1432 | 14 | { &hf_nbd_from, |
1433 | 14 | { "From", "nbd.from", FT_UINT64, BASE_HEX, |
1434 | 14 | NULL, 0x0, NULL, HFILL }}, |
1435 | 14 | { &hf_nbd_response_in, |
1436 | 14 | { "Response In", "nbd.response_in", FT_FRAMENUM, BASE_NONE, |
1437 | 14 | FRAMENUM_TYPE(FT_FRAMENUM_RESPONSE), 0x0, "The response to this NBD request is in this frame", HFILL }}, |
1438 | 14 | { &hf_nbd_response_to, |
1439 | 14 | { "Request In", "nbd.response_to", FT_FRAMENUM, BASE_NONE, |
1440 | 14 | FRAMENUM_TYPE(FT_FRAMENUM_REQUEST), 0x0, "This is a response to the NBD request in this frame", HFILL }}, |
1441 | 14 | { &hf_nbd_time, |
1442 | 14 | { "Time", "nbd.time", FT_RELATIVE_TIME, BASE_NONE, |
1443 | 14 | NULL, 0x0, "The time between the Call and the Reply", HFILL }}, |
1444 | | |
1445 | 14 | { &hf_nbd_export_name_len, |
1446 | 14 | { "Export Name Length", "nbd.export.name.len", FT_UINT32, BASE_DEC, |
1447 | 14 | NULL, 0x0, NULL, HFILL }}, |
1448 | 14 | { &hf_nbd_export_name, |
1449 | 14 | { "Export Name", "nbd.export.name", FT_STRING, BASE_NONE, |
1450 | 14 | NULL, 0x0, NULL, HFILL }}, |
1451 | 14 | { &hf_nbd_info_num, |
1452 | 14 | { "Number of Information Requests", "nbd.info.num", FT_UINT16, BASE_DEC, |
1453 | 14 | NULL, 0x0, NULL, HFILL }}, |
1454 | 14 | { &hf_nbd_info, |
1455 | 14 | { "Information Type", "nbd.info", FT_UINT16, BASE_DEC, |
1456 | 14 | VALS(nbd_info_vals), 0x0, NULL, HFILL }}, |
1457 | 14 | { &hf_nbd_query_num, |
1458 | 14 | { "Number of Queries", "nbd.query.num", FT_UINT32, BASE_DEC, |
1459 | 14 | NULL, 0x0, NULL, HFILL }}, |
1460 | 14 | { &hf_nbd_query, |
1461 | 14 | { "Query", "nbd.query", FT_UINT_STRING, BASE_NONE, |
1462 | 14 | NULL, 0x0, NULL, HFILL }}, |
1463 | 14 | { &hf_nbd_export_description, |
1464 | 14 | { "Export Description", "nbd.export.description", FT_STRING, BASE_NONE, |
1465 | 14 | NULL, 0x0, NULL, HFILL }}, |
1466 | 14 | { &hf_nbd_block_size_min, |
1467 | 14 | { "Minimum Block Size", "nbd.block_size.min", FT_UINT32, BASE_DEC, |
1468 | 14 | NULL, 0x0, NULL, HFILL }}, |
1469 | 14 | { &hf_nbd_block_size_prefer, |
1470 | 14 | { "Preferred Block Size", "nbd.block_size.prefer", FT_UINT32, BASE_DEC, |
1471 | 14 | NULL, 0x0, NULL, HFILL }}, |
1472 | 14 | { &hf_nbd_payload_size_max, |
1473 | 14 | { "Maximum Payload Size", "nbd.payload_size.max", FT_UINT32, BASE_DEC, |
1474 | 14 | NULL, 0x0, NULL, HFILL }}, |
1475 | 14 | { &hf_nbd_meta_context_id, |
1476 | 14 | { "Metadata Context ID", "nbd.meta_context.id", FT_UINT32, BASE_DEC, |
1477 | 14 | NULL, 0x0, NULL, HFILL }}, |
1478 | 14 | { &hf_nbd_meta_context_name, |
1479 | 14 | { "Metadata Context Name", "nbd.meta_context.name", FT_STRING, BASE_NONE, |
1480 | 14 | NULL, 0x0, NULL, HFILL }}, |
1481 | 14 | { &hf_nbd_error_msg_len, |
1482 | 14 | { "Message Length", "nbd.error_msg.len", FT_UINT16, BASE_DEC, |
1483 | 14 | NULL, 0x0, NULL, HFILL }}, |
1484 | 14 | { &hf_nbd_error_msg, |
1485 | 14 | { "Error Message", "nbd.error_msg", FT_STRING, BASE_NONE, |
1486 | 14 | NULL, 0x0, NULL, HFILL }}, |
1487 | 14 | { &hf_nbd_data, |
1488 | 14 | { "Data", "nbd.data", FT_BYTES, BASE_NONE, |
1489 | 14 | NULL, 0x0, NULL, HFILL }}, |
1490 | 14 | { &hf_nbd_hole_size, |
1491 | 14 | { "Hole Size", "nbd.hole_size", FT_UINT32, BASE_DEC|BASE_UNIT_STRING, |
1492 | 14 | UNS(&units_byte_bytes), 0x0, NULL, HFILL }}, |
1493 | 14 | { &hf_nbd_status_flags, |
1494 | 14 | { "Block Status Flags", "nbd.status_flags", FT_UINT32, BASE_DEC, |
1495 | 14 | NULL, 0x0, "Status flags as defined by metadata context", HFILL }}, |
1496 | | |
1497 | 14 | }; |
1498 | | |
1499 | | |
1500 | 14 | static int *ett[] = { |
1501 | 14 | &ett_nbd, |
1502 | 14 | &ett_nbd_hnd_flags, |
1503 | 14 | &ett_nbd_cli_flags, |
1504 | 14 | &ett_nbd_cmd_flags, |
1505 | 14 | &ett_nbd_reply_flags, |
1506 | 14 | &ett_nbd_trans_flags, |
1507 | 14 | }; |
1508 | | |
1509 | 14 | static ei_register_info ei[] = { |
1510 | 14 | { &ei_nbd_hnd_reply_error, {"nbd.hnd.reply.error", PI_RESPONSE_CODE, PI_NOTE, "Reply Error", EXPFILL }}, |
1511 | 14 | { &ei_nbd_unexpected_data, {"nbd.data.unexpected", PI_UNDECODED, PI_WARN, "Unexpected data", EXPFILL }}, |
1512 | 14 | }; |
1513 | | |
1514 | 14 | module_t *nbd_module; |
1515 | 14 | expert_module_t *expert_nbd; |
1516 | | |
1517 | 14 | proto_nbd = proto_register_protocol("Network Block Device", |
1518 | 14 | "NBD", "nbd"); |
1519 | 14 | proto_register_field_array(proto_nbd, hf, array_length(hf)); |
1520 | 14 | proto_register_subtree_array(ett, array_length(ett)); |
1521 | 14 | expert_nbd = expert_register_protocol(proto_nbd); |
1522 | 14 | expert_register_field_array(expert_nbd, ei, array_length(ei)); |
1523 | | |
1524 | 14 | nbd_module = prefs_register_protocol(proto_nbd, apply_nbd_prefs); |
1525 | 14 | prefs_register_bool_preference(nbd_module, "desegment_nbd_messages", |
1526 | 14 | "Reassemble NBD messages spanning multiple TCP segments", |
1527 | 14 | "Whether the NBD dissector should reassemble messages spanning multiple TCP segments." |
1528 | 14 | " To use this option, you must also enable \"Allow subdissectors to reassemble TCP streams\" in the TCP protocol settings", |
1529 | 14 | &nbd_desegment); |
1530 | | |
1531 | 14 | nbd_handle = register_dissector("nbd", dissect_nbd, proto_nbd); |
1532 | 14 | } |
1533 | | |
1534 | | static void |
1535 | | apply_nbd_prefs(void) |
1536 | 14 | { |
1537 | | // XXX - There should be a reset_uint_range ? |
1538 | 14 | dissector_delete_uint_range("tls.port", nbd_port_range, nbd_handle); |
1539 | 14 | nbd_port_range = prefs_get_range_value("NBD", "tcp.port"); |
1540 | 14 | dissector_add_uint_range("tls.port", nbd_port_range, nbd_handle); |
1541 | 14 | } |
1542 | | |
1543 | | void |
1544 | | proto_reg_handoff_nbd(void) |
1545 | 14 | { |
1546 | 14 | heur_dissector_add("tcp", dissect_nbd_tcp_heur, "NBD over TCP", "nbd_tcp", proto_nbd, HEURISTIC_ENABLE); |
1547 | 14 | dissector_add_uint_range_with_preference("tcp.port", NBD_TCP_PORTS, nbd_handle); |
1548 | 14 | tls_handle = find_dissector_add_dependency("tls", proto_nbd); |
1549 | 14 | apply_nbd_prefs(); |
1550 | 14 | } |
1551 | | |
1552 | | /* |
1553 | | * Editor modelines - https://www.wireshark.org/tools/modelines.html |
1554 | | * |
1555 | | * Local variables: |
1556 | | * c-basic-offset: 8 |
1557 | | * tab-width: 8 |
1558 | | * indent-tabs-mode: t |
1559 | | * End: |
1560 | | * |
1561 | | * vi: set shiftwidth=8 tabstop=8 noexpandtab: |
1562 | | * :indentSize=8:tabSize=8:noTabs=false: |
1563 | | */ |