性无码一区二区三区在线观看,少妇被爽到高潮在线观看,午夜精品一区二区三区,无码中文字幕人妻在线一区二区三区,无码精品国产一区二区三区免费

NAT穿透的工作原理

一、引言

1.1 背景:IPv4地址短缺,引入NAT

全球IPv4地址早已不夠用,因此人們發(fā)明了NAT(網(wǎng)絡(luò)地址轉(zhuǎn)換)來(lái)緩解這個(gè)問(wèn)題。

簡(jiǎn)單來(lái)說(shuō),大部分機(jī)器都使用私有IP地址,如果它們需要訪問(wèn)公網(wǎng)服務(wù),那么,

  • 出向流量:需要經(jīng)過(guò)一臺(tái)NAT設(shè)備,它會(huì)對(duì)流量進(jìn)行SNAT,將私有srcIP+Port轉(zhuǎn)換成NAT設(shè)備的公網(wǎng)IP+Port(這樣應(yīng)答包才能回來(lái)),然后再將包發(fā)出去;
  • 應(yīng)答流量(入向):到達(dá)NAT設(shè)備后進(jìn)行相反的轉(zhuǎn)換,然后再轉(zhuǎn)發(fā)給客戶端。

整個(gè)過(guò)程對(duì)雙方透明。

更多關(guān)于NAT的內(nèi)容,可參考(譯)NAT-網(wǎng)絡(luò)地址轉(zhuǎn)換(2016)。譯注。

以上就是本文所討論的問(wèn)題的基本背景。

1.2 需求:兩臺(tái)經(jīng)過(guò)NAT的機(jī)器建立點(diǎn)對(duì)點(diǎn)連接

在以上所描述的NAT背景下,我們從最簡(jiǎn)單的問(wèn)題開(kāi)始:如何在兩臺(tái)經(jīng)過(guò)NAT的機(jī)器之間建立點(diǎn)對(duì)點(diǎn)連接(直連)。如下圖所示:

直接用機(jī)器的IP互連顯然是不行的,因?yàn)樗鼈兌际撬接蠭P(例如192.168.1.x)。在Tailscale中,我們會(huì)建立一個(gè)WireGuard®隧道來(lái)解決這個(gè)問(wèn)題——但這并不是太重要,因?yàn)槲覀儗?/span>過(guò)去幾代人努力都整合到了一個(gè)工具集,這些技術(shù)廣泛適用于各種場(chǎng)景。例如,

  1. WebRTC使用這些技術(shù)在瀏覽器之間完成peer-to-peer語(yǔ)音、視頻和數(shù)據(jù)傳輸,
  2. VoIP電話和一些視頻游戲也使用類(lèi)似機(jī)制,雖然不是所有情況下都很成功。

接下來(lái),本文將在一般意義上討論這些技術(shù),并在合適的地方拿Tailscale和其他一些東西作為例子。

1.3 方案:NAT穿透

1.3.1 兩個(gè)必備前提:UDP+能直接控制socket

如果想設(shè)計(jì)自己的協(xié)議來(lái)實(shí)現(xiàn)NAT穿透,那必須滿足以下兩個(gè)條件:

  1. 協(xié)議應(yīng)該基于UDP。

理論上用TCP也能實(shí)現(xiàn),但它會(huì)給本已相當(dāng)復(fù)雜的問(wèn)題再增加一層復(fù)雜性,甚至還需要定制化內(nèi)核——取決于你想實(shí)現(xiàn)到什么程度。本文接下來(lái)都將關(guān)注在UDP上。

如果考慮TCP是想在NAT穿透時(shí)獲得面向流的連接(stream-orientedconnection),可以考慮用QUIC來(lái)替代,它構(gòu)建在UDP之上,因此我們能將關(guān)注點(diǎn)放在UDPNAT穿透,而仍然能獲得一個(gè)很好的流協(xié)議(streamprotocol)。

  1. 對(duì)收發(fā)包的socket有直接控制權(quán)。

例如,從經(jīng)驗(yàn)上來(lái)說(shuō),無(wú)法基于某個(gè)現(xiàn)有的網(wǎng)絡(luò)庫(kù)實(shí)現(xiàn)NAT穿透,因?yàn)槲覀?/span>必須在使用的“主要”協(xié)議之外,發(fā)送和接收額外的數(shù)據(jù)包。

某些協(xié)議(例如WebRTC)將NAT穿透與其他部分緊密集成。但如果你在構(gòu)建自己的協(xié)議,建議將NAT穿透作為一個(gè)獨(dú)立實(shí)體,與主協(xié)議并行運(yùn)行,二者僅僅是共享socket的關(guān)系,如下圖所示,這將帶來(lái)很大幫助:

1.3.2 保底方式:中繼

在某些場(chǎng)景中,直接訪問(wèn)socket這一條件可能很難滿足。

退而求其次的一個(gè)方式是設(shè)置一個(gè)localproxy(本地代理),主協(xié)議與這個(gè)proxy通信,后者來(lái)完成NAT穿透,將包中繼(relay)給對(duì)端。這種方式增加了一個(gè)額外的間接成本,但好處是:

  1. 仍然能獲得NAT穿透,
  2. 不需要對(duì)已有的應(yīng)用程序做任何改動(dòng)。

1.4 挑戰(zhàn):有狀態(tài)防火墻和NAT設(shè)備

有了以上鋪墊,下面就從最基本的原則開(kāi)始,一步步看如何實(shí)現(xiàn)一個(gè)企業(yè)級(jí)的NAT穿透方案。

我們的目標(biāo)是:在兩個(gè)設(shè)備之間通過(guò)UDP實(shí)現(xiàn)雙向通信,有了這個(gè)基礎(chǔ),上層的其他協(xié)議(WireGuard,QUIC,WebRTC等)就能做一些更酷的事情。

但即便這個(gè)看似最基本的功能,在實(shí)現(xiàn)上也要解決兩個(gè)障礙

  1. 有狀態(tài)防火墻
  2. NAT設(shè)備

二、穿透防火墻

有狀態(tài)防火墻是以上兩個(gè)問(wèn)題中相對(duì)比較容易解決的。實(shí)際上,大部分NAT設(shè)備都自帶了一個(gè)有狀態(tài)防火墻,因此要解決第二個(gè)問(wèn)題,必須先解決有第一個(gè)問(wèn)題。

有狀態(tài)防火墻具體有很多種類(lèi)型,有些你可能見(jiàn)過(guò):

  • WindowsDefenderfirewall
  • Ubuntu’sufw(usingiptables/nftables)
  • BSD/macOSpf
  • AWSSecurityGroups(安全組

2.1 有狀態(tài)防火墻

2.1.1 默認(rèn)行為(策略)

以上防火墻的配置都是很靈活的,但大部分配置默認(rèn)都是如下行為:

  1. 允許所有出向連接(allowsall“outbound”connections)
  2. 禁止所有入向連接(blocksall“inbound”connections)

可能有少量例外規(guī)則,例如allowinginboundSSH。

2.1.2 如何區(qū)分入向和出向包

連接(connection)和方向(direction)都是協(xié)議設(shè)計(jì)者頭腦中的概念,到了物理傳輸層,每個(gè)連接都是雙向的;允許所有的寶物雙向傳輸。那防火墻是如何區(qū)分哪些是入向包、哪些是出向包的呢?這就要回到“有狀態(tài)”(stateful)這三個(gè)字了:有狀態(tài)防火墻會(huì)記錄它看到的每個(gè)包,當(dāng)收到下一個(gè)包時(shí),會(huì)利用這些信息(狀態(tài))來(lái)判斷應(yīng)該做什么。

對(duì)UDP來(lái)說(shuō),規(guī)則很簡(jiǎn)單:如果防火墻之前看到過(guò)一個(gè)出向包(outbound),就會(huì)允許相應(yīng)的入向包(inbound)通過(guò),以下圖為例:

筆記本電腦中自帶了一個(gè)防火墻,當(dāng)該防火墻看到從這臺(tái)機(jī)器出去的2.2.2.2:1234->5.5.5.5:5678包時(shí),就會(huì)記錄一下:5.5.5.5:5678->2.2.2.2:1234入向包應(yīng)該放行。這里的邏輯是:我們信任的世界(即筆記本)想主動(dòng)與5.5.5.5:5678通信,因此應(yīng)該放行(allow)其回報(bào)路徑。

某些非常寬松的防火墻只要看到有從2.2.2.2:1234出去的包,就會(huì)允許所有從外部進(jìn)入2.2.2.2:1234的流量。這種防火墻對(duì)我們的NAT穿透來(lái)說(shuō)非常友好,但已經(jīng)越來(lái)越少見(jiàn)了。

2.2 防火墻朝向(face-off)與穿透方案

2.2.1 防火墻朝向相同

場(chǎng)景特點(diǎn):服務(wù)端IP可直接訪問(wèn)

在NAT穿透場(chǎng)景中,以上默認(rèn)規(guī)則對(duì)UDP流量的影響不大——只要路徑上所有防火墻的“朝向”是一樣的。一般來(lái)說(shuō),從內(nèi)網(wǎng)訪問(wèn)公網(wǎng)上的某個(gè)服務(wù)器都屬于這種情況。

我們唯一的要求是:連接必須是由防火墻后面的機(jī)器發(fā)起的。這是因?yàn)樵谒鲃?dòng)和別人通信之前,沒(méi)人能主動(dòng)和它通信,如下圖所示:

穿透方案:客戶端直連服務(wù)端,或hub-and-spoke拓?fù)?/span>

但上圖是假設(shè)了通信雙方中,其中一端(服務(wù)端)是能直接訪問(wèn)到的。在VPN場(chǎng)景中,這就形成了所謂的hub-and-spoke拓?fù)?/span>:中心的hub沒(méi)有任何防火墻策略,誰(shuí)都能訪問(wèn)到;防火墻后面的spokes連接到hub。如下圖所示:

2.2.2 防火墻朝向不同的方向

