1. 程式人生 > >ICE協議下NAT穿越的實現

ICE協議下NAT穿越的實現

前言:

之前寫了篇關於WebRTC的文章:iOS下音視訊通訊-基於WebRTC
,由於它是基於點對點連線的,自然而然需要NAT穿越的技術,否則訊息將無法傳遞。

在WebRTC使用了ICE協議框架,裡面提到了STUNTURN兩個協議,而NAT穿越實現就是由這兩個協議共同協調完成的。

正文:

一. 首先來簡單講講什麼是NAT?

原來這是因為IPV4引起的,我們上網很可能會處在一個NAT裝置(無線路由器之類)之後。
NAT裝置會在IP封包通過裝置時修改源/目的IP地址. 對於家用路由器來說, 使用的是網路地址埠轉換(NAPT), 它不僅改IP, 還修改TCP和UDP協議的埠號, 這樣就能讓內網中的裝置共用同一個外網IP. 舉個例子, NAPT維護一個類似下表的NAT表:

NAT對映


NAT裝置會根據NAT表對出去和進來的資料做修改, 比如將192.168.0.3:8888發出去的封包改成120.132.92.21:9202, 外部就認為他們是在和120.132.92.21:9202通訊. 同時NAT裝置會將120.132.92.21:9202收到的封包的IP和埠改成192.168.0.3:8888, 再發給內網的主機, 這樣內部和外部就能雙向通訊了, 但如果其中192.168.0.3:8888 == 120.132.92.21:9202這一對映因為某些原因被NAT裝置淘汰了, 那麼外部裝置就無法直接與192.168.0.3:8888通訊了。

 

我們的裝置經常是處在NAT裝置的後面, 比如在大學裡的校園網, 查一下自己分配到的IP, 其實是內網IP, 表明我們在NAT裝置後面, 如果我們在寢室再接個路由器, 那麼我們發出的資料包會多經過一次NAT.

二. NAT的副作用以及解決方案

國內移動無線網路運營商在鏈路上一段時間內沒有資料通訊後, 會淘汰NAT表中的對應項, 造成鏈路中斷。

這是NAT帶來的第一個副作用:NAT超時:

而國內的運營商一般NAT超時的時間為5分鐘,所以通常我們TCP長連線的心跳設定的時間間隔為3-5分鐘。**

而第二個副作用就是:我們這邊文章要提到的NAT牆。

NAT會有一個機制,所有外界對內網的請求,到達NAT的時候,都會被NAT所丟棄,這樣如果我們處於一個NAT裝置後面,我們將無法得到任何外界的資料。

但是這種機制有一個解決方案:就是如果我們A主動往B傳送一條資訊,這樣A就在自己的NAT上打了一個B的洞。這樣A的這條訊息到達B的NAT的時候,雖然被丟掉了,但是如果B這個時候在給A發信息,到達A的NAT的時候,就可以從A之前打的那個洞中,傳送給到A手上了。

簡單來講,就是如果A和B要進行通訊,那麼得事先A發一條資訊給B,B發一條資訊給A。這樣提前在各自的NAT上打了對方的洞,這樣下一次A和B之間就可以進行通訊了。

三. 四種NAT型別:

RFC3489 中將 NAT 的實現分為四大類:

  1. Full Cone NAT (完全錐形 NAT)

  2. Restricted Cone NAT (限制錐形 NAT ,可以理解為 IP 限制,Port不限制)

  3. Port Restricted Cone NAT (埠限制錐形 NAT,IP+Port 限制)

  4. Symmetric NAT (對稱 NAT)

其中完全最上層的完全錐形NAT的穿透性最好,而最下層的對稱形NAT的安全性最高。

簡單來講講這4種類型的NAT代表什麼:

  • 如果一個NAT是Full Cone NAT,那麼無論什麼IP地址訪問,都不會被NAT牆掉(這種基本很少)。
  • Restricted Cone NAT,僅僅是經過打洞的IP能穿越NAT,但是不限於Port。
  • Port Restricted Cone NAT,僅僅是經過打洞的IP+埠號能穿越NAT。
  • Symmetric NAT 這種也是僅僅是經過打洞的IP+埠號能穿越NAT,但是它有一個最大的和Cone型別的NAT的區別,它對外的公網Port是不停的變化的:
    比如A是一個對稱NAT,那麼A給B發信息,經過NAT對映到一個Port:10000,A給C發信息,經過NAT對映到一個Port:10001,這樣會導致一個問題,我們伺服器根本無法協調進行NAT打洞。

至於為什麼無法協調打洞,下面我們會從STUN和TURN的工作原理來講。

四. STUN和TURN的實現:

1.STUN Server主要做了兩件事:

  • 接受客戶端的請求,並且把客戶端的公網IP、Port封裝到ICE Candidate中。
  1. 通過一個複雜的機制,得到客戶端的NAT型別。

完成了這些STUN Server就會這些基本資訊傳送回客戶端,然後根據NAT型別,來判斷是否需要TURN伺服器協調進行下一步工作。

我們來講講這兩步具體做了什麼吧:
第一件事就不用說了,其實就是得到客戶端的請求,把源IP和Port拿到,新增到ICE Candidate中。

來講講第二件事,STUN是如何判斷NAT的型別的:

假設B是客戶端,C是STUN伺服器,C有兩個IP分別為IP1和IP2(至於為什麼要兩個IP,接著往下看):

STEP1.判斷客戶端是否在NAT後:

B向C的IP1的pot1埠傳送一個UDP 包。C收到這個包後,會把它收到包的源IP和port寫到UDP包中,然後把此包通過IP1和port1發還給B。這個IP和port也就是NAT的外網 IP和port(如果你不理解,那麼請你去看我的BLOG裡面的NAT的原理和分類),也就是說你在STEP1中就得到了NAT的外網IP。

熟悉NAT工作原理的朋友可以知道,C返回給B的這個UDP包B一定收到。如果在你的應用中,向一個STUN伺服器傳送資料包後,你沒有收到STUN的任何迴應包,那只有兩種可能:1、STUN伺服器不存在,或者你弄錯了port。2、你的NAT拒絕一切UDP包從外部向內部通過。

當B收到此UDP後,把此UDP中的IP和自己的IP做比較,如果是一樣的,就說明自己是在公網,下步NAT將去探測防火牆型別,我不想多說。如果不一樣,說明有NAT的存在,系統進行STEP2的操作。

STEP2.判斷是否處於Full Cone Nat下:

B向C的IP1傳送一個UDP包,請求C通過另外一個IP2和PORT(不同與SETP1的IP1)向B返回一個UDP資料包(現在知道為什麼C要有兩個IP了吧,雖然還不理解為什麼,呵呵)。

我們來分析一下,如果B收到了這個資料包,那說明什麼?說明NAT來著不拒,不對資料包進行任何過濾,這也就是STUN標準中的full cone NAT。遺憾的是,Full Cone Nat太少了,這也意味著你能收到這個資料包的可能性不大。如果沒收到,那麼系統進行STEP3的操作。

STEP3.判斷是否處於對稱NAT下:

B向C的IP2的port2傳送一個數據包,C收到資料包後,把它收到包的源IP和port寫到UDP包中,然後通過自己的IP2和port2把此包發還給B。

和step1一樣,B肯定能收到這個迴應UDP包。此包中的port是我們最關心的資料,下面我們來分析:
如果這個port和step1中的port一樣,那麼可以肯定這個NAT是個CONE NAT,否則是對稱NAT。道理很簡單:根據對稱NAT的規則,當目的地址的IP和port有任何一個改變,那麼NAT都會重新分配一個port使用,而在step3中,和step1對應,我們改變了IP和port。因此,如果是對稱NAT,那這兩個port肯定是不同的。

如果在你的應用中,到此步的時候PORT是不同的,那麼這個它就是處在一個對稱NAT下了。如果相同,那麼只剩下了restrict cone 和port restrict cone。系統用step4探測是是那一種。

STEP4.判斷是處於Restrict Cone NAT還是Port Restrict NAT之下:

B向C的IP2的一個埠PD傳送一個數據請求包,要求C用IP2和不同於PD的port返回一個數據包給B。

我們來分析結果:如果B收到了,那也就意味著只要IP相同,即使port不同,NAT也允許UDP包通過。顯然這是Restrict Cone NAT。如果沒收到,沒別的好說,Port Restrict NAT.

到這裡STUN Server一共通過這4步,判斷出客戶端處於什麼型別的NAT下,然後去做後續的處理:

這4步都會返回給客戶端它的公網IP、Port和NAT型別,除此之外:

  1. 如果A處於公網或者Full Cone Nat下,STUN不做其他的了,因為其他客戶端可以直接和A進行通訊。

     

    [email protected]

  2. 如果A處於Restrict Cone或者Port Restrict NAT下,STUN還會協調TURN進行NAT打洞。

     

    Paste_Image.png

  3. 如果A處於對稱NAT下,那麼點對點連線下,NAT是無法進行打洞的。所以為了通訊,只能採取最後的手段了,就是轉成C/S架構了,STUN會協調TURN進行訊息轉發。

     

    Paste_Image.png

2.TURN Server也主要做了兩件事:

  • 為NAT打洞:

如果A和B要互相通訊,那麼TURN Server,會命令A和B互相發一條資訊,這樣各自的NAT就留下了對方的洞,下次他們就可以之間進行通訊了。

  • 為對稱NAT提供訊息轉發:

當A或者B其中一方是對稱NAT時,那麼給這一方發信息,就只能通過TURN Server來轉發了。

最後補充一下,為什麼對稱NAT無法打洞:

假如A、B進行通訊,而B處於對稱NAT之下,那麼A與B通訊,STUN拿到A,B的公網地址和埠號都為10000,然後去協調TURN打洞,那麼TURN去命令A發信息給B,則A就在NAT打了個B的洞,但是這個B的洞是埠號為10000的洞,但是下次B如果給A發信息,因為B是對稱NAT,它給每個新的IP傳送資訊時,都重新對應一個公網埠,所以給A傳送請求可能是公網10001埠,但是A只有B的10000埠被打洞過,所以B的請求就被丟棄了。
顯然Server是無法協調客戶端打洞的,因為協調客戶端打得洞僅僅是上次對端為Server傳送埠的洞,並不適用於另一個請求。

最後的最後再補充一點,就是NAT打的洞也是具有時效性的,如果NAT超時了,那麼還是需要重新打洞的。
 

 


作者:塗耀輝
連結:https://www.jianshu.com/p/84e8c78ca61d
來源:簡書
簡書著作權歸作者所有,任何形式的轉載都請聯絡作者獲得授權並註明出處。