nat

一. Nat介紹

1.nat基本介紹

NAT分類

 

 

nat類型.png

NAPT類型介紹

NAT對待UDP的實現方式有4種,分別以下:git

  • Full Cone NAT: 徹底錐形NAT,全部從同一個內網IP和端口號發送過來的請求都會被映射成同一個外網IP和端口號,而且任何一個外網主機均可以經過這個映射的外網IP和端口號向這臺內網主機發送包。github

  • Restricted Cone NAT: 限制錐形NAT,它也是全部從同一個內網IP和端口號發送過來的請求都會被映射成同一個外網IP和端口號。與徹底錐形不一樣的是,外網主機只可以向先前已經向它發送過數據包的內網主機發送包。算法

  • Port Restricted Cone NAT: 端口限制錐形NAT,與限制錐形NAT很類似,只不過它包括端口號。也就是說,一臺IP地址X和端口P的外網主機想給內網主機發送包,必須是這臺內網主機先前已經給這個IP地址X和端口P發送過數據包。安全

  • Symmetric NAT: 對稱NAT,全部從同一個內網IP和端口號發送到一個特定的目的IP和端口號的請求,都會被映射到同一個IP和端口號。若是同一臺主機使用相同的源地址和端口號發送包,可是發往不一樣的目的地,NAT將會使用不一樣的映射。此外,只有收到數據的外網主機才能夠反過來向內網主機發送包。服務器

二. Stun介紹

1.stun基本介紹

STUN是一種網絡協議,它容許位於NAT(或多重NAT)後的客戶端找出本身的公網地址,查出本身位於哪一種類型的NAT以後以及NAT爲某一個本地端口所綁定的Internet端端口。這些信息被用來在兩個同時處於NAT路由器以後的主機之間創建UDP通訊。該協議由RFC 5389定義。網絡

STUN由三部分組成:app

  • STUN客戶端;socket

  • STUN服務器端;ide

  • NAT路由器。oop

STUN服務端部署在一臺有着兩個公網IP的服務器上,大概的結構參考下圖。STUN客戶端經過向服務器端發送不一樣的消息類型,根據服務器端不一樣的響應來作出相應的判斷,一旦客戶端得知了Internet端的UDP端口,通訊就能夠開始了。

2. stun的檢測過程

STUN協議定義了三類測試過程來檢測NAT類型:

  • Test1:STUN Client經過端口{IP-C1:Port-C1}向STUN Server{IP-S1:Port-S1}發送一個Binding Request(沒有設置任何屬性)。STUN Server收到該請求後,經過端口{IP-S1:Port-S1}把它所看到的STUN Client的IP和端口{IP-M1,Port-M1}做爲Binding Response的內容回送給STUN Client。

  • Test1#2:STUN Client經過端口{IP-C1:Port-C1}向STUN Server{IP-S2:Port-S2}發送一個Binding Request(沒有設置任何屬性)。STUN Server收到該請求後,經過端口{IP-S2:Port-S2}把它所看到的STUN Client的IP和端口{IP-M1#2,Port-M1#2}做爲Binding Response的內容回送給STUN Client。

  • Test2:STUN Client經過端口{IP-C1:Port-C1}向STUN Server{IP-S1:Port-S1}發送一個Binding Request(設置了Change IP和Change Port屬性)。STUN Server收到該請求後,經過端口{IP-S2:Port-S2}把它所看到的STUN Client的IP和端口{IP-M2,Port-M2}做爲Binding Response的內容回送給STUN Client。

  • Test3:STUN Client經過端口{IP-C1:Port-C1}向STUN Server{IP-S1:Port-S1}發送一個Binding Request(設置了Change Port屬性)。STUN Server收到該請求後,經過端口{IP-S1:Port-S2}把它所看到的STUN Client的IP和端口{IP-M3,Port-M3}做爲Binding Response的內容回送給STUN Client。

STUN協議的輸出是:

1)公網IP和Port; 2)防火牆是否設置; 3)客戶端是否在NAT以後,及所處的NAT的類型。

