2025年3月12日 星期三 甲辰(龙)年 月十一 设为首页 加入收藏
rss
您当前的位置:首页 > 计算机 > 文件格式与编码

WOL原理:网络远程唤醒 WOL Magic Packet

时间:03-02来源:作者:点击数:99

WOL原理

WOL(Wake on Lan),即局域网唤醒,从根本上来说是硬件设备提供的一项管理功能,该功能可以当电脑处于关机或休眠状态时,通过给网卡发送特定的数据包从而命令网卡向主板发送开机指令,进而实现上电开机.可见,网络唤醒需要硬件(主要是主板和网卡)的支持.

Magic Packet白皮书介绍:

The basic technical details of Magic Packet Technologyare simple and easy to understand. There is also a sec-ond set of details, which will be implementation spe-cific. In other words, silicon- or gate-levelimplementations of Magic Packet Technology may dif-fer from AMD's approach and be completely interoper-able, as long as the basic feature set is maintained.

https://wenku.baidu.com/view/d5a3282e453610661ed9f487.html

硬件设置

进入BIOS,将“Power Management Setup”中的“Wake Up On LAN”或“Resume by LAN”项设置为“Enable”或“On”,类似于这样,因为主板不一样,BIOS设置位置有可能有差异

比如我的主板设置是在:Setting-Advanced-Wake up event setup - Resume By PCI-E Device 设置为Enable.

有的人说还要设置boot的第一启动项为network,我试过不需要滴!

软件设置

BIOS设置好了,硬件已经满足条件了,然后需要OS级别的软件设置

Windows设置:

设备管理器-网卡设备-属性-高级, 在列表里能找到“Wakeup Capabilities(唤醒功能)”设置值为“MagicPacket”或”Both”.

Linux设置:

先通过 ifconfig 查看要wol的网卡.

这里需要说下,好多网友说没有eth0, 对! 我也没有这个网卡的信息,因为你用的肯定是Ubuntu的衍生版,在Ubuntu 16.04之后以太网卡名称由eth0,变成了enp3s0,还有systemd替换掉了initd来引导系统,参考:Linux网卡命名enp3s0说明

用ethtool命令 打印网卡信息

  • sudo ethtool enp3s0

打印结果:

  • ....
  • ....
  • Supports Wake-on: pumbg
  • Wake-on: g
  • Current message level: 0x00000033 (51)
  • drv probe ifdown ifup

Wake-on 参数:

  • d 表示禁用disable
  • g表示启用great

如果wake-on参数为d就要启用wol, 启用命令:

  • sudo ethtool -s enp3s0 wol g

必须要用sudo 管理员权限否则会提示:  Cannot get current wake-on-lan settings: Operation not permitted

  • -s 参数是修改以太网设备设置

supports wake-on 中的参数

  • p Wake on phy activity
  • u Wake on unicast messages
  • m Wake on multicast messages
  • b Wake on broadcast messages
  • a Wake on AR

获取MAC地址

Windows 直接在网络设备-属性里面, linux使用ifconfig 命令查看

  • enp3s0: flags=4163<UP,BROADCAST,RUNNING,MULTICAST> mtu 1500
  • inet 192.168.1.213 netmask 255.255.255.0 broadcast 192.168.1.255
  • inet6 fe80::a680:65a4:a006:5bbc prefixlen 64 scopeid 0x20<link>
  • ether d8:cb:8a:3f:32:d2 txqueuelen 1000 (以太网)
  • RX packets 890331 bytes 1003253740 (1.0 GB)
  • RX errors 0 dropped 0 overruns 0 frame 0
  • TX packets 555010 bytes 73345907 (73.3 MB)
  • TX errors 0 dropped 0 overruns 0 carrier 0 collisions 0

网络唤醒

到这;已经有了BIOS的支持,有了OS wake-on的支持,又知道了MAC地址,说明被控制(唤醒)机已经Ready了.

局域网唤醒

现在我们需要局域网的电脑或者设备去访问,访问不是随随便便访问的,因为Magic Packet是一种协议规则,必须要用规则发送,关于规则可看下一个段落解析Magic Packet包

我们用GUI工具去访问,常用的WOL工具有

WakeOnLanGui

下载地址:https://www.depicus.com/wake-on-lan/wake-on-lan-gui

WakeMeOnLan

下载地址:http://wakemeonlan.findmysoft.com

参数IP地址,MAC地址,任意一个端口号,IP地址也可以直接填写255.255.255.255

Linux下我没有找到GUI工具,只有wakeonlan命令了

  • sudo apt install wakeonlan

