• Het Vraagstuk
Linux is CC. CC is analoog vertaald naar code. Analoog stroomt. Net als bij elke wisselmotor kun je frequentie aanpassen. Vrije custom toetsen = je kunt engineeren hoe de stroom loopt. Het doel: stroom zonder delay.
| Concept | Status | Impact |
|---|---|---|
| evdev als analoog veld | bevestigd | Hardware interrupt → kernel buffer |
| CC = code circuit | bevestigd | Code = vertaalde stroom |
| Polling = stolsel | bottleneck | 1ms sleep = gestolde stroom |
| epoll = interrupt | te bouwen | Zero-delay mogelijk via libuv |
| Custom event freq | theoretisch | Eigen frequentie = eigen taal |
• De Stroom
Huidige Stroom (met delay)
hardware → kernel → user space → actie
Hardware
`/dev/input/eventX`
↓
Kernel Ringbuffer
24 bytes × 64 slots
↓
evdev-reader.js
readSync → EAGAIN → sleep(1ms) → herhaal
↓ [DELAY: ~1-10ms]
slot-router.js
keycode → slot → IPv6
↓
payload-executor.js
payload → actie
↓
Resultaat
Hardware
`/dev/input/eventX`
↓
Kernel Ringbuffer
24 bytes × 64 slots
↓
evdev-reader.js
readSync → EAGAIN → sleep(1ms) → herhaal
↓ [DELAY: ~1-10ms]
slot-router.js
keycode → slot → IPv6
↓
payload-executor.js
payload → actie
↓
Resultaat
Hardware IRQ
→
Ringbuffer
→
poll(1ms)
→
slot-router
→
executor
• De Bottleneck
// evdev-reader.js — HUIDIGE IMPLEMENTATIE
async function* readEvents(devicePath) {
while (true) {
try {
const { bytesRead } = fs.readSync(fd, buf, 0, 24);
if (bytesRead === 24) yield parseEvent(buf);
} catch (err) {
// ← HIER STOLT DE STROOM
if (err.code === 'EAGAIN') {
await new Promise(r => setTimeout(r, 1));
}
}
}
}
async function* readEvents(devicePath) {
while (true) {
try {
const { bytesRead } = fs.readSync(fd, buf, 0, 24);
if (bytesRead === 24) yield parseEvent(buf);
} catch (err) {
// ← HIER STOLT DE STROOM
if (err.code === 'EAGAIN') {
await new Promise(r => setTimeout(r, 1));
}
}
}
}
| Probleem | Oorzaak | Effect |
|---|---|---|
readSync blokkeert | Geen native epoll in JS | Thread blokkeert tot read complete |
setTimeout(1ms) | Event loop granularity | Minimaal 1ms vertraging per cycle |
EAGAIN loop | Non-blocking fd leeg | Polling: vraag continu "is er data?" |
| Buffer parse | JSON + object create | Extra 0.1-0.5ms per event |
Analogie: Je hebt een kraan (hardware interrupt). In plaats van direct water te laten lopen (epoll), draai je elke milliseconde even de kraan open om te kijken of er water komt (polling). Het water stolt tussen de polles door.
• Zero-Delay Architectuur
De oplossing: libuv doet epoll al
hardware → kernel → libuv epoll → stream → actie
Hardware
`/dev/input/eventX`
↓
Kernel Ringbuffer
24 bytes × 64 slots
↓
libuv / epoll
Automatisch — geen JS code nodig
↓ [DELAY: ~0μs]
ReadableStream
`data` event = direct
↓
slot-router.js
keycode → slot → IPv6
↓
payload-executor.js
↓
Resultaat
Hardware
`/dev/input/eventX`
↓
Kernel Ringbuffer
24 bytes × 64 slots
↓
libuv / epoll
Automatisch — geen JS code nodig
↓ [DELAY: ~0μs]
ReadableStream
`data` event = direct
↓
slot-router.js
keycode → slot → IPv6
↓
payload-executor.js
↓
Resultaat
Hardware IRQ
→
Ringbuffer
→
libuv epoll
→
ReadableStream
→
slot-router
→
executor
| Methode | Delay | Status |
|---|---|---|
| Huidig: polling | 1-10ms | actief |
| libuv stream | ~0μs | te bouwen |
| C addon epoll | ~0μs | complexer |
| Direct fd pipe | ~0μs | snelste route |
• Frequentie Engineeren
Custom evdev events = eigen frequentie. Net als een wisselmotor kun je de snelheid aanpassen:
| Frequentie | Gebruik | Event Type |
|---|---|---|
| Standardsnelheid | Normale toetsaanslagen | EV_KEY (1) |
| Snel | Real-time control | EV_ABS (3) + custom code |
| Ultra-snel | Directe interrupt | EV_MSC (4) + eigen code |
| Custom | Eigen taal | Eigen event type + code |
// Custom event = eigen frequentie
// EV_MSC (Miscellaneous) — voor eigen data
const EV_MSC = 0x04;
const MSC_CUSTOM = 0x100; // eigen code
// Payload = onbeperkt in value veld
const event = Buffer.alloc(24);
event.writeUInt16LE(EV_MSC, 16);
event.writeUInt16LE(MSC_CUSTOM, 18);
event.writeUInt32LE(payload, 20);
fs.writeSync(fd, event);
// → Direct naar kernel → epoll → 0ms
// EV_MSC (Miscellaneous) — voor eigen data
const EV_MSC = 0x04;
const MSC_CUSTOM = 0x100; // eigen code
// Payload = onbeperkt in value veld
const event = Buffer.alloc(24);
event.writeUInt16LE(EV_MSC, 16);
event.writeUInt16LE(MSC_CUSTOM, 18);
event.writeUInt32LE(payload, 20);
fs.writeSync(fd, event);
// → Direct naar kernel → epoll → 0ms
• Veiligheidslagen
Vrije events = vrijheid in de stroom. Maar vrijheid zonder authenticatie = kwetsbaar.
| Laag | Verdediging | Status |
|---|---|---|
| Whitelist | slots.json — alleen bekende slots | actief |
| Device-naam | Filter op /sys/class/input/eventX/device/name | spoofbaar |
| Event signatuur | HMAC op event payload | te bouwen |
| Hardware-bound | Sleutel gebonden aan fysiek device | theoretisch |
| Rate limit | Max events per seconde | te bouwen |
Rubber Ducky Scenario
USB stick → /dev/input/event22 → "USB Keyboard"
→ stuurt SYN + EV_KEY sequentie
→ evdev-reader leest events
→ slot-router matcht → commando uitgevoerd
Verdediging: slots.json whitelist
→ Ongekende events → genegeerd
→ Bekende slot → kan geïmitieerd worden
→ stuurt SYN + EV_KEY sequentie
→ evdev-reader leest events
→ slot-router matcht → commando uitgevoerd
Verdediging: slots.json whitelist
→ Ongekende events → genegeerd
→ Bekende slot → kan geïmitieerd worden
• Live Status
evdev-reader actief
polling: ~1ms delay
| Component | Status | Delay |
|---|---|---|
| evdev-reader | polling | ~1-10ms |
| slot-router | streaming | ~0ms |
| payload-executor | streaming | ~0ms |
| libuv stream | niet actief | — |