На сегодняшний день существет довольно много способов пробросить USB-устройство на другой компьютер или виртуалку по сети.
Из наиболее популярных — железячные такие как AnywhereUSB и чисто програмные продукты, из тех что я попробовал сам: USB Redirector и USB/IP.
Я бы хотел рассказать вам еще об одном интересном способе, который работает непосредственно с эмулятором QEMU.
Он так же является частью проекта spice, официально поддерживаемым RedHat.

UsbRedir, это открытый протокол для проброса usb-устройств по tcp на удаленный виртуальный сервер, разработанный при поддержке RedHat в рамках проекта spice. Но как оказалось им можно вполне успешно пользоваться и без spice. В роли сервера выступает usbredirserver, который шарит usb-устройство на определенный порт, а в качестве клиента сам QEMU, который эмулирует подключение экспортированного usb-устройства в определенный usb-контроллер вашей виртуальной машины. Благодаря такому подходу в качестве гостевой системы может использоваться абсолютно любая ОС, так как она даже не знает, что устройство является проброшенным удаленно, а вся логика ложится на QEMU.

Для начала несколько слов о вышеперчисленных решениях


  • AnywhereUSB — довольно неплохое решение, но дорогое, и имеет неприятние глюки, например бывает если расшаренная флешка отваливается, то переподключить ее обратно можно только физически вынув и вставив ее.
  • USB/IP — OpenSource проект. Вроде как был заброшен. По факту глючит довольно сильно. При разрыве соединения, машина частенько уходит в полнейший freezee, а windows показывает BSOD
  • USB Redirector — Замечательная софтина. Для расшаривания устройств с linux на linux бесплатна, во всех остальных случаях уже стоит денег, не так много как AnywhereUSB, но и не бесплатно как хотелось бы :)

Как видно есть из чего выбрать, но давайте же наконец попробуем еще один способ — UsbRedir?

Настройка виртуальной машины



Для того что бы было куда подключать экспортированные устройства, на виртуальной машине нужно создать необходимые usb-контроллеры:
  • uhci — для USB1.0
  • ehci — для USB2.0
  • xhci — для USB3.0


Для qemu (без libvirt)

Скачайте конфиг ehci-uhci.cfg, положите его в /etc/qemu/
$ curl http://cgit.freedesktop.org/spice/qemu/plain/docs/ich9-ehci-uhci.cfg --create-dirs -o /etc/qemu/ich9-ehci-uhci.cfg

И добавьте его в качестве опции в команду запуска виртуальной машины:
-readconfig /etc/qemu/ich9-ehci-uhci.cfg


Для libvirt

В исходном файле конфигурации виртуальной машины в узле <devices> удаляем все USB контроллеры и добавляем следущий блок:
<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>


Для libvirt (вариант второй)

Так как предыдущий вариант у меня почему-то не заработал, и я решил пойти другим путем, а именно явно указть libvirt конфиг который нужно подсунуть qemu:

Этот блок рамещается перед тегом </domain>:
<qemu:commandline>
<qemu:arg value="-readconfig"/>
<qemu:arg value="/etc/qemu/ich9-ehci-uhci.cfg"/>
</qemu:commandline>
Не забудьте так же скачать ehci-uhci.cfg и сохранить в /etc/qemu/ как в случае с qemu без libvirt

Кстати, если вы используете spice, то добавив к контроллерам еще 4 специальных девайса, станет возможен проброс usb-устройств с клиента spice на сервер.
Пример под спойлером
Для qemu

$ curl http://cgit.freedesktop.org/spice/qemu/plain/docs/ich9-ehci-uhci.cfg --create-dirs -o /etc/qemu/ich9-ehci-uhci.cfg

Добавляем следующие опции в команду запуска виртуальной машины:
-readconfig /etc/qemu/ich9-ehci-uhci.cfg
-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


Для libvirt

В исходном файле конфигурации виртуальной машины в узле <devices> удаляем все USB контроллеры и добавляем следущий блок:
<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>
<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>


Для libvirt (вариант второй)

