/* packet-usbip.c * Routines for USB/IP dissection * Copyright 2016, Christian Lamparter * * Wireshark - Network traffic analyzer * By Gerald Combs * Copyright 1998 Gerald Combs * * SPDX-License-Identifier: GPL-2.0-or-later */ /* * The USB/IP protocol follows a server/client architecture. It runs * on top of TCP/IP. The server exports the USB devices and the * clients imports them. The device driver for the exported USB * device runs on the client machine. * * See * * https://www.kernel.org/doc/Documentation/usb/usbip_protocol.txt */ #include #include #include #include #include #include "packet-usbip.h" #include "packet-usb.h" #include "packet-tcp.h" void proto_register_usbip(void); void proto_reg_handoff_usbip(void); static dissector_handle_t usbip_handle; /* Initialize the protocol and registered fields */ static int proto_usbip; static int hf_usbip_version; static int hf_usbip_operation; static int hf_usbip_command; static int hf_usbip_status; static int hf_usbip_number_devices; static int hf_usbip_path; static int hf_usbip_devid; static int hf_usbip_busid; static int hf_usbip_busnum; static int hf_usbip_devnum; static int hf_usbip_speed; static int hf_usbip_idVendor; static int hf_usbip_idProduct; static int hf_usbip_bcdDevice; static int hf_usbip_bDeviceClass; static int hf_usbip_bDeviceSubClass; static int hf_usbip_bDeviceProtocol; static int hf_usbip_bConfigurationValue; static int hf_usbip_bNumConfigurations; static int hf_usbip_bNumInterfaces; static int hf_usbip_bInterfaceClass; static int hf_usbip_bInterfaceSubClass; static int hf_usbip_bInterfaceProtocol; static int hf_usbip_padding; static int hf_usbip_device; static int hf_usbip_interface; static int hf_usbip_interval; static int hf_usbip_actual_length; static int hf_usbip_error_count; static int hf_usbip_seqnum; static int hf_usbip_cmd_frame; static int hf_usbip_ret_frame; static int hf_usbip_vic_frame; static int hf_usbip_direction; static int hf_usbip_ep; static int hf_usbip_transfer_flags; static int hf_usbip_transfer_buffer_length; static int hf_usbip_start_frame; static int hf_usbip_number_of_packets; static int hf_usbip_setup; static int hf_usbip_urb_data; /* Initialize the subtree pointers */ static int ett_usbip; static int ett_usbip_dev; static int ett_usbip_intf; enum usb_device_speed { USBIP_SPEED_UNKNOWN = 0, /* enumerating */ USBIP_SPEED_LOW, /* usb 1.0 */ USBIP_SPEED_FULL, /* usb 1.1 */ USBIP_SPEED_HIGH, /* usb 2.0 */ USBIP_SPEED_WIRELESS, /* wireless (usb 2.5) */ USBIP_SPEED_SUPER, /* usb 3.0 */ }; #define USBIP_SUPPORTED_VERSION 0x111 #define OP_REQUEST (0x80 << 8) #define OP_REPLY (0x00 << 8) /* ---------------------------------------------------------------------- * Import a remote USB device. */ #define OP_IMPORT 0x03 #define OP_REQ_IMPORT (OP_REQUEST | OP_IMPORT) #define OP_REP_IMPORT (OP_REPLY | OP_IMPORT) /* ---------------------------------------------------------------------- * Retrieve the list of exported USB devices. */ #define OP_DEVLIST 0x05 #define OP_REQ_DEVLIST (OP_REQUEST | OP_DEVLIST) #define OP_REP_DEVLIST (OP_REPLY | OP_DEVLIST) #define OP_CMD_SUBMIT 0x0001 #define OP_CMD_UNLINK 0x0002 #define OP_RET_SUBMIT 0x0003 #define OP_RET_UNLINK 0x0004 static const value_string usbip_operation_vals[] = { {OP_REP_IMPORT, "OP_REP_IMPORT" }, {OP_REP_DEVLIST, "OP_REP_DEVLIST" }, {OP_REQ_IMPORT, "OP_REQ_IMPORT" }, {OP_REQ_DEVLIST, "OP_REQ_DEVLIST" }, {0, NULL } }; static const value_string usbip_urb_vals[] = { {OP_CMD_SUBMIT, "OP_CMD_SUBMIT" }, {OP_CMD_UNLINK, "OP_CMD_UNLINK" }, {OP_RET_SUBMIT, "OP_RET_SUBMIT" }, {OP_RET_UNLINK, "OP_RET_UNLINK" }, {0, NULL } }; static const value_string usbip_speed_vals[] = { {USBIP_SPEED_UNKNOWN, "Speed Unknown" }, {USBIP_SPEED_LOW, "Low Speed" }, {USBIP_SPEED_FULL, "Full Speed" }, {USBIP_SPEED_HIGH, "High Speed" }, {USBIP_SPEED_WIRELESS, "Wireless Speed" }, {USBIP_SPEED_SUPER, "Super Speed" }, {0, NULL } }; static value_string_ext usbip_speed_vals_ext = VALUE_STRING_EXT_INIT(usbip_speed_vals); static value_string_ext usbip_operation_vals_ext = VALUE_STRING_EXT_INIT(usbip_operation_vals); static value_string_ext usbip_urb_vals_ext = VALUE_STRING_EXT_INIT(usbip_urb_vals); extern value_string_ext ext_usb_vendors_vals; extern value_string_ext ext_usb_products_vals; extern value_string_ext linux_negative_errno_vals_ext; static const value_string usb_endpoint_direction_vals[] = { {USBIP_DIR_OUT, "OUT" }, {USBIP_DIR_IN, "IN" }, {0, NULL } }; static expert_field ei_usbip; typedef struct _usbip_transaction_t { uint32_t seqnum; uint32_t devid; uint32_t ep; uint32_t dir; uint32_t cmd_frame; uint32_t ret_frame; uint32_t unlink_seqnum; } usbip_transaction_t; typedef struct _usbip_conv_info_t { /* holds OP_{CMD|RET}_{SUBMIT|UNLINK} */ wmem_tree_t *pdus; } usbip_conv_info_t; static int dissect_device_list_request(packet_info *pinfo) { col_set_str(pinfo->cinfo, COL_INFO, "Device List Request"); return 0; } static int dissect_device(proto_tree *tree, tvbuff_t *tvb, int offset) { uint32_t product; uint32_t vendor_id; uint32_t product_id; /* Device path on host (usually /sys/devices/usb/... */ proto_tree_add_item(tree, hf_usbip_path, tvb, offset, 256, ENC_ASCII | ENC_NA); offset += 256; /* Bus id string - Id of the bus the device is connected to */ proto_tree_add_item(tree, hf_usbip_busid, tvb, offset, 32, ENC_ASCII | ENC_NA); offset += 32; /* bus number */ proto_tree_add_item(tree, hf_usbip_busnum, tvb, offset, 4, ENC_BIG_ENDIAN); offset += 4; /* device number */ proto_tree_add_item(tree, hf_usbip_devnum, tvb, offset, 4, ENC_BIG_ENDIAN); offset += 4; /* USB Speed */ proto_tree_add_item(tree, hf_usbip_speed, tvb, offset, 4, ENC_BIG_ENDIAN); offset += 4; /* idVendor */ proto_tree_add_item_ret_uint(tree, hf_usbip_idVendor, tvb, offset, 2, ENC_BIG_ENDIAN, &vendor_id); offset += 2; /* idProduct */ product_id = tvb_get_ntohs(tvb, offset); product = vendor_id << 16 | product_id; proto_tree_add_uint_format_value(tree, hf_usbip_idProduct, tvb, offset, 2, product_id, "%s (0x%04x)", val_to_str_ext_const(product, &ext_usb_products_vals, "Unknown"), product_id); offset += 2; /* bcdDevice */ proto_tree_add_item(tree, hf_usbip_bcdDevice, tvb, offset, 2, ENC_BIG_ENDIAN); offset += 2; /* Device Class */ proto_tree_add_item(tree, hf_usbip_bDeviceClass, tvb, offset, 1, ENC_BIG_ENDIAN); offset += 1; /* Device Sub Class */ proto_tree_add_item(tree, hf_usbip_bDeviceSubClass, tvb, offset, 1, ENC_BIG_ENDIAN); offset += 1; /* Device Protocol */ proto_tree_add_item(tree, hf_usbip_bDeviceProtocol, tvb, offset, 1, ENC_BIG_ENDIAN); offset += 1; /* Current Configuration */ proto_tree_add_item(tree, hf_usbip_bConfigurationValue, tvb, offset, 1, ENC_BIG_ENDIAN); offset += 1; /* Number of Configurations */ proto_tree_add_item(tree, hf_usbip_bNumConfigurations, tvb, offset, 1, ENC_BIG_ENDIAN); offset += 1; /* Number of Interfaces */ proto_tree_add_item(tree, hf_usbip_bNumInterfaces, tvb, offset, 1, ENC_BIG_ENDIAN); offset += 1; return offset; } static int dissect_device_list_response(packet_info *pinfo, proto_tree *tree, tvbuff_t *tvb, int offset) { proto_item *ti_intf; proto_item *ti_dev; proto_tree *intf_tree = NULL; proto_tree *dev_tree = NULL; uint32_t num_of_devs; uint32_t i; uint8_t num_of_intf; uint8_t j; col_set_str(pinfo->cinfo, COL_INFO, "Device List Response"); proto_tree_add_item_ret_uint(tree, hf_usbip_number_devices, tvb, offset, 4, ENC_BIG_ENDIAN, &num_of_devs); offset += 4; for (i = 0; i < num_of_devs; i++) { num_of_intf = tvb_get_uint8(tvb, offset + 0x137); ti_dev = proto_tree_add_uint(tree, hf_usbip_device, tvb, offset, 0x138 + 4 * num_of_intf, i + 1); proto_item_set_generated(ti_dev); dev_tree = proto_item_add_subtree(ti_dev, ett_usbip_dev); offset = dissect_device(dev_tree, tvb, offset); for (j = 0; j < num_of_intf; j++) { ti_intf = proto_tree_add_uint(dev_tree, hf_usbip_interface, tvb, offset, 3, j + 1); intf_tree = proto_item_add_subtree(ti_intf, ett_usbip_intf); proto_tree_add_item(intf_tree, hf_usbip_bInterfaceClass, tvb, offset, 1, ENC_BIG_ENDIAN); offset += 1; proto_tree_add_item(intf_tree, hf_usbip_bInterfaceSubClass, tvb, offset, 1, ENC_BIG_ENDIAN); offset += 1; proto_tree_add_item(intf_tree, hf_usbip_bInterfaceProtocol, tvb, offset, 1, ENC_BIG_ENDIAN); offset += 1; proto_tree_add_item(intf_tree, hf_usbip_padding, tvb, offset, 1, ENC_NA); offset += 1; } } return offset; } static int dissect_import_request(packet_info *pinfo, proto_tree *tree, tvbuff_t *tvb, int offset) { col_set_str(pinfo->cinfo, COL_INFO, "Import Request"); proto_tree_add_item(tree, hf_usbip_busid, tvb, offset, 32, ENC_ASCII | ENC_NA); return offset + 32; } static int dissect_import_response(packet_info *pinfo, proto_tree *tree, tvbuff_t *tvb, int offset, uint32_t status) { col_set_str(pinfo->cinfo, COL_INFO, "Import Response"); if (status == 0) offset = dissect_device(tree, tvb, offset); return offset; } static int dissect_cmd_submit(packet_info *pinfo, proto_tree *tree, tvbuff_t *tvb, int offset) { col_set_str(pinfo->cinfo, COL_INFO, "URB Submit"); dissect_urb_transfer_flags(tvb, offset, tree, hf_usbip_transfer_flags, ENC_BIG_ENDIAN); offset += 4; proto_tree_add_item(tree, hf_usbip_transfer_buffer_length, tvb, offset, 4, ENC_BIG_ENDIAN); offset += 4; proto_tree_add_item(tree, hf_usbip_start_frame, tvb, offset, 4, ENC_BIG_ENDIAN); offset += 4; proto_tree_add_item(tree, hf_usbip_number_of_packets, tvb, offset, 4, ENC_BIG_ENDIAN); offset += 4; proto_tree_add_item(tree, hf_usbip_interval, tvb, offset, 4, ENC_BIG_ENDIAN); offset += 4; proto_tree_add_item(tree, hf_usbip_setup, tvb, offset, 8, ENC_NA); offset += 8; return offset; } static int dissect_ret_submit(packet_info *pinfo, proto_tree *tree, tvbuff_t *tvb, int offset) { col_set_str(pinfo->cinfo, COL_INFO, "URB Response"); proto_tree_add_item(tree, hf_usbip_status, tvb, offset, 4, ENC_BIG_ENDIAN); offset += 4; proto_tree_add_item(tree, hf_usbip_actual_length, tvb, offset, 4, ENC_BIG_ENDIAN); offset += 4; proto_tree_add_item(tree, hf_usbip_start_frame, tvb, offset, 4, ENC_BIG_ENDIAN); offset += 4; proto_tree_add_item(tree, hf_usbip_number_of_packets, tvb, offset, 4, ENC_BIG_ENDIAN); offset += 4; proto_tree_add_item(tree, hf_usbip_error_count, tvb, offset, 4, ENC_BIG_ENDIAN); offset += 4; proto_tree_add_item(tree, hf_usbip_setup, tvb, offset, 8, ENC_NA); offset += 8; return offset; } static int dissect_cmd_unlink(packet_info *pinfo, proto_tree *tree, tvbuff_t *tvb, int offset, usbip_conv_info_t *usbip_info, usbip_transaction_t *trans) { usbip_transaction_t *victim; uint32_t seqnum; col_set_str(pinfo->cinfo, COL_INFO, "URB Unlink"); proto_tree_add_item_ret_uint(tree, hf_usbip_seqnum, tvb, offset, 4, ENC_BIG_ENDIAN, &seqnum); trans->unlink_seqnum = seqnum; offset += 4; victim = (usbip_transaction_t *) wmem_tree_lookup32(usbip_info->pdus, seqnum); if (victim) { proto_item *ti; ti = proto_tree_add_uint(tree, hf_usbip_vic_frame, NULL, 0, 0, victim->cmd_frame); proto_item_set_generated(ti); } return offset; } static int dissect_ret_unlink(packet_info *pinfo, proto_tree *tree, tvbuff_t *tvb, int offset, usbip_conv_info_t *usbip_info, uint32_t seqnum) { usbip_transaction_t *victim; col_set_str(pinfo->cinfo, COL_INFO, "URB Unlink Response"); victim = (usbip_transaction_t *) wmem_tree_lookup32(usbip_info->pdus, seqnum); if (victim) { proto_item *ti; victim->ret_frame = pinfo->num; ti = proto_tree_add_uint(tree, hf_usbip_vic_frame, NULL, 0, 0, victim->cmd_frame); proto_item_set_generated(ti); } proto_tree_add_item(tree, hf_usbip_status, tvb, offset, 4, ENC_BIG_ENDIAN); offset += 4; return offset; } static usbip_conv_info_t * usbip_get_usbip_conv(packet_info *pinfo) { conversation_t *conversation; usbip_conv_info_t *usbip_info; conversation = find_or_create_conversation(pinfo); usbip_info = (usbip_conv_info_t *) conversation_get_proto_data(conversation, proto_usbip); if (!usbip_info) { usbip_info = wmem_new(wmem_file_scope(), usbip_conv_info_t); usbip_info->pdus = wmem_tree_new(wmem_file_scope()); conversation_add_proto_data(conversation, proto_usbip, usbip_info); } return usbip_info; } static int usbip_dissect_op(packet_info *pinfo, tvbuff_t *tvb, proto_tree *tree, int offset) { proto_item *ti = NULL; uint32_t operation; int32_t status; proto_tree_add_item(tree, hf_usbip_version, tvb, offset, 2, ENC_BIG_ENDIAN); offset += 2; proto_tree_add_item_ret_uint(tree, hf_usbip_operation, tvb, offset, 2, ENC_BIG_ENDIAN, &operation); offset += 2; proto_tree_add_item_ret_int(tree, hf_usbip_status, tvb, offset, 4, ENC_BIG_ENDIAN, &status); offset += 4; switch (operation) { case OP_REQ_IMPORT: offset = dissect_import_request(pinfo, tree, tvb, offset); break; case OP_REP_IMPORT: offset = dissect_import_response(pinfo, tree, tvb, offset, status); break; case OP_REQ_DEVLIST: offset = dissect_device_list_request(pinfo); break; case OP_REP_DEVLIST: offset = dissect_device_list_response(pinfo, tree, tvb, offset); break; default: proto_tree_add_item(tree, hf_usbip_urb_data, tvb, offset, -1, ENC_NA); offset = tvb_reported_length_remaining(tvb, offset); expert_add_info_format( pinfo, ti, &ei_usbip, "Dissector for USBIP Operation" " (%x) code not implemented, Contact" " Wireshark developers if you want this supported", operation); proto_item_append_text(ti, ": Undecoded"); break; } return offset; } static int usbip_dissect_urb(packet_info *pinfo, tvbuff_t *tvb, proto_tree *tree, proto_tree *orig, int offset, usbip_conv_info_t *usbip_info) { proto_item *ti = NULL; usbip_transaction_t *usbip_trans; uint32_t command; uint32_t devid; uint32_t seqnum; uint32_t dir; uint32_t ep; struct usbip_header header; proto_tree_add_item_ret_uint(tree, hf_usbip_command, tvb, offset, 4, ENC_BIG_ENDIAN, &command); offset += 4; proto_tree_add_item_ret_uint(tree, hf_usbip_seqnum, tvb, offset, 4, ENC_BIG_ENDIAN, &seqnum); offset += 4; dir = tvb_get_ntohl(tvb, offset + 4); ep = tvb_get_ntohl(tvb, offset + 8); devid = tvb_get_ntohl(tvb, offset); if (!PINFO_FD_VISITED(pinfo)) { if (command == OP_CMD_SUBMIT || command == OP_CMD_UNLINK) { usbip_trans = wmem_new(wmem_file_scope(), usbip_transaction_t); usbip_trans->devid = devid; usbip_trans->dir = dir; usbip_trans->ep = ep; usbip_trans->seqnum = seqnum; usbip_trans->cmd_frame = pinfo->num; usbip_trans->ret_frame = 0; usbip_trans->unlink_seqnum = 0; wmem_tree_insert32(usbip_info->pdus, seqnum, (void *) usbip_trans); } else { usbip_trans = (usbip_transaction_t *) wmem_tree_lookup32(usbip_info->pdus, seqnum); if (usbip_trans) usbip_trans->ret_frame = pinfo->num; } } else { usbip_trans = (usbip_transaction_t *) wmem_tree_lookup32(usbip_info->pdus, seqnum); } if (!usbip_trans) { usbip_trans = wmem_new(pinfo->pool, usbip_transaction_t); usbip_trans->cmd_frame = 0; usbip_trans->ret_frame = 0; usbip_trans->devid = 0; usbip_trans->unlink_seqnum = 0; usbip_trans->seqnum = seqnum; } /* only the OP_CMD_SUBMIT has a valid devid - in all other case we have to restore it from the transaction */ if (command == OP_RET_SUBMIT || command == OP_RET_UNLINK) { devid = usbip_trans->devid; ep = usbip_trans->ep; dir = usbip_trans->dir; } ti = proto_tree_add_uint(tree, hf_usbip_cmd_frame, NULL, 0, 0, usbip_trans->cmd_frame); proto_item_set_generated(ti); ti = proto_tree_add_uint(tree, hf_usbip_ret_frame, NULL, 0, 0, usbip_trans->ret_frame); proto_item_set_generated(ti); ti = proto_tree_add_uint(tree, hf_usbip_devid, NULL, 0, 0, devid); proto_item_set_generated(ti); ti = proto_tree_add_uint(tree, hf_usbip_direction, NULL, 0, 0, dir); proto_item_set_generated(ti); ti = proto_tree_add_uint(tree, hf_usbip_ep, NULL, 0, 0, ep); proto_item_set_generated(ti); proto_tree_add_item(tree, hf_usbip_devid, tvb, offset, 4, ENC_BIG_ENDIAN); offset += 4; proto_tree_add_item(tree, hf_usbip_direction, tvb, offset, 4, ENC_BIG_ENDIAN); offset += 4; proto_tree_add_item(tree, hf_usbip_ep, tvb, offset, 4, ENC_BIG_ENDIAN); offset += 4; header.ep = ep; header.dir = dir; header.devid = devid & 0x00ff; header.busid = devid >> 16; switch (command) { case OP_CMD_SUBMIT: offset = dissect_cmd_submit(pinfo, tree, tvb, offset); dissect_usb_common(tvb, pinfo, orig, USB_HEADER_USBIP, &header); break; case OP_CMD_UNLINK: offset = dissect_cmd_unlink(pinfo, tree, tvb, offset, usbip_info, usbip_trans); break; case OP_RET_SUBMIT: { uint32_t status; status = tvb_get_ntohl(tvb, offset); offset = dissect_ret_submit(pinfo, tree, tvb, offset); if (status == 0) dissect_usb_common(tvb, pinfo, orig, USB_HEADER_USBIP, &header); break; } case OP_RET_UNLINK: offset = dissect_ret_unlink(pinfo, tree, tvb, offset, usbip_info, usbip_trans->unlink_seqnum); break; default: proto_tree_add_item(tree, hf_usbip_urb_data, tvb, offset, -1, ENC_NA); offset = tvb_reported_length_remaining(tvb, offset); expert_add_info_format( pinfo, ti, &ei_usbip, "Dissector for USBIP Command" " (%x) code not implemented, Contact" " Wireshark developers if you want this supported", command); proto_item_append_text(ti, ": Undecoded"); break; } return offset; } static int dissect_usbip_common(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void *data _U_) { uint16_t version; int offset = 0; proto_item *ti = NULL; proto_tree *usbip_tree = NULL; usbip_conv_info_t *usbip_info; /* Make entries in Protocol column and Info column on summary display */ col_set_str(pinfo->cinfo, COL_PROTOCOL, "USBIP"); col_clear(pinfo->cinfo, COL_INFO); usbip_info = usbip_get_usbip_conv(pinfo); /* create display subtree for the protocol */ ti = proto_tree_add_item(tree, proto_usbip, tvb, 0, -1, ENC_NA); usbip_tree = proto_item_add_subtree(ti, ett_usbip); /* Get some values from the packet header */ version = tvb_get_ntohs(tvb, 0); /* check if this is a operation code by checking the version. */ if (version == USBIP_SUPPORTED_VERSION) { offset = usbip_dissect_op(pinfo, tvb, usbip_tree, offset); } else if (version == 0x0000) { offset = usbip_dissect_urb(pinfo, tvb, usbip_tree, tree, offset, usbip_info); } else { proto_tree_add_item(usbip_tree, hf_usbip_urb_data, tvb, offset, -1, ENC_NA); offset = tvb_reported_length_remaining(tvb, offset); expert_add_info_format( pinfo, ti, &ei_usbip, "Dissector for USBIP Version" " (%d.%d) not implemented, Contact" " Wireshark developers if you want this supported", version >> 8, version & 0xff); proto_item_append_text(ti, ": Undecoded"); } return offset; } #define FRAME_HEADER_LEN 8 static unsigned int get_usbip_message_len(packet_info *pinfo _U_, tvbuff_t *tvb, int offset, void *data _U_) { uint16_t version; /* Get some values from the packet header */ version = tvb_get_ntohs(tvb, offset); /* The USBIP's is split in two parts. * There's an userspace portion which consists of the usbipd daemon * and usbip tool. * The kernel part is done by two modules usbip-host.ko and vhci-hcd.ko (client). * * The userspace programs are generating and parsing the OP_REQ_* and OP_REP_* * data packets. They have all have a proper protocol version field. But data * can be split up in multiple packages, so reassembly is required. There's no * session id or sequence number to track packages. * * The kernel modules are handling the OP_CMD_* and OP_RET_* data packets. * There's no protocol version (The version is simply always 0x0000, because * the OP_CMD|RET are 4-Bytes long, whereas OP_REQ/OP_REP are only 2-Bytes). * data frames can be split into multiple packages. But it also can happen that * multiple data frames are aggregated into a single package. The OP_CMD_* and * OP_RET_* frames have a 4-Byte sequence number to track individual URBs. The * sequence counter will wrap around eventually. */ if (version == USBIP_SUPPORTED_VERSION) { uint16_t op = tvb_get_ntohs(tvb, offset + 2); switch (op) { case OP_REQ_IMPORT: return 40; case OP_REP_IMPORT: if (tvb_get_ntohl(tvb, offset + 4) == 0) { /* Status: OK */ return 0x140; } else { /* Status: Error */ return 0x8; } case OP_REQ_DEVLIST: return 8; case OP_REP_DEVLIST: { unsigned int expected_size = 0xc; unsigned int num_of_devs; unsigned int i; if (tvb_captured_length(tvb) < 0xc) { /* not enough segments to calculate the size */ return 0x0; } offset += 8; num_of_devs = tvb_get_ntohl(tvb, offset); offset += 4; if (num_of_devs == 0) return expected_size; if (tvb_captured_length_remaining(tvb, offset) < (int) (0x138 * num_of_devs)) return 0; for (i = 0; i < num_of_devs; i++) { uint8_t num_of_intf = tvb_get_uint8(tvb, offset + 0x137); int skip = num_of_intf * 4; expected_size += 0x138 + skip; offset += 0x138 + skip; } return expected_size; } } } else if (version == 0x0000) { uint32_t cmd = tvb_get_ntohl(tvb, offset); if (tvb_captured_length_remaining(tvb, offset) < USBIP_HEADER_LEN) return 0; switch (cmd) { case OP_RET_UNLINK: return USBIP_HEADER_LEN; case OP_CMD_UNLINK: return USBIP_HEADER_LEN; case OP_CMD_SUBMIT: { int expected_size = USBIP_HEADER_LEN; int actual_length = tvb_get_ntohl(tvb, offset + 0x18); int dir = tvb_get_ntohl(tvb, offset + 0xc); int n_iso = tvb_get_ntohl(tvb, offset + 0x20); if (dir == USBIP_DIR_OUT) expected_size += actual_length; if (n_iso > 0) expected_size += n_iso * 4 * 4; return expected_size; } case OP_RET_SUBMIT: { int expected_size = USBIP_HEADER_LEN; int actual_length = tvb_get_ntohl(tvb, offset + 0x18); int n_iso = tvb_get_ntohl(tvb, offset + 0x20); usbip_transaction_t *usbip_trans = NULL; usbip_conv_info_t *usbip_info = usbip_get_usbip_conv(pinfo); uint32_t status = tvb_get_ntohl(tvb, offset + 0x14); if (usbip_info) { usbip_trans = (usbip_transaction_t *) wmem_tree_lookup32( usbip_info->pdus, tvb_get_ntohl(tvb, offset + 4)); if (usbip_trans && usbip_trans->dir == USBIP_DIR_IN && status == 0) expected_size += actual_length; } if (status == 0) { if (n_iso >= 0) expected_size += n_iso * 4 * 4; } else expected_size = tvb_captured_length(tvb); return expected_size; } } } return tvb_captured_length(tvb); } static int dissect_usbip(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void *data) { tcp_dissect_pdus(tvb, pinfo, tree, true, FRAME_HEADER_LEN, get_usbip_message_len, dissect_usbip_common, data); return tvb_captured_length(tvb); } /* Register the protocol with Wireshark */ void proto_register_usbip(void) { /* Setup list of header fields */ static hf_register_info hf[] = { {&hf_usbip_version, {"Version", "usbip.version", FT_UINT16, BASE_HEX, NULL, 0x0, "Version of the protocol", HFILL}}, {&hf_usbip_operation, {"Operation", "usbip.operation", FT_UINT16, BASE_HEX | BASE_EXT_STRING, &usbip_operation_vals_ext, 0x0, "USBIP Operation", HFILL}}, {&hf_usbip_command, {"Command", "usbip.urb", FT_UINT32, BASE_HEX | BASE_EXT_STRING, &usbip_urb_vals_ext, 0x0, "USBIP URB Transaction", HFILL}}, {&hf_usbip_status, {"Status", "usbip.status", FT_INT32, BASE_DEC | BASE_EXT_STRING, &linux_negative_errno_vals_ext, 0, "USBIP Status", HFILL}}, {&hf_usbip_number_devices, {"Number of exported Devices", "usbip.number_of_devices", FT_UINT32, BASE_DEC, NULL, 0, NULL, HFILL}}, {&hf_usbip_path, {"System Path", "usbip.system_path", FT_STRING, BASE_NONE, NULL, 0, NULL, HFILL}}, {&hf_usbip_devid, {"Devid", "usbip.devid", FT_UINT32, BASE_HEX, NULL, 0x0, NULL, HFILL}}, {&hf_usbip_busid, {"Busid", "usbip.busid", FT_STRING, BASE_NONE, NULL, 0, NULL, HFILL}}, {&hf_usbip_busnum, {"Bus number", "usbip.bus_num", FT_UINT32, BASE_HEX, NULL, 0x0, NULL, HFILL}}, {&hf_usbip_devnum, {"Device Number", "usbip.dev_num", FT_UINT32, BASE_HEX, NULL, 0x0, NULL, HFILL}}, {&hf_usbip_speed, {"Connected Speed", "usbip.speed", FT_UINT32, BASE_DEC | BASE_EXT_STRING, &usbip_speed_vals_ext, 0, NULL, HFILL}}, {&hf_usbip_idVendor, {"idVendor", "usbip.idVendor", FT_UINT16, BASE_HEX | BASE_EXT_STRING, &ext_usb_vendors_vals, 0x0, NULL, HFILL}}, {&hf_usbip_idProduct, {"idProduct", "usbip.idProduct", FT_UINT16, BASE_HEX, NULL, 0x0, NULL, HFILL}}, {&hf_usbip_bcdDevice, {"bcdDevice", "usbip.bcdDevice", FT_UINT16, BASE_HEX, NULL, 0x0, NULL, HFILL}}, {&hf_usbip_bDeviceClass, {"bDeviceClass", "usbip.bDeviceClass", FT_UINT8, BASE_HEX | BASE_EXT_STRING, &usb_class_vals_ext, 0x0, NULL, HFILL}}, {&hf_usbip_bDeviceSubClass, {"bDeviceSubClass", "usbip.bDeviceSubClass", FT_UINT8, BASE_DEC, NULL, 0, NULL, HFILL}}, {&hf_usbip_bDeviceProtocol, {"bDeviceProtocol", "usbip.bDeviceProtocol", FT_UINT8, BASE_DEC, NULL, 0, NULL, HFILL}}, {&hf_usbip_bConfigurationValue, {"bConfigurationValue", "usbip.bConfigurationValue", FT_UINT8, BASE_DEC, NULL, 0, NULL, HFILL}}, {&hf_usbip_bNumConfigurations, {"bNumConfigurations", "usbip.bNumConfigurations", FT_UINT8, BASE_DEC, NULL, 0, NULL, HFILL}}, {&hf_usbip_bNumInterfaces, {"bNumInterfaces", "usbip.bNumInterfaces", FT_UINT8, BASE_DEC, NULL, 0, NULL, HFILL}}, {&hf_usbip_bInterfaceClass, {"bInterfaceClass", "usbip.bInterfaceClass", FT_UINT8, BASE_HEX | BASE_EXT_STRING, &usb_class_vals_ext, 0x0, NULL, HFILL}}, {&hf_usbip_bInterfaceSubClass, {"bInterfaceSubClass", "usbip.bInterfaceSubClass", FT_UINT8, BASE_HEX, NULL, 0x0, NULL, HFILL}}, {&hf_usbip_bInterfaceProtocol, {"bInterfaceProtocol", "usbip.bInterfaceProtocol", FT_UINT8, BASE_HEX, NULL, 0x0, NULL, HFILL}}, {&hf_usbip_padding, {"Padding", "usbip.padding", FT_BYTES, BASE_NONE, NULL, 0, NULL, HFILL}}, {&hf_usbip_device, {"Device", "usbip.device", FT_UINT32, BASE_DEC, NULL, 0, NULL, HFILL}}, {&hf_usbip_interface, {"Interface", "usbip.interface", FT_UINT32, BASE_DEC, NULL, 0, NULL, HFILL}}, {&hf_usbip_interval, {"Interval", "usbip.interval", FT_UINT32, BASE_DEC, NULL, 0, "maximum time for the request on the server-side host controller", HFILL}}, {&hf_usbip_actual_length, {"Actual length", "usbip.actual_length", FT_UINT32, BASE_DEC, NULL, 0, NULL, HFILL}}, {&hf_usbip_error_count, {"ISO error count", "usbip.iso.error_count", FT_UINT32, BASE_DEC, NULL, 0, NULL, HFILL}}, {&hf_usbip_seqnum, {"Sequence", "usbip.sequence_no", FT_UINT32, BASE_DEC, NULL, 0, "Sequence number", HFILL}}, {&hf_usbip_cmd_frame, {"Command frame", "usbip.cmd_frame", FT_FRAMENUM, BASE_NONE, NULL, 0, NULL, HFILL}}, {&hf_usbip_ret_frame, {"Return frame", "usbip.ret_frame", FT_FRAMENUM, BASE_NONE, NULL, 0, NULL, HFILL}}, {&hf_usbip_vic_frame, {"Victim frame", "usbip.vic_frame", FT_FRAMENUM, BASE_NONE, NULL, 0, "Frame which was forcefully cancelled", HFILL}}, {&hf_usbip_direction, {"Direction", "usbip.endpoint_number.direction", FT_UINT32, BASE_HEX, VALS(usb_endpoint_direction_vals), 0x00000001, "USB endpoint direction", HFILL}}, {&hf_usbip_ep, {"Endpoint", "usbip.endpoint_number", FT_UINT32, BASE_HEX, NULL, 0x0000000f, "USB endpoint number", HFILL}}, {&hf_usbip_transfer_flags, {"Transfer flags", "usbip.transfer_flags", FT_UINT32, BASE_HEX, NULL, 0x0, "USBIP Transferflag", HFILL}}, {&hf_usbip_transfer_buffer_length, {"Transfer buffer length [bytes]", "usbip.transfer_buffer_length", FT_UINT32, BASE_DEC, NULL, 0, "Data length in bytes", HFILL}}, {&hf_usbip_start_frame, {"ISO Start frame", "usbip.iso.start_frame", FT_INT32, BASE_DEC, NULL, 0, "For an ISO frame the actually selected frame to transmit", HFILL}}, {&hf_usbip_number_of_packets, {"Number of ISO descriptors", "usbip.iso.num_of_packets", FT_UINT32, BASE_DEC, NULL, 0, NULL, HFILL}}, {&hf_usbip_setup, {"Setup Data", "usbip.setup", FT_BYTES, BASE_NONE, NULL, 0, NULL, HFILL}}, {&hf_usbip_urb_data, {"Data", "usbip.data", FT_BYTES, BASE_NONE, NULL, 0, "Raw Data", HFILL}}, }; static int *ett[] = { &ett_usbip, &ett_usbip_dev, &ett_usbip_intf, }; static ei_register_info ei[] = { {&ei_usbip, { "usbip.unsupported_version", PI_MALFORMED, PI_ERROR, "Unsupported element", EXPFILL}}, }; expert_module_t *expert_usbip; proto_usbip = proto_register_protocol("USBIP Protocol", "USBIP", "usbip"); proto_register_field_array(proto_usbip, hf, array_length(hf)); proto_register_subtree_array(ett, array_length(ett)); usbip_handle = register_dissector("usbip", dissect_usbip, proto_usbip); expert_usbip = expert_register_protocol(proto_usbip); expert_register_field_array(expert_usbip, ei, array_length(ei)); } void proto_reg_handoff_usbip(void) { dissector_add_for_decode_as_with_preference("tcp.port", usbip_handle); } /* * Editor modelines - https://www.wireshark.org/tools/modelines.html * * Local Variables: * c-basic-offset: 2 * tab-width: 8 * indent-tabs-mode: nil * End: * * ex: set shiftwidth=2 tabstop=8 expandtab: * :indentSize=2:tabSize=8:noTabs=true: */