usb-redir: Add the posibility to filter out certain devices from redirecion

This patch adds the posibility to filter out certain devices from redirecion.
To use this pass the filter property to -device usb-redir.  The filter
property takes a string consisting of filter rules, the format for a rule is:
<class>:<vendor>:<product>:<version>:<allow>

-1 can be used to allow any value for a field.

Muliple rules can be concatonated using | as a separator. Note that if
a device matches none of the passed in rules, redirecting it will not be
allowed!

Example:
-device usb-redir,filter='-1:0x0781:0x5567👎0|0x08👎-1👎1'

This example will deny the Sandisk Cruzer Blade being redirected, as it
has a usb id of 0781:5567, it will allow any other usb mass storage devices,
and it will deny any other devices (the default for devices not matching any
of the rules.

Signed-off-by: Hans de Goede <hdegoede@redhat.com>
Signed-off-by: Gerd Hoffmann <kraxel@redhat.com>
This commit is contained in:
Hans de Goede 2012-01-25 09:50:39 +01:00 committed by Gerd Hoffmann
parent f76e1d8142
commit 6af165892c
2 changed files with 106 additions and 11 deletions

2
configure vendored
View File

@ -2584,7 +2584,7 @@ fi
# check for usbredirparser for usb network redirection support
if test "$usb_redir" != "no" ; then
if $pkg_config libusbredirparser >/dev/null 2>&1 ; then
if $pkg_config --atleast-version=0.3.3 libusbredirparser >/dev/null 2>&1 ; then
usb_redir="yes"
usb_redir_cflags=$($pkg_config --cflags libusbredirparser 2>/dev/null)
usb_redir_libs=$($pkg_config --libs libusbredirparser 2>/dev/null)

View File

@ -34,6 +34,7 @@
#include <sys/ioctl.h>
#include <signal.h>
#include <usbredirparser.h>
#include <usbredirfilter.h>
#include "hw/usb.h"
@ -72,6 +73,7 @@ struct USBRedirDevice {
/* Properties */
CharDriverState *cs;
uint8_t debug;
char *filter_str;
/* Data passed from chardev the fd_read cb to the usbredirparser read cb */
const uint8_t *read_buf;
int read_buf_size;
@ -84,6 +86,11 @@ struct USBRedirDevice {
struct endp_data endpoint[MAX_ENDPOINTS];
uint32_t packet_id;
QTAILQ_HEAD(, AsyncURB) asyncq;
/* Data for device filtering */
struct usb_redir_device_connect_header device_info;
struct usb_redir_interface_info_header interface_info;
struct usbredirfilter_rule *filter_rules;
int filter_rules_count;
};
struct AsyncURB {
@ -780,6 +787,7 @@ static int usbredir_handle_control(USBDevice *udev, USBPacket *p,
static void usbredir_open_close_bh(void *opaque)
{
USBRedirDevice *dev = opaque;
uint32_t caps[USB_REDIR_CAPS_SIZE] = { 0, };
usbredir_device_disconnect(dev);
@ -810,7 +818,9 @@ static void usbredir_open_close_bh(void *opaque)
dev->parser->interrupt_packet_func = usbredir_interrupt_packet;
dev->read_buf = NULL;
dev->read_buf_size = 0;
usbredirparser_init(dev->parser, VERSION, NULL, 0, 0);
usbredirparser_caps_set_cap(caps, usb_redir_cap_connect_device_version);
usbredirparser_init(dev->parser, VERSION, caps, USB_REDIR_CAPS_SIZE, 0);
usbredirparser_do_write(dev->parser);
}
}
@ -880,6 +890,17 @@ static int usbredir_initfn(USBDevice *udev)
return -1;
}
if (dev->filter_str) {
i = usbredirfilter_string_to_rules(dev->filter_str, ":", "|",
&dev->filter_rules,
&dev->filter_rules_count);
if (i) {
qerror_report(QERR_INVALID_PARAMETER_VALUE, "filter",
"a usb device filter string");
return -1;
}
}
dev->open_close_bh = qemu_bh_new(usbredir_open_close_bh, dev);
dev->attach_timer = qemu_new_timer_ms(vm_clock, usbredir_do_attach, dev);
@ -929,6 +950,44 @@ static void usbredir_handle_destroy(USBDevice *udev)
if (dev->parser) {
usbredirparser_destroy(dev->parser);
}
free(dev->filter_rules);
}
static int usbredir_check_filter(USBRedirDevice *dev)
{
if (dev->interface_info.interface_count == 0) {
ERROR("No interface info for device\n");
return -1;
}
if (dev->filter_rules) {
if (!usbredirparser_peer_has_cap(dev->parser,
usb_redir_cap_connect_device_version)) {
ERROR("Device filter specified and peer does not have the "
"connect_device_version capability\n");
return -1;
}
if (usbredirfilter_check(
dev->filter_rules,
dev->filter_rules_count,
dev->device_info.device_class,
dev->device_info.device_subclass,
dev->device_info.device_protocol,
dev->interface_info.interface_class,
dev->interface_info.interface_subclass,
dev->interface_info.interface_protocol,
dev->interface_info.interface_count,
dev->device_info.vendor_id,
dev->device_info.product_id,
dev->device_info.device_version_bcd,
0) != 0) {
return -1;
}
}
return 0;
}
/*
@ -957,6 +1016,7 @@ static void usbredir_device_connect(void *priv,
struct usb_redir_device_connect_header *device_connect)
{
USBRedirDevice *dev = priv;
const char *speed;
if (qemu_timer_pending(dev->attach_timer) || dev->dev.attached) {
ERROR("Received device connect while already connected\n");
@ -965,26 +1025,48 @@ static void usbredir_device_connect(void *priv,
switch (device_connect->speed) {
case usb_redir_speed_low:
DPRINTF("attaching low speed device\n");
speed = "low speed";
dev->dev.speed = USB_SPEED_LOW;
break;
case usb_redir_speed_full:
DPRINTF("attaching full speed device\n");
speed = "full speed";
dev->dev.speed = USB_SPEED_FULL;
break;
case usb_redir_speed_high:
DPRINTF("attaching high speed device\n");
speed = "high speed";
dev->dev.speed = USB_SPEED_HIGH;
break;
case usb_redir_speed_super:
DPRINTF("attaching super speed device\n");
speed = "super speed";
dev->dev.speed = USB_SPEED_SUPER;
break;
default:
DPRINTF("attaching unknown speed device, assuming full speed\n");
speed = "unknown speed";
dev->dev.speed = USB_SPEED_FULL;
}
if (usbredirparser_peer_has_cap(dev->parser,
usb_redir_cap_connect_device_version)) {
INFO("attaching %s device %04x:%04x version %d.%d class %02x\n",
speed, device_connect->vendor_id, device_connect->product_id,
device_connect->device_version_bcd >> 8,
device_connect->device_version_bcd & 0xff,
device_connect->device_class);
} else {
INFO("attaching %s device %04x:%04x class %02x\n", speed,
device_connect->vendor_id, device_connect->product_id,
device_connect->device_class);
}
dev->dev.speedmask = (1 << dev->dev.speed);
dev->device_info = *device_connect;
if (usbredir_check_filter(dev)) {
WARNING("Device %04x:%04x rejected by device filter, not attaching\n",
device_connect->vendor_id, device_connect->product_id);
return;
}
qemu_mod_timer(dev->attach_timer, dev->next_attach_time);
}
@ -1011,15 +1093,27 @@ static void usbredir_device_disconnect(void *priv)
for (i = 0; i < MAX_ENDPOINTS; i++) {
QTAILQ_INIT(&dev->endpoint[i].bufpq);
}
dev->interface_info.interface_count = 0;
}
static void usbredir_interface_info(void *priv,
struct usb_redir_interface_info_header *interface_info)
{
/* The intention is to allow specifying acceptable interface classes
for redirection on the cmdline and in the future verify this here,
and disconnect (or never connect) the device if a not accepted
interface class is detected */
USBRedirDevice *dev = priv;
dev->interface_info = *interface_info;
/*
* If we receive interface info after the device has already been
* connected (ie on a set_config), re-check the filter.
*/
if (qemu_timer_pending(dev->attach_timer) || dev->dev.attached) {
if (usbredir_check_filter(dev)) {
ERROR("Device no longer matches filter after interface info "
"change, disconnecting!\n");
usbredir_device_disconnect(dev);
}
}
}
static void usbredir_ep_info(void *priv,
@ -1318,6 +1412,7 @@ static void usbredir_interrupt_packet(void *priv, uint32_t id,
static Property usbredir_properties[] = {
DEFINE_PROP_CHR("chardev", USBRedirDevice, cs),
DEFINE_PROP_UINT8("debug", USBRedirDevice, debug, 0),
DEFINE_PROP_STRING("filter", USBRedirDevice, filter_str),
DEFINE_PROP_END_OF_LIST(),
};