NAT穿透

1. NAT簡介
NAT(Network Address Translation)又稱"網絡地址轉換",它是一種把內部私有網絡地址翻譯成合法網絡IP地址的技術
簡單地說,NAT就是在局域網內部使用私有地址(前面文章說到過每類IP地址都有私有地址),而當內部節點要與外部網絡進行通訊時,就在網關(可以理解爲 出口)處將內部地址替換成公用地址,從而在公網(internet)上正常使用.通過這種方法,您可以只申請一個合法IP地址,就把整個局域網中的計算機 接入Internet中.

2. NAT類型

NAT有三種類型:靜態NAT(Static NAT),動態地址NAT(Pooled NAT),網絡地址端口轉換NAPT.

靜態NAT:內部網絡中的每個主機都被永久映射成外部網絡中的某個合法的地址.
動態地址NAT:則是在外部 網絡中定 義了一系列的合法地址,採用動態分配的方法映射到內部網絡.
NAPT:則是把內部地址映射到外部網絡的一個IP地址的不同端口上.

網絡地址端口轉換NAPT(Network Address Port Translation)是人們比較熟悉的一種轉換方式,NAPT普遍應用於接入設備中,它可以將中小型的網絡隱藏在一個合法的IP地址後面.這個優點在 小型辦公室內非常實用,通過從ISP處申請的一個IP地址,將多個連接通過NAPT接入Internet.NAPT類型:
NAPT主要分兩大類:錐形NAT和對稱NAT.

錐形NAT類型:
錐形NAT在根據如何能創建端到端有效連接上有更多的分類.這個分類一般應用在Udp通信(而不是Tcp通信上),因爲NATs和防火牆阻止了試圖無條件傳入的TCP連接,除非明確設置NAT不這樣做.這些分類如下:完全錐形(Full Cone):
在一個完全錐形NAT中,任務主機A都可以通過主機B映射在NAT上的外部地址發包給內部主機B.(A可在公網也可在內網,以下也是).
受限錐形(Restricted Cone):
在一個受限錐形NAT,只有內部主機B向其發送過包的主機A纔可以對這個內部主機B發包(通過主機B映射在NAT上的外部地址).
端口受限錐形(Port Restricted Cone):
端口受限錐形NAT與受限錐形NAT類似,但是增加了對端口的限制,如內部主機B向一個端口爲q的主機A發送過包,那麼只有從這個主機A的端口q發出的包才能被髮送到內部主機B上去。
對稱NAT(Symmetric NAT):
對稱NAT,與Cone NAT是大不相同的,並不對會話進行端口綁定,而是分配一個全新的公網端口給每一個新的會話.

如果Client A同時發起兩個會話到S1和S2,對稱NAT會分配公共地址155.99.25.11:62000給Session1,然後分配另一個不同的公共地址 155.99.25.11:62001給Session2.對稱NAT能夠區別兩個不同的會話並進行地址轉換,應用程序每發出一個會話都會使用一個新的端 口.

3.NAT工作機制NAPT映射條目:記錄上面所說的轉化IP地址和端口的條目.
NAPT映射表:NAPT映射條目的集合.
NAPT帶來的後果:路由器只能根據動態建立的NAPT表來轉發來自外網的數據包,外網不能主動建立到內網的連接.
虛擬服務器和UPNP等轉發設置就是爲了解決這個問題而產生的,人爲(虛擬服務器)或者通過軟件(UPNP組件)打開一條到內網的通道.NAPT使得一組主機可以共享唯一的外部地址,當位於內部網絡中的主機通過NAT設備向外部主機發起會話請求時,
NAT設備就會查詢NAT表,看是否有相關會話記錄,如果有相關記錄,就會將內部IP地址及端口同時進行轉換,再轉發出去;
如果沒有相關記錄,進行IP地址和端口轉換的同時,還會在NAT表增加一條該會話的記錄。外部主機接收到數據包後,
用接受到的合法公網地址及端口作爲目的IP地址及端口來響應,NAT設備接收到外部回來的數據包,再根據NAT表中的記錄把目的地址及端口轉換成對應的內部IP地址及端口,
轉發給該內部主機。4. NAPT具體類型檢測 
前提條件:有一個公網的Server並且綁定了兩個公網IP(IP-1,IP-2).這個Server做UDP監聽(IP-1,Port-1),(IP-2,Port-2)並根據客戶端的要求進行應答.

