天天看點

P2P之UDP穿透NAT的原理與實作 - 增強篇

NAT(The IP Network Address Translator) 的概念和意義是什麼?

内網IP位址: 是指使用A/B/C類中的私有位址, 配置設定的IP位址在全球不懼有唯一性,也是以無法被其它外網主機直接通路。

公網IP位址: 是指具有全球唯一的IP位址,能夠直接被其它主機通路的。

NAT 最初的目的是為使用内網IP位址的計算機提供通過少數幾台具有公網的IP位址的計算機通路外部網絡的功能。NAT 負責将某些内網IP位址的計算機向外部網絡發出的IP資料包的源IP位址轉換為NAT自己的公網的IP位址,目的IP位址不變, 并将IP資料包轉發給路由器,最終到達外部的計算機。同時負責将外部的計算機傳回的IP資料包的目的IP位址轉換為内網的IP位址,源IP位址不變,并最終送達到内網中的計算機。

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

        | 192.168.0.5        |  Internat host            | 192.168.0.6        |  Internat host

                ^ port:2809                                      ^port: 1827                            

                |                                                |                            

                V                                                V                            

        | 192.168.0.1        | NAT device                | 192.168.0.2        | NAT device    

        | 61.51.99.86        |                           | 61.51.77.66        |               

                ^                                                ^                            

                V port:80                                        V port: 80                           

        | 61.51.202.88       | Internet host             | 61.51.76.102       | Internet host 

                              圖一: NAT 實作了私有IP的計算機分享幾個公網IP位址通路Internet的功能。

随着網絡的普及,IPv4的局限性暴露出來。公網IP位址成為一種稀缺的資源,此時NAT 的功能局限也暴露出來,同一個公網的IP位址,某個時間隻能由一台私有IP位址的計算機使用。于是NAPT(The IP Network Address/Port Translator)應運而生,NAPT實作了多台私有IP位址的計算機可以同時通過一個公網IP位址來通路Internet的功能。這在很大程度上暫時緩解了IPv4位址資源的緊張。

NAPT 負責将某些内網IP位址的計算機向外部網絡發出的TCP/UDP資料包的源IP位址轉換為NAPT自己的公網的IP位址,源端口轉為NAPT自己的一個端口。目的IP位址和端口不變, 并将IP資料包發給路由器,最終到達外部的計算機。同時負責将外部的計算機傳回的IP資料包的目的IP位址轉換内網的IP位址,目的端口轉為内網計算機的端口,源IP位址和源端口不變,并最終送達到内網中的計算機。

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

                | 192.168.0.5        |  Internat host            | 192.168.0.6        |  Internat host

                        port: 2809      ^                   ^ port: 1827

                                         \                 /

                                          v               v                             

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

                                        | 192.168.0.1        | NAT device 

                                        | 61.51.99.86        |            

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

        map port:9882 to 192.168.0.5:2809 ^              ^ map port: 9881 to 192.168.0.6:1827

                                         /                \

                             port:80    v                  v    port:80                         

                | 61.51.202.88       | Internet host             | 61.51.76.102       | Internet host 

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

                              圖二: NAPT 實作了私有IP的計算機分享一個公網IP位址通路Internet的功能。                                             

在我們的工作和生活中, NAPT的作用随處可見,絕大部分公司的網絡架構,都是通過1至N台支援NAPT的路由器來實作公司的所有計算機連接配接外部的Internet網絡的。包括本人在寫這篇文章的時候,也是在家中使用一台IBM筆記本通過一台寬帶連接配接的桌上型電腦來通路Internet的。我們本篇文章主要讨論的NAPT的問題。

NAPT(The IP Network Address/Port Translator) 為何阻礙了P2P軟體的應用?

通過NAPT 上網的特點決定了隻能由NAPT内的計算機主動向NAPT外部的主機發起連接配接,外部的主機想直接和NAPT内的計算機直接建立連接配接是不被允許的。IM(即時通訊)而言,這意味着由于NAPT内的計算機和NAPT外的計算機隻能通過伺服器中轉資料來進行通訊。對于P2P方式的下載下傳程式而言,意味着NAPT内的計算機不能接收到NAPT外部的連接配接,導緻連接配接數用過少,下載下傳速度很難上去。是以P2P軟體必須要解決的一個問題就是要能夠在一定的程度上解決NAPT内的計算機不能被外部連接配接的問題。

NAT(The IP Network Address Translator) 進行UDP穿透的原理是什麼?

