Usage of remote WakeOnLan in a IPv6 only environment?

Setting:

  • FritzBox 7530 v7.29
  • forwarding ports 7, 9, 80 and 433 IPv4 and IPv6 to HomeServer
  • CentOS Stream 8 HomeServer directly connected to FritzBox via Ethernet cable
  • own public IPv6 address, shared IPv4 address with other ISP customers
  • IPv6 DynDNS service correctly updating IPv6 prefix
  • Java script to send magic packets
  • forwarding works, apache and other services reachable when HomeServer is running
  • need to be able to start HomeServer remotely via WakeOnLan

Question: Why does the following script only start the HomeServer sporadically over the DynDNS domain or IPv6 address? Only the local broadcast IPv4 works every time.

Script:

import java.net.*;

public class WakeOnLan {
    
    public static final int PORT = 9;    
    
    public static void main(String[] args) {        
//      works every time 
//      local broadcast works every time      
        String ipStr = "192.168.178.255";
        
//      works only sometimes 
//      local link adress works only sometimes
        String ipStr = "fe80::IPv6 mac address of target";
        
//      works only sometimes        
//      public IPv6 prefix with IPv6 mac address of target 
        String ipStr = "public IPv6 prefix:IPv6 mac address of target";
        
//      mac adress of target
        String macStr = "mac address of target";
        
        try {
            byte[] macBytes = getMacBytes(macStr);
            byte[] bytes = new byte[6 + 16 * macBytes.length];
            for (int i = 0; i < 6; i++) {
                bytes[i] = (byte) 0xff;
            }
            for (int i = 6; i < bytes.length; i += macBytes.length) {
                System.arraycopy(macBytes, 0, bytes, i, macBytes.length);
            }
            
            InetAddress address = InetAddress.getByName(ipStr);
            DatagramPacket packet = new DatagramPacket(bytes, bytes.length, address, PORT);
            DatagramSocket socket = new DatagramSocket();
            socket.send(packet);
            socket.close();
            
            System.out.println("Wake-on-LAN packet sent to: "+address);
        }
        catch (Exception e) {
            System.out.println("Failed to send Wake-on-LAN packet:"+e);
            System.exit(1);
        }
        
    }
    
    private static byte[] getMacBytes(String macStr) throws IllegalArgumentException {
        byte[] bytes = new byte[6];
        String[] hex = macStr.split("(\\:|\\-)");
        if (hex.length != 6) {
            throw new IllegalArgumentException("Invalid MAC address.");
        }
        try {
            for (int i = 0; i < 6; i++) {
                bytes[i] = (byte) Integer.parseInt(hex[i], 16);
            }
        }
        catch (NumberFormatException e) {
            throw new IllegalArgumentException("Invalid hex digit in MAC address.");
        }
        return bytes;
    }
    
   
}

Solution 1:

Question: Why does the following script only start the HomeServer sporadically over the DynDNS domain or IPv6 address? Only the local broadcast IPv4 works every time.

Because the source host (or router) cannot deliver packets to unicast addresses if it doesn't know what destination MAC address to put on them. To determine the destination layer-2 address, it has to issue an ARP or Neighbour Discovery query, which your homeserver has to respond to – which it can't do while asleep.

If the ARP query is made while the homeserver is still powered on, WoL will continue working for a few minutes while the learned MAC address is in your neighbour/ARP cache, however the cache entries expire after just a few minutes of non-use.

(That being said, I've heard that some NICs have ARP & ND offload and can answer ARP queries on behalf of an asleep host, but I don't think it's a common feature.)

The same applies whether you use IPv4 port-forwarding to a specific destination, or whether you use IPv6. The last 64 bits of an IPv6 address are not automatically used as the MAC address, and do not go in the layer-2 header – they're merely a unique identifier that is often (but not always) derived from the MAC address, but do not avoid the need for ARP/NDP.

Meanwhile, WoL to broadcast IP addresses works because they always use a fixed destination MAC – e.g. 255.255.255.255 just always uses ff:ff:ff:ff:ff:ff, there is no need to make an ARP query for that. (And the WoL firmware doesn't care about the packet headers, it only looks at the magic payload.)


There is no IPv6 equivalent of remote directed broadcast, so if 192.168.178.255 is several routers away, you cannot easily replicate that with IPv6.

So in order to use Wake-on-LAN fully remotely (whether through IPv4 port-forwarding or through direct IPv6), you would need to add static neighbour cache entries to your router. Either add a normal static entry for the server's real IP+MAC combination, or reserve a separate IP address and assign it ff:ff:ff:ff:ff:ff as the MAC address, so that everything sent to it gets broadcast.

But it would be easier to have an always-on host, e.g. the cheapest Pi-equivalent you can find, and remotely SSH to it to run a Wake-on-LAN tool. (Some routers have a Wake-on-LAN applet, e.g. /tool wol on RouterOS.)

Now if you can run your Java program on the same subnet as the home server, IPv6 does support local broadcast – the equivalent of 255.255.255.255 is the ff02::1 address, which is the "all-nodes" multicast group. Note that this is link-scoped, so you'll have to suffix it with %zoneidx or fill in the sin6_scope_id field.

String ipStr = "255.255.255.255";

String ipStr = "ff02::1%eth0";

String ipStr = "ff02::1%wlan0";

Another option might be to use wake on pattern match (instead of the magic packet) – many NICs can be programmed with a few additional patterns that'll trigger wake, e.g. if wake-on-pattern is enabled in Windows, it will program the NIC so that the actual ARP query will itself trigger a system wakeup. So you don't even need to send a magic packet, you can just ping the host or send any UDP garbage to it. (The problem is that anyone who pings your host will wake it up.)