文章目录
- 背景
- 典型场景
- 编译
- usbredirparser
- usbredirfilter
- usbredirparser/usbredirproto
- usbredirhost
- usbredirect/usbredirtestclient
- 参考
背景
usbredir 是一种用于通过网络转发 USB 设备流量的网络协议。它也是一个软件包的名称,该软件包提供了一个解析库、一个 usbredirhost 库以及多个实现此协议的工具。
协议的具体内容可以参考usb-redirection-protocol,该文档还解释了 usbhost 和 usbguest 的含义。
usbredir 最初是为与 Spice 配合使用而创建的,但该协议和 usbredirhost 完全独立于 Spice,它们也可以用于创建 VNC 扩展,以便通过 VNC 连接将 USB 设备重定向到 QEMU 虚拟机。
usb guest 端目前仅在 QEMU 中实现,并作为 QEMU 的一部分发布(在 QEMU 中启用此功能需要在构建 QEMU 时提供 libusbredirparser 库)。
usbredir 支持通过过滤字符串配置 USB 设备过滤功能。
usbredir项目包含下面几个库/工具:
-
usbredirparser
一个包含 usbredir 协议解析器的库。 -
usbredirhost
一个实现 usbredir 连接中 usb-host端的库,这是实际usb设备所连接的一端。
希望实现 usb-host 的应用程序需要实现:
- 为设备提供一个 libusb 设备句柄
- 为 usbredir 数据的实际传输提供写和读的回调函数
- 监听 usbredir 和 libusb 的读/写事件,并调用其处理程序
-
usbredirect
usbredirect 二进制文件是一个 usbredir 客户端,用于将 USB 设备导出,以便通过 usbredir 协议从另一台(虚拟)机器使用。 -
usbredirtestclient
一个用于通过 TCP 使用 usbredir 协议 的小型测试客户端, 其使用usbredirparser库。
典型场景
一个典型场景:
- usb device:usb设备。
- client pc:usb设备直接连接的pc,一般是用户终端(pc或者VDI盒子)。
- guest vm:连接到usb设备并使用它的桌面(一般是VM),就像直接连接到它一样。
- usb host:使usb device可供usb guest使用的程序。
编译
- 下载源码
$ git clone https://gitlab.freedesktop.org/spice/usbredir.git
- 安装依赖
参考.gitlab-ci.yml
文件里的依赖
# debian环境
sudo apt install gcc clang meson ninja-build valgrind git bzip2 libglib2.0-dev libusb-1.0-0-dev
- 编译
$ meson . _build -Dfuzzing=disabled -Dtools=enabled
$ cd _build
$ meson compile
编译后,生成产物:
usbredirparser
usbredirparser/
├── meson.build
├── strtok_r.c
├── strtok_r.h
├── usbredirfilter.c
├── usbredirfilter.h
├── usbredirparser.c
├── usbredirparser.h
├── usbredirparser.map
├── usbredirproto-compat.h
└── usbredirproto.h # redirection protocol的相关定义
其中:usbredirfilter.h
、usbredirparser.h
、usbredirproto.h
是提供的对外接口头文件。
usbredirfilter
首先定义了usbredir的规则:
struct usbredirfilter_rule {int device_class; /* 0-255, -1 to match any class */int vendor_id; /* 0-65535, -1 to match any id */int product_id; /* 0-65535, -1 to match any id */int device_version_bcd; /* 0-65535, -1 to match any version */int allow; /* 0: deny redir for this device, non 0: allow */
};
通过该规则可以过滤哪些usb设备可以重定向哪些不可以。
随后定义了规则的转换函数:
int usbredirfilter_string_to_rules(const char *filter_str, const char *token_sep, const char *rule_sep,struct usbredirfilter_rule **rules_ret, int *rules_count_ret);
char *usbredirfilter_rules_to_string(const struct usbredirfilter_rule *rules,int rules_count, const char *token_sep, const char *rule_sep);
通过这两个函数可以在字符串和规则之间转换。
下面是检查函数,用户检查一个设备是否允许重定向:
int usbredirfilter_check(const struct usbredirfilter_rule *rules, int rules_count,uint8_t device_class, uint8_t device_subclass, uint8_t device_protocol,uint8_t *interface_class, uint8_t *interface_subclass,uint8_t *interface_protocol, int interface_count,uint16_t vendor_id, uint16_t product_id, uint16_t device_version_bcd,int flags);
对传入的规则进行完整性检查:
int usbredirfilter_verify(const struct usbredirfilter_rule *rules, int rules_count);
将规则打印到文件中:
void usbredirfilter_print(const struct usbredirfilter_rule *rules, int rules_count, FILE *out);
最后,释放库所分配的内存:
void usbredirfilter_free(void *ptr);
usbredirparser/usbredirproto
一个usbredir protocol的解析器,用于协议解析,和usbredirproto.h一起。
关于协议这块,可以参考官方文档:usb-redirection-protocol或者其他人的总结:USB重定向协议。
usbredirhost
usbredirhost/
├── meson.build
├── usbredirhost.c
├── usbredirhost.h
└── usbredirhost.map
其中usbredirhost.h
是提供的对外接口头文件。
usbredir的打开与关闭:
struct usbredirhost *usbredirhost_open(libusb_context *usb_ctx,libusb_device_handle *usb_dev_handle,usbredirparser_log log_func,usbredirparser_read read_guest_data_func,usbredirparser_write write_guest_data_func,void *func_priv, const char *version, int verbose, int flags);/* See docs/multi-thread.md */
struct usbredirhost *usbredirhost_open_full(libusb_context *usb_ctx,libusb_device_handle *usb_dev_handle,usbredirparser_log log_func,usbredirparser_read read_guest_data_func,usbredirparser_write write_guest_data_func,usbredirhost_flush_writes flush_writes_func,usbredirparser_alloc_lock alloc_lock_func,usbredirparser_lock lock_func,usbredirparser_unlock unlock_func,usbredirparser_free_lock free_lock_func,void *func_priv, const char *version, int verbose, int flags);
void usbredirhost_close(struct usbredirhost *host);
usbredirhost 获取设备/释放设备
int usbredirhost_set_device(struct usbredirhost *host,libusb_device_handle *usb_dev_handle);
设置usbredirhost的回调,在usbredirhost_fl_write_cb_owns_buffer标志位有效时,该回调负责返回应用程序的待写入缓冲区大小(以字节为单位)。
void usbredirhost_set_buffered_output_size_cb(struct usbredirhost *host,usbredirhost_buffered_output_size buffered_output_size_func);
数据处理相关:
int usbredirhost_read_guest_data(struct usbredirhost *host);
int usbredirhost_has_data_to_write(struct usbredirhost *host);
int usbredirhost_write_guest_data(struct usbredirhost *host);
void usbredirhost_free_write_buffer(struct usbredirhost *host, uint8_t *data);
获取usbredirfilter规则
void usbredirhost_get_guest_filter(struct usbredirhost *host,const struct usbredirfilter_rule **rules_ret, int *rules_count_ret);
检查usb设备是否允许重定向
int usbredirhost_check_device_filter(const struct usbredirfilter_rule *rules,int rules_count, libusb_device *dev, int flags);
usbredirect/usbredirtestclient
tools
├── meson.build
├── usbredirect.1
└── usbredirect.cusbredirtestclient/
├── meson.build
└── usbredirtestclient.c
运行参数:
$ ./usbredirect
./usbredirect need to act either as client (-to) or as server (-as)
Usage:usbredirect [OPTION?]Help Options:-h, --help Show help optionsApplication Options:--device Local USB device to be redirected identified as either VENDOR:PRODUCT "0123:4567" or BUS-DEVICE "5-2"--to Client URI to connect to--as Server URI to be run-k, --keepalive If we should set SO_KEEPALIVE flag on underlying socket-v, --verbose Set log level between 1-5 where 5 being the most verbose$ ./usbredirtestclient
Missing server argument
Usage: ./usbredirtestclient [-p|--port <port>] [-v|--verbose <0-3>] <server>
测试:
- usbredirect 作为server端将usb设备导出
$ sudo ./tools/usbredirect --as 0.0.0.0:5909 --device 058f:6387 --verbose 5
注意看此时系统日志:
[ 601.361432] usb-storage 1-12.1:1.0: USB Mass Storage device detected
[ 601.361930] scsi host6: usb-storage 1-12.1:1.0
[ 601.617526] usb 1-12.1: reset high-speed USB device number 8 using xhci_hcd
被导出的设备被reset掉了,本地lsusb是看不到这个设备了,设备从本地导出了。
- usbredirtestclient 连接到server端
连接到server端:
$ ./usbredirtestclient/usbredirtestclient -p 5909 192.168.0.26
usbredirparser: Peer version: usbredir 0.14.0, using 64-bits ids
interface 0 class 8 subclass 6 protocol 80
endpoint: 00, type: 0, interval: 0, interface: 0 max-packetsize: 0
endpoint: 01, type: 2, interval: 0, interface: 0 max-packetsize: 512
endpoint: 80, type: 0, interval: 0, interface: 0 max-packetsize: 0
endpoint: 82, type: 2, interval: 0, interface: 0 max-packetsize: 512
device info: speed: highclass 0 subclass 0 protocol 0vendor 0x058f product 6387
Get config: 1, status: 0
Set config: 1, status: 0
Get alt: 0, interface: 0, status: 0
interface 0 class 8 subclass 6 protocol 80
endpoint: 00, type: 0, interval: 0, interface: 0 max-packetsize: 0
endpoint: 01, type: 2, interval: 0, interface: 0 max-packetsize: 512
endpoint: 80, type: 0, interval: 0, interface: 0 max-packetsize: 0
endpoint: 82, type: 2, interval: 0, interface: 0 max-packetsize: 512
Set alt: 0, interface: 0, status: 0
> help
Available commands:
ctrl <endpoint> <request> <request_type> <value> <index> <length> [data]
quit
help
> quit
在客户端退出后,看下server端的系统日志:
[ 733.420713] usb 1-12.1: reset high-speed USB device number 8 using xhci_hcd
[ 733.628274] usb-storage 1-12.1:1.0: USB Mass Storage device detected
[ 733.628954] scsi host6: usb-storage 1-12.1:1.0
[ 734.654030] scsi 6:0:0:0: Direct-Access Generic Flash Disk 8.07 PQ: 0 ANSI: 4
[ 734.654755] sd 6:0:0:0: Attached scsi generic sg5 type 0
[ 734.657656] sd 6:0:0:0: [sdf] 15728640 512-byte logical blocks: (8.05 GB/7.50 GiB)
[ 734.658392] sd 6:0:0:0: [sdf] Write Protect is off
[ 734.658399] sd 6:0:0:0: [sdf] Mode Sense: 23 00 00 00
[ 734.659086] sd 6:0:0:0: [sdf] Write cache: disabled, read cache: enabled, doesn't support DPO or FUA
[ 734.725772] sdf: sdf1
[ 734.726165] sd 6:0:0:0: [sdf] Attached SCSI removable disk
可以发现,原来导出的设备重新被attach进去了,此时lsusb又可以看到该设备了,设备又回到本地了。
着两个工具对应的代码都很短,可以进去看看他们是怎么工作的,核心就是使用usbredirhost和usbredirparser这两个库。
参考
usbredir
usbredir
usbredir-0.7内容详解(一)
spice
USB重定向协议
USB虚拟化和虚拟桌面USB重定向
Spice Usbredirect 性能改进