NAT穿透 (跟UDP打洞实验)【维基】
一、NAT穿透
在计算机科学中,NAT穿越(NAT traversal)涉及TCP/IP网络中的一个常见问题,即在处于使用了NAT设备的私有TCP/IP网络中的主机之间建立连接的问题。
会遇到这个问题的通常是那些客户端网络交互应用程序的开发人员,尤其是在对等网络和VoIP领域中。IPsec VPN客户普遍使用NAT-T来达到使ESP包通过NAT的目的。
尽管有许多穿越NAT的技术,但没有一项是完美的,这是因为NAT的行为是非标准化的。这些技术中的大多数都要求有一个公共服务器,而且这个服务器 使用的是一个众所周知的、从全球任何地方都能访问得到的IP地址。一些方法仅在建立连接时需要使用这个服务器,而其它的方法则通过这个服务器中继所有的数 据——这就引入了带宽开销的问题。
两种常用的NAT穿越技术是:UDP打洞和STUN。除此之外,还有TURN, ICE, ALG,以及SBC。
目录
- 1 NAT traversal 与 IPsec
- 2 IETF 文献
-
3 相关技术
- 3.1 NAT 穿透技术与 NAT 行为
- 3.2 NAT 穿透基于 NAT 控制
- 3.3 NAT 穿透整合技术
- 3.4 University research papers
- 4 外部链接
NAT traversal 与 IPsec
为了实现IPsec应用于NAT之上, 下列的协议必须实作于 firewall:
- Internet Key Exchange (IKE) - User Datagram Protocol (UDP) port 500
- Encapsulating Security Payload (ESP) - IP protocol number 50
或者是 NAT-T 之例:
- IPsec NAT-T - UDP port 4500
在家庭路由器上,这通常通过启用"IPsec穿透"来实现。
IETF 文献
- RFC 1579 - Firewall Friendly FTP
- RFC 2663 - IP Network Address Translator (NAT) Terminology and Considerations
- RFC 2709 - Security Model with Tunnel-mode IPsec for NAT Domains
- RFC 2993 - Architectural Implications of NAT
- RFC 3022 - Traditional IP Network Address Translator (Traditional NAT)
- RFC 3027 - Protocol Complications with the IP Network Address Translator (NAT)
- RFC 3235 - Network Address Translator (NAT)-Friendly Application Design Guidelines
- RFC 3715 - IPsec-Network Address Translation (NAT) Compatibility
- RFC 3947 - Negotiation of NAT-Traversal in the IKE
- RFC 5128 - State of Peer-to-Peer (P2P) Communication across Network Address Translators (NATs)
相关技术
NAT 穿透技术与 NAT 行为
- Session Traversal Utilities for NAT (STUN)
- Traversal Using Relay NAT (TURN)
- NAT-T Negotiation of NAT-Traversal in the IKE
- Teredo tunneling uses NAT traversal to provide IPv6 connectivity.
- Session Border Controller (SBC)
- UDP打洞(UDP hole punching)
- TCP打洞(TCP hole punching)
- ICMP打洞(ICMP hole punching)
NAT 穿透基于 NAT 控制
- Realm-Specific IP (RSIP)
- Middlebox Communications (MIDCOM)
- SOCKS
- NAT Port Mapping Protocol (NAT PMP)
- Internet Gateway Device(IGD)协定, 由通用即插即用(UPnP)论坛所定义。
- Application Layer Gateway (ALG)
NAT 穿透整合技术
- Interactive Connectivity Establishment (ICE)
University research papers
- Cornell University - Characterization and Measurement of TCP Traversal through NATs and Firewalls
- Columbia University - An Analysis of the Skype Peer-to-Peer Internet Telephony
- Peer to peer communication across Network Address Translators (UDP Hole Punching)
- Internet By All Means - An article on how to maximize your chances to get around firewalls
外部链接
- NAT-Traversal Test
- How Skype & Co. get round firewalls
- NAT Traversal
二、UDP打洞
在计算机科学中,UDP打洞指的是一种普遍使用的NAT穿越技术。
描述
通过UDP打洞实现NAT穿越是一种在处于使用了NAT的私有网络中的Internet主机之间建立双向UDP连接的方法。由于NAT的行为是非标准化的,因此它并不能应用于所有类型的NAT。
其基本思想是这样的:让位于NAT后的两台主机都与处于公共地址空间的、众所周知的第三台服务器相连,然后,一旦NAT设备建立好UDP状态信息就转为直接通信,并寄希望于NAT设备会在分组其实是从另外一个主机传送过来的情况下仍然保持当前状态。
这项技术需要一个圆锥型NAT设备才能够正常工作。对称型NAT不能使用这项技术。
这项技术在P2P软件和VoIP电话领域被广泛采用。它是Skype用以绕过防火墙和NAT设备的技术之一。
相同的技术有时还被用于TCP连接——尽管远没有UDP成功。
算法
假设有两台分别处于各自的私有网络中的主机:A和B;N1和N2是两个网络的NAT设备,分别拥有IP地址P1和P2;S是一个使用了一个众所周知的、从全球任何地方都能访问得到的IP地址的公共服务器
步骤一:A和B分别和S建立UDP连接;NAT设备N1和N2创建UDP转换状态并分配临时的外部端口号
步骤二:S检查UDP包,看A和B的端口是否是正在被使用的(否则的话N1和N2应该是应用了端口随机分配,这会让打洞变得更麻烦)
步骤三:如果端口不是随机化的,那么A和B各自选择端口X和Y,并告知S。S会让A发送UDP包到P2:Y,让B发送UDP包到P1:X
步骤四:A和B通过转换好的IP地址和端口直接联系到对方的NAT设备;
UDP打洞实验
两台没有外网 IP、在 NAT 后边的主机如何直连?UDP打洞通常可行,但是需要第三方服务器。方法如下:
在服务器 S 上监听一个 UDP 端口,在收到 UDP 数据包后把源地址发回去。代码如下(github):
import sys import time import socket def main(port): s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) s.bind(('', port)) try: while True: data, addr = s.recvfrom(4096) back = 'Your address is %r\n' % (addr,) s.sendto(back.encode(), addr) print(time.strftime('%Y-%m-%d %H:%M:%S'), addr, 'just sent us a message:', data.decode('utf-8', 'replace'), end='') except KeyboardInterrupt: print() if __name__ == '__main__': try: main(int(sys.argv[1])) except (ValueError, IndexError): sys.exit('which port to listen?')
主机 A 发送数据包:
$ socat readline udp:xmpp.vim-cn.com:2727,sourceport=4567 my addr? Your address is ('a.b.c.d', 40060)
输入任意消息并回车,一个 UDP 就从本地的 4567 发送出去了。从上述示例我们可以看到,NAT 设备转发时是从 40060 端口发送出去的。为了让服务器返回的数据能够到达内网主机,在一段时间内,NAT 设备会记住外网来自 40060 端口的 UDP 数据包要发送给主机 a.b.c.d 的 4567 端口。完全圆锥型NAT不会在意外部数据包是从什么地方发回来的。受限圆锥型NAT会忽略掉其它主机的数据包,上例中只认可来自 xmpp.vim-cn.com 的数据包。端口受限圆锥型NAT更进一步地要求源端口(上例中是 2727)必须跟之前发出的数据包的目的端口一致。当然,「之前发出的数据包」不必是最后一个。所以,除了最后一种——对称NAT——之外,其它类型的NAT都是有可能成功穿透的。参见维基百科条目网络地址转换和STUN。
后来通过 pystun 程序,我得知我所处的 NAT 是完全圆锥型的。
在知道 A 的发送地址后,主机 B 就可以向这个地址发送数据了。接下来的操作使用 socat 命令就是:
# host A $ socat readline udp-listen:4567 # host B $ socat readline udp:A:4567
然后 B 先发送数据让 A 知道 B 的地址(socat 会 connect 到这个地址),双方就可以相互通信了。当然,因为是 UDP 协议,所以通信是不可靠的,丢包啊乱序啊都有可能
参考文献:
udp打洞实验 http://lilydjwg.is-programmer.com/2012/8/28/udp-hole-punching-experiment.35350.html