雪花新聞

一份完整的 IPv6 環境下 DNS 相關測試

摘要:假定用戶只有 IPv6 地址,DNS 也是使用 IPv6 地址 (DNS 必須有雙棧環境,因爲現在很多權威服務器沒有 IPv6 地址,純 IPv6 環境下無法正常工作),假定一個服務通過域名(同時擁有 A、AAAA 記錄)對外提供服務,測試服務是否可以正常訪問。// 獲取系統允許的 IPv4 或者 IPv6 地址       /* Perform the request, res will get the return code */       res = curl_easy_perform(curl)。

董濤,網易遊戲高級運維工程師,主要工作方向爲網易集團 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 全球部署更新

操作系統 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 記錄是一個域名指向 IPv4 地址的解析結果,即最常見的記錄類型, 例如 ipv6test.ntes53.netease.com. 1800 IN A 123.58.166.70
AAAA 是一個域名指向 IPv6 地址的解析結果。如果想要一個域名解析到 IPv6 地址,則需要設置此種類型的解析結果。同一個域名可以同時有 A 與 AAAA 兩種記錄類型, 例如 ipv6test.ntes53.netease.com. 1800 IN AAAA 2403:c80:100:3000::7b3a:a646
用戶直接使用的 DNS 服務器,各種平臺、操作系統上直接設置的 DNS 服務器,常見的有 8.8.8.8, 114.114.114.114
用於域名的管理。權威 DNS 服務器只對自己所擁有的域名進行域名解析,對於不是自己的域名則拒絕應答。例如網易的權威 DNS 服務器只會響應網易域名的請求,對於其他域名,則拒絕應答。
雙棧網絡環境即客戶端或服務器同時擁有 IPv4、IPv6 兩種網絡環境,可以簡單的理解爲機器上既有 IPv4 地址又有 IPv6 地址

測試場景

下文中所有測試使用的程序均爲測試方法中的程序

1.目前純 IPv4 環境下,僅新增 AAAA(IPv6) 記錄之後,對已有程序的影響

假定已經存在了一個程序(C 程序、python 程序、瀏覽器等),通過域名訪問某個服務,現在在 IPv4 環境下一切工作正常。當給這個域名增加了 AAAA 記錄之後,測試對目前的程序的影響。

域名解析

HTTP 請求

客戶端

結論

2.客戶端 IPv6/v4 雙棧環境下,測試程序的行爲

假定用戶的環境是雙棧環境,假定一個服務通過域名對外提供服務,測試這種情況下程序的行爲。

域名解析

HTTP 請求

客戶端

結論

3. 客戶端純 IPv6 環境下,測試能否正常工作

假定用戶只有 IPv6 地址,DNS 也是使用 IPv6 地址 (DNS 必須有雙棧環境,因爲現在很多權威服務器沒有 IPv6 地址,純 IPv6 環境下無法正常工作),假定一個服務通過域名(同時擁有 A、AAAA 記錄)對外提供服務,測試服務是否可以正常訪問。

域名解析

HTTP 請求

客戶端

結論

當某域名即存在 A 記錄 又存在 AAAA 記錄時:

4. DNS 解析測試

這裏測試了緩存服務器和權威服務器在各種網絡環境下,優先使用的解析鏈路。

結論

當權威服務器和緩存服務器均支持 ipv6 時,緩存服務器優先使用 ipv6 鏈路進行解析,其他情況均使用 ipv4 鏈路進行解析。

結論

參考資料

測試方法

解析域名

C/ C ++

Linux

#include <stdio.h> 
    #include <netdb.h> 
    #include <arpa/inet.h> 
 
    int main(void) 
{ 
        int i = 0; 
        char str[32] = {0}; 
         struct hostent* phost = NULL; 
 
        phost = gethostbyname("IPv6test.ntes53.netease.com"); 
        printf("%s", inet_ntoa(*((struct in_addr*)phost->h_addr)));  
         
        return 0; 
    } 

Windows

 #include <winsock.h> 
    #include <Windows.h> 
    #include <stdio.h> 
 
    #pragma comment (lib, "ws2_32.lib") 
 
    int main(void) { 
        WSADATA wsaData = {0,}; 
        struct in_addr addr = {0,}; 
        struct hostent *res; 
        int i = 0; 
 
        WSAStartup(MAKEWORD(2, 2), &wsaData); 
 
        res = gethostbyname("IPv6test.ntes53.netease.com."); 
        while (res->h_addr_list[i] != 0) { 
            addr.s_addr = *(u_long *) res->h_addr_list[i++]; 
            printf("IP Address: %s\n", inet_ntoa(addr)); 
        } 
 
        WSACleanup(); 
} 

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

#define WIN32_LEAN_AND_MEAN 
    #define _WIN32_WINNT 0x501 
    #include <windows.h> 
    #include <winsock2.h> 
    #include <stdio.h> 
    #include <string.h> 
    #include <stdlib.h> 
    #include <sys/types.h> 
    #include <ws2tcpip.h> 
 
    #pragma comment (lib, "Ws2_32.lib") 
 
    // int iResult; 
    WSADATA wsaData; 
    int iResult = WSAStartup(MAKEWORD(2,2), &wsaData); 
 
    int inet_pton(int af, const char *src, void *dst) 
{ 
        struct sockaddr_storage ss; 
        int size = sizeof(ss); 
        char src_copy[INET6_ADDRSTRLEN+1]; 
 
        ZeroMemory(&ss, sizeof(ss)); 
        /* stupid non-const API */ 
        strncpy (src_copy, src, INET6_ADDRSTRLEN+1) 
        src_copy[INET6_ADDRSTRLEN] = 0; 
 
        if (WSAStringToAddress(src_copy, af, NULL, (struct sockaddr *)&ss, &size) == 0) { 
            switch(af) { 
                case AF_INET: 
                    *(struct in_addr *)dst = ((struct sockaddr_in *)&ss)->sin_addr; 
                    return 1; 
                case AF_INET6: 
                    *(struct in6_addr *)dst = ((struct sockaddr_in6 *)&ss)->sin6_addr; 
                    return 1; 
            } 
        } 
        return 0; 
    } 
    const char *inet_ntop(int af, const void *src, char *dst, socklen_t size) 
{ 
        struct sockaddr_storage ss; 
        unsigned long s = size; 
        ZeroMemory(&ss, sizeof(ss)); 
        ss.ss_family = af; 
        switch(af) { 
            case AF_INET: 
                ((struct sockaddr_in *)&ss)->sin_addr = *(struct in_addr *)src; 
                break; 
            case AF_INET6: 
                ((struct sockaddr_in6 *)&ss)->sin6_addr = *(struct in6_addr *)src; 
                break; 
            default: 
                return NULL; 
        } 
        /* cannot direclty use &size because of strict aliasing rules */ 
        return (WSAAddressToString((struct sockaddr *)&ss, sizeof(ss), NULL, dst, &s) == 0)? 
               dst : NULL; 
    } 
    int lookup_host () 
{ 
        struct addrinfo hints, *res; 
        int errcode; 
        char addrstr[100]; 
        void *ptr; 
        memset (&hints, 0, sizeof (hints)); 
        hints.ai_family = AF_INET6; 
        errcode = getaddrinfo ("IPv6test.ntes53.netease.com", NULL, &hints, &res); 
        if (errcode != 0) 
        { 
            perror ("getaddrinfo"); 
            printf("%d",errcode); 
            return -1; 
        } 
        while (res) 
        { 
            // inet_ntop (res->ai_family, res->ai_addr->sa_data, addrstr, 100); 
            sockaddr_in in1; 
            memcpy(&in1.sin_addr, res->ai_addr->sa_data, sizeof(res)); 
            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); 
            // sockaddr_in6 in; 
            // memcpy(∈.sin6_addr, ptr, sizeof(ptr)); 
            printf ("IPv%d address: %s (%s)\n", res->ai_family == PF_INET6 ? 6 : 4, 
                   addrstr, res->ai_canonname); 
            //printf ("IPv%d address: %s (%s)\n", res->ai_family == PF_INET6 ? 6 : 4, 
            //        inet_ntoa(in.sin6_addr), res->ai_canonname); 
            res = res->ai_next; 
        } 
        return 0; 
    } 
    int main (void) 
{ 
        printf("start\n"); 
        lookup_host(); 
} 
    } 

Python

import socket 
result = socket.gethostbyname("IPv6test.ntes53.netease.com") 
print result 
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 包

import requests 
response = requests.get("http://IPv6test.ntes53.netease.com:8000", stream=True) 
print response.raw._fp.fp._sock.getpeername() 

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; 
} 
相關文章