Scenario

USB Device Passthrough

Use a USB device plugged into another machine.

Plug a USB device into one machine and use it on another, anywhere on your mesh — a serial adapter, a smart-card reader, a security token, a license dongle. The device appears in the remote system as if it were plugged in locally.

This needs no dedicated MeshHold feature. USB-over-network is a solved protocol — USB/IP — and it speaks plain TCP. MeshHold already moves arbitrary TCP over an encrypted, NAT-traversing, multi-hop tunnel, so the whole thing is one ordinary port-forward plus two off-the-shelf USB/IP endpoints.

Concepts

  • Server — the machine the device is physically plugged into. It runs usbipd and "exports" the device. Built into the Linux kernel.
  • Client — the machine where the device appears. It runs the usbip client and "attaches" the exported device.
  • The tunnel — a single forward carries USB/IP's TCP port 3240 between the two. Modern USB/IP multiplexes device listing, the import handshake, and all device traffic over that one connection, so one forward is enough — even for several devices at once.
  LINUX  (device here)                          WINDOWS  (device appears here)
  usbipd ──▶ 127.0.0.1:3240                      usbip ──▶ 127.0.0.1:3240
        ▲                                              ▲
        └────────────  MeshHold tunnel  ───────────────┘
            (a forward binds local 3240 → peer's 3240)

This guide uses the lowest-friction combination: device on Linux (the export side is in the kernel) appearing on Windows (via the actively-maintained usbip-win2 client). The reverse — exporting from Windows — is possible with usbipd-win but out of scope here.

The device side needs a physical USB port — a real machine (laptop, mini-PC, Raspberry Pi), not a cloud VPS. A VPS can still serve as a relay hop in the middle of the tunnel.

1. Linux — export the device

Install the USB/IP tools and load the host-side kernel module.

On Debian:

sudo apt-get install -y usbip

On Ubuntu there is no standalone usbip package — the tools ship inside linux-tools, and the kernel modules often sit in linux-modules-extra (absent on minimal/cloud images):

sudo apt-get update
sudo apt-get install -y \
  linux-tools-generic \
  linux-tools-$(uname -r) \
  linux-modules-extra-$(uname -r)
# The binaries may not be on PATH — locate them with:
#   ls /usr/lib/linux-tools/*/usbip

Load the export module and start the daemon (it listens on TCP 3240):

sudo modprobe usbip-core
sudo modprobe usbip-host
sudo usbipd -D

The usbipd from linux-tools has no bind-address flag — it listens on all interfaces. MeshHold only ever dials 127.0.0.1:3240, but if the box has a public IP, firewall the port to loopback:

bash sudo iptables -A INPUT -p tcp --dport 3240 -i lo -j ACCEPT sudo iptables -A INPUT -p tcp --dport 3240 -j DROP

List local devices, pick one, and bind it (this hands it from its normal driver to USB/IP):

usbip list -l
#  - busid 1-4.1 (1a86:5722)
#       QinHeng Electronics : USB Serial

sudo usbip bind --busid=1-4.1
usbip list -r 127.0.0.1   # confirm it is now "exportable"

Note the busid (1-4.1) — the client needs it. Bind several devices if you want; each exports one more over the same port.

unable to bind device almost always means the usbip-host module isn't loaded (lsmod | grep usbip is empty). Install linux-modules-extra-$(uname -r) and modprobe usbip-host again. A device held by another daemon (e.g. a smart-card reader claimed by pcscd) may need that service stopped first.

2. MeshHold — create the tunnel

On the Windows node, create a single forward binding local 127.0.0.1:3240 and tunneling it to the Linux node's 127.0.0.1:3240:

meshhold forwards add `
  --name usbip --forward --proto tcp `
  --peer-node <LINUX_NODE_ID> --peer-key <PEER_KEY_ID> `
  --listen 127.0.0.1:3240 --remote 127.0.0.1:3240

<LINUX_NODE_ID> is the Linux node's peer ID; <PEER_KEY_ID> is a management key with the tunnel capability (not password-gated) authorising the circuit — both available from the Network page, or issued with meshhold mgmt-keys add --caps=tunnel. Equivalent config.yaml:

port_forwards:
  - name: usbip
    direction: forward
    proto: tcp
    peer_node_id: "<LINUX_NODE_ID>"
    peer_key_id: "<PEER_KEY_ID>"
    listen_addr: "127.0.0.1:3240"
    remote_addr: "127.0.0.1:3240"

Confirm it's up — you want PHASE active:

meshhold forwards list

That one forward carries every exported device and inherits relay, multi-hop routing, and the masquerade transports from the tunnel for free. See The Private Mesh VPN for the full forwarding reference (CLI, REST, web UI).

3. Windows — attach the device

Download the client from the usbip-win2 releases page.

Pick a release whose notes say "attestation signed drivers" (v0.9.7.2 and later). Those install with driver-signature enforcement left on — no test-signing mode, no reboot dance. Install the .msi (it installs the virtual USB host-controller driver) and reboot if prompted.

Unsigned builds only: if you use an older build, first enable test signing in an admin prompt and reboot (bcdedit /set testsigning on). The desktop then shows a "Test Mode" watermark. An attestation-signed release avoids this entirely and is strongly preferred.

Because the forward makes the remote daemon reachable at localhost, point the client at 127.0.0.1 — the tunnel does the rest:

usbip list -r 127.0.0.1                 # should show the same busids
usbip attach -r 127.0.0.1 -b 1-4.1      # attach by busid
usbip port                              # list active attachments

Within a second or two the device appears in Device Manager and works like a locally-plugged device. Repeat usbip attach per busid to add more; usbip detach -p <port> removes one.

What to expect

  • It works like local hardware. HID devices, serial adapters, smart-card readers, and dongles all show up as real USB devices.
  • USB/IP is round-trip sensitive. Keyboards, serial, and tokens stay responsive even over a WAN. Bandwidth-heavy or isochronous devices (webcams, audio, fast storage) are far more sensitive to tunnel latency — and devices sharing one tunnel share its round-trip budget.
  • Security travels with the tunnel. The USB/IP stream is end-to-end encrypted by the libp2p circuit and never exposed on the public internet; keep usbipd bound to loopback behind the forward.

Teardown

Symmetric to setup:

# Windows
usbip detach -p 00              # port from `usbip port`
meshhold forwards stop usbip
# Linux
sudo usbip unbind --busid=1-4.1 # return the device to its normal driver
sudo killall usbipd

Download: github.com/vadimgrn/usbip-win2/releases