所以咱們進而整理出,經過STUN協議,咱們能夠檢測的類型一共有如下七種:

  • A:公開的互聯網IP:主機擁有公網IP,而且沒有防火牆,可自由與外部通訊;

  • B:徹底錐形NAT;

  • C:受限制錐形NAT;

  • D:端口受限制形NAT;

  • E:對稱型UDP防火牆:主機出口處沒有NAT設備,但有防火牆,且防火牆規則以下:從主機UDP端口A發出的數據包保持源地址,但只有從以前該主機發出包的目的IP/PORT發出到該主機端口A的包才能經過防火牆;

  • F:對稱型NAT;

  • G:防火牆限制UDP通訊。

3. stun協議的判斷過程

 

 

stun協議的判斷過程.png

STEP1:檢測客戶端是否有能力進行UDP通訊以及客戶端是否位於NAT後 -- Test1

客戶端創建UDP socket,而後用這個socket向服務器的(IP-1,Port-1)發送數據包要求服務器返回客戶端的IP和Port,客戶端發送請求後當即開始接受數據包。重複幾回。

  • a)若是每次都超時收不到服務器的響應,則說明客戶端沒法進行UDP通訊,多是:G防火牆阻止UDP通訊

  • b)若是能收到迴應,則把服務器返回的客戶端的(IP:PORT)同(Local IP: Local Port)比較:若是徹底相同則客戶端不在NAT後,這樣的客戶端是:A具備公網IP能夠直接監聽UDP端口接收數據進行通訊或者E。不然客戶端在NAT後要作進一步的NAT類型檢測(繼續)。

STEP2:檢測客戶端防火牆類型 -- Test2

STUN客戶端向STUN服務器發送請求,要求服務器從其餘IP和PORT向客戶端回覆包:

  • a)收不到服務器從其餘IP地址的回覆,認爲包前被前置防火牆阻斷,網絡類型爲E

  • b)收到則認爲客戶端處在一個開放的網絡上,網絡類型爲A

STEP3:檢測客戶端NAT是不是FULL CONE NAT -- Test2

客戶端創建UDP socket而後用這個socket向服務器的(IP-1,Port-1)發送數據包要求服務器用另外一對(IP-2,Port-2)響應客戶端的請求往回發一個數據包,客戶端發送請求後當即開始接受數據包。 重複這個過程若干次。

  • a)若是每次都超時,沒法接受到服務器的迴應,則說明客戶端的NAT不是一個Full Cone NAT,具體類型有待下一步檢測(繼續)。

  • b)若是可以接受到服務器從(IP-2,Port-2)返回的應答UDP包,則說明客戶端是一個Full Cone NAT,這樣的客戶端可以進行UDP-P2P通訊。

STEP4:檢測客戶端NAT是不是SYMMETRIC NAT -- Test1#2

客戶端創建UDP socket而後用這個socket向服務器的(IP-1,Port-1)發送數據包要求服務器返回客戶端的IP和Port, 客戶端發送請求後當即開始接受數據包。 重複這個過程直到收到迴應(必定可以收到,由於第一步保證了這個客戶端能夠進行UDP通訊)。用一樣的方法用一個socket向服務器的(IP-2,Port-2)發送數據包要求服務器返回客戶端的IP和Port。比較上面兩個過程從服務器返回的客戶端(IP,Port),若是兩個過程返回的(IP,Port)有一對不一樣則說明客戶端爲Symmetric NAT,這樣的客戶端沒法進行UDP-P2P通訊(檢測中止)由於對稱型NAT,每次鏈接端口都不同,因此沒法知道對稱NAT的客戶端,下一次會用什麼端口。不然是Restricted Cone NAT,是否爲Port Restricted Cone NAT有待檢測(繼續)。

STEP5:檢測客戶端NAT是Restricted Cone 仍是 Port Restricted Cone -- Test3

客戶端創建UDP socket而後用這個socket向服務器的(IP-1,Port-1)發送數據包要求服務器用IP-1和一個不一樣於Port-1的端口發送一個UDP 數據包響應客戶端, 客戶端發送請求後當即開始接受數據包。重複這個過程若干次。若是每次都超時,沒法接受到服務器的迴應,則說明客戶端是一個Port Restricted Cone NAT,若是可以收到服務器的響應則說明客戶端是一個Restricted Cone NAT。以上兩種NAT均可以進行UDP-P2P通訊。

