什麼是打洞,爲何要打洞
因爲Internet的快速發展 IPV4(網際協議版本4)地址不夠用,不能每一個主機分到一個公網IP 因此使用NAT地址轉換api
通常來講都是由私網內主機主動發起鏈接,數據包通過NAT地址轉換後送給公網上的服務器,鏈接創建之後可雙向傳送數據,NAT設備容許私網內主機主動向公網內主機發送數據,但卻禁止反方向的主動傳遞,但在一些特殊的場合須要不一樣私網內的主機進行互聯(例如P2P軟件、網絡會議、視頻傳輸等),TCP穿越NAT的問題必須解決。服務器
nat的幾種類型
如今基本使用這種,又分爲對稱和錐型NAT。網絡
錐型NAT,有徹底錐型、受限制錐型、端口受限制錐型三種:session
- a)Full Cone NAT(徹底圓錐型):從同一私網地址端口192.168.0.8:4000發至公網的全部請求都映射成同一個公網地址端口1.2.3.4:62000 ,192.168.0.8能夠收到任意外部主機發到1.2.3.4:62000的數據報。
- b)Address Restricted Cone NAT (地址限制圓錐型):從同一私網地址端口192.168.0.8:4000發至公網的全部請求都映射成同一個公網地址端口1.2.3.4:62000,只有當內部主機192.168.0.8先給服務器C 6.7.8.9發送一個數據報後,192.168.0.8才能收到6.7.8.9發送到1.2.3.4:62000的數據報。
- c)Port Restricted Cone NAT(端口限制圓錐型):從同一私網地址端口192.168.0.8:4000發至公網的全部請求都映射成同一個公網地址端口1.2.3.4:62000,只有當內部主機192.168.0.8先向外部主機地址端口6.7.8.9:8000發送一個數據報後,192.168.0.8才能收到6.7.8.9:8000發送到1.2.3.4:62000的數據報。
對稱NAT:socket
對於這種NAT。鏈接不一樣的外部Server,NAT打開的端口會變化。也就是內部機器A鏈接外網機器B時,NAT會打開一個端口,鏈接外網機器C時又會打開另一個端口。tcp
對於雙方都是Port Restricted Cone NAT的時候,則須要利用UDP打洞原理進行「先打洞,而後才能直接通訊」。spa
NAT進行打洞的流程與原理
若是A和B想要進行UDP通訊,則必須穿透雙方的NAT路由。假設爲NAT-A和NAT-B。設計
A發送數據包到公網S,B發送數據包到公網S,則S分別獲得了A和B的公網IP,code
S也和A B 分別創建了會話,由S發到NAT-A的數據包會被NAT-A直接轉發給A,視頻
由S發到NAT-B的數據包會被NAT-B直接轉發給B,除了S發出的數據包以外的則會被丟棄。
因此:如今A B 都能分別和S進行全雙工通信了,可是A B之間還不能直接通信。
解決辦法是:A向B的公網IP發送一個數據包,則NAT-A能接收來自NAT-B的數據包
並轉發給A了(即B如今能訪問A了);再由S命令B向A的公網IP發送一個數據包,則
NAT-B能接收來自NAT-A的數據包並轉發給B了(即A如今能訪問B了)。
以上就是「打洞」的原理。
UDP打洞的過程
一、雙方都經過UDP與服務器通信後,網關默認就是作了一個外網IP和端口號 與你內網IP與端口號的映射,這個無需設置的,服務器也不須要知道客戶的真正內網IP
二、用戶A先經過服務器知道用戶B的外網地址與端口
三、用戶A向用戶B的外網地址與端口發送消息,
四、在這一次發送中,用戶B的網關會拒收這條消息,由於它的映射中並無這條規則。
五、可是用戶A的網關就會增長了一條容許規則,容許接收從B發送過來的消息
六、服務器要求用戶B發送一個消息到用戶A的外網IP與端口號
七、用戶B發送一條消息,這時用戶A就能夠接收到B的消息,並且網關B也增長了容許規則
八、以後,因爲網關A與網關B都增長了容許規則,因此A與B均可以向對方的外網IP和端口號發送消息。
TCP打洞技術
tcp打洞也須要NAT設備支持才行。
tcp的打洞流程和udp的基本同樣,但tcp的api決定了tcp打洞的實現過程和udp不同。
tcp按cs方式工做,一個端口只能用來connect或listen,因此須要使用端口重用,才能利用本地nat的端口映射關係。(設置SO_REUSEADDR,在支持SO_REUSEPORT的系統上,要設置這兩個參數。)
鏈接過程:(以udp打洞的第2種狀況爲例(典型狀況))
nat後的兩個peer,A和B,A和B都bind本身listen的端口,向對方發起鏈接(connect),即便用相同的端口同時鏈接和等待鏈接。由於A和B發出鏈接的順序有時間差,假設A的syn包到達B的nat時,B的syn包尚未發出,那麼B的nat映射尚未創建,會致使A的鏈接請求失敗(鏈接失敗或沒法鏈接,若是nat返回RST或者icmp差錯,api上可能表現爲被RST;有些nat不返回信息直接丟棄syn包(反而更好)),(應用程序發現失敗時,不能關閉socket,closesocket()可能會致使NAT刪除端口映射;隔一段時間(1-2s)後未鏈接還要繼續嘗試);但後發B的syn包在到達A的nat時,因爲A的nat已經創建的映射關係,B的syn包會經過A的nat,被nat轉給A的listen端口,從而進去三次握手,完成tcp鏈接。
從應用程序角度看,鏈接成功的過程可能有兩種不一樣表現:(以上述假設過程爲例)
一、鏈接創建成功表現爲A的connect返回成功。即A端以TCP的同時打開流程完成鏈接。
二、A端經過listen的端口完成和B的握手,而connect嘗試持續失敗,應用程序經過accept獲取到鏈接,最終放棄connect(這時可closesocket(conn_fd))。
多數Linux和Windows的協議棧表現爲第2種。
但有一個問題是,創建鏈接的client端,其connect綁定的端口號就是主機listen的端口號,或許這個peer後續還會有更多的這種socket。雖然理論上說,socket是一個五元組,端口號是一個邏輯數字,傳輸層可以由於五元組的不一樣而區分開這些socket,可是是否存在實際上的異常,還有待更多觀察。
錐形nat打洞
對於Cone NAT.要採用UDP打洞.須要一個公網機器server C來充當」介紹人」.處於NAT以後的內網的A,B先分別和C通訊,打開各自的NAT端口.C這個時候知道A,B的公網IP: Port. 如今A和B想直接鏈接.好比A給B直接發包,除非B是Full Cone,不然不能通訊.反之亦然.
爲何啊?由於對於處於NAT以後的A,B。若是想A要與外界的D通訊,則首先必需要A發包到D,而後A通過NAT設備NA,NA把A的內網地址和端口轉換爲NA的外網地址和端口。和D通訊以後,D才能通過NA和A通訊。也就是說,只能A和外界主動通訊,外界不能主動和處於NA以後的A通訊。這種包會被NA直接丟棄的。這也就是上面所說的Port Restricted Cone 的情形啊! A(192.168.8.100:5000) -> NA(202.100.100.100:8000) -> D(292.88.88.88:2000)可是咱們能夠這樣.
A --- NA --- Server C --- NB --- B
- A,B 爲主機;
- NA, NB 爲NAT設備;
- Server C爲外網的機器;
- 若是A想與B通訊;
- A首先鏈接 C, C獲得A的外網NA的地址和端口;
- B也要鏈接C,C獲得B的外網NB的地址和端口;
- A告訴C說我要和B通信;
- C經過NB發信息給B,告訴B A的外網NA的地址和端口;
- B向NA發數據包(確定會被NA丟棄,由於NA上並無 A->NB 的合法session),可是NB上就創建了有B->NA的合法session了;
- B發數據包給C,讓 C 通知 A,我已經把洞打好了;
- A接受到通知後向 B 的外網發NB數據包,這樣就不會被丟棄掉了。由於對於NB來講,它看到的是A的外網NA的地址,而經過第6步,B已經讓NA成爲NB的合法通訊對象了。因此當NA發數據包給NB時,NB就會接收並轉發給B;
注意: 路由器和防火牆的UDP打洞的端口有個時間限制的,在必定時間內若是沒有數據通信會自動關閉
STUN
對稱型nat打洞
- 爲何能夠發送消息,但不過去呢?
IP協議中TTL
把ttl的值設置小一點,好比4,使其路由轉發的時候減到0,而把數據包丟棄
在IPv4中, TTL是IP協議的一個8個二進制位的值【0-255】. 這個值能夠被認爲是數據包在internet系統中能夠跳躍的次數上限. TTL是由數據包的發送者設置的, 在前往目的地的過程當中, 每通過一臺主機或設備, 這個值就要減小一. 若是在數據包到達目的地前, TTL值被減到了0,那麼這個包將做爲一個ICMP錯誤的數據包被丟棄。 Linux默認64
不少時候,咱們但願網絡中的兩臺主機可以直接進行通訊,即所謂的P2P通訊,而不須要其餘公共服務器的中轉。因爲主機可能位於防火牆或NAT以後,在進行P2P通訊以前,咱們須要進行檢測以確認它們之間可否進行P2P通訊以及如何通訊。這種技術一般稱爲NAT穿透(NAT Traversal)。最多見的NAT穿透是基於UDP的技術,如RFC3489中定義的STUN協議。
STUN和TURN技術淺析
STUN
STUN(Simple Traversal of User Datagram Protocol Through Network Address Translators),即簡單的用UDP穿透NAT,是個輕量級的協議,是基於UDP的完整的穿透NAT的解決方案。它容許應用程序發現它們與公共互聯網之間存在的NAT和防火牆及其餘類型。它也可讓應用程序肯定NAT分配給它們的公網IP地址和端口號。STUN是一種Client/Server的協議,也是一種Request/Response的協議,默認端口號是3478。
流程
應用程序(即STUN CLIENT)向NAT外的STUN SERVER經過UDP發送請求STUN 消息詢問自身的轉換後地址,STUN SERVER收到請求消息,產生響應消息,響應消息中攜帶請求消息的源端口,即STUN CLIENT在NAT上對應的外部端口。而後響應消息經過NAT發送給STUN CLIENT,STUN CLIENT經過響應消息體中的內容得知其在NAT上對應的外部地址,而且將其填入之後呼叫協議的UDP負載中,告知對端,同時還能夠在終端註冊時直接註冊這個轉換後的公有IP地址,這樣就解決了H.323/MGCP/SIP穿越NAT的通訊創建問題以及做爲被叫時的問題。本端的接收地址和端口號爲NAT外的地址和端口號。因爲經過STUN協議已在NAT上預先創建媒體流的NAT映射表項,故媒體流可順利穿越NAT。
另外STUN server並不是指一個專用的服務器,而是指一種功能、一個協議,咱們能夠在softswitch或者任何一個須要此功能的服務器上內置此協議, 後面代碼也包含一個簡單的Server實現。
可是在NAT採用對稱模式(symmetric NAT)工做時,STUN的方案就會出現問題。假如咱們在softswitch上提供STUN server功能,終端A經過STUN能夠得到NAT爲終端A與softswitch之間通訊分配的地址A',並將這個地址註冊在softswitch上,當一個公網上的終端B呼叫終端A時,A'和B經過softswitch完成呼叫創建過程。當B試圖向A'發送媒體流時,問題就出現了。由於對稱NAT只容許從softswitch發送數據給地址A',從B發送的媒體流將被丟棄。因此STUN沒法應用於工做在對稱模式的NAT.
STUN協議最大的優勢是無需現有NAT/FW設備作任何改動,同時STUN方式可在多個NAT串聯的網絡環境中使用. STUN的侷限性在於STUN並不適合支持TCP鏈接的穿越,同時STUN方式不支持對對稱NAT(Symmetric NAT).
TURN
在RFC5766中定義,英文全稱Traversal Using Relays around NAT(TURN):Relay Extensions to Session Traversal Utilities for NAT(STUN),即便用中繼穿透NAT:STUN的中繼擴展。簡單的說,TURN與STUN的共同點都是經過修改應用層中的私網地址達到NAT穿透的效果,異同點是TURN是經過兩方通信的「中間人」方式實現穿透。
若是一個主機位於NAT的後面,在某些狀況下它不可以與其餘主機點對點直接鏈接。在這些狀況下,它須要使用中間網點提供的中繼鏈接服務。TURN協議就是用來容許主機控制中繼的操做而且使用中繼與對端交換數據。TURN與其餘中繼控制協議不一樣的是它可以容許一個客戶端使用一箇中繼地址與多個對端鏈接。
TURN協議被設計爲ICE的一部分,用於NAT穿越,雖然如此,它也能夠在沒有ICE的地方單獨使用。