P2P穿透UDP/TCP--原理篇

前言

    NAT技術的出現從某種意義上解決了IPv4的32位地址不足的問題,它同時也對外隱藏了其內部網絡的結構。NAT設備(NAT,一般也被稱爲中間件)把內部網絡跟外部網絡隔離開來,並且可以讓內部的主機可以使用一個獨立的IP地址,並且可以爲每個連接動態地翻譯這些地址。此外,當內部主機跟外部主機通信時,NAT設備必須爲它分配一個唯一的端口號並連接到同樣的地址和端口(目標主機)。NAT的另一個特性是它只允許從內部發起的連接的請求,它拒絕了所有不是由內部發起的來到外部的連接,因爲它根本不知道要把這個連接轉發給內部的哪臺主機。
    P2P網絡已經日益流行。儘管p2p文件共享軟件引發了很多爭奪站,比如Nepster和KaZaA之間,但是還是有很多有用的並且合法的P2P軟件存在着,比如即時消息共享和文件共享。另一個P2P程序是一個叫OpenHash的項目,它爲公衆提供了一個可用的分佈式的哈希表,很多應用程序都在它的基礎上開發了出來,比如很多的即時通信軟件和可靠的CD標籤庫。
    不幸的是,兩個處於不同NAT後面的主機無法建立TCP連接,因爲各自的NAT都只允許外出的連接。NAT銷售商在已經爲NAT設備開發了端口映射的功能來解決這個問題。NAT管理員可以使用端口映射來爲那些需要接受那些不是從內部發起的連接請求的主機指定端口。但是這種解決方法根據情況還需要很多其他的支持。當有的服務器需要動態的分配端口的時候,這種方法就很受限了。再說了,如果一般的用戶沒有權限或者不懂得如何進入NAT設備爲他們指定端口映射,那這種方法就一點用處也沒有了。
    P2P協議對此已經闡述了少數通用的方法。第一個可被p2p協議使用的技術是:那些本來不能當作服務器的程序收到了來自請求者的消息後主動向請求者發起連接。這種情況只適用於只有一方在NAT後面的情況。第二種通用的方法是通過兩個主機都可以連接得到的代理路由數據,但是這種方法對於兩個NAT後面的主機來說效率太低了,因爲所有的數據都必須經過代理。其他相關的技術將在第三部分討論。
    我們努力的目標是找出一個可以讓NAT後面的兩個主機直接建立TCP連接的解決方案。特別地,我們已經開發出幾種方案可以用於那些支持端口分配地NAT和那些支持LSR路由的網絡。我們的方法是通過第三方提供建立直接連接需要的信息。根據不同的環境,我們開發了幾種不同的方案可以在可以預測和適當的時間的情況下建立連接。這些技巧都需要把數據包的TTL值設置得很小,並且捕捉和分析外傳的數據包以提供信息給第三方“媒人”。並且人爲地向網絡發送一些數據包用來檢測NAT所分配地端口。補充一點,如果端口分配是隨機地,我們就使用一種叫“birthday paradox”的方法減少檢測的次數。這種方法需要的空間是直接的窮舉所使用的空間的開方。
2. NAT的類型
    NAT必須考慮路由器的三個重要的特性:透明的地址分配、透明路由、ICMP包負載解析。
    地址分配是指在一個網絡會話開始的時候爲內部不可以路由的地址建立一個到可路由地址的映射。NAT必須爲原地址和目標地址都進行這樣的地址分配。NAT的地址分配有靜態的和動態的方式。靜態的地址分配必須預先在NAT中定義好,就比如每個會話都指派一對<內部地址,外部端口>映射到某對<外部地址,外部端口>。相反地,動態的映射在每次會話的時候才定義的,它並不保證以後的每次會話都使用相同的映射。
    一個相似的特性,NAT必須實現的是透明路由。正如上面提到的,NAT是一種特殊的路由,它在它所路由的數據包中翻譯地址。這種轉換基於數據流來改變相應的IP地址和端口。其次,這種轉換必須是設備透明的,這樣才保證對現有網絡的兼容性。一個不是很明顯的要求是,NAT必須保證內部網絡的數據包不被髮送到外部網絡去。
    最後一個NAT必須實現的特性是當收到ICMP錯誤包的時候,NAT使用正常的數據包做出同樣的轉換。當在網絡中發生錯誤時,比如當TTL過期了,一般地,發送人會收到一個ICMP錯誤包。ICMP錯誤包還包含了嘗試錯誤的數據包,這樣發送者就可以斷定是哪個數據包發生了錯誤。如果這些錯誤是從NAT外部產生地,在數據包頭部的地址將會被NAT分配的外部地址所代替,而不是內部地址。因此,NAT還是有必要跟對ICMP錯誤一樣,對在ICMP錯誤包中包含的數據包進行一個反向的轉換。
    雖然所有的NAT都實現了這三個特性,但是根據他們的特點和他們所支持的網絡環境,他們還可以進入分類。NAT可以分爲四種:Two-way NATs,Twice NATs,Multi-homed NATs和Traditional NATs。關於Two-way NATs,Twice NATs和Multi-homed NATs的特性討論請看[12]。Two-way NATs,一般也叫雙向NAT(Bidirectional NATs),在外部地址和內部地址間執行雙向轉換,儘管它個數據包只轉換一個IP地址。這種NAT是唯一一種允許從外部發起的連接請求。相反地,Twice NATs,它每路由一個數據包都對內部和外部的地址進行轉換。這種NAT用在那些外部地址和內部地址交疊的情況下。Multi-homed NATs對於Twice NATs來說,多了一個功能,它可以允許在內部使用不能路由的地址並且還可以有多個連接到外部網絡。之所以Multi-homed NATs能夠這樣做,是因爲它跟另一個保持通信以確定他們的地址映射是不變的。Multi-homed NATs允許大量的內部網絡和增加冗餘來允許多個連接到Internet。到目前爲止,最常用的NAT是傳統NAT(Traditional NATs),它可以分爲基礎NAT(Basic NATs)和NATP(Network Address Port Translation)兩種。
    基本NAT和NATP的區別它所能分配給內部地址的外部地址是否比內部地址多。一個Simple NAT用在那種所能分配的外部地址跟內部地址的數量相等或者更多的時候。這種NAT進行端口分配,因爲每個內部地址都可以分配到一個唯一的外部地址。NATPS用於當NAT所能用來分配的外部地址的數量比內部地址少的時候,最常見的一種情況是很多的內部機器共享一個外部IP地址。在這種情況下,NAT必須分配端口來補充IP用以消除網絡傳輸的不明確的機率。NAT和NATP共同的地方就是他們都阻止外來的連接,並且都可以進行靜態和動態的地址分配。
    NAPT是傳統NAT中最普遍的一種,因爲它允許很多的內部連接共享很少量的外部網絡地址。大部分爲小型網絡設計的商業NAT都是NAPT。我們選擇NATP作爲我們研究的對象就是因爲它的流行和它通過不允許外來連接限制了P2P協議。後面我們就把NATPs簡稱爲NATs了。
    我們首先要做的是得到商業防火牆以確定他們的特性跟資料上記載的是否一樣。我們使用NatCheck這個程序對三個常用的NAT設備進行了測試:Netgear MR814,Linksys BEFSR41,和Linksys BEFW11S4。三個NAT都有相似的行爲:他們對UDP和TCP都進行了一致的轉換,這表明在內部主機使用<內部IP:內部端口>的時候,NAT是否直接將<內部IP:內部端口>映射到<外部地址:外部端口>,而不管它連接出去的目標主機<目標IP:目標端口>是多少。一致轉換是靜態NAT與動態NAT截然不同的一個特點,因爲這不只是跟內部主機使用的地址有關,而且還跟端口有關。RFC3002明確指出要支持一致轉換。沒有一個NAT支持環路轉換,不管是TCP還是UDP,這表明NAT是否可以正確的處理兩個只知道對方外部地址的內部主機之間的連接。在我們的項目中,我們假設兩個主機是在不同的NAT後面的,所以這個測試跟我們的目標是無關的。最後,所有的NAT都提供了對非主動請求的外來TCP和UDP包都進行過濾,這個測試可以表明NAT是否預防非主動懇求的外來包進入到內部網絡。非主動請求過濾發生在除了Two-way NAT之外的所有NAT中,這也是在不同NAT後面的主機建立P2P連接最主要的障礙了。