由前述的一對多轉換模型得知,除對稱型NAT之外的模型,NAT網關對內部主機地址端口的映射都是相對固定的,因此比較容易實現NAT穿越。而對稱型NAT爲每一個鏈接提供一個映射,使得轉換後的公網地址和端口對不可預測。此時TURN能夠與STUN綁定提供穿越NAT的服務,即在公網服務器上提供一個「地址端口對」,全部此「地址端口對」接收到的數據會經由探測創建的鏈接轉發到內網主機上。TURN分配的這個映射「地址端口對」會經過STUN響應發給內部主機,後者將此信息放入創建鏈接的信令中通知通訊的對端。這種探針技術是一種通用方法,不用在NAT設備上爲每種應用協議開發功能,相對於ALG方式有必定廣泛性。可是TURN中繼服務會成爲通訊瓶頸。並且在客戶端中增長探針功能要求每一個應用都要增長代碼才能支持。

我理解上述stun+turn的方式能夠解決對稱nat的nat穿越問題,由於turn提供公網ip+port,中繼轉發。 就至關於源服務器和turn只創建一個鏈接,因此映射的ip和port不會變。 因此這個方案實際上是從源server到turn至少兩層nat。 其實至關於turn服務器提供了port mapping服務?

三. 對稱型nat打洞分析

實際上大部運營商提供的光貓上網服務都是錐形nat的。 而光纖入戶,3g 4g網絡,公共wifi登由於安全因素都是對稱nat。 對稱型nat是能夠打洞的。只要請求連接的對方是非端口限制錐型nat就都能實現打洞p2p連接的。 只有雙方都是對稱是必定沒法實現。 (ICE能夠解決?) 或一方對稱一方是端口限制錐型nat的狀況也沒法實現打洞 生日攻擊算法(端口預測)確實能夠解決對稱型與端口限制型之間的穿透

