2 * Routines for Network Block Device (NBD) dissection.
8 * Wireshark - Network traffic analyzer
9 * By Gerald Combs <gerald@wireshark.org>
10 * Copyright 1998 Gerald Combs
12 * This program is free software; you can redistribute it and/or
13 * modify it under the terms of the GNU General Public License
14 * as published by the Free Software Foundation; either version 2
15 * of the License, or (at your option) any later version.
17 * This program is distributed in the hope that it will be useful,
18 * but WITHOUT ANY WARRANTY; without even the implied warranty of
19 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20 * GNU General Public License for more details.
22 * You should have received a copy of the GNU General Public License
23 * along with this program; if not, write to the Free Software
24 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
31 #include <epan/packet.h>
32 #include <epan/prefs.h>
33 #include <epan/conversation.h>
34 #include <epan/wmem/wmem.h>
35 #include "packet-tcp.h"
37 static gint proto_nbd
= -1;
38 static int hf_nbd_magic
= -1;
39 static int hf_nbd_type
= -1;
40 static int hf_nbd_error
= -1;
41 static int hf_nbd_handle
= -1;
42 static int hf_nbd_from
= -1;
43 static int hf_nbd_len
= -1;
44 static int hf_nbd_response_in
= -1;
45 static int hf_nbd_response_to
= -1;
46 static int hf_nbd_time
= -1;
47 static int hf_nbd_data
= -1;
49 static gint ett_nbd
= -1;
52 static gboolean nbd_desegment
= TRUE
;
54 typedef struct _nbd_transaction_t
{
61 typedef struct _nbd_conv_info_t
{
62 wmem_tree_t
*unacked_pdus
; /* indexed by handle, whichs wraps quite frequently */
63 wmem_tree_t
*acked_pdus
; /* indexed by packet# and handle */
67 #define NBD_REQUEST_MAGIC 0x25609513
68 #define NBD_RESPONSE_MAGIC 0x67446698
70 #define NBD_CMD_READ 0
71 #define NBD_CMD_WRITE 1
72 #define NBD_CMD_DISC 2
73 static const value_string nbd_type_vals
[] = {
74 {NBD_CMD_READ
, "NBD_CMD_READ"},
75 {NBD_CMD_WRITE
, "NBD_CMD_WRITE"},
76 {NBD_CMD_DISC
, "NBD_CMD_DISC"},
81 /* This function will try to determine the complete size of a PDU
82 * based on the information in the header.
85 get_nbd_tcp_pdu_len(packet_info
*pinfo
, tvbuff_t
*tvb
, int offset
)
87 guint32 magic
, type
, packet
;
88 conversation_t
*conversation
;
89 nbd_conv_info_t
*nbd_info
;
90 nbd_transaction_t
*nbd_trans
=NULL
;
91 wmem_tree_key_t hkey
[3];
94 magic
=tvb_get_ntohl(tvb
, offset
);
97 case NBD_REQUEST_MAGIC
:
98 type
=tvb_get_ntohl(tvb
, offset
+4);
101 return tvb_get_ntohl(tvb
, offset
+24)+28;
105 case NBD_RESPONSE_MAGIC
:
107 * Do we have a conversation for this connection?
109 conversation
= find_conversation(pinfo
->fd
->num
,
110 &pinfo
->src
, &pinfo
->dst
,
112 pinfo
->srcport
, pinfo
->destport
, 0);
113 if (conversation
== NULL
) {
114 /* No, so just return the rest of the current packet */
115 return tvb_length(tvb
);
118 * Do we have a state structure for this conv
120 nbd_info
= (nbd_conv_info_t
*)conversation_get_proto_data(conversation
, proto_nbd
);
122 /* No, so just return the rest of the current packet */
123 return tvb_length(tvb
);
125 if(!pinfo
->fd
->flags
.visited
){
127 * Do we have a state structure for this transaction
129 handle
[0]=tvb_get_ntohl(tvb
, offset
+8);
130 handle
[1]=tvb_get_ntohl(tvb
, offset
+12);
134 nbd_trans
=(nbd_transaction_t
*)wmem_tree_lookup32_array(nbd_info
->unacked_pdus
, hkey
);
136 /* No, so just return the rest of the current packet */
137 return tvb_length(tvb
);
141 * Do we have a state structure for this transaction
143 handle
[0]=tvb_get_ntohl(tvb
, offset
+8);
144 handle
[1]=tvb_get_ntohl(tvb
, offset
+12);
145 packet
=pinfo
->fd
->num
;
151 nbd_trans
=(nbd_transaction_t
*)wmem_tree_lookup32_array(nbd_info
->acked_pdus
, hkey
);
153 /* No, so just return the rest of the current packet */
154 return tvb_length(tvb
);
157 /* If this is a read response we must add the datalen to
160 if(nbd_trans
->type
==NBD_CMD_READ
){
161 return 16+nbd_trans
->datalen
;
169 /* Did not really look like a NBD packet after all */
174 dissect_nbd_tcp_pdu(tvbuff_t
*tvb
, packet_info
*pinfo
, proto_tree
*parent_tree
, void* data _U_
)
176 guint32 magic
, error
, packet
;
180 proto_tree
*tree
=NULL
;
181 proto_item
*item
=NULL
;
182 conversation_t
*conversation
;
183 nbd_conv_info_t
*nbd_info
;
184 nbd_transaction_t
*nbd_trans
=NULL
;
185 wmem_tree_key_t hkey
[3];
187 col_set_str(pinfo
->cinfo
, COL_PROTOCOL
, "NBD");
189 col_clear(pinfo
->cinfo
, COL_INFO
);
191 item
= proto_tree_add_item(parent_tree
, proto_nbd
, tvb
, 0, -1, ENC_NA
);
192 tree
= proto_item_add_subtree(item
, ett_nbd
);
195 magic
=tvb_get_ntohl(tvb
, offset
);
196 proto_tree_add_item(tree
, hf_nbd_magic
, tvb
, offset
, 4, ENC_BIG_ENDIAN
);
200 /* grab what we need to do the request/response matching */
202 case NBD_REQUEST_MAGIC
:
203 case NBD_RESPONSE_MAGIC
:
204 handle
[0]=tvb_get_ntohl(tvb
, offset
+4);
205 handle
[1]=tvb_get_ntohl(tvb
, offset
+8);
211 conversation
= find_or_create_conversation(pinfo
);
214 * Do we already have a state structure for this conv
216 nbd_info
= (nbd_conv_info_t
*)conversation_get_proto_data(conversation
, proto_nbd
);
218 /* No. Attach that information to the conversation, and add
219 * it to the list of information structures.
221 nbd_info
= wmem_new(wmem_file_scope(), nbd_conv_info_t
);
222 nbd_info
->unacked_pdus
= wmem_tree_new(wmem_file_scope());
223 nbd_info
->acked_pdus
= wmem_tree_new(wmem_file_scope());
225 conversation_add_proto_data(conversation
, proto_nbd
, nbd_info
);
227 if(!pinfo
->fd
->flags
.visited
){
228 if(magic
==NBD_REQUEST_MAGIC
){
229 /* This is a request */
230 nbd_trans
=wmem_new(wmem_file_scope(), nbd_transaction_t
);
231 nbd_trans
->req_frame
=pinfo
->fd
->num
;
232 nbd_trans
->rep_frame
=0;
233 nbd_trans
->req_time
=pinfo
->fd
->abs_ts
;
234 nbd_trans
->type
=tvb_get_ntohl(tvb
, offset
);
235 nbd_trans
->datalen
=tvb_get_ntohl(tvb
, offset
+20);
241 wmem_tree_insert32_array(nbd_info
->unacked_pdus
, hkey
, (void *)nbd_trans
);
242 } else if(magic
==NBD_RESPONSE_MAGIC
){
247 nbd_trans
=(nbd_transaction_t
*)wmem_tree_lookup32_array(nbd_info
->unacked_pdus
, hkey
);
249 nbd_trans
->rep_frame
=pinfo
->fd
->num
;
252 hkey
[0].key
=&nbd_trans
->rep_frame
;
256 wmem_tree_insert32_array(nbd_info
->acked_pdus
, hkey
, (void *)nbd_trans
);
258 hkey
[0].key
=&nbd_trans
->req_frame
;
262 wmem_tree_insert32_array(nbd_info
->acked_pdus
, hkey
, (void *)nbd_trans
);
266 packet
=pinfo
->fd
->num
;
273 nbd_trans
=(nbd_transaction_t
*)wmem_tree_lookup32_array(nbd_info
->acked_pdus
, hkey
);
275 /* The bloody handles are reused !!! eventhough they are 64 bits.
276 * So we must verify we got the "correct" one
278 if( (magic
==NBD_RESPONSE_MAGIC
)
280 && (pinfo
->fd
->num
<nbd_trans
->req_frame
) ){
281 /* must have been the wrong one */
286 /* create a "fake" nbd_trans structure */
287 nbd_trans
=wmem_new(wmem_packet_scope(), nbd_transaction_t
);
288 nbd_trans
->req_frame
=0;
289 nbd_trans
->rep_frame
=0;
290 nbd_trans
->req_time
=pinfo
->fd
->abs_ts
;
291 nbd_trans
->type
=0xff;
292 nbd_trans
->datalen
=0;
295 /* print state tracking in the tree */
296 if(magic
==NBD_REQUEST_MAGIC
){
297 /* This is a request */
298 if(nbd_trans
->rep_frame
){
301 it
=proto_tree_add_uint(tree
, hf_nbd_response_in
, tvb
, 0, 0, nbd_trans
->rep_frame
);
302 PROTO_ITEM_SET_GENERATED(it
);
304 } else if(magic
==NBD_RESPONSE_MAGIC
){
305 /* This is a reply */
306 if(nbd_trans
->req_frame
){
310 it
=proto_tree_add_uint(tree
, hf_nbd_response_to
, tvb
, 0, 0, nbd_trans
->req_frame
);
311 PROTO_ITEM_SET_GENERATED(it
);
313 nstime_delta(&ns
, &pinfo
->fd
->abs_ts
, &nbd_trans
->req_time
);
314 it
=proto_tree_add_time(tree
, hf_nbd_time
, tvb
, 0, 0, &ns
);
315 PROTO_ITEM_SET_GENERATED(it
);
321 case NBD_REQUEST_MAGIC
:
322 proto_tree_add_item(tree
, hf_nbd_type
, tvb
, offset
, 4, ENC_BIG_ENDIAN
);
325 proto_tree_add_item(tree
, hf_nbd_handle
, tvb
, offset
, 8, ENC_BIG_ENDIAN
);
328 from
=tvb_get_ntoh64(tvb
, offset
);
329 proto_tree_add_item(tree
, hf_nbd_from
, tvb
, offset
, 8, ENC_BIG_ENDIAN
);
332 proto_tree_add_item(tree
, hf_nbd_len
, tvb
, offset
, 4, ENC_BIG_ENDIAN
);
335 switch(nbd_trans
->type
){
337 col_add_fstr(pinfo
->cinfo
, COL_INFO
, "Write Request Offset:0x%" G_GINT64_MODIFIER
"x Length:%d", from
, nbd_trans
->datalen
);
340 col_add_fstr(pinfo
->cinfo
, COL_INFO
, "Read Request Offset:0x%" G_GINT64_MODIFIER
"x Length:%d", from
, nbd_trans
->datalen
);
343 col_set_str(pinfo
->cinfo
, COL_INFO
, "Disconnect Request");
347 if(nbd_trans
->type
==NBD_CMD_WRITE
){
348 proto_tree_add_item(tree
, hf_nbd_data
, tvb
, offset
, nbd_trans
->datalen
, ENC_NA
);
351 case NBD_RESPONSE_MAGIC
:
352 item
=proto_tree_add_uint(tree
, hf_nbd_type
, tvb
, 0, 0, nbd_trans
->type
);
353 PROTO_ITEM_SET_GENERATED(item
);
355 error
=tvb_get_ntohl(tvb
, offset
);
356 proto_tree_add_item(tree
, hf_nbd_error
, tvb
, offset
, 4, ENC_BIG_ENDIAN
);
359 proto_tree_add_item(tree
, hf_nbd_handle
, tvb
, offset
, 8, ENC_BIG_ENDIAN
);
362 col_add_fstr(pinfo
->cinfo
, COL_INFO
, "%s Response Error:%d", (nbd_trans
->type
==NBD_CMD_WRITE
)?"Write":"Read", error
);
364 if(nbd_trans
->type
==NBD_CMD_READ
){
365 proto_tree_add_item(tree
, hf_nbd_data
, tvb
, offset
, nbd_trans
->datalen
, ENC_NA
);
370 return tvb_length(tvb
);
374 dissect_nbd_tcp_heur(tvbuff_t
*tvb
, packet_info
*pinfo
, proto_tree
*tree
, void *data
)
378 /* We need at least this much to tell whether this is NBD or not */
379 if(tvb_length(tvb
)<4){
383 /* Check if it looks like NBD */
384 magic
=tvb_get_ntohl(tvb
, 0);
386 case NBD_REQUEST_MAGIC
:
387 /* requests are 28 bytes or more */
388 if(tvb_length(tvb
)<28){
392 type
=tvb_get_ntohl(tvb
, 4);
402 tcp_dissect_pdus(tvb
, pinfo
, tree
, nbd_desegment
, 28, get_nbd_tcp_pdu_len
, dissect_nbd_tcp_pdu
, data
);
404 case NBD_RESPONSE_MAGIC
:
405 /* responses are 16 bytes or more */
406 if(tvb_length(tvb
)<16){
409 tcp_dissect_pdus(tvb
, pinfo
, tree
, nbd_desegment
, 16, get_nbd_tcp_pdu_len
, dissect_nbd_tcp_pdu
, data
);
418 void proto_register_nbd(void)
420 static hf_register_info hf
[] = {
422 { "Magic", "nbd.magic", FT_UINT32
, BASE_HEX
,
423 NULL
, 0x0, NULL
, HFILL
}},
425 { "Type", "nbd.type", FT_UINT32
, BASE_DEC
,
426 VALS(nbd_type_vals
), 0x0, NULL
, HFILL
}},
428 { "Error", "nbd.error", FT_UINT32
, BASE_DEC
,
429 NULL
, 0x0, NULL
, HFILL
}},
431 { "Length", "nbd.len", FT_UINT32
, BASE_DEC
,
432 NULL
, 0x0, NULL
, HFILL
}},
434 { "Handle", "nbd.handle", FT_UINT64
, BASE_HEX
,
435 NULL
, 0x0, NULL
, HFILL
}},
437 { "From", "nbd.from", FT_UINT64
, BASE_HEX
,
438 NULL
, 0x0, NULL
, HFILL
}},
439 { &hf_nbd_response_in
,
440 { "Response In", "nbd.response_in", FT_FRAMENUM
, BASE_NONE
,
441 NULL
, 0x0, "The response to this NBD request is in this frame", HFILL
}},
442 { &hf_nbd_response_to
,
443 { "Request In", "nbd.response_to", FT_FRAMENUM
, BASE_NONE
,
444 NULL
, 0x0, "This is a response to the NBD request in this frame", HFILL
}},
446 { "Time", "nbd.time", FT_RELATIVE_TIME
, BASE_NONE
,
447 NULL
, 0x0, "The time between the Call and the Reply", HFILL
}},
450 { "Data", "nbd.data", FT_BYTES
, BASE_NONE
,
451 NULL
, 0x0, NULL
, HFILL
}},
456 static gint
*ett
[] = {
460 module_t
*nbd_module
;
462 proto_nbd
= proto_register_protocol("Network Block Device",
464 proto_register_field_array(proto_nbd
, hf
, array_length(hf
));
465 proto_register_subtree_array(ett
, array_length(ett
));
467 nbd_module
= prefs_register_protocol(proto_nbd
, NULL
);
468 prefs_register_bool_preference(nbd_module
, "desegment_nbd_messages",
469 "Reassemble NBD messages spanning multiple TCP segments",
470 "Whether the NBD dissector should reassemble messages spanning multiple TCP segments."
471 " To use this option, you must also enable \"Allow subdissectors to reassemble TCP streams\" in the TCP protocol settings",
477 proto_reg_handoff_nbd(void)
479 heur_dissector_add("tcp", dissect_nbd_tcp_heur
, proto_nbd
);