如何使用 python 代码通过互联网唤醒局域网?

如何使用 python 代码通过互联网唤醒局域网?

我的家庭网络图很简单,如下:

internet       --> Optical modem --> router      --> pc1 ,pc2
111.111.111.111--> 192.168.1.1   -->192.168.31.1 --> 192.168.31.144,192.168.31.173

ISP提供一个静态IP,假设111.111.111.111光猫的IP地址是192.168.1.1,路由器的IP地址是192.168.31.1,PC1的IP地址是192.168.31.144,PC2的IP地址是192.168.31.173,PC1的MAC地址是xx.xx.xx.xx

openwrt在我的路由器上运行,在路由器中添加一些端口转发规则并打开防火墙:

forwarding rule
name        protocl       outer port    inner IP address   inner port 
wakeonwan   UDP           9             192.168.31.144      9
ssh         TCP and UDP   10000         192.168.31.1        22

并在openwrt中绑定ip地址和mac:

ip address               mac               interface
192.168.31.144           xx.xx.xx.xx       ??

从以下选项中选择哪个接口?

在此输入图像描述

远程登录我的路由器:

ssh [email protected] -p 10000

从路由器发出 wol 命令:

/usr/bin/wol -i 192.168.31.255 xx.xx.xx.xx

pc1 可以从路由器唤醒!
关闭 pc1,使用以下 python 代码从 pc2 唤醒它wakeonlan.py

from wakeonlan import send_magic_packet
send_magic_packet('xx.xx.xx.xx')

在pc2中执行该命令python3 wakeonlan.py可以成功唤醒我的pc1。

远程执行命令python3 wakeonwan.py(例如——在我公司的电脑上)无法唤醒我的pc1。

cat wakeonwan.py
mac = "xx.xx.xx.xx"
target_ip = "111.111.111.111"
from wakeonlan import send_magic_packet
send_magic_packet(mac, ip_address=target_ip,port=9)

没有遇到错误,为什么pc1无法唤醒wakeonwan.py
让我们用一个 lib-- 来实现paramiko

pip install paramiko

艾特wakeonwan-paramiko.py

import paramiko
from contextlib import contextmanager
host = '111.111.111.111'
username = 'root'
password = 'password'
ssh = paramiko.SSHClient()
ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy()) 
ssh.connect(host, username=username, password=password,port=10000)
stdin, stdout, stderr = ssh.exec_command("/usr/bin/wol -i 192.168.31.255 xx:xx:xx:xx")
ssh.close()

python3 wakeonwan-paramiko.py可以从外部网络唤醒我的家庭电脑,我觉得编辑wakeonwan.py远程唤醒电脑更简单吗?
如何修复它?

答案1

从本地网络之外发送 LAN 唤醒数据包的棘手部分是让路由器将数据包发送到本地网络,以便完全被动的 PC1 可以接收它。

魔术数据包被发送至路由器的外部 IP 地址,这使其成为单播数据包。路由器中的转发规则指定目标PC自己的IP地址作为内部IP地址。因此,根据转发单播流量的标准程序,路由器将首先发出 ARP 请求以查找目标 PC 的 MAC 地址...但不会得到任何答复,因为目标 PC 已关闭。等待 LAN 唤醒数据包的网络接口处于低功耗仅接收模式:它通常无法应答 ARP 请求。

当 ARP 请求失败时,路由器可能会发回一个 ICMP 错误数据包,指示“主机无法访问”...但是 Pythonwakeonlan 模块似乎没有能力处理此类错误响应,即使假设 ICMP 消息没有收到在返回途中被偏执的防火墙过滤掉。

事实上,将 MAC 地址嵌入到魔术数据包有效负载中并没有什么帮助,因为路由器既不知道也不关心这一点。对于路由器来说,魔术数据包只是另一个 TCP 或 UDP 数据包,将像平常一样进行处理。

您必须配置转发规则,将魔术数据包发送到内部网络的广播地址,以使其成为广播。这样,路由器在将数据包传递到内部网络之前就不需要进行 ARP 查询。因此,如果您的内部网络的网络掩码是255.255.255.0,那么您应该在转发规则中使用192.168.31.255作为内部IP地址。

但...正如 @AB 在评论中提到的,基于 Linux 的路由器通常不喜欢将单播转换为广播,因此这种方法可能行不通。

另一种解决方案是将目标 PC 的静态 ARP 表条目添加到路由器的 ARP 表中,这样就不需要进行 ARP 查询,而只需将数据包转发到内部网络即可。

通过快速源代码检查,唤醒网络Python模块好像使用SOCK_DGRAM,也就是说它使用UDP。因此,您应该能够将转发规则从“TCP 和 UDP”更改为“仅 UDP”。

相关内容