视频教程

youtube播放地址:https://youtu.be/T8Fx9b8z5w0
参考文档:https://tailscale.com/blog/how-nat-traversal-works

GIA高速VPS推荐: https://d.m123.org
家宽住宅VPS推荐: https://v.m123.org
自用专线机场推荐: https://b.m123.org

相关工具

在线检测NAT类型:https://natchecker.com
tailscale:https://tailscale.com/
easytier:https://easytier.cn/

视频文稿(忽略)

上期讲的stun打洞穿透让你在运营商没有提供公网ip的情况下,将内网的服务开放在互联网上,让你在异地访问家里的设备,看上去就好像有公网ip一样,但这个方案只适用于家里是fullcone的nat1环境,很多朋友反馈自家是nat2、nat3、甚至是nat4,还能异地访问吗?答案是可以的,通过tailscalezerotierNetBirdeasytier等p2p vpn方案,依然可以在运营商没有提供公网ip,实现nat2甚至是nat4的p2p直连异地访问家庭内网,即使你没有内网访问需求,了解本期相关知识对游戏联机和bt下载也会有所帮助

我的演示环境是一台电脑和一台手机在不同的网络中,目标是让他俩能在异地互联,开始之前你应该先关闭所有的代理工具,以确保检测结果正确,首先访问这个网址进行nat类型检测,这是我随便找的一个网站,如果你那无法访问的话,可以自己找找其他nat类型检测的网站或者工具,另外由于浏览器权限问题,通过网页检测nat类型可能不是很准确,点击开始检测,稍等片刻可以显示你当前的网络nat类型,手机同样进行nat检测,现在两端的nat类型都是nat1,fullcone全锥,这意味着两端都有非常宽松的nat环境,我们需要先花点篇幅讲讲各种nat类型

一个常见的家庭网络环境是你家的电脑或手机连接了路由器,路由器通过pppoe拨号获取运营商分配的假设是公网ip5.5.5.5

正常情况下,大部分家庭路由器默认都开启了防火墙,并且禁止所有外部网络主动访问家里的内网,以确保内网安全,也就是现在这个6.6.6.6的公网设备无法访问这台ip为192.168.1.2的电脑,当我们在浏览器通过http访问baidu.com的时候,电脑获取baidu.com的ip地址为6.6.6.6,然后构建这么一个数据包

从电脑192.168.1.2的随机空闲端口9527,发给百度6.6.6.6的80端口,数据包来到路由器,路由器的防火墙并没有禁止内网发到互联网的数据,所以数据包将会放行通过,并且防火墙会记录这个访问状态,百度收到数据后会返回我们需要的内容,数据来到路由器,根据之前讲的,路由器的防火墙禁止任何外部ip访问内网设备,按理来说防火墙会拒绝这个数据包,但防火墙记录了是我们先给百度发起的访问请求,如果禁止百度的数据通向内网的话就没法上网了,所以防火墙会放行这个记录了状态的外部数据包进入内网,这样电脑就能成功获取到百度的网页内容,我们可以把防火墙记录的状态想象成临时在上面打了个洞,电脑的数据从这个洞里发给百度,百度的数据穿过这个洞将数据包交给电脑

接着假设6666的90端口也想穿过这个洞往内网里发送数据,此时针对不同的防火墙过滤策略会有不同的情况,首先第一种情况,防火墙开这个洞的原因是为了让百度80端口发过来的数据能正常返回给电脑的9527端口,那应该只允许百度的80端口往洞里发送数据,于是拒绝了这个数据包,这种防火墙过滤行为叫做Address and Port-Dependent Filtering,直译过来就是地址和端口相关过滤,意思是你发过来的数据包要想经过这个洞,发送方的ip地址和端口必须和状态中的相关联,其他地方发过来的全都会禁止

如果防火墙过滤策略宽松一点,只看是不是6666发过来的数据包,至于使用6666哪个端口并不关心,那这个过滤行为叫做Address-Dependent Filtering,直译为地址相关过滤,也就是发送方的ip地址必须相关联,但端口可以不关联

再宽松一点,只要防火墙这个洞还开着,就允许任意ip的任意端口发过来的数据通过这个洞,那这个过滤行为叫做Endpoint-Independent Filtering,直译为端点无关过滤,端点可以理解为ip+端口,意思是这个洞虽然是电脑访问6666的80端口开的,但防火墙不关联6666+80这个端点,只要这个洞还在,任何人发过来的数据都能通过这个洞,记住这三种过滤行为,是理解各种nat类型的关键

