如何在公司訪問家裡的電腦

因為IPv4地址有限,最大42億個。為了更好的利用這有限的IP數量,網路分為區域網和廣域網,將IP分為了私有IP和公網IP,一個區域網裡的N多臺機器都可以共用一個公網IP,從而大大增加了"可用IP數量"。

當我們需要傳送網路包的時候,在IP層,需要填入源IP地址,和目的IP地址,也就是對應快遞的發貨地址和收貨地址。

但是我們家裡的區域網內,基本上都用192.168.xx.xx這樣的私有IP

如果我們在傳送網路包的時候,這麼填。對方在回資料包的時候該怎麼回?畢竟千家萬戶人用的都是192.168.0.1,網路怎麼知道該發給誰?

所以肯定需要將這個192.168.xx私有IP轉換成公有IP

因此在上篇文章最後,留了這麼個問題。區域網內用的是私有IP,公網用的都是公有IP。一個區域網裡的私有IP想訪問區域網外的公有IP,必然要做個IP轉換,這是在哪裡做的轉換呢?

答案是NAT裝置,全稱Network Address Translation,網路地址轉換。基本上家用路由器都支援這功能。

我們來聊下它是怎麼工作的:

NAT的工作原理

為了簡單,我們假設你很富,你家裡分到了一個公網IP地址 20.20.20.20,對應配到了你家自帶NAT功能的家用路由器上,你家裡需要上網的裝置有很多,比如你的手機,電腦都需要上網,他們構成了一個區域網,用的都是私有IP,比如192.168.xx。其中你在電腦上執行ifconfig命令,發現家裡的電腦IP是192.168.30.5。 你要訪問的公網IP地址是30.30.30.30。

於是就有下面這樣一張圖

當你準備傳送資料包的時候,你的電腦核心協議棧就會構造一個IP資料包。這個IP資料包報頭裡的傳送端IP地址填的就是192.168.30.5,接收端IP地址就是30.30.30.30。將資料包發到NAT路由器中。

此時NAT路由器會將IP資料包裡的源IP地址修改一下,私有IP地址192.168.30.5改寫為公網IP地址20.20.20.20,這叫SNATSource Network Address Translation,源地址轉換)。並且還會在NAT路由器內部留下一條 192.168.30.5 -> 20.20.20.20的對映記錄,這個資訊會在後面用到。之後IP資料包經過公網裡各個路由器的轉發,發到了接收端30.30.30.30,到這裡傳送流程結束。

如果接收端處理完資料了,需要發一個響應給你的電腦,那就需要將傳送端IP地址填上自己的30.30.30.30,將接收端地址填為你的公網IP地址20.20.20.20,發往NAT路由器。NAT路由器收到公網來的訊息之後,會檢查下自己之前留下的對映資訊,發現之前留下了這麼一條 192.168.30.5 -> 20.20.20.20記錄,就會將這個資料包的目的IP地址修改一下,變成內網IP地址192.168.30.5, 這也叫DNAT(Destination Network Address Translation,目的地址轉換)。 之後將其轉發給你的電腦上。

整個過程下來,NAT悄悄的改了IP資料包的傳送和接收端IP地址,但對真正的傳送方和接收方來說,他們卻對這件事情,一無所知

這就是NAT的工作原理。

NAPT的原理

到這裡,相信大家都有一個很大的疑問。

區域網裡並不只有一臺機器,區域網內每臺機器都在NAT下留下的對映資訊都會是 192.168.xx.xx -> 20.20.20.20,傳送訊息是沒啥事,但接收訊息的時候就不知道該回給誰了。

這問題相當致命,因此實際上大部分時候不會使用普通的NAT

那怎麼辦呢?

問題出在我們沒辦法區分內網裡的多個網路連線。

於是乎:

我們可以加入其他資訊去區分內網裡的各個網路連線,很自然就能想到埠。

但IP資料包(網路層)本身是沒有埠資訊的。常見的傳輸層協議TCP和UDP資料包文裡才有的資訊。

於是流程就變成了下面這樣子:

當你準備傳送資料包的時候,你的電腦核心協議棧就會先構造一個TCP或者UDP資料包頭,裡面寫入埠號,比如傳送埠是5000,接收埠是3000,然後在這個基礎上,加入IP資料包頭,填入傳送端和接收端的IP地址。

那資料包長這樣。

假設,傳送端IP地址填的就是192.168.30.5,接收端IP地址就是30.30.30.30。

將資料包發到NAT路由器中。

此時NAT路由器會將IP資料包裡的源IP地址和埠號修改一下,從192.168.30.5:5000改寫成20.20.20.20:6000。並且還會在NAT路由器內部留下一條 192.168.30.5:5000 -> 20.20.20.20:6000的對映記錄。之後資料包經過公網裡各個路由器的轉發,發到了接收端30.30.30.30:3000,到這裡傳送流程結束。

接收端響應時,就會在資料包裡填入傳送端地址是30.30.30.30:3000,將接收端是20.20.20.20:6000,發往NAT路由器。NAT路由器發現下自己之前留下過這麼一條 192.168.30.5:5000 -> 20.20.20.20:6000的記錄,就會將這個資料包的目的IP地址和埠修改一下,變回原來的192.168.30.5:5000。 之後將其轉發給你的電腦上。

如果區域網內有多個裝置,他們就會對映到不同的公網埠上,畢竟埠最大可達65535,完全夠用。這樣大家都可以相安無事。

像這種同時轉換IP和埠的技術,就是NAPT(Network Address Port Transfer , 網路地址埠轉換 )。

看到這裡,問題就來了。

那這麼說只有用到埠的網路協議才能被NAT識別出來並轉發?

但這怎麼解釋ping命令?ping基於ICMP協議,而ICMP協議報文裡並不帶埠資訊。我依然可以正常的ping通公網機器並收到回包。

事實上針對ICMP協議,NAT路由器做了特殊處理。ping報文頭裡有個Identifier的資訊,它其實指的是放出ping命令的程序id

對NAT路由器來說,這個Identifier的作用就跟埠一樣。

另外,當我們去抓包的時候,就會發現有兩個Identifier,一個後面帶個BE(Big Endian),另一個帶個LE(Little Endian)。

其實他們都是同一個數值,只不過大小端不同,讀出來的值不一樣。就好像同樣的數字345,反著讀就成了543。這是為了相容不同作業系統(比如linux和Windows)下大小端不同的情況。

內網穿透是什麼

看到這裡,我們大概也發現了。使用了NAT上網的話,前提得內網機器主動請求公網IP,這樣NAT才能將內網的IP埠轉成外網IP埠

反過來公網的機器想主動請求內網機器,就會被攔在NAT路由器上,此時由於NAT路由器並沒有任何相關的IP埠的對映記錄,因此也就不會轉發資料給內網裡的任何一臺機器。

舉個現實中的場景就是,你在你家裡的電腦上啟動了一個HTTP服務,地址是192.168.30.5:5000,此時你在公司辦公室裡想通過手機去訪問一下,卻發現訪問不了。

那問題就來了,有沒有辦法讓外網機器訪問到內網的服務?

有。

大家應該聽過一句話叫,"沒有什麼是加中間層不能解決的,如果有,那就再加一層"。

放在這裡,依然適用。

說到底,因為NAT的存在,我們只能從內網主動發起連線,否則NAT裝置不會記錄相應的對映關係,沒有對映關係也就不能轉發資料。

所以我們就在公網上加一臺伺服器x,並暴露一個訪問域名,再讓內網的服務主動連線伺服器x,這樣NAT路由器上就有對應的對映關係。接著,所有人都去訪問伺服器x,伺服器x將資料轉發給內網機器,再原路返回響應,這樣資料就都通了。這就是所謂的內網穿透

像上面提到的伺服器x,你也不需要自己去搭,已經有很多現成的方案,花錢就完事了,比如花某殼。

到這裡,我們就可以回答文章標題的問題。

為什麼我在公司裡訪問不了家裡的電腦?

那是因為家裡的電腦在區域網內,區域網和廣域網之間有個NAT路由器。由於NAT路由器的存在,外網服務無法主動連通區域網內的電腦。

兩個內網的聊天軟體如何建立通訊

好了,問題就叒來了。

我家機子是在我們小區的區域網裡,班花家的機子也是在她們小區的區域網裡。都在區域網裡,且NAT只能從內網連到外網,那我電腦上登入的QQ是怎麼和班花電腦裡的QQ連上的呢?

上面這個問法其實是存在個誤解,誤以為兩個qq客戶端應用是直接建立連線的。

然而實際上並不是,兩個qq客戶端之間還隔了一個伺服器。

