欢迎您访问 最编程 本站为您分享编程语言代码,编程技术文章!
您现在的位置是: 首页

pwnat - 基于 UDP 打洞技术的 NAT 点对点 P2P 穿透新方法,无需第三方服务器

最编程 2024-06-14 13:00:55
...

pwnat——一种无需第三方服务器就能完成NAT点对点P2P穿透的基于UDP打洞技术的新方法

  • 简介
    • 传统的udp打洞
    • UDP 打洞原理及过程
    • pwnat技术,无需第三方服务器!
    • pwnat用法
    • pwnat工作原理

前段原文:https://zhuanlan.zhihu.com/p/40816201
后端pwnat原文:https://github.com/samyk/pwnat
Photo 1,2 by Thomas Kvistholt on Unsplash
Photo 3 from *Autonomous NAT Traversal * http://samy.pl/pwnat/pwnat.pdf

本人只是组合和翻译一下大佬们的话而已。。。
注:ICMP包就是平常经常使用的命令ping所发出的数据包,常用来检测网络是否连通。

简介

P2P 通信最大的障碍就是 NAT(网络地址转换),NAT 使得局域网内的设备也可以与公网进行通讯,但是不同 NAT 下的设备之间通讯将会变得很困难。UDP 打洞就是用来使得设备间绕过 NAT 进行通讯的一种技术。传统的udp打洞需要第三方服务器介入,而如今github上的开源项目 pwnat使得无需借助第三方完成p2p穿透技术。

传统的udp打洞

先从介绍NAT开始,(英语:Network Address Translation,缩写:NAT),网络地址转换又称网络掩蔽、IP掩蔽,在计算机网络中是一种在IP数据包通过路由器或防火墙时重写来源IP地址或目的IP地址的技术。这种技术被普遍使用在有多台主机但只通过一个公有IP地址访问因特网的私有网络中。它是一个方便且得到了广泛应用的技术。当然,NAT也让主机之间的通信变得复杂,导致了通信效率的降低。
NAT 的工作原理如下:
在这里插入图片描述
首先,NAT A 网下的设备 1(192.168.1.101)想与某公网 IP 通讯,设备 1 将包发给 NAT A,然后 NAT A 对源 IP 进行转换发给 NAT B(中间可能还会经过多重 NAT)。

这样做的目的是,NAT B 并不知晓 NAT A 下的各个设备,他只能与 NAT A 本身通讯,因此发送给 NAT B 的包源 IP 必须是 NAT A 的公网 IP,不然 NAT B 没有办法进行回复。

接下来 NAT B 将回复包再发回 NAT A,此时就是 NAT 发挥作用的时候了,NAT A 现在要做的就是将包再分发回之前的设备,如何确定要发给谁呢?NAT 中记录了一张表,之前 192.168.1.101 通过 2333 端口与 42.120.241.46 端口 443 通讯了,并且 NAT A 是用 60001 的端口转发出去的,那么这次接受到发往该 NAT 60001 端口的包时就应该再通过 2333 端口转发给 192.168.1.101。经过这样的过程,NAT A 下的设备都可以连接到互联网了!

UDP 打洞原理及过程

如上图所示,由于 NAT 的存在,当 NAT A 的设备 1 想与 NAT B 下的设备通讯时,必然要将目标 IP 设置为 NAT B 的公网地址,而 NAT B 转发表中并没有记录过 NAT A 与自身网络下设备的通讯记录,因此 NAT B 会将包丢掉。

下面我们来看看 UDP 打洞是怎么解决这个问题的。

在这里插入图片描述
为了能够进行 UDP 打洞,我们需要一台公网的服务器作为中转站,它是 NAT A 与 NAT B 之间的信使。

(为了方便起见,我们把地址为 192.168.1.101 的设备称为设备 1,把地址为 192.168.1.2 的设备称为设备 2,信使服务器称为 S)