3. 相關研究工作
    三個來自科內爾的作者獨立於我們做了關於穿過NAT的TCP直接連接工作並且結果和我們的類似。他們的被稱爲NUTSS[4]的框架爲不同的NAT後的主機的UDP和TCP連接性作了準備,但是他們的TCP技術有一個重大的缺點。協議依靠於爲了能夠TCP連接的欺騙包,這包在真實的網絡作了限制。許多ISP作了進入過濾以防止欺騙包進入他們的網絡,這將導致作者的協議失敗。欺騙不能是真實連接主機的組成部分。爲了他們所信任的,作者提出了一個不依靠於欺騙的方案。然而,這技術依靠於平臺相關的TCP堆棧行爲。我們在這裏所描述的技術在相當於NUTSS[4]環境中爲連接性做真實的假設時避免了欺騙。
    爲了解決NAT給很多協議帶來的困難,一種MIDCOM的架構被提了出來[13]。MIDCOM是一種可以允許NAT或者防火牆後面的用戶根據需要改變NAT行爲的而允許連接的一種協議。這個系統雖然在某些情況下是適用的,當它卻不能保證每個時候都可以。在那些用戶沒有辦法控制NAT的情況下,這種方法對於P2P的連接還是行不通的。
    很多時候NAT或者防火牆後面的用戶通過代理服務器進行連接。一種商業的代理解決方法是Hopster[6]提供的,Hopsetr的代理在連接方本地以隧道級別的傳輸在本地運行,它通過HTTPS(端口443)連接到Hopster自己的機器。但是,因爲Hopster的代理需要所有的傳輸都經過他們的機器,所有他們的方法跟我們的比起來就顯得低效了,具體見第5部分。
    爲了能夠進行直接的P2P連接,出現了針對UDP的解決方法。UDP打洞技術[5]允許在有限的範圍內建立連接。STUN(The Simple Traversal of User Datagram Protocol through Network Address Translators)協議實現了一種打洞技術可以在有限的情況下允許對NAT行爲進行自動檢測然後建立UDP連接[10]。
    在UDP打洞技術中,NAT分配的外部端口被髮送給協助直接連接的第三方。在NAT後面的雙方都向對方的外部端口發送一個UDP包,這樣就在NAT上面創建了端口映射,雙方就此可以建立連接。一旦連接建立,就可以進行直接的UDP通信了。在UDP打洞技術可以成功的情況下,我們在這篇文章中所用的有的技術也同樣適用。建立TCP連接比UDP連接更有優勢。首先,UDP連接不能夠依賴建立好的連接,就是不能夠持久連接。UDP是無連接的並且沒有對明確的通信。一般地,NAT見了的端口映射如果一段時間不活動後就是過期。爲了保持UDP端口映射,必須每隔一段時間就發送UDP包,就算沒有數據的時候,只有這樣才能保持UDP通信正常。第二,很多防火牆都配置成拒絕任何的外來UDP連接。最後,一個單純的TCP連接的實現更直觀,並且現有的代碼簡單得修改就可以使用我們的技術。
    目前的工作由bryan Ford[2]完成,已經擴展了打洞技術,使能在正規的NAT後的不同主機間進行TCP連接。方法類似於UDP打洞,由於一個映射在各個主機的NAT上被建立以使TCP直接連接能被創建,同步或同時TCP打開。這工作的焦點在於開發一種工作於最多的被定義爲其它NAT所需要而描述的NAT的技術來和TCP打洞協調一致。我們工作的不同點在於我們所開發的方案能夠在目前各類NAT行爲上進行直接地TCP連接,包括不協調於TCP打洞的NAT。
    Gnutella有一個能使在兩個不同端點的TCP通信的方案[14],但這僅僅應用於一個端點在NAT後面的情形。這方案被稱爲Push Proxy並本質地建立多個能夠推連接請求到NAT後的服務器的節點。NAT後的服務器發送消息到端點詢問他們是否願意推爲代理。當NAT後的服務器指明它有一個文件和詢問匹配,服務器包含了一列同意被推爲代理的端點。當端點想下載文件,它發送一個Gnutella PUSH消息到一個push proxy,這代理允許消息通過到達NAT後的服務器。NAT後的服務器就打開一個連接到這個發送PUSH消息的端點,所以文件可以被傳送。這方法使其更容易建立連接,這僅僅處理了一個端點在NAT之後的情況。我們的方案解決的是當兩個端點都在NAT之後的更困難問題。
    Walfish[15]指出,採用間接的服務可以提供NAT或者防火牆後面主機之間的連通性,通過在兩主機向間接服務器打開一個連接,並且服務器在他們之間傳輸所有的通信,在這篇文章中我們會完成一個不需要這樣的間接服務器也能建立起這樣得連接
