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

【原創】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