第一步:檢測客戶端是否有能力進行UDP通信以及客戶端是否位於NAT後,是否有firewall?

第二步:檢測客戶端NAT是否是Full Cone NAT?
第三步:檢測客戶端NAT是否是Symmetric NAT?
第四步:檢測客戶端NAT是否是Restricted Cone NAT還是Port Restricted Cone NAT?

5. NAT穿透
5.1 轉發     
最可靠但又是最低 效的點對點通信方法,莫過於將p2p網絡通信看作一個C/S結構,通過服務器來轉發信息.如下圖,兩個客戶端A和B,均與服務器S初始化了一個TCP或 UDP連接,服務器S具有公網固定IP地址,兩個客戶端分佈在不同的私網中,這樣,他們各自的NAT代理服務器將不允許他們進行直連.
                                    Server S
                                          |
                                          |
            +----------------------+----------------------+
            |                                                           |
          NAT A                                                 NAT B
            |                                                           |
            |                                                           |
         Client A                                              Client B
         
取而代之的方式是,兩個客戶端可以把服務器S當作信使來轉發消息.比如,爲了將消息發送到B,A先發送一條信息給服務器S,服務器S再利用初始化時已經建立的連接,將信息轉發給B. 

這個方法的優勢是:它適合於任何NAT包括Symmetric NAT.但是它的劣勢也很明顯:它將全面依賴並消耗服務器的資源和網絡帶寬.名爲 TURN 的協議定義了一個利用轉發技術進行可靠通信的模型.          
5.2 反向連接 
這裏介紹第二種技術,但是它只能在通信的兩端只有一端處於NAT之後的情況下.舉例來說,假設客戶端A處於NAT之後,而客戶端B有一個公網IP地址,如下圖所示.        
   
                                   Server S
                            18.181.0.31:1235
                                         |
                                         |
            +----------------------+----------------------+
            |                                                           |
          NAT A                                                     |
    155.99.25.11:62000                                    |
            |                                                           |
            |                                                           |
         Client A                                              Client B
      10.0.0.1:1234                               138.76.29.7:1234


現在我們假設客戶端B將會與客戶端A初始化一個端對端連接會話.B將首先試圖連接A的一個地址---客戶端A認爲是它自己的地址 10.0.0.1:1234或者是從服務器S觀察到的地址155.99.25.11:62000.然而不論是連接哪一個,都不可能成功.第一種情況:試圖 直接連到10.0.0.1肯定會失敗,因爲10.0.0.1根本就不是一個可以在公網上路由的IP地址;第二種情況,從B傳來的請求將能夠到達端口NAT A的端口62000,但NAT A卻會拒絕這個連接請求,因爲只有外出的連接才允許進入.    在所有的嘗試都失敗之後,客戶端B就只能通過服務器S來請求A做一個"反向"連接到客戶端B,客戶端A將打開一個與客戶端B通訊的連接(在B的公網IP地 址和端口號上).NAT A允許這個連接通過,因爲這個連接起源於NAT A的內部,並且同時客戶端B能夠受這個連接因爲B並不位於NAT之後. 

這個方法的優勢是:它也適合於任何NAT包括Symmetric NAT.它的主要限制在於,只能有一端位於NAT之後.