總舵主(274197172) 13:11:18
. 問題陳述和假設
    假設兩個主機在不同的NAT後面,並且都知道對方的IP地址。如果這些主機想直接發起TCP連接,那肯定失敗。在直接的TCP連接中,必須有一方是發起者(創建初始的SYN包),另一方必須監聽。在兩個都在NAT後面的情況下,監聽者將不可能收到SYN包,因爲SYN包在到達NAT的時候就被丟棄了。這是因爲NAT或者防火牆不允許來自英特網的未請自來的數據包進入他們的網絡內部。所以,爲了在不同NAT後面的主機之間建立直接的連接,必須讓NAT以爲這個連接是經過內部主機發起的。我們可以通過讓兩邊的主機都發起一個TCP連接,也就是創建一個SYN包,這樣兩邊的NAT都會以爲這個連接是從內部發起的,是經過內部請求的,因此,就可以允許後續的數據經過它的網絡了。注意,雖然兩個端點都發送SYN包,但我們沒有使用TCP的同時打開。
    A的內部網絡                 B的內部網絡
┌────────┐       ┌────────┐
     主機A                 主機B      |
│192.168.2.2:400|        │192.168.2.2:500|
└────────┘       └────────┘
         ↑↑                 ↑↑
         ↓ /                 /
┌────────┐       ┌────────┐
    NAT NA                NAT NB    
│128.2.4.1:4000 |        │151.3.43.1:5000|
└────────┘       └────────┘
                 /_____/     
          /         目標        /
                             
          ┌──────────┐
                第三方X     
          │ 66.4.2.23:8000   |
          └──────────┘
                   英特網
          圖1:我們所開發的技術環境
    爲了在兩個端點間成功地建立一個TCP連接,每個端點必須知道它的夥伴在初始連接前的外部表面端口號。一旦一個從NAT的內部網絡請求被路由到外部網絡的一個IP地址的包到達時,這些端口將被NAT所選擇。就像記帳一樣,NAT用所選的外部端口號綁定到內部的IP地址和端口號。我們把這綁定稱爲映射。NAT不會向任何主機共享這個映射。我們的技術展示了爲何NAT映射可以被高效地確定。一旦兩個端點都知道他們夥伴的外部表面端口號,TCP連接就被兩個端點初始化。TCP序列號和應答號是TCP連接同步的完整組成部分。序列號不能夠指定,只能捕捉它。我們的文章將會說明怎麼協議管理這些參數以成功地在任何的環境下建立一個TCP連接。
    在不同的位於NAT後的主機間建立直接的TCP連接是一個難題,因爲NAT選擇外部端口不是NAT後的主機能直接理解到的,並且因爲成功的TCP連接需要序列號和應答號的協調。沒有工作於所有情況的單一方案。NAT的行爲依靠於它的實現,並且預測端口的能力依靠於內部網絡活動的數量。
    我們所做的兩個假設在我們測試過的NAT中都是成立的。第一個假設是我們假設主機都收不到來自外部網絡的ICMP TTL過期包。如果這些包被主機接收,那麼TCP連接就會被中斷。我們的很多解決方法中都依賴於能夠爲發送SYN的數據包設置一個很低的TTL值。當SYN包被路由丟棄的時候,TCMP TTL過期包會被髮送回NAT。用於我們實現的NAT必須沒有轉發收到的ICMP TTL過期包給內部網絡。如果NAT真的把這個包發送回內部網絡,也可以配置防火牆來阻止這類包。我們的另一個假設是NAT看到ICMP TTL過期包的時候映射的端口還不會失效。作爲另一個選擇,我們可以不修改TTL值,那就必須讓目標NAT不會產生TCP RST包。而實際上這個選擇是可行的,因爲很多的NAT爲了防止端口掃描一般都不發送TCP RST包。
5. 技術
 使用圖1的模型,我們的目標是讓在NA和NB後面的A和B建立直接的TCP連接。
 我們已經開發出幾種方法可以建立這樣的連接,根據NAT和網絡情況的不同,我們有對應的方法。考慮以下的信息作爲有序的三元組:<NA端口可預測性,NB端口可預測性,源地址可路由>。我們考慮下面幾種情況:
情況 1: <可預測的, 可預測的, LSR>
情況 2: <可預測的, 可預測的, no LSR>
情況 3: <隨機的, 可預測的, LSR>
情況 4: <隨機的, 可預測的, no LSR>
情況 5: <隨機的, 隨機的, LSR>
情況 6: <隨機的, 隨機的, no LSR>
注意:<隨機的, 可預測的,X>等同於<可預測的, 隨機的,X>。
5.1 連接前診斷
    作爲協助者X還有兩個端點A和的B,爲了確定A和B將試圖的連接屬於下面的哪種情況,X必須首先對各個端點做一些診斷。
    爲了使用1,3,5的情況,兩端必須確定在A和X還有B和X之間的LSR(鬆散源路由)是否是可用的。loose source routing(LSR)是一個允許IP包的創建者指定一列在數據包的路由中使用的託管IP地址的IP選項。這個選項的結果是在路由表裏的各個IP地址將按照路由表裏指定的順序收到數據包。LSR選項引來了一個安全性風險,這是因爲一個攻擊者能監聽到在路由表裏的會話。由於這一潛在的危險,許多路由器直接拋棄包含LSR選項的數據包。
    爲了確定從A穿過X到B時LSR是否可用,A可以簡單的嘗試用鬆散源路由穿過X連接到B,如果X收到這個包,這時LSR在A到B的第一半到X的行途是可用的。如果在一段指定的超時後X還沒收到任何包,這時可以假定LSR不可用。由於X可以用這種方法得知從A到B的一半行途是否允許LSR可用,所以它必須檢查也能收到從B來的LSR包。如果也能收到,則X可以斷定從A到B穿過X的LSR是可用的,任何的其它情況都必須假定LSR是無此選項。
    爲了確定是否NA可以隨機或者可預測地分配端口,A可以用連續的端口打開兩個到X的TCP連接。如果X得到的這些連接端口是連續的,則X可以斷定NA連續地分配了端口,同時是可預測的。當連接到B時,A必須使用連續的下一個端口來確保NA繼續按照X能預測的方式來分配端口。
    如果NA沒有連續地分配端口,但如果NA執行了一致地轉換,這時仍然可以預測到A的端口。A必須先打開一個在內端口PA到X的合法連接。NA將分配給這個連接一個隨機端口。當包被髮到X時,X可以非常清楚地看到NA所選擇的端口。A可以在相同端口打開第二個到X的不同端口的連接,這時X可以看到這兩個連接是否包含了相同的外部端口。如果是,則NA進行了一致性轉換。由於現在A必須用內部端口PA連接到B,所以X可以把NA所選的外部端口告訴B。把A到X的連接維持到A被連接到B以使NA不會改變端口映射是很重要的。
    如果嘗試了兩種端口預測方法後,X不能可靠地預測NA分配的端口,這時X必須假定NA是隨機的分配端口。
    當X完成對A的診斷,它可以同時用相同的方法對B進行診斷。一旦X擁有了所需的信息,連接協議就可以開始了。從這個診斷收集信息確保了詳細情況的執行。
5.2 序列號和應答號的協調
每個參與TCP連接者都維持兩個變量,一個序列號和一個應答號。在任何給定時刻,在任何主機的序列號是最後包發送的序列號。另外,在任何給定時刻,在任何主機的應答號是下一個預期包的應答號。通過三次握手的分步,初始的序列號和應答號被建立,如下:
    1.
在客戶端發送SYN包後,
     
客戶端的 seq#(序列號):Pack#(應答號):N/A
     
服務端的 seq#(序列號):N/Aack#(應答號):N/A

    2.
在服務端接收到SYN包和發送SYN+ACK後,
     
客戶端的 seq#(序列號):Pack#(應答號):N/A
     
服務端的 seq#(序列號):Qack#(應答號):P+1

    3.
在客戶端接收到SYN+ACK和發送ACK後,
     
客戶端的 seq#(序列號):Pack#(應答號):Q+1
     
服務端的 seq#(序列號):Qack#(應答號):P+1

    4.
在服務端接收到ACK後,
     
客戶端的 seq#(序列號):Pack#(應答號):Q+1
     
服務端的 seq#(序列號):Qack#(應答號):P+1

   
在三次握手最後的狀態必須被我們的方案所複製到,即使兩個端點假定爲客戶的角色。在每個方案的最後,各個端點的應答號必須是大於他們的夥伴的序列號。我們的方案完成這個協調。

5.3
低的TTL值確保
   
我們的方案某些是依賴於設置一個TCP包的TTL值,因此包將離開端點的內部網絡,但沒到達夥伴的NAT。對不同的網絡這個值將不同,因此它必須能被動態確定。
   
爲了確定夥伴距離有多遠,一個端點可以使用典型的路由追蹤方法。就是,發送從1開始而不斷增加的TTL值的SYN包。當TTL失效時各個包將導致ICMP TTL過期包被髮回到端點。通過分析返回的ICMP TTL過期包可以爲連接中低的TTL值確定一個保險值。
   
許多NAT不將ICMP TTL過期包發回內部主機,所以一個端點可以議定當一個ICMP TTL過期包沒有被返回時,用一個TTL值來引發一個包離開內部網。
   
同樣地,在NAT返回ICMP TTL過期包,通過分析夥伴的NAT的消息,端點必須以發現的保險的TTL值爲基礎。如果夥伴的NAT產生一個RST包,則端點可以使用一個比所產生的RST包小1TTL值。如果端點沒有得到RST包但開始停止接收ICMP TTL過期包,則可以確定夥伴的NAT用了拋棄不請自來的消息而沒有響應的保險行爲。事實上,這種情況和端點的NAT沒有返回ICMP TTL過期包是一樣的。
   
這個保險TTL值的確定不需要任何其它端點的參與。因此,它可以在保險低的TTL值被用於連接之前就被確定。

5.4
情況1<可預測的,可預測的,LSR>
A  X  B
|-------1a----->|<------1b------|
|<------2a------|-------2b----->|
|-------2b------|-------------->|
|<--------------|-------3a------|
|-------4a----->|<------4b------|
 
2:情況1
   
我們使用符號“NA:4000NB5000,選項/負荷來表示在英特網上從NANB所傳送的包內容。這符號意味着包有一個NAIP源地址,源端口4000,目的地址NBIP地址,和目的端口5000。此外,在目的端口後是其它的任何重要選項或負荷值。選項包含LSR:XSYN:PACK:QSYN+ACK:R,SLSR:X表明包將可鬆散源路由到XSYN:PACK:Q表明跟隨的序列號或者應答號的TCP包的類型。SYN+ACK:P,Q+1表明包是一個序列號爲P和應答號爲Q+1TCP SYN+ACK包。首先我們展示情況1<可預測的,可預測的,LSR>,其中所使用的事件序號在圖2中能找得到。
    1.A
B個發送一個可鬆散源路由的SYN包穿過協助者X到對方
     
aNA:4000NB:5000,LSR:X,SYN:P
     
bNB:5000NA:4000,LSR:X,SYN:Q
     
這兩個SYN包由TCPconnect()函數調用產生。SYNNAT NANB上都創建了預期的映射。在NA上的映射將允許後面被轉播向A而來自NB:5000的通信並籤準。
    2.X
緩存兩個包並向AB各自發了對方所用的ISN
     
aX:1234NA:3999,B剛用的ISN Q
     
bX:1235NB:4999,A剛用的ISN P
     
各個端點都需要他們的夥伴的ISN,這樣他們才能構造出一個合法的SYN+ACK包。
    3.A
B各向對方發送一個SYN+ACK
     
