再論NAT和穿透

icu發表於2006-01-17

1. 介紹
 今天的Internet的"middleboxes"已經普遍存在, 比如象網路地址轉換(NAT),主要是因為IPv4的地址空間消耗危機中產生的一個解決方案。然而,由這些"middleboxes"建立的不對稱定址和連線,已經成為點對點 (P2P)應用和協議中獨特的問題, 這些應用和協議包括例如網路電話和多人線上遊戲。這些話題甚至可能在使用 IPv6 協議後繼續存在, 比如說在 NAT 常被當作相容 IPv4 機制[NAT-PT]的地方,還有當NAT 不再需要之後,防火牆將會依然存在(這些問題)。

 當前發展的"middleboxes"最初計劃用在C/S結構中,即在那些相關的匿名客戶端主動去連線有著固定IP地址和DNS域名的可連線主機。大多數的"middleboxes"實現一個不對稱的溝通模型,即那些私有的內部網路上的主機可以和公網上的主機連線通訊,但是公網上的外部主機不能夠和內網上的主機通訊除了被 middlebox''s 的管理者明確地配置之外。 在 NAPT 的通常情形中,在內部網路上的一位客戶機在公網上並沒有一個唯一獨特的IP地址,但是可以在同一私網上的其他客戶機一樣,分享一個公網IP地址,並有NAPT管理。 這些在一臺"middlebox"後的不知道名稱和不易訪問的內部主機對客戶端軟體比如網頁瀏覽器並不是一個問題,它們之需要向外連線。而且這種不易訪問的特性有時候被視為對保護隱私有利。

 但是,在點對點的應用中,英特網上的主機通常會考慮要和"客戶"建立直接和彼此訪問的通話連線。呼叫者和被叫者可能會在不同的"middleboxes" 後面,兩者都可能沒有任何的固定IP地址或者其他的公網存在表現。舉例來說,一個通常的線上遊戲架構,是讓參加遊戲的主人連線到一個大家都知道的伺服器上設定一些初識值,以及連線後的使用目的。然後,為了在遊戲期間有更加快速和有效的遊戲速度,需要建立彼此直接的連線。同樣地,一個可共享的檔案可能可以讓一個大家都知道的資源搜尋引擎發現或者查詢到,但如果需要檔案資料傳輸,就需要和那臺共享檔案的主機建立直接的連線了。在點對點連線時,"middlebox"就生成了一個問題。因為在"middlebox"後面的那些需要用TCP或者UDP和其他機器連線的主機通常沒有固定可用的公網埠可以進行連線。 RFC 3235[ NAT-APPL]簡短地說明了這個問題,但是沒有提供任何的通常解決方案。

 在這一份文件中,我們就 P2P/ middlebox 問題有2點說明。 首先,我們總結那些在middleboxes存在時P2P應用程式可以工作的已知方法。其次,我們提供基於這些實踐的一套應用程式設計指導方針使P2P在middleboxes下應用的更健康。更進一步,我們提供的設計指導方針可以讓將來的 middleboxes 更有效率的支援支援 P2P 應用。 我們的重點是要能夠穿透 middleboxes,以提供更廣闊和更直接的P2P 應用。

 2. 術語
 在本章中我們首先簡要描述一下一些"middlebox"術語,我們這裡的重點是兩種常導致P2P應用出現問題的middlebox.

 防火牆
 一個防火牆限制私人內網和公眾英特網之間的通訊,典型地防火牆就是丟棄那些它認為未經許可的資料包。在資料包穿越一個防火牆時,它檢查但是不修改包裡的 IP地址和TCP/ UDP 埠資訊。
  
 網路地址轉換(NAT)
 當資料包穿過NAT時,NAT不僅檢查同時也修改資料的包頭資訊,並且允許更多的在NAT後的主機分享少數公網IP地址(通常只有1個)。

 NAT通常有2種主要型別:
 Basic Nat
 一個Basic NAT對映一個內在的私有IP地址到一個公網IP地址,但當資料包穿過NAT時,不更換它的TCP/UDP埠號。Basic Nat通常是隻用在一些具備公共IP地址池的NAT上,通過它可以地址繫結,即代表一臺內部主機。

 Network Address/Port Translator (NAPT)
 但是最通常的,當資料包穿過NAT時,一個NAPT檢查並修改它的TCP/UDP埠,那麼就可以允許多臺內網主機同時共享一個單獨的公網IP地址了。

 關於 NAT 的分類和術語,[NAT-TRAD] 和 [NAT-TERM]中有更多的資訊。那些將來分類的NAPT的附加術語在較近的工作[STUN]中被定義。當一個內網的主機經過一個NAT和外部進行TCP或者UDP連線的期間,NAPT分配一個公網IP 住址和埠,以便來自外部終端響應的資料包能被NAPT接收,解釋,並轉發給內網的主機。這個結果是由 NAPT 建立一個(私有IP地址,私有埠)和(公網IP地址,公網埠)之間的埠繫結實現的。在這個期間NAPT將為繫結的埠執行地址翻譯。一個關於P2P應用的問題是,當一個內部主機從一個私有IP,私有埠同時與外網上的多臺不同的主機建立多個連線時,NAT是如何運作的。

 Cone NAT
 建立一個埠,把一個(私有IP,私有埠)和(公用IP,公用埠)繫結後,對於以後來自同一私有IP和埠號的應用連線,cone NAT將重複使用這個繫結的埠,只要有一個連線會話,這個繫結埠就會保持啟用狀態。
 例如,下面圖表中,推想一下客戶端A通過一個cone NAT同時建立2個外部會話,從同樣的內部網路終端(10.0.0.1:1234)到2個不同的外部伺服器,S1和S2。cone NAT只會分配一個公用的終端,155.99.25.11:62000,都會到兩個會話,並在地址轉換期間確保客戶端埠的一致。Basic NAT和防火牆不修改通過middlebox的資料包中的埠號,這些型別的middlebox可以被認為是一種特殊的Cone Nat。

  Server S1                                     Server S2
         18.181.0.31:1235                              138.76.29.7:1235
                |                                             |
                |                                             |
                +----------------------+----------------------+
                                       |
           ^  Session 1 (A-S1)  ^      |      ^  Session 2 (A-S2)  ^
           |  18.181.0.31:1235  |      |      |  138.76.29.7:1235  |
           v 155.99.25.11:62000 v      |      v 155.99.25.11:62000 v
                                       |
                                    Cone NAT
                                  155.99.25.11
                                       |
           ^  Session 1 (A-S1)  ^      |      ^  Session 2 (A-S2)  ^
           |  18.181.0.31:1235  |      |      |  138.76.29.7:1235  |
           v   10.0.0.1:1234    v      |      v   10.0.0.1:1234    v
                                       |
                                    Client A
                                 10.0.0.1:1234

 Symmetric NAT
 對稱的NAT(Symmetric NAT),與Cone NAT有明顯差別,在所有會話期間中不會保持繫結(私有IP,私有埠)和(公共IP,公共埠)的埠不變。相反,它會為每個新對話重新分配一個新的公共埠。
 舉例來說,設想客戶A從同樣埠上要發起兩個外部對話,一個和S1連線,另一個和S2連線。Symmetric NAT可能會為第一個會話分配一個公共的端點 155.99.25.11:62000,而為第二個會話分配一個不同的公共端點155.99.25.11:62001。為了地址轉換,NAT可以區分這兩個會話傳輸的目的,因為和這些會話有關的外部端點(就是S1、S2)是不同的,甚至在通過地址轉換時丟失了客戶端的目的標示。(即丟了S1、S2的IP地址NAT也知道如何區分,我是這麼理解的,可能有誤。)

            Server S1                                     Server S2
         18.181.0.31:1235                              138.76.29.7:1235
                |                                             |
                |                                             |
                +----------------------+----------------------+
                                       |
           ^  Session 1 (A-S1)  ^      |      ^  Session 2 (A-S2)  ^
           |  18.181.0.31:1235  |      |      |  138.76.29.7:1235  |
           v 155.99.25.11:62000 v      |      v 155.99.25.11:62001 v
                                       |
                                  Symmetric NAT
                                  155.99.25.11
                                       |
           ^  Session 1 (A-S1)  ^      |      ^  Session 2 (A-S2)  ^
           |  18.181.0.31:1235  |      |      |  138.76.29.7:1235  |
           v   10.0.0.1:1234    v      |      v   10.0.0.1:1234    v
                                       |
                                    Client A
                                 10.0.0.1:1234

 Cone NAT和Symmetric NAT之間的比較與TCP/UDP之間的比較有些類似。(TCP需要繫結,UDP不需要,Cone NAT需要繫結,Symmetric NAT不需要)

 按照NAT從已知的公共IP,公共埠接收的資料限制,Cone NAT可以更進一步的進行分類。這種分類通常都是UDP連線的,因為NAT和防火牆會拒絕任何無條件的TCP連線,除非明確地以別的方式配置。

 Full Cone NAT
 在給一個新的外部會話建立了一個公共/私有的埠繫結後,一個full cone NAT就可以通過這個公共埠從公網上的任何外部端點接收資料通訊了。Full cone NAT也常常叫做"混合"NAT。

 Restricted Cone NAT
 當網路主機發一個或者幾個資料包給外部主機後,一個受限的cone NAT(Restricted Cone NAT)才會接受來自這個外部(源)IP地址的資料包。受限的cone NAT有效的運用了防火牆的原理,拒絕沒有要求的資料,只接收已知道的外部IP地址傳來的資料。(偏開原文,大意就是網路主機需要發一個請求給外部IP地址要求要資料,NAT才會接收這個外部IP地址傳來的資料,不然不接收。)

 Port-Restricted Cone NAT
 一個埠受限的cone NAT(Port-Restricted Cone NAT),也是這樣,只接收那些網路主機曾經發給一個外部IP地址和埠相匹配的資料。一個Port-Restricted cone NAT可以和對稱 NAT一樣(symmetric NAT),保護內部節點不接收未被請求的資料包,但是保持一個私有埠在連線過程中不變。
 (偏開原文,即不僅請求的IP和外部發來資料的IP是一樣的,PORT也要是一樣,這樣才接收資料。對稱NAT也有這個效果,但這種cone NAT保持埠不變,而對稱NAT要變埠號的)。

 最後,在這篇文件中我們定義一些新的術語來給middleboxes中有關P2P的行為進行分類:

 P2P-Application
 在這篇文件中P2P-Application被當做一個應用程式,在那些參與P2P連線的並在一個公共的登入伺服器註冊的終端執行,可以是一個私有端點,也可以是一個公共端點,或者兩者都是,並建立一個初步的會話。

 P2P-Middlebox
 P2P- Middlebox就是允許P2P應用程式交換資料的的middlebox。

 P2P-firewall
 一個P2P-防火牆就是提供防火牆功能的P2P- Middlebox,但不具備地址對映功能。

 P2P-NAT
 P2P-NAT就是提供NAT功能,也提供防火牆功能的P2P- Middlebox。一臺P2P- Middlebox必須為UDP傳輸至少提供Cone NAT功能,允許應用程式使用UDP建立完整的P2P連線。
   
 loopback translation(自環對映)
 當一臺位於私有域的主機,它的NAT試圖用此主機在公網的對映地址連線其他位於同樣NAT後的其他主機,相當於NAT裝置在資料包上做了兩次NAT轉換。在資料包到達目標主機之前,源主機的私有端點被轉換成NAT分配的公共端點,然後把目標主機的公共端點轉換成目標主機的私有端點。我們在上面提到的地址轉換用一臺NAT裝置完成就稱為"Loopback translation"。

 3. 用middleboxes進行P2P通訊的技術
 這段文章詳細的回顧一下使用現有的middlebox實現P2P通訊的技術,主要從應用程式或協議設計上來述說。

 3.1 Relaying(傳輸)
 最可靠的,但是效率最低的,實現P2P通訊的方法就是在傳輸過程中,把P2P通訊看成向C/S通訊的網路一樣。例如,推想2個客戶主機,A和B,各自發起一個TCP或UDP的連線,連線到一個大家都知道的擁有固定IP地址的伺服器S上。客戶主機都在各自的私有網路中,但是它們各自的middlebox不允許自己的客戶端可以直接和其它主機連線。
                                 Server S
                                    |
                                    |
             +----------------------+----------------------+
             |                                             |
           NAT A                                         NAT B
             |                                             |
             |                                             |
          Client A                                      Client B

 不能直接連線,兩個客戶端就使用S伺服器進行訊息的傳遞。例如,要傳送一條資訊到客戶端B,客戶端A以C/S連線方式簡單的傳送一條資訊到S伺服器,然後S伺服器使用已經和客戶端B建立的C/S連線傳送這條資訊到客戶端B。

 這種方法的優勢在於只要兩個客戶端都連在伺服器上,它就是有效的。它的明顯缺點是它需要了伺服器的處理並佔用了頻寬,而且即使伺服器的網路狀況良好,也有一定的通訊滯後問題。TRUN協議[TURN]定義了這種P2P應用的相關方法。
