IPv6技術詳解:基本概念、應用現狀、技術實踐(下篇)
本文來自微信技術架構部的原創技術分享。
1、前言
在上篇《IPv6技術詳解:基本概念、應用現狀、技術實踐(上篇)》,我們講解了IPV6的基本概念。
本篇將繼續從以下方面展開對IPV6的講解:
1)IPv6在Linux作業系統下的實現;
2)IPv6的實驗;
3)IPv6的過渡技術介紹;
4)IPv6在Linux平臺下socket程式設計應該注意的問題。
如您對IPV6的基本概念尚未了解,請先閱讀本文的上篇。
學習交流:
– 即時通訊開發交流3群:185926912[推薦]
– 移動端IM開發入門文章:《新手入門一篇就夠:從零開發移動端IM》
(本文同步釋出於:http://www.52im.net/thread-1607-1-1.html)
2、系列文章
文章太長,分為兩篇來講,本文是2篇文章中的第2篇:
本文是系列文章中的下篇,主要講解IPV6的應用現狀和技術實踐等。
3、Linux核心IPv6架構簡析
本文後面主要的分析都是基於Linux,會有涉及關於Linux核心對IPv6的實現。主要是因為,現在IPv6的參考資料不多,除了與IPv6相關的RFC之外,還有少數可以參閱的IPv6國外文獻,而Linux核心一直都與跟隨著IPv6的協議更新和變化,Linux核心IPv6的實現是十分重要的參考材料之一。而且從事後臺開發工作主要也是在Linux平臺下,熟悉Linux下IPv6的實現也是為以後的工作做知識儲備。
PS:客戶端開發的同學可以參考各自平臺的文件……
Linux在很早之前就已經開始支援IPv6,目前我們接觸最多的Linux核心版本都很好地支援IPv6,同時也是支援IPv4/IPv6雙棧體系。在Linux作業系統中,IPv4是預設必須開啟,IPv6是可選編譯和配置開啟。
例如在編譯核心的時候,需要選擇IPv6編譯選項才支援IPv6:
當開啟支援IPv6的Linux的核心網路雙棧的結構,如下圖:
▲ 圖11:Linux核心雙棧架構
Linux核心中,IPv6協議棧與IPv4協議棧並行關係,IPv6和IPv4完全是兩套不一樣的程式碼實現。
IPv6完整的協議棧邏輯模組包括:
1)網路層IPv6,核心邏輯:IPv6路由子系統;
2)傳輸層TCP/UDP實現:TCPv6、UDPv6;
3)控制報文協議ICMPv6,這裡值得一提的是ICMPv6在IPv6協議中的地位十分重要:
ICMPv6不僅提供了與ICMPv4相同的服務診斷功能,例如報告資料包的錯誤和提供簡單的echo服務,ICMPv6是IPv6中鄰居發現協議的重要組成部分,用於管理鏈路上的點到點的通訊;
4)鄰居子系統的實現:鄰居發現協議NDP(對應於IPv4裡面的ARP協議);
5)其他高階實現(IPv6 NAT、IPv6隧道、iPv6 IPSec等)。
由於我們平時的開發工作在應用層,以上1-4是將會接觸得最多。
4、IPv6實驗
本章我們通過實驗,加深對IPv6的認識。這裡的實驗沒有使用真實現網的IPv6接入點(目前國內絕大部分接入點都是教育網),而實驗的目的主要是觀察IPv6的資料包結構、IPv6的路由配置等,所以決定自己通過搭建中間路由器、應用伺服器的方式做實驗,便於抓包和程式碼分析。
客戶端:windows 7
路由器:中間路由器使用自己編譯和搭建的Linux系統(核心2.6.32.27)
應用伺服器:Ubuntu16.04LTS版本。
為什麼要使用自己編譯的Linux作為路由器?因為IPv6的實踐類能參考的文獻比較少,而Linux核心的IPv6模組是最重要的參考資源之一,在實踐中遇到問題可以使用打LOG和分析程式碼的方法解決。
4.1無狀態自動配置地址實驗
IPv6地址的獲取是最重要的環節之一。本實驗使用開源的無狀態自動配置服務radvd進行實驗。
▲ 圖12:IPv6無狀態自動配置
▲ 圖13:IPv6無狀態自動配置報文分析
無狀態自動配置過程:
1)由鏈路上的主機向鏈路發起“路由請求”報文,這個報文是以組播協議傳送,尋找鏈路上最合適的路由器;
2)路由器收到請求會返回“路由通告”報文,報文裡面帶著本鏈路的地址字首資訊主機將接收到的字首和自身的介面ID,組成完整的新地址;
3)主機嘗試使用新地址發起地址重複檢測,檢測鏈路上是否有其他主機也是這個地址,如果有,就停止使用該地址;如果沒有,就啟用這個新地址。
可以看到無狀態自動配置過程十分簡易(對比DHCPv4和DHCPv6來說),實際上,無狀態自動配置可以單獨組網使用,也可以配合有狀態自動配置一般會配合使用,加強網路節點管理。涉及自動配置和地址檢測等更多細節,可以查閱RFC1971、RFC4861。
4.2IPv6靜態路由配置實驗
本次實驗主要是瞭解windows和linux的靜態路由配置。
▲ 圖14:IPv6典型的網路拓撲
由於各自的網路字首(網段)不一致,在不使用預設路由的情況下,我們嘗試配置路由讓客戶端可以訪問到伺服器。
第一步:Windows 7配置靜態路由
去往伺服器的2001:db8:5::/64網段的路由
▲ 圖15:Windows配置IPv6路由
第二步:路由器1配置
▲ 圖16:Linux下配置IPv6路由
第三步:路由器2配置
▲ 圖17:Linux下配置IPv6路由
第四步:伺服器靜態路由配置
▲ 圖18:伺服器配置IPv6路由
第五步:結果
▲ 圖19:客戶端訪問伺服器
客戶端可以順利ping通伺服器。可以看到,IPv6下的路由配置,無論是windows還是linux,與IPv4的配置差別不大,熟悉IPv4各個平臺路由配置的同學可以很快上手IPv6的路由配置。
4.3IPv6的web服務
複用2的架構,在伺服器端部署一個web服務,在客戶端訪問該web服務。web服務沒有選擇像apache或者nginx這樣的龐然大物,而選擇了很輕量的boa。原因是boa雖然原始支援IPv6,但是我想粗暴的把所有IPv4的socket套接字都替換成IPv6版本,嘗試做一個自定義的升級。結果需要改動的程式碼非常少,不超過20行,boa就能完全支援IPv6。
配合實驗,寫了一個簡單的CGI,只是在版面echo字串。如下圖:
▲ 圖20:瀏覽器使用IPv6地址訪問網路資源
這裡值得注意的是,在瀏覽器中使用IPv6的地址訪問web資源,IPv6的地址必須要使用中括號“[]”包起來。
▲ 圖21:IPv6下的http報文
從Server端抓包看,IPv6下的Web服務http報文,除了網路層L3的報文頭部不一樣之外,其餘的都和IPv4版本的沒有太大差別差別。
4.4IPv6的過渡技術實驗
這部分將在過渡技術介紹中一起實驗。
5、IPv6的過渡技術
IPv6的提出,最重要的目的就是解決公網IPv4耗盡的問題,而且IPv6協議的設計就考慮到了更加好的效率、安全、擴充套件等方面,可以那麼說,IPv6是未來網路發展的大趨勢。
但為什麼IPv6已經發展了十幾年了,目前在我們的工作和生活中還是比較少接觸和使用。
這裡的原因是非常的複雜,有技術上障礙,因為IPv6和IPv4是兩個完全不相容的協議(在極少數的特定場景可以實現相容),如果要從支援IPv4升級到IPv6,無論是應用程式用客戶端、伺服器程式端、路由器等等,都要同時支援IPv6才能解決問題,這個的升級改造需要花費的成本是巨大的。
而且,正是由於技術上的升級花費大量的人力物力,無論是運營商還是網際網路服務商,一方面要重視使用者的體驗問題,這個肯定不能強制客戶更新換代硬體裝置和軟體,另一方面也要維護自身的投資和利益,更願意去選擇利用現有技術降低IPv4地址耗盡帶來的壓力,例如NAT的廣泛應用,就是IPv6推廣使用的一個重要的“障礙”。
由上所述,IPv4升級到IPv6肯定不會是一蹴而就的,是需要經歷一個十分漫長的過渡階段(用我廠通用的術語說,就是IPv4升級IPv6這個灰度的時間非常長),要數十年的時間都不為過。現階段,就出現了IPv4慢慢過渡到IPv6的技術(或者叫過渡時期的技術)。過渡技術要解決最重要的問題就是,如何利用現在大規模的IPv4網路進行IPv6的通訊。
要解決上面的問題,這裡主要介紹3種過渡技術:
1)雙棧技術;
2)隧道技術;
3)轉換技術(有一些文獻叫做翻譯技術)。
本章節會對以上的過渡技術,選取幾個典型的、我們未來最有機會接觸到的具體的過渡技術結合實驗觀察過渡技術的具體實現和資料包的表現形式。
5.1什麼是雙棧技術?
這種技術其實很好理解,就是通訊節點同時支援IPv4和IPv6雙棧。例如在同一個交換機下面有2個Linux的節點,2個節點都是IPv4/IPv6雙棧,節點間原來使用IPv4上的UDP協議通訊傳輸,現在需要升級為IPv6上的UDP傳輸。由於2個節點都支援IPv6,那隻要修改應用程式為IPv6的socket通訊基本達到目的了。
上面的例子在區域網通訊的改造是很容易的。但是在廣域網,問題就變得十分複雜了。因為主要問題是在廣域網上的2個節點間往往經過多個路由器,按照雙棧技術的部署要求,之間的所有節點都要支援IPv4/IPv6雙棧,並且都要配置了IPv4的公網IP才能正常工作,這裡就無法解決IPv4公網地址匱乏的問題。因此,雙棧技術一般不會直接部署到網路中,而是配合其他過渡技術一起使用,例如在隧道技術中,在隧道的邊界路由器就是雙棧的,其他參與通訊的節點不要求是雙棧的。
5.2什麼是隧道技術?
當前的網路是以IPv4為主,因此儘可能地充分利用IPv4網路進行IPv6通訊是十分好的手段之一。隧道技術就是這樣子的一種過渡技術。
隧道將IPv6的資料包文封裝在IPv4的報文頭部後面(IPv6的資料包文是IPv4的載荷部分),IPv6通訊節點之間傳輸的IPv6資料包就可以穿越IPv4網路進行傳輸。隧道技術的一個很重要的優點是透明性,通過隧道進行通訊的兩個IPv6節點(或者節點上的應用程式)幾乎感覺不到隧道的存在。
▲ 圖22:IPv6典型的隧道
上圖是一種典型的隧道技術:路由器-路由器隧道,兩個IPv6網路中的主機通過隧道方式穿越了IPv4進行通訊。其中C節點和D節點被稱為邊界路由器,邊界路由器必須要支援IPv4-IPv6雙棧。當IPv6網路1的主機A將IPv6資料包發給邊界路由器C,C對IPv6資料包進行IPv4封裝,然後在IPv4網路上進行傳輸,傳送到邊界路由器D,D收到IPv4的資料包後剝掉IPv4的包頭,還原IPv6的資料包,傳送到IPv6網路2的主機B。
根據隧道的出口入口的構成,隧道可以分為路由器-路由器,主機-路由器隧道、路由器-主機、主機-主機隧道等型別。
隧道的型別也分為手動配置型別和自動配置型別兩種,手動配置是指點對點的隧道是手動加以配置,例如手動配置點對點隧道外層的IPv4地址才能建立起隧道;自動配置是指隧道的建立和解除安裝是動態的,一般會把隧道外層的IPv4地址內嵌到資料包的目的IPv6地址裡面,在隧道路由器獲取該IPv6地址時候取出內嵌IPv4地址從而使用該IPv4地址作為隧道的對端來建立隧道。
下面就介紹幾種我們很可能會接觸到的具體的隧道技術。
在介紹具體的隧道技術前,特別要說明一下,Linux核心原生支援一種叫做sit(Simple Internet Transition)隧道。這個隧道專門用於IPv6-in-IPv4的資料封裝解封和傳輸,應用十分之廣泛,現在很多主流的IPv6隧道技術都能基於sit隧道實現。關於sit隧道的技術實現,可以查閱Linux核心原始碼 net/ipv6/sit.c 。
5.3隧道技術之6to4隧道
6to4是當前使用得比較廣泛的一種自動配置隧道技術,這種技術採用特殊的IPv6地址,稱為6to4地址,這種地址是以2002開頭,接著後面的32位就是內嵌的隧道對端的IPv4地址。當邊界路由器收到這類目的地址,取出IPv4地址建立隧道。
6to4隧道一般用在路由器-路由器、主機-路由器、路由器-主機場景,典型的應用場景是兩個IPv6的站點內主機通過6to4隧道進行相互訪問。
6to4隧道的一個限制是內嵌的IPv4地址必須是公網地址。
6to4隧道實驗過程如下。
如下圖,就是本次6to4實驗中使用的隧道架構,該架構是典型的路由器-路由器隧道,隧道兩側的IPv6網路對隧道的存在無感知。
▲ 圖23:6to4路由器-路由器隧道
在Linux下的sit隧道可以自適應為6to4隧道。
▲ 圖24:Linux下配置sit隧道(6to4)
上圖就是在路由器上配置sit隧道的命令,因為是使用6to4隧道,隧道的目的端點地址是從目的地址中獲取,因此只需要配置本地端點即可。
▲ 圖25:瀏覽器通過隧道訪問web服務
配置完隧道後,使用客戶端訪問web服務,可以正常訪問。
▲ 圖26:web伺服器端抓取http報文
在web服務端抓取http報文,可以看到,web服務獲取到就是一個普通的http請問報文。
▲ 圖27:隧道內抓取http報文
在隧道內抓取http報文,可以看到裡面的乾坤。這個不是一般的http報文,它比服務端抓取到的多了一層IPv4報文頭部,是隧道的外出通訊協議,隧道內層IPv6才是真正的資料。IPv4報文頭部中的協議欄位,不是我們熟悉的TCP(6)/UDP(17)協議,而是IPv6-in-IPv4專屬的隧道協議型別。
可以看到,經過隧道的資料包文,在隧道兩端的邊界路由器分別完成了隧道協議的封包和解包,在真正獲取到資料的節點看來,幾乎不感知隧道的存在。
5.4隧道技術之ISATAP隧道
ISATAP全稱是站點內自動隧道定址協議(Intra-Site Automatic Tunnel Addressing Protocol),用來為IPv4網路中的IPv6雙棧節點可以跨越IPv4網路訪問外部的IPv6節點。
ISATAP隧道一般用於主機-主機、主機-路由器的場景。
ISATAP隧道實驗過程如下所示。
如下圖就是本次實驗使用的架構,是一種典型的主機-路由器場景。實驗中需要在路由器2上部署radvd服務,用於客戶端進行無狀態自動配置地址。Linux下的ISATAP隧道也是可以使用sit隧道實現。
▲ 圖28:ISATAP主機-路由器隧道
▲ 圖29:Windows下配置ISATAP隧道
實驗用的客戶端使用windows 7,原生支援ISATAP隧道,如上圖,需要進入netsh開啟並且設定ISATAP的路由器地址(支援域名)。
▲ 圖30:ISATAP隧道中的無狀態自動配置
當客戶端設定完router後,隧道已經建立,客戶端便發起了無狀態自動配置流程,可以看到上面的截圖路由器通過隧道將字首資訊下發給客戶端,客戶端完成無狀態自動配置,獲取到公網IP地址。
▲ 圖31:ISATAP隧道介面地址
在windows 7上檢視ISATAP介面,獲取到公網地址。這個地址型別是ISATAP專用的地址結構,由64位全球單播路由字首:200(0):5e5f:w.x.y.z組成(w.x.y.z是客戶端的IPv4地址)。
▲ 圖32:使用ISATAP隧道訪問web服務
如上圖,使用ISATAP隧道訪問web服務,在隧道內的資料抓包,可以看到和6to4的類似,這裡就不再深入闡述。
5.5隧道技術之Teredo隧道
前面的隧道技術,主要是在IPv4的資料包文承載著IPv6的資料包文,這是一種特殊的資料包格式(IPV6-in-IPv4),不同於我們熟悉的TCP、UDP等傳輸層協議。而我們平常接觸到的網路都存在於NAT架構中(例如我們的辦公網路和家庭網路),在這種網路架構中,路由器僅對於TCP、UDP等傳輸層協議做NAT處理,而無法正確處理IPv6-in-IPv4這種報文,例如使用ISATAP隧道,IPv6雙棧節點與ISATAP路由器之前如果存在NAT,ISATAP建立隧道失敗;6to4隧道也會遇到同樣的問題。
Teredo隧道是有微軟公司主導的一項隧道技術,主要用於在NAT網路架構下建立穿越NAT的隧道。
Teredo隧道的核心思路,是將IPv6的資料封裝成IPv4的UDP資料包,利用NAT對IPv4的UDP支援進行穿越NAT的傳輸,當UDP包到達隧道的另外一端後,再把IPv4的包頭、UDP包頭剝離,還原IPv6的資料包,再進行下一步的IPv6資料通訊轉發。Teredo節點會分配一個以2001::/32的字首,而且地址中還包含Teredo的伺服器、標誌位和客戶端外部對映模糊地址和埠號等資訊。
Teredo的實現還會遇到NAT的型別不同而被限制的問題。NAT的型別有錐形NAT、受限制的NAT、對稱NAT幾種,Teredo只能在錐形NAT和受限制的NAT的環境下正常工作,而且在這兩種NAT需要處理的邏輯又是不一樣的。因此Teredo整體的實現會比較複雜。
實驗環境搭建:
在Linux平臺下有開源的Teredo實現版本:miredo。由於時間和文章篇幅的原因,而且部署miredo比較複雜,因此這裡的實驗等以後有機會再補充。
5.6什麼是轉換技術?(有一些文獻叫做:翻譯技術)
隧道技術是比較好地解決了在很長期一段時間內還是IPv4網路是主流的情況下IPv6節點(或者雙棧節點)間的通訊問題。但是由於IPv4到IPv6的過渡是十分漫長的,因此也需要解決IPv6節點與IPv4節點通訊的問題。協議轉換技術可以用來解決這個問題。
協議轉換技術根據協議在網路中位置的不同,分為網路層協議轉換、傳輸層協議轉換和應用層協議轉換等。協議轉換技術的核心思路就是在IPv4和IPv6通訊節點之間部署中間層,將IPv4和IPv6相互對映轉換。
我們非常熟悉的NAT也是一種典型的協議轉換技術,是將私網IPv4地址對映轉換為公網IPv4地址,這種轉換技術又稱為NAT44。而我們接著要重點介紹的名為NAT64/DNS64的協議轉換技術。
5.7轉換技術之NAT64/DNS64
提到NAT64/DNS64,相信做iOS客戶端開發的同學一定非常熟悉。在2016年中開始,蘋果要求app必須支援IPv6網路。而蘋果官方提供的過渡解決方案正是NAT64/DNS64。
以下是蘋果提供的技術圖:
▲ 圖33:蘋果提供的過渡技術解決方案
NAT64/DNS64分為NAT64、DNS64兩大方面,兩者需要結合使用。
DNS64在RFC6147中明確定義,將IPv6的地址記錄AAAA DNS查詢訊息轉換為IPv4的地址記錄查詢。當IPv6節點發起DNS請求,NAT64/DNS64中間層同時發起A域名查詢和AAAA域名查詢。如果僅有A域名查詢的IPv4地址響應,表明IPv6節點需要訪問一個IPv4的節點,NAT64/DNS64中間層將回應的IPv4地址轉換為IPv6地址,返回給IPv6節點。
IPv6節點使用獲取到的IPv6服務端地址進行訪問,資料包會經過NAT64/DNS64中間層,中間層將IPv6地址對映轉換為IPv4的地址進行訪問。
實驗環境搭建過程如下。
Linux平臺下有多個NAT64的開源軟體,實現方式各有不同,有純核心態實現的ecdysis,也有使用者態實現的tayga。
DNS64的實現可以使用著名的開源DNS服務BIND就可以很好地支援,詳細可以檢視上面2個開源軟體的搭建說明。
時間的原因,還沒有把NAT64/DNS64的開源軟體研究透徹,因此這裡的實踐等以後有機會再補上。
PS:在研究tayga和miredo原始碼的時候,發現了在Linux平臺上面有一些有趣的東西,如下圖,是tayga的軟體實現框架。
▲ 圖34:Linux下的一個有趣的虛擬裝置
Linux核心自帶了一個軟體虛擬裝置,也是一種隧道的實現(/dev/net/tun),該裝置可以實現將核心態的網路資料傳送到使用者態,使用者態修改後再返回給核心態,使用者態的程式負責完成NAT64這一次“偷龍轉鳳”操作。
關於/dev/net/tun裝置的實現,可以查閱Linux核心原始碼drivers/net/tun.c,一些著名的VPN軟體例如openvpn等,都是以它作為實現基礎。
本章只介紹了一些典型的過渡技術,其實過渡技術種類還有很多,有一些在實驗室階段,有一些已經商用,有一些已經被廢棄,但是總的來說,每一種過渡技術都是在解決特定時期特定場景下的過渡問題。
6、IPv6 Socket程式設計應該注意的問題
在《IPv6 Socket程式設計》一文中,ray已經很詳細介紹了IPv6下的socket程式設計細節和應該注意的問題。本章作為一個補充,介紹一下IPv6 socket程式設計可能還會遇到的問題。
6.1IPv6地址編碼
IPv4地址本質是一個32位整數,因此一般無論是儲存層還是邏輯層,都經常將點分制的IPv4字串地址轉為32位整數使用。而在IPv6,情況就複雜多了(可能也有同學就想到,光是原子性就很難保證了)。
舉一個典型的例子,現在有個需求,分別統計每個IP的訪問頻次。
在IPv4的情況下,最簡單就是STL用std::map搞定(單執行緒),土豪一點的可以開個16G的陣列用空間換時間。
但是在IPv6的場景下,那就尷尬了,IPv6可是個128位整數,可以用map嗎?可能會有人直接將原始的字串型別的IPv6地址作為key來累計。一旦那麼用,就要十分注意了。由於IPv6是支援前導0和連續0的壓縮表示方式,而且支援英文字母大小寫,例如:
2001:db8:4::41
2001:db8:4:0:0:0:0:0:41
2001:0db8:4::41
2001 B8:4::41
這4個都是合法的IPv6地址,如果將輸入毫無修改地作為key來累計,那必須會將累計邏輯分散了,最終得不到正確的頻率結果。類似的問題也在MAC地址(BSSID)上面,由於MAC地址分號間的數字前導0可以省略,並且也是支援大小寫英文字母,所以也是會同樣的問題。在微信安全中心,MAC地址的邏輯統一轉為64位整數處理,情況相對還好。
但是到了IPv6有木有更好的解決辦法呢?答案是肯定的,但是需要具體問題具體分析。
在上面的頻率例子比較優雅的做法,依然用map的話,可以利用自定義key型別解決,這個方法需要過載自定義型別的比較符號’<’,如下圖所示:
▲ 圖35:自定義IPv6地址結構
其中struct in6_addr就是一個128位的IPv6地址結構體。
▲ 圖36:使用std::map實現IPv6頻率
其實還有更優雅的方式,直接將IPv6的地址強制轉為2個64位整數來比較,if else會寫得更少一些,效率更高一些。
上面說到2個64位整數,微信安全中心有一些靜態的key-value資料查詢(批量寫,多次讀),其中key是MD5,我們將MD5也是作為2個64位整數來對待,將2個64位整數聯合排序,寫入記憶體,然後使用兩次二分查詢的方式搜尋,效率非常高。在這種場景下面,IPv6也是可以用類似的方法處理。
IPv6地址結構,以後很可能會給我們的程式設計或多或少帶來一些“未知”的坑-_-||。
6.2IPv6 socket“相容”IPv4的情況
在IPv4和IPv6共存的一個很長的時間裡,在socket程式設計上不得不面對的就是IPv6和IPv4一定程度的“相容問題”。而在文章前面有提到,IPv6和IPv4和完全不相容的兩種協議,但是IPv6協議的地址空間更大,是可以使用IPv6的地址表示IPv4地址,例如IPv4對映地址,因此,在很特殊的情況下,IPv4和IPv6可以實現“相容”,但是這種相容是很有限的。在Linux平臺下,這種“相容性”是如何表現的,我們這裡來分析一下。
在Linux下面,以IPv6下的UDP Socket舉例,詳細如下。
有個UDP協議的Server改造IPv6,該Server機器上有一個網路卡並且同時配置IPv6和IPv4地址,支援雙棧。Server程式建立IPv6 UDP socket套接字,繫結Server本地任意地址(IPv4和IPv6都是以全0地址為繫結任意地址)。客戶端是IPv4,向這個Server傳送UDP請求資料包。
▲ 圖37:IPv6服務收到IPv4報文
可以看到的是,IPv6的socket會正常收到客戶端的資料包文,並且會將IPv4地址轉化為對映地址,為了明確這個邏輯,我們分析Linux核心的實現。
▲ 圖38:IPv6下UDP socket收到IPv4資料包核心實現
IPv6的socket收到資料包,如果是IPv4協議,則將來源IPv4的IP地址轉為IPv6的IPv4對映地址。與實驗的結果很一致。
如果Server的IPv6 socket按照這個來源地址返回資料包,那麼核心又是如何處理的呢?
▲ 圖39:IPv6下UDP socket傳送IPv4資料包核心實現
首先核心會判斷目的地址是否為IPv6的IPv4對映地址,如果是對映地址,那麼要傳送的資料是IPv4資料,直接以IPv4協議棧的形式傳送該資料(udp_sendmsg是IPv4 udp傳送介面)。
可以看到,Linux核心本身對這類雙棧上的改造做了一定的適配,我們可以根據核心的這種特性去進行改造工作。
6.3使用鏈路本地地址
從前面的章節可以知道,IPv6具有自動配置地址的能力。鏈路本地地址是IPv6要求在每個介面預設自動配置生成的地址,用於鏈路上的通訊,路由器不能轉發鏈路本地地址。除了以上提到的特徵外,鏈路本地地址就是一個普通的IPv6地址,我們可以使用這類地址做socket程式設計通訊。
但是我們在IPv6 Socket程式設計的時候使用鏈路本地地址,有一個細節需要注意。
▲ 圖40:IPv6地址結構
在IPv6地址結構中(對應於IPv4的struct sockaddr_in),有一個我們非常陌生的欄位scope_id,這個欄位在我們使用鏈路本地地址來程式設計的時候是必須要使用的,這個欄位表示我們需要選擇介面ID。為什麼需要需要有這麼一個欄位,那是因為鏈路本地地址的特殊性,一個網路節點可以有多個網路介面,多個網路介面可以有相同的鏈路本地地址,例如我們需要bind一個本地鏈路地址,這個時候就會有衝突,作業系統無法決策需要繫結的是哪個介面的本地鏈路地址。
又例如,如果我們在直連的2個主機之間直接用鏈路本地地址ping的話,會ping失敗。
因此IPv6引入了scope_id來解決這個問題,scope_id指定了使用哪個網路介面。
如何檢視這個網路介面(網路卡)的scope_id是多少?
在Linux下檢視網路介面的scope_id:
▲ 圖41:Linux下檢視網路介面scope id
使用ip addr命令可以檢視每個介面的scope_id,如圖第一列的數字就是scope_id。
在windows下檢視scope_id:
▲ 圖42:Windows下檢視網路介面scope id
最後的百分號%後面的數字就是該網路介面的scope_id。
Windows下也可以使用route print -6檢視介面列表,列表第一列數字就是scope_id。
因此,在使用鏈路本地地址程式設計的時候,需要把這個scope_id賦值到sin6_scope_id欄位。
而在使用ping命令的時候,需要在地址後面加上%和scope_id才能ping成功,如圖:
▲ 圖43:使用鏈路本地地址ping
關於這個scope id,詳細可以檢視RFC2553。
7、全文總結
本文主要科普介紹了IPv6的基本內容,配合各種實驗分析比較清晰認識了IPv6的各種基本概念;也介紹一些“超綱”的內容(我們的工作中很可能不會接觸到),但是我覺得這類內容在技術實現上十分有趣,可以在一些技術的方法和思路上面可能會給我們一些通用的啟示,例如NAT64/DNS64就是使用中間層來處理IPv4和IPv6互通的問題,我們的工作中也確實經常遇到類似的技術問題。
IPv6本身是一個很龐大的體系,還有很多高階內容沒有介紹(IPv6-IPSec、移動IPv6等等)。而且檢視和IPv6相關的RFC,不斷在做修正,Linux核心的IPv6模組程式碼也不斷有配合新的RFC修改來做調整,引入新的邏輯,以適應各種場景的實際需求。有興趣的同學可以一直留意RFC的變化和緊跟Linux核心的版本釋出。
本文是我在結合各種文獻和實驗對IPv6理解的一個總結歸納,難免會有理解偏差和手抖的地方,希望各位同學熟悉的話能幫忙指出其中的錯誤,並且提供修改建議和意見,謝謝:)。
(——接上篇《IPv6技術詳解:基本概念、應用現狀、技術實踐(上篇)》,全文完——)
8、參考文獻
1、《深入解析IPv6(第3版)》
2、《TCP/IP詳解-卷1協議》
3、《TCP/IP協議原理與應用(第4版)》
4、《精通Linux核心網路》
5、RFC4380 “Teredo: Tunneling IPv6 over UDP through Network Address Translations”
6、RFC3596 “DNS Extensions to support IP version 6”
7、RFC4193 “Unique Local IPv6 Unicast Address”
8、RFC3879 “Deprecating Site Local Address”
9、RFC2553 “Basic Socket Interface Extensions for IPv6”
10、RFC4214 “Intra-Site Automatic Tunnel Address Protocol(ISATAP)”
11、RFC6147 “DNS Extensions for Network Address Translation from IPv6 Clients to IPv4 Servers”
12、RFC6052 “IPv6 Addressing of IPv4/IPv6 Translators”
13、RFC6146 “Stateful NAT64: Network Address and Protocol Translation from IPv6 Clients to IPv4 Servers”
14、RFC3068 “An Anycast Prefix for 6to4 Deployment”
15、RFC2460 “Internet Protocol, Version 6 Specification”
16、RFC4291 “IP Version 6 Addressing Architecture”
17、RFC1971 “IPv6 Stateless Address Autoconfiguration”
18、RFC4861 “Neighbor Discovery for IP version6”
9、文中使用到的開源軟體列表
1、Linux-2.6.32.27原始碼 (www.kernel.org)
2、Linux-3.10.108原始碼 (www.kernel.org)
3、Linux-4.9.75原始碼 (www.kernel.org)
4、無狀態自動配置服務radvd (http://www.litech.org/radvd/)
5、NAT64服務tayga(http://www.litech.org/tayga/)
6、NAT64/DNS64服務ecdysis (http://ecdysis.viagenie.ca/)
7、teredo服務mirodo(https://www.remlab.net/miredo/)
8、輕量級web伺服器boa (http://www.boa.org/)
附錄:更多相關文章
[1] 更多網路程式設計基礎資料:
《TCP/IP詳解 – 第11章·UDP:使用者資料包協議》
《技術往事:改變世界的TCP/IP協議(珍貴多圖、手機慎點)》
《通俗易懂-深入理解TCP協議(下):RTT、滑動視窗、擁塞處理》
《理論聯絡實際:Wireshark抓包分析TCP 3次握手、4次揮手過程》
《P2P技術詳解(一):NAT詳解——詳細原理、P2P簡介》
《P2P技術詳解(二):P2P中的NAT穿越(打洞)方案詳解》
《P2P技術詳解(三):P2P技術之STUN、TURN、ICE詳解》
《高效能網路程式設計(一):單臺伺服器併發TCP連線數到底可以有多少》
《高效能網路程式設計(二):上一個10年,著名的C10K併發連線問題》
《高效能網路程式設計(三):下一個10年,是時候考慮C10M併發問題了》
《高效能網路程式設計(四):從C10K到C10M高效能網路應用的理論探索》
《不為人知的網路程式設計(一):淺析TCP協議中的疑難雜症(上篇)》
《不為人知的網路程式設計(二):淺析TCP協議中的疑難雜症(下篇)》
《不為人知的網路程式設計(三):關閉TCP連線時為什麼會TIME_WAIT、CLOSE_WAIT》
《不為人知的網路程式設計(四):深入研究分析TCP的異常關閉》
《不為人知的網路程式設計(六):深入地理解UDP協議並用好它》
《不為人知的網路程式設計(七):如何讓不可靠的UDP變的可靠?》
《網路程式設計懶人入門(一):快速理解網路通訊協議(上篇)》
《網路程式設計懶人入門(二):快速理解網路通訊協議(下篇)》
《網路程式設計懶人入門(四):快速理解TCP和UDP的差異》
《網路程式設計懶人入門(五):快速理解為什麼說UDP有時比TCP更有優勢》
《技術掃盲:新一代基於UDP的低延時網路傳輸層協議——QUIC詳解》
《現代移動端網路短連線的優化手段總結:請求速度、弱網適應、安全保障》
《移動端IM開發者必讀(一):通俗易懂,理解行動網路的“弱”和“慢”》
《移動端IM開發者必讀(二):史上最全移動弱網路優化方法總結》
>> 更多同類文章 ……
[2] QQ、微信團隊分享的其它技術文章:
《騰訊技術分享:騰訊是如何大幅降低頻寬和網路流量的(圖片壓縮篇)》
《騰訊技術分享:騰訊是如何大幅降低頻寬和網路流量的(音視訊技術篇)》
《騰訊技術分享:Android版手機QQ的快取監控與優化實踐》
《微信團隊分享:iOS版微信的高效能通用key-value元件技術實踐》
《微信團隊分享:iOS版微信是如何防止特殊字元導致的炸群、APP崩潰的?》
《騰訊技術分享:Android手Q的執行緒死鎖監控系統技術實踐》
《QQ音樂團隊分享:Android中的圖片壓縮技術詳解(上篇)》
《QQ音樂團隊分享:Android中的圖片壓縮技術詳解(下篇)》
《騰訊團隊分享 :一次手Q聊天介面中圖片顯示bug的追蹤過程分享》
《微信團隊分享:微信Android版小視訊編碼填過的那些坑》
《微信團隊披露:微信介面卡死超級bug“15。。。。”的來龍去脈》
《月活8.89億的超級IM微信是如何進行Android端相容測試的》
《微信客戶端團隊負責人技術訪談:如何著手客戶端效能監控和優化》
《微信團隊原創分享:Android版微信的臃腫之困與模組化實踐之路》
《微信團隊原創分享:微信客戶端SQLite資料庫損壞修復實踐》
《騰訊原創分享(一):如何大幅提升行動網路下手機QQ的圖片傳輸速度和成功率》
《騰訊原創分享(二):如何大幅壓縮行動網路下APP的流量消耗(下篇)》
《騰訊原創分享(三):如何大幅壓縮行動網路下APP的流量消耗(上篇)》
《如約而至:微信自用的移動端IM網路層跨平臺元件庫Mars已正式開源》
《開源libco庫:單機千萬連線、支撐微信8億使用者的後臺框架基石 [原始碼下載]》
《微信新一代通訊安全解決方案:基於TLS1.3的MMTLS詳解》
《微信團隊原創分享:Android版微信後臺保活實戰分享(程式保活篇)》
《微信團隊原創分享:Android版微信後臺保活實戰分享(網路保活篇)》
《Android版微信從300KB到30MB的技術演進(PPT講稿) [附件下載]》
《微信團隊原創分享:Android版微信從300KB到30MB的技術演進》
《微信技術總監談架構:微信之道——大道至簡(PPT講稿) [附件下載]》
《微信海量使用者背後的後臺系統儲存架構(視訊+PPT) [附件下載]》
《微信非同步化改造實踐:8億月活、單機千萬連線背後的後臺解決方案》
《架構之道:3個程式設計師成就微信朋友圈日均10億釋出量[有視訊]》
《微信團隊原創分享:Android記憶體洩漏監控和優化技巧總結》
《微信團隊原創Android資源混淆工具:AndResGuard [有原始碼]》
《移動端IM實踐:Android版微信如何大幅提升互動效能(一)》
《移動端IM實踐:Android版微信如何大幅提升互動效能(二)》
《移動端IM實踐:WhatsApp、Line、微信的心跳策略分析》
《移動端IM實踐:谷歌訊息推送服務(GCM)研究(來自微信)》
《信鴿團隊原創:一起走過 iOS10 上訊息推送(APNS)的坑》
>> 更多同類文章 ……
(本文同步釋出於:http://www.52im.net/thread-1607-1-1.html)
相關文章
- Embedding技術與應用(3):Embeddings技術的實踐應用
- IPv6技術與應用創新
- HTML程式碼混淆技術:原理、應用和實現方法詳解HTML
- vivo直播應用技術實踐與探索
- 融合技術設施的實踐應用
- 華為快應用引擎技術架構詳解架構
- 深度學習核心技術實踐與圖神經網路新技術應用深度學習神經網路
- IPv6過渡技術之隧道技術
- 鏈路追蹤技術的應用及實踐
- 後臺開發 -- 核心技術與應用實踐
- 黃波:AI技術在知乎的應用實踐AI
- AIGC神器CLIP:技術詳解及應用示例AIGC
- Cube 技術解讀 | Cube 小程式技術詳解
- Cube 技術解讀 | Cube 卡片技術棧詳解
- JNI技術詳解
- 技術工坊|WASM應用區塊鏈虛擬機器的技術實踐(上海)ASM區塊鏈虛擬機
- IPv6過渡技術之雙棧技術
- 超詳細Maven技術應用指南Maven
- 資料加密新技術-實時雲渲染技術應用加密
- 請教這個應用技術如何實現?
- 2021 技術展望丨AV1 在 RTC 應用實踐中的現狀與展望
- 人臉識別:技術應用與商業實踐
- 得物技術時間切片的實踐與應用
- 大資料技術現狀大資料
- 騰訊 iOA 技術實踐
- SVG Sprite 技術實踐SVG
- IPv6過渡技術之NAT-PT技術
- 詳解愛奇藝ZoomAI影片增強技術的應用OOMAI
- iOS 7: iPhone/iPad應用開發技術詳解iOSiPhoneiPad
- SQL 注入技術詳解SQL
- oracle flashback技術詳解Oracle
- 防火牆技術詳解防火牆
- Service Mesh技術詳解
- 後臺開發:核心技術與應用實踐 -- C++C++
- 差分隱私技術在火山引擎的應用實踐
- 《大資料:技術與應用實踐指南》圖書資訊大資料
- 技術教程網 -- 實用技術參考 (轉)
- 下篇:技術 Leader 的思考方式