5.3 UDP打洞
第三種技術,也是這篇文章主要要介紹的,就是非常有名的"UDP打洞技術".這裏將 考慮兩種典型場景,來介紹連接的雙方應用程序如何按照計劃的進行通信的,第一種場景,我們假設兩個客戶端都處於不同的NAT之後;第二種場景,我們假設兩 個客戶端處於同一個NAT之後,但是它們彼此都不知道(他們在同一個NAT中).

5.3.1 處於不同NAT之後的客戶端通信
我們假設 Client A和Client B都擁有自己的私有IP地址,並且都處在不同的NAT之後,端對端的程序運行於 CLIENT A,CLIENT B,S之間,並且它們都開放了UDP端口1234. CLIENT A和CLIENT B首先分別與S建立通信會話,這時NAT A把它自己的UDP端口62000分配給CLIENT A與S的會話,NAT B也把自己的UDP端口31000分配給CLIENT B與S的會話.如下圖所示:
                                Server S
                            18.181.0.31:1234
                                   |
                                   |
            +----------------------+----------------------+
            |                                                           |
          NAT A                                                 NAT B
    155.99.25.11:62000                            138.76.29.7:31000
            |                                                           |
            |                                                           |
         Client A                                              Client B
      10.0.0.1:1234                                 10.1.1.3:1234
     

假如這個時候 CLIENT A 想與 CLIENT B建立一條UDP通信直連,如果 CLIENT A只是簡單的發送一個UDP信息到CLIENT B的公網地址
138.76.29.7:31000 的話,NAT B會不加考慮的將這個信息丟棄(除非NAT B是一個 full cone NAT),因爲這個UDP信息中所包含的地址信息,與CLIENT B和服務器S建立連接時存儲在NAT B中的服務器S的地址信息不符.同樣的,CLIENT B如果做同樣的事情,發送的UDP信息也會被NAT A丟棄.

假如 CLIENT A 開始發送一個UDP信息到CLIENT B的公網地址上,與此同時,他又通過S中轉發送了一個邀請信息給CLIENT B,請求CLIENT B也給CLIENT A發送一個UDP信息到 CLIENT A的公網地址上.這時CLIENT A向CLIENT B的公網IP(138.76.29.7:31000)發送的信息導致 NAT A 打開一個處於CLIENT A的私有地址和CLIENT B的公網地址之間的新的通信會話,與此同時NAT B也打開了一個處於CLIENT B的私有地址和CLIENT A的公網地址(155.99.25.11:62000)之間的新的通信會話.一旦這個新的UDP會話各自向對方打開了,CLIENT A和CLIENT B之間就可以直接通信,而無需S來牽線搭橋了.這就是所謂的打洞技術.
一旦這種處於NAT之後的端對端的直連建立之後,連接的雙方可以輪流擔任對方的"媒人",把對方介紹給其他的客戶端,這樣就極大的降低了服務器S的工作量.

5.3.2 處於相同NAT之後的客戶端通信
我們假設 Client A和Client B都擁有自己的私有IP地址,並且都處在相同的NAT之後,端對端的程序運行於 CLIENT A,CLIENT B,S之間,
CLIENT A和CLIENT B分別與S建立通信會話,經過NAT轉換後,A的公網端口被映射爲62000,B的公網端口映射爲62001.如下圖所示:

                               Server S
                            18.181.0.31:1234
                                   |
                                   |
                                  NAT
                         A-S 155.99.25.11:62000
                         B-S 155.99.25.11:62001
                                   |
            +----------------------+----------------------+
            |                                                           |
         Client A                                              Client B
      10.0.0.1:1234                                 10.1.1.3:1234
      
根 據前面介紹的"打洞"技術,CLIENT A將發送一個UDP信息到CLIENT B的公網地址上,數據包源端爲(10.0.0.1:124),目的端爲(155.99.25.11:62001).該數據包能否被B收到,取決於當前的 NAT是否支持"髮夾"轉換(hairpin轉換,也就是同一臺設備不同端口之間的UDP數據包能否到達).
首先,支持"髮夾"轉換的NAT設備還遠沒有支持"打洞"技術的NAT設備多,其次,即使NAT設備支持"髮夾"轉換,在這種情況下也應該通過網內端到端實現,而不是將數據包無謂 地經過NAT設備,這是一種對資源的浪費.

