一份完整的 IPv6 環境下 DNS 相關測試
董濤,網易游戲高級運維工程師,主要工作方向為網易集團 DNS 的運維與開發。
張欣接,網易集團 DNS 團隊負責人,負責網易域名系統的架構設計及生態建設。
一、IPv6 支持度報告
IPv6 簡介
IPv6(Internet Protocol version 6,互聯網通信協議第 6 版)是用于數據包交換互聯網絡的網絡層協議,是 IETF(互聯網工程任務小組 Internet Engineering Task Force,簡稱 IETF)設計的用來替代 IPv4 協議的互聯網協議版本。
隨著電子技術及網絡技術的發展,計算機網絡已經與人們的生活密切相關,可能身邊的每一樣電子設備都需要連入網絡,IPv4 的地址數量已經無法滿足。IPv6 的應用將徹底解決這些問題。IPv6 由 128 比特位構成,單從數量級上來說,IPv6 所擁有的地址容量是 IPv4 的約 8×10 28 倍,達到 2 128(約 3.4 × 10 38)個。這不但解決了網絡地址資源數量的問題,同時也為物聯網的發展提供了基礎。
IPv6 地址的表達形式采用 32 個十六進制數,由兩個邏輯部分組成:一個 64 位的網絡前綴和一個 64 位的主機地址,主機地址通常根據物理地址自動生成,叫做 EUI-64(或者 64- 位擴展唯一標識)。例如 2001:0db8:85a3:08d3:1319:8a2e:0370:7344 是一個合法的 IPv6 地址。
IPv6 全球部署更新
-
2008 年,歐盟發布了“歐洲部署 IPv6 行動計劃”
-
2009 年,日本發布《IPv6 行動計劃》
-
2010 年,美國政府發布 IPv6 行動計劃
-
2010 年,韓國發布“下一代互聯網協議(IPv6) 促進計劃”
-
2012 年,加拿大政府發布了《加拿大政府 IPv6 戰略》
-
2017 年,國務院辦公廳印發《推進互聯網協議第六版(IPv6)規模部署行動計劃》
操作系統 IPv6 支持度
應用軟件 IPv6 支持度
客戶端軟件
1、瀏覽器
服務器軟件
1、程序開發軟件
2、數據庫
總結
毋庸置疑,下一代互聯網 IPv6 是萬物互連,智能化時代基礎網絡的重要支撐協議,但是從一個只擁有 IPv4 協議的巨型網絡要全面、平穩地過渡到一個純 IPv6 網絡需要一段極為漫長的時間。從報告統計的數據來看,各種基礎軟件和應用軟件都已基本支持 IPv6。現在在國內的環境下,IPv6 的基礎環境還需要完善,為此工信部也發布了
《推進互聯網協議第六版(IPv6)規模部署行動計劃》(http://www.miit.gov.cn/n1146290/n4388791/c6166476/content.html)
推動各單位加快支持 IPv6。
IPv6 支持度報告的數據來源是:下一代國家互聯網中心在 2017 年 11 月發布的 IPv6 支持度報告
(https://www.ipv6ready.org.cn/public/download/ipv6.pdf), 感興趣的同學可以查看原文。
二、IPv6 環境下 DNS 相關測試
背景介紹
名詞簡介
-
A 記錄
A 記錄是一個域名指向 IPv4 地址的解析結果,即最常見的記錄類型, 例如 ipv6test.ntes53.netease.com. 1800 IN A 123.58.166.70
-
AAAA 記錄
AAAA 是一個域名指向 IPv6 地址的解析結果。如果想要一個域名解析到 IPv6 地址,則需要設置此種類型的解析結果。同一個域名可以同時有 A 與 AAAA 兩種記錄類型, 例如 ipv6test.ntes53.netease.com. 1800 IN AAAA 2403:c80:100:3000::7b3a:a646
-
緩存 DNS 服務器
用戶直接使用的 DNS 服務器,各種平臺、操作系統上直接設置的 DNS 服務器,常見的有 8.8.8.8, 114.114.114.114
-
權威 DNS 服務器
用于域名的管理。權威 DNS 服務器只對自己所擁有的域名進行域名解析,對于不是自己的域名則拒絕應答。例如網易的權威 DNS 服務器只會響應網易域名的請求,對于其他域名,則拒絕應答。
-
雙棧網絡環境
雙棧網絡環境即客戶端或服務器同時擁有 IPv4、IPv6 兩種網絡環境,可以簡單的理解為機器上既有 IPv4 地址又有 IPv6 地址
測試場景
下文中所有測試使用的程序均為測試方法中的程序
1.目前純 IPv4 環境下,僅新增 AAAA(IPv6) 記錄之后,對已有程序的影響
假定已經存在了一個程序(C 程序、python 程序、瀏覽器等),通過域名訪問某個服務,現在在 IPv4 環境下一切工作正常。當給這個域名增加了 AAAA 記錄之后,測試對目前的程序的影響。
域名解析
HTTP 請求
客戶端
結論
-
當在某域名原有的 A 記錄類型的基礎上新增 AAAA 記錄后,原有的程序工作正常
2.客戶端 IPv6/v4 雙棧環境下,測試程序的行為
假定用戶的環境是雙棧環境,假定一個服務通過域名對外提供服務,測試這種情況下程序的行為。
域名解析
HTTP 請求
客戶端
結論
-
當域名同時存在 A 與 AAAA 記錄,并且網絡類型為雙棧網絡時,絕大多數程序工作正常。僅有一種情況例外,即程序中使用了 gethostbyname 函數,同時 resolv.conf 中配置了 options inet6 時,此時程序會返回錯誤的解析結果
-
RFC 以及絕大多數實現方式,均回優先使用 IPv6 地址建立連接
-
雙棧環境下,客戶端使用 IPv4 與 IPv6 緩存 DNS 服務器獲取的解析結果是一致的
3. 客戶端純 IPv6 環境下,測試能否正常工作
假定用戶只有 IPv6 地址,DNS 也是使用 IPv6 地址 (DNS 必須有雙棧環境,因為現在很多權威服務器沒有 IPv6 地址,純 IPv6 環境下無法正常工作),假定一個服務通過域名(同時擁有 A、AAAA 記錄)對外提供服務,測試服務是否可以正常訪問。
域名解析
HTTP 請求
客戶端
結論
當某域名即存在 A 記錄 又存在 AAAA 記錄時:
-
如果程序中使用了 gethostbyname 時,程序可能會拿到錯誤的解析結果,取決于 resolv.conf 的配置(當配置了 option inet6 時,會獲取到錯誤的解析結果)
-
Windows 在這種情況下,部分應用工作不正常。在指定使用 IPv6 socket 的情況下,程序工作正常。
-
根據安卓官方的描述,Android 6.0 之后的版本已經支持 IPv6,但是根據對國內大多數廠商的安卓手機的調研,目前國內安卓手機很少可以原生支持 IPv6
4. DNS 解析測試
這里測試了緩存服務器和權威服務器在各種網絡環境下,優先使用的解析鏈路。
結論
當權威服務器和緩存服務器均支持 ipv6 時,緩存服務器優先使用 ipv6 鏈路進行解析,其他情況均使用 ipv4 鏈路進行解析。
結論
-
經過測試與查證,
gethostbyname
不支持 IPv6,使用此函數可能會拿到錯誤的結果或者程序拋出異常。建議使用getaddrinfo
函數取代此函數 -
目前已經存在 A 記錄的域名,添加 AAAA 記錄后,不管客戶端與服務端的網絡環境如何:
-
絕大多數情況下對客戶端與服務端工作正常
-
下面一種情況下會出現工作異常:
當使用了 C 的 gethostbyname 并且在 resolv.conf 中配置了 options inet6時,此函數返回錯誤的結果
-
-
經過測試,雙棧網絡下 IPv4 與 IPv6 的優先級:
-
優先使用 IPv6 發起解析請求
-
優先使用 IPv6 請求建立連接 (TCP, UDP)
-
優先解析 A 地址記錄
-
參考資料
-
Windows 8 IPv4 與 IPv6 選擇的方法:Connecting with IPv6 in Windows8
(https://blogs.msdn.microsoft.com/b8/2012/06/05/connecting-with-ipv6-in-windows-8/) -
Windows 當 IPv6 不可用后的回退機制:Is there any setting for connection timeout when IPv6 fallback to IPv4?
(https://social.technet.microsoft.com/Forums/en-US/d09e938a-a594-4766-8898-3926a81fc5dc/is-there-any-setting-for-connection-timeout-when-ipv6-fallback-to-ipv4?forum=w7itpronetworking) -
目前廣泛使用的 IPv4 與 IPv6 優先選擇算法為 Happy Eyeballs
(https://en.wikipedia.org/wiki/Happy_Eyeballs):-
目前使用此算法的項目有:Chrome, Opera 12.10, Firefox version 13, OS X, cURL
-
此算法會優先選擇 IPv6 鏈路使用
-
此算法的原理可參考 RFC 6555(Happy Eyeballs: Success with Dual-Stack Hosts)
(https://tools.ietf.org/html/rfc6555) -
此算法的簡略工作流程如下:
-
當客戶端是雙棧環境時,客戶端會向緩存 DNS 服務器發起域名 A 記錄與 AAAA 記錄的解析請求,并受到解析結果,對應下圖中的 1-4
-
客戶端獲取到解析地址后,會同時使用 IPv4 與 IPv6 兩種鏈路嘗試建立連接,對應下圖中的 6-7。當 IPv6 鏈路比 IPv4 鏈路先建立連接,或者 IPv4 已經建立連接,但是在很短的時間間隔內,IPv6 也成功建立連接后,則這兩種情況下客戶端應該使用 IPv6 鏈路完成后續的網絡請求,對應圖中的 8-12
-
測試方法
解析域名
C/ C ++
-
gethostbyname
Linux
Windows
getaddrinfo
- #include <stdio.h>
- #include <string.h>
- #include <stdlib.h>
- #include <netdb.h>
- #include <sys/types.h>
- #include <sys/socket.h>
- #include <arpa/inet.h>
- int lookup_host ()
- {
- struct addrinfo hints, *res;
- int errcode;
- char addrstr[100];
- void *ptr;
- memset (&hints, 0, sizeof (hints));
- hints.ai_family = AF_INET;
- errcode = getaddrinfo ("IPv6test.ntes53.netease.com", NULL, &hints, &res);
- if (errcode != 0)
- {
- perror ("getaddrinfo");
- return -1;
- }
- while (res)
- {
- inet_ntop (res->ai_family, res->ai_addr->sa_data, addrstr, 100);
- switch (res->ai_family)
- {
- case AF_INET:
- ptr = &((struct sockaddr_in *) res->ai_addr)->sin_addr;
- break;
- case AF_INET6:
- ptr = &((struct sockaddr_in6 *) res->ai_addr)->sin6_addr;
- break;
- }
- inet_ntop (res->ai_family, ptr, addrstr, 100);
- printf ("IPv%d address: %s (%s)\n", res->ai_family == PF_INET6 ? 6 : 4,
- addrstr, res->ai_canonname);
- res = res->ai_next;
- }
- return 0;
- }
- int main (void)
- {
- lookup_host();
- }
windows
Python
-
socket.gethostbyname
- import socket
- result = socket.gethostbyname("IPv6test.ntes53.netease.com")
- print result
- getaddrinfo
- import socket
- result = socket.getaddrinfo("IPv6test.ntes53.netease.com", 0, socket.AF_INET6)
- print result
- result = socket.getaddrinfo("IPv6test.ntes53.netease.com", 0, socket.AF_INET)
- print result
- result = socket.getaddrinfo("IPv6test.ntes53.netease.com", 0, socket.AF_UNSPEC)
- print result
當不指定 socktype 時,此值默認為 socket.AF_UNSPEC
。
HTTP 請求
Python
requests 包
C++
- #include <stdio.h>
- #include <curl/curl.h>
- int main(void)
- {
- CURL *curl;
- CURLcode res;
- curl = curl_easy_init();
- if(curl) {
- curl_easy_setopt(curl, CURLOPT_URL, "http://IPv6test.ntes53.netease.com:8000");
- /* example.com is redirected, so we tell libcurl to follow redirection */
- curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, 1L);
- // curl_easy_setopt(curl, CURL_IPRESOLVE_V6, 1L); // 使用 IPv6 地址
- // curl_easy_setopt(curl, CURL_IPRESOLVE_V4, 1L); // 使用 IPv4 地址
- // curl_easy_setopt(curl, CURL_IPRESOLVE_WHATEVER, 1L); // 獲取系統允許的 IPv4 或者 IPv6 地址
- /* Perform the request, res will get the return code */
- res = curl_easy_perform(curl);
- /* Check for errors */
- if(res != CURLE_OK)
- fprintf(stderr, "curl_easy_perform() failed: %s\n",
- curl_easy_strerror(res));
- /* always cleanup */
- curl_easy_cleanup(curl);
- }
- return 0;
- }