What I didn't mention in my recent blog about getting Home Assistant to turn my LG TV on using Wake on LAN (WoL) is that my complicated home network, uh, 'complicates' matters: In my house I have different VLANs for different purposes, and suffice to say the VLAN on which Home Assistant sits is not the same one as my media devices such as the TV.
WoL is a broadcast protocol indended for use between devices connected to the SAME network... 'Same', in this context, means 'the same layer-2 broadcast domain': WoL expects there to be no routers between the source of the 'magic packet' and the intended receipient. But I NEED it to traverse a router.
Anatomy of a WoL Packet
WoL is all about the 'magic packet'. This is a single network packet that commands a listening device to turn on, or perhaps more accurately, commands a device to turn on its higher-level processing. Obviously, a device must be 'on' in order to listen in the first place, but the idea is the listening is done in the network interface which takes very little power. When the network interface receives the 'magic packet' it can command the rest of the device to power up.
The data in the 'magic packet' is therefore simple. It's just a predefined header (called the 'sync stream') followed by 16 repetitions of the MAC address of the device that should respond to the turn-on request. Optionally there's some security stuff bolted on.
These 'Magic packets' are broadcast into the network and received by all devices. MAC addresses are unique so only a single device will match its MAC address to the one in the 'magic packet', and this device will turn itself on.
WoL on the Network
This WoL data needs to be encapsulated in a packet to get across the network. There are two ways this can happen.
Layer 2 WoL
In Layer 2, the WoL data is wrapped up in a packet with Ethertype 0x0842. This packet is sent to the broadcast MAC address (FF:FF:FF:FF:FF:FF). And there's nothing more to it.
This layer 2 packet can't leave the subnet without something actively bridging it. Some routers have helper functions for this, often they're used to redirect things like DHCP requests, but not the Mikrotiks. So I can't use this.
Layer 3 WoL
WoL can also be wrapped in a Layer 3 packet. The layer-2 Ethertype denotes IP, and the IP type denotes 17 (UDP). UDP in turn uses port 9 which is reserved for WoL. Because it's an IP packet, it's sent to the broadcast IP address as well as the broadcast MAC address.
Whether the packet is sent at layer 2 or 3 seems not to matter to the listening network interface. I assume it's only caring about the presence of the sync stream and the 16 repetitions of the MAC, and probably even checks for these without caring about any of the packet headers. Providing the network delivers the 'magic packet' to the interface, whether it's wrapped as layer 2 or 3 is irrelevant.
Although a layer 3 WoL 'magic packet' could be targeted at a unicast address (by specifying the target IP) and hence could traverse a router, this would require ARP on the destination subnet, and the device listening for the 'magic packet' is not expected to be running enough of a network stack to respond to ARP messages. So we still need to broadcast into the destination subnet, even with layer 3 WoL.
Bring on the Mikrotik
I use a Mikrotik router at the core of my network. With a bit of clever fiddling, this lets me catch a WoL 'magic packet' broadcast into one subnet and re-broadcast it out of a different subnet. There are some hoops to jump through to make this work.
Mikrotiks use a Linux kernel underneath, and the Linux kernel will flat out refuse to forward broadcast packets. This is usually desirable; broadcasts should indeed stay within their subnets. But even when we catch a broadcast packet and DNAT it, it's still silently dropped by the kernel. So we need to factor this in to our fiddling.
Source: Home Assistant WoL Packet Sending
My WoL 'magic packets' are originated by Home Assistant using its Wake on Lan integration. Usefully, this sends Layer 3 WoL packets, or none of what follows would be possible.
Because, in my home network, the destination device is in a different subnet, the WoL packet for the TV will never need to be broadcast into the same subnet from which it was originated. I can therefore configure Home Assistant to send its WoL packets to the IP address of the router rather than the subnet's broadcast address. This makes the packets unicast, and means they're not dropped by the Linux kernel in the Mikrotik.
The target MAC is the TV's MAC address, this will be put into the WoL 'magic packet's data field.
- service: wake_on_lan.send_magic_packet
data:
broadcast_port: 9
mac: 64:E4:CA:FE:CA:FE
broadcast_address: 172.16.0.254
Router: Getting the WoL Packet to the Destination Subnet
The WoL 'magic packet' from Home Assistant arrives at the router on its 172.16.0.254 interface address.
I use a destination-NAT (DNAT) rule to change the destination address of the packet from 172.16.0.254 to a spare IP address in the destination subnet, 172.16.1.139. The act of changing the destination subnet means the router will, well, route it into the subnet and send it on its way - in this manner the WoL 'magic packet' has traversed a router!
Here's the Mikrotik config. Note that I use UDP port 9 (WoL) as a further filter:
/ip firewall nat
add action=dst-nat chain=dstnat comment=\
"WoL redirect from MGMT to MAIN subnets" dst-address=172.16.0.254 \
dst-port=9 log=yes log-prefix=WOL-NAT protocol=udp to-addresses=\
172.16.1.139
Router: Turning the WoL Packet into a Broadcast
The WoL 'magic packet' has now arrived in the destination subnet, but in order to be sent out the router needs to find the MAC address associated with the destination IP address, 172.16.1.139. Because the packet is heading to a dummy address, we can do the final piece of trickery here and statically map the dummy address to the broadcast MAC address:
/ip arp
add address=172.16.1.139 comment="For WoL redirection" \
mac-address=FF:FF:FF:FF:FF:FF
So the router now looks up the MAC address for 172.16.1.139, finds it in its static configuration (so doesn't need to ARP for it) and sends it on its way to the destination subnet's broadcast MAC. Once the packet for the broadcast MAC address hits the switch, it's sent to every device. Including the TV. Which turns on.
Summary
It's a bit messy, but this works! We start by sending a WoL 'magic packet' as layer 3 to the router rather than the broadcast address of the source subnet. We use a combination of D-NAT and static ARP to redirect the 'magic packet' from the source subnet to a different, destination, subnet. This trick works by leveraging the fact that the listening devices don't care about the destination IP address in the 'magic packet'.
Comments