3 * usb mass storage (bulk-only transport) dissector
6 * SPDX-License-Identifier: GPL-2.0-or-later
12 #include <epan/packet.h>
13 #include "packet-usb.h"
14 #include "packet-scsi.h"
16 void proto_register_usbms_bot(void);
17 void proto_reg_handoff_usbms_bot(void);
19 #define IF_PROTOCOL_BULK_ONLY 0x50
21 /* protocols and header fields */
22 static int proto_usbms_bot
;
23 static int hf_usbms_bot_dCBWSignature
;
24 static int hf_usbms_bot_dCBWTag
;
25 static int hf_usbms_bot_dCBWDataTransferLength
;
26 static int hf_usbms_bot_dCBWFlags
;
27 static int hf_usbms_bot_dCBWTarget
;
28 static int hf_usbms_bot_dCBWLUN
;
29 static int hf_usbms_bot_dCBWCBLength
;
30 static int hf_usbms_bot_dCSWSignature
;
31 static int hf_usbms_bot_dCSWDataResidue
;
32 static int hf_usbms_bot_dCSWStatus
;
33 static int hf_usbms_bot_request
;
34 static int hf_usbms_bot_value
;
35 static int hf_usbms_bot_index
;
36 static int hf_usbms_bot_length
;
37 static int hf_usbms_bot_maxlun
;
39 static int ett_usbms_bot
;
41 static dissector_handle_t usbms_bot_bulk_handle
;
42 static dissector_handle_t usbms_bot_control_handle
;
44 /* there is one such structure for each masstorage conversation */
45 typedef struct _usbms_bot_conv_info_t
{
46 wmem_tree_t
*itl
; /* indexed by LUN */
47 wmem_tree_t
*itlq
; /* pinfo->num */
48 } usbms_bot_conv_info_t
;
51 static const value_string status_vals
[] = {
52 {0x00, "Command Passed"},
53 {0x01, "Command Failed"},
54 {0x02, "Phase Error"},
59 dissect_usbms_bot_reset(packet_info
*pinfo _U_
, proto_tree
*tree
, tvbuff_t
*tvb
, int offset
, bool is_request
, usb_trans_info_t
*usb_trans_info _U_
, urb_info_t
*urb _U_
)
62 proto_tree_add_item(tree
, hf_usbms_bot_value
, tvb
, offset
, 2, ENC_LITTLE_ENDIAN
);
65 proto_tree_add_item(tree
, hf_usbms_bot_index
, tvb
, offset
, 2, ENC_LITTLE_ENDIAN
);
68 proto_tree_add_item(tree
, hf_usbms_bot_length
, tvb
, offset
, 2, ENC_LITTLE_ENDIAN
);
71 /* no data in reset response */
76 dissect_usbms_bot_get_max_lun(packet_info
*pinfo _U_
, proto_tree
*tree
, tvbuff_t
*tvb
, int offset
, bool is_request
, usb_trans_info_t
*usb_trans_info _U_
, urb_info_t
*urb _U_
)
79 proto_tree_add_item(tree
, hf_usbms_bot_value
, tvb
, offset
, 2, ENC_LITTLE_ENDIAN
);
82 proto_tree_add_item(tree
, hf_usbms_bot_index
, tvb
, offset
, 2, ENC_LITTLE_ENDIAN
);
85 proto_tree_add_item(tree
, hf_usbms_bot_length
, tvb
, offset
, 2, ENC_LITTLE_ENDIAN
);
88 proto_tree_add_item(tree
, hf_usbms_bot_maxlun
, tvb
, offset
, 1, ENC_LITTLE_ENDIAN
);
94 typedef void (*usb_setup_dissector
)(packet_info
*pinfo
, proto_tree
*tree
, tvbuff_t
*tvb
, int offset
, bool is_request
, usb_trans_info_t
*usb_trans_info
, urb_info_t
*urb
);
96 typedef struct _usb_setup_dissector_table_t
{
98 usb_setup_dissector dissector
;
99 } usb_setup_dissector_table_t
;
100 #define USB_SETUP_RESET 0xff
101 #define USB_SETUP_GET_MAX_LUN 0xfe
102 static const usb_setup_dissector_table_t setup_dissectors
[] = {
103 {USB_SETUP_RESET
, dissect_usbms_bot_reset
},
104 {USB_SETUP_GET_MAX_LUN
, dissect_usbms_bot_get_max_lun
},
107 static const value_string setup_request_names_vals
[] = {
108 {USB_SETUP_RESET
, "RESET"},
109 {USB_SETUP_GET_MAX_LUN
, "GET MAX LUN"},
113 /* Dissector for mass storage control .
114 * Returns tvb_captured_length(tvb) if a class specific dissector was found
118 dissect_usbms_bot_control(tvbuff_t
*tvb
, packet_info
*pinfo
, proto_tree
*parent_tree
, void *data
)
122 usb_trans_info_t
*usb_trans_info
;
124 usb_setup_dissector dissector
= NULL
;
125 const usb_setup_dissector_table_t
*tmp
;
129 /* Reject the packet if data or usb_trans_info are NULL */
130 if (data
== NULL
|| ((urb_info_t
*)data
)->usb_trans_info
== NULL
)
132 urb
= (urb_info_t
*)data
;
133 usb_trans_info
= urb
->usb_trans_info
;
135 is_request
=(pinfo
->srcport
==NO_ENDPOINT
);
137 /* See if we can find a class specific dissector for this request */
138 for(tmp
=setup_dissectors
;tmp
->dissector
;tmp
++){
139 if (tmp
->request
== usb_trans_info
->setup
.request
){
140 dissector
=tmp
->dissector
;
144 /* No we could not find any class specific dissector for this request
145 * return 0 and let USB try any of the standard requests.
151 col_set_str(pinfo
->cinfo
, COL_PROTOCOL
, "USBMS");
152 ti
= proto_tree_add_protocol_format(parent_tree
, proto_usbms_bot
, tvb
, 0, -1, "USB Mass Storage");
153 tree
= proto_item_add_subtree(ti
, ett_usbms_bot
);
155 col_add_fstr(pinfo
->cinfo
, COL_INFO
, "%s %s",
156 val_to_str(usb_trans_info
->setup
.request
, setup_request_names_vals
, "Unknown type %x"),
157 is_request
?"Request":"Response");
160 proto_tree_add_item(tree
, hf_usbms_bot_request
, tvb
, offset
, 1, ENC_LITTLE_ENDIAN
);
164 dissector(pinfo
, tree
, tvb
, offset
, is_request
, usb_trans_info
, urb
);
165 return tvb_captured_length(tvb
);
169 create_usbms_bot_protocol_tree(tvbuff_t
*tvb
, proto_tree
*parent_tree
)
174 ti
= proto_tree_add_protocol_format(parent_tree
, proto_usbms_bot
, tvb
, 0, -1, "USB Mass Storage");
175 tree
= proto_item_add_subtree(ti
, ett_usbms_bot
);
181 dissect_usbms_bot_cbw(tvbuff_t
*tvb
, packet_info
*pinfo
, proto_tree
*parent_tree
, usbms_bot_conv_info_t
*usbms_bot_conv_info
)
183 proto_tree
*tree
= create_usbms_bot_protocol_tree(tvb
, parent_tree
);
193 proto_tree_add_item(tree
, hf_usbms_bot_dCBWSignature
, tvb
, offset
, 4, ENC_LITTLE_ENDIAN
);
197 proto_tree_add_item(tree
, hf_usbms_bot_dCBWTag
, tvb
, offset
, 4, ENC_LITTLE_ENDIAN
);
200 /* dCBWDataTransferLength */
201 proto_tree_add_item(tree
, hf_usbms_bot_dCBWDataTransferLength
, tvb
, offset
, 4, ENC_LITTLE_ENDIAN
);
202 datalen
=tvb_get_letohl(tvb
, offset
);
206 proto_tree_add_item(tree
, hf_usbms_bot_dCBWFlags
, tvb
, offset
, 1, ENC_LITTLE_ENDIAN
);
207 flags
=tvb_get_uint8(tvb
, offset
);
211 proto_tree_add_item(tree
, hf_usbms_bot_dCBWTarget
, tvb
, offset
, 1, ENC_LITTLE_ENDIAN
);
212 proto_tree_add_item(tree
, hf_usbms_bot_dCBWLUN
, tvb
, offset
, 1, ENC_LITTLE_ENDIAN
);
213 lun
=tvb_get_uint8(tvb
, offset
)&0x0f;
216 /* make sure we have a ITL structure for this LUN */
217 itl
=(itl_nexus_t
*)wmem_tree_lookup32(usbms_bot_conv_info
->itl
, lun
);
219 itl
=wmem_new(wmem_file_scope(), itl_nexus_t
);
221 itl
->conversation
=NULL
;
222 wmem_tree_insert32(usbms_bot_conv_info
->itl
, lun
, itl
);
225 /* make sure we have an ITLQ structure for this LUN/transaction */
226 itlq
=(itlq_nexus_t
*)wmem_tree_lookup32(usbms_bot_conv_info
->itlq
, pinfo
->num
);
228 itlq
=wmem_new(wmem_file_scope(), itlq_nexus_t
);
230 itlq
->scsi_opcode
=0xffff;
234 itlq
->task_flags
|=SCSI_DATA_READ
;
236 itlq
->task_flags
|=SCSI_DATA_WRITE
;
239 itlq
->data_length
=datalen
;
240 itlq
->bidir_data_length
=0;
241 itlq
->fc_time
=pinfo
->abs_ts
;
242 itlq
->first_exchange_frame
=pinfo
->num
;
243 itlq
->last_exchange_frame
=0;
246 itlq
->extra_data
=NULL
;
247 wmem_tree_insert32(usbms_bot_conv_info
->itlq
, pinfo
->num
, itlq
);
251 proto_tree_add_item(tree
, hf_usbms_bot_dCBWCBLength
, tvb
, offset
, 1, ENC_LITTLE_ENDIAN
);
252 cdbrlen
=tvb_get_uint8(tvb
, offset
)&0x1f;
256 if(cdblen
>tvb_captured_length_remaining(tvb
, offset
)){
257 cdblen
=tvb_captured_length_remaining(tvb
, offset
);
260 cdb_tvb
=tvb_new_subset_length_caplen(tvb
, offset
, cdblen
, cdbrlen
);
261 dissect_scsi_cdb(cdb_tvb
, pinfo
, parent_tree
, SCSI_DEV_UNKNOWN
, itlq
, itl
);
263 return tvb_captured_length(tvb
);
267 dissect_usbms_bot_csw(tvbuff_t
*tvb
, packet_info
*pinfo
, proto_tree
*parent_tree
, usbms_bot_conv_info_t
*usbms_bot_conv_info
)
269 proto_tree
*tree
= create_usbms_bot_protocol_tree(tvb
, parent_tree
);
276 proto_tree_add_item(tree
, hf_usbms_bot_dCSWSignature
, tvb
, offset
, 4, ENC_LITTLE_ENDIAN
);
280 proto_tree_add_item(tree
, hf_usbms_bot_dCBWTag
, tvb
, offset
, 4, ENC_LITTLE_ENDIAN
);
283 /* dCSWDataResidue */
284 proto_tree_add_item(tree
, hf_usbms_bot_dCSWDataResidue
, tvb
, offset
, 4, ENC_LITTLE_ENDIAN
);
288 proto_tree_add_item(tree
, hf_usbms_bot_dCSWStatus
, tvb
, offset
, 1, ENC_LITTLE_ENDIAN
);
289 status
=tvb_get_uint8(tvb
, offset
);
292 itlq
=(itlq_nexus_t
*)wmem_tree_lookup32_le(usbms_bot_conv_info
->itlq
, pinfo
->num
);
294 return tvb_captured_length(tvb
);
296 itlq
->last_exchange_frame
=pinfo
->num
;
298 itl
=(itl_nexus_t
*)wmem_tree_lookup32(usbms_bot_conv_info
->itl
, itlq
->lun
);
300 return tvb_captured_length(tvb
);
304 dissect_scsi_rsp(tvb
, pinfo
, parent_tree
, itlq
, itl
, 0);
306 /* just send "check condition" */
307 dissect_scsi_rsp(tvb
, pinfo
, parent_tree
, itlq
, itl
, 0x02);
309 return tvb_captured_length(tvb
);
313 usbms_bot_bulk_is_cbw(tvbuff_t
*tvb
, int offset
, bool is_request
)
315 return is_request
&& (tvb_reported_length(tvb
)==(unsigned)offset
+31) &&
316 tvb_get_letohl(tvb
, offset
) == 0x43425355;
320 usbms_bot_bulk_is_csw(tvbuff_t
*tvb
, int offset
, bool is_request
)
322 return !is_request
&& (tvb_reported_length(tvb
)==(unsigned)offset
+13) &&
323 tvb_get_letohl(tvb
, offset
) == 0x53425355;
326 /* dissector for mass storage bulk data */
328 dissect_usbms_bot_bulk(tvbuff_t
*tvb
, packet_info
*pinfo
, proto_tree
*parent_tree
, void* data
)
331 usbms_bot_conv_info_t
*usbms_bot_conv_info
;
336 tvbuff_t
*payload_tvb
;
338 /* Reject the packet if data is NULL */
341 urb
= (urb_info_t
*)data
;
342 if (urb
->conv
== NULL
)
345 /* verify that we do have a usbms_bot_conv_info */
346 usbms_bot_conv_info
=(usbms_bot_conv_info_t
*)urb
->conv
->class_data
;
347 if(!usbms_bot_conv_info
){
348 usbms_bot_conv_info
=wmem_new(wmem_file_scope(), usbms_bot_conv_info_t
);
349 usbms_bot_conv_info
->itl
=wmem_tree_new(wmem_file_scope());
350 usbms_bot_conv_info
->itlq
=wmem_tree_new(wmem_file_scope());
351 urb
->conv
->class_data
=usbms_bot_conv_info
;
352 urb
->conv
->class_data_type
= USB_CONV_MASS_STORAGE_BOT
;
353 } else if (urb
->conv
->class_data_type
!= USB_CONV_MASS_STORAGE_BOT
) {
354 /* Don't dissect if another USB type is in the conversation */
358 is_request
=(pinfo
->srcport
==NO_ENDPOINT
);
360 col_set_str(pinfo
->cinfo
, COL_PROTOCOL
, "USBMS");
362 col_clear(pinfo
->cinfo
, COL_INFO
);
366 * SCSI CDB inside CBW
368 if (usbms_bot_bulk_is_cbw(tvb
, offset
, is_request
)) {
369 return dissect_usbms_bot_cbw(tvb
, pinfo
, parent_tree
, usbms_bot_conv_info
);
374 * SCSI RESPONSE inside CSW
376 if (usbms_bot_bulk_is_csw(tvb
, offset
, is_request
)) {
377 return dissect_usbms_bot_csw(tvb
, pinfo
, parent_tree
, usbms_bot_conv_info
);
381 * Ok it was neither CDB not STATUS so just assume it is either data in/out
383 itlq
=(itlq_nexus_t
*)wmem_tree_lookup32_le(usbms_bot_conv_info
->itlq
, pinfo
->num
-1);
385 create_usbms_bot_protocol_tree(tvb
, parent_tree
);
386 return tvb_captured_length(tvb
);
389 itl
=(itl_nexus_t
*)wmem_tree_lookup32(usbms_bot_conv_info
->itl
, itlq
->lun
);
391 create_usbms_bot_protocol_tree(tvb
, parent_tree
);
392 return tvb_captured_length(tvb
);
396 * Workaround USBLL reassembly limitations by anticipating concatenated
397 * SCSI Data IN with CSW and SCSI Data OUT with next CBW. Proper would
398 * involve implementing a framework to allow USB class dissectors to signal
399 * expected transfer length on Bulk IN or Bulk OUT endpoint whenever CBW is
402 payload_tvb
= tvb_new_subset_length(tvb
, 0, itlq
->data_length
);
403 if (usbms_bot_bulk_is_cbw(tvb
, itlq
->data_length
, is_request
)) {
404 tvbuff_t
*cbw_tvb
= tvb_new_subset_length(tvb
, itlq
->data_length
, 31);
406 dissect_scsi_payload(payload_tvb
, pinfo
, parent_tree
, is_request
, itlq
, itl
, 0);
407 dissect_usbms_bot_cbw(cbw_tvb
, pinfo
, parent_tree
, usbms_bot_conv_info
);
408 return tvb_captured_length(tvb
);
409 } else if (usbms_bot_bulk_is_csw(tvb
, itlq
->data_length
, is_request
)) {
410 tvbuff_t
*csw_tvb
= tvb_new_subset_length(tvb
, itlq
->data_length
, 13);
412 dissect_scsi_payload(payload_tvb
, pinfo
, parent_tree
, is_request
, itlq
, itl
, 0);
413 dissect_usbms_bot_csw(csw_tvb
, pinfo
, parent_tree
, usbms_bot_conv_info
);
414 return tvb_captured_length(tvb
);
417 /* Create empty protocol tree so "usbms" filter displays this packet */
418 create_usbms_bot_protocol_tree(tvb
, parent_tree
);
419 dissect_scsi_payload(payload_tvb
, pinfo
, parent_tree
, is_request
, itlq
, itl
, 0);
420 return tvb_captured_length(payload_tvb
);
424 dissect_usbms_bot_bulk_heur(tvbuff_t
*tvb
, packet_info
*pinfo
, proto_tree
*tree
, void* data
)
426 static const unsigned char usbc
[] = {0x55, 0x53, 0x42, 0x43};
427 static const unsigned char usbs
[] = {0x55, 0x53, 0x42, 0x53};
428 if (tvb_reported_length(tvb
) < 4)
431 if (tvb_memeql(tvb
, 0, usbc
, sizeof(usbc
)) == 0 ||
432 tvb_memeql(tvb
, 0, usbs
, sizeof(usbs
)) == 0) {
433 dissect_usbms_bot_bulk(tvb
, pinfo
, tree
, data
);
441 proto_register_usbms_bot(void)
443 static hf_register_info hf
[] = {
444 { &hf_usbms_bot_dCBWSignature
,
445 { "Signature", "usbms.dCBWSignature", FT_UINT32
, BASE_HEX
,
446 NULL
, 0x0, NULL
, HFILL
}},
448 { &hf_usbms_bot_dCBWTag
,
449 { "Tag", "usbms.dCBWTag", FT_UINT32
, BASE_HEX
,
450 NULL
, 0x0, NULL
, HFILL
}},
452 { &hf_usbms_bot_dCBWDataTransferLength
,
453 { "DataTransferLength", "usbms.dCBWDataTransferLength", FT_UINT32
, BASE_DEC
,
454 NULL
, 0x0, NULL
, HFILL
}},
456 { &hf_usbms_bot_dCBWFlags
,
457 { "Flags", "usbms.dCBWFlags", FT_UINT8
, BASE_HEX
,
458 NULL
, 0x0, NULL
, HFILL
}},
460 { &hf_usbms_bot_dCBWTarget
,
461 { "Target", "usbms.dCBWTarget", FT_UINT8
, BASE_HEX_DEC
,
462 NULL
, 0x70, "Target Number when enabling multi-target mode", HFILL
}},
464 { &hf_usbms_bot_dCBWLUN
,
465 { "LUN", "usbms.dCBWLUN", FT_UINT8
, BASE_HEX
,
466 NULL
, 0x0f, NULL
, HFILL
}},
468 { &hf_usbms_bot_dCBWCBLength
,
469 { "CDB Length", "usbms.dCBWCBLength", FT_UINT8
, BASE_HEX
,
470 NULL
, 0x1f, NULL
, HFILL
}},
472 { &hf_usbms_bot_dCSWSignature
,
473 { "Signature", "usbms.dCSWSignature", FT_UINT32
, BASE_HEX
,
474 NULL
, 0x0, NULL
, HFILL
}},
476 { &hf_usbms_bot_dCSWDataResidue
,
477 { "DataResidue", "usbms.dCSWDataResidue", FT_UINT32
, BASE_DEC
,
478 NULL
, 0x0, NULL
, HFILL
}},
480 { &hf_usbms_bot_dCSWStatus
,
481 { "Status", "usbms.dCSWStatus", FT_UINT8
, BASE_HEX
,
482 VALS(status_vals
), 0x0, NULL
, HFILL
}},
484 { &hf_usbms_bot_request
,
485 { "bRequest", "usbms.setup.bRequest", FT_UINT8
, BASE_HEX
, VALS(setup_request_names_vals
), 0x0,
488 { &hf_usbms_bot_value
,
489 { "wValue", "usbms.setup.wValue", FT_UINT16
, BASE_HEX
, NULL
, 0x0,
492 { &hf_usbms_bot_index
,
493 { "wIndex", "usbms.setup.wIndex", FT_UINT16
, BASE_DEC
, NULL
, 0x0,
496 { &hf_usbms_bot_length
,
497 { "wLength", "usbms.setup.wLength", FT_UINT16
, BASE_DEC
, NULL
, 0x0,
500 { &hf_usbms_bot_maxlun
,
501 { "Max LUN", "usbms.setup.maxlun", FT_UINT8
, BASE_DEC
, NULL
, 0x0,
506 static int *usbms_bot_ett
[] = {
511 proto_usbms_bot
= proto_register_protocol("USB Mass Storage", "USBMS", "usbms");
512 proto_register_field_array(proto_usbms_bot
, hf
, array_length(hf
));
513 proto_register_subtree_array(usbms_bot_ett
, array_length(usbms_bot_ett
));
515 usbms_bot_bulk_handle
= register_dissector("usbms", dissect_usbms_bot_bulk
, proto_usbms_bot
);
516 usbms_bot_control_handle
= register_dissector("usbms.control", dissect_usbms_bot_control
, proto_usbms_bot
);
520 proto_reg_handoff_usbms_bot(void)
522 dissector_add_uint("usbms.bulk", IF_PROTOCOL_BULK_ONLY
, usbms_bot_bulk_handle
);
523 dissector_add_uint("usbms.control", IF_PROTOCOL_BULK_ONLY
, usbms_bot_control_handle
);
525 heur_dissector_add("usb.bulk", dissect_usbms_bot_bulk_heur
,
526 "Mass Storage USB Bulk-Only Transport bulk endpoint",
527 "ms_usb_bulk", proto_usbms_bot
, HEURISTIC_ENABLE
);
531 * Editor modelines - https://www.wireshark.org/tools/modelines.html
536 * indent-tabs-mode: nil
539 * vi: set shiftwidth=4 tabstop=8 expandtab:
540 * :indentSize=4:tabSize=8:noTabs=true: