wireshark/epan/dissectors/packet-usbip.c

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_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 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:
*/