5.3.3 一般"打洞"過程
綜合上面介紹的客戶端處於不同NAT之後和處於同一NAT之後,我們說下一般的"打洞"過程.
1. 打洞技術假定客戶端A和B可以與公網內的已知的集中服務器建立UDP連接(可以互發UDP數據包).當一個客戶端在S上登陸的時候,服務器記錄下該客戶端 的兩個endpoints(IP地址,UDP端口),一個是該客戶端確信自己是通過該ip和端口與服務器S進行通信的,另一個是服務器S記錄下的由服務 器"觀察"到的該客戶端實際與自己通信所使用的ip和端口.我們可以把前一個endpoint看作是客戶端的內網ip和端口,把後一個endpoint看 作是客戶端的內網ip和端口經過NAT轉換後的公網ip和端口.服務器可以從客戶端的登陸消息的消息體中得到該客戶端的內網endpoint相關信息,可 以通過對登陸消息的IP或UDP頭得到該客戶端的公網endpoint.
2.假設Client A想向B發起連接,於是A向服務器S發送消息,請求S幫助建立與B的UDP連接.這時,S將B的公網和內網的endpoint發給A.可知,A與B通過與S的一次通信就可以知道對方的公網和內網的endpoint.     
3.Client A通過B的內網endpoint發送UDP數據包.針對5.3.2節問題的解決方案.如果B和A在同一NAT後,則很快收到響應.如果B和A不在同一NAT後,則超時.
4.Client A通過B的外網endpoint發送UDP數據包.
回 到5.3.1節介紹的具體方法.CLIENT A發出UDP包(10.0.0.1:1234,138.76.29.7:31000),經NAT A轉換爲(155.99.25.11:62000,138.76.29.7:31000),經NAT B轉換爲(155.99.25.11:62000,10.1.1.3:1234).如果在此數據包到達NAT B前,B發送過UDP包到A的公網endpoint,則NAT B允許此包到達B機.在5.3.2節下,A發出UDP包(10.0.0.1:1234,155.99.25.11:62001),NAT 先轉換爲(155.99.25.11:62000,155.99.25.11:62001),再轉換爲 (155.99.25.11:62000,10.1.1.3:1234).
5.步驟3,4發送的數據包是爲了"打洞",打洞成功後,就進入真正的P2P傳輸了.
還有一種情況,B和A不在同一NAT後,C和A在同一NAT後,且B和C的內網endpoint一致,這個時候A從S拿到的目的端應該有2個.所以針對3,4步驟取先有迴應的目的端不可取,應該先做步驟3,有迴應直接到步驟5,沒有迴應到步驟4.

5.3.4 客戶端分別處於多層NAT之後
在有些網絡拓撲中就存在多層NAT設備,讓我們來看看下圖這種情況:
假如 NAT X 是由 Internet服務供應商(ISP)配置的一個大型NAT,它使用少量的公網IP地址來爲一些客戶羣提供服務,NAT A和NAT B則是
爲ISP的兩個客戶羣所配置的小一點的獨立NAT網關,它們爲各自客戶羣的私人家庭網絡提供IP地址.只有Server S和NAT X擁有公網固定IP地址,而NAT A 和 NAT B所擁有的"公網"IP地址對於ISP的尋址域來說則實際上"私有"的.
                                Server S
                            18.181.0.31:1234
                                   |
                                   |
                                 NAT X
                         A-S 155.99.25.11:62000
                         B-S 155.99.25.11:62001
                                   |
                                   |
            +----------------------+----------------------+
            |                                                           |
          NAT A                                                 NAT B
    192.168.1.1:30000                             192.168.1.2:31000
            |                                                           |
            |                                                           |
         Client A                                              Client B
      10.0.0.1:1234                                 10.1.1.3:1234