TCP/IP傳輸時主要用到TCP和UDP協定。TCP協定是可靠的,面向連接配接的傳輸協定。UDP是不可靠的,無連接配接的協定。根據TCP和UDP協定的實作原理,對于NAPT來進行穿透,主要是指的UDP協定。TCP協定也有可能,但是可行性非常小,要求更高,我們此處不作讨論,如果感興趣可以到Google上搜尋,有些文章對這個問題做了探讨性的描述。下面我們來看看利用UDP協定來穿透NAPT的原理是什麼:

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

                        | 192.168.0.5        |  Internat host            | 192.168.0.6        |  Internat host

                          UDP port: 2809        ^                   ^ UDP port: 1827

                                                 \                 /

                                                  v               v                             

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

                                                | 192.168.0.1        | NAT device 

                                                | 61.51.99.86        |            

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

  Session(192.168.0.6:1827 <-> 61.51.76.102:8098) ^              ^ Session(192.168.0.6:1827 <-> 61.51.76.102:8098)

               map port:9882 to 192.168.0.5:2809 /                \map port: 9881 to 192.168.0.6:1827

                                  UDP port:8098 v                  v    UDP port:8098                           

                        | 61.51.202.88       | Internet host             | 61.51.76.102       | Internet host 

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

                                      圖三: NAPT 是如何将私有IP位址的UDP資料包與公網主機進行透明傳輸的。

UDP協定包經NAPT透明傳輸的說明:

NAPT為每一個Session配置設定一個NAPT自己的端口号,依據此端口号來判斷将收到的公網IP主機傳回的TCP/IP資料包轉發給那台内網IP位址的計算機。在這裡Session是虛拟的,UDP通訊并不需要建立連接配接,但是對于NAPT而言,的确要有一個Session的概念存在。NAPT對于UDP協定包的透明傳輸面臨的一個重要的問題就是如何處理這個虛拟的Session。我們都知道TCP連接配接的Session以SYN包開始,以FIN包結束,NAPT可以很容易的擷取到TCP Session的生命周期,并進行處理。但是對于UDP而言,就麻煩了,NAPT并不知道轉發出去的UDP協定包是否到達了目的主機,也沒有辦法知道。而且鑒于UDP協定的特點,可靠很差,是以NAPT必須強制維持Session的存在,以便等待将外部送回來的資料并轉發給曾經發起請求的内網IP位址的計算機。NAPT具體如何處理UDP Session的逾時呢?不同的廠商提供的裝置對于NAPT的實作不近相同,也許幾分鐘,也許幾個小時,些NAPT的實作還會根據裝置的忙碌狀态進行智能計算逾時時間的長短。

                  [192.168.0.6:1827]

                            | UDP Packet[src ip:192.168.0.6 src port:1827 dst ip:61.51.76.102 dst port 8098]

                            v

        [pub ip: 61.51.99.86]NAT[priv ip: 192.168.0.1]

                            | UDP Packet[src ip:61.51.99.86 src port:9881 dst ip:61.51.76.102 dst port 8098]

                            v                   

                  [61.51.76.102:8098]

                                    圖四: NAPT 将内部發出的UDP協定包的源位址和源端口改變傳輸給公網IP主機。

                            ^

                            | UDP Packet[src ip:61.51.76.102 src port:8098 dst ip:192.168.0.6 dst port 1827]

                            ^   

                            | UDP Packet[src ip:61.51.76.102 src port:8098 dst ip:61.51.99.86 dst port 9881]    

                                    圖五: NAPT 将收到的公網IP主機傳回的UDP協定包的目的位址和目的端口改變傳輸給内網IP計算機。                                

現在我們大概明白了NAPT如何實作内網計算機和外網主機間的透明通訊。現在來看一下我們最關心的問題,就是NAPT是依據什麼政策來判斷是否要為一個請求發出的UDP資料包建立Session的呢?主要有一下幾個政策:

A. 源位址(内網IP位址)不同,忽略其它因素, 在NAPT上肯定對應不同的Session

B. 源位址(内網IP位址)相同,源端口不同,忽略其它的因素,則在NAPT上也肯定對應不同的Session

C. 源位址(内網IP位址)相同,源端口相同,目的位址(公網IP位址)相同,目的端口不同,則在NAPT上肯定對應同一個Session

D. 源位址(内網IP位址)相同,源端口相同,目的位址(公網IP位址)不同,忽略目的端口,則在NAPT上是如何處理Session的呢?

D的情況正式我們關心和要讨論的問題。依據目的位址(公網IP位址)對于Session的建立的決定方式我們将NAPT裝置劃分為兩大類:

Symmetric NAPT:

對于到同一個IP位址,任意端口的連接配接配置設定使用同一個Session; 對于到不同的IP位址, 任意端口的連接配接使用不同的Session. 

