📜 ⬆️ ⬇️

Forwarding USB to a virtual network via UsbRedir and QEMU



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.



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:

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 spoiler
For 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:

 #   vendorid:prodid $ usbredirserver -p 4000 125f:c82a #   usbbus-usbaddr $ usbredirserver -p 4000 003-011 


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:
 #    chardev-add socket,id=usbredirchardev1,port=4000,host=192.168.1.123 #    ehci  (USB-2.0) device_add usb-redir,chardev=usbredirchardev1,id=usbredirdev1,bus=ehci.0,debug=4 

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 udev

And 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/82
opennebula.org/opennebula-for-virtual-desktops
opennet.ru/opennews/art.shtml?num=30773
lists.gnu.org/archive/html/qemu-devel/2013-07/msg05244.html
askubuntu.com/questions/49910/how-to-distinguish-between-identical-usb-to-serial-adapters
bugzilla.redhat.com/show_bug.cgi?id=805172#c26

Source: https://habr.com/ru/post/265065/


All Articles