1. NAT 簡介
NAT(Network Address Translation ,網路位址轉譯) 是一種廣泛應用的解決IP 短缺的
有效方法, NAT 將內網地址轉和連接埠號碼換成合法的公網地址和連接埠號碼,建立一個會話,與公網主機進行通訊。
1.1. NAT 分類
NAT 從表面上看有三種類型:靜態 NAT 、動態地址 NAT 、地址連接埠轉換 NAPT 。
(1 )靜態NAT :靜態地址轉換將內部私網地址與合法公網地址進行一對一的轉換,且每個內部地址的轉換都是確定的。
(2 )動態NAT :動態地址轉換也是將內部本地地址與內部合法地址一對一的轉換,但是動態地址轉換是從合法位址集區中動態選擇一個未使用的地址來對內部私人地址進行轉換。
(3 )NAPT :它也是一種動態轉換,而且多個內部地址被轉換成同一個合法公網地址,使用不同的連接埠號碼來區分不同的主機,不同的進程。
從實現的技術角度,又可以將NAT 分成如下幾類:全錐NAT(Full Cone NAT) 、限制性錐NAT (Restricted Cone NAT )、連接埠限制性錐NAT( Port Restricted Cone NAT) 、對稱NAT ( Symmetric NAT) 。
(1 )全錐NAT :全錐NAT 把所有來自相同內部IP 位址和連接埠的請求映射到相同的外部IP 位址和連接埠。任何一個外部主機均可通過該映射發送資料包到該內部主機。
(2 )限制性錐NAT :限制性錐NAT 把所有來自相同內部IP 位址和連接埠的請求映射到相同的外部IP 位址和連接埠。但是, 和全錐NAT 不同的是:只有當內部主機先給外部主機發送資料包, 該外部主機才能向該內部主機發送資料包。
(3 )連接埠限制性錐NAT :連接埠限制性錐NAT 與限制性錐NAT 類似, 只是多了連接埠號碼的限制, 即只有內部主機先向外部地址:連接埠號碼對發送資料包, 該外部主機才能使用特定的連接埠號碼向內部主機發送資料包。
(4 )對稱NAT :對稱NAT 與上述3 種類型都不同, 不管是全錐NAT ,限制性錐NAT 還是連接埠限制性錐NAT ,它們都屬於錐NAT (Cone NAT )。當同一內部主機使用相同的連接埠與不同地址的外部主機進行通訊時, 對稱NAT 會重建立立一個Session ,為這個Session 分配不同的連接埠號碼,或許還會改變IP 位址。
1.2. NAT 的作用
NAT 不僅實現地址轉換,同時還起到防火牆的作用,隱藏內部網路的拓撲結構,保護內部主機。 NAT 不僅完美地解決了 lP 地址不足的問題,而且還能夠有效地避免來自網路外部的攻擊,隱藏並保護網路內部的電腦。 這樣對於外部主機來說,內部主機是不可見的。但是,對於P2P 應用來說,卻要求能夠建立端到端的串連,所以如何穿透NAT 也是P2P 技術中的一個關鍵。
2. P2P 穿透NAT
要讓處於NAT 裝置之後的擁有私人IP 位址的主機之間建立P2P 串連,就必須想辦法穿
透NAT ,現在常用的傳輸層協議主要有TCP 和UDP ,下面就是用這兩種協議來介紹穿透NAT 的策略。
2.1. 網路拓撲結構
下面假設有如圖1 所示網路拓撲結構圖。
圖1. 網路拓撲結構圖
Server (129.208.12.38 )是公網上的伺服器,NAT-A 和NAT-B 是兩個NAT 裝置(可能是整合NAT 功能的路由器,防火牆等),它們具有若干個合法公網IP ,在NAT-A 阻隔的私人網路中有若干台主機【ClientA-1 ,ClientA-N 】,在NAT-B 阻隔的私人網路中也有若干台主機【ClientB-1 ,ClientB-N 】。為了以後說明問題方便,只討論主機ClientA-1 和ClientB-1 。
假設主機ClientA-1 和主機ClientB-1 都和伺服器Server 建立了“串連”,如圖2 所示。
圖2.ClientA-1 ,ClientB-1 和Server 之間通訊
由於NAT 的透明性,所以ClientA-1 和ClientB-1 不用關心和Server 通訊的過程,它們只需要知道Server 開放服務的地址和連接埠號碼即可。根據圖1 ,假設在ClientA-1 中有進程使用socket (192.168.0.2 :7000 )和Server 通訊,在ClientB-1 中有進程使用socket (192.168.1.12:8000 )和Server 通訊。它們通過各自的NAT 轉換後分別變成了socket (202.103.142.29 :5000 )和socket (221.10.145.84 :6000 )。
2.2. 使用UDP 穿透NAT
通常情況下,當進程使用UDP 和外部主機通訊時,NAT 會建立一個Session ,這個Session 能夠保留多久並沒有標準,或許幾秒,幾分鐘,幾個小時。假設ClientA-1 在應用程式中看到了ClientB-1 線上,並且想和ClientB-1 通訊,一種辦法是Server 作為中間人,負責轉寄ClientA-1 和ClientB-1 之間的訊息,但是這樣伺服器太累,會吃不消。另一種方法就是讓ClientA-1 何ClientB-1 建立端到端的串連,然後他們自己通訊。這也就是P2P 串連。根據不同類型的NAT ,下面分別講解。
(1 )全錐NAT ,穿透全錐型NAT 很容易,根本稱不上穿透,因為全錐型NAT 將內部主機的映射到確定的地址,不會阻止從外部發送的串連請求,所以可以不用任何輔助手段就可以建立串連。
(2 )限制性錐NAT 和連接埠限制性錐NAT (簡稱限制性NAT ),穿透限制性錐NAT 會丟棄它未知的源地址發向內部主機的資料包。所以如果現在ClientA-1 直接發送UDP 資料包到ClientB-1 ,那麼資料包將會被NAT-B 無情的丟棄。所以採用下面的方法來建立ClientA-1 和ClientB-1 之間的通訊。
1 .ClientA-1 (202.103.142.29:5000 )發送資料包給Server ,請求和ClientB-1 (221.10.145.84:6000 )通訊。
2. Server 將ClientA-1 的地址和連接埠(202.103.142.29:5000 )發送給ClientB-1 ,告訴ClientB-1 ,ClientA-1 想和它通訊。
3. ClientB-1 向ClientA-1 (202.103.142.29:5000 )發送UDP 資料包,當然這個包在到達NAT-A 的時候,還是會被丟棄,這並不是關鍵的,因為發送這個UDP 包只是為了讓NAT-B 記住這次通訊的目的地址:連接埠號碼,當下次以這個地址和連接埠為源的資料到達的時候就不會被NAT-B 丟棄,這樣就在NAT-B 上打了一個從ClientB-1 到ClientA-1 的孔。
4. 為了讓ClientA-1 知道什麼時候才可以向ClientB-1 發送資料,所以ClientB-1 在向ClientA-1 (202.103.142.29:5000 )打孔之後還要向Server 發送一個訊息,告訴Server 它已經準備好了。
5. Server 發送一個訊息給ClientA-1 ,內容為:ClientB-1 已經準備好了,你可以向ClientB-1 發送訊息了。
6. ClientA-1 向ClientB-1 發送UDP 資料包。這個資料包不會被NAT-B 丟棄,以後ClientB-1 向ClientA-1 發送的資料包也不會被ClientA-1 丟棄,因為NAT-A 已經知道是ClientA-1 首先發起的通訊。至此,ClientA-1 和ClientB-1 就可以進行通訊了。
2.3. 使用TCP 穿透NAT
使用TCP 協議穿透NAT 的方式和使用UDP 協議穿透NAT 的方式幾乎一樣,沒有什麼本質上的區別,只是將不需連線的UDP 變成了連線導向的TCP 。值得注意是:
1. ClientB-1 在向ClientA-1 打孔時,發送的SYN 資料包,而且同樣會被NAT-A 丟棄。同時,ClientB-1 需要在原來的socket 上監聽,由於重用socket ,所以需要將socket 屬性設定為SO_REUSEADDR 。
2. ClientA-1 向ClientB-1 發送串連請求。同樣,由於ClientB-1 到ClientA-1 方向的孔已經打好,所以串連會成功,經過3 次握手後,ClientA-1 到ClientB-1 之間的串連就建立起來了。
2.4. 穿透對稱NAT
上面討論的都是怎樣穿透錐(Cone )NAT ,對稱NAT 和錐NAT 很不一樣。對於 對稱NAT ,當一個私網內主機和外部多個不同主機通訊時,對稱NAT 並不會像錐(Cone ,全錐,限制性錐,連接埠限制性錐)NAT 那樣分配同一個連接埠。而是會建立立一個Session ,重新分配一個連接埠。參考上面穿透限制性錐NAT 的過程,在步驟3 時:ClientB-1 (221.10.145.84: 。)向ClientA-1 打孔的時候,對稱NAT 將給ClientB-1 重新分配一個連接埠號碼,而這個連接埠號碼對於Server 、ClientB-1 、ClientA-1 來說都是未知的。同樣, ClientA-1 根本不會收到這個訊息,同時在步驟4 ,ClientB-1 發送給Server 的通知訊息中,ClientB-1 的socket 依舊是(221.10.145.84:6000 )。而且,在步驟6 時:ClientA-1 向它所知道但錯誤的ClientB-1 發送資料包時,NAT-1 也會重新給ClientA-1 分配連接埠號碼。所以,穿透對稱NAT 的機會很小。下面是兩種有可能穿透對稱NAT 的策略。
2.4.1 .同時開放TCP ( Simultaneous TCP open )策略
如果一個 對稱 NAT 接收到一個來自 本地 私人網 絡 外面的 TCP SYN 包, 這 個包想 發 起一個 “ 引入” 的 TCP 連 接,一般來 說 , NAT 會拒 絕這 個 連 接 請 求並扔掉 這 個 SYN 包,或者回送一個TCP RST (connection reset ,重建 連 接)包 給請 求方。但是,有一 種 情況 卻會接受這個“引入”串連。
RFC 規定:對於對稱NAT , 當 這 個接收到的 SYN 包中的源IP 位址 : 連接埠、目 標 IP 位址 : 連接埠都與NAT 登 記 的一個已 經 啟用的 TCP 會 話 中的地址資訊相符 時 , NAT 將會允許存取 這 個 SYN 包。 需要 特 別 指出 的是:怎樣才是一個已經啟用的TCP 串連。除了真正已經建立完成的TCP 串連外,RFC 規範指出: 如果 NAT 恰好看到一個 剛剛發 送出去的一個 SYN 包和 隨之 接收到的SYN 包中的地址 :連接埠 資訊相符合的 話 ,那 麼 NAT 將會 認為這 個 TCP 連 接已 經 被啟用,並將允 許這 個方向的 SYN 包 進 入 NAT 內部。 同時開放TCP 策略就是利用這個時機來建立串連的。
如果 Client A -1 和 Client B -1 能 夠 彼此正確的 預 知 對 方的 NAT 將會 給 下一個 TCP 連 接分配的公網 TCP 通訊埠,並且兩個客 戶 端能 夠 同 時 地 發 起一 個面向對方的 “ 外出 ” 的 TCP 連 接 請求 ,並在 對 方的 SYN 包到達之前,自己 剛發 送出去的 SYN 包都能 順 利的穿 過 自己的 NAT 的 話 ,一條端 對 端的 TCP 連 接就 能 成功地建立了 。
2.4.2. UDP 連接埠猜測策略
同時開放TCP 策略非常依賴於猜測對方的下一個連接埠,而且強烈依賴於發送串連請求的時機,而且還有網路的不確定性,所以能夠建立的機會很小,即使Server 充當同步時鐘的角色。下面是一種通過UDP 穿透的方法,由於UDP 不需要建立串連,所以也就不需要考慮“同時開放”的問題。
為了介紹ClientB-1 的詭計,先介紹一下STUN 協議。STUN (Simple Traversal of UDP Through NATs )協議是一個輕量級協議,用來探測被NAT 映射後的地址:連接埠。STUN 採用C/S 結構,需要探測自己被NAT 轉換後的地址:連接埠的Client 向Server 發送請求,Server 返回Client 轉換後的地址:連接埠。
參考4.2 節中穿透NAT 的步驟2 ,當ClientB-1 收到Server 發送給它的訊息後,ClientB-1 即開啟3 個socket 。socket-0 向STUN Server 發送請求,收到回複後,假設得知它被轉換後的地址:連接埠( 221.10.145.84:600 5 ),socket-1 向ClientA-1 發送一個UDP 包,socket-2 再次向另一個STUN Server 發送請求,假設得到它被轉換後的地址:連接埠( 221.10.145.84:60 20 )。通常,對稱NAT 分配連接埠有兩種策略,一種是按順序增加,一種是隨機分配。如果這裡對稱NAT 使用順序增加策略,那麼,ClientB-1 將兩次收到的地址:連接埠發送給Server 後,Server 就可以通知ClientA-1 在這個連接埠範圍內猜測剛才ClientB-1 發送給它的socket-1 中被NAT 映射後的地址:連接埠,ClientA-1 很有可能在孔有效期間內成功猜測到連接埠號碼,從而和ClientB-1 成功通訊。
2.4.3. 問題總結
從上面兩種穿透對稱NAT 的方法來看,都建立在了嚴格的假設條件下。但是現實中多數的NAT 都是錐NAT ,因為資源畢竟很重要,反觀對稱NAT ,由於太不節約連接埠號碼所以相對來說成本較高。所以,不管是穿透錐NAT ,還是對稱NAT ,現實中都是可以辦到的。除非對稱NAT 真的使用隨機演算法來分配可用的連接埠。