我們稱此種NAPT為 Symmetric NAPT. 也就是隻要本地綁定的UDP端口相同, 發出的目的IP位址不同,則會建立不同的Session.

        [202.223.98.78:9696] [202.223.98.78:9696] [202.223.98.78:9696]

                ^               ^                       ^

                |               |                       |

                v               v                       v

               9883            9882                    9881

                                 |

                             \ [NAT] /

                                 ^

                                 v                        

                          [192.168.0.6:1827]

                          圖六: Symmetric 的英文意思是對稱。多個端口對應多個主機,平行的,對稱的!

Cone NAPT:

對于到同一個IP位址,任意端口的連接配接配置設定使用同一個Session; 對于到不同的IP位址,任意端口的連接配接也使用同一個Session.

我們稱此種NAPT為 Cone NAPT. 也就是隻要本地綁定的UDP端口相同, 發出的目的位址不管是否相同, 都使用同一個Session.

                        ^          ^         ^

                         \         |        /

                          v        v       v

                                 9881

                                 [NAT]

                                   ^

                                   |

                                   v                      

                          圖七: Cone 的英文意思是錐。一個端口對應多個主機,是不是像個錐子?

現在絕大多數的NAPT屬于後者,即Cone NAT。本人在測試的過程中,隻好使用了一台日本的Symmetric NAT。還好不是自己的買的,我從不買日貨, 希望看這篇文章的朋友也自覺的不要購買日本的東西。Win9x/2K/XP/2003系統自帶的NAPT也是屬于 Cone NAT的。這是值的慶幸的,因為我們要做的UDP穿透隻能在Cone NAT間進行,隻要有一台不是Cone NAT,對不起,UDP穿透沒有希望了,伺服器轉發吧。後面會做詳細分析!

下面我們再來分析一下NAPT 工作時的一些資料結構,在這裡我們将真正說明UDP可以穿透Cone NAT的依據。這裡描述的資料結構隻是為了說明原理,不具有實際參考價值,真正感興趣可以閱讀Linux的中關于NAT實作部分的源碼。真正的NAT實作也沒有利用資料庫的,呵呵,為了速度!

Symmetric NAPT 工作時的端口映射資料結構如下:

内網資訊表:

[NAPT 配置設定端口] [ 内網IP位址 ] [ 内網端口 ] [ 外網IP位址 ] [ SessionTime 開始時間 ]

PRIMARY KEY( [NAPT 配置設定端口] ) -> 表示依據[NAPT 配置設定端口]建立主鍵,必須唯一且建立索引,加快查找.

UNIQUE( [ 内網IP位址 ], [ 内網端口 ] ) -> 表示這兩個字段聯合起來不能重複.

UNIQUE( [ 内網IP位址 ], [ 内網端口 ], [ 外網IP位址 ] ) -> 表示這三個字段聯合起來不能重複.

映射表:

[NAPT 配置設定端口] [ 外網端口 ]

UNIQUE( [NAPT 配置設定端口], [ 外網端口 ] ) -> 表示這兩個字段聯合起來不能重複.

Cone NAPT 工作時的端口映射資料結構如下:

[NAPT 配置設定端口] [ 内網IP位址 ] [ 内網端口 ] [ SessionTime 開始時間 ]

外網資訊表:

[ wid 主鍵辨別 ] [ 外網IP位址 ] [ 外網端口 ]

PRIMARY KEY( [ wid 主鍵辨別 ] ) -> 表示依據[ wid 主鍵辨別 ]建立主鍵,必須唯一且建立索引,加快查找.

UNIQUE( [ 外網IP位址 ], [ 外網端口 ] ) -> 表示這兩個字段聯合起來不能重複.

映射表: 實作一對多,的

[NAPT 配置設定端口] [ wid 主鍵辨別 ]

UNIQUE( [NAPT 配置設定端口], [ wid 主鍵辨別 ] ) -> 表示這兩個字段聯合起來不能重複.

UNIQUE( [ wid 主鍵辨別 ] ) -> 辨別此字段不能重複.

看完了上面的資料結構是更明白了還是更暈了? 呵呵! 多想一會兒就會明白了。通過NAT,内網計算機計算機向外連結是很容易的,NAPT會自動處理,我們的應用程式根本不必關心它是如何處理的。那麼外部的計算機想通路内網中的計算機如何實作呢?我們來看一下下面的流程:

c 是一台在NAPT後面的内網計算機,s是一台有外網IP位址的計算機。c 主動向 s 發起連接配接請求,NAPT依據上面描述的規則在自己的資料結構中記錄下來,建立一個Session. 然後 c 和 s 之間就可以實作雙向的透明的資料傳輸了。如下面所示:

   c[192.168.0.6:1827] <-> [priv ip: 192.168.0.1]NAPT[pub ip: 61.51.99.86:9881] <-> s[61.51.76.102:8098]

