1 /* packet-usb-masstorage.c
5 * usb mass storage dissector
8 * This program is free software; you can redistribute it and/or
9 * modify it under the terms of the GNU General Public License
10 * as published by the Free Software Foundation; either version 2
11 * of the License, or (at your option) any later version.
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU General Public License for more details.
18 * You should have received a copy of the GNU General Public License
19 * along with this program; if not, write to the Free Software
20 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
27 #include <epan/packet.h>
28 #include <epan/wmem/wmem.h>
29 #include <epan/conversation.h>
30 #include "packet-usb.h"
31 #include "packet-scsi.h"
33 /* protocols and header fields */
34 static int proto_usb_ms
= -1;
35 static int hf_usb_ms_dCBWSignature
= -1;
36 static int hf_usb_ms_dCBWTag
= -1;
37 static int hf_usb_ms_dCBWDataTransferLength
= -1;
38 static int hf_usb_ms_dCBWFlags
= -1;
39 static int hf_usb_ms_dCBWLUN
= -1;
40 static int hf_usb_ms_dCBWCBLength
= -1;
41 static int hf_usb_ms_dCSWSignature
= -1;
42 static int hf_usb_ms_dCSWDataResidue
= -1;
43 static int hf_usb_ms_dCSWStatus
= -1;
44 static int hf_usb_ms_request
= -1;
45 static int hf_usb_ms_value
= -1;
46 static int hf_usb_ms_index
= -1;
47 static int hf_usb_ms_length
= -1;
48 static int hf_usb_ms_maxlun
= -1;
50 static gint ett_usb_ms
= -1;
53 /* there is one such structure for each masstorage conversation */
54 typedef struct _usb_ms_conv_info_t
{
55 wmem_tree_t
*itl
; /* indexed by LUN */
56 wmem_tree_t
*itlq
; /* pinfo->fd->num */
60 static const value_string status_vals
[] = {
61 {0x00, "Command Passed"},
62 {0x01, "Command Failed"},
63 {0x02, "Phase Error"},
71 dissect_usb_ms_reset(packet_info
*pinfo _U_
, proto_tree
*tree
, tvbuff_t
*tvb
, int offset
, gboolean is_request
, usb_trans_info_t
*usb_trans_info _U_
, usb_conv_info_t
*usb_conv_info _U_
)
74 proto_tree_add_item(tree
, hf_usb_ms_value
, tvb
, offset
, 2, ENC_BIG_ENDIAN
);
77 proto_tree_add_item(tree
, hf_usb_ms_index
, tvb
, offset
, 2, ENC_BIG_ENDIAN
);
80 proto_tree_add_item(tree
, hf_usb_ms_length
, tvb
, offset
, 2, ENC_BIG_ENDIAN
);
83 /* no data in reset response */
88 dissect_usb_ms_get_max_lun(packet_info
*pinfo _U_
, proto_tree
*tree
, tvbuff_t
*tvb
, int offset
, gboolean is_request
, usb_trans_info_t
*usb_trans_info _U_
, usb_conv_info_t
*usb_conv_info _U_
)
91 proto_tree_add_item(tree
, hf_usb_ms_value
, tvb
, offset
, 2, ENC_BIG_ENDIAN
);
94 proto_tree_add_item(tree
, hf_usb_ms_index
, tvb
, offset
, 2, ENC_BIG_ENDIAN
);
97 proto_tree_add_item(tree
, hf_usb_ms_length
, tvb
, offset
, 2, ENC_BIG_ENDIAN
);
100 proto_tree_add_item(tree
, hf_usb_ms_maxlun
, tvb
, offset
, 1, ENC_BIG_ENDIAN
);
106 typedef void (*usb_setup_dissector
)(packet_info
*pinfo
, proto_tree
*tree
, tvbuff_t
*tvb
, int offset
, gboolean is_request
, usb_trans_info_t
*usb_trans_info
, usb_conv_info_t
*usb_conv_info
);
108 typedef struct _usb_setup_dissector_table_t
{
110 usb_setup_dissector dissector
;
111 } usb_setup_dissector_table_t
;
112 #define USB_SETUP_RESET 0xff
113 #define USB_SETUP_GET_MAX_LUN 0xfe
114 static const usb_setup_dissector_table_t setup_dissectors
[] = {
115 {USB_SETUP_RESET
, dissect_usb_ms_reset
},
116 {USB_SETUP_GET_MAX_LUN
, dissect_usb_ms_get_max_lun
},
119 static const value_string setup_request_names_vals
[] = {
120 {USB_SETUP_RESET
, "RESET"},
121 {USB_SETUP_GET_MAX_LUN
, "GET MAX LUN"},
125 /* Dissector for mass storage control .
126 * Returns TRUE if a class specific dissector was found
127 * and FALSE othervise.
130 dissect_usb_ms_control(tvbuff_t
*tvb
, packet_info
*pinfo
, proto_tree
*tree
, void *data
)
133 usb_conv_info_t
*usb_conv_info
= (usb_conv_info_t
*)data
;
134 usb_trans_info_t
*usb_trans_info
= usb_conv_info
->usb_trans_info
;
136 usb_setup_dissector dissector
= NULL
;
137 const usb_setup_dissector_table_t
*tmp
;
140 is_request
=(pinfo
->srcport
==NO_ENDPOINT
);
142 /* See if we can find a class specific dissector for this request */
143 for(tmp
=setup_dissectors
;tmp
->dissector
;tmp
++){
144 if (tmp
->request
== usb_trans_info
->setup
.request
){
145 dissector
=tmp
->dissector
;
149 /* No we could not find any class specific dissector for this request
150 * return FALSE and let USB try any of the standard requests.
157 col_set_str(pinfo
->cinfo
, COL_PROTOCOL
, "USBMS");
159 col_add_fstr(pinfo
->cinfo
, COL_INFO
, "%s %s",
160 val_to_str(usb_trans_info
->setup
.request
, setup_request_names_vals
, "Unknown type %x"),
161 is_request
?"Request":"Response");
164 proto_tree_add_item(tree
, hf_usb_ms_request
, tvb
, offset
, 1, ENC_LITTLE_ENDIAN
);
168 dissector(pinfo
, tree
, tvb
, offset
, is_request
, usb_trans_info
, usb_conv_info
);
173 /* dissector for mass storage bulk data */
175 dissect_usb_ms_bulk(tvbuff_t
*tvb
, packet_info
*pinfo
, proto_tree
*parent_tree
, void* data
)
177 usb_conv_info_t
*usb_conv_info
= (usb_conv_info_t
*)data
;
178 usb_ms_conv_info_t
*usb_ms_conv_info
;
179 proto_tree
*tree
=NULL
;
186 /* verify that we do have a usb_ms_conv_info */
187 usb_ms_conv_info
=(usb_ms_conv_info_t
*)usb_conv_info
->class_data
;
188 if(!usb_ms_conv_info
){
189 usb_ms_conv_info
=wmem_new(wmem_file_scope(), usb_ms_conv_info_t
);
190 usb_ms_conv_info
->itl
=wmem_tree_new(wmem_file_scope());
191 usb_ms_conv_info
->itlq
=wmem_tree_new(wmem_file_scope());
192 usb_conv_info
->class_data
=usb_ms_conv_info
;
196 is_request
=(pinfo
->srcport
==NO_ENDPOINT
);
198 col_set_str(pinfo
->cinfo
, COL_PROTOCOL
, "USBMS");
200 col_clear(pinfo
->cinfo
, COL_INFO
);
204 proto_item
*ti
= NULL
;
205 ti
= proto_tree_add_protocol_format(parent_tree
, proto_usb_ms
, tvb
, 0, -1, "USB Mass Storage");
207 tree
= proto_item_add_subtree(ti
, ett_usb_ms
);
210 signature
=tvb_get_letohl(tvb
, offset
);
214 * SCSI CDB inside CBW
216 if(is_request
&&(signature
==0x43425355)&&(tvb_length(tvb
)==31)){
223 proto_tree_add_item(tree
, hf_usb_ms_dCBWSignature
, tvb
, offset
, 4, ENC_LITTLE_ENDIAN
);
227 proto_tree_add_item(tree
, hf_usb_ms_dCBWTag
, tvb
, offset
, 4, ENC_LITTLE_ENDIAN
);
230 /* dCBWDataTransferLength */
231 proto_tree_add_item(tree
, hf_usb_ms_dCBWDataTransferLength
, tvb
, offset
, 4, ENC_LITTLE_ENDIAN
);
232 datalen
=tvb_get_letohl(tvb
, offset
);
236 proto_tree_add_item(tree
, hf_usb_ms_dCBWFlags
, tvb
, offset
, 1, ENC_LITTLE_ENDIAN
);
237 flags
=tvb_get_guint8(tvb
, offset
);
241 proto_tree_add_item(tree
, hf_usb_ms_dCBWLUN
, tvb
, offset
, 1, ENC_LITTLE_ENDIAN
);
242 lun
=tvb_get_guint8(tvb
, offset
)&0x0f;
245 /* make sure we have a ITL structure for this LUN */
246 itl
=(itl_nexus_t
*)wmem_tree_lookup32(usb_ms_conv_info
->itl
, lun
);
248 itl
=wmem_new(wmem_file_scope(), itl_nexus_t
);
250 itl
->conversation
=NULL
;
251 wmem_tree_insert32(usb_ms_conv_info
->itl
, lun
, itl
);
254 /* make sure we have an ITLQ structure for this LUN/transaction */
255 itlq
=(itlq_nexus_t
*)wmem_tree_lookup32(usb_ms_conv_info
->itlq
, pinfo
->fd
->num
);
257 itlq
=wmem_new(wmem_file_scope(), itlq_nexus_t
);
259 itlq
->scsi_opcode
=0xffff;
263 itlq
->task_flags
|=SCSI_DATA_READ
;
265 itlq
->task_flags
|=SCSI_DATA_WRITE
;
268 itlq
->data_length
=datalen
;
269 itlq
->bidir_data_length
=0;
270 itlq
->fc_time
=pinfo
->fd
->abs_ts
;
271 itlq
->first_exchange_frame
=pinfo
->fd
->num
;
272 itlq
->last_exchange_frame
=0;
275 itlq
->extra_data
=NULL
;
276 wmem_tree_insert32(usb_ms_conv_info
->itlq
, pinfo
->fd
->num
, itlq
);
280 proto_tree_add_item(tree
, hf_usb_ms_dCBWCBLength
, tvb
, offset
, 1, ENC_LITTLE_ENDIAN
);
281 cdbrlen
=tvb_get_guint8(tvb
, offset
)&0x1f;
285 if(cdblen
>tvb_length_remaining(tvb
, offset
)){
286 cdblen
=tvb_length_remaining(tvb
, offset
);
289 cdb_tvb
=tvb_new_subset(tvb
, offset
, cdblen
, cdbrlen
);
290 dissect_scsi_cdb(cdb_tvb
, pinfo
, parent_tree
, SCSI_DEV_UNKNOWN
, itlq
, itl
);
292 return tvb_length(tvb
);
297 * SCSI RESPONSE inside CSW
299 if((!is_request
)&&(signature
==0x53425355)&&(tvb_length(tvb
)==13)){
303 proto_tree_add_item(tree
, hf_usb_ms_dCSWSignature
, tvb
, offset
, 4, ENC_LITTLE_ENDIAN
);
307 proto_tree_add_item(tree
, hf_usb_ms_dCBWTag
, tvb
, offset
, 4, ENC_LITTLE_ENDIAN
);
310 /* dCSWDataResidue */
311 proto_tree_add_item(tree
, hf_usb_ms_dCSWDataResidue
, tvb
, offset
, 4, ENC_LITTLE_ENDIAN
);
315 proto_tree_add_item(tree
, hf_usb_ms_dCSWStatus
, tvb
, offset
, 1, ENC_LITTLE_ENDIAN
);
316 status
=tvb_get_guint8(tvb
, offset
);
319 itlq
=(itlq_nexus_t
*)wmem_tree_lookup32_le(usb_ms_conv_info
->itlq
, pinfo
->fd
->num
);
321 return tvb_length(tvb
);
323 itlq
->last_exchange_frame
=pinfo
->fd
->num
;
325 itl
=(itl_nexus_t
*)wmem_tree_lookup32(usb_ms_conv_info
->itl
, itlq
->lun
);
327 return tvb_length(tvb
);
331 dissect_scsi_rsp(tvb
, pinfo
, parent_tree
, itlq
, itl
, 0);
333 /* just send "check condition" */
334 dissect_scsi_rsp(tvb
, pinfo
, parent_tree
, itlq
, itl
, 0x02);
336 return tvb_length(tvb
);
340 * Ok it was neither CDB not STATUS so just assume it is either data in/out
342 itlq
=(itlq_nexus_t
*)wmem_tree_lookup32_le(usb_ms_conv_info
->itlq
, pinfo
->fd
->num
);
344 return tvb_length(tvb
);
347 itl
=(itl_nexus_t
*)wmem_tree_lookup32(usb_ms_conv_info
->itl
, itlq
->lun
);
349 return tvb_length(tvb
);
352 dissect_scsi_payload(tvb
, pinfo
, parent_tree
, is_request
, itlq
, itl
, 0);
353 return tvb_length(tvb
);
357 dissect_usb_ms_bulk_heur(tvbuff_t
*tvb
, packet_info
*pinfo
, proto_tree
*tree
, void* data
)
359 const gchar usbc
[] = {0x55, 0x53, 0x42, 0x43};
360 const gchar usbs
[] = {0x55, 0x53, 0x42, 0x53};
361 if (tvb_reported_length(tvb
) < 4)
364 if (tvb_memeql(tvb
, 0, usbc
, sizeof(usbc
)) == 0 ||
365 tvb_memeql(tvb
, 0, usbs
, sizeof(usbs
)) == 0) {
366 dissect_usb_ms_bulk(tvb
, pinfo
, tree
, data
);
374 proto_register_usb_ms(void)
376 static hf_register_info hf
[] = {
377 { &hf_usb_ms_dCBWSignature
,
378 { "Signature", "usbms.dCBWSignature", FT_UINT32
, BASE_HEX
,
379 NULL
, 0x0, NULL
, HFILL
}},
381 { &hf_usb_ms_dCBWTag
,
382 { "Tag", "usbms.dCBWTag", FT_UINT32
, BASE_HEX
,
383 NULL
, 0x0, NULL
, HFILL
}},
385 { &hf_usb_ms_dCBWDataTransferLength
,
386 { "DataTransferLength", "usbms.dCBWDataTransferLength", FT_UINT32
, BASE_DEC
,
387 NULL
, 0x0, NULL
, HFILL
}},
389 { &hf_usb_ms_dCBWFlags
,
390 { "Flags", "usbms.dCBWFlags", FT_UINT8
, BASE_HEX
,
391 NULL
, 0x0, NULL
, HFILL
}},
393 { &hf_usb_ms_dCBWLUN
,
394 { "LUN", "usbms.dCBWLUN", FT_UINT8
, BASE_HEX
,
395 NULL
, 0x0f, NULL
, HFILL
}},
397 { &hf_usb_ms_dCBWCBLength
,
398 { "CDB Length", "usbms.dCBWCBLength", FT_UINT8
, BASE_HEX
,
399 NULL
, 0x1f, NULL
, HFILL
}},
401 { &hf_usb_ms_dCSWSignature
,
402 { "Signature", "usbms.dCSWSignature", FT_UINT32
, BASE_HEX
,
403 NULL
, 0x0, NULL
, HFILL
}},
405 { &hf_usb_ms_dCSWDataResidue
,
406 { "DataResidue", "usbms.dCSWDataResidue", FT_UINT32
, BASE_DEC
,
407 NULL
, 0x0, NULL
, HFILL
}},
409 { &hf_usb_ms_dCSWStatus
,
410 { "Status", "usbms.dCSWStatus", FT_UINT8
, BASE_HEX
,
411 VALS(status_vals
), 0x0, NULL
, HFILL
}},
413 { &hf_usb_ms_request
,
414 { "bRequest", "usbms.setup.bRequest", FT_UINT8
, BASE_HEX
, VALS(setup_request_names_vals
), 0x0,
418 { "wValue", "usbms.setup.wValue", FT_UINT16
, BASE_HEX
, NULL
, 0x0,
422 { "wIndex", "usbms.setup.wIndex", FT_UINT16
, BASE_DEC
, NULL
, 0x0,
426 { "wLength", "usbms.setup.wLength", FT_UINT16
, BASE_DEC
, NULL
, 0x0,
430 { "Max LUN", "usbms.setup.maxlun", FT_UINT8
, BASE_DEC
, NULL
, 0x0,
435 static gint
*usb_ms_subtrees
[] = {
440 proto_usb_ms
= proto_register_protocol("USB Mass Storage", "USBMS", "usbms");
441 proto_register_field_array(proto_usb_ms
, hf
, array_length(hf
));
442 proto_register_subtree_array(usb_ms_subtrees
, array_length(usb_ms_subtrees
));
444 new_register_dissector("usbms", dissect_usb_ms_bulk
, proto_usb_ms
);
448 proto_reg_handoff_usb_ms(void)
450 dissector_handle_t usb_ms_bulk_handle
;
451 dissector_handle_t usb_ms_control_handle
;
453 usb_ms_bulk_handle
= find_dissector("usbms");
454 dissector_add_uint("usb.bulk", IF_CLASS_MASS_STORAGE
, usb_ms_bulk_handle
);
456 usb_ms_control_handle
= new_create_dissector_handle(dissect_usb_ms_control
, proto_usb_ms
);
457 dissector_add_uint("usb.control", IF_CLASS_MASS_STORAGE
, usb_ms_control_handle
);
459 heur_dissector_add("usb.bulk", dissect_usb_ms_bulk_heur
, proto_usb_ms
);