
To date, there are quite a few ways to forward a USB device to another computer or virtual machine over the network.
Of the most popular, hardware such as AnywhereUSB and purely software products, from those that I tried myself: USB Redirector and USB / IP.
I would like to tell you about another interesting method that works directly with the QEMU emulator.
It is also part of the spice project, officially supported by RedHat.
UsbRedir, is an open protocol for forwarding usb-devices via tcp to a remote virtual server, developed with the support of RedHat in the framework of the spice project. But as it turned out they can be quite successfully used without spice. The server is usbredirserver, which fumbles a usb device on a specific port, and QEMU itself as a client, which emulates the connection of an exported usb device to a specific usb controller of your virtual machine. Thanks to this approach, absolutely any OS can be used as a guest system, since it does not even know that the device is remotely forwarded, and all the logic rests on QEMU.
')
First, a few words about the solutions listed above.
- AnywhereUSB is a pretty good solution, but expensive, and has unpleasant glitches, for example it happens if the shared flash drive falls off, then you can reconnect it only by physically removing and inserting it.
- USB / IP - OpenSource project. It seems like he was abandoned. In fact, it's pretty buggy. When the connection is broken, the machine often goes into complete freezee, and windows shows BSOD
- USB Redirector - Great software. To share devices from linux to linux is free, in all other cases it is already worth the money, not as much as AnywhereUSB, but not for free as I would like :)
As you can see, there are plenty to choose from, but let's finally try another way - UsbRedir?
Virtual Machine Configuration
In order to connect the exported devices, you need to create the necessary usb controllers on the virtual machine:
- uhci - for USB1.0
- ehci - for USB2.0
- xhci - for USB3.0
For qemu (without libvirt)
Add options to the virtual machine start command:
-device ich9-usb-ehci1,id=ehci,addr=1d.7,multifunction=on -device ich9-usb-uhci1,id=uhci-1,addr=1d.0,multifunction=on,masterbus=ehci.0,firstport=0 -device ich9-usb-uhci2,id=uhci-2,addr=1d.1,multifunction=on,masterbus=ehci.0,firstport=2 -device ich9-usb-uhci3,id=uhci-3,addr=1d.2,multifunction=on,masterbus=ehci.0,firstport=4
For libvirt
In the source virtual machine configuration file in the
<devices> node, remove all USB controllers and add the following block:
<controller type='usb' index='0' model='ich9-ehci1'> <address type='pci' domain='0x0000' bus='0x00' slot='0x08' function='0x7'/> </controller> <controller type='usb' index='0' model='ich9-uhci1'> <master startport='0'/> <address type='pci' domain='0x0000' bus='0x00' slot='0x08' function='0x0' multifunction='on'/> </controller> <controller type='usb' index='0' model='ich9-uhci2'> <master startport='2'/> <address type='pci' domain='0x0000' bus='0x00' slot='0x08' function='0x1'/> </controller> <controller type='usb' index='0' model='ich9-uhci3'> <master startport='4'/> <address type='pci' domain='0x0000' bus='0x00' slot='0x08' function='0x2'/> </controller>
By the way, if you use spice, then adding 3 more special devices to the controllers, it will be possible to forward usb devices from the spice client to the server.Example under the spoilerFor qemu
Add the following options to the virtual machine start command, in addition to the controllers we defined earlier:
-chardev spicevmc,name=usbredir,id=usbredirchardev1 -device usb-redir,chardev=usbredirchardev1,id=usbredirdev1,debug=3 -chardev spicevmc,name=usbredir,id=usbredirchardev2 -device usb-redir,chardev=usbredirchardev2,id=usbredirdev2,debug=3 -chardev spicevmc,name=usbredir,id=usbredirchardev3 -device usb-redir,chardev=usbredirchardev3,id=usbredirdev3,debug=3
For libvirt
In the source virtual machine configuration file in the <devices> node, we add the following options, in addition to the controllers we defined earlier:
<redirdev bus='usb' type='spicevmc'><address type='usb' bus='0' port='3'/></redirdev> <redirdev bus='usb' type='spicevmc'><address type='usb' bus='0' port='4'/></redirdev> <redirdev bus='usb' type='spicevmc'><address type='usb' bus='0' port='5'/></redirdev> <redirdev bus='usb' type='spicevmc'><address type='usb' bus='0' port='6'/></redirdev>
Now everything is ready for forwarding.
Server startup
The usbredirserver package can be found in standard repositories in almost all popular linux distributions.
We insert the USB flash drive into the computer, watch the output of usb devices:
$ lsusb ... Bus 003 Device 011: ID 125f:c82a A-DATA Technology Co., Ltd. ...
We see that the couple vendorid: prodid is equal to 125f: c82a, and the kernel has determined the flash drive 003-001 usbbus-usbaddr, respectively.
Now let's share it on port 4000:
Connect device to virtual machine
Through options when starting VM
The device that needs to be connected to the VM can be specified at startup by adding the following options to the startup command
For qemu
-chardev socket,id=usbredirchardev1,port=4000,host=192.168.1.123 -device usb-redir,chardev=usbredirchardev1,id=usbredirdev1,bus=ehci.0,debug=4
For libvirt
This block is placed in front of the
</ devices> tag , next to the controllers we defined earlier:
<redirdev bus='usb' type='tcp'> <source mode='connect' host='192.168.1.123' service='4000'/> </redirdev>
It can also be executed using the virsh attach-device command.Or through qemu-monitor
We go to the hypervisor and in qemu-monitor of our machine we execute the following commands:
To turn off the flash drive, this command is enough:
device_del usbredirdev1
That's all, after these steps, your VM will see your flash drive and will be able to work with it natively.
If there are many devices and they are all the same
Here an interesting problem appeared, how to forward several identical devices to different VMs?
In this case, it is worth noting that all devices have the same pair of vendorid: prodid, and the pair of usbbus-usbaddr is not at all constant, if you only remove and insert the device, it will immediately change its usbaddr.
I solved it with udev.
By the way, if you don’t quite understand how udev works, the Debian Wiki has a cool
article about udevAnd so let's get started
First we need to find out the serial number of our device, by which we will identify it in udev:
Run udev-monitor:
$ udevadm monitor --environment --udev
And insert our device, then we will immediately see a list of variables of this device that udev has kindly initialized for us:
... UDEV [189056.151508] add /devices/virtual/bdi/8:16 (bdi) ACTION=add DEVPATH=/devices/virtual/bdi/8:16 ID_SERIAL_SHORT=11C130317234004B SEQNUM=4352 SUBSYSTEM=bdi USEC_INITIALIZED=189056149826 ...
Information about the serial number and other attributes can be obtained in another way, but it should be borne in mind that for writing the rules we will use the variables from the command above, and not the attributes from the command below. Otherwise, the remove trigger will not be executed when the device is disconnected.
$ udevadm info -a -n /dev/bus/usb/003/011 | grep '{serial}'
Now create the file /etc/udev/rules.d/99-usb-serial.rules and write the following rules into it:
ACTION=="add", ENV{ID_SERIAL_SHORT}="11C130317234004B", RUN+="/usr/bin/usbredirserver -p 4000 $attr{busnum}-$attr{devnum}" ACTION=="remove", ENV{ID_SERIAL_SHORT}="11C130317234004B", RUN+="/usr/bin/fuser -k 4000/tcp"
Reload udev rules:
$ udevadm control --reload-rules
Done, now when our device is connected, it will automatically scroll to the port we need, and when usbredirserver is disconnected, it will stop working.
By analogy, we add other devices.
That's all. Thank you for your interest :)
UPD: Those who are interested in what came out of this, you can see here.
Sources:
umvirt.ru/node/82opennebula.org/opennebula-for-virtual-desktopsopennet.ru/opennews/art.shtml?num=30773lists.gnu.org/archive/html/qemu-devel/2013-07/msg05244.htmlaskubuntu.com/questions/49910/how-to-distinguish-between-identical-usb-to-serial-adaptersbugzilla.redhat.com/show_bug.cgi?id=805172#c26