In this post I will talk about the traffic that an USB device generates (when it’s driver is automatically loaded when the device is connected) and the different output when the device driver is not automatically loaded.
For this it would be used an open-source package, wireshark, that allows a person to monitor the sent/received packets.
First of all, to be able to monitor the USB there must be a module loaded “usbmon“. To load the module one should only run the following command in his terminal: modprobe usbmon and now you can open wireshark and start monitoring the traffic. Also wireshark has some filter capabilities that you may like to use, to not have all the transferred packets in your output.
Let’s first see the output when no USB device is connected:
Here it is pretty simple, the host ask for the status of the ports, and then it receives it’s answer. Some of the info are: if the port is enabled, suspended, powered. Also here the host send a series of requests asking for the device file descriptor and the device answers accordingly.
And this is all that is shown.
Now after a new device is connected (memory storage USB), let’s check a part of wireshark’s output:
In the image there is the traffic created by that device (only a chunk of it).
The URB (from the image) stands for USB request block and is like a ‘packet’ with data; used for a communication between the host and an USB endpoint.
Now let’s see what happens when a USB stick is plugged in and the system would not automatically bind the driver when I connect the device. To do this is pretty simple, you only put the “0” value in the /sys/bus/usb/drivers_autoprobe file. Something like this, from the CLI, echo 0 | sudo tee /sys/bus/usb/drivers_autoprobe.
Now let’s see the output from wireshark when the system will not automatically load the drivers for the new connected USB devices.
Part of some output would not be in the image because is the same as where the system would automatically bind the device (when the host requests the status of the ports).
It seems like the system would only make the standard requests (excluding the SETs part, there are only the GETs). Here there is no continuous traffic monitored by the wireshark like in the first case, when the driver would load automatically.
Here someone could also observer that there are 2 GET DESCRIPTOR Request/Response CONFIGURATION. Why this? Because in the first Request, the host does not know how many bytes to allocate for all the descriptors. The host first send a message to the device with the field wLength = 9 (bytes), the space needed by the configuration descriptor:
bLength -- 1 byte
bDescriptorType -- 1 byte
wTotalLength -- 2 bytes
bNumInterfaces -- 1 byte
bConfigurationValue -- 1 byte
iConfiguration -- 1 byte
bmAttributes -- 1 byte
bMaxPower -- 1 byte
Next the device (Response) would send the total size that the host would have to allocate (wTotalLength – in this space the configuration descriptor is included).
Now the host would send another Request where the bLength = wTotalLength and the device would Respond with a message that contains it’s descriptors.
And the Response:
Also, by disabling the auto-loading of the drivers, there should not be any further traffic with the device.
If the device was connected, while in drivers_autoload file was 0 and then (without disconnecting the device and connecting it again) is written 1 in the file then the device would remain unseen and wireshark would not show any further output. This happens because there is no new interrupt signal that the computer can catch so that it can set an active configuration and further some active interfaces.
All the USB devices comes with a set of configurations and can export multiple interfaces (for each specific configuration) – some endpoints.
Eg: A more fancy keyboard with a display can have one configuration and two interfaces: one for the effective keyboard and one for the screen. Each interface is attached to its specific driver.
For binding the device, one should know what Interface and what Configuration is needed. After having that information is needed to first set the wanted configuration in the /sys/bus/usb/device/bus_id/bConfigurationValue (you can find the bus_id by simply having a look at dmesg command output). After setting the wanted configuration, you must now load the driver for a wanted interface and for doing this you could simply write echo bus_id:Configuration_value (the one you selected).Interface_value | sudo tee /sys/bus/usb/drivers_probe.
Until now, I had talked a lot about how a device connects to the system, but an interesting thing is how the kernel knows which driver to load for a specific device. The system knows what driver to handle a specific interface by looking at the modalias (a file that contains hardware information about the device like the vendor id, product id, device class, device subclass etc.).
How is formed:
The system uses an alias model for getting the module for a device and then bind that device to its specific drivers.
You can take a look at the aliases with: less /lib/modules/`uname -r`/modules.alias.
# Aliases extracted from modules themselves.
alias pci:v00008086d00000958sv*sd*bc*sc*i* iosf_mbi
alias pci:v00008086d00002280sv*sd*bc*sc*i* iosf_mbi
alias pci:v00008086d00000F00sv*sd*bc*sc*i* iosf_mbi
alias crypto-aes-asm aes_x86_64
alias aes-asm aes_x86_64
alias crypto-aes aes_x86_64
alias aes aes_x86_64
alias crypto-des-asm des3_ede_x86_64
alias des-asm des3_ede_x86_64
alias crypto-des des3_ede_x86_64
alias des des3_ede_x86_64
alias crypto-des3_ede-asm des3_ede_x86_64
alias des3_ede-asm des3_ede_x86_64
The asterix (*) means has the same meaning as in globbing – any character, any number of times. This is used to match multiple device with that specific module.
Also those set of descriptors: device descriptor, configuration descriptor, interface descriptor and endpoint descriptor can identify an USB device and tell the computer the purpose/purposes of that device.
Depending on a specific value of a field one can tell the what is the role of the connected device: bDeviceClass (from the device descriptor). Also, this field may be 0x00, which means that each interface specifies its class (think at a video camera that can server the role of a video camera, duh…and the role of a microphone) – the bInterfaceClass. For a full list of the classes that a device may have you can take a look here.
This is for today, guys. Thanks for reading and I hope you find some interesting information.
“If programmers deserve to be rewarded for creating innovative programs, by the same token they deserve to be punished if they restrict the use of these programs.” Richard Stallman
Hope you are having a great day,