刚才的讲解有个错误,电脑的ip是路由器分配的局域网ip192.168.1.2,这个ip只能在局域网内使用,无法在公网进行路由,所以路由器还有个职责就是进行网络地址转换,简称nat,加入nat步骤后我们重新访问百度,数据来到路由器先进行nat处理,将数据包的192.168.1.2替换成运营商分配给路由器的公网ip,将9527端口替换为路由器随机的空闲端口假设为4134,并且记录这个映射关系,接着防火墙记录这个状态,然后将数据发给百度,百度将我们需要的数据返回,防火墙根据状态记录接受这个数据包,接着路由器进行nat处理,将数据包里的目标ip和端口根据nat映射表修改回原来的ip和端口,并将其转发给电脑,和防火墙的三种过滤行为一样,nat也有对应的三种映射行为,此时我继续通过电脑访问知乎,从电脑的ip192.168.1.2,复用之前的端口9527,发给知乎7.7.7.7的80端口,数据包来到路由器,假设刚才的nat映射还没有被回收,路由器发现映射表中有一条电脑9527端口的映射记录,映射在公网ip的4134端口,而当前的数据包的源端口还是9527,于是直接复用这条映射规则,将数据包的192.168.1.2替换成运营商分配给路由器的公网ip,将9527端口替换为路由器的4134端口,这种不管访问目标端点是什么的nat映射行为叫做Endpoint-Independent Mapping,直译为端点无关映射,意思是这条nat映射虽然是发给百度6.6.6.6的80端口创建的,但我不关联这个端点,只要是从电脑192.168.1.2的9527端口发过来的,不管发给谁,都复用这条映射规则

相对应的,如果路由器发现虽然数据包是从电脑的9527端口发过来的,但访问目标并不是映射规则中对应的端点,于是新建一条映射规则,将数据包的192.168.1.2替换成运营商分配给路由器的公网ip,将9527端口替换为路由器随机的空闲端口假设为1234,并在映射表中记录这个映射关系,这种nat映射行为叫做Address and Port-Dependent Mapping,直译为地址和端口相关映射,意思是只要访问的目标ip或端口不同,就使用新的空闲端口开一条映射规则,除此之外还有一种映射规则是Address-Dependent Mapping,直译为地址相关映射,也就是只关联ip地址,端口不同并不会创建新的映射,这种在现实中很少见就不多费口舌了

可以发现nat的映射规则主要针对内部发往外部的数据包,防火墙的过滤规则主要针对外部发往内部的数据包

nat的出站映射行为,和入站过滤行为,共同决定了你家的网络属于哪种NAT类型,另外上面提到的防火墙其实是nat的过滤行为,把它类比成防火墙是为了便于大家理解

参考这张表格,rfc3489定义了我们目前常见的4种nat类型,rfc5780改进 NAT 行为的检测方法,当映射行为是端点无关,且过滤行为是端点无关,则为nat1的全锥fullcone,当映射行为是端点无关,且过滤行为是ip地址相关,则为nat2的Restricted Cone ip地址限制锥,当映射行为是端点无关,且过滤行为是ip地址和端口相关,则为nat3的port Restricted Cone 端口限制锥,当映射行为是ip地址和端口相关,且过滤行为是ip地址和端口相关,则为nat4的Symmetric 对称nat,其他几种行为组合比较少见,在rfc3489中没有相关定义,另外对于端点无关的nat映射行为可统称为easy nat,其他均为hard nat,也就是只有对称nat属于hard nat

有了以上前置知识,接下来演示各种nat类型的p2p打洞情况就更容易理解,首先演示fullcone对fullcone,刚才我们在网页上测试了电脑和手机同属于nat1的fullcone,也就是端点无关映射和端点无关过滤,这是最宽松、最容易实现P2P打洞的NAT类型

