wireshark/epan/dissectors/packet-usbip.c
Michael Mann 268841f3e0 Combine Decode As and port preferences for tcp.port dissector table.
This patch introduces new APIs to allow dissectors to have a preference for
a (TCP) port, but the underlying data is actually part of Decode As functionality.
For now the APIs are intentionally separate from the regular APIs that register a
dissector within a dissector table.  It may be possible to eventually combine the
two so that all dissectors that register with a dissector table have an opportunity
to "automatically" have a preference to adjust the "table value" through the
preferences dialog.

The tcp.port dissector table was used as the guinea pig.  This will eventually be
expanded to other dissector tables as well (most notably UDP ports).  Some
dissectors that "shared" a TCP/UDP port preference were also converted. It also
removed the need for some preference callback functions (mostly when the callback
function was the proto_reg_handoff function) so there is cleanup around that.

Dissectors that has a port preference whose default was 0 were switched to using
the dissector_add_for_decode_as_with_preference API rather than dissector_add_uint_with_preference

Also added comments for TCP ports used that aren't IANA registered.

Change-Id: I99604f95d426ad345f4b494598d94178b886eb67
Reviewed-on: https://code.wireshark.org/review/17724
Reviewed-by: Michael Mann <mmann78@netscape.net>
2016-10-08 02:44:53 +00:00

1097 lines
36 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
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
/*
* 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://git.kernel.org/cgit/linux/kernel/git/torvalds/linux.git/plain/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 {
USB_SPEED_UNKNOWN = 0, /* enumerating */
USB_SPEED_LOW, /* usb 1.0 */
USB_SPEED_FULL, /* usb 1.1 */
USB_SPEED_HIGH, /* usb 2.0 */
USB_SPEED_WIRELESS, /* wireless (usb 2.5) */
USB_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[] = {
{USB_SPEED_UNKNOWN, "Speed Unknown" },
{USB_SPEED_LOW, "Low Speed" },
{USB_SPEED_FULL, "Full Speed" },
{USB_SPEED_HIGH, "High Speed" },
{USB_SPEED_WIRELESS, "Wireless Speed" },
{USB_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;
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");
proto_tree_add_item(tree, hf_usbip_transfer_flags, tvb, offset, 4,
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(wmem_packet_scope(), 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)
{
/* Check that there's enough data */
if (tvb_reported_length(tvb) < 4) {
/* usbip's smallest packet size is 4 */
return 0;
}
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, &usb_urb_status_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 - http://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:
*/