前两天稍微研究了下通过UDP建隧道穿过NAT路由器, 自己写了个实现, 中间因为考试等事宜耽误了几天, 今天终于能用了.
说起udp隧道, 不得不说下nat. nat现在在市面上再常见不过, 市面上几乎所有的家用路由器, 网吧, 企业, 学校, 手机wifi热点, 无一不使用nat. nat是当前共享上网的主要方式.
不过nat的弊端也是明显的, nat一般只有少数个外网端口, 但却为多数个内网主机提供网络服务. 大家想想, 家里宽带是不是只有一个口接WAN, 其余四个都接LAN. 这就造成了内网主机无法直接与外网主机进行点对点通讯. 首先, 外部主机对内部主机是无知的, 无法对其发出tcp连接, 这还好说, 可以让内部主机主动对外部主机发出连接, 但假如两天主机都在nat内, 就很苦恼了. udp隧道是通过一个在公共互联网的主机作为服务器, 扮演一个中介的角色, 让两台处于nat内部的主机接上线.
首先先要说一下nat的种类, 在stun的标准文档(RFC3489)中定义了两大种nat类型: 1.对称型nat, 2.圆锥形nat, 对于第一种对称性nat, stun是无能为力的. 圆锥形又分三种, 完全圆锥型NAT、受限圆锥型NAT和端口受限圆锥型NAT, 这三种stun都可以穿过.
对称型nat.
如下图所示:
对称型nat在转发同一主机同一端口号(ip和port都相同)发出的的数据时, 如果对端服务器不是同一个, 则选用不同的端口号进行转发, 这将导致无法进行nat穿越.
如图所示
- 在客户端: nat内主机192.168.1.100通过同一端口8000分别向5.6.7.8:56214和5.6.7.9:12540发出数据包. 地址对: { 192.168.1.100:8000 – 5.6.7.8:56214 } { 192.168.1.100:8000 – 5.6.7.9:12540 }
- 在路由器: nat路由器1.2.3.4分别选用了两个不同的端口转发数据包(1.2.3.4:34594和1.2.3.4:34595 ), 地址对: { 1.2.3.4:34594 – 5.6.7.8:56214 } { 1.2.3.4:34595 – 5.6.7.9:12540 }
- 在服务器端: 两个服务器分别接收到不同的端口发来的数据
圆锥形nat.
如下如所示:
圆锥形nat在转发同一主机同一端口号(ip和port都相同)发出的的数据时, 选用同一端口号进行转发. 所以可以利用这一点进行nat穿越
如图所示:
- 在客户端: nat内主机192.168.1.100通过同一端口8000分别向5.6.7.8:56214和5.6.7.9:12540发出数据包. 地址对: { 192.168.1.100:8000 – 5.6.7.8:56214 } { 192.168.1.100:8000 – 5.6.7.9:12540 }
- 在路由器: nat路由器1.2.3.4选用相同的的端口转发数据包(1.2.3.4:34594 ), 地址对: { 1.2.3.4:34594 – 5.6.7.8:56214 } { 1.2.3.4:34594 – 5.6.7.9:12540 }
- 在服务器端: 两个服务器分别接收到相同的端口发来的数据
而圆锥形nat又分三种:
- 完全圆锥型NAT : 同一主机同一端口号的数据都映射到nat路由器的同一端口, 任意外部主机向1.2.3.4:34594发送数据192.168.1.100:8000都能收到.
- 受限圆锥型NAT :同一主机同一端口号的数据都映射到nat路由器的同一端口, 但必须内部主机192.168.1.100用8000向外部主机1.2.3.4发送一个数据报之后才能接受外部主机发来的数据
- 端口受限圆锥型NAT : 同一主机同一端口号的数据都映射到nat路由器的同一端口, 但必须内部主机192.168.1.100用8000向外部主机1.2.3.4的34594端口发送一个数据报之后才能接受外部主机发来的数据
nat原理明白了, 接下来就可以设计如何nat穿越了.
分三步
- 内部主机分别向处于外网的一台公共STUN服务器发送login信息, 报告自己的ip和端口 (图中的黑色)
- 当服务器收到两台主机发来login后分别向两台主机发送对端的ip和端口 (图中的蓝色)
- 两台内部主机用原先想服务器发login信息的端口向对端ip和端口通讯 (图中的粉色)
原理明白了很简单
下面是我自己的一个实现
服务端:
1 |
|
客户端:
1 |
|