現在讓我們假設Client A和Client B想要建立一條端對端 的UDP直連.Client A和 Client B只知道Server S記錄的他們真正的公網地址
155.99.25.11:62000和155.99.25.11:62001,而且他們只能通過這個公網地址建立連接,即NAT X必須得支持"loopback translation"(也稱hairpin轉換)才行.

Client A和 Client B也知道對方內網地址10.0.0.1:1234和10.1.1.3:1234,毫無疑問,通過內網地址是建立不了連接的.Client A和 Client B並不知道對方NAT B和NAT A的地址192.168.1.2:31000和192.168.1.1:30000,即便假設我們通過某種途徑得知了這些地址,還是不能夠保證這樣就能進 行通話了,因爲這些地址是由ISP的私有尋址域分配的,可能會與私有域所分配的其他無關客戶端地址相沖突.

5.3.5 UDP在空閒狀態下的超時問題
由於UDP轉換協議提供的"洞"不是絕對可靠的,多數NAT 設備內部都有一個UDP轉換的空閒狀態計時器,如果在一段時間內沒有UDP數據通信,NAT設備會關掉由"打洞"操作打出來的"洞",做爲應用程序來講如 果想要做到與設備無關,就最好在穿越NAT的以後設定一個穿越的有效期.很遺憾目前沒有標準有效期,這個有效期與NAT設備內部的配置有關,最短的只有 20秒左右.在這個有效期內,即使沒有p2p數據包需要傳輸,應用程序爲了維持該"洞"可以正常工作,也必須向對方發送"打洞"維持包.這個維持包是需要 雙方應用都發送的,只有一方發送不會維持另一方的session正常工作.除了頻繁發送"打洞"維持包以外,還有一個方法就是在當前的"洞"有效期過期之 前,p2p客戶端雙方重新"打洞",丟棄原有的"洞",這也不失爲一個有效的方法.

5.4 . UPD端口號預言
在使用"UDP打洞技術"時有一點必須要注意:它只能在雙方的NAT都是cone NAT時才能正常工作.這些NAT在使用時保持着端口的
綁定----[私有IP,私有UDP端口]對和[公網IP,公網UDP端口]對的一一對應.
如果像 symmetricNAT那樣給每個新的會話都分配一個新的公網端口,那麼UDP應用程序想要與其他外部客戶端進行通話,就無法重複使用已經建立好的通信轉換.

讓我們來考慮這樣一種情況,有兩個客戶端A和B,他們都藏在不同的Symmetric NAT後面,他們都開放了一個UDP連接給具有固定IP的Server S.如下圖:
                                  Server S
                              18.181.0.31:1234
                                     |
                                     |
              +----------------------+----------------------+
              |                                                           |
       Symmetric NAT A                               Symmetric NAT B
   A-S 155.99.25.11:62000                        B-S 138.76.29.7:31000
              |                                                           |
              |                                                           |
           Client A                                             Client B
        10.0.0.1:1234                                 10.1.1.3:1234
        