Этот блок рамещается перед тегом </domain>:
<qemu:commandline>
<qemu:arg value="-readconfig"/>
<qemu:arg value="/etc/qemu/ich9-ehci-uhci.cfg"/>
<qemu:arg value="-chardev"/>
<qemu:arg value="spicevmc,name=usbredir,id=usbredirchardev1"/>
<qemu:arg value="-device"/>
<qemu:arg value="usb-redir,chardev=usbredirchardev1,id=usbredirdev1,bus=ehci.0,debug=3"/>
<qemu:arg value="-chardev"/>
<qemu:arg value="spicevmc,name=usbredir,id=usbredirchardev2"/>
<qemu:arg value="-device"/>
<qemu:arg value="usb-redir,chardev=usbredirchardev2,id=usbredirdev2,bus=ehci.0,debug=3"/>
<qemu:arg value="-chardev"/>
<qemu:arg value="spicevmc,name=usbredir,id=usbredirchardev3"/>
<qemu:arg value="-device"/>
<qemu:arg value="usb-redir,chardev=usbredirchardev3,id=usbredirdev3,bus=ehci.0,debug=3"/>
</qemu:commandline>
Не забудьте так же скачать конфиг ehci-uhci.cfg, и сохранить его в /etc/qemu/ как в случае с qemu без libvirt


Теперь все готово для осуществления проброса.

Запуск сервера


Пакет usbredirserver можно найти в стандартных репозиториях практически во всех популярных дистрибутивах linux.

Вставляем флешку в компьютер, смотрим вывод usb-устройств:
$ lsusb
...
Bus 003 Device 011: ID 125f:c82a A-DATA Technology Co., Ltd. 
...


Видим что пара vendorid:prodid равна 125f:c82a, а ядро определило флешке 003-001 usbbus-usbaddr соотвественно.

Теперь давайте расшарим ее на 4000 порт:

# Используя пару vendorid:prodid
$ usbredirserver -p 4000 125f:c82a
# Используя пару usbbus-usbaddr
$ usbredirserver -p 4000 003-011


Подключение устройства к виртуальной машине



Через опции при запуске ВМ



Устройство которое нужно подключить к ВМ можно указать при запуске, добавив следующие опции в команду запуска

Для qemu

-chardev 
-device usb-redir,chardev=usbredirchardev1,id=usbredirdev1,bus=ehci.0,debug=4


Для libvirt

Этот блок рамещается перед тегом </domain>:
<qemu:commandline>
<qemu:arg value="-chardev"/>
<qemu:arg value="socket,id=usbredirchardev1,port=4000,host=192.168.1.123"/>
<qemu:arg value="-device"/>
<qemu:arg value="usb-redir,chardev=usbredirchardev1,id=usbredirdev1,bus=ehci.0,debug=4"/>
</qemu:commandline>


Или через qemu-monitor


Заходим на гипервизор и в qemu-monitor нашей машины выполняем следующие команды:
# Добавляем наше устройство
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

Что бы отключить флешку достаточно такой команды:
device_del usbredirdev1


Если у вас libvirt, то команды в qemu-monitor можно отправить следующим образом:
$ virsh qemu-monitor-command --hmp my_vm 'chardev-add socket,id=usbredirchardev1,port=4000,host=192.168.1.123'
$ virsh qemu-monitor-command --hmp my_vm 'device_add usb-redir,chardev=usbredirchardev1,id=usbredirdev1,bus=ehci.0,debug=4'
$ virsh qemu-monitor-command --hmp my_vm 'device_del usbredirdev1'

На этом все, после данных шагов ваша ВМ увидит вашу флешку и сможет с ней нативно работать.

Если устройств много и все они одинаковые


Вот тут появилась интересная задачка, как пробросить несколько одинаковых девайсов на разные ВМ?
При этом, стоит отметить, все устройства имеют одинаковую пару vendorid:prodid, а пара usbbus-usbaddr совсем не постоянна, стоит только вынуть и вставить устройство, так оно сразу поменяет свой usbaddr.

Я решил ее при помощи udev.
Кстати если вы не совсем понимаете как работает udev, на Debian Wiki есть классная статья о udev

И так приступим


Для начала нам надо узнать серийник нашего устройства, по которому и будем идентифицировать его в udev:

Запустим udev-монитор:
$ udevadm monitor --environment --udev

И вставим наше устройство, после этого мы сразу увидим список переменных этого устройства которые udev любезно инициализировал для нас:
...
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
...