3.2 逆向連線 (Connection reversal)
 第二種技術是在只有一個客戶位於middlebox後的條件下運用的。舉例來說, 推想客戶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

 客戶A有一個私有IP地址10.0.0.1,並且一個應用程式使用TCP埠1234。這個客戶端和伺服器S的公網IP地址18.181.0.31和埠1235建立了一個連線。NAT A為了客戶端A和伺服器S的會話,臨時分配了一個終端地址,其TCP埠62000,它自己的IP地址是155.99.25.11:因此,伺服器S認為客戶端A用的是IP地址155.99.25.11,埠是62000。然而,客戶端B有著自己的固定IP地址,138.76.29.7,並且在它上面的P2P應用程式可以在埠1234接收TCP連線。

 現在推想客戶端B想要與客戶端A建立一個P2P連線會話。B可能用客戶端A本身的地址,即10.0.0.1:1234,也可能用在伺服器S上得到到的地址,155,99.25.11:62000,去嘗試連線。然而無論在哪一種情況下,連線都會失敗。第一種情況下,指向IP地址10.0.0.1的通訊包會被丟棄,因為10.0.0.1不是一個公網固定IP地址。第二種情況下,來自客戶端B的TCP SYN請求包將會到達NAT A的62000埠,但NAT A將會拒絕這個請求,因為NAT A只允許向外傳送資料。

 在嘗試和客戶端A建立直接連線失敗後,客戶端B會利用伺服器S傳遞一個請求,讓客戶端A去主動連線客戶。客戶端A在通過伺服器S接收到傳遞的請求後,會使用客戶端B的公共IP地址和埠建立一個TCP連線。 因為這個連線是在防火牆內部發起的,所以NAT A允許這個連線建立,而客戶端B也能接收這個連線,因為它並不處於middlebox後面。當前實現P2P系統的一種技術,它有一個主要的侷限性,就是它只能允許P2P中一方在NAT後面:而兩方都在NAT後面的情況是很常見的,這種方法就會失敗。因為這種逆向連線並不是解決問題的普遍方法,通常不推薦這個方法。應用程式可以選擇試一試逆向連線,但當"向前"或"逆向"都不能建立連線時,應用程式應該能夠自動的可以選擇另外的連線機制,比如relaying(即3.1說的)。

 3.3 UDP hole punching
 第三種技術,也是這篇文章的一個重要點之一,就是被稱為"UDP Hole Punching"的技術。當兩個需要通訊的主機可能都在middlebox後面的時候,UDP hole punching依賴於cone NAT和普通防火牆的一些特性,允許合適的P2P應用程式以"punch holes"方式通過middlebox並且建立彼此之間直接的連線。這種技術在RFC 3027[NAT- PORT]的5.1節中簡要的提及,並且在英特網[KEGEL]非證實的提到,也在最近的一些協議[TEREDO, ICE]中用到。正如名字中的所提到的,這種技術只能用於UDP連線。

 我們將會考慮兩個特別情況,並且考慮應用程式如何完善的處理兩者之間的握手連線。第一種情況下,也是較為普通的情況,兩個在不通的NAT後面的客戶端要求直接的進行P2P連線。第二種情況,兩臺客戶端位於同一個NAT後面,但不能肯定(兩臺客戶端位於同一個NAT後面)。

 3.3.1 位於不同NAT後面(Peers behind different NATs)
 假設客戶端A和B都有自己的私有IP地址,也都位於不同的NAT後面。P2P應用程式在A、B和伺服器S上執行,用的都是UDP埠1234。A和B各自和伺服器S建立UDP通訊連線,使NAT A為A的連線分配一個自己的公共埠62000,而NAT B為B的連線分配的是31000埠。

                                 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

 現在推想一下,客戶端A想要直接和B建立一個UDP通訊會話。假設A簡單的發一個UDP資訊包到B的公共地址138.76.29.7:31000,然而NAT B將會丟棄這些進入的資料資訊(除非它是一個FULL cone NAT),原因是NAT B和S已經建立的外部會話,而A傳送的資訊中的源地址和埠號是和S不匹配的(可以參照一下上面的內容,匹配才能接受)。同樣,假如B傳送一個條UDP資料包給A的公網地址,NAT A也會丟棄。

 但是,假設A發出一個UDP資料資訊給B的公網IP地址,同時也通過伺服器S傳遞一個請求給B,要求B也發一個UDP資訊給A的公網IP地址。A直接向B的公共IP地址(138.76.29.7:31000)傳送的資料包會讓NAT A在A的私有地址和B的公網地址之間建立了一個新的連線會話。同時,B到A的公網地址(155.99.25.11:62000)的資訊會導致NAT B在B的私有地址和A的公共地址之間建立一個新的連線會話。一旦這種新的UDP連線在兩者之間建立起來,客戶端A和B就不需要伺服器S的"介紹"就能彼此直接通訊了。

 UDP hole punching技術有幾個很有用的特點。一旦在兩個位於middlebox後面的客戶端建立了一個直接的P2P連線,在連線中的任何一方都可以扮演一個"介紹人"的角色,依次繼續和另一個客戶端建立連線,減少了最初的伺服器S的負擔。如果說有[STUN]的話,假如兩個中的任意一個或兩個都碰巧不在middlebox後面,上述應用程式將同樣可以建立P2P通訊通道,應用程式不需要嘗試明確middlebox的型別。Hole punching技術甚至可以自動的運用在多級NAT下面,多重NAT就是那些客戶端需要經歷多級地址轉換才能進入公網。

 3.3.2 位於同一NAT後(Peers behind the same NAT)
 現在考慮兩臺客戶端(但並不確定)都在同一個NAT後面的情況,因此會有私有IP地址空間。客戶端A與伺服器S建立一個UDP會話,NAT會分配一個公共埠62000。客戶端B與伺服器S也建立一個簡單的連線,NAT為此分配一個公共埠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

 假想A和B使用UDP hole punching技術與伺服器S的建立一個外部的通訊路線做為中間介紹。然後A和B將可以通過伺服器S得到各自公共IP地址和埠號,然後使用這些地址各自向對方傳送資料。兩個客戶能夠以這種方式彼此通訊,只要NAT不僅僅允許外網上的主機可以和內網上的主機進行UDP傳輸會話,也可以允許內網上的主機可以和其他內網的主機進行UDP會話。我們在"loopback translation"中設計到這種情況,因為來自私有網路的資料包到達NAT後,會"looped back"到私有網路上就象從公網來的一樣。例如,當A向B的公共IP地址傳送一個UDP包,這個包的包頭有一個源IP地址和埠,是10.0.0.1:1234,而目的地址是155.99.25.11.62001。NAT接受到這個包,會把源地址轉換(對映)為155.99.25.11:62000(就是A的公網地址),把目的地址轉換為10.1.1.3:1234,然後發給B。即使NAT支援迴環對映,NAT的轉換和傳送步驟看上去是多餘的,在A和B通訊時似乎為NAT新增了潛在的負擔。

 這個問題的解決方法是直接的。當A和B一開始在伺服器S上交換地址資訊時,它們就可以包含他們自己的IP地址和埠號,並且是可見的,對伺服器S也是可見的。客戶端根據它們得到的地址同時開始向對方發資料包,並建立成功的通訊。假如這兩個客戶端都在同一NAT後面,資料包象通訊一開始就能直接到達,而不需要通過NAT就能建立直接連線。假如這兩個客戶端位於不同的NAT後,到達彼此私有地址的資料包會被丟棄,但是客戶端可以通過各自的公共地址來建立連線。重要的是這些資料包需要通過一些方法去鑑別,然而,在這種情況下,A發到B的私有地址的資料包完全有可能到達A私網內其他無關的終端,B發到A的包也是這樣。

 3.3.3 Peers separated by multiple NATs(多級NAT)
 在有多重NAT裝置的拓撲結構中,如果沒有一些拓撲的知識,在兩個客戶端之間建立理想的P2P鏈路是不可能的。看看下面的舉的例子。

                                 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

 假設NAT X是由一個英特網服務提供者(ISP)設定的一個大型NAT,在一些公網IP地址上擁有許多使用者,NAT A和B是小使用者群的NAT閘道器,由ISP的使用者自己獨自配置,有各自的私有網路和使用者群,使用的是ISP提供的IP地址。只有SERVER S和NAT X有自己全球固定的IP地址,而NAT A和B用的"公共"IP地址實際上是ISP地址域中私有地址,而客戶端A和B的地址對NAT A和B來說也是私有的地址。每當客戶端需要和伺服器S建立一個外部的連線,都會導致NAT A和B和客戶端建立一個單獨的公共/私有連線,然後讓NAT X為每個連線會話建立一個公共/私有連線。

 現在推想客戶A和B嘗試建立一個直接的P2P UDP連線。對客戶端A來說,最佳的方法是傳送一個資料資訊到客戶端B在NAT B上,屬於ISP的地址域的公共IP地址192.168.1.2:31000,對客戶端B來說就是發資訊到A在NAT A的公共IP地址192.168.1.1:30000(原文是NAT B,是不是筆誤,還是我理解有問題?)。不幸的是,A和B並沒有知道這些地址的方法,因為伺服器S只能看到客戶端"全域性"的公共IP地址,就是155.99.25.11:62000和155.99.25.11:62001。甚至當A和B有某些方法可以得到這些地址,但他們依然不能保證這些地址是有用的,因為這些由ISP的私有地址域分配的地址可能與客戶自己分配的私有地址由衝突。客戶端因此沒有選擇只能使用由伺服器S知道的公共IP地址來通訊,並且依賴NAT X來提供loopback translation。