唤醒目标主机:

  • wakeonlan d8:cb:8a:3f:32:d2

输出:

  • Sending magic packet to 255.255.255.255:9 with d8:cb:8a:3f:32:d2

局域网手机唤醒

在同一个局域网下,手机也可以唤醒目标主机

Android APP: Wake On Lan

iphone APP:RemoteBoot

下载地址:自己搜.....

互联网远程唤醒

虽然 WOL (Wake on Lan) 网络唤醒原本的设计就是 LAN 局域网环境下使用的,但其实我们也是可以想办法让其在 WAN 广域网 (即互联网) 下使用——Wake On Wan。这样,在公司唤醒家里的电脑(在家唤醒公司电脑...........)

第一步:设置端口映射(虚拟服务器)

因为广域网不知道我们局域网内对应的主机是哪一个,所以只访问外网的IP不会把Magic Packet包发到局域网指定的电脑上.我们需要路由器端口映射的支持

登陆路由器找到 传输控制-NAT设置-虚拟服务器

路由器必须为顶级路由而非二级即二级以下

如果路由器LAN设置的是DHCP动态分配IP,有可能这次分配的局域网IP与重启之后局域网IP不统一(如果设备少于DHCP分配区间,IP会一直续租), 所以最好做一个静态地址分配或者IP与MAC绑定.

对于外网,如果IP是运营商固定IP可以直接使用公网IP,如果非固定IP(自动获取IP与PPPoE拨号)可以通过花生壳进行DDNS动态域名解析.使用动态域名替代公网IP.

第二步:查看公网IP

通过百度 ip 查看对应公网IP

第三步:网络访问目标

OK! 此时,可以通过手机APP进行互联网的唤醒了,关闭WiFi,打开4G.好吧,不关WiFi也行.

depicus 提供了在线远程唤醒   (现在貌似唤不起来了!!!)

https://www.depicus.com/wake-on-lan/woli

网站这样解释:

Wake on Lan Magic Packets can be sent over the Internet - why not try waking up one of your machines with our free Wake On Wan Service. Want a quick way to use this page ?

Bookmark https://www.depicus.com/wake-on-lan/woli?m=001143BDA600&i=82.110.108.30&s=255.255.255.255&p=4321 will get you straight there without the need to press those pesky send buttons.

换句话说,我们可以不用下载APP,只要保存一个书签,当需要的时候访问书签即可.

https://www.depicus.com/wake-on-lan/woli?m=d8cb8a3f32d2&i=58.37.39.82&s=255.255.255.255&p=4321 

TeamViewer 自带Lan网络唤醒

在TeamViewer-其他-常规-网络设置中就有自带的Lan网络唤醒

填写公共地址IP或端口映射对应的NAT网址,手机端TeamViewer登陆对应的账户,当主机关机时我们就可以通过手机端TeamViewer唤醒电脑

解析Magic Packet包

我们已经知道,Magic Packet是一种协议规则,必须遵从规则发送,Magic Packet的包格式很简单,首先是六个FF,然后是重复十六次待唤醒电脑的MAC

  • FF FF FF FF FF FF d8 cb 8a 3f 32 d2 d8 cb 8a 3f 32 d2 .......

理论上可以在任意网络封包中打包Magic Packet,不过一般选择UDP或IPX

WOL 技术被提出了将近20年,绝大多数的现代网卡都支持在超低功耗下监听特定的报文,如 ARP。如果设备网卡接收到一个与自己 MAC 地址相同的幻数据包,则网卡会向计算机的电源或主板发出信号以唤醒计算机。大部分的幻数据包在数据链路层(OSI模型第2层)上发送,当发送时,使用广播地址广播到给定的网络上,不使用IP地址(OSI模型第3层)。当然这是绝大部分情况,幻数据包也可以使用特定的 IP 地址进行发送。

幻数据包最简单的构成是6字节的255(FF FF FF FF FF FF FF),紧接着为目标计算机的48位MAC地址,重复16次,数据包共计102字节。有时数据包内还会紧接着4-6字节的密码信息。这个帧片段可以包含在任何协议中,最常见的是包含在 UDP 中。

例如 MAC 地址为 11 22 33 44 55 66 的目标计算机,幻数据包的格式为:

幻数据包还有一些基本限制条件:

  • 需要知道目标计算机 MAC 地址
  • 不提供送达确认
  • 可能无法在局域网之外工作
  • 需要硬件进行支持

创建幻数据包