aNB:5000NA:4000,LSR:X,SYN+ACK:Q,P+1
     
bNA:4000NB:5000,LSR:X,SYN+ACK:P,Q+1
     
這兩個SYN+ACK包是由運行於各個端點的獨立線程所產生的。AB重新使用了原來的序列號PQ作爲在SYN+ACK中的序列號,並保證了所複製的序列號和應答號的最後狀態和5.2節討論的真實的TCP連接一樣。
    4.A
B各向對方發送一個ACK
     
aNA:4000NB:5000,LSR:X,ACK:Q+1
     
bNB:5000NA:4000,LSR:X,ACK:P+1
     
一旦欺騙的SYN+ACK包被接收到,TCP堆棧將自動地爲我們做這一步。
    5.X
拋棄兩個到達的ACK包,因爲沒有任何一個端點期望接收到這個ACK

   
2假定了AB知道他們的夥伴將使用哪個端口來工作;由於各個端點和它的夥伴都必須提前知道對方的信息,所以這一假設是合理的。首先第一步,X必須完成對AB的端口預測,因此X能預測到NAT設備選擇的端口。A必須知道NB工作於5000端口,同時B必須知道NA工作於4000端口。爲簡單化,可以假定X自己沒有在NAT後面,但唯一的假定前提是X必須預先直接連接到AB
   
情況1存在一種變種的方案。X可以發送在第2和第3步所需的SYN+ACK欺騙包,這似乎好於發送信息到AB以使他們自己能夠僞造SYN+ACK包。我們選擇目前的方法是因爲如果X發送SYN+ACK欺騙包,他們將被路由器拋棄的總比通過的多。此外,發送從XABSYN+ACK僞造包需要超級用戶權限。而AB爲了其它的目的必須且已經運行於超級用戶權限。
   
在我們的技術中,從步驟25是這樣考慮的,我們將申明函數Case1(integer extPortA, integer extPortB)作爲執行步驟25,把參數extPortAextPortB各自取代外部端口40005000
5.5 情況2<可預測的,可預測的,no LSR>
A  X  B
|-------2a----->|<------2b------|
|-------3a----->|<------3b------|
|<------4a------|-------4b----->|
|-------5b------|-------------->|
|<--------------|-------5a------|
|-------6a----->|<------6b------|
 
3:情況2
   
情況1依靠於可用的鬆散源路由。許多路由器目前設置了預防鬆散源路由,同時將具有代表性地拋棄請求服務的包。同樣地,依靠於鬆散源路由的技術在實際中將有很高的概率會失敗。如果松散源路由不可利用,SYN序列號可以利用一個外出通道(他們預先連接到X的連接)和X通信,而不是物理性地讓X看到包。注意圖2的步驟2X知道TCP序列號PQ,因爲X正確地接收到兩個SYN包。沒了LSR就不是這種情況了。
   
爲了初始連接,每端主機發送一個初始的SYN包到他們的夥伴,雖然他們知道將不能到達目的地。他們這時嗅探包離開網絡,記錄序列號,並報告這個信息給XX需要這些包的TCP序列號,如此它將能夠轉播這些信息回到AB,那樣他們就能夠產生SYN+ACK包。兩條路線所發的包都不會到達他們所標明地址的目的地。
   
更簡單的方案是每個端點各發送一個不被考慮的SYN到他們夥伴。在接收端適當的設置NAT和防火牆,他們將由於沒有映射存在而不會向前發送這些包到內部網絡主機。一些NAT和一些防火牆將發送TCP重置包(RST)到這個不請自來的包的源地址。如果NAT產生RST包,AB不能簡單的像圖2裏步驟1的建議一樣發送一個SYN包到對方,因爲在接受到RSTNANB將終止所建立的洞。如果NAT沒有產生RST包,打開的TCP連接將不會突然終止。
   
另一種確保SYN包將不會到達它的目的網絡的方法是發送帶有低於夥伴的NAT路徑長度的TTL值的SYN包。這包將會在去目的地的路上被明確地拋棄,並且TCP RST包將不會被任何發送者看到。更確切地說,一個ICMP過期包將被看到,並且產生一個問題,因爲ICMP過期包突然地終止一個TCP連接。然而,如果使用者能夠設置他們本地的防火牆拋棄ICMP包或者如果NAT不會發送ICMP消息包到它的內部網絡,TCP連接的嘗試將不會突然終止。
   
一個方案不能包括簡單的欺騙源地址的SYN包,使發送者不會接收到ICMP包或者RST包。這樣做會在中間件創建一個殘廢的映射。中間件在看到一個SYN包時,將創建一個從內部IP地址和端口到外部IP和端口的映射。然而,由於一個欺騙的SYN包有一個錯誤的源IP地址,映射將不會對應到內部網絡的主機。此外,一個方案不能包括把TTL設置低到連中間件都看不到SYN包,因爲這樣做不會創建我們需要的允許外部後面發到內網的通信的映射。
   
假定一個<可預測的,可預測的,no LSR>環境,這種連接就像我們現在在圖3所描述的。
    1.X
做了關於5.1節描述的端口預測。X預測到NA的下一端口是4000並預測到NB的下一端口是5000,並經由已經存在的連接告知AB
    2.A
B各自發送一個SYN包到對方,他們知道這個包將會被對方的NAT拋棄或者由於TTL過期而被拋棄。
     
aNA:4000NB:5000,SYN:P
     
bNB:5000NA:4000,SYN:Q
     
這一點是在各個端點的真實TCPconnect()函數調用所產生的。SYN包由TCP堆棧產生。這在NAT上創建了允許後面的來自夥伴IP地址和端口的通信通過併到達端點的映射。
    3.A
B各自發送他們自己所留意的ISNPQ)到X
     
aNA:3999X:1234,剛使用的ISNP
     
bNB:4999X:1235,剛使用的ISNQ
     
各個端點都需要它的夥伴的ISN,如此他們才能夠構造合法的SYN+ACK包從而發到他們的夥伴。
    4.X
發送AB的對方各自所留意的ISNAB
     
aX:1234NA:3999,B剛使用的ISNQ
     
bX:1235NB:4999,A剛使用的ISNP
    5.A
B各自發送一個SYN+ACK包到對方
     