NAT A 分配了它自己的UDP端口62000,用來保持客戶端A與服務器S的通信會話,NAT B 也分配了31000端口,用來保持客戶端B與 
服務器S的通信會話.通過與服務器S的對話,客戶端A和客戶端B都相互知道了對方所映射的真實IP和端口.
客戶端A發送一條UDP消息到 138.76.29.7:31001(請注意到端口號的增加),同時客戶端B發送一條UDP消息到 155.99.25.11:62001.
如果NAT A 和NAT B繼續分配端口給新的會話,並且從A-S和B-S的會話時間消耗得並不多的話,那麼一條處於客戶端A和客戶端B之間的雙向會話通道就建立了. 
客戶端A發出的消息送達B導致了NAT A打開了一個新的會話,並且我們希望 NAT A將會指派62001端口給這個新的會話,因爲62001是
繼62000後,NAT會自動指派給 從服務器S到客戶端A之間的新會話的端口號;類似的,客戶端B發出的消息送達A導致了NAT B打開了
一個新的會話,並且我們希望 NAT B將會指派31001這個端口給新的會話;如果兩個客戶端都正確的猜測到了對方新會話被指派的端口號,
那麼這個客戶端A----客戶端B的雙向連接就被打通了.其結果如下圖所示:
                                  Server S
                              18.181.0.31:1234
                                     |
                                     |
              +----------------------+----------------------+
              |                                                           |
            NAT A                                                  NAT B
   A-S 155.99.25.11:62000                        B-S 138.76.29.7:31000
   A-B 155.99.25.11:62001                        B-A 138.76.29.7:31001
              |                                                           |
              |                                                           |
           Client A                                              Client B
        10.0.0.1:1234                                 10.1.1.3:1234
明 顯的,有許多因素會導致這個方法失敗:如果這個預言的新端口(62001和31001) 恰好已經被一個不相關的會話所使用,那麼NAT就會跳過這個端口號,這個連接就會宣告失敗;如果兩個NAT有時或者總是不按照順序來生成新的端口號,那麼 這個方法也是行不通的.如果隱藏在NAT A後的一個不同的客戶端X(或者在NAT B後)打開了一個新的"外出"UDP 連接,並且無論這個連接的目的如何,只要這個動作發生在客戶端A建立了與服務器S的連接之後,客戶端A與客戶端B建立連接之前,那麼這個無關的客戶端X 就會趁人不備地"偷"到這個我們渴望分配的端口.所以,這個方法變得如此脆弱而且不堪一擊,只要任何一個NAT方包含以上碰到的問題,這個方法都不會奏 效. 
 最後,如果P2P的一方處在兩級或者兩級以上的NAT下面,並且這些NAT接近這個客戶端是symmetric的話,端口號預言是無效的.
因此,並不推薦使用這個方法來寫新的P2P應用程序. 


5.5 同時開放TCP連接

這裏有一種方法能夠在某種情況下建立一個穿透NAT的端對端TCP直連.我們知道,絕大多數的TCP會話的建立,都是通過一端先發送一個SYN包開 始,另一方則回發一個SYN-ACK包的過程.然而,這裏確實存在另外一種情況,就是P2P的雙方各自同時地發出一個SYN包到對方的公網地址上,然後各 自都單獨地返回一個ACK響應來建立一個TCP會話.這個過程被稱之爲"Simultaneous open"(同時開放連接).

如果一個NAT接收到一個來自私有網絡外面的TCP SYN包,這個包想發起一個"引入"的TCP連接,一般來說,NAT會拒絕這個連接請求並扔掉這個SYN 包,或者回送一個TCP RST(connection reset,重建連接)包給請求方.但是,有一種情況,當這個接收到的SYN包中的源IP地址和端口,目標IP地址和端口都與NAT登記的一個已經激活的 TCP會話中的地址信息相符時,NAT將會放行這個SYN 包,讓它進入NAT內部.特別要指出,如果NAT恰好看到一個剛剛發送出去的一個SYN包也和上面接收到的SYN包中的地址信息相符合的話,那麼NAT將 會認爲這個TCP連接已經被激活,並將允許這個方向的SYN包進入NAT內部.

如果Client A和Client B能夠彼此正確的預知對方的NAT將會給下一個TCP連接分配的公網TCP端口,並且兩個客戶端能夠同時地發起一個"外出"的TCP連接,並在對方的 SYN包到達之前,自己剛發送出去的SYN包都能順利的穿過自己的NAT的話,一條端對端的TCP連接就成功地建立了.
不幸的是,這個詭計比5.4節所講的UDP端口預言更容易被粉碎,並且對時間的敏感性的依賴更多.

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章