🪟 Windows TippsThe Gemini desktop app is now available for Windows(11.09.2026 um 17:06 Uhr)
🪟 Windows TippsAnytype Announces Next-Generation Anytwo Platform(11.09.2026 um 21:27 Uhr)
⚠️ Malware / Trojaner / VirenWindows 11 just dropped the tool ransomware abused, Microsoft says don’t restore WMIC(10.09.2026 um 20:11 Uhr)
⚠️ Malware / Trojaner / VirenVorsicht: Android-Malware verschlüsselt Ihre Handys und nimmt heimlich Fotos auf(11.09.2026 um 09:35 Uhr)
🕵️ SicherheitslückenMicrosoft geht endlich eines der nervigsten Probleme von Windows 11 an(11.09.2026 um 11:58 Uhr)
💾 IT Security ToolsSysinternals Suite(11.09.2026 um 12:00 Uhr)
🪟 Windows TippsThe Gemini desktop app is now available for Windows(11.09.2026 um 17:06 Uhr)
🪟 Windows TippsAnytype Announces Next-Generation Anytwo Platform(11.09.2026 um 21:27 Uhr)
⚠️ Malware / Trojaner / VirenWindows 11 just dropped the tool ransomware abused, Microsoft says don’t restore WMIC(10.09.2026 um 20:11 Uhr)
⚠️ Malware / Trojaner / VirenVorsicht: Android-Malware verschlüsselt Ihre Handys und nimmt heimlich Fotos auf(11.09.2026 um 09:35 Uhr)
🕵️ SicherheitslückenMicrosoft geht endlich eines der nervigsten Probleme von Windows 11 an(11.09.2026 um 11:58 Uhr)
💾 IT Security ToolsSysinternals Suite(11.09.2026 um 12:00 Uhr)

🔧 Programmierung 🕛 vor 1 Monat 7 Min Lesezeit
0

Mouse over WiFi

↗ Quelle (dev.to)
🗣️ Stimme:
📑 Inhaltsübersicht

Today I found that I needed to connect a USB mouse to my laptop — and both

USB-C ports were occupied, one by the monitor, one by power. Turns out it's

not a huge deal on Linux if you have another PC nearby.



The reasonable fix is a €3 USB-C adapter. But the adapter is three days of

shipping away, and the other PC — a little Rockchip ARM board that lives on

my desk — was right there, with free USB-A ports and a network connection to

my laptop. On Linux, "the mouse is plugged into the wrong computer" is not

really a hardware problem. It's a routing problem.





Pick your layer



There are two ways to ship a USB device across a network:



Forward the USB device itself — that's usbip, which is in the mainline

kernel. The remote host sees a real USB device, URBs get shuttled over TCP,

and everything that could talk to the physical device can talk to the remote

one. That's the right tool when something needs the device — firmware

flashers, smartcard readers, license dongles.



Forward the input events — a mouse is not an interesting USB device. By

the time applications see it, it's just a stream of tiny input_event

structs coming out of /dev/input/eventN: relative X, relative Y, button,

sync. The kernel will happily accept the same structs back through

/dev/uinput and synthesize a new input device out of them. So instead of

tunneling USB, you can read events on one machine and replay them on the

other. Less protocol, less latency sensitivity, and it works regardless of

what's on the other end — Wayland, X11, a bare console.



For a mouse, events win. The tool for this is



Two unit files on the server. The socket:




CODE
# /etc/systemd/system/zxc-mouse.socket
[Unit]
Description=Export Logitech mouse over Tailscale (netevent)

[Socket]
ListenStream=8763
BindToDevice=tailscale0
Accept=yes
MaxConnections=1

[Install]
WantedBy=sockets.target






And the per-connection service it spawns:




CODE
# /etc/systemd/system/[email protected]
[Unit]
Description=netevent mouse stream to %i

[Service]
ExecStart=/usr/bin/netevent cat /dev/input/by-id/platform-noserial-if01-event-mouse
StandardInput=socket
StandardOutput=socket
StandardError=journal
DynamicUser=yes
SupplementaryGroups=input






Almost every line here is doing security or lifecycle work:





  • Accept=yes is inetd mode: each TCP connection spawns a fresh
    instance with the connection as stdin/stdout. netevent cat doesn't know
    the network exists — it writes to "stdout" and systemd has already wired
    that to the socket.


  • BindToDevice=tailscale0 scopes the listener with SO_BINDTODEVICE:
    connections arriving via any other interface never reach it. The mouse is
    reachable over the tailnet and nothing else — no LAN exposure, and
    Tailscale brings the encryption and authentication that raw TCP doesn't.


  • MaxConnections=1 — a second client gets refused instead of fighting
    over the grab.


  • DynamicUser=yes + SupplementaryGroups=input is my favorite part.
    The prototype needed sudo for exactly one reason: reading
    /dev/input/event8, which is root:input 0660. But that's not a root
    problem, it's a group problem — so let systemd mint a throwaway user
    that exists only for the lifetime of the connection and holds exactly one
    privilege: the input group. No sudo rules, no long-lived root process,
    no real user to keep in the group forever (permanent input membership
    is a standing keylogger license — every process you run can read every
    keystroke).