aNB:5000NA:4000,SYN+ACK:Q,P+1
     
bNA:4000NB:5000,SYN+ACK:P,Q+1
     
這是三次握手的第二部分。此外,AB重用了他們原來的序列號PQ作爲在SYN+ACK中的序列號,並保證了所複製的序列號和應答號的最後狀態和5.2節討論的真實的TCP連接一樣。
    6.A
B各自發送一個ACK包到對方,他們知道這包將會被對方的NAT拋棄或者由於TTL過期而被拋棄。
     
aNA:4000NB:5000,ACK:Q+1
     
bNB:5000NA:4000,ACK:P+1
      TCP
堆棧將自動地爲我們發送這些ACK包來完成三次握手。我們不想ACK包到達他們的目的地,由於沒有任何一方在等待ACK包。

   
作爲步驟45而和情況1很相似的另一種方法,是X可以欺騙AB所需的SYN+ACK包。然而,我們爲了某些類似在情況1中的原因選擇了目前的方法。
   
由於步驟26在我們的技術中是可重複利用的,所以我們做了函數Case2(integer extPortA, integer extPortB)作爲執行步驟26,用參數extPortAextPortB分別替代了外部端口40005000

5.6
情況3<隨機的,可預測的,LSR>
A  X  B
|-------2a----->|  |
|  |-------2b----->|
|  |<------2c------|
|          Case1|(m,5000)       |
 
4:情況3
   
情況3<隨機的,可預測的,LSR>類似於圖2所描述的情況1。然而,X不能夠預測到兩個NAT中的一個的端口,比方說NAA首先不得不發送它的SYN包來允許X查看NA所選的端口。X將報告這一信息給B,因此B能夠發送它的SYN包到正確的目標IP地址和端口。這個情況1的修改版在圖4中描述並解釋如下。
    1.X
做了關於5.1節描述的端口預測。X不能預測到NA的下一端口,但能夠預測到NB的下一端口是5000,並經由已經存在的連接告知AB
    2.A
B同步經由X
     
aNA:mNB:5000,LSR:X,SYN:P
     
bXB知道NA工作於端口m
     
cNB:5000NA:mLSR:XSYN:Q
     
這兩個SYN包是由TCPconnect()函數調用所產生。
     
這兩個SYNNATNANB上各自創建了預期的映射。
    3.
調用Case1(m,5000)

5.7
情況4<隨機的,可預測的,no LSR>
A  X  B
|-------2a----->|  |
|-------------->|  |
|-------------->|  |
|
  |  |
|----------
---->|-------3------>|
|<--------------|-------4-------|
|<--------------|---------------|
|<--------------|---------------|
|  |
  |
|<--------------|---------------|
|-------5------>|  |
|  |-------6------>|
|          Case2|(m,5000)       |
 
5:情況4
   
情況4的環境是<隨機的,可預測的,no LSR>。我們已經爲這個依靠於隨機的且不拒絕一個帶了殘廢的且對應於在NAT後主機在前面的初始連接的ACK或者校驗和域的TCP包的NAT環境開發了一個方案。這方案被呈現在圖5中並解釋如下。
    1.X
做了關於5.1節描述的端口預測。X不能預測到NA的下一端口,但能預測到NB的下一端口是5000,並經由已經存在的連接告知AB
    2.A
發送TSYN包到B,這些包不是被對方的NAT所拋棄就是由於TTL過期而被拋棄。
      i = 0
      While i < T
          NA:rand
NB:5000, SYN:anything
          i = i+1
      End While
     
這會在NAT NA上創建T個映射,B將最後用SYN+ACK猜到的就是其中一個。
    3.X
通知B開始發送SYN+ACK包到NA
    4.B
發送許多SYN+ACK包到NA直到有一個到達A
      i = 1024
      While A
還沒報告成功
          NB:5000
NA:i,
         
SYN+ACK:,anything,anything, Payload:i
          i = i+1
      End While
    5.A
報告穿過NAT的包負荷。
      NA:3999
X:1234,工作於端口m
      A
通過監聽來自NB的任何SYN+ACK包的數據報,將可看到這個殘廢的SYN+ACK包。
    6.X
告訴B連接到A的端口m
      B
現在知道SYN包可以發向哪裏。
    7.
調用Case2(m,5000)

   
在步驟2A所發送的TSYN包是獨立於任何TCPconnect()函數調用。他們僅僅是使用在NAT上創建了T個映射的libnet庫所產生的包。另一方面,Case2調用的步驟2所產生的SYN包是由ABTCPconnect()函數調用所產生的。這情況4環境的方案依靠於隨機生成端口的NAT的行爲。這方案依靠於拒絕帶有錯誤的如序列號或者校驗和域的TCP包的中間件。
    T
值能夠被選擇,像B在生成TSYN+ACK到隨機目的端口號後有95%的機會猜到一個正確的外部端口。其實,ANAT隨機地選擇T的數目(它的外部端口數),這時B必須一直維持猜測直到在ANAT可選集中B選擇到了一個。我們能夠使用一個概率分析來構造一個高效的且最小工作量被AB所預算的設想。設PrGB猜到在T個實驗中最少一個是正確的概率。價設ANAT已經在[102565535]的範圍中選擇了T個不同的端口號,如果B選擇T個不同的端口號,在ANAT可選集中B不能選擇到一個號的可能性是
    Pr_G =n-T/n * n-1-T/n-1 * n-2-T/n-2 * . . . * n-(T -1)-T/n-(T -1)
其中n是可能選擇的端口數(n=65535-1024=64511)。
         T-1
    Pr_G =∏n-i-T/n-i
         i=0
反之,在T個實驗中猜到一個是正確的可能性是
    PrG = 1-Pr_G
像之前的規定,T可能被選擇如
    PrG > 95%
      T-1
    1-∏n-i-T/n-i> 95%
      i=0
計算T的量爲T=439的這個乘積。
      439-1
    1-∏64511-i-439/64511-i=0.9506> 95%
      i=0
這結果說明如果A發送439個使在ANAT外部端口得到不同的隨機的映射的SYN包,並且B發送許多不同的隨機的到目的端口的SYN+ACK包,B在它發送第440AYN+ACK包之前就有大於95%的機會正確地猜測到439個外部端口映射的其中一個。
   
