@rdr
How FOG wakes a host through the fog-agent wake relay
Summary
FOG wakes a host in two ways at the same time. First, the FOG server and the storage nodes send the magic packet on their own subnets. Second, if the relay is on, the server asks up to three awake agents on the host’s subnet to send the packet. The server always picks the target and the senders. An agent only sends.
The problem
A Wake-on-LAN (WoL) magic packet is a broadcast. A broadcast stays on one subnet. Before the relay, only the FOG server and the storage nodes sent the packet. So FOG could not wake a host on a subnet with no FOG server and no storage node.
To send a broadcast to a remote subnet (“directed broadcast”), the routers must forward it. Most networks disable that router feature, because attackers used it for amplification attacks. The wolbroadcast plugin depends on that feature.
The relay removes the gap. Every such subnet has FOG hosts on it. When one of them is awake, it can send the packet for its neighbor.
How FOG knows which hosts share a subnet
Each fog-agent reports its network interfaces to the server on every poll. For each interface, the report gives:
the IPv4 address and the prefix length (for example, 10.1.5.23 and /24)
whether the interface is up and has a link (a NIC with no cable is not up)
whether the interface is wireless
The server does not trust a network address from the agent. It calculates the network address and the broadcast address itself, from the address and the prefix. It stores one row for each address in the hostNetwork table.
Example:
Host
Reported
Network the server calculates
Host 41 (asleep)
10.1.5.23/24
10.1.5.0/24
Host 77 (awake)
10.1.5.80/24
10.1.5.0/24
Host 90 (awake)
10.1.0.12/16
10.1.0.0/16
Hosts 41 and 77 share a subnet: the network address and the prefix are both equal. Host 90 does not share it. Its network address is different, and a /16 and a /24 are never one subnet.
To find senders, the server does one database lookup. It takes the rows of the sleeping host, and it finds other hosts with the same network address and the same prefix. It then keeps only the hosts that meet all of these conditions:
The interface is up and has a link.
The interface has a broadcast address. A /31 or /32 link has none.
The interface is not wireless. An access point does not pass a broadcast to a machine that is asleep, because that machine is no longer connected to the access point.
The host polled in the last 900 seconds, so it is probably awake.
The host is not the sleeping host.
The server sorts the result by the most recent poll and keeps the first three.
Limits of this method
The sleeping host must run fog-agent. The server uses the sleeping host’s own last report to find its subnet. A host with the legacy FOG Client, or with no client, has no rows, so the relay cannot help it. The old path still runs for it.
The sleeping host’s subnet is its last report. A laptop that moved to a different subnet while off is looked for on the old subnet.
Only IPv4. A magic packet uses an IPv4 broadcast.
The flow, step by step
Step: someone asks for a wake. The source is the Wake Up button, a group wake, or a scheduled wol task. Each of these calls Host::wakeOnLAN().
Step: the old path runs first, and it does not change. The server sends a request to every storage node and to itself. Each of them sends the magic packet on its own subnets. If a storage node shares the host’s subnet, this path is enough.
Step: the relay path runs as an addition. It does nothing unless the global setting FOG_AGENT_WAKE_RELAY_ENABLED is 1. The default is 0. The server finds up to three senders, as described above. It writes one agentWake row for each pair of sleeping host and sender. Each row expires after 600 seconds.
Step: the sender agent receives the request on its next poll. The agent does not listen on a network port. The request is part of the normal poll answer:
"wake": {"targets": [{"id": 41, "macs": ["00:11:22:33:44:55"]}]}
The block contains no destination address. It contains only the host id and the MACs of that host. The server leaves out pending MACs that nobody approved.
Step: the agent sends the packet.
The agent parses each MAC and builds it again. It refuses a MAC that is not valid.
It builds the 102-byte magic packet.
It sends the packet to UDP port 9, at 255.255.255.255 and at the broadcast address of each of its own interfaces.
It sends to at most 32 hosts per poll, and at most one packet for each MAC on each interface.
Step: the agent reports the result. The result is sent with a packet count, or failed with a reason. The server accepts the result only if a pending agentWake row names this sender and this sleeping host. Otherwise it returns 404. So an agent cannot report on a host that the server did not ask it to wake.
Why the design has this shape
Choice
Reason
The server picks the target and the senders
A magic packet has no authentication. The control must be on who can ask. Only the server knows which hosts are real FOG hosts.
The request is part of the poll, with no network port on the agent
An open port lets anyone who can reach it ask for a broadcast.
The request has no address field
An agent that accepts a destination can be used to send traffic at any address.
Three senders, not one
Extra packets cost almost nothing. With one sender, the wake fails silently if that sender goes to sleep.
Requests expire after 600 seconds
A laptop that comes back next week must not send an old wake.
Off by default
One customer machine sends traffic for another. The estate owner must choose that.
Wireless interfaces are never senders
The access point does not deliver the broadcast to a sleeping machine.
The cost: delay
A relayed wake goes out on the sender’s next poll. With the default interval, that is up to 5 minutes. Most WoL use in FOG is a scheduled overnight task, so the delay is acceptable. The old path still sends immediately.
What the relay is not
It cannot wake a device that FOG does not manage.
It does not replace the storage-node path or the wolbroadcast plugin.
Agents do not talk to each other. Every decision is the server’s.
It does not wake a host across the internet. A magic packet stays on one subnet.
Sources
Design: fog-agent/docs/design/0011-wake-relay.md
Wire format: fog-agent/docs/design/protocol-v1.md, section “Wake”
Server: packages/web/src/Agent/WakeRelay.php (senders()), packages/web/src/Agent/NetworkFacts.php, packages/web/src/Items/Host.php (wakeOnLAN())
Agent: internal/network/network.go, internal/provider/wake/