P2P NAT穿透技术

前几天在做一个文件点对点传输的项目,涉及到NAT穿透,从上个星期到今天一直在调试和测试,最后到今天,看来已经战败!

以下是两篇是我作为技术依据的文章:

http://blog.csdn.net/ssihc0/archive/2008/10/10/3053395.aspx
http://hi.baidu.com/wangzhe1945/blog/item/3e72fffe47fc2f365d60080c.html
http://hi.baidu.com/wangzhe1945/blog/item/5ccd3fa4e3ee67f09152ee38.html

总的技术原理归纳如下:
首先还是 AB分别和服务器S分别建立连接,S记录AB的互联网实际终端。然后S分别向AB发送对方的实际终端。接着,从A和B向S连接时使用的端口,AB都异步调用connect函数连接对方的实际终端(就是S告诉的终端),同时,AB双方都在同一个本地端口监听到来的连接(也可以先监听,再connect更好)。由于双方都向对方发送了connect请求(假设各自的SYN封包已经穿过了自己的NAT),因此在对方connect请求到达本地的监听端口时,路由器会认为这个请求是刚刚那个connect会话的一部分,是已经被许可的,本地监听端口就会用SYN-ACK响应,同意连接。这样,TCP穿透NAT的点对点连接就成功了。  

自己写的代码穿透失败。下载了别人p2p TCP 穿透的代码 http://download.csdn.net/source/700961 ,发现如果2个机器在同一个局域网(同一内网)内是可以传输文件,但是不同的陆游器(不同内网)下,穿透不成功。


我不是高手,p2p TCP 穿透 战败。

除非是特定的环境,不然TCP打洞有点"碰运气"的成份在内.因为要不断尝试某些可能的端口,以达到连上.UDP打洞能成功,那是因为UDP协议不需要"连接",只需要消息就能传播,但TCP却不一样,还要三次握手,你的程序能知道在本机绑定的TCP端口是什么,但经过路由后,服务器根本不知道要路由是用哪个端口进行映射.如果路由开了UPNP功能的话,那么可以进行UPNP映射,但在一些二级交换机的网络中,这招一样不灵.我看过一些象music box等软件进行的TCP打洞,其它就是使用UPNP映射,如果路由没打开UPNP功能,它只能使用UDP打洞.

可以基于这么一点考虑:

如果是NAT是full cone nat, session1 对应的pair( local ip + port + nat outside ip + port)在接下来的session2中不会变化。
来尝试。
有可能会成功。

local machine A:
  (192.168.1.101:8000) connect to Server(10.56.1.182:80) with Nat(10.56.1.183:1001) =====> PAIR(192.168.1.101:8000 <=> 10.56.1.183:1001) 建立
  192.168.1.101:8000 release connect but listen at it(reuse addr)

local machine B:
  (192.168.0.101:8000) connect to Server(10.56.1.182:80) with Nat(10.56.2.13:1001) =====> PAIR(192.168.0.101:8000 <=> 10.56.2.13:1001) 建立
  192.168.0.101:8000 release connect but listen at it(reuse addr)


Server(10.56.1.182:80)
  Server tell machine A(192.168.1.101:8000 <=> 10.56.1.183:100) to connect to local machine B(PAIR(192.168.0.101:8000 <=> 10.56.2.13:1001))
  Server tell machine B(PAIR(192.168.0.101:8000 <=> 10.56.2.13:1001)) to connect to local machine A(192.168.1.101:8000 <=> 10.56.1.183:100)

因为B(PAIR(192.168.0.101:8000 <=> 10.56.2.13:1001))
  A(192.168.1.101:8000 <=> 10.56.1.183:100)
的映射对仍存在,故内网A或B可以收到请求,从而建立连接。

以上只是对full cone nat有意义。


TCP穿越NAT技术上是可能实现的。关键是要构筑正确的TCP三次握手的过程,这里面就要求对连接的双方A,B,都要满足三次握手的正确条件,及SYN包,SYN-ACK包,ACK包,中间的每个序号和应答号都要正确,且在此过程中,不能被RST包所复位。你可以参考网上那篇经典文章 natblaster.pdf。说的很清楚。


我原来有研究过一段时间,不过最后没有完成,后来有段时间看了资料,有启发,但是没有去做,现在说说大致的想法,你可以尝试去做下,不一定成功!
第一,A或者B和S的链接都是成功的,这个我们先这样认定,也就是说A或者B和S的握手都是正确的,不管经过了多少的路由器,TCP的包中的内容都是固定的。
第二,这时候如果把S向A发送的包给拦截下来的话,也就是说假设S已经不像A发送数据,那么B把自己的TCP发送的包中的IP地址和端口改成S向A发送包的IP地址和端口,这时候A一样认为是S给他发送了数据,并向S发送了返回TCP包,那么S接收到,判断TCP包序号的时候就是错误的值,但是这个包确实能被S接收到,并最终丢弃!
第三,假设,我只是假设,并没有确实的代码实现,也没有测试过,假设S这时候不像A和B发送任何的数据,但是前面已经把对方的IP地址,端口号,以及TCP序号等都发送到了A和B,那么A把IP地址,端口号,以及TCP序号,进行相应的修改,同时B把同S的链接中的IP和端口号也同时改成了A的IP和端口号,这样就可以欺骗B机器自己,接收到A发送过来的数据!对于B机器来说这个链接不能是新建立的,因为新建立的链接需要重新握手。可能真正困难的是B机器如何将和S的链接中的IP地址和端口改成A机器和S链接封包中的IP和端口。
其实整体的思路就是保持A,B同S的链接,保证这个链接不会断,导致重新握手,然后就是欺骗对方机器和欺骗自己,让这2台机器还是认为自己是在和S对话,其实数据是在A和B中传送。

原文地址:https://www.cnblogs.com/zzxap/p/2175627.html