僅僅發送TSYN包的原因是這至少要使用兩個資源,第一個是網絡帶寬的使用量,第二是在NAT上創建映射的數量。
5.8 情況5<隨機的,隨機的,LSR>
┌─────┐ ┌─────┐
NA端口瞭解NB端口瞭解|
└─────┘ └─────┘
   
           
   
       /  /     
   
        //      
   
        //      
   
       /  /     
┌─────┐ ┌─────┐
    A         B   
└─────┘ └─────┘
 
6:資源圖表死鎖
A  X  B
|-------2a----->|<------2b------|
|<------2c------|-------2c----->|
|-------3a----->|<------3b------|
|          Case1|(m,n)          |
 
7:情況5
   
在情況5中的環境是<隨機的,隨機的,LSR>。爲了允許X同步到ABB必須要知道NA預先發送它的SYN包時所選的端口號。爲了確定NA選擇的端口,X不得不看ASYN包。ASYN包不能被髮送直到X確定NB所選的端口號爲止。這個死鎖被製成圖6的插圖。A控制NA端口資源以致不外發SYN包,有效的預防了X知道NA所選的端口號。同樣的,B也控制NB端口資源。在他們能夠釋放所控制資源之前,每端都需要對方的端口。我們讓AB各發送兩個SYN包可鬆散源路由穿過X且不連接到TCP connect()調用的方案來預防這個死鎖。這兩個SYN包在各自的NAT上創建了所需的映射並允許X獲得兩個資源,同時等同於情況12的類似方式連接。我們情況5的方案展示在圖7中並解釋如下。
    1.X
做了關於5.1節述的端口預測。X不能預測到NA或者NB的下一端口並經由已經存在的連接告知AB
    2.A
B都發送一個SYN包鬆散源路由穿過X
    
aNA:mNB:anything SYN:anything,LSR:X
    
bNB:nNA:anything SYN:anything,LSR:X
    
cX報告mB並報告nA
     
這兩個SYN將在各自的NAT上創建所需的映射。
    3.A
B發送一個SYN到對方鬆散源路由穿過X
     
aNA:mNB:n,LSR:X,SYN:P
  
    bNB:nNA:m,LSR:X,SYN:Q
     
因爲一致轉換,即使目標端口和之前的步驟有所不同,NAT仍然爲這個包利用所使用的相同映射(因此相同的外部端口號)。
    4.
調用Case1(m,n)

   
注意,SYN包發送步驟2不是關聯到任何TCPconnect()函數調用,而更合適的,步驟3SYN包發送應歸於一個TCPconnect()函數調用。同理,Case1的調用中SYN+ACK包發送步驟3不綁定到TCPaccept()函數子程序。

5.9
情況6<隨機的,隨機的,no LSR>
A  X  B
|-------2------>|<------2-------|
|-------------->|<--------------|
|-------------->|<--------------|
|
  |  |
|-------------->|<--------------|
|-------3-------|-------------->|
|<--------------|-------3-------|
|---------------|-------
------->|
|<--------------|---------------|
|---------------|-------------->|
|<--------------|---------------|
|
  |  |
|---------------|-------------->|
|<--------------|---------------|
|-------4------>|<------4-------|
|<------5-------|-------5------>
|
|          Case2|(m,n)          |
 
8:情況6
   
情況6的環境是<隨機的,隨機的,no LSR>。回顧圖6的資源圖表死鎖,AB從包不可鬆散源路由保存端口的資源信息。這情況的方案被畫成圖8並解釋如下。
    1.X
做了關於5.1節述的端口預測。X不能預測到NA或者NB的下一端口並經由已經存在的連接告知AB
    2.A
發送TSYN包到B同時B發送TSYN包到A,這些包將被在另一端的NAT拋棄或者由於TTL到期而被拋棄。
      i=0
      While i<T
          NA:rand
NB:rand,SYN:anything
          NB:rand
NA:rand,SYN:anything
          i=i+1
      End While
   
這些SYN包在兩邊的NAT上各創建了T個映射。
    3.B
A發送許多SYN+ACK包到他們的夥伴的NAT直到有一個到達他們的夥伴那裏。
      i=1024
      While A
還沒報告成功
          NB:rand
NA:i
   
       SYN+ACK:,anything,anything,Payload:i
          NA:rand
NB:i
          SYN+ACK:,anything,anything,Payload:i
          i=i+1
      End While
    4.A
B報告通過NAT的包負荷。
      NA:3999
X:1234,工作於端口m
      NB:4999
X:1235,工作於端口n
    5.X
告訴B連接到A的端口m並告訴A連接到B的端口n
      A
B現在知道他們的夥伴的外部端口號。
    6.
調用Case2(m,n)

   
情況6遠比情況4困難,因爲各個端點必須在對方的NAT上正確地猜到一個完整的映射〈源端口,目的端口〉。在情況4中,在非隨機的NAT後的端點能夠正確地猜到目的端口。當其中一個NAT是可預測時,源端口就被固定了。情況6的搜索空間是情況4的平方,替代64511種可能的是4161669121種結合需要猜測。
6. 實現
   
我們已經在LINUX工作站,通過調用libnetlibpcap使用C實現了第2種和第4種情況。第1356種情況沒有實現。
   
協助者和端點都連接到由單獨功能組成的庫。協助者程序natblaster_server()只需提供其協助者所要監聽的端口號。端點連接程序需要提供7個參數:(1)協助者的IP地址和(2)端口號,(3)本地端點外部IP地址,(4)內部IP地址,和(5)端口號,(6)夥伴外部IP地址,和(7)端口號。本地端點和夥伴的端口號必須由協助者幫助爲試圖的連接建立一個唯一標誌符。三元組<本地外部IP,夥伴內部IP,夥伴內部端口>被用於在協助者上的唯一標誌。庫試圖提供一個指定端口的套接字,然而,所返回的套接字不被保證是所指定的端口號。假設natblaster_connect()工作,庫返回一個有效的套接字句柄。
   
爲了測試我們的實現,我們在分離的網絡上運行兩個端點,每個都位於不同的且有效的NAT後面。第三部分的程序被運行於不在NAT之後的第三部計算機上。我們在英特網上做了比本地網絡更多的測試,來確保測試更真實。
   