也就是說,兩個在內網的客戶端登入qq時都會主動向公網的聊天伺服器建立連線,這時兩方的NAT路由器中都會記錄有相應的對映關係。當在其中一個qq上傳送訊息時,資料會先到伺服器,再通過伺服器轉發到另外一個客戶端上。反過來也一樣,通過這個方式讓兩臺內網的機子進行資料傳輸。

兩個內網的應用如何直接建立連線

上面的情況,是兩個客戶端通過第三方伺服器進行通訊,但有些場景就是要拋開第三端,直接進行兩端通訊,比如P2P下載,這種該怎麼辦呢?

這種情況下,其實也還是離不開第三方伺服器的幫助。

假設還是A和B兩個區域網內的機子,A內網對應的NAT裝置叫NAT_A,B內網裡的NAT裝置叫NAT_B,和一個第三方伺服器server。

流程如下:

step1和2: A主動去連server,此時A對應的NAT_A就會留下A的內網地址和外網地址的對映關係,server也拿到了A對應的外網IP地址和埠。

step3和4: B的操作和A一樣,主動連第三方server,NAT_B內留下B的內網地址和外網地址的對映關係,然後server也拿到了B對應的外網IP地址和埠。

step5和step6以及step7: 重點來了。此時server發訊息給A,讓A主動發UDP訊息到B的外網IP地址和埠。此時NAT_B收到這個A的UDP資料包時,這時候根據NAT_B的設定不同,導致這時候有可能NAT_B能直接轉發資料到B,那此時A和B就通了。但也有可能不通,直接丟包,不過丟包沒關係,這個操作的目的是給NAT_A上留下有關B的對映關係

step8和step9以及step10: 跟step5一樣熟悉的配方,此時server再發訊息給B,讓B主動發UDP訊息到A的外網IP地址和埠。NAT_B上也留下了關於A到對映關係,這時候由於之前NAT_A上有過關於B的對映關係,此時NAT_A就能正常接受B的資料包,並將其轉發給A。到這裡A和B就能正常進行資料通訊了。這就是所謂的NAT打洞

step11: 注意,之前我們都是用的UDP資料包,目的只是為了在兩個區域網的NAT上打個洞出來,實際上大部分應用用的都是TCP連線,所以,這時候我們還需要在A主動向B發起TCP連線。到此,我們就完成了兩端之間的通訊。

這裡估計大家會有疑惑。

埠已經被udp用過了,TCP再用,那豈不是埠重複佔用(address already in use)?

其實並不會,埠重複佔用的報錯常見於兩個TCP連線在不使用SO_REUSEADDR的情況下,重複使用了某個IP埠。而UDP和TCP之間卻不會報這個錯。之所以會有這個錯,主要是因為在一個linux核心中,核心收到網路資料時,會通過五元組(傳輸協議,源IP,目的IP,源埠,目的埠)去唯一確定資料接受者。當五元組都一模一樣的時候,核心就不知道該把資料發給誰。而UDP和TCP之間"傳輸協議"不同,因此五元組也不同,所以也就不會有上面的問題。

NAPT還分為好多種型別,上面的nat打洞方案,都能成功嗎?

關於NAPT,確實還細分為好幾種型別,比如完全錐形NAT和限制型NAT啥的,但這並不是本文的重點。所以我就略過了。我們現在常見的都是錐形NAT。上面的打洞方案適用於大部分場景,這其中包括限制最多的埠受限錐形NAT

總結

  • IPV4地址有限,但通過NAT路由器,可以使得整個內網N多臺機器,對外只使用一個公網IP,大大節省了IP資源。
  • 內網機子主動連線公網IP,中間的NAT會將內網機子的內網IP轉換為公網IP,從而實現內網和外網的資料互動。
  • 普通的NAT技術,只會修改網路包中的傳送端和接收端IP地址,當內網裝置較多時,將有可能導致衝突。因此一般都會使用NAPT技術,同時修改傳送端和接收端的IP地址和埠。
  • 由於NAT的存在,公網IP是無法訪問內網服務的,但通過內網穿透技術,就可以讓公網IP訪問內網服務。一波操作下來,就可以在公司的網路裡訪問家裡的電腦。

最後留個問題,有了NAT之後,原本並不富裕的IPv4地址突然就變得非常夠用了。

那我們為什麼還需要IPv6?

另外IPv6號稱地址多到每粒沙子都能擁有自己的IP地址,那我們還需要NAT嗎?

作者:小白debug
       連結:
       https://juejin.cn/post/7170850066473680927
       來源:稀土掘金