對於一端是對稱nat,一端是端口限制性Cone nat的狀況是能夠打洞成功的,特別是咱們實驗的對稱nat的端口變化仍是有規律的(加1),咱們使用端口猜想的方法進行打洞成功率仍是很是高的。對於端口變化無規律的對稱nat,這個猜想仍是靠算法的設計,你能夠看看A New Method for Symmetric NAT Traversal in UDP and TCP (http://www.goto.info.waseda.ac.jp/~wei/file/wei-apan-v10.pdf)另外若是你是作應用的話建議不要始終把本身侷限於必定要打洞成功的思路上,對於一些路由器是能夠經過配置支持穿透的,好比upnp;對於實在打洞不成功的狀況你能夠經過設計一箇中轉服務器來完成本身的應用;

UDP hole punching

對於Cone NAT要採用UDP打洞.須要一個公網機器C來充當」介紹人」. 內網的A,B先分別和C通訊.打開各自的NAT端口. C這個時候知道A,B的公網IP: Port. 如今A和B想直接鏈接.好比A給B發.除非B是Full Cone.不然不能通訊.反之亦然. 可是咱們能夠這樣: A要鏈接B.A給B發一個UDP包.同時.A讓那個介紹人給B發一個命令,讓B同時給A發一個UDP包.這樣雙方的NAT都會記錄對方的IP,而後就會容許互相通訊.

同一個NAT後面的狀況

若是A,B在同一個NAT後面.若是用上面的技術來進行互連.那麼若是NAT支持loopback(就是本地到本地的轉換),A,B能夠鏈接,可是比較浪費帶寬和NAT.有一種辦法是,A,B和介紹人通訊的時候,同時把本身的local IP也告訴服務器.A,B通訊的時候,同時發local ip和公網IP.誰先到就用哪一個IP.可是local ip就有可能不知道發到什麼地方去了.好比A,B在不一樣的NAT後面可是他們各自的local ip段同樣.A給B的local IP發的UDP就可能發給本身內部網裏面的了.

還有一個辦法是服務器來判斷A,B是否在一個NAT後面.(網絡拓樸不一樣會不會有問題?)

把4種類型分別標爲1234,有兩臺主機A:portA和B:portB(port都爲外網端口,是與打洞服務器通訊的端口),以及打洞服務器S,情景是B拿到了A:portA的信息,要與A通訊。 (1)、A爲類型1;不管B爲哪一種類型,均可以直接與A:portA udp通訊; (2)、A爲類型2;不管B爲哪一種類型,在A知道B以前都沒法直接鏈接,B給S發一個打洞請求,S轉發該請求到A。若B的類型爲1,則A:portA可直接與B:portB udp通訊;若B的類型爲2或3,則A:portA和B:portB各自向對方發送一個一字節的udp包,分別在本身的路由器上打洞,今後A:portA和B:portB可進行udp通訊;若B爲類型4,則portB在與不一樣的ip:port通訊時會不同,因此A:portA先向B發送一個一字節的udp包,在路由器上打洞,而後等待B:portB先發送數據,A:portA接收到B:portB的數據後,即知道portB,也可互通數據了; (3)、A爲類型3;不管B爲哪一種類型,在A知道B以前都沒法直接鏈接,B給S發一個打洞請求,S轉發該請求到A。若B的類型爲1,則A:portA可直接與B:portB udp通訊;若B的類型爲2或3,則A:portA和B:portB各自向對方發送一個一字節的udp包,分別在本身的路由器上打下洞,今後A:portA和B:portB可進行udp通訊;若B爲類型4,則portB在與不一樣的ip:port通訊時會不同,而A又要求知道portB的纔可以讓B:portB連進來,因此這種狀況A只能猜想與A:portA通訊的portB,通訊機率小; (4)、A爲類型4;不管B爲哪一種類型,在A知道B以前都沒法直接鏈接,B給S發一個打洞請求,S轉發該請求到A。若B的類型爲1,則A:portA可直接與B:portB udp通訊;若B的類型爲2,則B:portB先向A發送一個一字節的udp包,在路由器上打洞,而後等待A:portA先發送數據,B:portB接收到A:portA的數據後,即知道portA,也可互通數據了;若B的類型爲3,見(3)中B爲類型4的描述;若B爲4,雙方沒法知道對方的端口,沒法通訊。

STUN :RFC 5389 - Session Traversal Utilities for (NAT) (STUN) ICE:RFC 5245 - Interactive Connectivity Establishment (ICE): A Methodology for Network Address Translator (NAT) Traversal for Offer/Answer Protocols

測試結果記錄

目前在公司和在家中測試,stun返回結果都是對稱型nat。

<pre class="md-fences md-end-block" lang="" contenteditable="false" cid="n186" mdtype="fences" style="box-sizing: border-box; overflow: visible; font-family: monospace, monospace; font-size: 0.9rem; white-space: pre; line-height: 1.71429em; display: block; break-inside: avoid; text-align: left; background-image: ; background-position: var(--code-block-bg-color); background-size: ; background-repeat: var(--code-block-bg-color); background-attachment: ; background-origin: ; background-clip: ; position: relative !important; margin-bottom: 3em; padding-left: 1ch; padding-right: 1ch; margin-left: 2em; width: inherit;">
bolen@DESKTOP-G4LODA2 MINGW64 /c/projects/gocode/src/github.com/ccding/go-stun (master)
./go-stun.exe -v 2019/01/06 08:00:42 Do Test1 2019/01/06 08:00:42 Send To: 18.191.223.12:3478 2019/01/06 08:00:43 Received: {packet nil: false, local: 42.101.65.132:5031, remote: 18.191.223.12:3478, cha nged: <nil>, other: 18.219.110.18:3479, identical: false} 2019/01/06 08:00:43 Do Test2 2019/01/06 08:00:43 Send To: 18.191.223.12:3478 2019/01/06 08:00:52 Received: Nil 2019/01/06 08:00:52 Do Test1 2019/01/06 08:00:52 Send To: 18.219.110.18:3479 2019/01/06 08:00:53 Received: {packet nil: false, local: 42.101.65.134:28392, remote: 18.219.110.18:3479, ch anged: <nil>, other: 18.191.223.12:3478, identical: false} NAT Type: Symmetric NAT External IP Family: 1 External IP: 42.101.65.132 External Port: 5031 ​ bolen@DESKTOP-G4LODA2 MINGW64 /c/projects/gocode/src/github.com/ccding/go-stun (master) go build

bolen@DESKTOP-G4LODA2 MINGW64 /c/projects/gocode/src/github.com/ccding/go-stun (master)
./go-stun.exe -v 2019/01/06 08:03:57 Do Test1 2019/01/06 08:03:57 Send To: 217.10.68.145:10000 2019/01/06 08:03:58 Received: {packet nil: false, local: 42.101.65.133:18231, remote: 217.10.68.145:10000, c hanged: 217.116.122.141:10001, other: <nil>, identical: false} 2019/01/06 08:03:58 Do Test2 2019/01/06 08:03:58 Send To: 217.10.68.145:10000 2019/01/06 08:04:07 Received: Nil 2019/01/06 08:04:07 Do Test1 2019/01/06 08:04:07 Send To: 217.116.122.141:10001 2019/01/06 08:04:07 Received: {packet nil: false, local: 42.101.65.134:19019, remote: 217.116.122.141:10001, changed: 217.10.68.145:10000, other: <nil>, identical: false} NAT Type: Symmetric NAT External IP Family: 1 External IP: 42.101.65.133 External Port: 18231 ​ bolen@DESKTOP-G4LODA2 MINGW64 /c/projects/gocode/src/github.com/ccding/go-stun (master) ./go-stun.exe -v
2019/01/06 08:05:34 Do Test1
2019/01/06 08:05:34 Send To: 217.10.68.145:10000
2019/01/06 08:05:34 Received: {packet nil: false, local: 42.101.65.133:23317, remote: 217.10.68.145:10000, changed: 217.116 .122.141:10001, other: <nil>, identical: false}
2019/01/06 08:05:34 Do Test2
2019/01/06 08:05:34 Send To: 217.10.68.145:10000
2019/01/06 08:05:44 Received: Nil
2019/01/06 08:05:44 Do Test1
2019/01/06 08:05:44 Send To: 217.116.122.141:10001
2019/01/06 08:05:44 Received: {packet nil: false, local: 42.101.65.134:44004, remote: 217.116.122.141:10001, changed: 217.1 0.68.145:10000, other: <nil>, identical: false}
NAT Type: Symmetric NAT
External IP Family: 1
External IP: 42.101.65.133
External Port: 23317

bolen@DESKTOP-G4LODA2 MINGW64 /c/projects/gocode/src/github.com/ccding/go-stun (master)
$ ./go-stun.exe -v
2019/01/06 08:06:02 Do Test1
2019/01/06 08:06:02 Send To: 217.10.68.145:10000
2019/01/06 08:06:03 Received: {packet nil: false, local: 42.101.65.133:13858, remote: 217.10.68.145:10000, changed: 217.116 .122.141:10001, other: <nil>, identical: false}
2019/01/06 08:06:03 Do Test2
2019/01/06 08:06:03 Send To: 217.10.68.145:10000
2019/01/06 08:06:12 Received: Nil
2019/01/06 08:06:12 Do Test1
2019/01/06 08:06:12 Send To: 217.116.122.141:10001
2019/01/06 08:06:13 Received: {packet nil: false, local: 42.101.65.134:25117, remote: 217.116.122.141:10001, changed: 217.1 0.68.145:10000, other: <nil>, identical: false}
NAT Type: Symmetric NAT
External IP Family: 1
External IP: 42.101.65.133
External Port: 13858</pre>

解決方案彙總

  1. stun 配合upnp(愛奇藝等內容服務商的方案)

    https://github.com/jflyup/nat_traversal (c語言實現的,repo裏聲稱解決了including symmetric NATs問題,網上有測試成功案例。)

    https://github.com/sjtcumt/wp/tree/master/p2p/p2psrv(解決了對稱nat-對稱nat,對稱nat-端口限制錐形nat的問題,但不穩定)

    https://github.com/ccding/go-stun

    以上stun解決對稱nat的問題,基本上都是利用了端口猜想和生日攻擊算法。

  2. ICE,可解決所有nat穿越問題。

相關文章
相關標籤/搜索