The lifecycle falls out of the socket activation for free:



Sequence diagram: laptop connects over the tailnet to port 8763, zxc-pc spawns an instance which grabs the mouse; events stream into the uinput clone; on disconnect the instance exits and the grab is released



Nobody is grabbing anything while no one is attached. The mouse is a shared

resource with connection-scoped ownership, which is exactly the semantics

you'd want and exactly what an always-running daemon wouldn't give you.



On the laptop, the attaching end is one more unit:




CODE
# /etc/systemd/system/zxc-mouse.service
[Unit]
Description=Attach Logitech mouse exported by zxc-pc

[Service]
ExecStart=/bin/sh -c 'socat -u TCP:zxc-pc:8763,keepalive,keepidle=5,keepintvl=5,keepcnt=2 STDOUT | netevent create --duplicates=resume'
Restart=always
RestartSec=2
RestartSteps=8
RestartMaxDelaySec=60
DynamicUser=yes
SupplementaryGroups=input






Same trick on this side — netevent create needs /dev/uinput, which is

also root:input 0660, so the same throwaway-user-plus-one-group pattern

covers it. socat instead of nc because of the keepalive knobs: a mouse

stream is silent when the mouse is idle, so without keepalives a dead peer

looks identical to a boring evening. With keepidle=5,keepintvl=5,keepcnt=2

a vanished server is detected in ~15 seconds, the pipe exits, and

Restart=always walks the backoff (2 s → 60 s) until the server is back.



One deliberate omission: this unit has no [Install] section. It cannot

be enabled at boot, even by accident. systemctl start zxc-mouse is the new

"plug in the mouse"; systemctl stop zxc-mouse is unplugging it — and

handing it back to the machine it's physically attached to.



Did the interface scoping actually work? Two checks:




CODE
$ ssh zxc-pc ss -tln | grep 8763
LISTEN 0 4096 *%tailscale0:8763 *:* # the % is SO_BINDTODEVICE

$ nc -z -w3 192.168.88.247 8763 # via LAN: refused
$ systemctl start zxc-mouse && systemctl is-active zxc-mouse
active # via tailnet: streaming









The landmine



I promised a landmine. First start of the polished new service:




CODE
sh[1510469]: error: error while expecting hello packet: Success






The client connects, expects netevent's protocol hello, and gets EOF —

reported with errno 0, the error message equivalent of a shrug. The server

journal has the real story:




CODE
netevent[61925]: error: failed to grab input device: Device or resource busy






Something already held the exclusive grab. It was the prototype — the SSH

pipe from two hours earlier. I had killed my end of it, but the remote

netevent cat had no idea: it only touches its dead socket when the mouse

moves, and the mouse, grabbed and therefore inert, had not moved. A

process whose only job is to report motion, kept alive by the absence of

motion, holding the grab forever. The new service died against it on every

retry.



One pkill on the server later, the service came up and stayed up.






Was it worth it?



Latency over the LAN (Tailscale negotiates a direct path between machines

on the same network) is imperceptible — mouse events are a few dozen bytes

each, and the event clock is carried in the protocol. The whole thing cost

three unit files and one 130 KB tool, runs with no root process and no sudo

rule on either machine, exposes one port on one virtual interface, and

gives the mouse back the moment I stop the service.



The dongle is still not here. The mouse never found out it's being

forwarded.

Vollständiger Original-Bericht
Ausführliche Details, Code-Beispiele & Hersteller-Stellungnahme auf dev.to.
↗ Original-Artikel auf dev.to lesen
Wie bewertest du diesen Beitrag?
1 Klick Feedback
Teilen mit Netzwerk & Team:

Community-Analysen & Experten-Meinungen 0

Verfasse deine eigene Analyse, teile Workarounds oder diskutiere diesen Vorfall im Blog.
Noch keine Community-Analyse verfasst. Markiere einen Textabschnitt oder klicke oben auf Eigene Analyse verfassen“!
Community Pulse: Relevanz-Einschätzung
1 Klick Experten-Votum
🔴 Akute Relevanz 0%
🟡 In Evaluierung 0%
🟢 Keine Auswirkung 0%
Spannende Innovation 0%
Verwandte Story-Cluster & Quellen (Vektor-KI)
Port 8095 Engine
1 Quelle
The Gemini desktop app is now available for Windows
1 Quelle
Anytype Announces Next-Generation Anytwo Platform
1 Quelle
Windows 11 just dropped the tool ransomware abused, Microsoft says don’t restore WMIC
Ähnliche Beiträge
🔍 Verwandte News

Auch interessante Nachrichten Mouse over WiFi

Thematisch verwandte Begriffe: Mouse, over, WiFi · 6 Treffer

Laden...

Videos werden geladen ...

Laden...

Beiträge werden geladen ...

Laden...

Videos werden geladen ...

Laden...

Beiträge werden geladen ...

Laden...

Videos werden geladen ...

Laden...

Beiträge werden geladen ...

Laden...

Videos werden geladen ...