MSWSP: add ids for another unknown Property Set
[wireshark-wip.git] / epan / dissectors / packet-nbd.c
blobb19d8958b392288eb1700373c91c3902fdd94025
1 /* packet-nbd.c
2 * Routines for Network Block Device (NBD) dissection.
4 * Ronnie sahlberg 2006
6 * $Id$
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.
27 #include "config.h"
29 #include <glib.h>
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 {
55 guint32 req_frame;
56 guint32 rep_frame;
57 nstime_t req_time;
58 guint32 datalen;
59 guint8 type;
60 } 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 */
64 } nbd_conv_info_t;
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"},
77 {0, NULL}
81 /* This function will try to determine the complete size of a PDU
82 * based on the information in the header.
84 static guint
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];
92 guint32 handle[2];
94 magic=tvb_get_ntohl(tvb, offset);
96 switch(magic){
97 case NBD_REQUEST_MAGIC:
98 type=tvb_get_ntohl(tvb, offset+4);
99 switch(type){
100 case NBD_CMD_WRITE:
101 return tvb_get_ntohl(tvb, offset+24)+28;
102 default:
103 return 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,
111 pinfo->ptype,
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);
121 if (!nbd_info) {
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);
131 hkey[0].length=2;
132 hkey[0].key=handle;
133 hkey[1].length=0;
134 nbd_trans=(nbd_transaction_t *)wmem_tree_lookup32_array(nbd_info->unacked_pdus, hkey);
135 if(!nbd_trans){
136 /* No, so just return the rest of the current packet */
137 return tvb_length(tvb);
139 } else {
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;
146 hkey[0].length=1;
147 hkey[0].key=&packet;
148 hkey[1].length=2;
149 hkey[1].key=handle;
150 hkey[2].length=0;
151 nbd_trans=(nbd_transaction_t *)wmem_tree_lookup32_array(nbd_info->acked_pdus, hkey);
152 if(!nbd_trans){
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
158 * the pdu size
160 if(nbd_trans->type==NBD_CMD_READ){
161 return 16+nbd_trans->datalen;
162 } else {
163 return 16;
165 default:
166 break;
169 /* Did not really look like a NBD packet after all */
170 return 0;
173 static int
174 dissect_nbd_tcp_pdu(tvbuff_t *tvb, packet_info *pinfo, proto_tree *parent_tree, void* data _U_)
176 guint32 magic, error, packet;
177 guint32 handle[2];
178 guint64 from;
179 int offset=0;
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);
197 offset+=4;
200 /* grab what we need to do the request/response matching */
201 switch(magic){
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);
206 break;
207 default:
208 return 4;
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);
217 if (!nbd_info) {
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);
237 hkey[0].length=2;
238 hkey[0].key=handle;
239 hkey[1].length=0;
241 wmem_tree_insert32_array(nbd_info->unacked_pdus, hkey, (void *)nbd_trans);
242 } else if(magic==NBD_RESPONSE_MAGIC){
243 hkey[0].length=2;
244 hkey[0].key=handle;
245 hkey[1].length=0;
247 nbd_trans=(nbd_transaction_t *)wmem_tree_lookup32_array(nbd_info->unacked_pdus, hkey);
248 if(nbd_trans){
249 nbd_trans->rep_frame=pinfo->fd->num;
251 hkey[0].length=1;
252 hkey[0].key=&nbd_trans->rep_frame;
253 hkey[1].length=2;
254 hkey[1].key=handle;
255 hkey[2].length=0;
256 wmem_tree_insert32_array(nbd_info->acked_pdus, hkey, (void *)nbd_trans);
257 hkey[0].length=1;
258 hkey[0].key=&nbd_trans->req_frame;
259 hkey[1].length=2;
260 hkey[1].key=handle;
261 hkey[2].length=0;
262 wmem_tree_insert32_array(nbd_info->acked_pdus, hkey, (void *)nbd_trans);
265 } else {
266 packet=pinfo->fd->num;
267 hkey[0].length=1;
268 hkey[0].key=&packet;
269 hkey[1].length=2;
270 hkey[1].key=handle;
271 hkey[2].length=0;
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)
279 && (nbd_trans)
280 && (pinfo->fd->num<nbd_trans->req_frame) ){
281 /* must have been the wrong one */
282 nbd_trans=NULL;
285 if(!nbd_trans){
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){
299 proto_item *it;
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){
307 proto_item *it;
308 nstime_t ns;
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);
320 switch(magic){
321 case NBD_REQUEST_MAGIC:
322 proto_tree_add_item(tree, hf_nbd_type, tvb, offset, 4, ENC_BIG_ENDIAN);
323 offset+=4;
325 proto_tree_add_item(tree, hf_nbd_handle, tvb, offset, 8, ENC_BIG_ENDIAN);
326 offset+=8;
328 from=tvb_get_ntoh64(tvb, offset);
329 proto_tree_add_item(tree, hf_nbd_from, tvb, offset, 8, ENC_BIG_ENDIAN);
330 offset+=8;
332 proto_tree_add_item(tree, hf_nbd_len, tvb, offset, 4, ENC_BIG_ENDIAN);
333 offset+=4;
335 switch(nbd_trans->type){
336 case NBD_CMD_WRITE:
337 col_add_fstr(pinfo->cinfo, COL_INFO, "Write Request Offset:0x%" G_GINT64_MODIFIER "x Length:%d", from, nbd_trans->datalen);
338 break;
339 case NBD_CMD_READ:
340 col_add_fstr(pinfo->cinfo, COL_INFO, "Read Request Offset:0x%" G_GINT64_MODIFIER "x Length:%d", from, nbd_trans->datalen);
341 break;
342 case NBD_CMD_DISC:
343 col_set_str(pinfo->cinfo, COL_INFO, "Disconnect Request");
344 break;
347 if(nbd_trans->type==NBD_CMD_WRITE){
348 proto_tree_add_item(tree, hf_nbd_data, tvb, offset, nbd_trans->datalen, ENC_NA);
350 break;
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);
357 offset+=4;
359 proto_tree_add_item(tree, hf_nbd_handle, tvb, offset, 8, ENC_BIG_ENDIAN);
360 offset+=8;
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);
367 break;
370 return tvb_length(tvb);
373 static gboolean
374 dissect_nbd_tcp_heur(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void *data)
376 guint32 magic, type;
378 /* We need at least this much to tell whether this is NBD or not */
379 if(tvb_length(tvb)<4){
380 return FALSE;
383 /* Check if it looks like NBD */
384 magic=tvb_get_ntohl(tvb, 0);
385 switch(magic){
386 case NBD_REQUEST_MAGIC:
387 /* requests are 28 bytes or more */
388 if(tvb_length(tvb)<28){
389 return FALSE;
391 /* verify type */
392 type=tvb_get_ntohl(tvb, 4);
393 switch(type){
394 case NBD_CMD_READ:
395 case NBD_CMD_WRITE:
396 case NBD_CMD_DISC:
397 break;
398 default:
399 return FALSE;
402 tcp_dissect_pdus(tvb, pinfo, tree, nbd_desegment, 28, get_nbd_tcp_pdu_len, dissect_nbd_tcp_pdu, data);
403 return TRUE;
404 case NBD_RESPONSE_MAGIC:
405 /* responses are 16 bytes or more */
406 if(tvb_length(tvb)<16){
407 return FALSE;
409 tcp_dissect_pdus(tvb, pinfo, tree, nbd_desegment, 16, get_nbd_tcp_pdu_len, dissect_nbd_tcp_pdu, data);
410 return TRUE;
411 default:
412 break;
415 return FALSE;
418 void proto_register_nbd(void)
420 static hf_register_info hf[] = {
421 { &hf_nbd_magic,
422 { "Magic", "nbd.magic", FT_UINT32, BASE_HEX,
423 NULL, 0x0, NULL, HFILL }},
424 { &hf_nbd_type,
425 { "Type", "nbd.type", FT_UINT32, BASE_DEC,
426 VALS(nbd_type_vals), 0x0, NULL, HFILL }},
427 { &hf_nbd_error,
428 { "Error", "nbd.error", FT_UINT32, BASE_DEC,
429 NULL, 0x0, NULL, HFILL }},
430 { &hf_nbd_len,
431 { "Length", "nbd.len", FT_UINT32, BASE_DEC,
432 NULL, 0x0, NULL, HFILL }},
433 { &hf_nbd_handle,
434 { "Handle", "nbd.handle", FT_UINT64, BASE_HEX,
435 NULL, 0x0, NULL, HFILL }},
436 { &hf_nbd_from,
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 }},
445 { &hf_nbd_time,
446 { "Time", "nbd.time", FT_RELATIVE_TIME, BASE_NONE,
447 NULL, 0x0, "The time between the Call and the Reply", HFILL }},
449 { &hf_nbd_data,
450 { "Data", "nbd.data", FT_BYTES, BASE_NONE,
451 NULL, 0x0, NULL, HFILL }},
456 static gint *ett[] = {
457 &ett_nbd,
460 module_t *nbd_module;
462 proto_nbd = proto_register_protocol("Network Block Device",
463 "NBD", "nbd");
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",
472 &nbd_desegment);
476 void
477 proto_reg_handoff_nbd(void)
479 heur_dissector_add("tcp", dissect_nbd_tcp_heur, proto_nbd);