Last Updated: 2023-06-18 06:25:00 Sunday
-- TOC --
远程开机已经存在很多年了。从Intel和IBM在1997年公布标准以来,现在几乎所有的主板都支持网络开机。经过合适的设计,我们可以通过局域网或者广域网上开启另一台电脑,甚至用手机开启电脑!
网络开机叫做Wake-on-LAN
,缩写是WoL
。过程很简单,即通过发送一组特殊格式的网络封包(Magic Packet)给具有某个MAC地址的电脑,让该电脑从睡眠模式甚至是关机模式苏醒,即从ACPI的Sx(S3,S4,S5)模式返回S0运行模式。
前提:关机状态电脑的网口还在工作,灯还在闪,收到magic packet后,触发开机!能够被wake起来的电脑,在关机后,网口的灯一定是亮着的!
Magic Packet示例如下,是个能用的:
再来一个图,说明了上图最后的6个全0的byte的含义:
“Magic Packet”格式非世界标准,但被很多网卡制造商支持.
下面是一段现成的代码,这段代码中组装的magic packet,与上图稍微有点不一样。而且,图片上使用port 7,下面代码使用port 9。
#!/usr/bin/env python
import argparse
import socket
import struct
import logging
import time
LOG = logging.getLogger(__name__)
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, broadcast='255.255.255.255',
port=9, send_times=3):
"""
Wake up computers having any of the given mac addresses.
Wake on lan must be enabled on the host device.
:param macs: a list of machines's macaddress to wake.
:param broadcast: the ip address of the host to send the magic packet
to (default "255.255.255.255")
:param port: the port of the host to send the magic packet to
(default 9)
:param kwargs:
:return:
"""
packets = []
for mac in macs:
packet = create_magic_packet(mac)
packets.append(packet)
try:
sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
except BaseException as e:
LOG.error("Wake up, Create sock failed: {err}".format(err=str(e)))
# print("Wake up, Create sock failed: {err}".format(err=str(e)))
return False
ret = True
try:
sock.setsockopt(socket.SOL_SOCKET, socket.SO_BROADCAST, 1)
for idx,packet in enumerate(packets):
if idx % 10 == 0:
time.sleep(0.1)
for i in range(send_times):
sock.sendto(packet, (broadcast, port))
except BaseException as e:
LOG.error("Wake up failed: {err}".format(err=str(e)))
# print("Wake up failed: {err}".format(err=str(e)))
ret = False
finally:
sock.close()
return ret
BROADCAST_IP = '255.255.255.255'
DEFAULT_PORT = 9
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, broadcast=args.i, port=args.p)
if __name__ == '__main__': # pragma: nocover
main()
这种用udp socket的方法,貌似只能通过发送局域网广播报文来实现wake on lan,如果是单播报文,这种方式没法控制二层报文的mac地址,报文都发不出去!
开启wake on lan功能,需要设置BIOS/UEFI!还可能需要在OS中配置网卡驱动。
遇到一个case,HP的电脑,Intel网卡,在Intel网卡驱动的属性中,找到电源管理,勾选只允许魔包唤醒(这个tab的其它两个选项默认都是有√的)。
下图是用上面的代码发出的magic packet,目的MAC地址是全F:
如果是定向广播地址(网段广播),二层报文的目的mac也是全F!
跨网段呢?
如果跨了公网,要唤醒的电脑的IP是在某个私网内,就要具体问题具体分析了,反正目标是把magic packet发到电脑的网卡上!
发单播唤醒报文是没啥意义的,交换机的2层广播是肯定有的,没有广播,arp和dhcp都不能正常工作。交换机在查找mac转发表的时候,如果找不到,自己就会在所有端口上广播此frame(Mac转发)。
驱动支持
网卡驱动在系统关闭的时候,要保留WOL功能开启,不能彻底把网卡的电断了。如果关机状态下,网卡不闪烁,肯定不能唤醒。
本文链接:https://cs.pynote.net/net/202202211/
-- EOF --
-- MORE --