项目地址:https://github.com/ZhangGaoxing/wake-on-lan

该项目为 Xamarin 跨平台项目,包含 Xamarin.Android 与 UWP 。支持自动扫描添加局域网设备。

Python代码:

  • #!/usr/bin/env python
  • # -*- encoding: utf-8 -*-
  • """
  • Small module for use with the wake on lan protocol.
  • """
  • from __future__ import absolute_import
  • from __future__ import unicode_literals
  • import argparse
  • import socket
  • import struct
  • import re
  • BROADCAST_IP = '255.255.255.255'
  • DEFAULT_PORT = 9
  • def create_magic_packet(macaddress):
  • """
  • Create a magic packet.
  • A magic packet is a packet that can be used with the for wake on lan
  • protocol to wake up a computer. The packet is constructed from the
  • mac address given as a parameter.
  • Args:
  • macaddress (str): the mac address that should be parsed into a
  • magic packet.
  • """
  • if len(macaddress) == 12:
  • pass
  • elif len(macaddress) == 17:
  • sep = macaddress[2]
  • macaddress = macaddress.replace(sep, '')
  • else:
  • raise ValueError('Incorrect MAC address format')
  • # Pad the synchronization stream
  • data = b'FFFFFFFFFFFF' + (macaddress * 16).encode()
  • send_data = b''
  • # Split up the hex values in pack
  • for i in range(0, len(data), 2):
  • send_data += struct.pack(b'B', int(data[i: i + 2], 16))
  • return send_data
  • def send_magic_packet(*macs, **kwargs):
  • """
  • Wake up computers having any of the given mac addresses.
  • Wake on lan must be enabled on the host device.
  • Args:
  • macs (str): One or more macaddresses of machines to wake.
  • Keyword Args:
  • ip_address (str): the ip address of the host to send the magic packet
  • to (default "255.255.255.255")
  • port (int): the port of the host to send the magic packet to
  • (default 9)
  • """
  • packets = []
  • ip = kwargs.pop('ip_address', BROADCAST_IP)
  • port = kwargs.pop('port', DEFAULT_PORT)
  • for k in kwargs:
  • raise TypeError('send_magic_packet() got an unexpected keyword '
  • 'argument {!r}'.format(k))
  • for mac in macs:
  • packet = create_magic_packet(mac)
  • packets.append(packet)
  • sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
  • sock.setsockopt(socket.SOL_SOCKET, socket.SO_BROADCAST, 1)
  • # 如果输入的是域名,将域名转换为IP
  • ipv4_regex = re.compile(r'(?:25[0-5]|2[0-4]\d|[0-1]?\d?\d)(?:\.(?:25[0-5]|2[0-4]\d|[0-1]?\d?\d)){3}',re.IGNORECASE)
  • if not ipv4_regex.match(ip):
  • ip=socket.gethostbyname(ip)
  • sock.connect((ip, port))
  • for packet in packets:
  • sock.send(packet)
  • sock.close()
  • print('sent to '+ip)
  • def main(argv=None):
  • """
  • Run wake on lan as a CLI application.
  • """
  • parser = argparse.ArgumentParser(
  • description='Wake one or more computers using the wake on lan'
  • ' protocol.')
  • parser.add_argument(
  • 'macs',
  • metavar='mac address',
  • nargs='+',
  • help='The mac addresses or of the computers you are trying to wake.')
  • parser.add_argument(
  • '-i',
  • metavar='ip',
  • default=BROADCAST_IP,
  • help='The ip address of the host to send the magic packet to.'
  • ' (default {})'.format(BROADCAST_IP))
  • parser.add_argument(
  • '-p',
  • metavar='port',
  • type=int,
  • default=DEFAULT_PORT,
  • help='The port of the host to send the magic packet to (default 9)')
  • args = parser.parse_args(argv)
  • send_magic_packet(*args.macs, ip_address=args.i, port=args.p)
  • if __name__ == '__main__': # pragma: nocover
  • main()

terminal执行:

  • python3 WakeOnWan.py -i test.tpddns.cn -p 9 d8:cb:8a:3f:32:d2
  • python3 WakeOnWan.py -i 192.168.1.105 -p 9 d8:cb:8a:3f:32:d2