首先,设备 1 和设备 2 都向 S 注册自己,S 中能记录各个设备此时使用的公网 IP 地址和端口号,例如设备 1 是 123.122.53.20:31000,设备 2 是 42.120.241.46:41000。

然后设备 1 与设备 2 都向 S 获取对方的公网 IP 与之前预留的端口号,就像这样:
在这里插入图片描述
然后就是最关键的一步,打洞。

设备 1 向 42.120.241.46:41000 发一个包,NAT B 自然能接收到这个包,然而它不知道来自 NAT A 的包应该发给谁,因此 NAT B 将这个包舍弃。但是由于设备 1 向 42.120.241.46:41000 发过包,NAT A 会记录:以后来自 42.120.241.46:41000 的包都发给设备1

设备 2 也做相同的操作,让 NAT B 也知道:以后来自 123.122.53.20:31000 的包都发给设备 2

至此,NAT A 与 NAT B 都互相为对方保留了端口,就可以愉快地通讯了。

当然了,大致原理是很简单的,实际操作起来情况可能会更复杂,会涉及到丢包、多重 NAT 等问题的处理,本文仅起到一个科普解释的作用。

pwnat技术,无需第三方服务器!

pwnat是目前唯一可以在防火墙/ NAT上打孔的工具,使用新开发的技术,利用NAT转换表的属性,能够在不需要proxy代理、第三方服务器、upnp、DMZ、sproofing、dns转换的情况下实现NAT中的P2P通信。

相关技术可以查看EEE P2P’10会议报告:http://samy.pl/pwnat/pwnat.pdf

pwnat用法

usage: ./pwnat <-s | -c> <args>

  -c    client mode
        <args>: [local ip] <local port> <proxy host> [proxy port (def:2222)] <remote host> <remote port>

  -s    server mode
        <args>: [local ip] [proxy port (def:2222)] [[allowed host]:[allowed port] ...]

  -6    use IPv6
  -v    show debug output (up to 2)
  -h    show this help and exit

例子:

其中一台NAT后的主机输入指令作为服务端:

  ./pwnat -s

例如其中一台NAT后的主机作为客户端想访问google

  ./pwnat -c 8000 <pwnat.server.com> google.com 80

之后打开http://localhost:8000 就可以访问google了!

pwnat工作原理

我穿透NAT的方法是双重的,我将在下面描述。

(注:作者这里把两台NAT之后的主机其中一台作为服务端。其中一台作为客户端)
为了建立完整的隧道,客户端需要知道服务器的公共IP地址,服务器需要知道客户端的公共IP地址。 但是,在真正的客户端 - 服务器模型中,服务器直到客户端连接才会知道客户端的IP,而NAT主机则通常会丢弃未知的传入数据包。 在pwnat中,服务器不需要知道客户端IP地址也能完成点对点通信。

以下是pwnat服务器如何获知客户端的IP地址:

通过让客户端“假装”成为一个互联网上任意的iCMP跳跃点( a random hop on the Internet)来解决这个问题。traceroute命令使用相同的技术用于检测Internet上的跳跃点,但我现在正在反过来使用这个技术以便穿透NAT。

具体来说,当服务器启动时,它开始向固定地址3.3.3.3发送固定的 ICMP回应请求包(ICMP echo request packets)。显然,我们无法从3.3.3.3收到返回的ICMP回应数据包(ICMP echo packets)。然而,3.3.3.3 并不是我们可以访问的主机,我们也不是想伪装成它来发“ICMP回应”数据包。相反,pwnat技术的实现原理在于,当我们的客户端想要连接服务端时,客户端(知道服务器IP地址)会向服务器发送”ICMP超时数据包“(ICMP Time Exceeded packet)。 这个ICMP数据包里面包含了服务器发送到3.3.3.3的“原始”固定ICMP回应请求数据包。

