Remove all $Id$ from top of file
[metze/wireshark/wip.git] / epan / dissectors / packet-nbd.c
1 /* packet-nbd.c
2  * Routines for Network Block Device (NBD) dissection.
3  *
4  * Ronnie sahlberg 2006
5  *
6  * Wireshark - Network traffic analyzer
7  * By Gerald Combs <gerald@wireshark.org>
8  * Copyright 1998 Gerald Combs
9  *
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.
14  *
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.
19  *
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.
23  */
24
25 #include "config.h"
26
27 #include <glib.h>
28
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"
34
35 void proto_register_nbd(void);
36 void proto_reg_handoff_nbd(void);
37
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;
49
50 static gint ett_nbd = -1;
51
52
53 static gboolean nbd_desegment = TRUE;
54
55 typedef struct _nbd_transaction_t {
56         guint32 req_frame;
57         guint32 rep_frame;
58         nstime_t req_time;
59         guint32 datalen;
60         guint8 type;
61 } 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 */
65 } nbd_conv_info_t;
66
67
68 #define NBD_REQUEST_MAGIC               0x25609513
69 #define NBD_RESPONSE_MAGIC              0x67446698
70
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"},
78         {0, NULL}
79 };
80
81
82 /* This function will try to determine the complete size of a PDU
83  * based on the information in the header.
84  */
85 static guint
86 get_nbd_tcp_pdu_len(packet_info *pinfo, tvbuff_t *tvb, int offset)
87 {
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];
93         guint32 handle[2];
94
95         magic=tvb_get_ntohl(tvb, offset);
96
97         switch(magic){
98         case NBD_REQUEST_MAGIC:
99                 type=tvb_get_ntohl(tvb, offset+4);
100                 switch(type){
101                 case NBD_CMD_WRITE:
102                         return tvb_get_ntohl(tvb, offset+24)+28;
103                 default:
104                         return 28;
105                 }
106         case NBD_RESPONSE_MAGIC:
107                 /*
108                  * Do we have a conversation for this connection?
109                  */
110                 conversation = find_conversation(pinfo->fd->num,
111                                 &pinfo->src, &pinfo->dst,
112                                 pinfo->ptype,
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);
117                 }
118                 /*
119                  * Do we have a state structure for this conv
120                  */
121                 nbd_info = (nbd_conv_info_t *)conversation_get_proto_data(conversation, proto_nbd);
122                 if (!nbd_info) {
123                         /* No, so just return the rest of the current packet */
124                         return tvb_length(tvb);
125                 }
126                 if(!pinfo->fd->flags.visited){
127                         /*
128                          * Do we have a state structure for this transaction
129                          */
130                         handle[0]=tvb_get_ntohl(tvb, offset+8);
131                         handle[1]=tvb_get_ntohl(tvb, offset+12);
132                         hkey[0].length=2;
133                         hkey[0].key=handle;
134                         hkey[1].length=0;
135                         nbd_trans=(nbd_transaction_t *)wmem_tree_lookup32_array(nbd_info->unacked_pdus, hkey);
136                         if(!nbd_trans){
137                                 /* No, so just return the rest of the current packet */
138                                 return tvb_length(tvb);
139                         }
140                 } else {
141                         /*
142                          * Do we have a state structure for this transaction
143                          */
144                         handle[0]=tvb_get_ntohl(tvb, offset+8);
145                         handle[1]=tvb_get_ntohl(tvb, offset+12);
146                         packet=pinfo->fd->num;
147                         hkey[0].length=1;
148                         hkey[0].key=&packet;
149                         hkey[1].length=2;
150                         hkey[1].key=handle;
151                         hkey[2].length=0;
152                         nbd_trans=(nbd_transaction_t *)wmem_tree_lookup32_array(nbd_info->acked_pdus, hkey);
153                         if(!nbd_trans){
154                                 /* No, so just return the rest of the current packet */
155                                 return tvb_length(tvb);
156                         }
157                 }
158                 /* If this is a read response we must add the datalen to
159                  * the pdu size
160                  */
161                 if(nbd_trans->type==NBD_CMD_READ){
162                         return 16+nbd_trans->datalen;
163                 } else {
164                         return 16;
165                 }
166         default:
167                 break;
168         }
169
170         /* Did not really look like a NBD packet after all */
171         return 0;
172 }
173
174 static int
175 dissect_nbd_tcp_pdu(tvbuff_t *tvb, packet_info *pinfo, proto_tree *parent_tree, void* data _U_)
176 {
177         guint32 magic, error, packet;
178         guint32 handle[2];
179         guint64 from;
180         int offset=0;
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];
187
188         col_set_str(pinfo->cinfo, COL_PROTOCOL, "NBD");
189
190         col_clear(pinfo->cinfo, COL_INFO);
191
192         item = proto_tree_add_item(parent_tree, proto_nbd, tvb, 0, -1, ENC_NA);
193         tree = proto_item_add_subtree(item, ett_nbd);
194
195
196         magic=tvb_get_ntohl(tvb, offset);
197         proto_tree_add_item(tree, hf_nbd_magic, tvb, offset, 4, ENC_BIG_ENDIAN);
198         offset+=4;
199
200
201         /* grab what we need to do the request/response matching */
202         switch(magic){
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);
207                 break;
208         default:
209                 return 4;
210         }
211
212         conversation = find_or_create_conversation(pinfo);
213
214         /*
215          * Do we already have a state structure for this conv
216          */
217         nbd_info = (nbd_conv_info_t *)conversation_get_proto_data(conversation, proto_nbd);
218         if (!nbd_info) {
219                 /* No.  Attach that information to the conversation, and add
220                  * it to the list of information structures.
221                  */
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());
225
226                 conversation_add_proto_data(conversation, proto_nbd, nbd_info);
227         }
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);
237
238                         hkey[0].length=2;
239                         hkey[0].key=handle;
240                         hkey[1].length=0;
241
242                         wmem_tree_insert32_array(nbd_info->unacked_pdus, hkey, (void *)nbd_trans);
243                 } else if(magic==NBD_RESPONSE_MAGIC){
244                         hkey[0].length=2;
245                         hkey[0].key=handle;
246                         hkey[1].length=0;
247
248                         nbd_trans=(nbd_transaction_t *)wmem_tree_lookup32_array(nbd_info->unacked_pdus, hkey);
249                         if(nbd_trans){
250                                 nbd_trans->rep_frame=pinfo->fd->num;
251
252                                 hkey[0].length=1;
253                                 hkey[0].key=&nbd_trans->rep_frame;
254                                 hkey[1].length=2;
255                                 hkey[1].key=handle;
256                                 hkey[2].length=0;
257                                 wmem_tree_insert32_array(nbd_info->acked_pdus, hkey, (void *)nbd_trans);
258                                 hkey[0].length=1;
259                                 hkey[0].key=&nbd_trans->req_frame;
260                                 hkey[1].length=2;
261                                 hkey[1].key=handle;
262                                 hkey[2].length=0;
263                                 wmem_tree_insert32_array(nbd_info->acked_pdus, hkey, (void *)nbd_trans);
264                         }
265                 }
266         } else {
267                 packet=pinfo->fd->num;
268                 hkey[0].length=1;
269                 hkey[0].key=&packet;
270                 hkey[1].length=2;
271                 hkey[1].key=handle;
272                 hkey[2].length=0;
273
274                 nbd_trans=(nbd_transaction_t *)wmem_tree_lookup32_array(nbd_info->acked_pdus, hkey);
275         }
276         /* The bloody handles are reused !!! eventhough they are 64 bits.
277          * So we must verify we got the "correct" one
278          */
279         if( (magic==NBD_RESPONSE_MAGIC)
280         &&  (nbd_trans)
281         &&  (pinfo->fd->num<nbd_trans->req_frame) ){
282                 /* must have been the wrong one */
283                 nbd_trans=NULL;
284         }
285
286         if(!nbd_trans){
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;
294         }
295
296         /* print state tracking in the tree */
297         if(magic==NBD_REQUEST_MAGIC){
298                 /* This is a request */
299                 if(nbd_trans->rep_frame){
300                         proto_item *it;
301
302                         it=proto_tree_add_uint(tree, hf_nbd_response_in, tvb, 0, 0, nbd_trans->rep_frame);
303                         PROTO_ITEM_SET_GENERATED(it);
304                 }
305         } else if(magic==NBD_RESPONSE_MAGIC){
306                 /* This is a reply */
307                 if(nbd_trans->req_frame){
308                         proto_item *it;
309                         nstime_t ns;
310
311                         it=proto_tree_add_uint(tree, hf_nbd_response_to, tvb, 0, 0, nbd_trans->req_frame);
312                         PROTO_ITEM_SET_GENERATED(it);
313
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);
317                 }
318         }
319
320
321         switch(magic){
322         case NBD_REQUEST_MAGIC:
323                 proto_tree_add_item(tree, hf_nbd_type, tvb, offset, 4, ENC_BIG_ENDIAN);
324                 offset+=4;
325
326                 proto_tree_add_item(tree, hf_nbd_handle, tvb, offset, 8, ENC_BIG_ENDIAN);
327                 offset+=8;
328
329                 from=tvb_get_ntoh64(tvb, offset);
330                 proto_tree_add_item(tree, hf_nbd_from, tvb, offset, 8, ENC_BIG_ENDIAN);
331                 offset+=8;
332
333                 proto_tree_add_item(tree, hf_nbd_len, tvb, offset, 4, ENC_BIG_ENDIAN);
334                 offset+=4;
335
336                 switch(nbd_trans->type){
337                 case NBD_CMD_WRITE:
338                         col_add_fstr(pinfo->cinfo, COL_INFO, "Write Request  Offset:0x%" G_GINT64_MODIFIER "x Length:%d", from, nbd_trans->datalen);
339                         break;
340                 case NBD_CMD_READ:
341                         col_add_fstr(pinfo->cinfo, COL_INFO, "Read Request  Offset:0x%" G_GINT64_MODIFIER "x Length:%d", from, nbd_trans->datalen);
342                         break;
343                 case NBD_CMD_DISC:
344                         col_set_str(pinfo->cinfo, COL_INFO, "Disconnect Request");
345                         break;
346                 }
347
348                 if(nbd_trans->type==NBD_CMD_WRITE){
349                         proto_tree_add_item(tree, hf_nbd_data, tvb, offset, nbd_trans->datalen, ENC_NA);
350                 }
351                 break;
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);
355
356                 error=tvb_get_ntohl(tvb, offset);
357                 proto_tree_add_item(tree, hf_nbd_error, tvb, offset, 4, ENC_BIG_ENDIAN);
358                 offset+=4;
359
360                 proto_tree_add_item(tree, hf_nbd_handle, tvb, offset, 8, ENC_BIG_ENDIAN);
361                 offset+=8;
362
363                 col_add_fstr(pinfo->cinfo, COL_INFO, "%s Response  Error:%d", (nbd_trans->type==NBD_CMD_WRITE)?"Write":"Read", error);
364
365                 if(nbd_trans->type==NBD_CMD_READ){
366                         proto_tree_add_item(tree, hf_nbd_data, tvb, offset, nbd_trans->datalen, ENC_NA);
367                 }
368                 break;
369         }
370
371         return tvb_length(tvb);
372 }
373
374 static gboolean
375 dissect_nbd_tcp_heur(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void *data)
376 {
377         guint32 magic, type;
378
379         /* We need at least this much to tell whether this is NBD or not */
380         if(tvb_length(tvb)<4){
381                 return FALSE;
382         }
383
384         /* Check if it looks like NBD */
385         magic=tvb_get_ntohl(tvb, 0);
386         switch(magic){
387         case NBD_REQUEST_MAGIC:
388                 /* requests are 28 bytes or more */
389                 if(tvb_length(tvb)<28){
390                         return FALSE;
391                 }
392                 /* verify type */
393                 type=tvb_get_ntohl(tvb, 4);
394                 switch(type){
395                 case NBD_CMD_READ:
396                 case NBD_CMD_WRITE:
397                 case NBD_CMD_DISC:
398                         break;
399                 default:
400                         return FALSE;
401                 }
402
403                 tcp_dissect_pdus(tvb, pinfo, tree, nbd_desegment, 28, get_nbd_tcp_pdu_len, dissect_nbd_tcp_pdu, data);
404                 return TRUE;
405         case NBD_RESPONSE_MAGIC:
406                 /* responses are 16 bytes or more */
407                 if(tvb_length(tvb)<16){
408                         return FALSE;
409                 }
410                 tcp_dissect_pdus(tvb, pinfo, tree, nbd_desegment, 16, get_nbd_tcp_pdu_len, dissect_nbd_tcp_pdu, data);
411                 return TRUE;
412         default:
413                 break;
414         }
415
416         return FALSE;
417 }
418
419 void proto_register_nbd(void)
420 {
421         static hf_register_info hf[] = {
422         { &hf_nbd_magic,
423         { "Magic", "nbd.magic", FT_UINT32, BASE_HEX,
424                 NULL, 0x0, NULL, HFILL }},
425         { &hf_nbd_type,
426         { "Type", "nbd.type", FT_UINT32, BASE_DEC,
427                 VALS(nbd_type_vals), 0x0, NULL, HFILL }},
428         { &hf_nbd_error,
429         { "Error", "nbd.error", FT_UINT32, BASE_DEC,
430                 NULL, 0x0, NULL, HFILL }},
431         { &hf_nbd_len,
432         { "Length", "nbd.len", FT_UINT32, BASE_DEC,
433                 NULL, 0x0, NULL, HFILL }},
434         { &hf_nbd_handle,
435         { "Handle", "nbd.handle", FT_UINT64, BASE_HEX,
436                 NULL, 0x0, NULL, HFILL }},
437         { &hf_nbd_from,
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 }},
446         { &hf_nbd_time,
447         { "Time", "nbd.time", FT_RELATIVE_TIME, BASE_NONE,
448                 NULL, 0x0, "The time between the Call and the Reply", HFILL }},
449
450         { &hf_nbd_data,
451         { "Data", "nbd.data", FT_BYTES, BASE_NONE,
452                 NULL, 0x0, NULL, HFILL }},
453
454         };
455
456
457         static gint *ett[] = {
458                 &ett_nbd,
459         };
460
461         module_t *nbd_module;
462
463         proto_nbd = proto_register_protocol("Network Block Device",
464                                             "NBD", "nbd");
465         proto_register_field_array(proto_nbd, hf, array_length(hf));
466         proto_register_subtree_array(ett, array_length(ett));
467
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",
473                 &nbd_desegment);
474
475 }
476
477 void
478 proto_reg_handoff_nbd(void)
479 {
480         heur_dissector_add("tcp", dissect_nbd_tcp_heur, proto_nbd);
481 }