3.3.4 Consistent prot binddings(保持埠繫結)
 hole punching 技術有一個需要注意的地方:它只能工作在兩臺NAT都是cone NAT(或沒有NAT 防火牆)的情況下,只要UDP埠還在使用,它就需要保持一個固定的埠把一個給定(私有IP,私有UDP埠)和一個(公共IP,公共UDP埠)繫結。象對稱NAT一樣,為每個新的連線會話分配一個新的公共埠,對一個UDP應用程式來說,為了和不同的外部通訊重用一個已經存在的地址轉換是不可以的。(這邊稍微有點糊塗,再多看看。) 既然cone NAT運用是相當普遍的,UDP hole punching技術應用的也相當廣泛,但是還有一小部分是對等NAT配置,因此不能支援這種技術。


 3.4 UDP port number prediction

 有關UDP hole punching技術在上面已經被討論過,它可以允許在一些對等NAT存在的地方也能建立P2P UDP連線會話。這種方法有時被稱為"N+1"技術 [BIDIR ]並且由Takeda[SYM-STUN]詳細介紹。這種方法分析NAT的工作方式並且試圖預測它為將來的連線會話分配的公共埠。再次考慮那兩個客戶的狀態,A和B,在各自分開的NAT後面,已經與一臺擁有永久地址的伺服器S建立了UDP連線。
                                  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之間建立連線。通過與伺服器的通訊,A和B可以從伺服器S上得到對方的公共IP地址和埠號。客戶端A現在傳送一個UDP資料包到地址138.76.29.7,埠31001(注意埠數目的增加),而客戶端B同時傳送一個資料包到地址的155,99.25.11,埠62001上。如果NAT A和B依次為新的連線分配埠,如果從A-S和B-S連線建立後沒過多少時間,那在A和B之間的一個雙向通訊通道就可以工作起來。A到B的資料包讓NAT A建立一個新的連線,NAT A(所期望的)分配一個公共埠62001,因為之前A和S的連線會話用的62000埠,接下來就是62001。同樣的,B到A的資料包將讓NAT B開啟一個新連線,並將(也是所期望的)分配一個埠31001。如果客戶端可以正確的預測到NAT為新的連線分配的埠,一條雙向的UDP通訊通道就會象如下圖所示一樣建立起來。

                                  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


 顯而易見有很多情況都能導致這種方法失敗。假如任意一個預測的埠碰巧已經被其他無關的連線佔用,NAT將會錯過正確的埠,連線嘗試也將失敗。假如任意一個NAT有時或者總是選擇非連續的埠號,這個方法也將失敗。假如在A(B)建立了它和S的連線之後,但在傳送第一個資料包到B(A)之前,一個不同的客戶端在NAT(也或者B)開啟一個新的外部連線到任何外部主機,無關的客戶端會不注意的"偷"了(A TO B或者B TO A)所要求的埠。因此在任一NAT都包含不止一臺客戶端時,這種方法很少使用。

 實際上,如果那些NAT是cone NAT,或者一個是cone NAT,另一個是對稱NAT,這種情況下的P2P應用程式依然需要工作,應用程式需要實現查明在任何一個上與end [STUN]有關的NAT是哪一鍾,並按此來修改它的工作方式,這樣增加了演算法的複雜程式並讓網路變的脆弱。最終,假如任何一方客戶端在2級以上的NAT下並且離客戶端最近的NAT是對稱的,預測埠的方式是無法工作的。對所有這些原因來說,應用程式是無法實現這種方法的,在這裡被提及是為了歷史和資訊目的(就是告訴大家有這麼回事,我想)

 3.5. Simultaneous TCP open(TCP同時開啟)
 在一對節點都在已存在middlebox後,有一種建立直接P2P TCP連線的方法有時候會被使用。大多數TCP連線都是從一個終端發從一個SYN包到另一個終端,另一箇中斷同步響應一個SYN-ACK包。無論怎樣,對於兩個終端來說,同時通過傳送同步包到對方然後用一個ACK包應答來建立一個TCP連線是可行的。這種過程就被稱為"simultaneous open"(同時開啟)

 如果一個middlebox從嘗試建立一個TCP連線的私有網路的外面接受一個TCP SYN包,middlebox通常以丟棄這個SYN包或者傳送一個TCP RST(連線復位)包的方式來拒絕這個連線嘗試。但是,如果同步包與源和目的地址埠一起到達,那麼會讓middlebox相信一個TCP連線已經建立起來,然後middlebox將會允許資料包通過。特別是如果middlebox剛剛得到並轉換了一個從同樣地址和埠來的SYN包,它將認為連線是成立的並允許進來的SYN通過。如果客戶端A和B能彼此預測公共埠,它們各自的middlebox將分配下一個TCP連線埠,如果其中一個客戶端和另一個客戶端建立一個外部的TCP連線,可以在對方SYN到達本地middlebox之前就傳送SYN包通過它本地自己的middlebox,那麼P2P TCP連線就可以工作了。

 令人遺憾的是,這個方法也可能比上面說的UDP埠號預測方法更脆弱並對時效更加敏感。首先,除非在進行TCP連線時,兩個middleboxes是簡單的防火牆或者cone NAT,在各自嘗試猜測公共埠號來讓NAT分配新的連線時,和上面(UDP埠預測)說到的完全一樣的事情會導致連線失敗。另外,如果有一方的客戶傳送的同步包太迅速的到達對面的middlebox,遠端middlebox可能會用一個RST包拒絕SYN包,接下來就會導致本地的middlebox關閉對話並且在將來SYN重發時使用了相同但無用的埠號。最終,對simultaneous open的支援作為一個TCP的特殊應用,沒有在廣泛的系統中被使用。因此,這個方法也只為歷史因素在這裡被同樣提及;它不建議被應用程式使用。在現有NAT上想要實現P2P直接通訊的應用程式應該使用UDP。

 4. Application design guidelines(應用程式設計思路)

 4.1 What works with P2P middleboxes(如何和P2P middlebox一起工作)
 既然UDP hole punching是在兩個都位於NAT後的主機之間建立P2P直接通訊方法中最有效率的一個,並且它可以在多種現有NAT上使用,如果要求建立有效的P2P通訊,通常建議這種方法,但當直接通訊不能建立時,就得依靠簡單的傳播。(還不怎麼明白)

 4.2 Peers behind the same NAT(主機在同一個NAT後面)
 實際上有相當數量的使用者不止2個IP地址,會有3個或者更多的IP地址。這樣的話,告訴登入伺服器究竟是哪一個地址變的就有些困難了。因此在這種情況下,應用程式應該傳送它所有的IP地址。

 4.3 Peer discovery(主機發現)

 應用程式會傳送一些資料包到幾個地址,以發現哪一個地址是最合適的,應用程式可能變成(後面的不太明白,自己理解吧,sorry), 由於主機可能會不恰當的選擇一個路由地址當做一個內部區域網(例如11.0.1.1,已經被DOD網路分配,DOD是一種網路模型)。因此應用程式應該小心的傳送推測的呼叫包。
 申請把包送到幾地址發現, 哪一個適宜適合一規定貴族使用可能成為一個亂扔淨價的'' 空間廢品''的顯著的源頭,

 4.4 TCP P2P applications (TCP P2P應用程式)

 被程式設計師們廣泛使用的SOCKET API,常用於C/S結構應用設計中。在它的通常使用方式中,一個SOCKET能繫結一個TCP或UDP埠。一個應用程式不會被允許用同樣的埠(TCP or UDP)和多個SOCKET繫結來和多個外部主機同時建立連線(或)用一個SOCKET在埠上監聽而其他SOCKET來建立外部連線。但是上述單個SOCKET的埠繫結限制在UDP上不是問題,因為UDP是基於資料包文的協議。UDP P2P應用程式設計者可以用recvfrom()和sendto()函式來讓一個SOCKET不僅傳送而且可以從多個主機上接受資料包文。

 這不是TCP具有的情況。由於TCP,每個輸入和輸出連線都要和一個單獨的SOCKET有聯絡。Linux Sockets API用SO_REUSEADDR選項的幫助來解決這個問題(是不是應該這麼說?),這個選項好象不起作用,但可以
 (在標準Single Unix裡沒有這個函式)。Win32 API提供了一個相同的呼叫SetReuseAddress。使用任何上述的選擇,應用程式可以複用一個TCP埠的多個SOCKET。就是說,可以開啟兩個繫結在同樣埠上的TCP stream Socket,一個用與listen(),另一個用與其它主機connect()

 4.5 Use of midcom protocol()

 如果應用程式知道它們需要穿越的middlebox並且這些middlebox實現midcom 協議,應用程式能使用midcom協議更容易的穿越middlebox。
 例如,P2P應用程式需要NAT middlebox保持終端埠的繫結狀態。假如middlebox可以支援midcom,P2P應用程式可以控制修改繫結埠(或者繫結地址)的引數,例如生存時間,maxidletime(?),因此應用程式不僅可以直接的連線外部主機而且也可以從外部主機接受連線;這樣就不需要定期保持埠繫結的狀態。當應用程式不再需要繫結,也可以使用midcom協議簡單的取消繫結。

 5. NAT Design Guidelines (NAT設計指導)
 這部分討論網路地址轉換的設計,他們會影響P2P應用程式。

 5.1 Deprecat the use of symmetric NATs (不贊成使用對等NAT)
 對等NAT在那些C/S結構的應用中比如網路瀏覽中得到廣泛應用,它們只需要建立一個向外的連線即可。但是現在,比如實時訊息和語音會議等P2P應用程式被廣泛的應用。對等NAT不能支援保留終端的定義並且不適用於P2P應用程式。不建議使用對等NAT來支援P2P應用程式。
 一個P2P-middlebox必須在UDP通訊時具備Cone NAT的特點,允許應用程式可以建立使用UDP hole punching技術建立穩定的P2P連線。理論上,一個P2P-middlebox應該也允許應用程式既可以經過TCP,也可以通過UDP建立P2P連線。

 5.2 Add incremental cone-NAT support to symmetric NAT devices (增加遞增的cone-NAT以支援對等NAT裝置)

 一種可以讓對等NAT裝置擴充套件支援P2P應用程式的是分配它的可轉讓的埠空間,為一到一的連線預訂一個合適的埠,為一個一到多的連線預訂合適的一套不同的埠。
 更進一步(未來?),一個NAT裝置可以明確的被那些P2P應用程式和主機配置,因此NAT裝置可以自動由正確的埠資料塊來分配一個P2P埠。

 5.3 Maintain consisten port bindings for UDP ports (保持UDP埠的繫結)
 這份資料對NAT設計者最主要和最重要的建議是NAT保持一個固定的埠,繫結一個給定的(內部IP地址,內部UDP埠)和一個相應的(公共IP地址,公共UDP埠)
 只要有任何連線存在並使用這個埠繫結,這個埠繫結就要存在。通過檢查每一個包的源和目的IP地址和埠號,NAT可能會過濾關於每一個連線基礎的資料包(? 俺8懂)。當在一個私有網路上的節點建立了一個新的外部連線時,使用了一個現有的已經轉換過的UDP連線會話的IP地址和UDP埠號,NAT應該保證新的UDP連線作為現有連線給定一個同樣的公共IP地址和UDP埠。

 5.3.1 Preserving port numbers(保持埠號)  (就是客戶端用啥埠,NAT分配啥埠這個意思吧)
 一些NAT,當建立一個新的UDP連線時,會嘗試給一個相應的私有埠號分配一個同樣的公共埠號,如果這個埠號碰巧是可用的。例如,假如地址是10.0.0.1的客戶端A用一個從埠1234傳送的資料包建立一個外部的UDP連線,NAT的公共埠1234碰巧是可用的,那麼NAT會為連線使用NAT公共IP地址上的埠號1234作為轉換客戶端A的地址。由於如果內部網路的最多一個節點正在使用這個埠號,它是對一個NAT保持埠唯一可行的方法,這種方式可能對一些希望只用特別的UDP埠號的過去的UDP程式有幫助,但不建議應用程式依靠這種方式。
 另外, 一個NAT不應該在一個新連線中保持埠號,如果確實如此將與保持公共和私有終端地址繫結的目的相牴觸。例如,假定客戶端A在內部的1234埠上與外部伺服器S建立了一個連線,NAT A已經為這個連線分配了公共埠62000,因為埠號1234在NAT上此時是不可用的。現在假想在NAT上的埠號1234後來可以使用了,而此時A和S的連線仍然存在,客戶端A用同樣的內部埠1234建立一個新的連線到外部節點B上。在這種情況下,因為埠繫結已經在客戶端A的埠1234和NAT的公共埠62000上建立,所以這個繫結應該繼續保持,新連線也應該用62000埠作為公共埠,來對應客戶端A的埠1234。NAT不應該僅僅因為埠1234變的可用了就為新的連線分配公共埠1234:這種特點不可能在任何情況下都對應用程式有幫助,由於應用程式已經對一個轉換的埠號進行操作,並且它會中斷應用程式用UDP hole punching技術建立P2P連線的嘗試。

 5.4 Maintaining consistent port bindings for TCP ports (為TCP埠保持埠繫結)
 和UDP地址轉換的特點一致,cone NAT也應該對TCP連線保持私有和公共IP地址,TCP埠號的繫結不變,就如上面的關於UDP描述的方式一樣。保持TCP終端繫結不變將增加NAT對P2P TCP應用程式在從同樣源埠建立多個TCP連線時的相容性。

 5.5 Large timeout for P2P applications (P2P程式的大超時?)
 我們推薦middlebox在P2P應用時使用的最小超時大約5分鐘(300秒),即,在P2P應用時為埠繫結或為分配埠時,來給middlebox配置這個空閒超時時間。當middlebox慣用目前配置時,就常常嘗試使用更短的時間。但是更短的超時時間是有些問題的。考慮一個有16個終端節點的P2P應用程式,他們將每10秒發給網路一個保持活動狀態的資料包以避免NAT超時。這樣做是因為一個可能會以middlebox超時時間的5倍傳送保持活動的資料包,在這種情況下,保持活動的包將在網路上被丟棄。

 5.6 Support loopback translation(支援自環轉換)
 我們強烈建議middlebox 支援自環轉換,允許在一臺middlebox後面的主機可以和其它位於同一middlebox後的主機通過它們的可能被轉換的公共端點通訊。支援自環轉換是相當重要的,特別是在那些大容量多層NAT中,作為第一級的NAT。如第3.3.3 部分的描述,位於同一臺一級NAT下,但是第二級NAT不同的主機無法通過UDP hole punching彼此通訊,即使全部middlebox保持端點在NAT上的對映不變,除非第一級NAT支援自環轉換。

 

相關文章