文章目錄
- 背景
- 典型場景
- 編譯
- 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 性能改進