Wake-on-LAN worked on wired hosts only, for two independent reasons — one on the host, one on the client. Both are fixed here; Punktfunk still never arms anything itself.
The host asked the wrong tool about Wi-Fi
warn_if_not_armed ran ethtool <iface> for every NIC. For a Wi-Fi card that is the wrong question: the magic-packet trigger lives in nl80211's WoWLAN state, and most wireless drivers print Wake-on: d whether or not it is armed. So an armed Wi-Fi host was logged as NOT armed, and told to run ethtool -s wlan0 wol g — a command its driver rejects.
A NIC with /sys/class/net/<iface>/phy80211 is now recognised as wireless and asked iw phy <phy> wowlan show instead, with WoWLAN-correct guidance in the warning: iw … wowlan enable magic-packet, plus the NetworkManager 802-11-wireless.wake-on-wlan magic that survives a reconnect. Wired NICs keep the ethtool path unchanged.
Fallbacks for when iw can't answer (missing binary, driver without the command, or privilege a user-level host service may lack):
a positive ethtool reading counts — brcmfmac and other SoC Wi-Fi really do report the bit there — but a negative one never does;
failing that, sysfs device/power/wakeup reading disabled, which is world-readable and conclusive in the negative: the kernel will not arm the device to wake the machine at all.
The client's broadcast followed the default route
255.255.255.255 from an unbound socket leaves via the default route, which on a machine running a VPN or mesh network is not the LAN the host sleeps on. A station in WoWLAN sleep stays associated and its AP buffers broadcast frames for it until the next DTIM beacon — but only if the datagram reaches the wireless segment at all.
send_magic_packet now sends from a socket bound to each non-loopback interface's own address, aiming that NIC's directed broadcast and the limited broadcast at it. A failed bind falls back to the routed socket, so no segment is lost. The public API is unchanged, so no client needed a rebuild.
Tests
The iw and ethtool output parsers are split from the commands that produce them, so both unit-test on any platform, and a new end-to-end test asserts a real listener receives the 102 magic-packet bytes.
Docs
wake-on-lan.md gains "Over Wi-Fi" and "Linux (Wi-Fi)" sections and documents all four host log lines; the old "Wired Ethernet is what works, Wi-Fi is unreliable" limit is gone, replaced with the honest caveats (some APs drop broadcast to sleeping stations; some adapters lose power in deeper sleep states). troubleshooting.md now says to search logs for Wake-on- rather than Wake-on-LAN, since the Wi-Fi line reads Wake-on-WLAN.
Verification
On Ubuntu 26.04 with 12 interfaces (good coverage for the per-interface bind): cargo fmt --all --check, cargo clippy -p punktfunk-core -p punktfunk-host --all-targets --locked -- -D warnings, and both wol test sets green.
⚠️Not exercised against real Wi-Fi hardware — no Wi-Fi Linux box was reachable, so the iw parser is tested against synthetic output only. In particular it is still unconfirmed whether iw phy <phy> wowlan show requires root; the sysfs power/wakeup fallback exists precisely for that case, but it wants one run on a real Wi-Fi host.
Not changed
Punktfunk still only reads NIC state — two read-only subprocesses (iw … wowlan show, ethtool <iface> with no -s) and two sysfs reads. It never arms a card; the commands that would are printed for the user to run. The Windows host still runs no arming check at all; its manual steps are documented instead.
Wake-on-LAN worked on wired hosts only, for two independent reasons — one on the host, one on the client. Both are fixed here; Punktfunk still never arms anything itself.
## The host asked the wrong tool about Wi-Fi
`warn_if_not_armed` ran `ethtool <iface>` for every NIC. For a Wi-Fi card that is the wrong question: the magic-packet trigger lives in nl80211's WoWLAN state, and most wireless drivers print `Wake-on: d` whether or not it is armed. So an **armed Wi-Fi host was logged as NOT armed**, and told to run `ethtool -s wlan0 wol g` — a command its driver rejects.
A NIC with `/sys/class/net/<iface>/phy80211` is now recognised as wireless and asked `iw phy <phy> wowlan show` instead, with WoWLAN-correct guidance in the warning: `iw … wowlan enable magic-packet`, plus the NetworkManager `802-11-wireless.wake-on-wlan magic` that survives a reconnect. Wired NICs keep the ethtool path unchanged.
Fallbacks for when `iw` can't answer (missing binary, driver without the command, or privilege a user-level host service may lack):
- a **positive** ethtool reading counts — brcmfmac and other SoC Wi-Fi really do report the bit there — but a negative one never does;
- failing that, sysfs `device/power/wakeup` reading `disabled`, which is world-readable and conclusive in the negative: the kernel will not arm the device to wake the machine at all.
## The client's broadcast followed the default route
`255.255.255.255` from an unbound socket leaves via the default route, which on a machine running a VPN or mesh network is not the LAN the host sleeps on. A station in WoWLAN sleep stays associated and its AP buffers broadcast frames for it until the next DTIM beacon — but only if the datagram reaches the wireless segment at all.
`send_magic_packet` now sends from a socket bound to **each** non-loopback interface's own address, aiming that NIC's directed broadcast and the limited broadcast at it. A failed bind falls back to the routed socket, so no segment is lost. The public API is unchanged, so no client needed a rebuild.
## Tests
The `iw` and `ethtool` output parsers are split from the commands that produce them, so both unit-test on any platform, and a new end-to-end test asserts a real listener receives the 102 magic-packet bytes.
## Docs
`wake-on-lan.md` gains "Over Wi-Fi" and "Linux (Wi-Fi)" sections and documents all four host log lines; the old "Wired Ethernet is what works, Wi-Fi is unreliable" limit is gone, replaced with the honest caveats (some APs drop broadcast to sleeping stations; some adapters lose power in deeper sleep states). `troubleshooting.md` now says to search logs for `Wake-on-` rather than `Wake-on-LAN`, since the Wi-Fi line reads `Wake-on-WLAN`.
## Verification
On Ubuntu 26.04 with 12 interfaces (good coverage for the per-interface bind): `cargo fmt --all --check`, `cargo clippy -p punktfunk-core -p punktfunk-host --all-targets --locked -- -D warnings`, and both wol test sets green.
⚠️ **Not exercised against real Wi-Fi hardware** — no Wi-Fi Linux box was reachable, so the `iw` parser is tested against synthetic output only. In particular it is still unconfirmed whether `iw phy <phy> wowlan show` requires root; the sysfs `power/wakeup` fallback exists precisely for that case, but it wants one run on a real Wi-Fi host.
## Not changed
Punktfunk still only *reads* NIC state — two read-only subprocesses (`iw … wowlan show`, `ethtool <iface>` with no `-s`) and two sysfs reads. It never arms a card; the commands that would are printed for the user to run. The Windows host still runs no arming check at all; its manual steps are documented instead.
The host's arming check asked `ethtool` about every NIC, which is the wrong
question for Wi-Fi: the magic-packet trigger lives in nl80211's WoWLAN state,
and most wireless drivers print `Wake-on: d` whether or not it is armed. An
armed Wi-Fi host was therefore told it was NOT armed, and handed an
`ethtool -s wlan0 wol g` its driver rejects. A NIC with an nl80211 phy
(`/sys/class/net/<i>/phy80211`) is now asked `iw phy <phy> wowlan show`
instead, and the warning carries WoWLAN-correct guidance — `iw ... wowlan
enable magic-packet`, plus the NetworkManager
`802-11-wireless.wake-on-wlan magic` that survives a reconnect. Two fallbacks
for when `iw` can't answer (missing binary, driver without the command, or
privilege the user-level host service lacks): a POSITIVE ethtool reading
counts (brcmfmac & co do report there), a negative one never does, and sysfs
`device/power/wakeup` reading `disabled` is conclusive in the negative.
The client sender now emits from a socket bound to EACH non-loopback
interface's own address rather than leaving the path to the routing table. A
station in WoWLAN sleep stays associated and its AP buffers broadcast frames
for it until the next DTIM beacon — but only if the datagram reaches the
wireless segment at all, and with a VPN or mesh interface holding the default
route `255.255.255.255` never did. A failed bind falls back to the routed
socket, so no segment is lost.
Tests: `iw`/`ethtool` output parsing split from the commands so both are unit-
tested on any platform, and a new end-to-end test asserts a real listener
receives the 102 magic-packet bytes.
Verified on Linux (Ubuntu 26.04, 12 interfaces): `cargo fmt --all --check`,
`cargo clippy -p punktfunk-core -p punktfunk-host --all-targets --locked
-- -D warnings`, and both wol test sets green. NOT yet exercised against real
Wi-Fi hardware — no Wi-Fi Linux box was reachable.
enricobuehler
scheduled this pull request to auto merge when all checks succeed 2026-08-12 22:51:54 +00:00
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.
Wake-on-LAN worked on wired hosts only, for two independent reasons — one on the host, one on the client. Both are fixed here; Punktfunk still never arms anything itself.
The host asked the wrong tool about Wi-Fi
warn_if_not_armedranethtool <iface>for every NIC. For a Wi-Fi card that is the wrong question: the magic-packet trigger lives in nl80211's WoWLAN state, and most wireless drivers printWake-on: dwhether or not it is armed. So an armed Wi-Fi host was logged as NOT armed, and told to runethtool -s wlan0 wol g— a command its driver rejects.A NIC with
/sys/class/net/<iface>/phy80211is now recognised as wireless and askediw phy <phy> wowlan showinstead, with WoWLAN-correct guidance in the warning:iw … wowlan enable magic-packet, plus the NetworkManager802-11-wireless.wake-on-wlan magicthat survives a reconnect. Wired NICs keep the ethtool path unchanged.Fallbacks for when
iwcan't answer (missing binary, driver without the command, or privilege a user-level host service may lack):device/power/wakeupreadingdisabled, which is world-readable and conclusive in the negative: the kernel will not arm the device to wake the machine at all.The client's broadcast followed the default route
255.255.255.255from an unbound socket leaves via the default route, which on a machine running a VPN or mesh network is not the LAN the host sleeps on. A station in WoWLAN sleep stays associated and its AP buffers broadcast frames for it until the next DTIM beacon — but only if the datagram reaches the wireless segment at all.send_magic_packetnow sends from a socket bound to each non-loopback interface's own address, aiming that NIC's directed broadcast and the limited broadcast at it. A failed bind falls back to the routed socket, so no segment is lost. The public API is unchanged, so no client needed a rebuild.Tests
The
iwandethtooloutput parsers are split from the commands that produce them, so both unit-test on any platform, and a new end-to-end test asserts a real listener receives the 102 magic-packet bytes.Docs
wake-on-lan.mdgains "Over Wi-Fi" and "Linux (Wi-Fi)" sections and documents all four host log lines; the old "Wired Ethernet is what works, Wi-Fi is unreliable" limit is gone, replaced with the honest caveats (some APs drop broadcast to sleeping stations; some adapters lose power in deeper sleep states).troubleshooting.mdnow says to search logs forWake-on-rather thanWake-on-LAN, since the Wi-Fi line readsWake-on-WLAN.Verification
On Ubuntu 26.04 with 12 interfaces (good coverage for the per-interface bind):
cargo fmt --all --check,cargo clippy -p punktfunk-core -p punktfunk-host --all-targets --locked -- -D warnings, and both wol test sets green.⚠️ Not exercised against real Wi-Fi hardware — no Wi-Fi Linux box was reachable, so the
iwparser is tested against synthetic output only. In particular it is still unconfirmed whetheriw phy <phy> wowlan showrequires root; the sysfspower/wakeupfallback exists precisely for that case, but it wants one run on a real Wi-Fi host.Not changed
Punktfunk still only reads NIC state — two read-only subprocesses (
iw … wowlan show,ethtool <iface>with no-s) and two sysfs reads. It never arms a card; the commands that would are printed for the user to run. The Windows host still runs no arming check at all; its manual steps are documented instead.