背景介绍
最近想用家里的台式机远程到笔记本上,但是发现并不成功,所以尝试ping了一下,发现只有笔记本ping台式机能成功,但台式机ping笔记本一直失败,本来以为只是防火墙的问题,然而发现事实并没有这么简单。
两台电脑的网络配置如下:
- 笔记本:
- 台式机:
检查防火墙设置
因为之前遇到过类似的问题,我第一个想法是检查防火墙的设置,因为防火墙可能会拦截ICMP报文,导致单侧能ping通的问题。需要将不能被ping通的设备的如下设置打开:
“Windows Defender防火墙” -> “高级设置” -> “入站规则” -> “文件和打印机共享(回显请求-ICMPv4-In)”
比如像我这种情况属于笔记本不能被ping通,就要检查笔记本的这个设置是否打开。
分析网络拓扑
检查之后发现该设置处于打开状态,因此可以排除是防火墙的原因。回头一看两个设备的ip和掩码,可以发现两台电脑并不在同一个网络。因为是租的房子网线是已经接好的,我只知道有个光猫和无线路由器,具体的连接方式没有梳理过(也不想梳理那一堆陈年老网线)。但是从两台电脑执行ipconfig得到的结果看,我猜测网络拓扑应该是这样的结构:
台式机和无线路由器的WAN口连接在光猫上。光猫工作在路由模式,提供整个局域网到公网的出口。笔记本和其他设备连接在无线路由器的WLAN上,相当于一个小的局域网,而从这个网络上发出的流量会在无线路由器上先做一次NAT转换,如果要发往公网,还会再在光猫做一次NAT转换。
为了证明这个猜想,可以用笔记本ping台式机,同时在台式机上用wireshark抓一下ICMP报文,结果如下:
通过网络抓包可以证实上面的猜想,因为ICMP报文的源地址是192.168.1.2,该地址应该是无线路由器的WAN口地址,从笔记本发出的流量在到达无线路由器后接受一次NAT转换,源地址被修改为192.168.1.2,然后到达台式机。
而对于台式机ping笔记本的请求,因为两台电脑不在同一网段,台式机首先查询自身的路由表,如果没有记录则发往默认网关,这里可以使用tracert追踪路由:
从tracert的结果看,台式机的路由表里应该没有对应的配置,发出的ping请求直接到了默认网关,然后默认网关继续往外发送,没有经过无线路由器。
修改路由表配置
在分析网络拓扑之后,对于这种要访问内网的情况一般需要端口映射之类的技术,但我看到网上一些文章说一部分路由器可以查路由表将WAN口收到的数据从LAN口发出去。而我登录无线路由器的管理页面后发现系统路由表有配置192.168.0.0网段的规则,因此想尝试在台式机路由表中增加配置的方式,让台式机的ping包发往无线路由器(这一步操作可能需要用管理员权限):
修改后台式机的路由表如下:
可以看到现在目的地址属于192.168.0.0网段的数据都会发往192.168.1.2,也就是前面抓包分析得到的路由器的WAN口地址。
添加完路由配置后,发现依然还是无法从台式机ping通,但是是路由器丢弃了报文还是其他原因导致的呢?再用tracert和wireshark分析一下:
这里可以从MAC地址判断,从截图下方可以看出ICMP报文发往了80:89:17:d8:f4:79,查询台式机的arp缓存表可以知道这个MAC地址对应的就是192.168.1.2:
可以再ping 192.168.1.2测试一下连通性,发现是可以连通的。由此可以判断ICMP报文到达了无线路由器的WAN口,但被无线路由器直接丢弃了,所以tracert没有信息显示。因为tracert显示的路由信息依赖于网络节点返回的ICMP告警报文。
设置虚拟服务器
后来查了我这款路由器的说明,发现它不支持对内网主机的直接访问,要想访问到内网主机需要先将端口映射出去。可以通过虚拟服务器或者设置DMZ两种方式进行。这里因为端口相对固定,因此选择通过设置虚拟服务器的方式。
以TP-LINK路由器为例,设置虚拟服务器在"转发规则"下,需要进行如下配置:
因为笔记本的IP是通过DHCP获取的,所以可能会发生变化,因此在设置虚拟服务器的IP前需要先在DHCP中设置静态地址保留,避免重新连接后IP发生变化,TP-LINK可以在"DHCP服务器"下设置静态地址保留。
如果是台式机也可以通过本机的"网络和Internet设置"中选择使用指定的IP。但是因为笔记本涉及多个网络间的切换,如果在本机配置可能导致切换到其他网络中有问题
然后将笔记本的地址添加到虚拟服务器中,远程连接默认使用3389端口,为了测试我还顺便添加了一个8080端口。
现在,理论上访问无线路由器WAN口的上述端口就可以访问到笔记本的对应端口。因为此时引入了运输层的概念,而ICMP工作在网络层,所以无法再通过ping进行测试了。这里我用了一个python小程序来验证台式机是否能访问到笔记本的对应端口:
from socket import *
port = 8080
listenSocket = socket(AF_INET, SOCK_STREAM) #使用TCP连接
listenSocket.bind(("", port))
listenSocket.listen(1)
print("正在监听8080端口")
while True:
connectionSocket = listenSocket.accept()[0]
msg = connectionSocket.recv(1024).decode()
print(msg)
connectionSocket.close()
在台式机使用telnet连接192.168.1.2的8080端口,数据会转发到笔记本上:
telnet 192.168.1.2 8080
sen hello hello 听得到吗
笔记本上输出如下:
说明通过虚拟服务器的方式,可以实现对局域网下主机的访问。