1. 程式人生 > 其它 >[轉]P2P 之 TCP穿透NAT的原理

[轉]P2P 之 TCP穿透NAT的原理

原文:http://www.cppblog.com/tiger/articles/12710.html

-------------

由於工作需要,在網上找了很長時間P2P的資料,好像只有《P2P 之 UDP穿透NAT的原理與實現(附原始碼)》比較有實際意義,可惜又是用UDP實現的,無奈只好找了份英文資料啃,發現很有參考價值,就根據理解翻譯了一部分,分享給大家,由於水平有限,有錯望多多包涵。

摘要
防火牆和網路地址轉換(NAT)裝置對於傳統的P2P協議存在一定的問題。一些中間裝置抑制了來自外部網路到內部網路的TCP請求,這篇文章的目的就是尋找一個能夠在兩個NAT裝置內部的主機間建立TCP連線的方法。我們已經在兩個普通的硬體條件下實現了這個功能。

1.入門
由於32位IP地址的減少,現在出現了很多通過一個internet地址代理內部網路上網的裝置,這就是NAT技術。這些裝置對於INTERNET已經越來越重要了,但是它們的獨立發展因為缺乏標準而對現在的internet協議造成危害。

2.技術
典型的NAT和防火牆裝置都是不允許外部地址主動請求而進入內部網路的,如果使用者的程式需要在兩個內部網路間建立直接性的連線,那麼兩個內部裝置之間必須是相互信任的。如果A和B兩個部分都初始化了TCP連線,NAT裝置就會認為它們之間是相互信任的,也就允許它們之間的連線了。
圖1是一個例子,目標是能夠讓A和B(分別在NATA和NATB後)建立TCP連線。
我們討論了多種在特定的NAT裝置環境下的TCP連線方案。
如果我們的情況如下:
1、可預測NA的埠,可預測NB的埠,可指定源IP的特定路由
2、可預測NA的埠,可預測NB的埠,不可指定源IP的特定路由
3、隨機的NA埠,可預測NB的埠,可指定源IP的特定路由
4、隨機的NA埠,可預測NB的埠,不可指定源IP的特定路由
5、隨機的NA埠,隨機的NB埠,可指定源IP的特定路由
6、隨機的NA埠,隨機的NB埠,不可指定源IP的特定路由

我們必須作出這4種假設:
1、 兩個主機都不受NAT裝置所限制;
2、 我們可以配置網路裝置使得主機看不到來自外部網路的ICMP包(TTL超過限制),因為這些ICMP資料包無論被任何一方接收到都是中斷TCP連線。我們討論的一些解決方案就依賴通過傳送一個初始TTL很小的SYN包來建立TCP連線。一旦SYN包被路由器丟棄,ICMP TTL 超時包就會被傳送到NAT裝置,我們不允許NAT裝置將這個超時TTL的返回包傳送到內部網路,即使NAT會將這個包傳送到內部,也需要通過配置防火牆來限制這個包到達主機;
3、 即使NAT裝置看到ICMP超時的資料包,裝置本身的對映表將不會作任何改動;
4、 內部網路的其它主機不會佔用搶佔這個埠,因為如果網路特別繁忙,這個埠可能會無效。

3.1第一種情況
我們可以通過圖2表示的順序解決問題:
1)A和B可以設定LSR(IP報頭中的一個選項)通過X路由傳送SYN資料包.
2)X可以快取它們的資料包並且傳送欺騙的SYN+ACKS給NA和NB.
3)A和B可以通過由X傳送來的資料進行應答.
4)X丟棄這兩個ACK包,因為它已經可以確定A和B相互應答成功.


 
 
 
圖2是假設A和B都事先彼此的NAT的通訊埠,A知道B的通訊埠是NB:5000,B知道A的是NA:4000,並且要求X不在任何NAT裝置的後面.實際中這兩個埠是預測得到的,預測過程如圖3:
 
 
3.2第二種情況
第一種情況依賴與自由設定路由,但是現在很多路由器大多都限制這樣做,並且會丟棄這樣的服務請求包。因此在實際應用中,這種方案失敗的可能性很大。如果自由設定路由不可行,我們可以通過out-of-band通道(他們預先與X連線好的TCP連線)來傳送原本必須將資料包路由到X才能看到的包。注意在圖二的第二步X已經知道了TCP的序號Q和P,因為X已經收到了這兩個SYN包,但是如果資料包沒有路由經過X就不可能收到它們。為了初始化這個連線,兩個主機發送初始SYN包,並且他們都知道是不可能到達目標的,但是它們都可以記住自己的SYN號(個人看法,通過鉤子獲得傳送的資料SYN包)並且可以傳送給X,X得到了它們的SYN包,就可以欺騙它們傳送ACK包了。有兩種方法可以傳送無法到達目標的資料包。簡單的方法就是每個主機發送一個SYN給對方,要求應答包不會到達內部網路.如果NAT(防火牆)會將應答包傳回給內部網路,通常是傳送TCP的reset包(RST),如果NAT生成RST包,A和B就不能簡單地傳送一個向圖2中SYN給彼此,因為如果這樣NA和NB就無法打洞了呵呵,如果NAT不傳送RST包,那麼這個TCP連線就不會被中斷。另外一個傳送無法到達目標網路的SYN包的方法是減小TTL值,使它們無法彼此到達。如果使用者無法配置防火牆丟棄這個ICMP應答包,或者NAT不繼續傳送這個ICMP,這個TCP就不會立即關閉。這個解決方案不能使用一種簡單的欺騙,因為我們必須保證源地址的SYN包傳送者不會沒有收到ICMP的RST包,否則會導致中間裝置建立錯誤路由.僅僅依靠SYN包,NAT就可以建立從internet IP和埠到外部IP和埠的路由.由於欺騙的SYN包是錯誤的源IP(並非發出者X),這個路由將不會發送到X而是傳送到NA或者NB。另外,這種方案都需要設定TTL到足夠小,以便於對方的NAT不會收到到各自發出的初始SYN包,否則就無法完成打洞。(圖4)
 
 
 

3.3第三種情況
比前兩種簡單,但是X將無法預見NA或者NB的埠。B將先給X傳送一個SYN包告訴以便於X知道它所選用的埠號,然後X將這個資訊傳送到A,A就可以向這個確定的地址和埠傳送SYN,圖5是第一種情況的變形::
1)X向圖3一樣預測埠,但是它不能預測到NA的下一個埠號,但是可以預測NB的下一個埠號是5000,並且可以通知A和B這個節點已經建立了連線;
2)A和B同步節點X;
3)X可以欺騙A和B;
4)A和B相互發送ACK;
5)X丟棄發給它的ACK,因為它已經可以確認它們已經建立連線。