網絡工程—NAT穿透的詳細講解及分析

一、什麼是NAT?爲什麼要使用NAT?
NAT是將私有地址轉換爲合法IP地址的技術,通俗的講就是將內網與內網通信時怎麼將內網私有IP地址轉換爲可在網絡中傳播的合法IP地址。NAT的出現完美地解決了lP地址不足的問題,而且還能夠有效地避免來自網絡外部的攻擊,隱藏並保護網絡內部的計算機。  
二、NAT的分類
    STUN標準中,根據內部終端的地址(LocalIP:LocalPort)到NAT出口的公網地址(PublicIP:PublicPort)的影射方式,把NAT分爲四種類型:
1、Full Cone NAT: 內網主機建立一個socket(LocalIP:LocalPort) 第一次使用這個socket給外部主機發送數據時NAT會給其分配一個公網(PublicIP:PublicPort),以後用這個socket向外面任何主機發送數據都將使用這對(PublicIP:PublicPort)。此外,任何外部主機只要知道這個(PublicIP:PublicPort)就可以發送數據給(PublicIP:PublicPort),內網的主機就能收到這個數據包。
2、Restricted Cone NAT: 內網主機建立一個socket(LocalIP:LocalPort) 第一次使用這個socket給外部主機發送數據時NAT會給其分配一個公網(PublicIP:PublicPort),以後用這個socket向外面任何主機發送數據都將使用這對(PublicIP:PublicPort)。此外,如果任何外部主機想要發送數據給這個內網主機,只要知道這個(PublicIP:PublicPort)並且內網主機之前用這個socket曾向這個外部主機IP發送過數據。只要滿足這兩個條件,這個外部主機就可以用自己的(IP,任何端口)發送數據給(PublicIP:PublicPort),內網的主機就能收到這個數據包。
3、Port Restricted Cone NAT: 內網主機建立一個socket(LocalIP:LocalPort) 第一次使用這個socket給外部主機發送數據時NAT會給其分配一個公網(PublicIP:PublicPort),以後用這個socket向外面任何主機發送數據都將使用這對(PublicIP:PublicPort)。此外,如果任何外部主機想要發送數據給這個內網主機,只要知道這個(PublicIP:PublicPort)並且內網主機之前用這個socket曾向這個外部主機(IP,Port)發送過數據。只要滿足這兩個條件,這個外部主機就可以用自己的(IP,Port)發送數據給(PublicIP:PublicPort),內網的主機就能收到這個數據包。
4、Symmetric NAT: 內網主機建立一個socket(LocalIP,LocalPort),當用這個socket第一次發數據給外部主機1時,NAT爲其映射一個(PublicIP-1,Port-1),以後內網主機發送給外部主機1的所有數據都是用這個(PublicIP-1,Port-1),如果內網主機同時用這個socket給外部主機2發送數據,NAT會爲其分配一個(PublicIP-2,Port-2), 以後內網主機發送給外部主機2的所有數據都是用這個(PublicIP-2,Port-2).如果NAT有多於一個公網IP,則PublicIP-1和PublicIP-2可能不同,如果NAT只有一個公網IP,則Port-1和Port-2肯定不同,也就是說一定不能是PublicIP-1等於 PublicIP-2且Port-1等於Port-2。此外,如果任何外部主機想要發送數據給這個內網主機,那麼它首先應該收到內網主機發給他的數據,然後才能往回發送,否則即使他知道內網主機的一個(PublicIP,Port)也不能發送數據給內網主機,這種NAT無法實現P2P通信,但是如果另一方是Full Cone NAT,還是可以實現穿透的,下面我會詳細分析各種類型NAT穿透的情況。
NAT 功能通常被集成到路由器、防火牆、ISDN路由器或者單獨的NAT設備中。所以我們大家很少會知道NAT,上面NAT類型的概念描述是比較通俗的,但爲了更便於理解,我再舉例闡述一下NAT的原理。
    現有通信的雙方A和B,當A和B都是在公網的時候,通信是不用NAT的。假設A在內網,內網IP是192.168.1.3,端口號是5000,A經過NAT後的IP是221.221.221.100,端口號是8000,B的IP是202.105.124.100,端口是8500。如果B要去主動連接A,即使B知道A經過NAT後的IP和端口也是無法連接成功的,因爲A沒有向B(202.105.124.100:8500)發送過數據,所以B的數據包會被A的NAT丟棄,於是連接失敗。但是A如果去主動連接B,由於B是在公網,所以會連接成功,通信也就會建立。這也就是反彈連接木馬“反彈”二字的精髓。
當客戶端A和B都是處在內網的時候,雙方由於都不知道對方的公網IP和端口,就會無從下手,所以要在客戶端A和B之間架設一臺服務器S來爲它們牽線,而且S是處在公網,以保證A和B都能連接到S。客戶端A和B登錄時都首先連接S,S就會知道A和B經過NAT後的IP和端口,當A想要連接B時,就像S發出請求,S會把B經過NAT後的IP和端口告訴A,同時S向B發送A經過NAT後的IP和端口,並要求B發送數據給A,B發送數據到達A時會被A的NAT拋棄,但是B的NAT會有B發送數據到A的記錄,這是A再向B發送數據時就會被B的NAT放行,因爲B曾經向A的外網IP和端口發送過數據。可能有點亂,下面以故事的形式敘述一下這個情景。
人物:A(男) NAT_A(A家接線員)   B(女) NAT_B  (B家接線員) S
場景介紹:A想認識B,但是不知道B的電話,S跟A、B都是朋友,並且知道A和B的電話。接線員的職責:對往外轉接的電話不做詢問,對往內轉接的電話則要過濾以免有騷擾電話。過濾規則:在一定時間內沒有撥打過的號碼就過濾。
首先A給S打電話:
         A說:我想認識你朋友B,你把她電話給我唄。
         S說:行,她的電話是PublicIP_B,我讓她先給你打個電話,要不她家接線員不幫你轉接。
         A說:好。

S跟B打電話:
         S說:我有一個朋友A,人挺好的,他想認識你,你給他打個電話,他的電話號碼是PublicIP_A。
         B說:行,打完告訴你。
         S說:好的。

B打電話到A家,B家接線員NET_B看到女主人想往PublicIP_A打電話就轉接到A家了,同時把號碼PublicIP_A記錄下來,A家接線員NAT_A一看號碼是個近期沒打過的號,就給掛斷了。

B給S打電話:
         B說:我打完電話了
         S說:好,等着吧,一會他就給你打進來了。

S給A打電話:
         S說:他給你打完電話了,你快點給她打。
  
A打電話到B家, A家接線員NET_A看到男主人想往PublicIP_B打電話就轉接到B家了,B家接線員NET_B看到是剛剛撥過的PublicIP_A號碼打過來的,就轉接給B了,A和B的電話也就打通了。
A和B通話:
           A說:電話終於打通了,想認識你挺困難的。
           B說:是啊。
           ∶
           ∶
以上雖然和實際不太一樣,但穿透的整體過程基本就是這樣。A往B發送數據的唯一阻礙就是NET_B,所以想要成功發送數據,必須把NET_B穿一個洞,A是無法完成這項工作的,所以就得讓B完成這個打洞操作,也就是讓B往A發送數據,這樣NET_B就會誤以爲A發送的數據是上次會話的一部分從而不予阻攔。
但是,由於NAT的類型沒有一個統一的標準,所以NAT穿透使用的技術有很多種,穿透的成功率也不一樣。還有些NAT類型的內網之間幾乎無法穿透。下面我們用實例詳細分析一下各種NAT類型穿透的可行性。

A機器在私網(192.168.0.3)     
    A側NAT服務器(221.221.221.100)     
    B機器在另一個私網(192.168.0.5)     
    B側NAT服務器(210.30.224.70)     
    C機器在公網(210.202.14.36)作爲A和B之間的中介     
    A機器連接C機器,假使是A(192.168.0.3:5000)-> A側NAT(轉換後221.221.221.100:8000)-> C(210.202.14.36:2000)     
    B機器也連接C機器,假使是B(192.168.0.5:5000)-> B側NAT(轉換後210.30.224.70:8000)-> C(210.202.14.36:2000)     
    A機器連接過C機器後,A向C報告了自己的內部地址(192.168.0.3:5000),此時C不僅知道了A的外部地址(C通過自己看到的221.221.221.100:8000)也知道了A的內部地址。同理C也知道了B的外部地址(210.30.224.70:8000)和   內部地址(192.168.0.5:5000)。之後,C作爲中介,把A的兩個地址告訴了B,同時也把B的兩個地址告訴了A。     
    假設A先知道了B的兩個地址,則A從192.168.0.3:5000處同時向B的兩個地址192.168.0.5:5000和210.30.224.70:8000發包,由於A和B在兩個不同的NAT後面,故從A(192.168.0.3:5000)到B(192.168.0.5:5000)的包肯定不通,現在看A(192.168.0.3:5000)到B(210.30.224.70:8000)的包,分如下兩種情況:     
1、B側NAT屬於Full  Cone  NAT     
    則無論A側NAT屬於Cone  NAT還是Symmetric  NAT,包都能順利到達B。如果程序設計得好,使得B主動到A的包也能借用A主動發起建立的通道的話,則即使A側NAT屬於Symmetric  NAT,B發出的包也能順利到達A。
結論1:只要單側NAT屬於Full  Cone  NAT,即可實現雙向通信。   
    2、B側NAT屬於Restricted  Cone或Port  Restricted  Cone     
    則包不能到達B。再細分兩種情況     
  (1)、A側NAT屬於Restricted  Cone或Port  Restricted  Cone     
    雖然先前那個初始包不曾到達B,但該發包過程已經在A側NAT上留下了足夠的記錄:A(192.168.0.3:5000)->(221.221.221.100:8000)->B(210.30.224.70:8000)。如果在這個記錄沒有超時之前,B也重複和A一樣的動作,即向A(221.221.221.100:8000)發包,雖然A側NAT屬於Restricted   Cone或Port Restricted Cone,但先前A側NAT已經認爲A已經向B(210.30.224.70:8000)發過包,故B向A(221.221.221.100:8000)發包能夠順利到達A。同理,此後A到B的包,也能順利到達。     
   結論2:只要兩側NAT都不屬於Symmetric  NAT,也可雙向通信。換種說法,只要兩側NAT都屬於Cone  NAT,即可雙向通信。     
  (2)、A側NAT屬於Symmetric  NAT     
   因爲A側NAT屬於Symmetric  NAT,且最初A到C發包的過程在A側NAT留下了如下記錄:A(192.168.0.3:5000)->(221.221.221.100:8000)-> C(210.202.14.36:2000),故A到B發包過程在A側NAT上留下的記錄爲:   
  A(192.168.0.3:5000)->(221.221.221.100:8001)->B(210.30.224.70:8000)(注意,轉換後端口產生了變化)。而B向A的發包,只能根據C給他的關於A的信息,發往A(221.221.221.100:8000),因爲A端口受限,故此路不通。再來看B側NAT,由於B也向A發過了包,且B側NAT屬於Restricted   Cone或Port   Restricted  Cone,故在B側NAT上留下的記錄爲:B(192.168.0.5:5000)->(210.30.224.70:8000)->A(221.221.221.100:8000),此後,如果A還繼續向B發包的話(因爲同一目標,故仍然使用前面的映射),如果B側NAT屬於Restricted   Cone,則從A(221.221.221.100:8001)來的包能夠順利到達B;如果B側NAT屬於Port  Restricted  Cone,則包永遠無法到達B。     
  結論3:一側NAT屬於Symmetric  NAT,另一側NAT屬於Restricted   Cone,也可雙向通信。     
   反過來想,則可以得出另一個結論:兩個都是Symmetric NAT或者一個是Symmetric  NAT、另一個是Port Restricted Cone,則不能雙向通信,因爲NAT無法穿透。     
   上面的例子雖然只是分析了最初發包是從A到B的情況,但是,由於兩者的對稱性,前面得出的幾條結論沒有方向性,雙向都適用。        
   我們上面得出了四條結論,natcheck網站則把他歸結爲一條:只要兩側NAT都屬於Cone  NAT(含Full  Cone、Restricted  Cone和Port  Restricted  Cone三者),即可雙向通信。沒有把我們的結論3包括進去。
一般情況下,只有比較注重安全的大公司會使用Symmetric NAT,禁止使用P2P類型的通信,很多地方使用的都是Cone  NAT,因此穿透技術還是有發展前景的。
三、使用UDP、TCP穿透NAT
        上面講的情況可以直接應用於UDP穿透技術中,使用TCP 協議穿透NAT 的方式和使用UDP 協議穿透NAT 的方式幾乎一樣,沒有什麼本質上的區別,只是將無連接的UDP 變成了面向連接的TCP 。值得注意是:
        1、 B在向A打洞時,發送的SYN 數據包,而且同樣會被NAT_A 丟棄。同時,B需要在原來的socket 上監聽,由於重用socket ,所以需要將socket 屬性設置爲SO_REUSEADDR 。
        A向B發送連接請求。同樣,由於B到A方向的孔已經打好,所以連接會成功,經過3 次握手後,A到B之間的連接就建立起來了。具體過程如下:

1、 S啓動兩個網絡偵聽,一個叫【主連接】偵聽,一個叫【協助打洞】的偵聽。
2、 A和B分別與S的【主連接】保持聯繫。
3、 當A需要和B建立直接的TCP連接時,首先連接S的【協助打洞】端口,併發送協助連接申請。同時在該端口號上啓動偵聽。注意由於要在相同的網絡終端上綁定到不同的套接字上,所以必須爲這些套接字設置 SO_REUSEADDR 屬性(即允許重用),否則偵聽會失敗。
4、 S的【協助打洞】連接收到A的申請後通過【主連接】通知B,並將A經過NAT-A轉換後的公網IP地址和端口等信息告訴B。
5、 B收到S的連接通知後首先與S的【協助打洞】端口連接,隨便發送一些數據後立即斷開,這樣做的目的是讓S能知道B經過NAT-B轉換後的公網IP和端口號。
6、 B嘗試與A的經過NAT-A轉換後的公網IP地址和端口進行connect,大多數路由器對於不請自到的SYN請求包直接丟棄而導致connect失敗,但NAT-B會紀錄此次連接的源地址和端口號,爲接下來真正的連 接做好了準備,這就是所謂的打洞,即B向A打了一個洞,下次A就能直接連接到B剛纔使用的端口號了。
7、 客戶端B打洞的同時在相同的端口上啓動偵聽。B在一切準備就緒以後通過與S的【主連接】回覆消息“我已經準備好”,S在收到以後將B經過NAT-B轉換後的公網IP和端口號告訴給A。
8、 A收到S回覆的B的公網IP和端口號等信息以後,開始連接到B公網IP和端口號,由於在步驟6中B曾經嘗試連接過A的公網IP地址和端口,NAT-B紀錄了此次連接的信息,所以當A主動連接B時,NAT-B會認爲是合法的SYN數據,並允許通過,從而直接的TCP連接建立起來了。
   
         
參考網址:
http://midcom-p2p.sourceforge.net/draft-ford-midcom-p2p-01.txt
http://www.vckbase.com/document/viewdoc/?id=1773

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章