C++ 代码:

  • #include <stdio.h>
  • #include <string.h>
  • #include <stdlib.h>
  • #include <unistd.h>
  • #include <sys/types.h>
  • #include <sys/stat.h>
  • #include <sys/socket.h>
  • #include <netinet/in.h>
  • #include <arpa/inet.h>
  • #include <netdb.h>
  • void fill_magic_buf(void *magic_buf, void *mac)
  • {
  • int i;
  • char *ptr;
  • ptr = magic_buf;
  • memset(ptr, 0xFF, 6);
  • ptr += 6;
  • for(i = 0; i < 16; ++i) {
  • memcpy(ptr, mac, 6);
  • ptr += 6;
  • }
  • }
  • void usage(void)
  • {
  • printf("usage...\n");
  • }
  • int main(int argc, char **argv)
  • {
  • int s;
  • int packet_num = 10;
  • char c;
  • unsigned char mac[6] = {0x00, 0x1A, 0x92, 0xE5, 0x1B, 0xA7};
  • char dstip[256] = "192.168.9.180";
  • int port = 9;
  • struct sockaddr_in address;
  • char magic_buf[6 + 6 * 16] = {0};
  • daemon(0,0); /* run in background */
  • while((c = getopt(argc, argv, "d:m:p:")) != -1) {
  • switch(c) {
  • case 'd':
  • strcpy(dstip, optarg);
  • break;
  • case 'm':
  • sscanf(optarg, "%x:%x:%x:%x:%x:%x",
  • (unsigned int*)&mac[0], (unsigned int*)&mac[1],
  • (unsigned int*)&mac[2], (unsigned int*)&mac[3],
  • (unsigned int*)&mac[4], (unsigned int*)&mac[5]);
  • break;
  • case 'p':
  • port = atoi(optarg);
  • break;
  • default:
  • usage();
  • return -1;
  • }
  • }
  • s = socket(AF_INET, SOCK_DGRAM, 0);
  • if (s == -1) {
  • perror("Opening socket");
  • exit(EXIT_FAILURE);
  • }
  • memset(&address, 0, sizeof(struct sockaddr_in));
  • address.sin_family = AF_INET;
  • address.sin_addr.s_addr = inet_addr(dstip);
  • address.sin_port = htons(port);
  • fill_magic_buf(magic_buf, mac);
  • /* ten packets. TODO: use ping to check whether the destination is on or else. */
  • while (packet_num-- > 0) {
  • if (sendto(s, magic_buf, sizeof(magic_buf), 0,
  • (struct sockaddr *)&address, sizeof(address)) < 0) {
  • printf("sendto\n");
  • exit(EXIT_FAILURE);
  • }
  • sleep(1);
  • }
  • exit(EXIT_SUCCESS);
  • }

注意事项

1: 如果电脑非正常关机(比如按Power键来强制关机)还是无法WOL的,只有正常关机后,网卡仍会处于活动状态可以接收网络数据

2:如果是外网IP访问,路由器做端口映射必须要是顶级接入的路由器,因为二级以下NAT只能在是内网映射

3:Linux设置wol的时候,重启后enp3s0的设置又恢复Wake-on: d 状态, 写个脚本让开机执行 (Ubuntu18.04已修复)

在~/.profile 文件中添加

  • .config/wol.sh

wol.sh

  • #!/bin/sh
  • echo "12345678" | sudo -S ethtool -s enp3s0 wol g

-S表示bash脚本免输密码,参考:bash脚本,自动输入sudo的密码

关于 MAC 地址的扫描获取,这里只说一下思路,详细请查阅代码。第一种方式,也是我最开始想到的方式,使用 Ping 来 Ping 整个网段。开了四个线程,1-255大概需要30多秒,稍微有点慢,而且 .NET 的 Ping 类在 Android 上无法限制秒数。第二种方式,百度到的,直接向整个网段发送 UDP 消息,2秒解决战斗。扫描完成后获取 ARP 表就行。

下面给出的是发送幻数据包的方法:

  • publicstaticasyncvoidWake( stringbroadcast, intport, byte[] mac )
  • {
  • using(UdpClient udp = newUdpClient)
  • {
  • udp.EnableBroadcast = true;
  • byte[] packet = newbyte[ 6+ 16* 6];
  • for( inti = 0; i < 6; i++)
  • {
  • packet[i] = 0xFF;
  • }
  • for( inti = 0; i < 16; i++)
  • {
  • for( intj = 0; j < 6; j++)
  • {
  • packet[ 6+ i * 6+ j] = mac[j];
  • }
  • }
  • awaitudp.SendAsync(packet, packet.Length, broadcast, port);
  • }
  • }

 

方便获取更多学习、工作、生活信息请关注本站微信公众号城东书院 微信服务号城东书院 微信订阅号
推荐内容
相关内容
栏目更新
栏目热门