場(chǎng)景特點(diǎn):服務(wù)端IP不可直接訪問(wèn)

但如果兩個(gè)“客戶端”相連,以上方式就不行了,此時(shí)兩邊的防火墻相向而立,如下圖所示:

根據(jù)前面的討論,這種情況意味著:兩邊要同時(shí)發(fā)起連接請(qǐng)求,但也意味著兩邊都無(wú)法發(fā)起有效請(qǐng)求,因?yàn)閷?duì)方先發(fā)起請(qǐng)求才能在它的防火墻上打開(kāi)一條縫讓我們進(jìn)去!如何破解這個(gè)問(wèn)題呢?一種方式是讓用戶重新配置一邊或兩邊的防火墻,打開(kāi)一個(gè)端口,允許對(duì)方的流量進(jìn)來(lái)。

  1. 這顯然對(duì)用戶不友好,在像Tailscale這樣的mesh網(wǎng)絡(luò)中的擴(kuò)展性也不好,在mesh網(wǎng)絡(luò)中,我們假設(shè)對(duì)端會(huì)以一定的粒度在公網(wǎng)上移動(dòng)。
  2. 此外,在很多情況下用戶也沒(méi)有防火墻的控制權(quán)限:例如在咖啡館或機(jī)場(chǎng)中,連接的路由器是不受你控制的(否則你可能就有麻煩了)。

因此,我們需要尋找一種不用重新配置防火墻的方式。

穿透方案:兩邊同時(shí)主動(dòng)建連,在本地防火墻為對(duì)方打開(kāi)一個(gè)洞

解決的思路還是先重新審視前面提到的有狀態(tài)防火墻規(guī)則:

  • 對(duì)于UDP,其規(guī)則(邏輯)是:包必須先出去才能進(jìn)來(lái)(packetsmustflowoutbeforepacketscanflowbackin)。
  • 注意,這里除了要滿足包的IP和端口要匹配這一條件之外,并沒(méi)有要求包必須是相關(guān)的(related)。換句話說(shuō),只要某些包帶著正確的來(lái)源和目的地址出去了,任何看起來(lái)像是響應(yīng)的包都會(huì)被防火墻放進(jìn)來(lái)——即使對(duì)端根本沒(méi)收到你發(fā)出去的包。

因此,要穿透這些有狀態(tài)防火墻,我們只需要共享一些信息:讓兩端提前知道對(duì)方使用的ip:port

  • 手動(dòng)靜態(tài)配置是一種方式,但顯然擴(kuò)展性不好;
  • 我們開(kāi)發(fā)了一個(gè)coordinationserver,以靈活、安全的方式來(lái)同步ip:port信息。

有了對(duì)方的ip:port信息之后,兩端開(kāi)始給對(duì)方發(fā)送UDP包。在這個(gè)過(guò)程中,我們預(yù)料到某些寶物將會(huì)被丟棄。因此,雙方必須要接受某些包會(huì)丟失的事實(shí),因此如果是重要信息,你必須自己準(zhǔn)備好重傳。對(duì)UDP來(lái)說(shuō)丟包是可接受的,但這里尤其需要接受。

來(lái)看一下具體建連(穿透)過(guò)程:

  1. 如圖所示,筆記本出去的第一包,2.2.2.2:1234->7.7.7.7:5678,穿過(guò)WindowsDefender防火墻進(jìn)入到公網(wǎng)。

對(duì)方的防火墻會(huì)將這個(gè)包攔截掉,因?yàn)樗鼪](méi)有7.7.7.7:5678->2.2.2.2:1234的流量記錄。但另一方面,WindowsDefender此時(shí)已經(jīng)記錄了出向連接,因此會(huì)允許7.7.7.7:5678->2.2.2.2:1234的應(yīng)答包進(jìn)來(lái)。

  1. 接著,第一個(gè)7.7.7.7:5678->2.2.2.2:1234穿過(guò)它自己的防火墻到達(dá)公網(wǎng)。

到達(dá)客戶端側(cè)時(shí),WindowsDefender認(rèn)為這是剛才出向包的應(yīng)答包,因此就放行它進(jìn)入了!此外,右側(cè)的防火墻此時(shí)也記錄了:2.2.2.2:1234->7.7.7.7:5678的包應(yīng)該放行。

  1. 筆記本收到服務(wù)器發(fā)來(lái)的包之后,發(fā)送一個(gè)包作為應(yīng)答。這個(gè)包穿過(guò)WindowsDefender防火墻和服務(wù)端防火墻(因?yàn)檫@是對(duì)服務(wù)端發(fā)送的包的應(yīng)答包),達(dá)到服務(wù)端。

成功!這樣我們就建立了一個(gè)穿透兩個(gè)相向防火墻的雙向通信連接。而初看之下,這項(xiàng)任務(wù)似乎是不可能完成的。

2.3 關(guān)于穿透防火墻的一些思考

穿透防火墻并非永遠(yuǎn)這么輕松,有時(shí)會(huì)受一些第三方系統(tǒng)的間接影響,需要仔細(xì)處理。那穿透防火墻需要注意什么呢?重要的一點(diǎn)是:通信雙方必須幾乎同時(shí)發(fā)起通信,這樣才能在路徑上的防火墻打開(kāi)一條縫,而且兩端還都是活著的。

2.3.1 雙向主動(dòng)建連:旁路信道

如何實(shí)現(xiàn)“同時(shí)”呢?一種方式是兩端不斷重試,但顯然這種方式很浪費(fèi)資源。假如雙方都知道何時(shí)開(kāi)始建連就好了。

  • 這聽(tīng)上去是雞生蛋蛋生雞的問(wèn)題了:雙方想要通信,必須先提前通個(gè)信。
  • 但實(shí)際上,我們可以通過(guò)旁路信道(sidechannel)來(lái)達(dá)到這個(gè)目的,并且這個(gè)旁路信道并不需要很fancy:它可以有幾秒鐘的延遲、只需要傳送幾KB的信息,因此即使是一個(gè)配置非常低的虛擬機(jī),也能為幾千臺(tái)機(jī)器提供這樣的旁路通信服務(wù)。在遙遠(yuǎn)的過(guò)去,我曾用XMPP聊天消息作為旁路,效果非常不錯(cuò)。另一個(gè)例子是WebRTC,它需要你提供一個(gè)自己的“信令信道”(signallingchannel,這個(gè)詞也暗示了WebRTC的IPtelephonyancestry),并將其配置到WebRTCAPI。在Tailscale,我們的協(xié)調(diào)服務(wù)器(coordinationserver)和DERP(DetourEncryptedRoutingProtocol)服務(wù)器集群是我們的旁路信道。

2.3.2 非活躍連接被防火墻清理

有狀態(tài)防火墻內(nèi)存通常比較有限,因此會(huì)定期清理不活躍的連接(UDP常見(jiàn)的是30s),因此要保持連接alive的話需要定期通信,否則就會(huì)被防火墻關(guān)閉,為避免這個(gè)問(wèn)題,我們,

  1. 要么定期向?qū)Ψ桨l(fā)包來(lái)keepalive,
  2. 要么有某種帶外方式來(lái)按需重建連接。

2.3.3 問(wèn)題都解決了?不,挑戰(zhàn)剛剛開(kāi)始

對(duì)于防火墻穿透來(lái)說(shuō),我們并不需要關(guān)心路徑上有幾堵墻——只要它們是有狀態(tài)防火墻且允許出向連接,這種同時(shí)發(fā)包(simultaneoustransmission)機(jī)制就能穿透任意多層防火墻。這一點(diǎn)對(duì)我們來(lái)說(shuō)非常友好,因?yàn)橹恍枰獙?shí)現(xiàn)一個(gè)邏輯,然后能適用于任何地方了。

…對(duì)嗎?

其實(shí),不完全對(duì)。這個(gè)機(jī)制有效的前提是:我們能提前知道對(duì)方的ip:port。而這就涉及到了我們今天的主題:NAT,它會(huì)使前面我們剛獲得的一點(diǎn)滿足感頓時(shí)消失。

下面,進(jìn)入本文正題

三、NAT的本質(zhì)

3.1 NAT設(shè)備與有狀態(tài)防火墻

可以認(rèn)為NAT設(shè)備是一個(gè)增強(qiáng)版的有狀態(tài)防火墻,雖然它的增強(qiáng)功能對(duì)于本文場(chǎng)景來(lái)說(shuō)并不受歡迎:除了前面提到的有狀態(tài)攔截/放行功能之外,它們還會(huì)在數(shù)據(jù)包經(jīng)過(guò)時(shí)修改這些包。

3.2 NAT穿透與SNAT/DNAT

具體來(lái)說(shuō),NAT設(shè)備能完成某種類(lèi)型的網(wǎng)絡(luò)地址轉(zhuǎn)換,例如,替換源或目的IP地址或端口。

  • 討論連接問(wèn)題和NAT穿透問(wèn)題時(shí),我們只會(huì)受sourceNAT——SNAT的影響
  • DNAT不會(huì)影響NAT穿透。

3.3 SNAT的意義:解決IPv4地址短缺問(wèn)題

SNAT最常見(jiàn)的使用場(chǎng)景是將很多設(shè)備連接到公網(wǎng),而只使用少數(shù)幾個(gè)公網(wǎng)IP。例如對(duì)于消費(fèi)級(jí)路由器,會(huì)將所有設(shè)備的(私有)IP地址映射為單個(gè)連接到公網(wǎng)的IP地址。

這種方式存在的意義是:我們有遠(yuǎn)多于可用公網(wǎng)IP數(shù)量的設(shè)備需要連接到公網(wǎng),(至少對(duì)IPv4來(lái)說(shuō)如此,IPv6的情況后面會(huì)討論)。NAT使多個(gè)設(shè)備能共享同一IP地址,因此即使面臨IPv4地址短缺的問(wèn)題,我們?nèi)匀荒懿粩鄶U(kuò)張互聯(lián)網(wǎng)的規(guī)模。