由此可見,一台外網IP位址的計算機想和NAPT後面的内網計算機通訊的條件就是要求NAPT後面的内網計算機主動向外網IP位址的計算機發起一個UDP資料包。外網IP位址的計算機利用收到的UDP資料包擷取到NAPT的外網IP位址和映射的端口,以後就可以和内網IP的計算機透明的進行通訊了。

現在我們再來分析一下我們最關心的兩個NAPT後面的内網計算機如何實作直接通訊呢? 兩者都無法主動發出連接配接請求,誰也不知道對方的NAPT的公網IP位址和NAPT上面映射的端口号。是以我們要靠一個公網IP位址的伺服器幫助兩者來建立連接配接。當兩個NAPT後面的内網計算機分别連接配接了公網IP位址的伺服器後,伺服器可以從收到的UDP資料包中擷取到這兩個NAPT裝置的公網IP位址和這兩個連接配接建立的Session的映射端口。兩個内網計算機可以從伺服器上擷取到對方的NAPT裝置公網IP位址和映射的端口了。

我們假設兩個内網計算機分别為A和B,對應的NAPT分别為AN和BN, 如果A在擷取到B對應的BN的IP位址和映射的端口後,迫不急待的向這個IP

位址和映射的端口發送了個UDP資料包,會有什麼情況發生呢?依據上面的原理和資料結構我們會知道,AN會在自己的資料結構中生成一條記錄,辨別一個新Session的存在。BN在收到資料包後,從自己的資料結構中查詢,沒有找到相關記錄,是以将包丢棄。B是個慢性子,此時才慢吞吞的向着AN的IP位址和映射的端口發送了一個UDP資料包,結果如何呢?當然是我們期望的結構了,AN在收到資料包後,從自己的資料結構中查找到了記錄,是以将資料包進行處理發送給了A。A 再次向B發送資料包時,一切都時暢通無阻了。OK, 大工告成!且慢,這時對于Cone NAPT而言,對于Symmetric NAPT呢?呵呵,自己分析一下吧...

NAPT(The IP Network Address/Port Translator) 進行UDP穿透的具體情況分析!

首先明确的将NAPT裝置按照上面的說明分為: Symmetric NAPT 和 Cone NAPT, Cone NAPT 是我們需要的。Win9x/2K/XP/2003 自帶的NAPT也為Cone NAPT。

第一種情況, 雙方都是Symmetric NAPT:

此情況應給不存在什麼問題,肯定是不支援UDP穿透。

第二種情況, 雙方都是Cone NAPT:

此情況是我們需要的,可以進行UDP穿透。

第三種情況, 一個是Symmetric NAPT, 一個是Cone NAPT:

此情況比較複雜,但我們按照上面的描述和資料機構進行一下分析也很容易就會明白了, 分析如下,

假設: A -> Symmetric NAT, B -> Cone NAT

1. A 想連接配接 B, A 從伺服器那兒擷取到 B 的NAT位址和映射端口, A 通知伺服器,伺服器告知 B A的NAT位址和映射端口, B 向 A 發起連接配接,A 肯定無法接收到。此時 A 向 B 發起連接配接, A 對應的NAT建立了一個新的Session,配置設定了一個新的映射端口, B 的 NAT 接收到UDP包後,在自己的映射表中查詢,無法找到映射項,是以将包丢棄了。

2. B 想連接配接 A, B 從伺服器那兒擷取到 A 的NAT位址和映射端口, B 通知伺服器, 伺服器告知 A B的NAT位址和映射端口,A 向 B 發起連接配接, A 對應的NAT建立了一個新的Session,配置設定了一個新的映射端口B肯定無法接收到。此時 B 向 A 發起連接配接, 由于 B 無法擷取 A 建立的新的Session的映射端口,仍是使用伺服器上擷取的映射端口進行連接配接, 是以 A 的NAT在接收到UDP包後,在自己的映射表中查詢,無法找到映射項, 是以将包丢棄了。

根據以上分析,隻有當連接配接的兩端的NAT都為Cone NAT的情況下,才能進行UDP的内網穿透互聯。

NAPT(The IP Network Address/Port Translator) 進行UDP穿透如何進行現實的驗證和分析!

需要的網絡結構如下:

三個NAT後面的内網機器,兩個外網伺服器。其中兩台Cone NAPT,一台 Symmetric NAPT。

驗證方法:

可以使用本程式提供的源碼,編譯,然後分别運作伺服器程式和用戶端。修改過後的源碼增加了用戶端之間直接通過IP位址和端口發送消息的指令,利用此指令,你可以手動的驗證NAPT的穿透情況。為了友善操作,推薦你使用一個遠端登陸軟體,可以直接在一台機器上操作所有的相關的計算機,這樣很友善,一個人就可以完成所有的工作了。呵呵,本人就是這麼完成的。歡迎有興趣和經驗的朋友來信批評指正,共同進步。

繼續閱讀