爲了創建不會到達夥伴那裏並不返回錯誤消息的包,我們設置了TTL值使其低到不會到達夥伴那裏。設置低的TTL值由調用IP_TTL選項的setsockopt()系統調用完成。這選項也需要一個TTL值。這個值必須低於到夥伴的跳躍數,但必須要大於到外部最遠的NAT的跳躍數。這套接字選項對於套接字的整個生存期來說必須不是持久不變的。例如,在三次握手成功之後,setsockopt()應該被再一次調用來提高 TTL值,這樣後面的數據才確保能到達端點。依靠的是一個低的TTL只工作於是否一個ICMP TTL過期包不被端點的TCP堆棧看到,因爲這過期包會導致在端點的套接字失敗。我們所測試的NAT都不向前發送ICMP TTL過期包到內部網絡。另一種方法是將發送普通包並希望夥伴的NAT默默地丟棄它們,然而,一些NAT可能發送RST包來響應那些未請自來的數據。這行爲是詳細的執行過程。我們沒使用5.3節所描述的TTL決定技術;而是我們選擇了我們知道是合適的並且低而普通的TTL值。
   
我們爲連接前診斷實現了連續的端口分配方法,但沒有實現一致轉換。我們的實現沒有使用一致轉換。
   
我們的情況2和情況4的實現都非常成功並且能打開直接的TCP連接。情況2真實的打開了連接,而情況4有很高的機率是成功的(就是上面所討論的,成功率取決於在預測相位的端口發送SYNSYNACK的數量)。
   
由於5.9節最後所給的原因,我們沒有實現情況6。我們沒實現情況135是因爲LSR在英特網上不是典型地可用的並且我們相信這在實踐中會有較低的成功率。
   
如前面所論述的,我們使用了增加系統調用所需的標準的Berkelet網絡實現。例如,當我們發送一個SYN包但需要知道序列號時,這個SYN包由使用標準的connect()調用所發送的,之後首先開始一個線程來爲所發的SYN包觀測數據報。這線程能報告所使用的序列號。
   
情況2和情況4必須有root的執行權,因爲這是libnetlibpcap所要求的。由於無需欺騙或者探嗅,第三方可以以一般用戶的權限運行。

7.
總結
   
我們已經證明了如何在兩個不同的典型的NAT後面的主機之間建立直接的TCP連接,這些解決方案都沒有以任何方式改變TCP/IP棧,而是爲這些主機建立連接起到了槓槓作用。我們的方案可以爲很多的程序所應用,從點對點的通信到即時消息通信。對於這個問題已經存在解決方法包括代理都沒有有效地利用網絡資源並且伸縮性不好。
   
隨着IPv6的到來,NAT也許就不再是個網站的組成了,但是,這些情況包括可預測NAT也可以被應用到使用狀態的防火牆後面的主機。跟NAT相似,狀態防火牆有能力只允許從他們所包含的內部網絡發起的連接。我們的解決方案雙方都可以初始話一個這些防火牆都允許的TCP連接。我們的幾種方案在配置有IDSes的場合是不可取的,因爲在第四和第六種情況下,都使用了類似於端口掃描的技術。這種情況下最後關閉這樣的網絡監視設備。儘管如此,我們地解決方案對於大部分的網絡來說一般是足夠的,甚至對於那些可能都不存在的使用隨機分配地址的NAT來說也是適用的。

8.
參考文獻
[1] Bryan Ford. NatCheck: Hosted by the MIDCOM-P2P
    project on SourceForge.
    http://midcom-p2p.sourceforge.net.
[2] Bryan Ford, Pyda Srisuresh, and Dan Kegel. Peer-to-Peer
    Communication Across Network Address Translators. In
    USENIX Annual Technical Conference, Anaheim, CA, April
    2005.
[3] Groove Networks. http://groove.net.
[4] Saikat Guha, Yutaka Takeday, and Paul Francis. NUTSS: A
    SIP-based approach to UDP and TCP Network Connectivity.
    In SIGCOMM 2004 Workshops, Aug 2004.
[5] M. Holdrege and P. Srisuresh. Protocol Complications with
    the IP Network Address Translator. RFC 3027, Internet
    Engineering Task Force, January 2001.
[6] Hopster: Bypass Firewall Bypass Proxy Software.
    http://www.hopster.com.
[7] Information Sciences Institute. Transmission Control
    Protocol (TCP). RFC 793, Internet Engineering Task Force,
    September 1981.
[8] Brad Karp, Sylvia Ratnasamy, Sean Rhea, and Scott
    Shenker. Spurring Adoption of DHTs with OpenHash, a
    Public DHT Service. In Proceedings of the 3rd International
    Workshop on Peer-to-Peer Systems, Feb 2004.
[9] Y. Rekhter, B. Moskowitz, D. Karrenberg, G. J. de Groot,
    and E. Lear. Address Allocation for Private Internets. RFC
    1918, Internet Engineering Task Force, February 1996.
[10] J. Rosenberg, J. Weinberger, C. Huitema, and R. Mahy.
     STUN - Simple Traversal of User Datagram Protocol (UDP).
     RFC 3489, Internet Engineering Task Force, September
     2003.
[11] P. Srisuresh and K. Egevang. Traditional IP Network
     Address Translator (Traditional NAT). RFC 3022, Internet
     Engineering Task Force, January 2001.
[12] P. Srisuresh and M. Holdrege. IP Network Address
     Translator (NAT) Terminology and Considerations. RFC
     2663, Internet Engineering Task Force, August 1999.
[13] P. Srisuresh, J. Kuthan, J. Rosenberg, A. Molitor, and
     A. Rayhan. Middlebox communication architecture and
     framework. RFC 3303, Internet Engineering Task Force,
     August 2002.
[14] Jason Thomas, Andrew Mickish, and Susheel Daswani. Push
     Proxy: Protocol Document 0.6, June 2003.
[15] Michael Walfish, Jeremy Stribling, Maxwell Krohn, Hari
     Balakrishnan, Robert Morris, and Scott Shenker.
     Middleboxes No Longer Considered Harmful. In
     Proceedings of USENIX Symposium on Operating Systems
     Design and Implementation, December 2004.
 
相關測試代碼請看
http://sourceforge.net/projects/natblaster
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章