Информацию о серийнике и других аттрибутах можно получить и другим способом, но стоит учитывать что для написания правил мы будем использовать именно переменные из команды выше, а не аттрибуты из команды ниже. В противном случае не будет отрабатывать триггер remove при отключении устройства.
$ udevadm info -a -n /dev/bus/usb/003/011 | grep '{serial}'


Теперь создадаим файл /etc/udev/rules.d/99-usb-serial.rules и запишем в него следующие правила:
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"


Перезагрузим udev-правила:
$ udevadm control --reload-rules

Готово, теперь при подключении нашего устройства, оно будет автоматически шарится на нужный нам порт, а при отключении usbredirserver будет прекращать свою работу.
По аналогии добавляем и остальные устройства.

На этом все. Спасибо за проявленный интерес :)

UPD: Тем кому интересно, что из этого в итоге получилось, можете посмотреть здесь



Источники:


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

Комментарии (16)


  1. amaranth
    20.08.2015 13:39

    Можно ли пробросить с помощью данной методики Sentinel HL на HyperV под управлением Windows Server 2012?


    1. kvaps
      20.08.2015 13:49

      Неа, работает только в связке с qemu


      1. ink08
        20.08.2015 15:04

        Можно, если на Windows поставить клиент и подключаться по сети к серверу. Но такая связка уже денег стоит


        1. amaranth
          20.08.2015 15:13

          Какой клиент?


          1. ink08
            20.08.2015 15:17

            ответил в личку


        1. kvaps
          20.08.2015 15:15

          Вы точно не путаете UsbRedir с USB Redirector?


          1. ink08
            20.08.2015 15:17

            Да, точно, я USB Redirector имел в виду


  1. VGusev2007
    21.08.2015 11:01

    Как считаете, возможно-ли пробросить таким образом USB2COM? Или подобные хитрые железки?


    1. kvaps
      21.08.2015 12:42

      Думаю что пробросить можно пробросить любое usb-устройство, которое определится и появится в /dev/bus/usb


      1. VGusev2007
        21.08.2015 13:52

        Я пришёл к выводу, что пробросить то можно всё что угодно… А вот реально работать может ДАЛЕКО, ДАЛЕКО не всё… То ли команды USB не все пробрасываются, или чего ещё… Пробовал в своё время: USB/IP — не работало. Пробовал пробрасывать через настройки ESXi — не заработало.

        В любом случае, спасибо за статью. Надо попробовать. Но если у Вас будет возможность пробросить что-то специфическое — будет здорово. Есть же всякие, видеокарты USB, видеотюнеры… — Но я предлагаю, Вам попробовать пробросить USB видеокамеру… — По-моему отличный тест будет!


        1. kvaps
          21.08.2015 19:57

          Я уже пробрасывал встроенную в ноутбук камеру таким образом, вот вам пара скриншотов для подтверждения


          1. VGusev2007
            22.08.2015 00:19

            Выглядит обнадёживающе… Хочу пробросить таким образом USB2COM, чтобы считывать показания с древних электроустановок на работе.


  1. Turilion
    21.08.2015 16:03

    Не совсем понимаю зачем такой огород городить, если устройство можно просто воткнуть в usb порт на хосте и пробросить в виртуалку штатными средствами qemu.


    1. kvaps
      21.08.2015 20:38
      +1

      В данной статье хотелось описать именно удаленный проброс usb, то есть с одного компьютера на гипервизор, а не в пределах одного гипервизора.

      Кстати, как было сказано выше в статье, после подобной настроки qemu, станет возможен проброс usb по протоколу spice простым пользователям через virt-viewer


      1. Turilion
        24.08.2015 01:07

        Ну если так, то и тут есть куча более простых решений, навскидку сразу USB-over-Network. Клиент-серверная фигня, причём позволяет пробросить что угодно куда угодно, главное, что бы были USB.

        PS к тому же кроссплатформенная, и когда последний раз смотрел была ещё и бесплатная)


  1. jkreet
    24.09.2015 00:11

    Нужно пробросить HASP в виртуалку на Proxmox 3.4
    ОС в виртуалке — Win2012 R2

    Делаю всё по вашей инструкции, устройства пробрасываются, но в Windows возникает ошибка USB\DEVICE_DESCRIPTOR_FAILURE

    Пробовал также другие USB-устройства (флешка, сетевушка и пр.) — результат тот же.
    Подскажите, в чём может быть проблема?