在仅有 IPv6 的环境中是否使用远程 WakeOnLan?

在仅有 IPv6 的环境中是否使用远程 WakeOnLan?

环境:

  • FritzBox 7530 v7.29
  • 将端口 7、9、80 和 433 IPv4 和 IPv6 转发到 HomeServer
  • CentOS Stream 8 HomeServer 通过以太网电缆直接连接到 FritzBox
  • 拥有公共 IPv6 地址,与其他 ISP 客户共享 IPv4 地址
  • IPv6 DynDNS 服务正确更新 IPv6 前缀
  • Java脚本发送魔术包
  • 转发功能有效,当 HomeServer 运行时,Apache 和其他服务可访问
  • 需要能够通过 WakeOnLan 远程启动 HomeServer

问题: 为什么以下脚本仅偶尔通过 DynDNS 域或 IPv6 地址启动 HomeServer?只有本地广播 IPv4 每次都有效。

脚本:

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;
    }
    
   
}

答案1

问题:为什么以下脚本仅偶尔通过 DynDNS 域或 IPv6 地址启动 HomeServer?只有本地广播 IPv4 每次都有效。

因为如果源主机(或路由器)不知道要将数据包放在哪个目标 MAC 地址上,它就无法将数据包传送到单播地址。要确定目标第 2 层地址,它必须发出 ARP 或邻居发现查询,您的家庭服务器必须对此作出响应 - 而它在睡眠状态下无法执行此操作。

如果在家庭服务器仍处于通电状态时进行 ARP 查询,则 WoL 将继续工作几分钟,而学习到的 MAC 地址位于您的邻居/ARP 缓存中,但是缓存条目在几分钟不使用后就会过期。

(话虽如此,我听说有些 NIC 具有 ARP 和 ND 卸载功能,可以代表睡眠主机回答 ARP 查询,但我不认为这是一个常见的功能。)

无论您使用 IPv4 端口转发到特定目的地,还是使用 IPv6,情况都是一样。IPv6 地址的最后 64 位不是自动用作 MAC 地址,并且不会进入第 2 层报头——它们仅仅是一个唯一标识符,通常(但并非总是)源自MAC 地址,但不避免对 ARP/NDP 的需求。

同时,WoL 广播 IP 地址之所以有效,是因为它们始终使用固定的目标 MAC – 例如,255.255.255.255始终使用ff:ff:ff:ff:ff:ff,无需为此进行 ARP 查询。(而且 WoL 固件不关心数据包头,它只关注魔术有效载荷。)


IPv6 上没有远程指挥广播,因此如果192.168.178.255距离有多个路由器,则无法轻松地使用 IPv6 复制它。

因此,为了完全远程使用局域网唤醒(无论是通过 IPv4 端口转发还是通过直接 IPv6),您需要向路由器添加静态邻居缓存条目。要么为服务器的真实 IP+MAC 组合添加一个普通的静态条目,或者保留一个单独的 IP 地址并为其分配 ff:ff:ff:ff:ff:ff 作为 MAC 地址,以便发送给它的所有内容都会被广播。

但是,拥有一个始终在线的主机(例如,您能找到的最便宜的 Pi 等效主机)并远程通过 SSH 连接到该主机以运行局域网唤醒工具会更容易。(某些路由器有局域网唤醒小程序,例如/tool wol在 RouterOS 上。)

现在,如果你可以在与家庭服务器相同的子网中运行 Java 程序,则 IPv6 确实支持当地的广播 – 相当于255.255.255.255地址ff02::1,即“所有节点”多播组。请注意,这是链路范围的,因此您必须在其后加上或填写 sin6_scope_id 字段。%zoneidx

String ipStr = "255.255.255.255";

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

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

另一个选择可能是使用模式匹配时唤醒(而不是魔术包)——许多网卡可以用一些额外的模式来编程,这些模式将触发唤醒,例如,如果在 Windows 中启用了唤醒模式,它将对网卡进行编程,以便实际ARP 查询本身会触发系统唤醒。因此您甚至不需要发送魔术包,只需 ping 主机或向其发送任何 UDP 垃圾即可。(问题是任何 ping 您的主机的人都会唤醒它。)

相关内容