演示用到的是目前非常流行的p2p vpn组网工具tailscale,以下简称ts,ts可以在双方都没有公网ip的情况下实现异地组网,其工作原理就是wireguard vpn+云端下发配置+p2p打洞,网上有很多组网教程大家可以自行搜索了解,本期的重点是讲解不同nat类型之间的直连打洞情况,不会过多涉及组网内容,如果你想了解wireguard vpn可以回看这期视频,点击这里注册账号,建议使用不需要翻墙就能登录的邮箱,比如微软或者苹果,这些问卷调查随便填,接着安装对应操作系统的ts客户端,安装之后打开,会在任务栏显示ts图标,进入设置页面,登录刚才注册的账号,点击connect连接,不出意外的话就能在这里看到当前这台mac电脑就已经是在线状态了,接着连接安卓端,同样登录刚才注册的账号,点击连接,此时另一台设备也上线了,刷新当前网页,此时这两台不在同一个物理局域网的设备就已经处在同一个虚拟局域网了,即使在双方都没有公网ip的情况下,这两个100开头的ip就是他们在虚拟局域网里的ip,并不是公网ip,你可以把这两个ip想象成192.168.1.2和192.168.1.3,都在同一个局域网了,那这两台设备就应该可以相互通信了,来测试一下,先在设置中将ts的路径添加到命令行环境中,便于我们在终端运行,接着打开你的cmd终端工具,输入tailscale指令,可以正常返回相应内容,输入这条status指令查看当前状态,可以看到这两台设备的名称以及对应的局域网ip,接着用电脑ping一下手机的ip是否能联通,可以看到有延迟,说明二者可以相互通信,并且看到前三个数据包是通过ts的derp服务进行中转,第四个数据包就直接和这个公网ip直连了,你那有可能第二个数据包就直连了,再次输入status查看当前状态,显示和手机的状态是direct直连,也就是p2p打洞连接成功了

当电脑和手机点击connect连上ts之后,会先和ts最近的derp中转服务器建立连接,对国内用户来讲,最近的derp服务器位于美利坚合众国,假设电脑端内部的9527端口映射到公网ip5.5.5.5的1234端口,中转服务器得到了电脑与之通信的公网ip和端口,假设手机端内部的9527端口映射到公网ip7.7.7.7的2345端口,中转服务器得到了手机与之通信的公网ip和端口,一开始电脑和手机不确定双方是否能直接传输数据,所以最初是通过ts的中转服务器收发数据,不过他们已经通过ts获取到了对方的公网ip和端口,接着就可以尝试进行p2p连接,双方同时往对方的端点发送数据,由于双方都是nat1,所以出站的映射行为是端点无关,也就是不管目标ip是什么,只要是从同一设备的同一端口发过来的数据,都映射在同一公网端口,电脑是1234,手机是2345,并且由于都是nat1,所以入站的过滤行为也是端点无关,也就是不管是从哪来发过来的数据,我通通接受,于是顺利完成了p2p直连,当双方确认能直连后,wireguard的加密数据就不再经过ts的中转服务器了,而是直接转发,相较于数据去美国转一圈,直连延迟要低得多,注意以上讲的是大致流程,实际通讯细节会有出入,不影响我们整体的理解

接着演示第二种情况,我将这台手机切换到nat3的端口限制锥,测试一下电脑的nat1和手机的nat3是否能够打洞成功,再次尝试连接,同样可以成功打洞,状态显示为直连

前面流程都一样,直接从双方尝试p2p连接开始,双方的打洞数据并不一定是同一时间发送,假设电脑发送的数据先穿过nat,nat1映射行为是端点无关,所以映射在同一公网端口1234,数据到达手机,手机处于nat3端口限制锥环境,他的过滤行为是ip端口相关联,开这个洞的时候关联的是这条访问中转服务器的连接,所以只允许这条链接往里面发送数据,电脑发过来的数据会被丢弃,通信失败,期间电脑会不断尝试连接,于是进行第二次尝试,此时手机也开始尝试给电脑发送打洞数据,不管是nat1还是nat3,在映射行为上都是一样的端口无关,所以手机发出去的nat映射也是在同一公网端口2345,当手机的数据穿过nat后,电脑发过来的数据又先到了手机,手机的过滤行为依然是ip端口相关联,但现在这个洞不仅和这条连接关联,还和这条连接关联,这条连接的访问目标是电脑公网的1234端口,而这条就是从电脑公网的1234端口发过来的,于是接受了这条数据并交给手机处理,对于手机发给电脑的数据,nat1随便就过了,至此nat1和nat3也就成功p2p直连了

