Rust usbip protocol library and tooling
Find a file
Davíð Steinn Geirsson ed4fa758f1 fix: guarantee RET_SUBMIT before RET_UNLINK wire ordering
The previous TOCTOU fix prevented sending both responses from two
threads, but a channel-ordering problem remained: the spawn_blocking
task and the UNLINK handler both sent to the same mpsc channel from
different threads, so RET_UNLINK could arrive at the kernel before
RET_SUBMIT.  The kernel (vhci_rx.c) then gives back the URB via the
unlink path, and the subsequent RET_SUBMIT triggers "cannot find a
urb" → VDEV_EVENT_ERROR_TCP → device disconnect.

Fix by making the spawn_blocking task the sole sender of RET_UNLINK
when the URB is still in-flight.  The UNLINK handler now stores its
response header in the InFlightUrb entry instead of sending
immediately.  The spawn_blocking task drains this after sending
RET_SUBMIT (or instead of it, if cancelled), guaranteeing same-thread
FIFO ordering through the channel.  The UNLINK handler only sends
RET_UNLINK directly for the "not found" case, where the entry has
already been removed — meaning RET_SUBMIT is already in the channel.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-25 12:53:49 +00:00
cli fix: UAC1 audio device recognition, idle timeout, and ISO byte order 2026-03-25 11:52:31 +00:00
docs/superpowers feat: add UAC1 loopback test device and fix endpoint attribute dispatch 2026-03-25 01:43:31 +00:00
lib fix: guarantee RET_SUBMIT before RET_UNLINK wire ordering 2026-03-25 12:53:49 +00:00
.gitignore Update .gitignore, remove obsolete plan doc 2026-03-25 00:30:38 +00:00
Cargo.lock feat: concurrent ISO pipelining via nusb update and &self handlers 2026-03-22 15:10:28 +00:00
Cargo.toml feat: add usbip-rs CLI tool with vsock transport 2026-03-22 10:41:42 +00:00
flake.lock feat: add usbip-rs CLI tool with vsock transport 2026-03-22 10:41:42 +00:00
flake.nix Update flake.nix to allow nix flake check torun tests 2026-03-25 00:07:13 +00:00
LICENSE Simplify documentation (fixes #59) 2025-12-25 20:58:48 +08:00
README.md feat: add UAC1 loopback test device and fix endpoint attribute dispatch 2026-03-25 01:43:31 +00:00

usbip-rs

Implements a userspace server-side server for the usbip protocol, and a tool for the client-side to set up the socket and hand it off to the kernel. In usbip terminology, the "server"/"host" is the side that has the physical device being shared, and the "client" is the side the device is being shared to.

This project is based on https://github.com/jiegec/usbip, with significant changes and additions.

Why?

Implementing the server side in userspace instead of the kernel's usbip_host driver reduces the attack surface considerably. The kernel is not exposed directly to potentially untrusted network input. The userspace server talks to the USB device via /dev/bus/usb/..., and can validate the client's traffic before passing it on to the real USB device. It also makes it possible to implement strong sandboxing via seccomp and namespaces for the server side. The client side is of course still all in the kernel, since making the USB devices visible to the client kernel's drivers is the whole point. But for my use case, the server side is trusted and client side is untrusted, so securing the host side is critical.

There are a few other userspace usbip server implementations around, but as far as I could tell, none of them support isochronous mode. Isochronous is needed for e.g. USB audio devices and webcams. This project does, though my testing so far is limited to USB audio devices which work well.

This implementation also allows for reversing the connection flow. In standard usbip, the server side listens for connections, and the client side connects to it to access the device. usbip-rs supports having the client listen and the server initiating the connection, which maps much better to my needs.

usbip-rs also supports using vsock instead of TCP as the transport, making the "ip" in "usbip" not really true. This is useful when sharing a device to a virtual machine on the same hardware.

Building

With Nix

nix build

With Cargo

Requires libusb and libudev (Linux) system dependencies.

cargo build --release

The CLI binary is at target/release/usbip-rs.

Usage

The CLI has four top-level commands: client, host, test_hid, and test_uac. Transport addresses use the format vsock:<port>, vsock:<cid>:<port>, tcp:<port>, tcp:<host>:<port>, or fc-vsock:<path>:<port>.

Pass through a real USB device

On the client machine (needs vhci_hcd kernel module):

usbip-rs client listen tcp:3240

On the host machine (where the USB device is plugged in):

usbip-rs host connect tcp:<client-ip>:3240 1-2

The device argument is a bus ID (e.g. 1-2) or device path (e.g. /dev/bus/usb/001/002).

Test with a simulated HID keyboard

# Client side
usbip-rs client listen vsock:5000

# Host side (simulated keyboard, no real device needed)
usbip-rs test_hid connect vsock:5000

Test with a simulated UAC1 loopback audio device

# Client side
usbip-rs client listen tcp:3240

# Host side (simulated USB audio device, no real device needed)
usbip-rs test_uac connect tcp:3240

The UAC1 loopback device advertises 48kHz 16-bit stereo playback and capture. Audio sent to the playback endpoint is looped back to the capture endpoint.

Project structure

├── cli/          CLI binary (usbip-rs)
├── lib/          Library crate (usbip-rs)
│   ├── src/
│   └── examples/
└── flake.nix     Nix build

License

MIT — see LICENSE.