为什么要这样做呢?好吧,我们假装是互联网上的一个ICMP跳越点,礼貌地告诉服务器它原来的“ICMP回应请求”数据包无法传递到3.3.3.3。而你的NAT是一个聪明的设备,它会注意到”ICMP超时数据包“内的数据包与服务器主权发出“ICMP回应请求”数据包相匹配。然后它将”ICMP超时数据包“转发回NAT后面的服务器,包括来自客户端的完整IP数据包头,从而让服务器知道客户端IP地址是什么!(如下图所示)
来自Andreas M¨uller等人的论文

服务器(1.2.3.4):ICMP回应请求数据包- > 3.3.3.3

服务器(1.2.3.4):ICMP回应请求数据包 - > 3.3.3.3

服务器(1.2.3.4):ICMP回应请求数据包 - > 3.3.3.3

客户端(6.7.8.9):ICMP回应请求超时数据包(包括服务器发送到到3.3.3.3的ICMP回应请求数据包) - > 1.2.3.4
服务器的NAT:检查来自客户端的ICMP回应请求超时数据包中是否含有服务器的ICMP回应请求数据包,如果包含,则将整个数据包发送到服务器。

不相信我?只需使用traceroute命令跟踪NAT后面的任何主机。您会注意到路由器预先完全不知道会从哪个IP地址返回ICMP回应请求超时数据包。您的路由器只知道根据ICMP超时数据包中的数据将这些数据发送给您。现在,服务器仅仅只知道了客户端IP地址,我们还没有发送任何其他数据的方法。对于完整的通信,我们使用我以前的软件chownat中使用的相同方法(注:就是上文提到的UDP打洞)来穿透两个NAT。

NAT后面的客户端与不在NAT后面的机器通信的示例(注:传统CS架构):
机器A - > NAT A - > net - > quake服务器
机器A将UDP数据包发送到服务器,打开“会话”(session)。
NAT A看到了这一点并说:
”如果任何UDP数据包从相同的主机和端口返回,我将其路由到机器A.”
服务器发回UDP数据包,到达NAT A,NAT A看到正确的主机和端口,将其发送到机器A.机器A和服务器现在能够毫无问题地进行通信。

而现在则是是pwnat如何在客户端和服务器知道彼此IP的情况下完成通信。
目标是实现这一个通信:机器A(ssh客户端) - > NAT A - > net - > NAT B - >机器B(ssh服务器)
当你在机器B上启动pwnat服务器时,它会不断发送 UDP数据包到机器A.当然,NAT A不会接收这些数据包,所以它会丢弃它们中的每一个。而机器B不会停止发送数据包。 一旦你启动计算机A上的pwnat客户端,它开始发送UDP数据包到机器B.注意:pwnat默认源端口和目的端口都是2222。而任何未经授权的用户都可以设置UDP源端口和目标端口。通常,机器A发送到NAT B的UDP数据包将被丢弃。 但是,由于机器B曾经发送类似的数据包从相同的端口出去,NAT B假设这些是先前发出去的数据包的响应并让它们重新进入。一旦机器B看到这些数据包,它就会将握手数据包发送回机器A.这些数据包不会被NAT A丢弃,因为同样的原因:NAT A曾经看到相同的数据包从相同的端口出去,因此返回NAT的数据包看起来就像对出去的数据包的响应。

最后,双方都通过UDP进行完全通信,从而允许基于TCP的一些协议进行隧道传输。

注意:pwnat服务器和客户端上有一个保持活动(keepalive)的过程,它始终保持UDP“会话”处于活动状态。 它发送的数据包具有0字节的有效负载,仅在客户端不发送数据时发送。 否则,它可能发送保持活跃数据包的最快速度是每5秒一个数据包。 如果任何其他类型的数据通过隧道传输,则不会传输保持活动的数据包。

原作者: Samy Kamkar

pwnat 基于Daniel Meekins的udptunnel 制作
http://code.google.com/p/udptunnel/