当前位置: 首页 > web >正文

家庭宽带的内网穿透实践

家庭宽带的内网穿透实践

龙生龙,凤生凤,老鼠的儿子会打洞。我们今天来学习 “打洞” !

背景

众所周知,当前运营商在IPv4环境下面,由于地址资源不够,启用了大内网策略。导致家庭宽带到路由器这一层都分配了运营商的NAT内网IP。这一环境对我们家里有自建NAS或者想暴露公网服务的需求的人来说,特别不友好。当然,如果是纯IPv6环境另说,IPv6环境可以直连。所以针对NAT的内网IPv4环境,我们需要使用内网穿透。本文实现了一个小demo讲解内网穿透的原理,带你一步一步实现家庭宽带的IPv4内网穿透。

拓扑介绍

当我们分配到了运营商的大内网IPv4地址之后,家里的网络拓扑极有可能变为这样:

[家庭NAS节点] -- IP: 192.168.0.100|v[NAT网关1 家庭路由器节点] -- 内网IP: 192.168.0.1 -- 外网IP: 10.50.206.110|v[NAT网关2 运营商节点] -- 公网IP: 111.8.72.140   -> 想办法让其透传到NAS节点端口提供网络服务

值得注意的是,如果从家庭NAS节点到运营商节点之间有多个NAT网关,且不受我们控制的话,那么内网穿透就无法完成。从本人实践来看的话,目前移动运营商网络(其它的大家可以实践看下),只会在我们家庭网络出口之上给一层NAT,即如上的网络拓扑。如果我们想要把内网NAS节点上面的服务监听端口透传到运营商节点的话,走过的NAT路径应该就是这样:

家庭终端节点 [连接端口1] <->  [NAT端口1] 家庭路由器 [连接端口2] <-> [NAT端口3] 运营商节点 [公网端口]

打洞原理

我们知道,当一个内网终端访问其它公网服务的时候,在经过的NAT网关上面就会依次分配端口映射,当访问结束之后,连接断开,对应的经过的NAT网关上面的映射就会删除。根据我们上文网络拓扑的假设,家庭终端访问其它公网服务(例如qq.com)的路径应该就是这样:

家庭终端节点 [连接端口1] <->  [NAT端口1] 家庭路由器 [连接端口2] <-> [NAT端口3] 运营商节点 [公网端口] <-> qq.com

NAT打洞的原理就是让这条端口映射的链路一直保持,并且能够反向从运营商的公网节点的端口访问回来。那么实现这样的功能,就需要解决三个问题:

  1. 如何让这条连接上面的端口映射一直保持;
  2. 如何得到运营商公网节点上面的IP和端口号;
  3. 如何能够让路径反向访问到我们的TCP监听端口。

端口复用

我们对如上三个问题进行分析,首先就会发现问题1和问题3底下肯定是应该属于不同的socket。但是如果想让链路上面的NAT端口映射保留,就得保留问题1的通信链接的同时,在相同的连接端口号上面复用做问题3的TCP监听。那么引出了第一个解决方案,端口复用。我们知道,Linux系统里面已经支持将一个端口绑定到一条连接的同时,将这个端口复用为监听端口,只需要使用套接字的SO_REUSEPORT和SO_REUSEPORT属性。那么我们就可以假定有一个连接保持的同时,能够在相同的端口上面运行TCP监听服务,做到让NAT端口映射一直保留。

链接保留

解决问题1的思路有很多,例如一般我们假定NAT网关上面的映射不会立即删除,而是隔段时间之后删除。那么我们在很短的时间内通过断开重连的方式就能让映射一直保留。更普遍的做法是我们巧妙利用HTTP请求的keep alive头来让已有的公网HTTP服务器帮我们保持连接。

STUN获取端口

到目前我们解决了问题1和问题3,那么如何获取当前内网这个端口对应的运营商公网节点的IP和端口呢?我们可以搬出STUN协议了。简短介绍STUN的功能,就是我们编写正确的STUN协议请求通过socket发送给STUN服务器之后,STUN服务器就会把我们的公网IP和端口信息通过返回消息给到我们。

端口放行

我们家庭正常路由上面会有防火墙设置,当路由器外部的设备向我们主动发起连接的时候,会被阻止。所以我们应该把对应的路由器上面的端口放行掉。最简单的方法就是使用路由器的DMZ功能,直接将内网里面提供服务的那个设备映射出去,相当于放行了映射设备的所有端口。另外种方法是通过路由器的端口映射功能,将内网服务的端口映射为路由器的对外端口(经过本人实践,发现tplink路由器上面只有内外映射为相同端口才能打通,内外端口设置为不同就不行,暂时本人还不知道原因)。

最终效果

那么通过端口复用方式让同一端口做三件事就实现了我们的打洞需求:用HTTP请求keep alive特性保留链接从而保留NAT端口映射,用相同端口请求STUN服务器获取到公网网关节点的IP和对应端口,在端口上面监听服务并做相关逻辑。最后的效果图如下,实现代码示例放于github gists:
内网服务
目前实现的这个架构里面有一个缺点,就是TCP监听服务必须为端口复用。如果我们想把一个已有的服务暴露出去,但是很多已有的服务的套接字是非复用端口的,怎么办呢?很简单,只用把上图中的TCP监听服务换为一个代理服务就行了,代理服务的监听端口实行复用,收到的数据无脑转发给内网服务就行。如下:
代理转发
关于如何实现一个高效的代理转发功能,这里就不细讲了,可以通过写个程序专门做转发或者用iptables工具实现内核优化级别的转发等。

以下是极好的内网穿透相关项目:
一个python写的内网穿透工具,Natter。很容易拿来阅读学习。
一个c语言写的内网穿透工具,natmap。
一个软硬路由公网神器(包含了上述实现原理的内网穿透),lucky。

http://www.xdnf.cn/news/6238.html

相关文章:

  • 数学实验(Matlab符号运算)
  • 面试篇: Redis(持续更新)
  • vue3基础学习 [简单标签] (vscode)
  • More Effective C++:改善编程与设计(上)
  • Redis内存淘汰策略和过期键删除策略有哪些?
  • Flutter在键盘的上方加一个完成按钮
  • JAVA异常体系
  • Linux proc文件系统 内存影射
  • YOLO11解决方案之热力图探索
  • 二分查找的边界问题
  • KUKA机器人中断编程3—暂停功能的编程
  • Selenium-Java版(环境安装)
  • fio 命令在 Linux 系统中的应用示例
  • Android锁
  • android studio导入项目
  • json-server的用法-基于 RESTful API 的本地 mock 服务
  • jQuery知识框架
  • Spring Cloud Gateway 聚合 Swagger 文档:一站式API管理解决方案
  • 鸿蒙OSUniApp 实现精美的用户登录和注册页面#三方框架 #Uniapp
  • Vue ElementUI原生upload修改字体大小和区域宽度
  • WeakAuras Lua Script ICC (BarneyICC)
  • 【周输入】510周阅读推荐-2
  • TTS-Web-Vue系列:Vue3实现侧边栏与顶部导航的双向联动
  • 23-单调队列-滑动窗口
  • LeetCode 每日一题 3341. 到达最后一个房间的最少时间 I + II
  • NAT网关(网络地址转换网关)的用途与场景
  • Mac的web服务器
  • [滑动窗口]越短越合法(可转化成越长越合法)
  • 【每天一个知识点】模型轻量化(Model Compression and Acceleration)技术
  • 麒麟环境下Selenium的使用