接着我将电脑也切换到nat3的环境中,测试一下电脑的nat3和手机的nat3是否能够打洞成功,再次尝试连接,ts提供的derp中转服务器都在国外,在gfw的加持下可能出现无法连接的情况,不过最终我们还是成功打洞了,打洞成功之后就是p2p直连了,不需要通过derp服务器中转数据

前面流程都一样,直接从双方尝试p2p连接开始,由于双方都是nat3,所以出站的映射行为是端点无关,所以映射在同一公网端口,电脑是1234,手机是2345,电脑收到手机的数据后,nat3的过滤行为是ip端口相关联,这个洞不仅关联这条连接,也同时关联这条连接,这条连接的目标ip端口是手机公网的2345,而这条入站连接刚好来自手机公网的2345端口,于是接受,手机收到电脑的数据后,同样nat3的过滤行为是ip端口相关联,这个洞不仅关联这条连接,也同时关联这条连接,这条连接的目标ip端口是电脑公网的1234,而这条入站连接刚好来自电脑公网的1234端口,于是接受,至此nat3和nat3也就成功p2p直连了,这里省略了不同时间发送打洞数据的情况,刚才的nat1对nat3已经讲过了就不再重复了,基本差不多自己捋一捋

刚才我们演示的都是easy nat对easy nat,接下来演示easy nat对hard nat,看看能否打洞成功,我将电脑切回nat1,将手机切入nat4的对称网络,再次尝试打洞,可以看到使用ts,我们依然可以让nat1和nat4进行p2p直连

前面流程都一样,直接从双方尝试p2p连接开始,nat1的电脑映射行为是端点无关,所以映射在同一公网端口1234,nat4的手机映射行为是端点相关联,也就是只要访问的目标ip或端口不同,就使用新的空闲端口开一条映射规则,这个洞是给这条连接6666中转服务器创建的映射,而现在我们要访问电脑的1234端口,属于不同的目标,所以会新开一个映射,从手机9527端口发来的数据映射到公网ip7777的3456端口,电脑访问的目标依旧是中转服务器给他的手机端点2345,显然作为ip端口相关过滤行为的nat4,不会让数据通过,期间电脑会不断尝试,不过再怎么尝试也无济于事,直到手机发过来的数据到达电脑这边,电脑作为nat1会欣然接受这个连接,当收到手机公网3456端口发过来的打洞数据后电脑获取到了新的手机端点,于是通过这个端点连接手机,当收到电脑公网1234端口发过来的数据时,这个洞就是专门为你而开的,于是放行通过,至此nat1和nat4就成功p2p直连了

接着将电脑网络切入nat3中,手机依然保持nat4,大家可以暂停思考一下这个状态是否还能成功打洞,可以看到这次所有的数据包都是通过derp服务器进行中转发送,也就是无法成功进行p2p直连

前面流程都一样,直接从双方尝试p2p连接开始,nat3的电脑映射行为是端点无关,所以映射在同一公网端口1234,nat4的手机映射行为是端点相关,和刚才一样使用新的空闲端口开一条映射规则,电脑访问的目标依旧是中转服务器给他的手机端点2345,显然作为ip端口相关过滤行为的nat4不会让数据通过,期间电脑会不断尝试,不过再怎么尝试也无济于事,手机发过来的数据到达电脑这边,电脑的nat3属于端点相关过滤,他只给手机公网的2345端口发送过数据,没有主动给3456端口发送数据,所以会拒绝这个数据包通过,双方不管重发多少次数据都无法改变这一状况,所以无法打洞成功,最终使用中转收发数据

但有些情况下你可能发现ts的nat3和nat4可以打洞成功,大概率是因为路由器开启了upnp或nat-pmp等端口映射功能,ts会自动把相应的内部端口映射到外部,在网页上检测是nat3但实际对于ts来讲属于nat1的fullcone,那就变成了nat1对nat4的情况了

