wireshark/epan/dissectors/packet-usbip.c
Tomasz Moń 1a60de0b76
USB: Sanitize USB 2.0 endpoint max packet size
Introduce Wireshark specific enum to facilitate USB speed specific
dissection. Any similarity of actual enum values with any protocol
is coincidence and should not be relied upon.

Rename speed defines in USBIP dissector to not collide with Wireshark
USB speed enum. The values used in USBIP are implementation specific.

Allow user to set capture speed in USBLL dissector preferences. Use the
selected speed in USB dissector to sanitize endpoint maximum packet size
value based on speed specfic requirements from USB 2.0 specification.

Close #18062
2022-05-25 20:26:24 +02:00

1080 lines
35 KiB
C

/* packet-usbip.c
* Routines for USB/IP dissection
* Copyright 2016, Christian Lamparter <chunkeey@googlemail.com>
*
* Wireshark - Network traffic analyzer
* By Gerald Combs <gerald@wireshark.org>
* 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 <config.h>
#include <epan/packet.h>
#include <epan/expert.h>
#include <epan/etypes.h>
#include <epan/conversation.h>
#include "packet-usbip.h"
#include "packet-usb.h"
#include "packet-tcp.h"
void proto_register_usbip(void);
void proto_reg_handoff_usbip(void);
/* Initialize the protocol and registered fields
*/
static int proto_usbip = -1;
static int hf_usbip_version = -1;
static int hf_usbip_operation = -1;
static int hf_usbip_command = -1;
static int hf_usbip_status = -1;
static int hf_usbip_number_devices = -1;
static int hf_usbip_path = -1;
static int hf_usbip_devid = -1;
static int hf_usbip_busid = -1;
static int hf_usbip_busnum = -1;
static int hf_usbip_devnum = -1;
static int hf_usbip_speed = -1;
static int hf_usbip_idVendor = -1;
static int hf_usbip_idProduct = -1;
static int hf_usbip_bcdDevice = -1;
static int hf_usbip_bDeviceClass = -1;
static int hf_usbip_bDeviceSubClass = -1;
static int hf_usbip_bDeviceProtocol = -1;
static int hf_usbip_bConfigurationValue = -1;
static int hf_usbip_bNumConfigurations = -1;
static int hf_usbip_bNumInterfaces = -1;
static int hf_usbip_bInterfaceClass = -1;
static int hf_usbip_bInterfaceSubClass = -1;
static int hf_usbip_bInterfaceProtocol = -1;
static int hf_usbip_padding = -1;
static int hf_usbip_device = -1;
static int hf_usbip_interface = -1;
static int hf_usbip_interval = -1;
static int hf_usbip_actual_length = -1;
static int hf_usbip_error_count = -1;
static int hf_usbip_seqnum = -1;
static int hf_usbip_cmd_frame = -1;
static int hf_usbip_ret_frame = -1;
static int hf_usbip_vic_frame = -1;
static int hf_usbip_direction = -1;
static int hf_usbip_ep = -1;
static int hf_usbip_transfer_flags = -1;
static int hf_usbip_transfer_buffer_length = -1;
static int hf_usbip_start_frame = -1;
static int hf_usbip_number_of_packets = -1;
static int hf_usbip_setup = -1;
static int hf_usbip_urb_data = -1;
/* Initialize the subtree pointers */
static gint ett_usbip = -1;
static gint ett_usbip_dev = -1;
static gint ett_usbip_intf = -1;
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 = EI_INIT;
typedef struct _usbip_transaction_t
{
guint32 seqnum;
guint32 devid;
guint32 ep;
guint32 dir;
guint32 cmd_frame;
guint32 ret_frame;
guint32 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)
{
guint32 product;
guint32 vendor_id;
guint32 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;
guint32 num_of_devs;
guint32 i;
guint8 num_of_intf;
guint8 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_guint8(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, guint32 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;
guint32 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,
guint32 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;
guint32 operation;
gint32 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;
guint32 command;
guint32 devid;
guint32 seqnum;
guint32 dir;
guint32 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: {
guint32 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_)
{
guint16 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_)
{
guint16 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) {
guint16 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) < (gint) (0x138 * num_of_devs))
return 0;
for (i = 0; i < num_of_devs; i++) {
guint8 num_of_intf = tvb_get_guint8(tvb, offset + 0x137);
int skip = num_of_intf * 4;
expected_size += 0x138 + skip;
offset += 0x138 + skip;
}
return expected_size;
}
}
} else if (version == 0x0000) {
guint32 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;
if (tvb_get_ntohl(tvb, offset + 0xc) == USBIP_DIR_OUT)
expected_size += tvb_get_ntohl(tvb, offset + 0x18);
expected_size += tvb_get_ntohl(tvb, offset + 0x20) * 4 * 4;
return expected_size;
}
case OP_RET_SUBMIT: {
int expected_size = USBIP_HEADER_LEN;
usbip_transaction_t *usbip_trans = NULL;
usbip_conv_info_t *usbip_info = usbip_get_usbip_conv(pinfo);
guint32 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 += tvb_get_ntohl(tvb, offset + 0x18);
}
if (status == 0)
expected_size += tvb_get_ntohl(tvb, offset + 0x20) * 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_UINT8, BASE_HEX, VALS(usb_endpoint_direction_vals), 0x1,
"USB endpoint direction", HFILL}},
{&hf_usbip_ep,
{"Endpoint", "usbip.endpoint_number",
FT_UINT8, BASE_HEX, NULL, 0xf,
"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 gint *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));
expert_usbip = expert_register_protocol(proto_usbip);
expert_register_field_array(expert_usbip, ei, array_length(ei));
}
void
proto_reg_handoff_usbip(void)
{
dissector_handle_t usbip_handle;
usbip_handle = create_dissector_handle(dissect_usbip, proto_usbip);
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:
*/