Don't do fcn calls in arg of g_?to??(); Macro may very well eval args multiple times.
[obnox/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  * $Id$
7  *
8  * Wireshark - Network traffic analyzer
9  * By Gerald Combs <gerald@wireshark.org>
10  * Copyright 1998 Gerald Combs
11  *
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.
16  *
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.
21  *
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., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
25  */
26
27 #ifdef HAVE_CONFIG_H
28 # include "config.h"
29 #endif
30
31 #include <stdlib.h>
32 #include <ctype.h>
33
34 #include <epan/prefs.h>
35
36 #include <glib.h>
37 #include <epan/packet.h>
38 #include <epan/conversation.h>
39 #include <epan/emem.h>
40 #include "packet-tcp.h"
41
42 static gint proto_nbd                   = -1;
43 static int hf_nbd_magic = -1;
44 static int hf_nbd_type = -1;
45 static int hf_nbd_error = -1;
46 static int hf_nbd_handle = -1;
47 static int hf_nbd_from = -1;
48 static int hf_nbd_len = -1;
49 static int hf_nbd_response_in = -1;
50 static int hf_nbd_response_to = -1;
51 static int hf_nbd_time = -1;
52 static int hf_nbd_data = -1;
53
54 static gint ett_nbd = -1;
55
56
57 static gboolean nbd_desegment = TRUE;
58
59 typedef struct _nbd_transaction_t {
60         guint32 req_frame;
61         guint32 rep_frame;
62         nstime_t req_time;
63         guint32 datalen;
64         guint8 type;
65 } nbd_transaction_t;
66 typedef struct _nbd_conv_info_t {
67         emem_tree_t *unacked_pdus;    /* indexed by handle, whichs wraps quite frequently  */
68         emem_tree_t *acked_pdus;    /* indexed by packet# and handle */
69 } nbd_conv_info_t;
70
71
72 #define NBD_REQUEST_MAGIC               0x25609513
73 #define NBD_RESPONSE_MAGIC              0x67446698
74
75 #define NBD_CMD_READ                    0
76 #define NBD_CMD_WRITE                   1
77 #define NBD_CMD_DISC                    2
78 static const value_string nbd_type_vals[] = {
79         {NBD_CMD_READ,  "NBD_CMD_READ"},
80         {NBD_CMD_WRITE, "NBD_CMD_WRITE"},
81         {NBD_CMD_DISC,  "NBD_CMD_DISC"},
82         {0, NULL}
83 };
84
85
86 /* This function will try to determine the complete size of a PDU
87  * based on the information in the header.
88  */
89 static guint
90 get_nbd_tcp_pdu_len(packet_info *pinfo, tvbuff_t *tvb, int offset)
91 {
92         guint32 magic, type, packet;
93         conversation_t *conversation;
94         nbd_conv_info_t *nbd_info;
95         nbd_transaction_t *nbd_trans=NULL;
96         emem_tree_key_t hkey[3];
97         guint32 handle[2];
98
99         magic=tvb_get_ntohl(tvb, offset);
100
101         switch(magic){
102         case NBD_REQUEST_MAGIC:
103                 type=tvb_get_ntohl(tvb, offset+4);
104                 switch(type){
105                 case NBD_CMD_WRITE:
106                         return tvb_get_ntohl(tvb, offset+24)+28;
107                 default:
108                         return 28;
109                 }
110         case NBD_RESPONSE_MAGIC:
111                 /*
112                  * Do we have a conversation for this connection?
113                  */
114                 conversation = find_conversation(pinfo->fd->num,
115                                 &pinfo->src, &pinfo->dst,
116                                 pinfo->ptype,
117                                 pinfo->srcport, pinfo->destport, 0);
118                 if (conversation == NULL) {
119                         /* No, so just return the rest of the current packet */
120                         return tvb_length(tvb);
121                 }
122                 /*
123                  * Do we have a state structure for this conv
124                  */
125                 nbd_info = conversation_get_proto_data(conversation, proto_nbd);
126                 if (!nbd_info) {
127                         /* No, so just return the rest of the current packet */
128                         return tvb_length(tvb);
129                 }
130                 if(!pinfo->fd->flags.visited){
131                         /*
132                          * Do we have a state structure for this transaction
133                          */
134                         handle[0]=tvb_get_ntohl(tvb, offset+8);
135                         handle[1]=tvb_get_ntohl(tvb, offset+12);
136                         hkey[0].length=2;
137                         hkey[0].key=handle;
138                         hkey[1].length=0;
139                         nbd_trans=se_tree_lookup32_array(nbd_info->unacked_pdus, hkey);
140                         if(!nbd_trans){
141                                 /* No, so just return the rest of the current packet */
142                                 return tvb_length(tvb);
143                         }
144                 } else {
145                         /*
146                          * Do we have a state structure for this transaction
147                          */
148                         handle[0]=tvb_get_ntohl(tvb, offset+8);
149                         handle[1]=tvb_get_ntohl(tvb, offset+12);
150                         packet=pinfo->fd->num;
151                         hkey[0].length=1;
152                         hkey[0].key=&packet;
153                         hkey[1].length=2;
154                         hkey[1].key=handle;
155                         hkey[2].length=0;
156                         nbd_trans=se_tree_lookup32_array(nbd_info->acked_pdus, hkey);
157                         if(!nbd_trans){
158                                 /* No, so just return the rest of the current packet */
159                                 return tvb_length(tvb);
160                         }
161                 }
162                 /* If this is a read response we must add the datalen to
163                  * the pdu size
164                  */
165                 if(nbd_trans->type==NBD_CMD_READ){
166                         return 16+nbd_trans->datalen;
167                 } else {
168                         return 16;
169                 }
170         default:
171                 break;
172         }
173
174         /* Did not really look like a NBD packet after all */
175         return 0;
176 }
177
178 static void
179 dissect_nbd_tcp_pdu(tvbuff_t *tvb, packet_info *pinfo, proto_tree *parent_tree)
180 {
181         guint32 magic, error, packet;
182         guint32 handle[2];
183         guint64 from;
184         int offset=0;
185         proto_tree *tree=NULL;
186         proto_item *item=NULL;
187         conversation_t *conversation;
188         nbd_conv_info_t *nbd_info;
189         nbd_transaction_t *nbd_trans=NULL;
190         emem_tree_key_t hkey[3];
191
192         col_set_str(pinfo->cinfo, COL_PROTOCOL, "NBD");
193
194         col_clear(pinfo->cinfo, COL_INFO);
195
196         item = proto_tree_add_item(parent_tree, proto_nbd, tvb, 0, -1, ENC_NA);
197         tree = proto_item_add_subtree(item, ett_nbd);
198
199
200         magic=tvb_get_ntohl(tvb, offset);
201         proto_tree_add_item(tree, hf_nbd_magic, tvb, offset, 4, ENC_BIG_ENDIAN);
202         offset+=4;
203
204
205         /* grab what we need to do the request/response matching */
206         switch(magic){
207         case NBD_REQUEST_MAGIC:
208         case NBD_RESPONSE_MAGIC:
209                 handle[0]=tvb_get_ntohl(tvb, offset+4);
210                 handle[1]=tvb_get_ntohl(tvb, offset+8);
211                 break;
212         default:
213                 return;
214         }
215
216         conversation = find_or_create_conversation(pinfo);
217
218         /*
219          * Do we already have a state structure for this conv
220          */
221         nbd_info = conversation_get_proto_data(conversation, proto_nbd);
222         if (!nbd_info) {
223                 /* No.  Attach that information to the conversation, and add
224                  * it to the list of information structures.
225                  */
226                 nbd_info = se_alloc(sizeof(nbd_conv_info_t));
227                 nbd_info->unacked_pdus=se_tree_create_non_persistent(EMEM_TREE_TYPE_RED_BLACK, "nbd_unacked_pdus");
228                 nbd_info->acked_pdus=se_tree_create_non_persistent(EMEM_TREE_TYPE_RED_BLACK, "nbd_acked_pdus");
229
230                 conversation_add_proto_data(conversation, proto_nbd, nbd_info);
231         }
232         if(!pinfo->fd->flags.visited){
233                 if(magic==NBD_REQUEST_MAGIC){
234                         /* This is a request */
235                         nbd_trans=se_alloc(sizeof(nbd_transaction_t));
236                         nbd_trans->req_frame=pinfo->fd->num;
237                         nbd_trans->rep_frame=0;
238                         nbd_trans->req_time=pinfo->fd->abs_ts;
239                         nbd_trans->type=tvb_get_ntohl(tvb, offset);
240                         nbd_trans->datalen=tvb_get_ntohl(tvb, offset+20);
241
242                         hkey[0].length=2;
243                         hkey[0].key=handle;
244                         hkey[1].length=0;
245
246                         se_tree_insert32_array(nbd_info->unacked_pdus, hkey, (void *)nbd_trans);
247                 } else if(magic==NBD_RESPONSE_MAGIC){
248                         hkey[0].length=2;
249                         hkey[0].key=handle;
250                         hkey[1].length=0;
251
252                         nbd_trans=se_tree_lookup32_array(nbd_info->unacked_pdus, hkey);
253                         if(nbd_trans){
254                                 nbd_trans->rep_frame=pinfo->fd->num;
255
256                                 hkey[0].length=1;
257                                 hkey[0].key=&nbd_trans->rep_frame;
258                                 hkey[1].length=2;
259                                 hkey[1].key=handle;
260                                 hkey[2].length=0;
261                                 se_tree_insert32_array(nbd_info->acked_pdus, hkey, (void *)nbd_trans);
262                                 hkey[0].length=1;
263                                 hkey[0].key=&nbd_trans->req_frame;
264                                 hkey[1].length=2;
265                                 hkey[1].key=handle;
266                                 hkey[2].length=0;
267                                 se_tree_insert32_array(nbd_info->acked_pdus, hkey, (void *)nbd_trans);
268                         }
269                 }
270         } else {
271                 packet=pinfo->fd->num;
272                 hkey[0].length=1;
273                 hkey[0].key=&packet;
274                 hkey[1].length=2;
275                 hkey[1].key=handle;
276                 hkey[2].length=0;
277
278                 nbd_trans=se_tree_lookup32_array(nbd_info->acked_pdus, hkey);
279         }
280         /* The bloody handles are reused !!! eventhough they are 64 bits.
281          * So we must verify we got the "correct" one
282          */
283         if( (magic==NBD_RESPONSE_MAGIC)
284         &&  (nbd_trans)
285         &&  (pinfo->fd->num<nbd_trans->req_frame) ){
286                 /* must have been the wrong one */
287                 nbd_trans=NULL;
288         }
289
290         if(!nbd_trans){
291                 /* create a "fake" nbd_trans structure */
292                 nbd_trans=ep_alloc(sizeof(nbd_transaction_t));
293                 nbd_trans->req_frame=0;
294                 nbd_trans->rep_frame=0;
295                 nbd_trans->req_time=pinfo->fd->abs_ts;
296                 nbd_trans->type=0xff;
297                 nbd_trans->datalen=0;
298         }
299
300         /* print state tracking in the tree */
301         if(magic==NBD_REQUEST_MAGIC){
302                 /* This is a request */
303                 if(nbd_trans->rep_frame){
304                         proto_item *it;
305
306                         it=proto_tree_add_uint(tree, hf_nbd_response_in, tvb, 0, 0, nbd_trans->rep_frame);
307                         PROTO_ITEM_SET_GENERATED(it);
308                 }
309         } else if(magic==NBD_RESPONSE_MAGIC){
310                 /* This is a reply */
311                 if(nbd_trans->req_frame){
312                         proto_item *it;
313                         nstime_t ns;
314
315                         it=proto_tree_add_uint(tree, hf_nbd_response_to, tvb, 0, 0, nbd_trans->req_frame);
316                         PROTO_ITEM_SET_GENERATED(it);
317
318                         nstime_delta(&ns, &pinfo->fd->abs_ts, &nbd_trans->req_time);
319                         it=proto_tree_add_time(tree, hf_nbd_time, tvb, 0, 0, &ns);
320                         PROTO_ITEM_SET_GENERATED(it);
321                 }
322         }
323
324
325         switch(magic){
326         case NBD_REQUEST_MAGIC:
327                 proto_tree_add_item(tree, hf_nbd_type, tvb, offset, 4, ENC_BIG_ENDIAN);
328                 offset+=4;
329
330                 proto_tree_add_item(tree, hf_nbd_handle, tvb, offset, 8, ENC_BIG_ENDIAN);
331                 offset+=8;
332
333                 from=tvb_get_ntoh64(tvb, offset);
334                 proto_tree_add_item(tree, hf_nbd_from, tvb, offset, 8, ENC_BIG_ENDIAN);
335                 offset+=8;
336
337                 proto_tree_add_item(tree, hf_nbd_len, tvb, offset, 4, ENC_BIG_ENDIAN);
338                 offset+=4;
339
340                 if(check_col(pinfo->cinfo, COL_INFO)){
341                         switch(nbd_trans->type){
342                         case NBD_CMD_WRITE:
343                                 col_add_fstr(pinfo->cinfo, COL_INFO, "Write Request  Offset:0x%" G_GINT64_MODIFIER "x Length:%d", from, nbd_trans->datalen);
344                                 break;
345                         case NBD_CMD_READ:
346                                 col_add_fstr(pinfo->cinfo, COL_INFO, "Read Request  Offset:0x%" G_GINT64_MODIFIER "x Length:%d", from, nbd_trans->datalen);
347                                 break;
348                         case NBD_CMD_DISC:
349                                 col_set_str(pinfo->cinfo, COL_INFO, "Disconnect Request");
350                                 break;
351                         }
352                 }
353
354                 if(nbd_trans->type==NBD_CMD_WRITE){
355                         proto_tree_add_item(tree, hf_nbd_data, tvb, offset, nbd_trans->datalen, ENC_NA);
356                 }
357                 break;
358         case NBD_RESPONSE_MAGIC:
359                 item=proto_tree_add_uint(tree, hf_nbd_type, tvb, 0, 0, nbd_trans->type);
360                 PROTO_ITEM_SET_GENERATED(item);
361
362                 error=tvb_get_ntohl(tvb, offset);
363                 proto_tree_add_item(tree, hf_nbd_error, tvb, offset, 4, ENC_BIG_ENDIAN);
364                 offset+=4;
365
366                 proto_tree_add_item(tree, hf_nbd_handle, tvb, offset, 8, ENC_BIG_ENDIAN);
367                 offset+=8;
368
369                 if(check_col(pinfo->cinfo, COL_INFO)){
370                         col_add_fstr(pinfo->cinfo, COL_INFO, "%s Response  Error:%d", (nbd_trans->type==NBD_CMD_WRITE)?"Write":"Read", error);
371                 }
372
373                 if(nbd_trans->type==NBD_CMD_READ){
374                         proto_tree_add_item(tree, hf_nbd_data, tvb, offset, nbd_trans->datalen, ENC_NA);
375                 }
376                 break;
377         }
378
379         return;
380 }
381
382 static gboolean
383 dissect_nbd_tcp_heur(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree)
384 {
385         guint32 magic, type;
386
387         /* We need at least this much to tell whether this is NBD or not */
388         if(tvb_length(tvb)<4){
389                 return FALSE;
390         }
391
392         /* Check if it looks like NBD */
393         magic=tvb_get_ntohl(tvb, 0);
394         switch(magic){
395         case NBD_REQUEST_MAGIC:
396                 /* requests are 28 bytes or more */
397                 if(tvb_length(tvb)<28){
398                         return FALSE;
399                 }
400                 /* verify type */
401                 type=tvb_get_ntohl(tvb, 4);
402                 switch(type){
403                 case NBD_CMD_READ:
404                 case NBD_CMD_WRITE:
405                 case NBD_CMD_DISC:
406                         break;
407                 default:
408                         return FALSE;
409                 }
410
411                 tcp_dissect_pdus(tvb, pinfo, tree, nbd_desegment, 28, get_nbd_tcp_pdu_len, dissect_nbd_tcp_pdu);
412                 return TRUE;
413         case NBD_RESPONSE_MAGIC:
414                 /* responses are 16 bytes or more */
415                 if(tvb_length(tvb)<16){
416                         return FALSE;
417                 }
418                 tcp_dissect_pdus(tvb, pinfo, tree, nbd_desegment, 16, get_nbd_tcp_pdu_len, dissect_nbd_tcp_pdu);
419                 return TRUE;
420         default:
421                 break;
422         }
423
424         return FALSE;
425 }
426
427 void proto_register_nbd(void)
428 {
429         static hf_register_info hf[] = {
430         { &hf_nbd_magic,
431         { "Magic", "nbd.magic", FT_UINT32, BASE_HEX,
432                 NULL, 0x0, NULL, HFILL }},
433         { &hf_nbd_type,
434         { "Type", "nbd.type", FT_UINT32, BASE_DEC,
435                 VALS(nbd_type_vals), 0x0, NULL, HFILL }},
436         { &hf_nbd_error,
437         { "Error", "nbd.error", FT_UINT32, BASE_DEC,
438                 NULL, 0x0, NULL, HFILL }},
439         { &hf_nbd_len,
440         { "Length", "nbd.len", FT_UINT32, BASE_DEC,
441                 NULL, 0x0, NULL, HFILL }},
442         { &hf_nbd_handle,
443         { "Handle", "nbd.handle", FT_UINT64, BASE_HEX,
444                 NULL, 0x0, NULL, HFILL }},
445         { &hf_nbd_from,
446         { "From", "nbd.from", FT_UINT64, BASE_HEX,
447                 NULL, 0x0, NULL, HFILL }},
448         { &hf_nbd_response_in,
449         { "Response In", "nbd.response_in", FT_FRAMENUM, BASE_NONE,
450                 NULL, 0x0, "The response to this NBD request is in this frame", HFILL }},
451         { &hf_nbd_response_to,
452         { "Request In", "nbd.response_to", FT_FRAMENUM, BASE_NONE,
453                 NULL, 0x0, "This is a response to the NBD request in this frame", HFILL }},
454         { &hf_nbd_time,
455         { "Time", "nbd.time", FT_RELATIVE_TIME, BASE_NONE,
456                 NULL, 0x0, "The time between the Call and the Reply", HFILL }},
457
458         { &hf_nbd_data,
459         { "Data", "nbd.data", FT_BYTES, BASE_NONE,
460                 NULL, 0x0, NULL, HFILL }},
461
462         };
463
464
465         static gint *ett[] = {
466                 &ett_nbd,
467         };
468
469         module_t *nbd_module;
470
471         proto_nbd = proto_register_protocol("Network Block Device",
472                                             "NBD", "nbd");
473         proto_register_field_array(proto_nbd, hf, array_length(hf));
474         proto_register_subtree_array(ett, array_length(ett));
475
476         nbd_module = prefs_register_protocol(proto_nbd, NULL);
477         prefs_register_bool_preference(nbd_module, "desegment_nbd_messages",
478                 "Reassemble NBD messages spanning multiple TCP segments",
479                 "Whether the NBD dissector should reassemble messages spanning multiple TCP segments."
480                 " To use this option, you must also enable \"Allow subdissectors to reassemble TCP streams\" in the TCP protocol settings",
481                 &nbd_desegment);
482
483 }
484
485 void
486 proto_reg_handoff_nbd(void)
487 {
488         heur_dissector_add("tcp", dissect_nbd_tcp_heur, proto_nbd);
489 }