对于ts而言p2p直连只能到此为止了,而同样作为p2p组网工具的easytier提供了更多的nat穿透方案,甚至支持打通nat4对nat4,以下简称et,在这里下载安装对应的客户端,遗憾的是目前还不支持苹果的ios系统,打开客户端之后,可以在这里切换为中文界面,手机端也是同样的操作,et可以不用注册账号,在这里填入网络名称,注意不要和别人重复,然后设置密码,手机端也要输入同样的网络名称和密码,接着启用两端的网络,稍等片刻可以在节点信息栏中看到当前状态,和et的服务器是p2p直连,这里的服务器类似ts的derp中转服务器,服务器是有公网ip的,属于无nat,p2p直连理所应当,和nat4的手机连接是relay中转,手机这边也显示和nat3的电脑连接是relay中转,稍等片刻,让他们打会洞,不出意外的话就能看到二者p2p直连成功了,可以尝试ping一下这个虚拟局域网ip,可以ping通,另外还可以在客户端查看当前的nat类型,电脑是nat3端口限制型,手机端是nat4对称型,这个结果要比网页检测更准确,所以你也可以把它当一个跨平台的nat类型检测工具

et是怎么做到nat3和nat4成功打洞呢?答案就是通过端口预测进行端口扫描

我们刚才的问题是电脑发给手机的数据,由于这个洞是开给中转服务器的所以不让进,手机发给电脑的数据,由于这个洞没有主动给手机公网的3456端口发送过数据,所以也不让进,但是开这个洞的目的是访问电脑公网的1234端口,所以假设电脑这边能主动给手机端公网的3456端口发送数据,这个洞会接受电脑公网1234端口发过来的数据,接着手机再发数据给电脑,电脑也会接受这个来自手机公网3456端口的数据,就能成功进行p2p通信了,现在的问题是电脑根本就不知道手机那边开了哪个端口,他只有从中转服务器拿到的2345端口,所以他只能在附近一个个端口试,端口总共有65535个,首先试试2345端口,没反应,再试2346、2347、2348等没开放的端口,也不会有任何反映,直到遇到3456端口,发现对方有回复,于是就打洞成功了,假设一秒尝试一次,极端情况下得尝试65535秒,也就是18个小时,即使一秒尝试100次也要10分钟,这谁能忍?所以手机端这边会对外发起上百条连接,开上百个洞,我这里就意思一下画一个,电脑端随便碰到任何一个洞就成功了,这样就大大提高了效率,一般在30秒内就会有结果

最后将电脑切入nat4的对称类型,手机端依旧保持nat4,nat4对nat4还能成功打通吗,运行两端的网络,手机端依旧显示对称型,电脑端显示对称型 easy,easy的映射端口是有规律的递增或递减,目前连接方式为relay中转,打洞过程和刚才的nat3对nat4一样通过端口预测扫描,但由于两端都是端点相关映射,没有固定端口,每次都要开新的洞,并且新开的洞也只接受某个固定端点进入,要命的是双方根本就不知道对方用哪个端口访问了自己的哪个端口,只有我刚好给你这个端口发,你也刚好从这个端口给我发,通过不断碰撞最终有可能打洞成功,但我建议还是不要尝试了,这种方式从运营商的角度来看更像是在发动网络攻击,如果你用wireshark进行抓包的话会看到双方正在不同端口上不断发送udp打洞数据,他会疯狂占用nat映射表,可能导致家庭网络出问题,而且这里还只是演示两个人的情况,如果再有三五个客户端,两两之间都要打通的话更是灾难级的,et客户端也提供了关闭nat4打洞的选项,所以对于nat4和nat4,我更建议你想办法让其中一方脱离nat4,大家的网络环境都不一样,具体怎么调整需要视环境而定,比如开启软路由的fullconenat 、将设备放入dmz区域,开启路由器upnp等手段,对于有公网ip的用户来说通过上述手段应该就能改变家里的nat类型,但对于家里没有公网ip,处在运营商大内网的CGNAT用户来讲做完上述操作后可能并没有任何改变,因为处在大内网环境中我们最少要经历两次nat,家里路由器做一次nat转换为大内网ip,运营商路由器还要做一次nat转换为公网ip,最终决定NAT类型的是更严格的那一次,由于我们只能改变自己家里路由器的nat行为,无法修改运营商路由器的nat行为,如果运营商是nat4,你就算直接用电脑进行pppoe拨号,取消路由器这一层nat也无法改变最终结果是运营商的nat4,欢迎大家在评论区反馈你那运营商是什么nat类型,当然也有可能是你家路由器配置的nat行为比运营商的更严格,如果你确定运营商是nat4的话,可以尝试联系运营商更改nat类型,或者更换其他运营商,或者自己搭建中转服务器,当然你能忍受龟速的话也可以用官方提供的免费中转服务

最后修改:2025 年 12 月 13 日 07 : 46 PM