Исследование возможности применения raw-gadget как источника USB-трафика для тестирования usbredir

Материал из ALT Linux Wiki

Usbredir — библиотека из состава протокола SPICE, реализующая проброс USB-трафика по сети.

raw-gadget — это модуль ядра Linux, реализующий низкоуровневый интерфейс для подсистемы USB-гаджетов Linux. Он используется для эмуляции USB-устройств, как физических, так и виртуальных.

Диаграмма потоков данных, по которой проводится исследование, изображена на картинке.

Block-schema.png


Цель исследования — пробросить эмулируемое usb-устройство, изменять (мутировать) его данные и сканировать полученный трафик tcp пакетов по протоколу usbredir.

Этапы исследования:

1) Сборка ядра с необходимыми модулями
Для эмуляции USB-устройства со стороны kernel-space необходимо собрать ядро, добавив в config следующие переменные:

CONFIG_USB_GADGET=m
CONFIG_USB_RAW_GADGET=m
CONFIG_USB_DUMMY_HCD=m

2) Проверка модулей (kernel-space)
После установки и загрузки собранного ядра загружаем модули:

su -
modprobe raw_gadget
modprobe dummy_hcd

после загрузки модуля raw_gadget появится новое устройство:

$ ll /dev/raw-gadget 
crw------- 1 root root 10, 123 окт 11 19:34 /dev/raw-gadget

после загрузки модуля dummy_hcd появится новый виртуальный хост контроллер:

$ cat /sys/class/udc/dummy_udc.0/uevent 
USB_UDC_NAME=dummy_udc

dmesg:

[ 1487.422857] dummy_hcd dummy_hcd.0: remove, state 4
[ 1487.422870] usb usb1: USB disconnect, device number 1
[ 1487.423287] dummy_hcd dummy_hcd.0: stopped
[ 1487.423293] dummy_hcd dummy_hcd.0: USB bus 1 deregistered
[ 1516.342898] dummy_hcd dummy_hcd.0: USB Host+Gadget Emulator, driver 02 May 2005
[ 1516.342905] dummy_hcd dummy_hcd.0: Dummy host controller
[ 1516.342908] dummy_hcd dummy_hcd.0: new USB bus registered, assigned bus number 1
[ 1516.343310] hub 1-0:1.0: USB hub found
[ 1516.343320] hub 1-0:1.0: 1 port detected

Для автозагрузки модулей при каждом включении машины:

su -
echo raw_gadget >> /etc/modules
echo dummy_hcd >> /etc/modules

3) Проверка модулей (user-space)
Теперь из пространства пользователя нужно запустить приложение, которое будет обращаться к модулю raw_gadget и передавать ему данные об эмулируемом USB-устройстве.
В репозитории исходного кода raw_gadget есть пару примеров ( гаджетов ): keyboard.c (эмулирует USB-клавиатуру) и printer.c (эмулирует этап перечисления USB-принтера).
Оба примера собираются командой make; пример запуска бинарника:

$ sudo ./keyboard

или

$ sudo ./printer

После инициализации приложение-эмулятор клавиатуры в цикле каждую секунду печатает символ 'x', при этом в списке USB устройств можно увидеть новое:

$ lsusb
...
Bus 001 Device 002: ID 046d:c312 Logitech, Inc. DeLuxe 250 Keyboard
…

4) Проброс в виртуальную машину
Теперь нужно пробросить это устройство в qemu используя библиотеку libusbredir, для этого создаём виртуальную машину:
- выделяем объем:

$ qemu-img create -f qcow2 altlinux.qcow2 15G

- запускаем установщик:

$ qemu-system-x86_64 -hda altlinux.qcow2 -m 4G -enable-kvm -cpu host,nx -monitor stdio -netdev user,id=net0 -device e1000,netdev=net0 -cdrom image/regular-kde5-latest-x86_64.iso

- пишем скрипт запуска run.sh:

#!/bin/bash

qemu-system-x86_64 \
-hda altlinux.qcow2 \
-m 4G \
-enable-kvm \
-cpu host,nx \
-monitor stdio \
-nic user,hostfwd=tcp::8888-:22 \
-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 \
-chardev socket,id=usbredirchardev1,port=4000,host=127.0.0.1 \
-device usb-redir,chardev=usbredirchardev1,id=usbredirdev1,bus=ehci.0,debug=4

Далее, для автоматического проброса эмулируемой usb-клавиатуры пишем правило udev ( /etc/udev/rules.d/99-raw-gadget.rules ):

ACTION=="add", ATTR{idVendor}=="046d", ATTR{idProduct}=="c312", RUN+="/usr/bin/usbredirect --device 046d:c312 --as 127.0.0.1:4000 --verbose 5 2>&1 >> /home/user/raw_gadget_redir.txt" 
ACTION=="remove", ATTR{idVendor}=="046d", ATTR{idProduct}=="c312", RUN+="/usr/bin/fuser -k 4000/tcp"

Однако, данный подход как и ручной проброс не сработал:
- запуск эмуляции:

$ sudo ./keyboard
...
ioctl(USB_RAW_IOCTL_EP_WRITE): Cannot send after transport endpoint shutdown

- проброс:

$ sudo usbredirect --device 046d:c312 --as 127.0.0.1:4000 --verbose 5
(usbredirect:12580): usbredirect-ERROR **: 20:22:46.937: usbredirhost: error resetting device: LIBUSB_ERROR_NOT_FOUND
Ловушка трассировки/останова

Ошибкой завершается и попытка проброса «принтера»:

$ sudo ./printer
...
ioctl(USB_RAW_IOCTL_EP_READ): Cannot send after transport endpoint shutdown
ioctl(USB_RAW_IOCTL_EP_WRITE): Cannot send after transport endpoint shutdown

5) Статус решения проблемы
Ошибка Cannot send after transport endpoint shutdown впервые обсуждается в issue проекта usb-proxy, основанном на использовании модуля raw_gadget, после обсуждения которой разработчик добавил информацию с предложением пересобрать модуль raw_gadget из ветки dev.
Пересборкой модуля проблему решить не удалось, зарегистрирована ошибка.