2 * Routines for Network Block Device (NBD) dissection.
6 * Wireshark - Network traffic analyzer
7 * By Gerald Combs <gerald@wireshark.org>
8 * Copyright 1998 Gerald Combs
10 * This program is free software; you can redistribute it and/or
11 * modify it under the terms of the GNU General Public License
12 * as published by the Free Software Foundation; either version 2
13 * of the License, or (at your option) any later version.
15 * This program is distributed in the hope that it will be useful,
16 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 * GNU General Public License for more details.
20 * You should have received a copy of the GNU General Public License
21 * along with this program; if not, write to the Free Software
22 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
29 #include <epan/packet.h>
30 #include <epan/prefs.h>
31 #include <epan/conversation.h>
32 #include <epan/wmem/wmem.h>
33 #include "packet-tcp.h"
35 void proto_register_nbd(void);
36 void proto_reg_handoff_nbd(void);
38 static gint proto_nbd = -1;
39 static int hf_nbd_magic = -1;
40 static int hf_nbd_type = -1;
41 static int hf_nbd_error = -1;
42 static int hf_nbd_handle = -1;
43 static int hf_nbd_from = -1;
44 static int hf_nbd_len = -1;
45 static int hf_nbd_response_in = -1;
46 static int hf_nbd_response_to = -1;
47 static int hf_nbd_time = -1;
48 static int hf_nbd_data = -1;
50 static gint ett_nbd = -1;
53 static gboolean nbd_desegment = TRUE;
55 typedef struct _nbd_transaction_t {
62 typedef struct _nbd_conv_info_t {
63 wmem_tree_t *unacked_pdus; /* indexed by handle, whichs wraps quite frequently */
64 wmem_tree_t *acked_pdus; /* indexed by packet# and handle */
68 #define NBD_REQUEST_MAGIC 0x25609513
69 #define NBD_RESPONSE_MAGIC 0x67446698
71 #define NBD_CMD_READ 0
72 #define NBD_CMD_WRITE 1
73 #define NBD_CMD_DISC 2
74 static const value_string nbd_type_vals[] = {
75 {NBD_CMD_READ, "NBD_CMD_READ"},
76 {NBD_CMD_WRITE, "NBD_CMD_WRITE"},
77 {NBD_CMD_DISC, "NBD_CMD_DISC"},
82 /* This function will try to determine the complete size of a PDU
83 * based on the information in the header.
86 get_nbd_tcp_pdu_len(packet_info *pinfo, tvbuff_t *tvb, int offset)
88 guint32 magic, type, packet;
89 conversation_t *conversation;
90 nbd_conv_info_t *nbd_info;
91 nbd_transaction_t *nbd_trans=NULL;
92 wmem_tree_key_t hkey[3];
95 magic=tvb_get_ntohl(tvb, offset);
98 case NBD_REQUEST_MAGIC:
99 type=tvb_get_ntohl(tvb, offset+4);
102 return tvb_get_ntohl(tvb, offset+24)+28;
106 case NBD_RESPONSE_MAGIC:
108 * Do we have a conversation for this connection?
110 conversation = find_conversation(pinfo->fd->num,
111 &pinfo->src, &pinfo->dst,
113 pinfo->srcport, pinfo->destport, 0);
114 if (conversation == NULL) {
115 /* No, so just return the rest of the current packet */
116 return tvb_length(tvb);
119 * Do we have a state structure for this conv
121 nbd_info = (nbd_conv_info_t *)conversation_get_proto_data(conversation, proto_nbd);
123 /* No, so just return the rest of the current packet */
124 return tvb_length(tvb);
126 if(!pinfo->fd->flags.visited){
128 * Do we have a state structure for this transaction
130 handle[0]=tvb_get_ntohl(tvb, offset+8);
131 handle[1]=tvb_get_ntohl(tvb, offset+12);
135 nbd_trans=(nbd_transaction_t *)wmem_tree_lookup32_array(nbd_info->unacked_pdus, hkey);
137 /* No, so just return the rest of the current packet */
138 return tvb_length(tvb);
142 * Do we have a state structure for this transaction
144 handle[0]=tvb_get_ntohl(tvb, offset+8);
145 handle[1]=tvb_get_ntohl(tvb, offset+12);
146 packet=pinfo->fd->num;
152 nbd_trans=(nbd_transaction_t *)wmem_tree_lookup32_array(nbd_info->acked_pdus, hkey);
154 /* No, so just return the rest of the current packet */
155 return tvb_length(tvb);
158 /* If this is a read response we must add the datalen to
161 if(nbd_trans->type==NBD_CMD_READ){
162 return 16+nbd_trans->datalen;
170 /* Did not really look like a NBD packet after all */
175 dissect_nbd_tcp_pdu(tvbuff_t *tvb, packet_info *pinfo, proto_tree *parent_tree, void* data _U_)
177 guint32 magic, error, packet;
181 proto_tree *tree=NULL;
182 proto_item *item=NULL;
183 conversation_t *conversation;
184 nbd_conv_info_t *nbd_info;
185 nbd_transaction_t *nbd_trans=NULL;
186 wmem_tree_key_t hkey[3];
188 col_set_str(pinfo->cinfo, COL_PROTOCOL, "NBD");
190 col_clear(pinfo->cinfo, COL_INFO);
192 item = proto_tree_add_item(parent_tree, proto_nbd, tvb, 0, -1, ENC_NA);
193 tree = proto_item_add_subtree(item, ett_nbd);
196 magic=tvb_get_ntohl(tvb, offset);
197 proto_tree_add_item(tree, hf_nbd_magic, tvb, offset, 4, ENC_BIG_ENDIAN);
201 /* grab what we need to do the request/response matching */
203 case NBD_REQUEST_MAGIC:
204 case NBD_RESPONSE_MAGIC:
205 handle[0]=tvb_get_ntohl(tvb, offset+4);
206 handle[1]=tvb_get_ntohl(tvb, offset+8);
212 conversation = find_or_create_conversation(pinfo);
215 * Do we already have a state structure for this conv
217 nbd_info = (nbd_conv_info_t *)conversation_get_proto_data(conversation, proto_nbd);
219 /* No. Attach that information to the conversation, and add
220 * it to the list of information structures.
222 nbd_info = wmem_new(wmem_file_scope(), nbd_conv_info_t);
223 nbd_info->unacked_pdus = wmem_tree_new(wmem_file_scope());
224 nbd_info->acked_pdus = wmem_tree_new(wmem_file_scope());
226 conversation_add_proto_data(conversation, proto_nbd, nbd_info);
228 if(!pinfo->fd->flags.visited){
229 if(magic==NBD_REQUEST_MAGIC){
230 /* This is a request */
231 nbd_trans=wmem_new(wmem_file_scope(), nbd_transaction_t);
232 nbd_trans->req_frame=pinfo->fd->num;
233 nbd_trans->rep_frame=0;
234 nbd_trans->req_time=pinfo->fd->abs_ts;
235 nbd_trans->type=tvb_get_ntohl(tvb, offset);
236 nbd_trans->datalen=tvb_get_ntohl(tvb, offset+20);
242 wmem_tree_insert32_array(nbd_info->unacked_pdus, hkey, (void *)nbd_trans);
243 } else if(magic==NBD_RESPONSE_MAGIC){
248 nbd_trans=(nbd_transaction_t *)wmem_tree_lookup32_array(nbd_info->unacked_pdus, hkey);
250 nbd_trans->rep_frame=pinfo->fd->num;
253 hkey[0].key=&nbd_trans->rep_frame;
257 wmem_tree_insert32_array(nbd_info->acked_pdus, hkey, (void *)nbd_trans);
259 hkey[0].key=&nbd_trans->req_frame;
263 wmem_tree_insert32_array(nbd_info->acked_pdus, hkey, (void *)nbd_trans);
267 packet=pinfo->fd->num;
274 nbd_trans=(nbd_transaction_t *)wmem_tree_lookup32_array(nbd_info->acked_pdus, hkey);
276 /* The bloody handles are reused !!! eventhough they are 64 bits.
277 * So we must verify we got the "correct" one
279 if( (magic==NBD_RESPONSE_MAGIC)
281 && (pinfo->fd->num<nbd_trans->req_frame) ){
282 /* must have been the wrong one */
287 /* create a "fake" nbd_trans structure */
288 nbd_trans=wmem_new(wmem_packet_scope(), nbd_transaction_t);
289 nbd_trans->req_frame=0;
290 nbd_trans->rep_frame=0;
291 nbd_trans->req_time=pinfo->fd->abs_ts;
292 nbd_trans->type=0xff;
293 nbd_trans->datalen=0;
296 /* print state tracking in the tree */
297 if(magic==NBD_REQUEST_MAGIC){
298 /* This is a request */
299 if(nbd_trans->rep_frame){
302 it=proto_tree_add_uint(tree, hf_nbd_response_in, tvb, 0, 0, nbd_trans->rep_frame);
303 PROTO_ITEM_SET_GENERATED(it);
305 } else if(magic==NBD_RESPONSE_MAGIC){
306 /* This is a reply */
307 if(nbd_trans->req_frame){
311 it=proto_tree_add_uint(tree, hf_nbd_response_to, tvb, 0, 0, nbd_trans->req_frame);
312 PROTO_ITEM_SET_GENERATED(it);
314 nstime_delta(&ns, &pinfo->fd->abs_ts, &nbd_trans->req_time);
315 it=proto_tree_add_time(tree, hf_nbd_time, tvb, 0, 0, &ns);
316 PROTO_ITEM_SET_GENERATED(it);
322 case NBD_REQUEST_MAGIC:
323 proto_tree_add_item(tree, hf_nbd_type, tvb, offset, 4, ENC_BIG_ENDIAN);
326 proto_tree_add_item(tree, hf_nbd_handle, tvb, offset, 8, ENC_BIG_ENDIAN);
329 from=tvb_get_ntoh64(tvb, offset);
330 proto_tree_add_item(tree, hf_nbd_from, tvb, offset, 8, ENC_BIG_ENDIAN);
333 proto_tree_add_item(tree, hf_nbd_len, tvb, offset, 4, ENC_BIG_ENDIAN);
336 switch(nbd_trans->type){
338 col_add_fstr(pinfo->cinfo, COL_INFO, "Write Request Offset:0x%" G_GINT64_MODIFIER "x Length:%d", from, nbd_trans->datalen);
341 col_add_fstr(pinfo->cinfo, COL_INFO, "Read Request Offset:0x%" G_GINT64_MODIFIER "x Length:%d", from, nbd_trans->datalen);
344 col_set_str(pinfo->cinfo, COL_INFO, "Disconnect Request");
348 if(nbd_trans->type==NBD_CMD_WRITE){
349 proto_tree_add_item(tree, hf_nbd_data, tvb, offset, nbd_trans->datalen, ENC_NA);
352 case NBD_RESPONSE_MAGIC:
353 item=proto_tree_add_uint(tree, hf_nbd_type, tvb, 0, 0, nbd_trans->type);
354 PROTO_ITEM_SET_GENERATED(item);
356 error=tvb_get_ntohl(tvb, offset);
357 proto_tree_add_item(tree, hf_nbd_error, tvb, offset, 4, ENC_BIG_ENDIAN);
360 proto_tree_add_item(tree, hf_nbd_handle, tvb, offset, 8, ENC_BIG_ENDIAN);
363 col_add_fstr(pinfo->cinfo, COL_INFO, "%s Response Error:%d", (nbd_trans->type==NBD_CMD_WRITE)?"Write":"Read", error);
365 if(nbd_trans->type==NBD_CMD_READ){
366 proto_tree_add_item(tree, hf_nbd_data, tvb, offset, nbd_trans->datalen, ENC_NA);
371 return tvb_length(tvb);
375 dissect_nbd_tcp_heur(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void *data)
379 /* We need at least this much to tell whether this is NBD or not */
380 if(tvb_length(tvb)<4){
384 /* Check if it looks like NBD */
385 magic=tvb_get_ntohl(tvb, 0);
387 case NBD_REQUEST_MAGIC:
388 /* requests are 28 bytes or more */
389 if(tvb_length(tvb)<28){
393 type=tvb_get_ntohl(tvb, 4);
403 tcp_dissect_pdus(tvb, pinfo, tree, nbd_desegment, 28, get_nbd_tcp_pdu_len, dissect_nbd_tcp_pdu, data);
405 case NBD_RESPONSE_MAGIC:
406 /* responses are 16 bytes or more */
407 if(tvb_length(tvb)<16){
410 tcp_dissect_pdus(tvb, pinfo, tree, nbd_desegment, 16, get_nbd_tcp_pdu_len, dissect_nbd_tcp_pdu, data);
419 void proto_register_nbd(void)
421 static hf_register_info hf[] = {
423 { "Magic", "nbd.magic", FT_UINT32, BASE_HEX,
424 NULL, 0x0, NULL, HFILL }},
426 { "Type", "nbd.type", FT_UINT32, BASE_DEC,
427 VALS(nbd_type_vals), 0x0, NULL, HFILL }},
429 { "Error", "nbd.error", FT_UINT32, BASE_DEC,
430 NULL, 0x0, NULL, HFILL }},
432 { "Length", "nbd.len", FT_UINT32, BASE_DEC,
433 NULL, 0x0, NULL, HFILL }},
435 { "Handle", "nbd.handle", FT_UINT64, BASE_HEX,
436 NULL, 0x0, NULL, HFILL }},
438 { "From", "nbd.from", FT_UINT64, BASE_HEX,
439 NULL, 0x0, NULL, HFILL }},
440 { &hf_nbd_response_in,
441 { "Response In", "nbd.response_in", FT_FRAMENUM, BASE_NONE,
442 NULL, 0x0, "The response to this NBD request is in this frame", HFILL }},
443 { &hf_nbd_response_to,
444 { "Request In", "nbd.response_to", FT_FRAMENUM, BASE_NONE,
445 NULL, 0x0, "This is a response to the NBD request in this frame", HFILL }},
447 { "Time", "nbd.time", FT_RELATIVE_TIME, BASE_NONE,
448 NULL, 0x0, "The time between the Call and the Reply", HFILL }},
451 { "Data", "nbd.data", FT_BYTES, BASE_NONE,
452 NULL, 0x0, NULL, HFILL }},
457 static gint *ett[] = {
461 module_t *nbd_module;
463 proto_nbd = proto_register_protocol("Network Block Device",
465 proto_register_field_array(proto_nbd, hf, array_length(hf));
466 proto_register_subtree_array(ett, array_length(ett));
468 nbd_module = prefs_register_protocol(proto_nbd, NULL);
469 prefs_register_bool_preference(nbd_module, "desegment_nbd_messages",
470 "Reassemble NBD messages spanning multiple TCP segments",
471 "Whether the NBD dissector should reassemble messages spanning multiple TCP segments."
472 " To use this option, you must also enable \"Allow subdissectors to reassemble TCP streams\" in the TCP protocol settings",
478 proto_reg_handoff_nbd(void)
480 heur_dissector_add("tcp", dissect_nbd_tcp_heur, proto_nbd);