P2P 之 UDP穿透NAT的原理與實現

2021-04-06 10:53:30 字數 4985 閱讀 6929

原創:shootingstars

參考:http://midcom-p2p.sourceforge.net/draft-ford-midcom-p2p-01.txt

論壇上經常有對p2p原理的討論,但是討論歸討論,很少有實質的東西產生(源**)。呵呵,在這裡我就用自己實現的乙個源**來說明udp穿越nat的原理。

首先先介紹一些基本概念:

nat(network address translators),網路位址轉換:網路位址轉換是在ip位址日益缺乏的情況下產生的,它的主要目的就是為了能夠位址重用。nat分為兩大類,基本的nat和napt(network address/port translator)。

最開始nat是執行在路由器上的乙個功能模組。

最先提出的是基本的nat,它的產生基於如下事實:乙個私有網路(域)中的節點中只有很少的節點需要與外網連線(呵呵,這是在上世紀90年代中期提出的)。那麼這個子網中其實只有少數的節點需要全球唯一的ip位址,其他的節點的ip位址應該是可以重用的。

因此,基本的nat實現的功能很簡單,在子網內使用乙個保留的ip子網段,這些ip對外是不可見的。子網內只有少數一些ip位址可以對應到真正全球唯一的ip位址。如果這些節點需要訪問外部網路,那麼基本nat就負責將這個節點的子網內ip轉化為乙個全球唯一的ip然後傳送出去。(基本的nat會改變ip包中的原ip位址,但是不會改變ip包中的埠)

關於基本的nat可以參看rfc 1631

另外一種nat叫做napt,從名稱上我們也可以看得出,napt不但會改變經過這個nat裝置的ip資料報的ip位址,還會改變ip資料報的tcp/udp埠。基本nat的裝置可能我們見的不多(呵呵,我沒有見到過),napt才是我們真正討論的主角。看下圖:

server s1          

18.181.0.31:1235          

|^  session 1 (a-s1)  ^      |  

|  18.181.0.31:1235  |      |   

v 155.99.25.11:62000 v      |    

|nat

155.99.25.11

|^  session 1 (a-s1)  ^      |  

|  18.181.0.31:1235  |      |  

v   10.0.0.1:1234    v      |  

|client a

10.0.0.1:1234

有乙個私有網路10.*.*.*,client a是其中的一台計算機,這個網路的閘道器(乙個nat裝置)的外網ip是155.99.25.11(應該還有乙個內網的ip位址,比如10.0.0.10)。如果client a中的某個程序(這個程序建立了乙個udp socket,這個socket繫結1234埠)想訪問外網主機18.181.0.31的1235埠,那麼當資料報通過nat時會發生什麼事情呢?

首先nat會改變這個資料報的原ip位址,改為155.99.25.11。接著nat會為這個傳輸建立乙個session(session是乙個抽象的概念,如果是tcp,也許session是由乙個syn包開始,以乙個fin包結束。而udp呢,以這個ip的這個埠的第乙個udp開始,結束呢,呵呵,也許是幾分鐘,也許是幾小時,這要看具體的實現了)並且給這個session分配乙個埠,比如62000,然後改變這個資料報的源埠為62000。所以本來是(10.0.0.1:1234->18.181.0.31:1235)的資料報到了網際網路上變為了(155.99.25.11:62000->18.181.0.31:1235)。

一旦nat建立了乙個session後,nat會記住62000埠對應的是10.0.0.1的1234埠,以後從18.181.0.31傳送到62000埠的資料會被nat自動的**到10.0.0.1上。(注意:這裡是說18.181.0.31傳送到62000埠的資料會被**,其他的ip傳送到這個埠的資料將被nat拋棄)這樣client a就與server s1建立以了乙個連線。

呵呵,上面的基礎知識可能很多人都知道了,那麼下面是關鍵的部分了。

看看下面的情況:

server s1          server s2

18.181.0.31:1235          138.76.29.7:1235

|          |

|          |

+----------+----------+

|^  session 1 (a-s1)  ^      |      ^  session 2 (a-s2)  ^

|  18.181.0.31:1235  |      |      |  138.76.29.7:1235  |

v 155.99.25.11:62000 v      |      v 155.99.25.11:62000 v

|cone nat

155.99.25.11

|^  session 1 (a-s1)  ^      |      ^  session 2 (a-s2)  ^

|  18.181.0.31:1235  |      |      |  138.76.29.7:1235  |

v   10.0.0.1:1234    v      |      v   10.0.0.1:1234    v

|client a

10.0.0.1:1234

接上面的例子,如果client a的原來那個socket(繫結了1234埠的那個udp socket)又接著向另外乙個server s2傳送了乙個udp包,那麼這個udp包在通過nat時會怎麼樣呢?

這時可能會有兩種情況發生,一種是nat再次建立乙個session,並且再次為這個session分配乙個埠號(比如:62001)。另外一種是nat再次建立乙個session,但是不會新分配乙個埠號,而是用原來分配的埠號62000。前一種nat叫做symmetric nat,後一種叫做cone nat。我們期望我們的nat是第二種,呵呵,如果你的nat剛好是第一種,那麼很可能會有很多p2p軟體失靈。(可以慶幸的是,現在絕大多數的nat屬於後者,即cone nat)

