(转)NAT与NAT穿透 原理
原文:http://blog.csdn.net/ustcgy/article/details/5655050
?
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端口預言更容易被粉碎,并且對時間的敏感性的依賴更多.
這個屬于TCP穿透,它同樣基于NAT對TCP穿透的支持,基本原理和UDP穿透一致.具體實現后面會繼續介紹.
轉載于:https://www.cnblogs.com/yinsua/p/4567047.html
總結
以上是生活随笔為你收集整理的(转)NAT与NAT穿透 原理的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: PHP array_combine()
- 下一篇: 编译gcc4.4.6与ICE遇到的几个问