3.4 SNAT過(guò)程:以家用路由器為例

假設(shè)你的筆記本連接到家里的WiFi,下面看一下它連接到公網(wǎng)某個(gè)服務(wù)器時(shí)的情形:

  1. 筆記本發(fā)送UDPpacket192.168.0.20:1234->7.7.7.7:5678。

這一步就好像筆記本有一個(gè)公網(wǎng)IP一樣,但源地址192.168.0.20是私有地址,只能出現(xiàn)在私有網(wǎng)絡(luò),公網(wǎng)不認(rèn),收到這樣的包時(shí)它不知道如何應(yīng)答。

  1. 家用路由器出場(chǎng),執(zhí)行SNAT。

包經(jīng)過(guò)路由器時(shí),路由器發(fā)現(xiàn)這是一個(gè)它沒(méi)有見(jiàn)過(guò)的新會(huì)話(session)。它知道192.168.0.20是私有IP,公網(wǎng)無(wú)法給這樣的地址回包,但它有辦法解決:

  • 在它自己的公網(wǎng)IP上挑一個(gè)可用的UDP端口,例如2.2.2.2:4242,然后創(chuàng)建一個(gè)NATmapping:192.168.0.20:1234<-->2.2.2.2:4242,然后將包發(fā)到公網(wǎng),此時(shí)源地址變成了2.2.2.2:4242而不是原來(lái)的192.168.0.20:1234。因此服務(wù)端看到的是轉(zhuǎn)換之后地址,接下來(lái),每個(gè)能匹配到這條映射規(guī)則的包,都會(huì)被路由器改寫(xiě)IP和端口。

  1. 反向路徑是類(lèi)似的,路由器會(huì)執(zhí)行相反的地址轉(zhuǎn)換,將2.2.2.2:4242變回192.168.0.20:1234。對(duì)于筆記本來(lái)說(shuō),它根本感知不知道這正反兩次變換過(guò)程。

這里是拿家用路由器作為例子,但辦公網(wǎng)的原理是一樣的。不同之處在于,辦公網(wǎng)的NAT可能有多臺(tái)設(shè)備組成(高可用、容量等目的),而且它們有不止一個(gè)公網(wǎng)IP地址可用,因此在選擇可用的公網(wǎng)ip:port來(lái)做映射時(shí),選擇空間更大,能支持更多客戶端。

3.5 SNAT給穿透帶來(lái)的挑戰(zhàn)

現(xiàn)在我們遇到了與前面有狀態(tài)防火墻類(lèi)似的情況,但這次是NAT設(shè)備:通信雙方不知道對(duì)方的ip:port是什么,因此無(wú)法主動(dòng)建連,如下圖所示:

但這次比有狀態(tài)防火墻更糟糕,嚴(yán)格來(lái)說(shuō),在雙方發(fā)包之前,根本無(wú)法確定(自己及對(duì)方的)ip:port信息,因?yàn)?/span>只有出向包經(jīng)過(guò)路由器之后才會(huì)產(chǎn)生NATmapping(即,可以被對(duì)方連接的ip:port信息)。

因此我們又回到了與防火墻遇到的問(wèn)題,并且情況更糟糕:雙方都需要主動(dòng)和對(duì)方建連,但又不知道對(duì)方的公網(wǎng)地址是多少,只有當(dāng)對(duì)方先說(shuō)話之后,我們才能拿到它的地址信息。

如何破解以上死鎖呢?這就輪到STUN登場(chǎng)了。

四、穿透“NAT+防火墻”:STUN(SessionTraversalUtilitiesforNAT)協(xié)議

STUN既是一些對(duì)NAT設(shè)備行為的詳細(xì)研究,也是一種協(xié)助NAT穿透的協(xié)議。本文主要關(guān)注STUN協(xié)議。

4.1 STUN原理

STUN基于一個(gè)簡(jiǎn)單的觀察:從一個(gè)會(huì)被NAT的客戶端訪問(wèn)公網(wǎng)服務(wù)器時(shí),服務(wù)器看到的是NAT設(shè)備的公網(wǎng)ip:port地址,而非該客戶端的局域網(wǎng)ip:port地址。

也就是說(shuō),服務(wù)器能告訴客戶端它看到的客戶端的ip:port是什么。因此,只要將這個(gè)信息以某種方式告訴通信對(duì)端(peer),后者就知道該和哪個(gè)地址建連了!這樣就又簡(jiǎn)化為前面的防火墻穿透問(wèn)題了。

本質(zhì)上這就是STUN協(xié)議的工作原理,如下圖所示:

  • 筆記本向STUN服務(wù)器發(fā)送一個(gè)請(qǐng)求:“從你的角度看,我的地址什么?”
  • STUN服務(wù)器返回一個(gè)響應(yīng):“我看到你的UDP包是從這個(gè)地址來(lái)的:ip:port”。

4.2 為什么NAT穿透邏輯和主協(xié)議要共享同一個(gè)socket

理解了STUN原理,也就能理解為什么我們?cè)谖恼麻_(kāi)頭說(shuō),如果要實(shí)現(xiàn)自己的NAT穿透邏輯和主協(xié)議,就必須讓二者共享同一個(gè)socket

  1. 每個(gè)socket在NAT設(shè)備上都對(duì)應(yīng)一個(gè)映射關(guān)系(私網(wǎng)地址->公網(wǎng)地址),
  2. STUN服務(wù)器只是輔助穿透的基礎(chǔ)設(shè)施,
  3. 與STUN服務(wù)器通信之后,在NAT及防火墻設(shè)備上打開(kāi)了一個(gè)連接,允許入向包進(jìn)來(lái)(回憶前面內(nèi)容,只要目的地址對(duì),UDP包就能進(jìn)來(lái),不管這些包是不是從STUN服務(wù)器來(lái)的),
  4. 因此,接下來(lái)只要將這個(gè)地址告訴我們的通信對(duì)端(peer),讓它往這個(gè)地址發(fā)包,就能實(shí)現(xiàn)穿透了。

4.3 STUN的問(wèn)題:不能穿透所有NAT設(shè)備(例如企業(yè)級(jí)NAT網(wǎng)關(guān))

有了STUN,我們的穿透目的似乎已經(jīng)實(shí)現(xiàn)了:每臺(tái)機(jī)器都通過(guò)STUN來(lái)獲取自己的私網(wǎng)socket對(duì)應(yīng)的公網(wǎng)ip:port,然后把這個(gè)信息告訴對(duì)端,然后兩端同時(shí)發(fā)起穿透防火墻的嘗試,后面的過(guò)程就和上一節(jié)介紹的防火墻穿透一樣了,對(duì)嗎?

答案是:看情況。某些情況下確實(shí)如此,但有些情況下卻不行。通常來(lái)說(shuō),

  • 對(duì)于大部分家用路由器場(chǎng)景,這種方式是沒(méi)問(wèn)題的;
  • 但對(duì)于一些企業(yè)級(jí)NAT網(wǎng)關(guān)來(lái)說(shuō),這種方式無(wú)法奏效。

NAT設(shè)備的說(shuō)明書(shū)上越強(qiáng)調(diào)它的安全性,STUN方式失敗的可能性就越高。(但注意,從實(shí)際意義上來(lái)說(shuō),NAT設(shè)備在任何方面都并不會(huì)增強(qiáng)網(wǎng)絡(luò)的安全性,但這不是本文重點(diǎn),因此不展開(kāi)。)

4.4 重新審視STUN的前提

再次審視前面關(guān)于STUN的假設(shè):當(dāng)STUN服務(wù)器告訴客戶端在公網(wǎng)看來(lái)它的地址是2.2.2.2:4242時(shí),那所有目的地址是2.2.2.2:4242的包就都能穿透防火墻到達(dá)該客戶端。

這也正是問(wèn)題所在:這一點(diǎn)并不總是成立。

  • 某些NAT設(shè)備的行為與我們假設(shè)的一致,它們的有狀態(tài)防火墻組件只要看到有客戶端自己發(fā)起的出向包,就會(huì)允許相應(yīng)的入向包進(jìn)入;因此只要利用STUN功能,再加上兩端同時(shí)發(fā)起防火墻穿透,就能把連接打通;
  • 另外一些NAT設(shè)備就要困難很多了,它會(huì)針對(duì)每個(gè)目的地址來(lái)生成一條相應(yīng)的映射關(guān)系。在這樣的設(shè)備上,如果我們用相同的socket來(lái)分別發(fā)送數(shù)據(jù)包到5.5.5.5:1234and7.7.7.7:2345,我們就會(huì)得到2.2.2.2上的兩個(gè)不同的端口,每個(gè)目的地址對(duì)應(yīng)一個(gè)。如果反向包的端口用的不對(duì),包就無(wú)法通過(guò)防火墻。如下圖所示:

五、中場(chǎng)補(bǔ)課:NAT正式術(shù)語(yǔ)

知道NAT設(shè)備的行為并不是完全一樣之后,我們來(lái)引入一些正式術(shù)語(yǔ)。

5.1 早期術(shù)語(yǔ)

如果之前接觸過(guò)NAT穿透,可能會(huì)聽(tīng)說(shuō)過(guò)下面這些名詞:

  • “FullCone”
  • “RestrictedCone”
  • “Port-RestrictedCone”
  • “Symmetric”NATs

這些都是NAT穿透領(lǐng)域的早期術(shù)語(yǔ)。

但其實(shí)這些術(shù)語(yǔ)相當(dāng)讓人困惑。我每次都要查一下RestrictedConeNAT是什么意思。從實(shí)際經(jīng)驗(yàn)來(lái)看,我并不是唯一對(duì)此感到困惑的人。例如,如今互聯(lián)網(wǎng)上將“easy”NAT歸類(lèi)為FullCone,而實(shí)際上它們更應(yīng)該歸類(lèi)為Port-RestrictedCone。

5.2 近期研究與新術(shù)語(yǔ)

最近的一些研究和RFC已經(jīng)提出了一些更準(zhǔn)確的術(shù)語(yǔ)。

  • 首先,它們明確了如下事實(shí):NAT設(shè)備的行為差異表現(xiàn)在多個(gè)維度,而并非只有早期研究中所說(shuō)的“cone”這一個(gè)維度,因此基于“cone”來(lái)劃分類(lèi)別并不是很有幫助。
  • 其次,新研究和新術(shù)語(yǔ)能更準(zhǔn)確地描述NAT在做什么。

前面提到的所謂"easy"和"hard"NAT,只在一個(gè)維度有不同:NAT映射是否考慮到目的地址信息。RFC4787中,

  • easyNAT及其變種稱為“Endpoint-IndependentMapping”(EIM,終點(diǎn)無(wú)關(guān)的映射)

但是,從“命名很難”這一程序員界的偉大傳統(tǒng)來(lái)說(shuō),EIM這個(gè)詞其實(shí)也并不是100%準(zhǔn)確,因?yàn)檫@種NAT仍然依賴endpoint,只不過(guò)依賴的是源endpoint:每個(gè)sourceip:port對(duì)應(yīng)一個(gè)映射——否則你的包就會(huì)和別人的包混在一起,導(dǎo)致混亂。

嚴(yán)格來(lái)說(shuō),EIM應(yīng)該稱為“DestinationEndpointIndependentMapping”(DEIM?),但這個(gè)名字太拗口了,而且按照慣例,Endpoint永遠(yuǎn)指的是DestinationEndpoint。

  • hardNAT以及變種稱為“Endpoint-DependentMapping”(EDM,終點(diǎn)相關(guān)的映射)。

EDM中還有一個(gè)子類(lèi)型,依據(jù)是只根據(jù)dst_ip做映射,還是根據(jù)dst_ip+dst_port做映射。對(duì)于NAT穿透來(lái)說(shuō),這種區(qū)分對(duì)來(lái)說(shuō)是一樣的:它們都會(huì)導(dǎo)致STUN方式不可用。

5.3 老的cone類(lèi)型劃分

你可能會(huì)有疑問(wèn):根據(jù)是否依賴endpoint這一條件,只能組合出兩種可能,那為什么傳統(tǒng)分類(lèi)中會(huì)有四種cone類(lèi)型呢?答案是cone包含了兩個(gè)正交維度的NAT行為

  • NAT映射行為:前面已經(jīng)介紹過(guò)了,
  • 有狀態(tài)防火墻行為:與前者類(lèi)似,也是分為與endpoint相關(guān)還是無(wú)關(guān)兩種類(lèi)型。

因此最終組合如下:

NATConeTypes

Endpoint無(wú)關(guān)NATmapping

Endpoint相關(guān)NATmapping(alltypes)

Endpoint無(wú)關(guān)防火墻

FullConeNAT

N/A*

Endpoint相關(guān)防火墻(dst.IPonly)

RestrictedConeNAT

N/A*

Endpoint相關(guān)防火墻(dst.IP+port)

Port-RestrictedConeNAT

SymmetricNAT

分解到這種程度之后就可以看出,cone類(lèi)型對(duì)NAT穿透場(chǎng)景來(lái)說(shuō)并沒(méi)有什么意義。我們關(guān)心的只有一點(diǎn):是否是Symmetric——換句話說(shuō),一個(gè)NAT設(shè)備是EIM還是EDM類(lèi)型的。

5.4 針對(duì)NAT穿透場(chǎng)景:簡(jiǎn)化NAT分類(lèi)

以上討論可知,雖然理解防火墻的具體行為很重要,但對(duì)于編寫(xiě)NAT穿透代碼來(lái)說(shuō),這一點(diǎn)并不重要。我們的兩端同時(shí)發(fā)包方式(simultaneoustransmissiontrick)能有效穿透以上三種類(lèi)型的防火墻。在真實(shí)場(chǎng)景中,我們主要在處理的是IP-and-portendpoint-dependent防火墻。

因此,對(duì)于實(shí)際NAT穿透實(shí)現(xiàn),我們可以將以上分類(lèi)簡(jiǎn)化成:

Endpoint-IndependentNATmapping

Endpoint-DependentNATmapping(dst.IPonly)

Firewallisyes

EasyNAT

HardNAT

5.5 更多NAT規(guī)范(RFC)

想了解更多新的NAT術(shù)語(yǔ),可參考

  • RFC4787(NATBehavioralRequirementsforUDP)
  • RFC5382(forTCP)
  • RFC5508(forICMP)

如果自己實(shí)現(xiàn)NAT,那應(yīng)該(should)遵循這些RFC的規(guī)范,這樣才能使你的NAT行為符合業(yè)界慣例,與其他廠商的設(shè)備或軟件良好兼容。

六、穿透NAT+防火墻:STUN不可用時(shí),fallback到中繼模式

6.1 問(wèn)題回顧與保底方式(中繼)

補(bǔ)完基礎(chǔ)知識(shí)(尤其是定義了什么是hardNAT)之后,回到我們的NAT穿透主題。

  • 第1~4節(jié)已經(jīng)解決了STUN和防火墻穿透的問(wèn)題,
  • hardNAT對(duì)我們來(lái)說(shuō)是個(gè)大問(wèn)題,只要路徑上出現(xiàn)一個(gè)這種設(shè)備,前面的方案就行不通了。

準(zhǔn)備放棄了嗎?這才進(jìn)入NAT真正有挑戰(zhàn)的部分:如果已經(jīng)試過(guò)了前面介紹的所有方式仍然不能穿透,我們?cè)撛趺崔k呢?

  • 實(shí)際上,確實(shí)有很多NAT實(shí)現(xiàn)在這種情況下都會(huì)選擇放棄,向用戶報(bào)一個(gè)“無(wú)法連接”之類(lèi)的錯(cuò)誤。
  • 但對(duì)我們來(lái)說(shuō),這么快就放棄顯然是不可接受的——解決不了連通性問(wèn)題,Tailscale就沒(méi)有存在的意義。

我們的保底解決方式是:創(chuàng)建一個(gè)中繼連接(relay)實(shí)現(xiàn)雙方的無(wú)障礙地通信。但是,中繼方式性能不是很差嗎?這要看具體情況:

  • 如果能直連,那顯然沒(méi)必要用中繼方式;
  • 但如果無(wú)法直連,而中繼路徑又非常接近雙方直連的真實(shí)路徑,并且?guī)捵銐虼?,那中繼方式并不會(huì)明顯降低通信質(zhì)量。延遲肯定會(huì)增加一點(diǎn),帶寬會(huì)占用一些,但相比完全連接不上,還是更能讓用戶接受的

不過(guò)要注意:我們只有在無(wú)法直連時(shí)才會(huì)選擇中繼方式。實(shí)際場(chǎng)景中,

  1. 對(duì)于大部分網(wǎng)絡(luò),我們都能通過(guò)前面介紹的方式實(shí)現(xiàn)直連,
  2. 剩下的長(zhǎng)尾用中繼方式來(lái)解決,并不算一個(gè)很糟的方式。

此外,某些網(wǎng)絡(luò)會(huì)阻止NAT穿透,其影響比這種hardNAT大多了。例如,我們觀察到UCBerkeleyguestWiFi禁止除DNS流量之外的所有outboundUDP流量。不管用什么NAT黑科技,都無(wú)法繞過(guò)這個(gè)攔截。因此我們終歸還是需要一些可靠的fallback機(jī)制。

6.2 中繼協(xié)議:TURN、DERP

有多種中繼實(shí)現(xiàn)方式。

  1. TURN(TraversalUsingRelaysaroundNAT):經(jīng)典方式,核心理念是
  • 用戶(人)先去公網(wǎng)上的TURN服務(wù)器認(rèn)證,成功后后者會(huì)告訴你:“我已經(jīng)為你分配了ip:port,接下來(lái)將為你中繼流量”,然后將這個(gè)ip:port地址告訴對(duì)方,讓它去連接這個(gè)地址,接下去就是非常簡(jiǎn)單的客戶端/服務(wù)器通信模型了。

Tailscale并不使用TURN。這種協(xié)議用起來(lái)并不是很好,而且與STUN不同,它沒(méi)有真正的交互性,因?yàn)榛ヂ?lián)網(wǎng)上并沒(méi)有公開(kāi)的TURN服務(wù)器。

  1. DERP(DetouredEncryptedRoutingProtocol)

這是我們創(chuàng)建的一個(gè)協(xié)議,DERP,

  • 它是一個(gè)通用目的包中繼協(xié)議,運(yùn)行在HTTP之上,而大部分網(wǎng)絡(luò)都是允許HTTP通信的。它根據(jù)目的公鑰(destination’spublickey)來(lái)中繼加密的流量(encryptedpayloads)。

前面也簡(jiǎn)單提到過(guò),DERP既是我們?cè)贜AT穿透失敗時(shí)的保底通信方式(此時(shí)的角色與TURN類(lèi)似),也是在其他一些場(chǎng)景下幫助我們完成NAT穿透的旁路信道。換句話說(shuō),它既是我們的保底方式,也是有更好的穿透鏈路時(shí),幫助我們進(jìn)行連接升級(jí)(upgradetoapeer-to-peerconnection)的基礎(chǔ)設(shè)施。

6.3 小結(jié)

有了“中繼”這種保底方式之后,我們穿透的成功率大大增加了。如果此時(shí)不再閱讀本文接下來(lái)的內(nèi)容,而是把上面介紹的穿透方式都實(shí)現(xiàn)了,我預(yù)計(jì):

  • 90%的情況下,你都能實(shí)現(xiàn)直連穿透;
  • 剩下的10%里,用中繼方式能穿透一些(some);

這已經(jīng)算是一個(gè)“足夠好”的穿透實(shí)現(xiàn)了。

七、穿透NAT+防火墻:企業(yè)級(jí)改進(jìn)

如果你并不滿足于“足夠好”,那我們可以做的事情還有很多!

本節(jié)將介紹一些五花八門(mén)的tricks,在某些特殊場(chǎng)景下會(huì)幫到我們。單獨(dú)使用這項(xiàng)技術(shù)都無(wú)法解決NAT穿透問(wèn)題,但將它們巧妙地組合起來(lái),我們能更加接近100%的穿透成功率。

7.1 穿透hardNAT:暴力端口掃描

回憶hardNAT中遇到的問(wèn)題,如下圖所示,關(guān)鍵問(wèn)題是:easyNAT不知道該往hardNAT方的哪個(gè)ip:port發(fā)包。

必須要往正確的ip:port發(fā)包,才能穿透防火墻,實(shí)現(xiàn)雙向互通。怎么辦呢?

  1. 首先,我們能知道hardNAT的一些ip:port,因?yàn)槲覀冇蠸TUN服務(wù)器。

這里先假設(shè)我們獲得的這些IP地址都是正確的(這一點(diǎn)并不總是成立,但這里先這么假設(shè)。而實(shí)際上,大部分情況下這一點(diǎn)都是成立的,如果對(duì)此有興趣,可以參考REQ-2inRFC4787)。

  1. IP地址確定了,剩下的就是端口了。總共有65535中可能,我們能遍歷這個(gè)端口范圍嗎?

如果發(fā)包速度是100packets/s,那最壞情況下,需要10分鐘來(lái)找到正確的端口。還是那句話,這雖然不是最優(yōu)的,但總比連不上好。

這很像是端口掃描(事實(shí)上,確實(shí)是),實(shí)際中可能會(huì)觸發(fā)對(duì)方的網(wǎng)絡(luò)入侵檢測(cè)軟件。

7.2 基于生日悖論改進(jìn)暴力掃描:hardside多開(kāi)端口+easyside隨機(jī)探測(cè)

利用birthdayparadox算法,我們能對(duì)端口掃描進(jìn)行改進(jìn)。

  • 上一節(jié)的基本前提是:hardside只打開(kāi)一個(gè)端口,然后easyside暴力掃描65535個(gè)端口來(lái)尋找這個(gè)端口;
  • 這里的改進(jìn)是:在hardsize開(kāi)多個(gè)端口,例如256個(gè)(即同時(shí)打開(kāi)256個(gè)socket,目的地址都是easyside的ip:port),然后easyside隨機(jī)探測(cè)這邊的端口。

這里省去算法的數(shù)學(xué)模型,如果你對(duì)實(shí)現(xiàn)干興趣,可以看看我寫(xiě)的pythoncalculator。計(jì)算過(guò)程是“經(jīng)典”生日悖論的一個(gè)小變種。下面是隨著easysiderandomprobe次數(shù)(假設(shè)hardsize256個(gè)端口)的變化,兩邊打開(kāi)的端口有重合(即通信成功)的概率:

隨機(jī)探測(cè)次數(shù)

成功概率

174

50%

256

64%

1024

98%

2048

99.9%

根據(jù)以上結(jié)果,如果還是假設(shè)100ports/s這樣相當(dāng)溫和的探測(cè)速率,那2秒鐘就有約50%的成功概率。即使非常不走運(yùn),我們?nèi)匀荒茉?/span>20s時(shí)幾乎100%穿透成功,而此時(shí)只探測(cè)了總端口空間的4%。

非常好!雖然這種hardNAT給我們帶來(lái)了嚴(yán)重的穿透延遲,但最終結(jié)果仍然是成功的。那么,如果是兩個(gè)hardNAT,我們還能處理嗎?

7.3 雙hardNAT場(chǎng)景

這種情況下仍然可以用前面的多端口+隨機(jī)探測(cè)方式,但成功概率要低很多了:

  • 每次通過(guò)一臺(tái)hardNAT去探測(cè)對(duì)方的端口(目的端口)時(shí),我們自己同時(shí)也生成了一個(gè)隨機(jī)源端口,
  • 這意味著我們的搜索空間變成了二維{srcport,dstport}對(duì),而不再是之前的一維dstport空間。

這里我們也不就具體計(jì)算展開(kāi),只告訴結(jié)果:仍然假設(shè)目的端打開(kāi)256個(gè)端口,從源端發(fā)起2048次(20秒),成功的概率是:0.01%

如果你之前學(xué)過(guò)生日悖論,就并不會(huì)對(duì)這個(gè)結(jié)果感到驚訝。理論上來(lái)說(shuō),

  • 要達(dá)到99.9%的成功率,我們需要兩邊各進(jìn)行170,000次探測(cè)——如果還是以100packets/sec的速度,就需要28分鐘。
  • 要達(dá)到50%的成功率,“只”需要54,000packets,也就是9分鐘。
  • 如果不使用生日悖論方式,而且暴力窮舉,需要1.2年時(shí)間!

對(duì)于某些應(yīng)用來(lái)說(shuō),28分鐘可能仍然是一個(gè)可接受的時(shí)間。用半個(gè)小時(shí)暴力穿透NAT之后,這個(gè)連接就可以一直用著——除非NAT設(shè)備重啟,那樣就需要再次花半個(gè)小時(shí)穿透建個(gè)新連接。但對(duì)于交互式應(yīng)用來(lái)說(shuō),這樣顯然是不可接受的。

更糟糕的是,如果去看常見(jiàn)的辦公網(wǎng)路由器,你會(huì)震驚于它的activesessionlowlimit有多么低。例如,一臺(tái)JuniperSRX300最多支持64,000activesessions。也就是說(shuō),

  • 如果我們想創(chuàng)建一個(gè)成功的穿透連接,就會(huì)把它的整張session表打爆(因?yàn)槲覀円┝μ綔y(cè)65535個(gè)端口,每次探測(cè)都是一條新連接記錄)!這顯然要求這臺(tái)路由器能從容優(yōu)雅地處理過(guò)載的情況。
  • 這只是創(chuàng)建一條連接帶來(lái)的影響!如果20臺(tái)機(jī)器同時(shí)對(duì)這臺(tái)路由器發(fā)起穿透呢?絕對(duì)的災(zāi)難!

至此,我們通過(guò)這種方式穿透了比之前更難一些的網(wǎng)絡(luò)拓?fù)洹_@是一個(gè)很大的成就,因?yàn)?/span>家用路由器一般都是easyNAT,hardNAT一般都是辦公網(wǎng)路由器或云NAT網(wǎng)關(guān)。這意味著這種方式能幫我們解決

  • home-to-office(家->辦公室)
  • home-to-cloud(家->云)

的場(chǎng)景,以及一部分

  • office-to-cloud(辦公室->云)
  • cloud-to-cloud(云->辦公室)

場(chǎng)景。

7.4 控制端口映射(portmapping)過(guò)程:UPnP/NAT-PMP/PCP協(xié)議

如果我們能讓NAT設(shè)備的行為簡(jiǎn)單點(diǎn),不要把事情搞這么復(fù)雜,那建立連接(穿透)就會(huì)簡(jiǎn)單很多。真有這樣的好事嗎?還真有,有專門(mén)的一種協(xié)議叫端口映射協(xié)議(portmappingprotocols)。通過(guò)這種協(xié)議禁用掉前面遇到的那些亂七八糟的東西之后,我們將得到一個(gè)非常簡(jiǎn)單的“請(qǐng)求-響應(yīng)”。

下面是三個(gè)具體的端口映射協(xié)議:

  1. UPnPIGD(UniversalPlug’n’PlayInternetGatewayDevice)

最老的端口控制協(xié)議,誕生于1990s晚期,因此使用了很多上世紀(jì)90年代的技術(shù)(XML、SOAP、multicastHTTPoverUDP——對(duì),HTTPoverUDP),而且很難準(zhǔn)確和安全地實(shí)現(xiàn)這個(gè)協(xié)議。但以前很多路由器都內(nèi)置了UPnP協(xié)議,現(xiàn)在仍然很多。

請(qǐng)求和響應(yīng):

  • “你好,請(qǐng)將我的lan-ip:port轉(zhuǎn)發(fā)到公網(wǎng)(WAN)”,“好的,我已經(jīng)為你分配了一個(gè)公網(wǎng)映射wan-ip:port”。
  1. NAT-PMP

UPnPIGD出來(lái)幾年之后,Apple推出了一個(gè)功能類(lèi)似的協(xié)議,名為NAT-PMP(NATPortMappingProtocol)。

但與UPnP不同,這個(gè)協(xié)議做端口轉(zhuǎn)發(fā),不管是在客戶端還是服務(wù)端,實(shí)現(xiàn)起來(lái)都非常簡(jiǎn)單。

  1. PCP

稍后一點(diǎn),又出現(xiàn)了NAT-PMPv2版,并起了個(gè)新名字PCP(PortControlProtocol)。

因此要更好地實(shí)現(xiàn)穿透,可以

  1. 先判斷本地的默認(rèn)網(wǎng)關(guān)上是否啟用了UPnPIGD,NAT-PMPandPCP
  2. 如果探測(cè)發(fā)現(xiàn)其中任何一種協(xié)議有響應(yīng),我們就申請(qǐng)一個(gè)公網(wǎng)端口映射,

可以將這理解為一個(gè)加強(qiáng)版STUN:我們不僅能發(fā)現(xiàn)自己的公網(wǎng)ip:port,而且能指示我們的NAT設(shè)備對(duì)我們的通信對(duì)端友好一些——但并不是為這個(gè)端口修改或添加防火墻規(guī)則。

  1. 接下來(lái),任何到達(dá)我們NAT設(shè)備的、地址是我們申請(qǐng)的端口的包,都會(huì)被設(shè)備轉(zhuǎn)發(fā)到我們。

但我們不能假設(shè)這個(gè)協(xié)議一定可用

  1. 本地NAT設(shè)備可能不支持這個(gè)協(xié)議;
  2. 設(shè)備支持但默認(rèn)禁用了,或者沒(méi)人知道還有這么個(gè)功能,因此從來(lái)沒(méi)開(kāi)過(guò);
  3. 安全策略要求關(guān)閉這個(gè)特性。