好了,我們看到,通過nat,子網內的計算機向外鏈結是很容易的(nat相當於透明的,子網內的和外網的計算機不用知道nat的情況)。

但是如果外部的計算機想訪問子網內的計算機就比較困難了(而這正是p2p所需要的)。

那麼我們如果想從外部傳送乙個資料報給內網的計算機有什麼辦法呢?首先,我們必須在內網的nat上打上乙個「洞」(也就是前面我們說的在nat上建立乙個session),這個洞不能由外部來打,只能由內網內的主機來打。而且這個洞是有方向的,比如從內部某台主機(比如:192.168.0.10)向外部的某個ip(比如:219.237.60.1)傳送乙個udp包,那麼就在這個內網的nat裝置上打了乙個方向為219.237.60.1的「洞」,(這就是稱為udp hole punching的技術)以後219.237.60.1就可以通過這個洞與內網的192.168.0.10聯絡了。(但是其他的ip不能利用這個洞)。

現在我們來看看乙個p2p軟體的流程,以下圖為例:

server s (219.237.60.1)||

+----------+----------+

|          |

nat a (外網ip:202.187.45.3)          nat b (外網ip:187.34.1.56)

|   (內網ip:192.168.0.1)          | (內網ip:192.168.0.1)

|          |

client a  (192.168.0.20:4000)          client b (192.168.0.10:40000)

首先,client a登入伺服器,nat a為這次的session分配了乙個埠60000,那麼server s收到的client a的位址是202.187.45.3:60000,這就是client a的外網位址了。同樣,client b登入server s,nat b給此次session分配的埠是40000,那麼server s收到的b的位址是187.34.1.56:40000。

此時,client a與client b都可以與server s通訊了。如果client a此時想直接傳送資訊給client b,那麼他可以從server s那兒獲得b的公網位址187.34.1.56:40000,是不是client a向這個位址傳送資訊client b就能收到了呢?答案是不行,因為如果這樣傳送資訊,nat b會將這個資訊丟棄(因為這樣的資訊是不請自來的,為了安全,大多數nat都會執行丟棄動作)。現在我們需要的是在nat b上打乙個方向為202.187.45.3(即client a的外網位址)的洞,那麼client a傳送到187.34.1.56:40000的資訊,client b就能收到了。這個打洞命令由誰來發呢,呵呵,當然是server s。

總結一下這個過程:如果client a想向client b傳送資訊,那麼client a傳送命令給server s,請求server s命令client b向client a方向打洞。呵呵,是不是很繞口,不過沒關係,想一想就很清楚了,何況還有源**呢(侯老師說過:在源**面前沒有秘密 8)),然後client a就可以通過client b的外網位址與client b通訊了。

注意:以上過程只適合於cone nat的情況,如果是symmetric nat,那麼當client b向client a打洞的埠已經重新分配了,client b將無法知道這個埠(如果symmetric nat的埠是順序分配的,那麼我們或許可以猜測這個埠號,可是由於可能導致失敗的因素太多,我們不推薦這種猜測埠的方法)。

下面是乙個模擬p2p聊天的過程的源**,過程很簡單,p2pserver執行在乙個擁有公網ip的計算機上,p2pclient執行在兩個不同的nat後(注意,如果兩個客戶端執行在乙個nat後,本程式很可能不能執行正常,這取決於你的nat是否支援loopback translation,詳見http://midcom-p2p.sourceforge.net/draft-ford-midcom-p2p-01.txt,當然,此問題可以通過雙方先嘗試連線對方的內網ip來解決,但是這個**只是為了驗證原理,並沒有處理這些問題),後登入的計算機可以獲得先登入計算機的使用者名稱,後登入的計算機通過send username message的格式來傳送訊息。如果傳送成功,說明你已取得了直接與對方連線的成功。

P2P之NAT穿透原理

p2p之nat穿透原理介紹 一次專案中,對於主動協議接入的裝置,客戶希望能夠裝置端直接推送碼流到客戶端以此減少中心 的負載。所以對p2p這塊方案做了了解,這裡做下整理。即對等計算機網路,是一種在對等者 peer 之間分配任務和工作負載的分布式應用架構 是對等計算模型在應用層形成的一種組網或網路形式。...

P2P之UDP穿透NAT的原理與實現

nat network address translators 網路位址轉換 網路位址轉換是在ip位址日益缺乏的情況下產生的,它的主要目的就是為了能夠位址重用。nat分為兩大類,基本的nat和napt network address port translator 最開始nat是執行在路由器上的乙個...

P2P之UDP穿透NAT的原理與實現 補充)

這是另乙個位作者的補充 p2p原理的解釋與實現 關鍵字 p2p nat 打洞 關於採用udp協議進行打洞以進行p2p會話的原理,我本來想寫一篇文章作說明,但是現在已經有一篇文章把原理性的東西解釋清楚了,我在這裡不再作這部分的重複,可以參見這裡 p2p 之 udp穿透nat的原理與實現 附源 下面解釋...