Revert "TODO epan/dissectors/asn1/kerberos/packet-kerberos-template.c new GSS flags"
[wireshark-sm.git] / epan / dissectors / packet-nbd.c
blobcdebd24add8b1606d2cfb7791df60e7c0cd1f377
1 /* packet-nbd.c
2 * Routines for Network Block Device (NBD) dissection.
4 * https://github.com/NetworkBlockDevice/nbd/blob/master/doc/proto.md
6 * Ronnie sahlberg 2006
8 * Wireshark - Network traffic analyzer
9 * By Gerald Combs <gerald@wireshark.org>
10 * Copyright 1998 Gerald Combs
12 * SPDX-License-Identifier: GPL-2.0-or-later
15 #include "config.h"
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"
26 void proto_register_nbd(void);
27 void proto_reg_handoff_nbd(void);
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;
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;
99 static expert_field ei_nbd_hnd_reply_error;
100 static expert_field ei_nbd_unexpected_data;
102 static dissector_handle_t nbd_handle;
103 static dissector_handle_t tls_handle;
105 static bool nbd_desegment = true;
107 static void apply_nbd_prefs(void);
109 #define NBD_TCP_PORTS "10809" /* IANA-registered */
111 static range_t *nbd_port_range;
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;
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;
142 #define NBD_HND_INIT_MAGIC 0x4e42444d41474943 // "NBDMAGIC"
143 #define NBD_HND_OPT_MAGIC 0x49484156454F5054 // "IHAVEOPT"
144 #define NBD_HND_REPLY_MAGIC 0x03e889045565a9
145 #define NBD_HND_OLD_MAGIC 0x00420281861253
147 #define NBD_REQUEST_MAGIC 0x25609513
148 #define NBD_RESPONSE_MAGIC 0x67446698
149 #define NBD_STRUCTURED_REPLY_MAGIC 0x668e33ef
151 #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 #define NBD_OPT_STARTTLS 5
156 #define NBD_OPT_INFO 6
157 #define NBD_OPT_GO 7
158 #define NBD_OPT_STRUCTURED_REPLY 8
159 #define NBD_OPT_LIST_META_CONTEXT 9
160 #define NBD_OPT_SET_META_CONTEXT 10
161 #define NBD_OPT_EXTENDED_HEADERS 11
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}
178 #define NBD_INFO_EXPORT 0
179 #define NBD_INFO_NAME 1
180 #define NBD_INFO_DESCRIPTION 2
181 #define NBD_INFO_BLOCK_SIZE 3
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}
191 #define NBD_REP_ACK 1
192 #define NBD_REP_SERVER 2
193 #define NBD_REP_INFO 3
194 #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)
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}
224 #define NBD_CMD_READ 0
225 #define NBD_CMD_WRITE 1
226 #define NBD_CMD_DISC 2
227 #define NBD_CMD_FLUSH 3
228 #define NBD_CMD_TRIM 4
229 #define NBD_CMD_CACHE 5
230 #define NBD_CMD_WRITE_ZEROES 6
231 #define NBD_CMD_BLOCK_STATUS 7
232 #define NBD_CMD_RESIZE 8
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}
247 #define NBD_REPLY_NONE 0
248 #define NBD_REPLY_OFFSET_DATA 1
249 #define NBD_REPLY_OFFSET_HOLE 2
250 #define NBD_REPLY_BLOCK_STATUS 5
251 #define NBD_REPLY_BLOCK_STATUS_EXT 6
252 #define NBD_REPLY_ERROR 32769
253 #define NBD_REPLY_ERROR_OFFSET 32770
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}
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
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}
289 #define NBD_FLAG_NO_ZEROES 0x0002
291 static bool
292 nbd_from_server(packet_info *pinfo)
294 if (value_is_in_range(nbd_port_range, pinfo->srcport)) {
295 return true;
296 } else if (value_is_in_range(nbd_port_range, pinfo->destport)) {
297 return false;
299 return false;
302 static nbd_conv_info_t*
303 get_nbd_conv_info(packet_info *pinfo)
305 conversation_t *conversation;
306 nbd_conv_info_t *nbd_info;
308 conversation = find_or_create_conversation(pinfo);
311 * Do we already have a state structure for this conv
313 nbd_info = (nbd_conv_info_t *)conversation_get_proto_data(conversation, proto_nbd);
315 if (!nbd_info) {
316 /* No. Attach that information to the conversation, and add
317 * it to the list of information structures.
319 nbd_info = wmem_new(wmem_file_scope(), nbd_conv_info_t);
320 nbd_info->no_zeroes = false;
321 nbd_info->state = wmem_tree_new(wmem_file_scope());
322 nbd_info->opts = wmem_tree_new(wmem_file_scope());
323 nbd_info->unacked_pdus = wmem_tree_new(wmem_file_scope());
324 nbd_info->acked_pdus = wmem_tree_new(wmem_file_scope());
326 conversation_add_proto_data(conversation, proto_nbd, nbd_info);
328 return nbd_info;
331 static void
332 nbd_set_state(packet_info *pinfo, nbd_state_e state)
334 nbd_conv_info_t *nbd_info;
335 nbd_state_e current_state;
337 nbd_info = get_nbd_conv_info(pinfo);
338 current_state = (nbd_state_e)GPOINTER_TO_UINT(wmem_tree_lookup32_le(nbd_info->state, pinfo->num));
339 if (current_state != state) {
340 wmem_tree_insert32(nbd_info->state, pinfo->num, GUINT_TO_POINTER(state));
344 /* This function will try to determine the complete size of a PDU
345 * based on the information in the header.
347 static unsigned
348 get_nbd_tcp_pdu_len(packet_info *pinfo, tvbuff_t *tvb, int offset, void *data _U_)
350 uint32_t magic, type, packet;
351 conversation_t *conversation;
352 nbd_conv_info_t *nbd_info;
353 nbd_transaction_t *nbd_trans=NULL;
354 wmem_tree_key_t hkey[3];
355 uint32_t handle[2];
357 magic=tvb_get_ntohl(tvb, offset);
359 switch(magic){
360 case NBD_REQUEST_MAGIC:
361 type=tvb_get_ntohs(tvb, offset+6);
362 switch(type){
363 case NBD_CMD_WRITE:
364 return tvb_get_ntohl(tvb, offset+24)+28;
365 default:
367 * NB: Length field should always be present (and zero)
368 * for other types too.
370 return 28;
372 case NBD_RESPONSE_MAGIC:
374 * Do we have a conversation for this connection?
376 conversation = find_conversation_pinfo(pinfo, 0);
377 if (conversation == NULL) {
378 /* No, so just return the rest of the current packet */
379 return tvb_captured_length(tvb);
382 * Do we have a state structure for this conv
384 nbd_info = (nbd_conv_info_t *)conversation_get_proto_data(conversation, proto_nbd);
385 if (!nbd_info) {
386 /* No, so just return the rest of the current packet */
387 return tvb_captured_length(tvb);
389 if(!pinfo->fd->visited){
391 * Do we have a state structure for this transaction
393 handle[0]=tvb_get_ntohl(tvb, offset+8);
394 handle[1]=tvb_get_ntohl(tvb, offset+12);
395 hkey[0].length=2;
396 hkey[0].key=handle;
397 hkey[1].length=0;
398 nbd_trans=(nbd_transaction_t *)wmem_tree_lookup32_array(nbd_info->unacked_pdus, hkey);
399 if(!nbd_trans){
400 /* No, so just return the rest of the current packet */
401 return tvb_captured_length(tvb);
403 } else {
405 * Do we have a state structure for this transaction
407 handle[0]=tvb_get_ntohl(tvb, offset+8);
408 handle[1]=tvb_get_ntohl(tvb, offset+12);
409 packet=pinfo->num;
410 hkey[0].length=1;
411 hkey[0].key=&packet;
412 hkey[1].length=2;
413 hkey[1].key=handle;
414 hkey[2].length=0;
415 nbd_trans=(nbd_transaction_t *)wmem_tree_lookup32_array(nbd_info->acked_pdus, hkey);
416 if(!nbd_trans){
417 /* No, so just return the rest of the current packet */
418 return tvb_captured_length(tvb);
421 /* If this is a read response we must add the datalen to
422 * the pdu size
424 if(nbd_trans->type==NBD_CMD_READ){
425 return 16+nbd_trans->datalen;
426 } else {
427 return 16;
429 case NBD_STRUCTURED_REPLY_MAGIC:
430 return tvb_get_ntohl(tvb, offset+16)+20;
431 default:
432 break;
435 /* Did not really look like a NBD packet after all */
436 return 0;
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,
449 static int * const nbd_reply_flags[] = {
450 &hf_nbd_reply_flags_done,
451 NULL,
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,
471 static int
472 dissect_nbd_structured_reply(tvbuff_t *tvb, packet_info *pinfo _U_, proto_tree *tree, unsigned type)
474 proto_item *item;
475 int offset = 0;
476 uint32_t len;
478 switch (type) {
479 case NBD_REPLY_OFFSET_DATA:
480 proto_tree_add_item(tree, hf_nbd_from, tvb, offset, 8, ENC_BIG_ENDIAN);
481 offset += 8;
483 proto_tree_add_item(tree, hf_nbd_data, tvb, offset, -1, ENC_NA);
484 offset = tvb_reported_length(tvb);
485 break;
487 case NBD_REPLY_OFFSET_HOLE:
488 proto_tree_add_item(tree, hf_nbd_from, tvb, offset, 8, ENC_BIG_ENDIAN);
489 offset += 8;
491 proto_tree_add_item(tree, hf_nbd_hole_size, tvb, offset, 4, ENC_NA);
492 offset = tvb_reported_length(tvb);
493 break;
495 case NBD_REPLY_BLOCK_STATUS:
496 proto_tree_add_item(tree, hf_nbd_meta_context_id, tvb, offset, 4, ENC_BIG_ENDIAN);
497 offset += 4;
498 while (tvb_reported_length_remaining(tvb, offset)) {
499 proto_tree_add_item(tree, hf_nbd_len, tvb, offset, 4, ENC_BIG_ENDIAN);
500 offset += 4;
501 proto_tree_add_item(tree, hf_nbd_status_flags, tvb, offset, 4, ENC_BIG_ENDIAN);
502 offset += 4;
504 break;
506 case NBD_REPLY_ERROR:
507 proto_tree_add_item(tree, hf_nbd_error, tvb, offset, 4, ENC_BIG_ENDIAN);
508 offset += 4;
509 proto_tree_add_item_ret_uint(tree, hf_nbd_error_msg_len, tvb, offset, 2, ENC_BIG_ENDIAN, &len);
510 offset += 2;
511 proto_tree_add_item(tree, hf_nbd_error_msg, tvb, offset, len, ENC_UTF_8);
512 break;
514 case NBD_REPLY_ERROR_OFFSET:
515 proto_tree_add_item(tree, hf_nbd_error, tvb, offset, 4, ENC_BIG_ENDIAN);
516 offset += 4;
517 proto_tree_add_item_ret_uint(tree, hf_nbd_error_msg_len, tvb, offset, 2, ENC_BIG_ENDIAN, &len);
518 offset += 2;
519 proto_tree_add_item(tree, hf_nbd_error_msg, tvb, offset, len, ENC_UTF_8);
520 offset += len;
521 proto_tree_add_item(tree, hf_nbd_from, tvb, offset, 8, ENC_BIG_ENDIAN);
522 offset += 8;
525 if (tvb_reported_length_remaining(tvb, offset)) {
526 item = proto_tree_add_item(tree, hf_nbd_data, tvb, offset, -1, ENC_NA);
527 expert_add_info(pinfo, item, &ei_nbd_unexpected_data);
530 return tvb_reported_length(tvb);
533 static int
534 dissect_nbd_tcp_pdu(tvbuff_t *tvb, packet_info *pinfo, proto_tree *parent_tree, void* data _U_)
536 uint32_t magic, error, packet, data_len, type;
537 uint32_t handle[2];
538 uint64_t from;
539 int offset=0;
540 proto_tree *tree=NULL;
541 proto_item *item=NULL;
542 nbd_conv_info_t *nbd_info;
543 nbd_transaction_t *nbd_trans=NULL;
544 wmem_tree_key_t hkey[3];
546 col_set_str(pinfo->cinfo, COL_PROTOCOL, "NBD");
548 col_clear(pinfo->cinfo, COL_INFO);
550 item = proto_tree_add_item(parent_tree, proto_nbd, tvb, 0, -1, ENC_NA);
551 tree = proto_item_add_subtree(item, ett_nbd);
554 magic=tvb_get_ntohl(tvb, offset);
555 proto_tree_add_item(tree, hf_nbd_magic, tvb, offset, 4, ENC_BIG_ENDIAN);
556 offset+=4;
559 /* grab what we need to do the request/response matching */
560 switch(magic){
561 case NBD_REQUEST_MAGIC:
562 case NBD_RESPONSE_MAGIC:
563 case NBD_STRUCTURED_REPLY_MAGIC:
564 handle[0]=tvb_get_ntohl(tvb, offset+4);
565 handle[1]=tvb_get_ntohl(tvb, offset+8);
566 break;
567 default:
568 return 4;
571 nbd_info = get_nbd_conv_info(pinfo);
572 if(!pinfo->fd->visited){
573 switch (magic) {
574 case NBD_REQUEST_MAGIC:
575 /* This is a request */
576 nbd_trans=wmem_new(wmem_file_scope(), nbd_transaction_t);
577 nbd_trans->req_frame=pinfo->num;
578 nbd_trans->rep_frame=0;
579 nbd_trans->req_time=pinfo->abs_ts;
580 nbd_trans->type=tvb_get_ntohl(tvb, offset);
581 nbd_trans->datalen=tvb_get_ntohl(tvb, offset+20);
583 hkey[0].length=2;
584 hkey[0].key=handle;
585 hkey[1].length=0;
587 wmem_tree_insert32_array(nbd_info->unacked_pdus, hkey, (void *)nbd_trans);
588 break;
590 case NBD_RESPONSE_MAGIC:
591 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.
597 hkey[0].length=2;
598 hkey[0].key=handle;
599 hkey[1].length=0;
601 nbd_trans=(nbd_transaction_t *)wmem_tree_lookup32_array(nbd_info->unacked_pdus, hkey);
602 if(nbd_trans){
603 nbd_trans->rep_frame=pinfo->num;
605 hkey[0].length=1;
606 hkey[0].key=&nbd_trans->rep_frame;
607 hkey[1].length=2;
608 hkey[1].key=handle;
609 hkey[2].length=0;
610 wmem_tree_insert32_array(nbd_info->acked_pdus, hkey, (void *)nbd_trans);
611 hkey[0].length=1;
612 hkey[0].key=&nbd_trans->req_frame;
613 hkey[1].length=2;
614 hkey[1].key=handle;
615 hkey[2].length=0;
616 wmem_tree_insert32_array(nbd_info->acked_pdus, hkey, (void *)nbd_trans);
618 break;
619 default:
620 ws_assert_not_reached();
622 } else {
623 packet=pinfo->num;
624 hkey[0].length=1;
625 hkey[0].key=&packet;
626 hkey[1].length=2;
627 hkey[1].key=handle;
628 hkey[2].length=0;
630 nbd_trans=(nbd_transaction_t *)wmem_tree_lookup32_array(nbd_info->acked_pdus, hkey);
632 /* The bloody handles are reused !!! even though they are 64 bits.
633 * So we must verify we got the "correct" one
635 if( (magic==NBD_RESPONSE_MAGIC || magic==NBD_STRUCTURED_REPLY_MAGIC)
636 && (nbd_trans)
637 && (pinfo->num<nbd_trans->req_frame) ){
638 /* must have been the wrong one */
639 nbd_trans=NULL;
642 if(!nbd_trans){
643 /* create a "fake" nbd_trans structure */
644 nbd_trans=wmem_new(pinfo->pool, nbd_transaction_t);
645 nbd_trans->req_frame=0;
646 nbd_trans->rep_frame=0;
647 nbd_trans->req_time=pinfo->abs_ts;
648 if (magic == NBD_REQUEST_MAGIC) {
649 nbd_trans->type=tvb_get_ntohl(tvb, offset);
650 nbd_trans->datalen=tvb_get_ntohl(tvb, offset+20);
651 } else {
652 nbd_trans->type=0xffff;
653 nbd_trans->datalen=0;
657 /* print state tracking in the tree */
658 switch (magic) {
659 case NBD_REQUEST_MAGIC:
660 /* This is a request */
661 if(nbd_trans->rep_frame){
662 proto_item *it;
664 it=proto_tree_add_uint(tree, hf_nbd_response_in, tvb, 0, 0, nbd_trans->rep_frame);
665 proto_item_set_generated(it);
667 break;
668 case NBD_RESPONSE_MAGIC:
669 case NBD_STRUCTURED_REPLY_MAGIC:
670 /* This is a reply */
671 if(nbd_trans->req_frame){
672 proto_item *it;
673 nstime_t ns;
675 it=proto_tree_add_uint(tree, hf_nbd_response_to, tvb, 0, 0, nbd_trans->req_frame);
676 proto_item_set_generated(it);
678 nstime_delta(&ns, &pinfo->abs_ts, &nbd_trans->req_time);
679 it=proto_tree_add_time(tree, hf_nbd_time, tvb, 0, 0, &ns);
680 proto_item_set_generated(it);
685 switch(magic){
686 case NBD_REQUEST_MAGIC:
687 proto_tree_add_bitmask(tree, tvb, offset, hf_nbd_cmd_flags,
688 ett_nbd_cmd_flags, nbd_cmd_flags, ENC_BIG_ENDIAN);
689 offset+=2;
691 proto_tree_add_item(tree, hf_nbd_type, tvb, offset, 2, ENC_BIG_ENDIAN);
692 offset+=2;
694 proto_tree_add_item(tree, hf_nbd_handle, tvb, offset, 8, ENC_BIG_ENDIAN);
695 offset+=8;
697 from=tvb_get_ntoh64(tvb, offset);
698 proto_tree_add_item(tree, hf_nbd_from, tvb, offset, 8, ENC_BIG_ENDIAN);
699 offset+=8;
701 proto_tree_add_item(tree, hf_nbd_len, tvb, offset, 4, ENC_BIG_ENDIAN);
702 offset+=4;
704 col_add_fstr(pinfo->cinfo, COL_INFO, "%s Request", val_to_str(nbd_trans->type, nbd_type_vals, "Unknown (%d)"));
705 switch(nbd_trans->type){
706 case NBD_CMD_WRITE:
707 case NBD_CMD_READ:
708 case NBD_CMD_TRIM:
709 case NBD_CMD_CACHE:
710 case NBD_CMD_WRITE_ZEROES:
711 case NBD_CMD_BLOCK_STATUS:
712 col_append_fstr(pinfo->cinfo, COL_INFO, " Offset:0x%" PRIx64 " Length:%d", from, nbd_trans->datalen);
713 break;
716 if(nbd_trans->type==NBD_CMD_WRITE){
717 proto_tree_add_item(tree, hf_nbd_data, tvb, offset, nbd_trans->datalen, ENC_NA);
719 break;
720 case NBD_RESPONSE_MAGIC:
721 item=proto_tree_add_uint(tree, hf_nbd_type, tvb, 0, 0, nbd_trans->type);
722 proto_item_set_generated(item);
724 error=tvb_get_ntohl(tvb, offset);
725 proto_tree_add_item(tree, hf_nbd_error, tvb, offset, 4, ENC_BIG_ENDIAN);
726 offset+=4;
728 proto_tree_add_item(tree, hf_nbd_handle, tvb, offset, 8, ENC_BIG_ENDIAN);
729 offset+=8;
731 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)"));
733 if(nbd_trans->type==NBD_CMD_READ){
734 proto_tree_add_item(tree, hf_nbd_data, tvb, offset, nbd_trans->datalen, ENC_NA);
736 break;
737 case NBD_STRUCTURED_REPLY_MAGIC:
738 /* structured reply flags */
739 proto_tree_add_bitmask(tree, tvb, offset, hf_nbd_reply_flags,
740 ett_nbd_reply_flags, nbd_reply_flags, ENC_BIG_ENDIAN);
741 offset+=2;
742 item = proto_tree_add_item_ret_uint(tree, hf_nbd_reply_type, tvb, offset, 2, ENC_BIG_ENDIAN, &type);
743 if (type & 0x8000) {
744 expert_add_info(pinfo, item, &ei_nbd_hnd_reply_error);
746 offset+=2;
748 proto_tree_add_item(tree, hf_nbd_handle, tvb, offset, 8, ENC_BIG_ENDIAN);
749 offset+=8;
751 proto_tree_add_item_ret_uint(tree, hf_nbd_len, tvb, offset, 4, ENC_BIG_ENDIAN, &data_len);
752 offset+=4;
754 dissect_nbd_structured_reply(tvb_new_subset_length(tvb, offset, data_len), pinfo, tree, type);
755 /*offset += data_len; */
758 return tvb_captured_length(tvb);
761 static int
762 dissect_nbd_transmission(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void *data _U_)
764 uint32_t magic, type;
765 unsigned pdu_fixed;
767 /* We want 8 to test the type */
768 if (tvb_captured_length(tvb) < 8) {
769 return 0;
772 magic = tvb_get_ntohl(tvb, 0);
773 type = tvb_get_ntohs(tvb, 6);
775 switch(magic){
776 case NBD_REQUEST_MAGIC:
777 /* verify type */
778 if (!try_val_to_str(type, nbd_type_vals)) {
779 return 0;
781 pdu_fixed = 28;
782 break;
783 case NBD_RESPONSE_MAGIC:
784 pdu_fixed = 16;
785 break;
786 case NBD_STRUCTURED_REPLY_MAGIC:
787 /* verify type */
788 if (!try_val_to_str(type, nbd_reply_type_vals)) {
789 return 0;
791 pdu_fixed = 20;
792 break;
793 default:
794 return 0;
797 nbd_set_state(pinfo, STATE_HND_DONE);
798 tcp_dissect_pdus(tvb, pinfo, tree, nbd_desegment, pdu_fixed, get_nbd_tcp_pdu_len, dissect_nbd_tcp_pdu, data);
799 return tvb_captured_length(tvb);
802 static unsigned
803 get_nbd_opt_len(packet_info *pinfo _U_, tvbuff_t *tvb, int offset, void *data _U_)
805 unsigned pdu_len = tvb_get_uint32(tvb, offset + 12, ENC_BIG_ENDIAN);
807 return 16 + pdu_len;
810 static int
811 dissect_nbd_opt_pdu(tvbuff_t *tvb, packet_info *pinfo, proto_tree *parent_tree, void* data _U_)
813 proto_item *item;
814 proto_tree *tree;
815 int offset = 0;
816 uint32_t opt, data_len, name_len, info_num;
817 nbd_conv_info_t *nbd_info;
818 nbd_option_t *nbd_opt;
819 const uint8_t *export_name;
821 item = proto_tree_add_item(parent_tree, proto_nbd, tvb, 0, -1, ENC_NA);
822 tree = proto_item_add_subtree(item, ett_nbd);
824 proto_tree_add_item(tree, hf_nbd_hnd_magic, tvb, offset, 8, ENC_BIG_ENDIAN);
825 offset += 8;
827 nbd_info = get_nbd_conv_info(pinfo);
828 if (!PINFO_FD_VISITED(pinfo)) {
829 nbd_opt = wmem_new(wmem_file_scope(), nbd_option_t);
830 nbd_opt->req_frame=pinfo->num;
831 nbd_opt->rep_frame=0;
832 nbd_opt->req_time=pinfo->abs_ts;
833 nbd_opt->opt=tvb_get_ntohl(tvb, offset);
835 wmem_tree_insert32(nbd_info->opts, pinfo->num, (void *)nbd_opt);
836 } else {
837 nbd_opt = (nbd_option_t*)wmem_tree_lookup32(nbd_info->opts, pinfo->num);
838 if (nbd_opt && nbd_opt->rep_frame) {
839 item = proto_tree_add_uint(tree, hf_nbd_response_in, tvb, 0, 0, nbd_opt->rep_frame);
840 proto_item_set_generated(item);
844 proto_tree_add_item_ret_uint(tree, hf_nbd_hnd_opt, tvb, offset, 4, ENC_BIG_ENDIAN, &opt);
845 col_append_sep_str(pinfo->cinfo, COL_INFO, NULL, val_to_str(opt, nbd_opt_vals, "Unknown (%d)"));
847 offset += 4;
849 proto_tree_add_item_ret_uint(tree, hf_nbd_len, tvb, offset, 4, ENC_BIG_ENDIAN, &data_len);
850 offset += 4;
852 if (data_len) {
853 switch (opt) {
854 case NBD_OPT_EXPORT_NAME:
855 proto_tree_add_item_ret_string(tree, hf_nbd_export_name, tvb, offset, data_len, ENC_UTF_8, pinfo->pool, &export_name);
856 col_append_sep_str(pinfo->cinfo, COL_INFO, ":", export_name);
857 break;
858 case NBD_OPT_INFO:
859 case NBD_OPT_GO:
860 proto_tree_add_item_ret_uint(tree, hf_nbd_export_name_len, tvb, offset, 4, ENC_BIG_ENDIAN, &name_len);
861 offset += 4;
862 proto_tree_add_item(tree, hf_nbd_export_name, tvb, offset, name_len, ENC_UTF_8);
863 offset += name_len;
864 proto_tree_add_item_ret_uint(tree, hf_nbd_info_num, tvb, offset, 2, ENC_BIG_ENDIAN, &info_num);
865 offset += 2;
866 for (unsigned i = 0; i < info_num; ++i) {
867 proto_tree_add_item(tree, hf_nbd_info, tvb, offset, 2, ENC_BIG_ENDIAN);
868 offset += 2;
870 break;
871 case NBD_OPT_LIST_META_CONTEXT:
872 case NBD_OPT_SET_META_CONTEXT:
873 proto_tree_add_item_ret_uint(tree, hf_nbd_export_name_len, tvb, offset, 4, ENC_BIG_ENDIAN, &name_len);
874 offset += 4;
875 proto_tree_add_item(tree, hf_nbd_export_name, tvb, offset, name_len, ENC_UTF_8);
876 offset += name_len;
877 proto_tree_add_item_ret_uint(tree, hf_nbd_query_num, tvb, offset, 4, ENC_BIG_ENDIAN, &info_num);
878 offset += 4;
879 for (unsigned i = 0; i < info_num; ++i) {
880 proto_tree_add_item_ret_length(tree, hf_nbd_query, tvb, offset, 2, ENC_BIG_ENDIAN, &name_len);
881 offset += name_len;
883 break;
884 default:
885 proto_tree_add_item(tree, hf_nbd_data, tvb, offset, data_len, ENC_NA);
889 return tvb_captured_length(tvb);
892 static unsigned
893 get_nbd_opt_reply_len(packet_info *pinfo _U_, tvbuff_t *tvb, int offset, void *data _U_)
895 unsigned pdu_len = tvb_get_uint32(tvb, offset + 16, ENC_BIG_ENDIAN);
897 return 20 + pdu_len;
900 static int
901 dissect_nbd_opt_reply(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, unsigned type)
903 proto_item *item;
904 int offset = 0;
905 uint32_t name_len, info_type;
907 switch (type) {
908 case NBD_REP_SERVER:
909 proto_tree_add_item_ret_uint(tree, hf_nbd_export_name_len, tvb, offset, 4, ENC_BIG_ENDIAN, &name_len);
910 offset += 4;
911 proto_tree_add_item(tree, hf_nbd_export_name, tvb, offset, name_len, ENC_UTF_8);
912 offset += name_len;
913 break;
914 case NBD_REP_INFO:
915 proto_tree_add_item_ret_uint(tree, hf_nbd_info, tvb, offset, 2, ENC_BIG_ENDIAN, &info_type);
916 offset += 2;
917 switch (info_type) {
918 case NBD_INFO_EXPORT:
919 proto_tree_add_item(tree, hf_nbd_export_size, tvb, offset, 8, ENC_BIG_ENDIAN);
920 offset += 8;
922 proto_tree_add_bitmask(tree, tvb, offset, hf_nbd_trans_flags,
923 ett_nbd_trans_flags, nbd_trans_flags, ENC_BIG_ENDIAN);
924 offset += 2;
925 break;
926 case NBD_INFO_NAME:
927 proto_tree_add_item(tree, hf_nbd_export_name, tvb, offset, tvb_reported_length_remaining(tvb, offset), ENC_UTF_8);
928 offset = tvb_reported_length(tvb);
929 break;
930 case NBD_INFO_DESCRIPTION:
931 proto_tree_add_item(tree, hf_nbd_export_description, tvb, offset, tvb_reported_length_remaining(tvb, offset), ENC_UTF_8);
932 offset = tvb_reported_length(tvb);
933 break;
934 case NBD_INFO_BLOCK_SIZE:
935 proto_tree_add_item(tree, hf_nbd_block_size_min, tvb, offset, 4, ENC_BIG_ENDIAN);
936 offset += 4;
937 proto_tree_add_item(tree, hf_nbd_block_size_prefer, tvb, offset, 4, ENC_BIG_ENDIAN);
938 offset += 4;
939 proto_tree_add_item(tree, hf_nbd_payload_size_max, tvb, offset, 4, ENC_BIG_ENDIAN);
940 offset += 4;
942 break;
943 case NBD_REP_META_CONTEXT:
944 proto_tree_add_item(tree, hf_nbd_meta_context_id, tvb, offset, 4, ENC_BIG_ENDIAN);
945 offset += 4;
946 proto_tree_add_item(tree, hf_nbd_meta_context_name, tvb, offset, tvb_reported_length_remaining(tvb, offset), ENC_UTF_8);
947 offset = tvb_reported_length(tvb);
950 if (tvb_reported_length_remaining(tvb, offset)) {
951 if (type & UINT32_C(1 << 31)) {
952 proto_tree_add_item(tree, hf_nbd_error_msg, tvb, offset, -1, ENC_UTF_8);
953 } else {
954 item = proto_tree_add_item(tree, hf_nbd_data, tvb, offset, -1, ENC_NA);
955 expert_add_info(pinfo, item, &ei_nbd_unexpected_data);
959 return tvb_reported_length(tvb);
962 static int
963 dissect_nbd_opt_reply_pdu(tvbuff_t *tvb, packet_info *pinfo, proto_tree *parent_tree, void* data _U_)
965 proto_item *item, *gen_item;
966 proto_tree *tree;
967 int offset = 0;
968 uint32_t opt, reply, data_len;
969 nbd_conv_info_t *nbd_info;
970 nbd_option_t *nbd_opt;
972 item = proto_tree_add_item(parent_tree, proto_nbd, tvb, 0, -1, ENC_NA);
973 tree = proto_item_add_subtree(item, ett_nbd);
975 item = proto_tree_add_item(tree, hf_nbd_hnd_magic, tvb, offset, 8, ENC_BIG_ENDIAN);
976 offset += 8;
978 nbd_info = get_nbd_conv_info(pinfo);
979 nbd_opt = (nbd_option_t*)wmem_tree_lookup32_le(nbd_info->opts, pinfo->num);
981 proto_tree_add_item_ret_uint(tree, hf_nbd_hnd_opt, tvb, offset, 4, ENC_BIG_ENDIAN, &opt);
982 col_append_sep_str(pinfo->cinfo, COL_INFO, NULL, val_to_str(opt, nbd_opt_vals, "Unknown (%d)"));
983 offset += 4;
985 if (nbd_opt && nbd_opt->opt == opt) {
986 nbd_opt->rep_frame = pinfo->num;
988 gen_item = proto_tree_add_uint(tree, hf_nbd_response_to, tvb, 0, 0, nbd_opt->req_frame);
989 proto_item_set_generated(gen_item);
990 proto_tree_move_item(tree, item, gen_item);
991 item = gen_item;
993 nstime_t ns;
994 nstime_delta(&ns, &pinfo->abs_ts, &nbd_opt->req_time);
995 gen_item = proto_tree_add_time(tree, hf_nbd_time, tvb, 0, 0, &ns);
996 proto_item_set_generated(gen_item);
997 proto_tree_move_item(tree, item, gen_item);
1000 item = proto_tree_add_item_ret_uint(tree, hf_nbd_hnd_reply, tvb, offset, 4, ENC_BIG_ENDIAN, &reply);
1001 col_append_sep_str(pinfo->cinfo, COL_INFO, NULL, val_to_str(reply, nbd_hnd_reply_vals, "Unknown (%d)"));
1002 if (reply & UINT64_C(0x80000000)) {
1003 expert_add_info(pinfo, item, &ei_nbd_hnd_reply_error);
1005 if (opt == NBD_OPT_STARTTLS && reply == NBD_REP_ACK) {
1006 ssl_starttls_ack(tls_handle, pinfo, nbd_handle);
1009 offset += 4;
1011 proto_tree_add_item_ret_uint(tree, hf_nbd_len, tvb, offset, 4, ENC_BIG_ENDIAN, &data_len);
1012 offset += 4;
1014 dissect_nbd_opt_reply(tvb_new_subset_length(tvb, offset, data_len), pinfo, tree, reply);
1015 return tvb_captured_length(tvb);
1018 static unsigned
1019 get_nbd_export_len(packet_info *pinfo, tvbuff_t *tvb _U_, int offset _U_, void *data _U_)
1021 nbd_conv_info_t *nbd_info;
1023 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 return 10 + (nbd_info->no_zeroes ? 0 : 124);
1029 static int
1030 dissect_nbd_export_pdu(tvbuff_t *tvb, packet_info *pinfo, proto_tree *parent_tree, void* data _U_)
1032 proto_item *item;
1033 proto_tree *tree;
1034 int offset = 0;
1035 nbd_conv_info_t *nbd_info;
1036 nbd_option_t *nbd_opt;
1038 item = proto_tree_add_item(parent_tree, proto_nbd, tvb, 0, -1, ENC_NA);
1039 tree = proto_item_add_subtree(item, ett_nbd);
1041 nbd_info = get_nbd_conv_info(pinfo);
1042 nbd_opt = (nbd_option_t*)wmem_tree_lookup32_le(nbd_info->opts, pinfo->num);
1044 if (nbd_opt && nbd_opt->opt == NBD_OPT_EXPORT_NAME) {
1045 nbd_opt->rep_frame = pinfo->num;
1047 item = proto_tree_add_uint(tree, hf_nbd_response_to, tvb, 0, 0, nbd_opt->req_frame);
1048 proto_item_set_generated(item);
1050 nstime_t ns;
1051 nstime_delta(&ns, &pinfo->abs_ts, &nbd_opt->req_time);
1052 item = proto_tree_add_time(tree, hf_nbd_time, tvb, 0, 0, &ns);
1053 proto_item_set_generated(item);
1055 item = proto_tree_add_uint(tree, hf_nbd_hnd_opt, tvb, 0, 0, nbd_opt->opt);
1056 proto_item_set_generated(item);
1059 proto_tree_add_item(tree, hf_nbd_export_size, tvb, offset, 8, ENC_BIG_ENDIAN);
1060 offset += 8;
1062 proto_tree_add_bitmask(tree, tvb, offset, hf_nbd_trans_flags,
1063 ett_nbd_trans_flags, nbd_trans_flags, ENC_BIG_ENDIAN);
1064 col_set_str(pinfo->cinfo, COL_INFO, "Transmission Flags");
1065 offset += 2;
1067 if (tvb_captured_length_remaining(tvb, offset)) {
1068 proto_tree_add_item(tree, hf_nbd_reserved, tvb, offset, -1, ENC_NA);
1071 return tvb_captured_length(tvb);
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.
1078 static int * const nbd_hnd_flags[] = {
1079 &hf_nbd_hnd_flags_fixed_new,
1080 &hf_nbd_hnd_flags_no_zeroes,
1081 NULL,
1084 static int * const nbd_cli_flags[] = {
1085 &hf_nbd_cli_flags_fixed_new,
1086 &hf_nbd_cli_flags_no_zeroes,
1087 NULL,
1090 static unsigned
1091 get_nbd_old_len(packet_info *pinfo _U_, tvbuff_t *tvb _U_, int offset _U_, void *data _U_)
1093 return 144; // 8 + 8 + 4 + 124
1096 static int
1097 dissect_nbd_old_pdu(tvbuff_t *tvb, packet_info *pinfo, proto_tree *parent_tree, void* data _U_)
1099 proto_item *item;
1100 proto_tree *tree;
1101 int offset = 0;
1103 col_set_str(pinfo->cinfo, COL_INFO, "Oldstyle Handshake");
1105 item = proto_tree_add_item(parent_tree, proto_nbd, tvb, 0, -1, ENC_NA);
1106 tree = proto_item_add_subtree(item, ett_nbd);
1108 proto_tree_add_item(tree, hf_nbd_hnd_magic, tvb, offset, 8, ENC_BIG_ENDIAN);
1109 offset += 8;
1111 proto_tree_add_item(tree, hf_nbd_export_size, tvb, offset, 8, ENC_BIG_ENDIAN);
1112 offset += 8;
1114 proto_tree_add_bitmask(tree, tvb, offset, hf_nbd_hnd_flags,
1115 ett_nbd_hnd_flags, nbd_hnd_flags, ENC_BIG_ENDIAN);
1116 offset += 2;
1118 proto_tree_add_bitmask(tree, tvb, offset, hf_nbd_trans_flags,
1119 ett_nbd_trans_flags, nbd_trans_flags, ENC_BIG_ENDIAN);
1120 offset += 2;
1122 proto_tree_add_item(tree, hf_nbd_reserved, tvb, offset, 124, ENC_NA);
1124 return tvb_captured_length(tvb);
1127 static int
1128 dissect_nbd_hnd(tvbuff_t *tvb, packet_info *pinfo, proto_tree *parent_tree, void *data _U_)
1130 proto_item *item;
1131 proto_tree *tree;
1132 int offset = 0;
1133 uint64_t magic;
1134 //nbd_conv_info_t *nbd_info;
1136 nbd_state_e new_state;
1138 //nbd_info = get_nbd_conv_info(pinfo);
1139 bool from_server = nbd_from_server(pinfo);
1141 /* We want 8 to test the magic number */
1142 if (tvb_captured_length_remaining(tvb, offset) < 8) {
1143 return 0;
1146 magic = tvb_get_uint64(tvb, offset, ENC_BIG_ENDIAN);
1148 switch (magic) {
1149 case NBD_HND_INIT_MAGIC:
1150 item = proto_tree_add_item(parent_tree, proto_nbd, tvb, 0, 8, ENC_NA);
1151 tree = proto_item_add_subtree(item, ett_nbd);
1153 proto_tree_add_item(tree, hf_nbd_hnd_magic, tvb, offset, 8, ENC_BIG_ENDIAN);
1154 col_set_str(pinfo->cinfo, COL_INFO, "Handshake Start");
1155 nbd_set_state(pinfo, STATE_HND_INIT);
1156 break;
1157 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.
1161 new_state = from_server ? STATE_HND_INIT : STATE_HND_OPT;
1162 nbd_set_state(pinfo, new_state);
1163 if (from_server) {
1164 item = proto_tree_add_item(parent_tree, proto_nbd, tvb, 0, 8, ENC_NA);
1165 tree = proto_item_add_subtree(item, ett_nbd);
1167 proto_tree_add_item(tree, hf_nbd_hnd_magic, tvb, offset, 8, ENC_BIG_ENDIAN);
1168 col_set_str(pinfo->cinfo, COL_INFO, "Newstyle Handshake");
1169 } else {
1170 tcp_dissect_pdus(tvb, pinfo, parent_tree, nbd_desegment, 16, get_nbd_opt_len, dissect_nbd_opt_pdu, data);
1172 break;
1173 case NBD_HND_REPLY_MAGIC:
1174 nbd_set_state(pinfo, STATE_HND_OPT);
1175 tcp_dissect_pdus(tvb, pinfo, parent_tree, nbd_desegment, 20, get_nbd_opt_reply_len, dissect_nbd_opt_reply_pdu, data);
1176 break;
1177 case NBD_HND_OLD_MAGIC:
1178 tcp_dissect_pdus(tvb, pinfo, parent_tree, nbd_desegment, 20, get_nbd_old_len, dissect_nbd_old_pdu, data);
1179 break;
1180 default:
1181 return 0;
1184 return tvb_captured_length(tvb);
1187 static int
1188 dissect_nbd(tvbuff_t *tvb, packet_info *pinfo, proto_tree *parent_tree, void* data _U_)
1190 int offset=0;
1191 proto_tree *tree=NULL;
1192 proto_item *item=NULL;
1193 nbd_conv_info_t *nbd_info;
1195 nbd_state_e current_state;
1197 col_set_str(pinfo->cinfo, COL_PROTOCOL, "NBD");
1199 col_clear(pinfo->cinfo, COL_INFO);
1201 bool from_server = nbd_from_server(pinfo);
1202 nbd_info = get_nbd_conv_info(pinfo);
1203 current_state = (nbd_state_e)GPOINTER_TO_UINT(wmem_tree_lookup32_le(nbd_info->state, pinfo->num));
1204 nbd_option_t *nbd_opt;
1205 nbd_opt = (nbd_option_t*)wmem_tree_lookup32_le(nbd_info->opts, pinfo->num);
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.)
1213 if (!dissect_nbd_transmission(tvb, pinfo, parent_tree, data)) {
1214 if (!dissect_nbd_hnd(tvb, pinfo, parent_tree, data)) {
1216 item = proto_tree_add_item(parent_tree, proto_nbd, tvb, 0, -1, ENC_NA);
1217 tree = proto_item_add_subtree(item, ett_nbd);
1219 if (current_state == STATE_HND_INIT) {
1220 uint64_t flags;
1221 if (from_server) {
1222 proto_tree_add_bitmask(tree, tvb, offset, hf_nbd_hnd_flags,
1223 ett_nbd_hnd_flags, nbd_hnd_flags, ENC_BIG_ENDIAN);
1224 col_set_str(pinfo->cinfo, COL_INFO, "Handshake Flags");
1225 } else {
1226 proto_tree_add_bitmask_ret_uint64(tree, tvb, offset, hf_nbd_cli_flags,
1227 ett_nbd_hnd_flags, nbd_cli_flags, ENC_BIG_ENDIAN, &flags);
1228 col_set_str(pinfo->cinfo, COL_INFO, "Client Flags");
1229 if (flags & NBD_FLAG_NO_ZEROES) {
1230 nbd_info->no_zeroes = true;
1233 } else if (current_state == STATE_HND_OPT && nbd_opt && nbd_opt->opt == NBD_OPT_EXPORT_NAME) {
1234 tcp_dissect_pdus(tvb, pinfo, tree, nbd_desegment, 10, get_nbd_export_len, dissect_nbd_export_pdu, data);
1238 return tvb_captured_length(tvb);
1241 static bool
1242 dissect_nbd_tcp_heur(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void *data)
1244 uint32_t magic, type;
1245 uint64_t magic64;
1246 conversation_t *conversation;
1247 conversation = find_or_create_conversation(pinfo);
1249 /* We need at least this much to tell whether this is NBD or not */
1250 if(tvb_captured_length(tvb)<4){
1251 return false;
1254 /* Check if it looks like NBD */
1255 magic=tvb_get_ntohl(tvb, 0);
1256 switch(magic){
1257 case NBD_REQUEST_MAGIC:
1258 /* requests are 28 bytes or more */
1259 if(tvb_captured_length(tvb)<28){
1260 return false;
1262 /* verify type */
1263 type=tvb_get_ntohs(tvb, 6);
1264 if (!try_val_to_str(type, nbd_type_vals)) {
1265 return false;
1267 conversation_set_dissector(conversation, nbd_handle);
1268 tcp_dissect_pdus(tvb, pinfo, tree, nbd_desegment, 28, get_nbd_tcp_pdu_len, dissect_nbd_tcp_pdu, data);
1269 return true;
1270 case NBD_RESPONSE_MAGIC:
1271 /* responses are 16 bytes or more */
1272 if(tvb_captured_length(tvb)<16){
1273 return false;
1275 conversation_set_dissector(conversation, nbd_handle);
1276 tcp_dissect_pdus(tvb, pinfo, tree, nbd_desegment, 16, get_nbd_tcp_pdu_len, dissect_nbd_tcp_pdu, data);
1277 return true;
1278 case NBD_STRUCTURED_REPLY_MAGIC:
1279 /* structured replies are 20 bytes or more,
1280 * and the length is in bytes 17-20. */
1281 if(tvb_captured_length(tvb)<20){
1282 return false;
1284 conversation_set_dissector(conversation, nbd_handle);
1285 tcp_dissect_pdus(tvb, pinfo, tree, nbd_desegment, 20, get_nbd_tcp_pdu_len, dissect_nbd_tcp_pdu, data);
1286 default:
1287 break;
1290 if (tvb_captured_length(tvb) < 8){
1291 return false;
1293 magic64 = tvb_get_uint64(tvb, 0, ENC_BIG_ENDIAN);
1294 switch (magic64) {
1295 case NBD_HND_INIT_MAGIC:
1296 case NBD_HND_OPT_MAGIC:
1297 case NBD_HND_REPLY_MAGIC:
1298 case NBD_HND_OLD_MAGIC:
1299 conversation_set_dissector(conversation, nbd_handle);
1300 dissect_nbd(tvb, pinfo, tree, data);
1301 return true;
1302 default:
1303 break;
1306 return false;
1309 void proto_register_nbd(void)
1311 static hf_register_info hf[] = {
1312 { &hf_nbd_hnd_magic,
1313 { "Magic", "nbd.hnd.magic", FT_UINT64, BASE_HEX,
1314 NULL, 0x0, NULL, HFILL }},
1315 { &hf_nbd_hnd_flags,
1316 { "Handshake Flags", "nbd.hnd.flags", FT_UINT16, BASE_HEX,
1317 NULL, 0x0, NULL, HFILL }},
1318 { &hf_nbd_hnd_flags_fixed_new,
1319 { "Fixed Newstyle", "nbd.hnd.flags.fixed_new", FT_BOOLEAN, 16,
1320 TFS(&tfs_set_notset), 0x0001, NULL, HFILL }},
1321 { &hf_nbd_hnd_flags_no_zeroes,
1322 { "No Zeroes", "nbd.hnd.flags.no_zeroes", FT_BOOLEAN, 16,
1323 TFS(&tfs_set_notset), NBD_FLAG_NO_ZEROES, NULL, HFILL }},
1324 { &hf_nbd_cli_flags,
1325 { "Client Flags", "nbd.cli.flags", FT_UINT32, BASE_HEX,
1326 NULL, 0x0, NULL, HFILL }},
1327 { &hf_nbd_cli_flags_fixed_new,
1328 { "Fixed Newstyle", "nbd.cli.flags.fixed_new", FT_BOOLEAN, 32,
1329 TFS(&tfs_set_notset), 0x00000001, NULL, HFILL }},
1330 { &hf_nbd_cli_flags_no_zeroes,
1331 { "No Zeroes", "nbd.cli.flags.no_zeroes", FT_BOOLEAN, 32,
1332 TFS(&tfs_set_notset), NBD_FLAG_NO_ZEROES, NULL, HFILL }},
1333 { &hf_nbd_hnd_opt,
1334 { "Option", "nbd.hnd.opt", FT_UINT32, BASE_HEX,
1335 VALS(nbd_opt_vals), 0x0, NULL, HFILL }},
1336 { &hf_nbd_hnd_reply,
1337 { "Reply", "nbd.hnd.reply", FT_UINT32, BASE_HEX,
1338 VALS(nbd_hnd_reply_vals), 0x0, NULL, HFILL }},
1339 { &hf_nbd_magic,
1340 { "Magic", "nbd.magic", FT_UINT32, BASE_HEX,
1341 NULL, 0x0, NULL, HFILL }},
1342 { &hf_nbd_cmd_flags,
1343 { "Command Flags", "nbd.cmd.flags", FT_UINT16, BASE_HEX,
1344 NULL, 0x0, NULL, HFILL }},
1345 { &hf_nbd_cmd_flags_fua,
1346 { "Forced Unit Access", "nbd.cmd.flags.fua", FT_BOOLEAN, 16,
1347 TFS(&tfs_set_notset), 0x0001, NULL, HFILL }},
1348 { &hf_nbd_cmd_flags_no_hole,
1349 { "No Hole", "nbd.cmd.flags.no_hole", FT_BOOLEAN, 16,
1350 TFS(&tfs_set_notset), 0x0002, NULL, HFILL }},
1351 { &hf_nbd_cmd_flags_df,
1352 { "Don't Fragment", "nbd.cmd.flags.df", FT_BOOLEAN, 16,
1353 TFS(&tfs_set_notset), 0x0004, NULL, HFILL }},
1354 { &hf_nbd_cmd_flags_req_one,
1355 { "Request One", "nbd.cmd.flags.req_one", FT_BOOLEAN, 16,
1356 TFS(&tfs_set_notset), 0x0008, NULL, HFILL }},
1357 { &hf_nbd_cmd_flags_fast_zero,
1358 { "Fast Zero", "nbd.cmd.flags.fast_zero", FT_BOOLEAN, 16,
1359 TFS(&tfs_set_notset), 0x0010, NULL, HFILL }},
1360 { &hf_nbd_cmd_flags_payload_len,
1361 { "Payload Len", "nbd.cmd.flags.payload_len", FT_BOOLEAN, 16,
1362 TFS(&tfs_set_notset), 0x0020, NULL, HFILL }},
1363 { &hf_nbd_reply_flags,
1364 { "Reply Flags", "nbd.reply.flags", FT_UINT16, BASE_HEX,
1365 NULL, 0x0, NULL, HFILL }},
1366 { &hf_nbd_reply_flags_done,
1367 { "Done", "nbd.reply.flags.done", FT_BOOLEAN, 16,
1368 TFS(&tfs_set_notset), 0x0001, NULL, HFILL }},
1369 { &hf_nbd_export_size,
1370 { "Export Size", "nbd.export.size", FT_UINT64, BASE_DEC|BASE_UNIT_STRING,
1371 UNS(&units_byte_bytes), 0x0, NULL, HFILL }},
1372 { &hf_nbd_trans_flags,
1373 { "Transmission Flags", "nbd.export.trans.flags", FT_UINT16, BASE_HEX,
1374 NULL, 0x0, NULL, HFILL }},
1375 { &hf_nbd_trans_flags_has_flags,
1376 { "Has Flags", "nbd.trans.flags.has_flags", FT_BOOLEAN, 16,
1377 TFS(&tfs_set_notset), 0x0001, NULL, HFILL }},
1378 { &hf_nbd_trans_flags_read_only,
1379 { "Read Only", "nbd.trans.flags.read_only", FT_BOOLEAN, 16,
1380 TFS(&tfs_set_notset), 0x0002, NULL, HFILL }},
1381 { &hf_nbd_trans_flags_flush,
1382 { "Flush", "nbd.trans.flags.flush", FT_BOOLEAN, 16,
1383 TFS(&tfs_supported_not_supported), 0x0004, NULL, HFILL }},
1384 { &hf_nbd_trans_flags_fua,
1385 { "Forced Unit Access", "nbd.trans.flags.fua", FT_BOOLEAN, 16,
1386 TFS(&tfs_supported_not_supported), 0x0008, NULL, HFILL }},
1387 { &hf_nbd_trans_flags_rotational,
1388 { "Rotational", "nbd.trans.flags.rotational", FT_BOOLEAN, 16,
1389 TFS(&tfs_set_notset), 0x0010, NULL, HFILL }},
1390 { &hf_nbd_trans_flags_trim,
1391 { "Trim", "nbd.trans.flags.trim", FT_BOOLEAN, 16,
1392 TFS(&tfs_supported_not_supported), 0x0020, NULL, HFILL }},
1393 { &hf_nbd_trans_flags_write_zeroes,
1394 { "Write Zeroes", "nbd.trans.flags.write_zeroes", FT_BOOLEAN, 16,
1395 TFS(&tfs_supported_not_supported), 0x0040, NULL, HFILL }},
1396 { &hf_nbd_trans_flags_df,
1397 { "Don't Fragment", "nbd.trans.flags.df", FT_BOOLEAN, 16,
1398 TFS(&tfs_supported_not_supported), 0x0080, NULL, HFILL }},
1399 { &hf_nbd_trans_flags_multi_conn,
1400 { "Multiple Connections", "nbd.trans.flags.multi_conn", FT_BOOLEAN, 16,
1401 TFS(&tfs_supported_not_supported), 0x0100, NULL, HFILL }},
1402 { &hf_nbd_trans_flags_resize,
1403 { "Resize", "nbd.trans.flags.resize", FT_BOOLEAN, 16,
1404 TFS(&tfs_supported_not_supported), 0x0200, NULL, HFILL }},
1405 { &hf_nbd_trans_flags_cache,
1406 { "Cache", "nbd.trans.flags.cache", FT_BOOLEAN, 16,
1407 TFS(&tfs_supported_not_supported), 0x0400, NULL, HFILL }},
1408 { &hf_nbd_trans_flags_fast_zero,
1409 { "Fast Zeroes", "nbd.trans.flags.fast_zero", FT_BOOLEAN, 16,
1410 TFS(&tfs_supported_not_supported), 0x0800, NULL, HFILL }},
1411 { &hf_nbd_trans_flags_block_status_payload,
1412 { "Block Status Payload", "nbd.trans.flags.block_status_payload", FT_BOOLEAN, 16,
1413 TFS(&tfs_supported_not_supported), 0x1000, NULL, HFILL }},
1414 { &hf_nbd_reserved,
1415 { "Reserved (Zeroes)", "nbd.reserved", FT_BYTES, BASE_NONE,
1416 NULL, 0x0, NULL, HFILL }},
1417 { &hf_nbd_type,
1418 { "Type", "nbd.type", FT_UINT16, BASE_DEC,
1419 VALS(nbd_type_vals), 0x0, NULL, HFILL }},
1420 { &hf_nbd_reply_type,
1421 { "Reply Type", "nbd.reply.type", FT_UINT16, BASE_DEC,
1422 VALS(nbd_reply_type_vals), 0x0, NULL, HFILL }},
1423 { &hf_nbd_error,
1424 { "Error", "nbd.error", FT_UINT32, BASE_DEC,
1425 VALS(nbd_error_vals), 0x0, NULL, HFILL }},
1426 { &hf_nbd_len,
1427 { "Length", "nbd.len", FT_UINT32, BASE_DEC,
1428 NULL, 0x0, NULL, HFILL }},
1429 { &hf_nbd_handle,
1430 { "Handle", "nbd.handle", FT_UINT64, BASE_HEX,
1431 NULL, 0x0, NULL, HFILL }},
1432 { &hf_nbd_from,
1433 { "From", "nbd.from", FT_UINT64, BASE_HEX,
1434 NULL, 0x0, NULL, HFILL }},
1435 { &hf_nbd_response_in,
1436 { "Response In", "nbd.response_in", FT_FRAMENUM, BASE_NONE,
1437 FRAMENUM_TYPE(FT_FRAMENUM_RESPONSE), 0x0, "The response to this NBD request is in this frame", HFILL }},
1438 { &hf_nbd_response_to,
1439 { "Request In", "nbd.response_to", FT_FRAMENUM, BASE_NONE,
1440 FRAMENUM_TYPE(FT_FRAMENUM_REQUEST), 0x0, "This is a response to the NBD request in this frame", HFILL }},
1441 { &hf_nbd_time,
1442 { "Time", "nbd.time", FT_RELATIVE_TIME, BASE_NONE,
1443 NULL, 0x0, "The time between the Call and the Reply", HFILL }},
1445 { &hf_nbd_export_name_len,
1446 { "Export Name Length", "nbd.export.name.len", FT_UINT32, BASE_DEC,
1447 NULL, 0x0, NULL, HFILL }},
1448 { &hf_nbd_export_name,
1449 { "Export Name", "nbd.export.name", FT_STRING, BASE_NONE,
1450 NULL, 0x0, NULL, HFILL }},
1451 { &hf_nbd_info_num,
1452 { "Number of Information Requests", "nbd.info.num", FT_UINT16, BASE_DEC,
1453 NULL, 0x0, NULL, HFILL }},
1454 { &hf_nbd_info,
1455 { "Information Type", "nbd.info", FT_UINT16, BASE_DEC,
1456 VALS(nbd_info_vals), 0x0, NULL, HFILL }},
1457 { &hf_nbd_query_num,
1458 { "Number of Queries", "nbd.query.num", FT_UINT32, BASE_DEC,
1459 NULL, 0x0, NULL, HFILL }},
1460 { &hf_nbd_query,
1461 { "Query", "nbd.query", FT_UINT_STRING, BASE_NONE,
1462 NULL, 0x0, NULL, HFILL }},
1463 { &hf_nbd_export_description,
1464 { "Export Description", "nbd.export.description", FT_STRING, BASE_NONE,
1465 NULL, 0x0, NULL, HFILL }},
1466 { &hf_nbd_block_size_min,
1467 { "Minimum Block Size", "nbd.block_size.min", FT_UINT32, BASE_DEC,
1468 NULL, 0x0, NULL, HFILL }},
1469 { &hf_nbd_block_size_prefer,
1470 { "Preferred Block Size", "nbd.block_size.prefer", FT_UINT32, BASE_DEC,
1471 NULL, 0x0, NULL, HFILL }},
1472 { &hf_nbd_payload_size_max,
1473 { "Maximum Payload Size", "nbd.payload_size.max", FT_UINT32, BASE_DEC,
1474 NULL, 0x0, NULL, HFILL }},
1475 { &hf_nbd_meta_context_id,
1476 { "Metadata Context ID", "nbd.meta_context.id", FT_UINT32, BASE_DEC,
1477 NULL, 0x0, NULL, HFILL }},
1478 { &hf_nbd_meta_context_name,
1479 { "Metadata Context Name", "nbd.meta_context.name", FT_STRING, BASE_NONE,
1480 NULL, 0x0, NULL, HFILL }},
1481 { &hf_nbd_error_msg_len,
1482 { "Message Length", "nbd.error_msg.len", FT_UINT16, BASE_DEC,
1483 NULL, 0x0, NULL, HFILL }},
1484 { &hf_nbd_error_msg,
1485 { "Error Message", "nbd.error_msg", FT_STRING, BASE_NONE,
1486 NULL, 0x0, NULL, HFILL }},
1487 { &hf_nbd_data,
1488 { "Data", "nbd.data", FT_BYTES, BASE_NONE,
1489 NULL, 0x0, NULL, HFILL }},
1490 { &hf_nbd_hole_size,
1491 { "Hole Size", "nbd.hole_size", FT_UINT32, BASE_DEC|BASE_UNIT_STRING,
1492 UNS(&units_byte_bytes), 0x0, NULL, HFILL }},
1493 { &hf_nbd_status_flags,
1494 { "Block Status Flags", "nbd.status_flags", FT_UINT32, BASE_DEC,
1495 NULL, 0x0, "Status flags as defined by metadata context", HFILL }},
1500 static int *ett[] = {
1501 &ett_nbd,
1502 &ett_nbd_hnd_flags,
1503 &ett_nbd_cli_flags,
1504 &ett_nbd_cmd_flags,
1505 &ett_nbd_reply_flags,
1506 &ett_nbd_trans_flags,
1509 static ei_register_info ei[] = {
1510 { &ei_nbd_hnd_reply_error, {"nbd.hnd.reply.error", PI_RESPONSE_CODE, PI_NOTE, "Reply Error", EXPFILL }},
1511 { &ei_nbd_unexpected_data, {"nbd.data.unexpected", PI_UNDECODED, PI_WARN, "Unexpected data", EXPFILL }},
1514 module_t *nbd_module;
1515 expert_module_t *expert_nbd;
1517 proto_nbd = proto_register_protocol("Network Block Device",
1518 "NBD", "nbd");
1519 proto_register_field_array(proto_nbd, hf, array_length(hf));
1520 proto_register_subtree_array(ett, array_length(ett));
1521 expert_nbd = expert_register_protocol(proto_nbd);
1522 expert_register_field_array(expert_nbd, ei, array_length(ei));
1524 nbd_module = prefs_register_protocol(proto_nbd, apply_nbd_prefs);
1525 prefs_register_bool_preference(nbd_module, "desegment_nbd_messages",
1526 "Reassemble NBD messages spanning multiple TCP segments",
1527 "Whether the NBD dissector should reassemble messages spanning multiple TCP segments."
1528 " To use this option, you must also enable \"Allow subdissectors to reassemble TCP streams\" in the TCP protocol settings",
1529 &nbd_desegment);
1531 nbd_handle = register_dissector("nbd", dissect_nbd, proto_nbd);
1534 static void
1535 apply_nbd_prefs(void)
1537 // XXX - There should be a reset_uint_range ?
1538 dissector_delete_uint_range("tls.port", nbd_port_range, nbd_handle);
1539 nbd_port_range = prefs_get_range_value("NBD", "tcp.port");
1540 dissector_add_uint_range("tls.port", nbd_port_range, nbd_handle);
1543 void
1544 proto_reg_handoff_nbd(void)
1546 heur_dissector_add("tcp", dissect_nbd_tcp_heur, "NBD over TCP", "nbd_tcp", proto_nbd, HEURISTIC_ENABLE);
1547 dissector_add_uint_range_with_preference("tcp.port", NBD_TCP_PORTS, nbd_handle);
1548 tls_handle = find_dissector_add_dependency("tls", proto_nbd);
1549 apply_nbd_prefs();
1553 * Editor modelines - https://www.wireshark.org/tools/modelines.html
1555 * Local variables:
1556 * c-basic-offset: 8
1557 * tab-width: 8
1558 * indent-tabs-mode: t
1559 * End:
1561 * vi: set shiftwidth=8 tabstop=8 noexpandtab:
1562 * :indentSize=8:tabSize=8:noTabs=false: