【原創】IP攝像頭技術縱覽(七)---P2P技術—UDP打洞實現內網NAT穿透

【原創】IP攝像頭技術縱覽(七)—P2P技術—UDP打洞實現內網NAT穿透

本文屬於《IP攝像頭技術縱覽》系列文章之一:

Author: chad
Mail: [email protected]

本文可以自由轉載,但轉載請務必註明出處以及本聲明信息。

NAT技術的實際需求在10幾年前就已經出現,爲了解決這個問題,10幾年來全世界的牛人早已經研究好了完整的解決方案,網上有大量優秀的解決方案文章,筆者自知無法超越,所以秉承拿來主義,將優秀文章根據個人實驗及理解整理匯錄於此,用於解釋IP攝像頭整個技術鏈路。

  P2P(peer-to-peer, 點對點技術)又稱對等互聯網絡技術,研究該技術的原因在於:首先我們不希望我們的視頻數據通過服務器中轉,這樣容易造成隱私泄漏;再者,如果我們自己是IP攝像頭供貨商,通過服務器中轉的方式也會增加我們的產品成本,畢竟當用戶數量非常龐大時,服務器數量以及帶寬都是一筆不小的開銷。基於這兩點,可以說P2P通信方式是IP攝像頭實現的最好方式。而P2P通信中最重要的一點就是NAT(Network Address Translation,網絡地址轉換)穿透。

  本文主要內容包含:P2P通信與網絡設備的關係、不同的網絡設備特徵對P2P產生的影響、網絡地址轉換(NAT)的類型、NAT類型的檢測方法、協議防火牆的突破方法、隧道技術、對於不同的NAT類型採取的穿透方法。

  目前P2P通信在穿透上至少存在着兩個問題:防火牆穿透和NAT穿透,兩者對於網絡訪問的限制是處於不同角度而實現的,其中防火牆是基於網絡數據傳輸安全上的考慮,其行爲主要表現爲對網絡協議和訪問端口的限制,實際上每種限制都包含了兩個方向:進和出。而NAT則是基於網絡地址轉換的實現對內網主機進行的保護,目前來說NAT的存在至少存在以下兩方面的意義:解決IPV4地址 匱乏的問題和保護網內主機的目的,所以即使將來IPV6解決了IP地址數量上的問題,但出於對內網主機的保護,NAT仍然有其存在的必要。
  綜上所述,要實現一個完善的P2P程序必須至少突破以上兩個方面的限制,當然,實際情況會存在一些無法突破的情況,比如雙方都是對稱型NAT或對稱型與端口限制型NAT的通信,對於此類問題在實際開發時可使用服務器轉發或代理服務來處理。這篇文章的目的是提出一個能夠在兩個NAT設備內部的主機間建立直接的Internet連接的方法,同時又儘量不依賴於第三方主機。

1、NAT簡介

  NAT(Network Address Translation,網絡地址轉換)技術的出現從某種意義上解決了IPv4的32位地址不足的問題,它同時也對外隱藏了其內部網絡的結構。NAT設備(NAT,一般也被稱爲中間件)把內部網絡跟外部網絡隔離開來,並且可以讓內部的主機可以使用一個獨立的IP地址,並且可以爲每個連接動態地翻譯這些地址。此外,當內部主機跟外部主機通信時,NAT設備必須爲它分配一個唯一的端口號並連接到同樣的地址和端口(目標主機)。NAT的另一個特性是它只允許從內部發起的連接的請求,它拒絕了所有不是由內部發起的來到外部的連接,因爲它根本不知道要把這個連接轉發給內部的哪臺主機。
  NAT必須考慮路由器的三個重要的特性:透明的地址分配、透明路由、ICMP包負載解析。
  地址分配是指在一個網絡會話開始的時候爲內部不可以路由的地址建立一個到可路由地址的映射。NAT必須爲原地址和目標地址都進行這樣的地址分配。NAT的地址分配有靜態的和動態的方式。靜態的地址分配必須預先在NAT中定義好,就比如每個會話都指派一對<內部地址,外部端口>映射到某對<外部地址,外部端口>。相反地,動態的映射在每次會話的時候才定義的,它並不保證以後的每次會話都使用相同的映射。
  
  最後一個NAT必須實現的特性是當收到ICMP錯誤包的時候,NAT使用正常的數據包做出同樣的轉換。當在網絡中發生錯誤時,比如當TTL過期了,一般地,發送人會收到一個ICMP錯誤包。ICMP錯誤包還包含了嘗試錯誤的數據包,這樣發送者就可以斷定是哪個數據包發生了錯誤。如果這些錯誤是從NAT外部產生地,在數據包頭部的地址將會被NAT分配的外部地址所代替,而不是內部地址。因此,NAT還是有必要跟對ICMP錯誤一樣,對在ICMP錯誤包中包含的數據包進行一個反向的轉換。
  
  爲了能夠進行直接的P2P連接,出現了針對UDP的解決方法。UDP打洞技術允許在有限的範圍內建立連接。STUN(The Simple Traversal of User Datagram Protocol through Network Address Translators)協議實現了一種打洞技術可以在有限的情況下允許對NAT行爲進行自動檢測然後建立UDP連接。
  
  UDP打洞技術相對簡單,但是UDP連接不能夠持久連接。一般地,NAT建了的端口映射如果一段時間不活動後就會過期。爲了保持UDP端口映射,必須每隔一段時間發送一次UDP心跳包,只有這樣才能保持UDP通信正常。
  
  TCP打洞據說很牛,但是沒見過具體的實現,本人水平有限,曾迷失在TCP打洞技術實現中,後來直接放棄,專門研究UDP打洞,世界瞬間明亮了。
  

2、NAT類型及檢測

  
  通過UDP打洞實現NAT穿越是一種在處於使用了NAT的私有網絡中的Internet主機之間建立雙向UDP連接的方法。由於NAT的行爲是非標準化的,因此它並不能應用於所有類型的NAT。

  從功能上來說,NAT可以分爲:傳統NAT,雙向NAT(Bi-Directional NAT),兩次NAT(Twice NAT),多宿主NAT(Multihomed NAT),但是市場上現在最多的是傳統NAT,尤其是NAPT設備,所以本文的穿透也是針對NAPT展開,NAT共分爲兩大類:非對稱NAT(Cone NAT)和對稱NAT(Symmetric NAT)。這兩大類NAT又可細分爲以下下四種類型:

1) 非對稱NAT(Cone NAT)

a) 全ConeNAT(Full Cone NAT)
  內網主機建立一個UDP socket(LocalIP:LocalPort) 第一次使用這個socket給外部主機發送數據時NAT會給其分配一個公網(PublicIP:PublicPort),以後用這個socket向外面任何主機發送數據都將使用這對(PublicIP:PublicPort)。此外,任何外部主機只要知道這個(PublicIP:PublicPort)就可以發送數據給(PublicIP:PublicPort),內網的主機就能收到這個數據包

b) 限制性ConeNAT (Restricted Cone NAT)
  內網主機建立一個UDP socket(LocalIP:LocalPort) 第一次使用這個socket給外部主機發送數據時NAT會給其分配一個公網(PublicIP:PublicPort),以後用這個socket向外面任何主機發送數據都將使用這對(PublicIP:PublicPort)。此外,如果任何外部主機想要發送數據給這個內網主機,只要知道這個(PublicIP:PublicPort)並且內網主機之前用這個socket曾向這個外部主機IP發送過數據。只要滿足這兩個條件,這個外部主機就可以用自己的(IP,任何端口)發送數據給(PublicIP:PublicPort),內網的主機就能收到這個數據包
c) 端口限制性 ConeNAT(Port Restricted Cone NAT)
  內網主機建立一個UDP socket(LocalIP:LocalPort) 第一次使用這個socket給外部主機發送數據時NAT會給其分配一個公網(PublicIP:PublicPort),以後用這個socket向外面任何主機發送數據都將使用這對(PublicIP:PublicPort)。此外,如果任何外部主機想要發送數據給這個內網主機,只要知道這個(PublicIP:PublicPort)並且內網主機之前用這個socket曾向這個外部主機(IP,Port)發送過數據。只要滿足這兩個條件,這個外部主機就可以用自己的(IP,Port)發送數據給(PublicIP:PublicPort),內網的主機就能收到這個數據包

2) 對稱NAT(Symmetric NAT)

  內網主機建立一個UDP 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無法實現UDP-P2P通信。
  
類型識別
  既然有着這些不同類型的NAT,那麼我們在實際應用過程中就應該對處於不同NAT類型組合之後的終端給出不同的打洞策略。
  所以,在給出具體的NAT穿透策略之前,我們需要先識別當前的NAT是什麼類型,然後再根據對方的NAT是什麼類型,由此得到一個具體的穿透策略。
  STUN檢測流程如下:
  這裏寫圖片描述
  類型檢測的前提條件是:有一個公網的Server並且綁定了兩個公網IP(IP-1,IP-2)。這個Server做UDP監聽(IP-1,Port-1),(IP-2,Port-2)並根據客戶端的要求進行應答。

第一步:檢測客戶端是否有能力進行UDP通信以及客戶端是否位於NAT後?

  客戶端建立UDP socket然後用這個socket向服務器的(IP-1,Port-1)發送數據包要求服務器返回客戶端的IP和Port, 客戶端發送請求後立即開始接受數據包,要設定socket Timeout(300ms),防止無限堵塞. 重複這個過程若干次。如果每次都超時,無法接受到服務器的迴應,則說明客戶端無法進行UDP通信,可能是防火牆或NAT阻止UDP通信,這樣的客戶端也就 不能P2P了(檢測停止)。
  當客戶端能夠接收到服務器的迴應時,需要把服務器返回的客戶端(IP,Port)和這個客戶端socket的 (LocalIP,LocalPort)比較。如果完全相同則客戶端不在NAT後,這樣的客戶端具有公網IP可以直接監聽UDP端口接收數據進行通信(檢 測停止)。否則客戶端在NAT後要做進一步的NAT類型檢測(繼續)。

第二步:檢測客戶端NAT是否是Full Cone NAT?

  客戶端建立UDP socket然後用這個socket向服務器的(IP-1,Port-1)發送數據包要求服務器用另一對(IP-2,Port-2)響應客戶端的請求往回 發一個數據包,客戶端發送請求後立即開始接受數據包,要設定socket Timeout(300ms),防止無限堵塞. 重複這個過程若干次。如果每次都超時,無法接受到服務器的迴應,則說明客戶端的NAT不是一個Full Cone NAT,具體類型有待下一步檢測(繼續)。如果能夠接受到服務器從(IP-2,Port-2)返回的應答UDP包,則說明客戶端是一個Full Cone NAT,這樣的客戶端能夠進行UDP-P2P通信(檢測停止)。

第三步:檢測客戶端NAT是否是Symmetric NAT?

  客戶端建立UDP socket然後用這個socket向服務器的(IP-1,Port-1)發送數據包要求服務器返回客戶端的IP和Port, 客戶端發送請求後立即開始接受數據包,要設定socket Timeout(300ms),防止無限堵塞. 重複這個過程直到收到迴應(一定能夠收到,因爲第一步保證了這個客戶端可以進行UDP通信)。
  用同樣的方法用一個socket向服務器的(IP-2,Port-2)發送數據包要求服務器返回客戶端的IP和Port。
  比較上面兩個過程從服務器返回的客戶端(IP,Port),如果兩個過程返回的(IP,Port)有一對不同則說明客戶端爲Symmetric NAT,這樣的客戶端無法進行UDP-P2P通信(檢測停止)。否則是Restricted Cone NAT,是否爲Port Restricted Cone NAT有待檢測(繼續)。

第四步:檢測客戶端NAT是否是Restricted Cone NAT還是Port Restricted Cone NAT?

  客戶端建立UDP socket然後用這個socket向服務器的(IP-1,Port-1)發送數據包要求服務器用IP-1和一個不同於Port-1的端口發送一個UDP 數據包響應客戶端, 客戶端發送請求後立即開始接受數據包,要設定socket Timeout(300ms),防止無限堵塞. 重複這個過程若干次。如果每次都超時,無法接受到服務器的迴應,則說明客戶端是一個Port Restricted Cone NAT,如果能夠收到服務器的響應則說明客戶端是一個Restricted Cone NAT。以上兩種NAT都可以進行UDP-P2P通信。

  注:以上檢測過程中只說明了可否進行UDP-P2P的打洞通信,具體怎麼通信一般要藉助於Rendezvous Server。另外對於Symmetric NAT不是說完全不能進行UDP-P2P達洞通信,可以進行端口預測打洞,不過不能保證成功。

我在家中的NAT類型如下圖:
這裏寫圖片描述
在公司的NAT類型如下圖:
這裏寫圖片描述

我的全部實驗主要在家–公司–阿里雲服務器–多個朋友家裏完成。

3、UDP內網穿透實驗

  通過UDP打洞實現NAT穿越是一種在處於使用了NAT的私有網絡中的Internet主機之間建立雙向UDP連接的方法。由於NAT的行爲是非標準化的,因此它並不能應用於所有類型的NAT。
  其基本思想是這樣的:讓位於NAT後的兩臺主機都與處於公共地址空間的服務器相連,然後,一旦NAT設備建立好UDP狀態信息就轉爲直接通信,這項技術需要一個圓錐型NAT設備才能夠正常工作。對稱型NAT不能使用這項技術。

UDP打洞的過程大體上如下:
主機A和主機B都是通過NAT設備訪問互聯網,主機S位於互聯網上。
1. A和B都與S之間通過UDP進行心跳連接
2. A通知S,要與B通信
3. S把B的公網IP、port告訴A,同時把A的公網IP、port告訴B
4. A向B的公網IP、port發送數據(這個數據包應該會被丟棄,但是打開了B回來的窗戶)
5. B向A的公網IP、port發送數據(這個數據包就會被A接受,之後A和B就建立起了連接)

理論非常簡單,所以此處不再貼代碼,我的linux代碼實現請這裏—————————>>>UDP打洞代碼下載<<<——————————–。
程序中我調用了readline庫函數,所以,編譯前請保證系統中已經安裝了readline庫。
ubuntu安裝readline庫:

sudo apt-get install libreadline5-dev

實驗截圖如下(代碼有改動,這個是實驗中截圖):
這裏寫圖片描述

程序工作流程說明:
1、在一個開放IP/PORT的主機上運行server程序,並設定監聽端口。
2、兩個客戶端程序分別運行在兩個網絡環境中,並且使用login 命令登錄服務器,登錄時需要指定服務器ip信息以及自己的登錄名。登錄成功服務器會自動推送用戶列表到客戶端。
3、任何一方都可發起通信,通信時直接輸入sendto 對方用戶名 數據
4、鏈接會在第一次通信時建立,鏈接建立成功後服務器遍失去作用,此時關閉服務器後兩個客戶端依然能夠正常通信。

4、公開的免費STUN服務器

下面的地址不一定管用,可以多試試。

    stun01.sipphone.com
    stun.ekiga.net
    stun.fwdnet.net
    stun.ideasip.com
    stun.iptel.org
    stun.rixtelecom.se
    stun.schlund.de
    stunserver.org
    stun.softjoys.com
    stun.voiparound.com
    stun.voipbuster.com
    stun.voipstunt.com
    stun.voxgratia.org
    stun.xten.com
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章