這一點(diǎn)非常常見(jiàn),因?yàn)閁PnP協(xié)議曾曝出一些高危漏洞(后面都修復(fù)了,因此如果是較新的設(shè)備,可以安全地使用UPnP——如果實(shí)現(xiàn)沒(méi)問(wèn)題)。不幸的是,某些設(shè)備的配置中,UPnP,NAT-PMP,PCP是放在一個(gè)開(kāi)關(guān)里的(可能統(tǒng)稱為“UPnP”功能),一開(kāi)全開(kāi),一關(guān)全關(guān)。因此如果有人擔(dān)心UPnP的安全性,他連另外兩個(gè)也用不了。

最后,終歸來(lái)說(shuō),只要這種協(xié)議可用,就能有效地減少一次NAT,大大方便建連過(guò)程。但接下來(lái)看一些不常見(jiàn)的場(chǎng)景。

7.5 多NAT協(xié)商(NegotiatingnumerousNATs)

目前為止,我們看到的客戶端和服務(wù)端都各只有一個(gè)NAT設(shè)備。如果有多個(gè)NAT設(shè)備會(huì)怎么樣?例如下面這種拓?fù)洌?/span>

這個(gè)例子比較簡(jiǎn)單,不會(huì)給穿透帶來(lái)太大問(wèn)題。包從客戶端A經(jīng)過(guò)多次NAT到達(dá)公網(wǎng)的過(guò)程,與前面分析的穿過(guò)多層有狀態(tài)防火墻是一樣的:

  • 額外的這層(NAT設(shè)備)對(duì)客戶端和服務(wù)端來(lái)說(shuō)都不可見(jiàn),我們的穿透技術(shù)也不關(guān)心中間到底經(jīng)過(guò)了多少層設(shè)備。
  • 真正有影響的其實(shí)只是最后一層設(shè)備,因?yàn)閷?duì)端需要在這一層設(shè)備上找到入口讓包進(jìn)來(lái)。

具體來(lái)說(shuō),真正有影響的是端口轉(zhuǎn)發(fā)協(xié)議。

  1. 客戶端使用這種協(xié)議分配端口時(shí),為我們分配端口的是最靠近客戶端的這層NAT設(shè)備;
  2. 而我們期望的是讓最離客戶端最遠(yuǎn)的那層NAT來(lái)分配,否則我們得到的就是一個(gè)網(wǎng)絡(luò)中間層分配的ip:port,對(duì)端是用不了的;
  3. 不幸的是,這幾種協(xié)議都不能遞歸地告訴我們下一層NAT設(shè)備是多少——雖然可以用traceroute之類(lèi)的工具來(lái)探測(cè)網(wǎng)絡(luò)路徑,再加上猜路上的設(shè)備是不是NAT設(shè)備(嘗試發(fā)送NAT請(qǐng)求)——但這個(gè)就看運(yùn)氣了。

這就是為什么互聯(lián)網(wǎng)上充斥著大量的文章說(shuō)double-NAT有多糟糕,以及警告用戶為保持后向兼容不要使用double-NAT。但實(shí)際上,double-NAT對(duì)于絕大部分互聯(lián)網(wǎng)應(yīng)用來(lái)說(shuō)都是不可見(jiàn)的(透明的),因?yàn)榇蟛糠謶?yīng)用并不需要主動(dòng)地做這種NAT穿透。

但我也絕不是在建議你在自己的網(wǎng)絡(luò)中設(shè)置double-NAT。

  1. 破壞了端口映射協(xié)議之后,某些視頻游戲的多人(multiplayer)模式就會(huì)無(wú)法使用,
  2. 也可能會(huì)使你的IPv6網(wǎng)絡(luò)無(wú)法派上用場(chǎng),后者是不用NAT就能雙向直連的一個(gè)好方案。

但如果double-NAT并不是你能控制的,那除了不能用到這種端口映射協(xié)議之外,其他大部分東西都是不受影響的。

double-NAT的故事到這里就結(jié)束了嗎?——并沒(méi)有,而且更大型的double-NAT場(chǎng)景將展現(xiàn)在我們面前。

7.6 運(yùn)營(yíng)商級(jí)NAT帶來(lái)的問(wèn)題

即使用NAT來(lái)解決IPv4地址不夠的問(wèn)題,地址仍然是不夠用的,ISP(互聯(lián)網(wǎng)服務(wù)提供商)顯然無(wú)法為每個(gè)家庭都分配一個(gè)公網(wǎng)IP地址。那怎么解決這個(gè)問(wèn)題呢?ISP的做法是不夠了就再嵌套一層NAT

  1. 家用路由器將你的客戶端SNAT到一個(gè)“intermediate”IP然后發(fā)送到運(yùn)營(yíng)商網(wǎng)絡(luò),
  2. ISP’snetwork中的NAT設(shè)備再將這些intermediateIPs映射到少量的公網(wǎng)IP。

后面這種NAT就稱為“運(yùn)營(yíng)商級(jí)NAT”(carrier-gradeNAT,或稱電信級(jí)NAT),縮寫(xiě)CGNAT。如下圖所示:

CGNAT對(duì)NAT穿透來(lái)說(shuō)是一個(gè)大麻煩。

  • 在此之前,辦公網(wǎng)用戶要快速實(shí)現(xiàn)NAT穿透,只需在他們的路由器上手動(dòng)設(shè)置端口映射就行了。
  • 但有了CGNAT之后就不管用了,因?yàn)槟銦o(wú)法控制運(yùn)營(yíng)商的CGNAT!

好消息是:這其實(shí)是double-NAT的一個(gè)小變種,因此前面介紹的解決方式大部分還仍然是適用的。某些東西可能會(huì)無(wú)法按預(yù)期工作,但只要肯給ISP交錢(qián),這些也都能解決。除了portmappingprotocols,其他我們已經(jīng)介紹的所有東西在CGNAT里都是適用的。

新挑戰(zhàn):同一CGNAT側(cè)直連,STUN不可用

但我們確實(shí)遇到了一個(gè)新挑戰(zhàn):如何直連兩個(gè)在同一CGNAT但不同家用路由器中的對(duì)端呢?如下圖所示:

在這種情況下,STUN就無(wú)法正常工作了:STUN看到的是客戶端在公網(wǎng)(CGNAT后面)看到的地址,而我們想獲得的是在“middlenetwork”中的ip:port,這才是對(duì)端真正需要的地址,

解決方案:如果端口映射協(xié)議能用:一端做端口映射

怎么辦呢?

如果你想到了端口映射協(xié)議,那恭喜,答對(duì)了!如果peer中任何一個(gè)NAT支持端口映射協(xié)議,對(duì)我們就能實(shí)現(xiàn)穿透,因?yàn)樗峙涞膇p:port正是對(duì)端所需要的信息。

這里諷刺的是:double-NAT(指CGNAT)破壞了端口映射協(xié)議,但在這里又救了我們!當(dāng)然,我們假設(shè)這些協(xié)議一定可用,因?yàn)镃GNATISP傾向于在它們的家用路由器側(cè)關(guān)閉這些功能,已避免軟件得到“錯(cuò)誤的”結(jié)果,產(chǎn)生混淆。

解決方案:如果端口映射協(xié)議不能用:NAThairpin模式

如果不走運(yùn),NAT上沒(méi)有端口映射功能怎么辦?

讓我們回到基于STUN的技術(shù),看會(huì)發(fā)生什么。兩端在CGNAT的同一側(cè),假設(shè)STUN告訴我們A的地址是2.2.2.2:1234,B的地址是2.2.2.2:5678。

那么接下來(lái)的問(wèn)題是:如果A向2.2.2.2:5678發(fā)包會(huì)怎么樣?期望的CGNAT行為是:

  1. 執(zhí)行A的NAT映射規(guī)則,即對(duì)2.2.2.2:1234->2.2.2.2:5678進(jìn)行SNAT。
  2. 注意到目的地址2.2.2.2:5678匹配到的是B的入向NAT映射,因此接著對(duì)這個(gè)包執(zhí)行DNAT,將目的IP改成B的私有地址。
  3. 通過(guò)CGNAT的internal接口(而不是public接口,對(duì)應(yīng)公網(wǎng))將包發(fā)給B。

這種NAT行為有個(gè)專門(mén)的術(shù)語(yǔ),叫hairpinning(直譯為發(fā)卡,意思是像發(fā)卡一樣,沿著一邊上去,然后從另一邊繞回來(lái)),

大家應(yīng)該猜到的一個(gè)事實(shí)是:不是所以NAT都支持hairpin模式。實(shí)際上,大量well-behavedNAT設(shè)備都不支持hairpin模式,

  • 因?yàn)樗鼈兌加?/span>“只有src_ip是私有地址且dst_ip是公網(wǎng)地址的包才會(huì)經(jīng)過(guò)我”之類(lèi)的假設(shè)。
  • 因此對(duì)于這種目的地址不是公網(wǎng)、需要讓路由器把包再轉(zhuǎn)回內(nèi)網(wǎng)的包,它們會(huì)直接丟棄
  • 這些邏輯甚至是直接實(shí)現(xiàn)在路由芯片中的,因此除非升級(jí)硬件,否則單靠軟件編程無(wú)法改變這種行為。

Hairpin是所有NAT設(shè)備的特性(支持或不支持),并不是CGNAT獨(dú)有的。

  1. 在大部分情況下,這個(gè)特性對(duì)我們的NAT穿透目的來(lái)說(shuō)都是無(wú)所謂的,因?yàn)槲覀兤谕?/span>兩個(gè)LANNAT設(shè)備會(huì)直接通信,不會(huì)再向上繞到它們的默認(rèn)網(wǎng)關(guān)CGNAT來(lái)解決這個(gè)問(wèn)題

Hairpin特性可有可無(wú)這件事有點(diǎn)遺憾,這可能也是為什么hairpin功能經(jīng)常broken的原因。

  1. 一旦必須涉及到CGNAT,那hairpinning對(duì)連接性來(lái)說(shuō)就至關(guān)重要了。

Hairpinning使內(nèi)網(wǎng)連接的行為與公網(wǎng)連接的行為完成一致,因此我們無(wú)需關(guān)心目的地址類(lèi)型,也不用知曉自己是否在一臺(tái)CGNAT后面。

如果hairpinning和portmappingprotocols都不可用,那只能降級(jí)到中繼模式了。

7.7 全I(xiàn)Pv6網(wǎng)絡(luò):理想之地,但并非問(wèn)題全無(wú)

行文至此,一些讀者可能已經(jīng)對(duì)著屏幕咆哮:不要再用IPv4了!花這么多時(shí)間精力解決這些沒(méi)意義的東西,還不如直接換成IPv6!

  • 的確,之所以有這些亂七八糟的東西,就是因?yàn)镮Pv4地址不夠了,我們一直在用越來(lái)越復(fù)雜的NAT來(lái)給IPv4續(xù)命。
  • 如果IP地址夠用,無(wú)需NAT就能讓世界上的每個(gè)設(shè)備都有一個(gè)自己的公網(wǎng)IP地址,這些問(wèn)題不就解決了嗎?

簡(jiǎn)單來(lái)說(shuō),是的,這也正是IPv6能做的事情。但是,也只說(shuō)對(duì)了一半:在理想的全I(xiàn)Pv6世界中,所有這些東西會(huì)變得更加簡(jiǎn)單,但我們面臨的問(wèn)題并不會(huì)完全消失——因?yàn)?/span>有狀態(tài)防火墻仍然還是存在的。

  • 辦公室中的電腦可能有一個(gè)公網(wǎng)IPv6地址,但你們公司肯定會(huì)架設(shè)一個(gè)防火墻,只允許你的電腦主動(dòng)訪問(wèn)公網(wǎng),而不允許反向主動(dòng)建連。
  • 其他設(shè)備上的防火墻也仍然存在,應(yīng)用類(lèi)似的規(guī)則。

因此,我們?nèi)匀粫?huì)用到

  1. 本文最開(kāi)始介紹的防火墻穿透技術(shù),以及
  2. 幫助我們獲取自己的公網(wǎng)ip:port信息的旁路信道
  3. 仍然需要在某些場(chǎng)景下fallback到中繼模式,例如fallback到最通用的HTTP中繼協(xié)議,以繞過(guò)某些網(wǎng)絡(luò)禁止outboundUDP的問(wèn)題。

但我們現(xiàn)在可以拋棄STUN、生日悖論、端口映射協(xié)議、hairpin等等東西了。這是一個(gè)好消息!

全球IPv4/IPv6部署現(xiàn)狀

另一個(gè)更加嚴(yán)峻的現(xiàn)實(shí)問(wèn)題是:當(dāng)前并不是一個(gè)全I(xiàn)Pv6世界。目前世界上

  • 大部分還是IPv4,
  • 大約33%是IPv6,而且分布極度不均勻,因此某些通信對(duì)所在的可能是100%IPv6,也可能是0%,或二者之間。

不幸的是,這意味著,IPv6**還**無(wú)法作為我們的解決方案。就目前來(lái)說(shuō),它只是我們的工具箱中的一個(gè)備選。對(duì)于某些peer來(lái)說(shuō),它簡(jiǎn)直是完美工具,但對(duì)其他peer來(lái)說(shuō),它是用不了的。如果目標(biāo)是“任何情況下都能穿透(連接)成功”,那我們就仍然需要IPv4+NAT那些東西。

新場(chǎng)景:NAT64/DNS64

IPv4/IPv6共存也引出了一個(gè)新的場(chǎng)景:NAT64設(shè)備。

前面介紹的都是NAT44設(shè)備:它們將一個(gè)IPv4地址轉(zhuǎn)換成另一IPv4地址。NAT64從名字可以看出,是將一個(gè)內(nèi)側(cè)IPv6地址轉(zhuǎn)換成一個(gè)外側(cè)IPv4地址。利用DNS64設(shè)備,我們能將IPv4DNS應(yīng)答給IPv6網(wǎng)絡(luò),這樣對(duì)終端來(lái)說(shuō),它看到的就是一個(gè)全I(xiàn)Pv6網(wǎng)絡(luò),而仍然能訪問(wèn)IPv4公網(wǎng)。

如果需要處理DNS問(wèn)題,那這種方式工作良好。例如,如果連接到google.com,將這個(gè)域名解析成IP地址的過(guò)程會(huì)涉及到DNS64設(shè)備,它又會(huì)進(jìn)一步involveNAT64設(shè)備,但后一步對(duì)用戶來(lái)說(shuō)是無(wú)感知的。

對(duì)于NAT和防火墻穿透來(lái)說(shuō),我們會(huì)關(guān)心每個(gè)具體的IP地址和端口。

解決方案:CLAT(Customer-sidetransLATor)

如果設(shè)備支持CLAT(Customer-sidetranslator—fromCustomerXLAT),那我們就很幸運(yùn):

  • CLAT假裝操作系統(tǒng)有直接IPv4連接,而背后使用的是NAT64,以對(duì)應(yīng)用程序無(wú)感知。在有CLAT的設(shè)備上,我們無(wú)需做任何特殊的事情。
  • CLAT在移動(dòng)設(shè)備上非常常見(jiàn),但在桌面電腦、筆記本和服務(wù)器上非常少見(jiàn),因此在后者上,必須自己做CLAT做的事情:檢測(cè)NAT64+DNS64的存在,然后正確地使用它們。

解決方案:CLAT不存在時(shí),手動(dòng)穿透NAT64設(shè)備

  1. 首先檢測(cè)是否存在NAT64+DNS64。

方法很簡(jiǎn)單:向ipv4only.arpa.發(fā)送一個(gè)DNS請(qǐng)求。這個(gè)域名會(huì)解析到一個(gè)已知的、固定的IPv4地址,而且是純IPv4地址。如果得到的是一個(gè)IPv6地址,就可以判斷有DNS64服務(wù)器做了轉(zhuǎn)換,而它必然會(huì)用到NAT64。這樣就能判斷出NAT64的前綴是多少。

  1. 此后,要向IPv4地址發(fā)包時(shí),發(fā)送格式為{NAT64prefix+IPv4address}的IPv6包。類(lèi)似地,收到來(lái)源格式為{NAT64prefix+IPv4address}的包時(shí),就是IPv4流量。
  2. 接下來(lái),通過(guò)NAT64網(wǎng)絡(luò)與STUN通信來(lái)獲取自己在NAT64上的公網(wǎng)ip:port,接下來(lái)就回到經(jīng)典的NAT穿透問(wèn)題了——除了需要多做一點(diǎn)點(diǎn)事情。

幸運(yùn)的是,如今的大部分v6-only網(wǎng)絡(luò)都是移動(dòng)運(yùn)營(yíng)商網(wǎng)絡(luò),而幾乎所有手機(jī)都支持CLAT。運(yùn)營(yíng)v6-only網(wǎng)絡(luò)的ISPs會(huì)在他們給你的路由器上部署CLAT,因此最后你其實(shí)不需要做什么事情。但如果想實(shí)現(xiàn)100%穿透,就需要解決這種邊邊角角的問(wèn)題,即必須顯式支持從v6-only網(wǎng)絡(luò)連接v4-only對(duì)端。

7.8 將所有解決方式集成到ICE協(xié)議

針對(duì)具體場(chǎng)景,該選擇哪種穿透方式?

至此,我們的NAT穿透之旅終于快結(jié)束了。我們已經(jīng)覆蓋了有狀態(tài)防火墻、簡(jiǎn)單和高級(jí)NAT、IPv4和IPv6。只要將以上解決方式都實(shí)現(xiàn)了,NAT穿透的目的就達(dá)到了!

但是,

  • 對(duì)于給定的peer,如何判斷改用哪種方式呢?
  • 如何判斷這是一個(gè)簡(jiǎn)單有狀態(tài)防火墻的場(chǎng)景,還是該用到生日悖論算法,還是需要手動(dòng)處理NAT64呢?
  • 還是通信雙方在一個(gè)WiFi網(wǎng)絡(luò)下,連防火墻都沒(méi)有,因此不需要任何操作呢?

早期NAT穿透比較簡(jiǎn)單,能讓我們精確判斷出peer之間的路徑特點(diǎn),然后針對(duì)性地采用相應(yīng)的解決方式。但后面,網(wǎng)絡(luò)工程師和NAT設(shè)備開(kāi)發(fā)工程師引入了一些新理念,給路徑判斷造成很大困難。因此我們需要簡(jiǎn)化客戶端側(cè)的思考(判斷邏輯)。

這就要提到InteractiveConnectivityEstablishment(ICE,交換式連接建立)協(xié)議了。與STUN/TURN類(lèi)似,ICE來(lái)自電信領(lǐng)域,因此其RFC充滿了SIP、SDP、信令會(huì)話、撥號(hào)等等電話術(shù)語(yǔ)。但如果忽略這些領(lǐng)域術(shù)語(yǔ),我們會(huì)看到它描述了一個(gè)極其優(yōu)雅的判斷最佳連接路徑的算法。

真的?這個(gè)算法是:每種方法都試一遍,然后選擇最佳的那個(gè)方法。就是這個(gè)算法,驚喜嗎?

來(lái)更深入地看一下這個(gè)算法。

ICE(InteractiveConnectivityEstablishment)算法

這里的討論不會(huì)嚴(yán)格遵循ICEspec,因此如果是在自己實(shí)現(xiàn)一個(gè)可互操作的ICE客戶端,應(yīng)該通讀RFC8445,根據(jù)它的描述來(lái)實(shí)現(xiàn)。這里忽略所有電信術(shù)語(yǔ),只關(guān)注核心的算法邏輯,并提供幾個(gè)在ICE規(guī)范允許范圍的靈活建議。

  1. 為實(shí)現(xiàn)和某個(gè)peer的通信,首先需要確定我們自己用的(客戶端側(cè))這個(gè)socket的地址,這是一個(gè)列表,至少應(yīng)該包括:
  • 我們自己的IPv6ip:ports我們自己的IPv4LANip:ports(局域網(wǎng)地址)通過(guò)STUN服務(wù)器獲取到的我們自己的IPv4WANip:ports(公網(wǎng)地址,可能會(huì)經(jīng)過(guò)NAT64轉(zhuǎn)換)通過(guò)端口映射協(xié)議獲取到的我們自己的IPv4WANip:port(NAT設(shè)備的端口映射協(xié)議分配的公網(wǎng)地址)運(yùn)營(yíng)商提供給我們的endpoints(例如,靜態(tài)配置的端口轉(zhuǎn)發(fā)
  1. 通過(guò)旁路信道與peer互換這個(gè)列表。兩邊都拿到對(duì)方的列表后,就開(kāi)始互相探測(cè)對(duì)方提供的地址。列表中地址沒(méi)有優(yōu)先級(jí),也就是說(shuō),如果對(duì)方給的了15個(gè)地址,那我們應(yīng)該把這15個(gè)地址都探測(cè)一遍。

這些探測(cè)包有兩個(gè)目的

  • 打開(kāi)防火墻,穿透NAT,也就是本文一直在介紹的內(nèi)容;健康檢測(cè)。我們?cè)诓粩嘟粨Q(最好是已認(rèn)證的)“ping/pong”包,來(lái)檢測(cè)某個(gè)特定的路徑是不是端到端通的。
  1. 最后,一小會(huì)兒之后,從可用的備選地址中(根據(jù)某些條件)選擇“最佳”的那個(gè),任務(wù)完成!

這個(gè)算法的優(yōu)美之處在于:只要選擇最佳線路(地址)的算法是正確的,那就總能獲得最佳路徑。

  • ICE會(huì)預(yù)先對(duì)這些備選地址進(jìn)行排序(通常:LAN>WAN>WAN+NAT),但用戶也可以自己指定這個(gè)排序行為。
  • 從v0.100.0開(kāi)始,Tailscale從原來(lái)的hardcode優(yōu)先級(jí)切換成了根據(jù)round-triplatency的方式,它大部分情況下排序的結(jié)果和LAN>WAN>WAN+NAT是一致的。但相比于靜態(tài)排序,我們是動(dòng)態(tài)計(jì)算每條路徑應(yīng)該屬于哪個(gè)類(lèi)別。

ICEspec將協(xié)議組織為兩個(gè)階段:

  1. 探測(cè)階段
  2. 通信階段

但不一定要嚴(yán)格遵循這兩個(gè)步驟的順序。在Tailscale,

  • 我們發(fā)現(xiàn)更優(yōu)的路徑之后就會(huì)自動(dòng)切換過(guò)去,
  • 所有的連接都是先選擇DERP模式(中繼模式)。這意味著連接立即就能建立(優(yōu)先級(jí)最低但100%能成功的模式),用戶不用任何等待,
  • 然后并行進(jìn)行路徑發(fā)現(xiàn)。通常幾秒鐘之后,我們就能發(fā)現(xiàn)一條更優(yōu)路徑,然后將現(xiàn)有連接透明升級(jí)(upgrade)過(guò)去。

但有一點(diǎn)需要關(guān)心:非對(duì)稱路徑。ICE花了一些精力來(lái)保證通信雙方選擇的是相同的網(wǎng)絡(luò)路徑,這樣才能保證這條路徑上有雙向流量,能保持防火墻和NAT設(shè)備的連接一直處于open狀態(tài)。自己實(shí)現(xiàn)的話,其實(shí)并不需要花同樣大的精力來(lái)實(shí)現(xiàn)這個(gè)保證,但需要確保你所有使用的所有路徑上,都有雙向流量。這個(gè)目標(biāo)就很簡(jiǎn)單了,只需要定期在所有已使用的路徑上發(fā)ping/pong就行了。

健壯性與降級(jí)

要實(shí)現(xiàn)健壯性,還需要檢測(cè)當(dāng)前已選擇的路徑是否已經(jīng)失敗了(例如,NAT設(shè)備維護(hù)清掉了所有狀態(tài)),如果失敗了就要降級(jí)(downgrade)到其他路徑。這里有兩種方式:

  1. 持續(xù)探測(cè)所有路徑,維護(hù)一個(gè)降級(jí)時(shí)會(huì)用的備用地址列表;
  2. 直接降級(jí)到保底的中繼模式,然后再通過(guò)路徑探測(cè)升級(jí)到更好的路徑。

考慮到發(fā)生降級(jí)的概率是非常小的,因此這種方式可能是更經(jīng)濟(jì)的。

7.9 安全

最后需要提到安全。

本文的所有內(nèi)容都假設(shè):我們使用的上層協(xié)議已經(jīng)有了自己的安全機(jī)制(例如QUIC協(xié)議有TLS證書(shū),WireGuard協(xié)議有自己的公鑰)。如果還沒(méi)有安全機(jī)制,那顯然是要立即補(bǔ)上的。一旦動(dòng)態(tài)切換路徑,基于IP的安全機(jī)制就是無(wú)用的了(IP協(xié)議最開(kāi)始就沒(méi)怎么考慮安全性),至少要有端到端的認(rèn)證。

  • 嚴(yán)格來(lái)說(shuō),如果上層協(xié)議有安全機(jī)制,那即使收到是欺騙性的ping/pong流量,問(wèn)題都不大,最壞的情況也就是攻擊者誘導(dǎo)兩端通過(guò)他們的系統(tǒng)來(lái)中繼流量。而有了端到端安全機(jī)制,這并不是一個(gè)大問(wèn)題(取決于你的威脅模型)。
  • 但出于謹(jǐn)慎考慮,最好還是對(duì)路徑發(fā)現(xiàn)的包也做認(rèn)證和加密。具體如何做可以咨詢你們的應(yīng)用安全工程師。

八、結(jié)束語(yǔ)

我們終于完成了NAT穿透的目標(biāo)!

如果實(shí)現(xiàn)了以上提到的所有技術(shù),你將得到一個(gè)業(yè)內(nèi)領(lǐng)先的NAT穿透軟件,能在絕大多數(shù)場(chǎng)景下實(shí)現(xiàn)端到端直連。如果直連不了,還可以降級(jí)到保底的中繼模式(對(duì)于長(zhǎng)尾來(lái)說(shuō)只能靠中繼了)。

但這些工作相當(dāng)復(fù)雜!其中一些問(wèn)題研究起來(lái)很有意思,但很難做到完全正確,尤其是那些非常邊邊角角的場(chǎng)景,真正出現(xiàn)的概率極小,但解決它們所需花費(fèi)的經(jīng)歷又極大。不過(guò),這種工作只需要做一次,一旦解決了,你就具備了某種超級(jí)能力:探索令人激動(dòng)的、相對(duì)還比較嶄新的端到端應(yīng)用(peer-to-peerapplications)世界。

8.1 跨公網(wǎng)端到端直連

去中心化軟件領(lǐng)域中的許多有趣想法,簡(jiǎn)化之后其實(shí)都變成了跨過(guò)公網(wǎng)(互聯(lián)網(wǎng))實(shí)現(xiàn)端到端直連這一問(wèn)題,開(kāi)始時(shí)可能覺(jué)得很簡(jiǎn)單,但真正做才發(fā)現(xiàn)比想象中難多了?,F(xiàn)在知道如何解決這個(gè)問(wèn)題了,動(dòng)手開(kāi)做吧!

8.2 結(jié)束語(yǔ)之TL;DR

實(shí)現(xiàn)健壯的NAT穿透需要下列基礎(chǔ):

  1. 一種基于UDP的協(xié)議;
  2. 能在程序內(nèi)直接訪問(wèn)socket;
  3. 有一個(gè)與peer通信的旁路信道;
  4. 若干STUN服務(wù)器;
  5. 一個(gè)保底用的中繼網(wǎng)絡(luò)(可選,但強(qiáng)烈推薦)

然后需要:

  1. 遍歷所有的ip:port;
  2. 查詢STUN服務(wù)器來(lái)獲取自己的公網(wǎng)ip:port信息,以及判斷自己這一側(cè)的NAT的“難度”(difficulty);
  3. 使用portmapping協(xié)議來(lái)獲取更多的公網(wǎng)ip:ports;
  4. 檢查NAT64,通過(guò)它獲取自己的公網(wǎng)ip:port;
  5. 將自己的所有公網(wǎng)ip:ports信息通過(guò)旁路信道與peer交換,以及某些加密秘鑰來(lái)保證通信安全;
  6. 通過(guò)保底的中繼方式與對(duì)方開(kāi)始通信(可選,這樣連接能快速建立)
  7. 如果有必要/想這么做,探測(cè)對(duì)方的提供的所有ip:port,以及執(zhí)行生日攻擊(birthdayattacks)來(lái)穿透harderNAT;
  8. 發(fā)現(xiàn)更優(yōu)路徑之后,透明升級(jí)到該路徑;
  9. 如果當(dāng)前路徑斷了,降級(jí)到其他可用的路徑;
  10. 確保所有東西都是加密的,并且有端到端認(rèn)證。
聲明:本內(nèi)容為作者獨(dú)立觀點(diǎn),不代表電子星球立場(chǎng)。未經(jīng)允許不得轉(zhuǎn)載。授權(quán)事宜與稿件投訴,請(qǐng)聯(lián)系:editor@netbroad.com
覺(jué)得內(nèi)容不錯(cuò)的朋友,別忘了一鍵三連哦!
贊 1
收藏 1
關(guān)注 181
成為作者 賺取收益
全部留言
0/200
成為第一個(gè)和作者交流的人吧