IPV6兼容解決方案

一、背景


蘋果IPv6審核政策

? ? WWDC2015蘋果宣布在iOS9支持純IPv6的網絡服務,并且要求2016年提交到AppStore的應用必須兼容純IPv6的網絡,要求適配的系統版本是iOS9以上(包括iOS9),否則會有過審被拒的可能,貼別是子2016年6月1日起,國內陸陸續續出現了大量App無法通過IPv6審核;

二、簡單結論

由于目前大部分服務器(包括阿里云)都沒有Internet類型的IPv6地址,所以我們現在遇到的問題主要是App在IPv6-only網絡下去訪問IPv4-only網絡的服務端,如果服務器需要兼容IPv6需要做很多軟件和硬件上的全面升級。

幸好,從一開始設計IPv6就考慮到了向后兼容的問題,運營商會提供一個中間節點,使用DNS64/NAT64等技術,負責協議的轉換和地址的轉換,打通IPv6和IPv4之間的鏈路。這樣我們在IPv6的環境下也是可以訪問IPv4的后臺資源的,我們的后臺也就暫時不需要做什么變動。

對于使用域名方式訪問服務端的情況,App

Client端只需將網絡層通信接口改造成兼容IPv6即可,App Client在IPv6-Only網絡下會按照兩種方式進行域名解析,如果域名配置了AAAA記錄,則直接返回配置的IPv6地址,如果只有A記錄,則DNS64會合成IPv6地址后返回給App Client,大部分非分區類型手機游戲和非游戲類App適合此場景。

對于使用IP方式訪問服務端的情況,App

Client端既需要將網絡層通信接口改造成兼容IPv6,又需要實現IPv4和IPv6間的地址轉換(App Client從服務器拉取的地址列表一般是IPv4類型的,連接服務端時需要用IPv6類型的IP,服務端返回包時不需要轉換,因為NAT64服務已經自動做了轉換),對于分區或分服類手機游戲適合此場景。

兼容IPv6的網絡接口可以直接使用蘋果官方提供的版本,逐一在App Client端代碼中做替換即可。

可以參考如下文檔,示例了如果在App

Client中進行代碼修改以適應這個問題。

http://www.atatech.org/articles/54871

因為上述鏈接文檔是從代碼和協議層面對原理進行深入剖析,可能不太適合一些部署類的用戶,因此接下來將從相對概要的層面對這個問題進行分析。

三、方案分析

3.1 IPV6概述

A、什么是IPV6

IPv6是Internet

Protocol Version 6的縮寫,簡單的概括IPv6就是現行的互聯網協議(IPv4)的下一代IP協議。IPv6由128位二進制數組成,可提供龐大的IP地址資源,足以讓地球上每個生物乃至每厘米都能分配到一個或多個IP地址。將這128位的地址按每16位劃分為一個段,將每個段轉換成十六進制數字,并用冒號隔開。

??? IPv4地址示例:192.168.1.1

??? IPv6地址示例:2001:0db8:85a3:08d3:1319:8a2e:0370:7344


B、為什么要接入IPv6

??? 目前互聯網廣泛應用的IPv4技術,理論上IPv4是一個32位的二進制數的地址,可編址1600萬個網絡、40億臺主機。但在采用了A、B、C三類編址方式后,可用的網絡地址和主機地址數目大打折扣,歐美國家掌握著核心技術,且互聯網發展較早,因此擁有約3/4的IP資源。造成我國及其他發展中國家的IP地址資源不足的困局,隨著中國互聯網用戶的不斷增加和電子、網絡技術的蓬勃發展,缺乏IP地址資源,將嚴重制約我國及其他發展中國家互聯網的應用和發展。


C、IPv6發展現狀

??? 由于從IPv4網絡完全過渡到IPv6網絡需要全球互聯網基礎設施中的網絡軟件和網絡硬件設備以及終端設備都支持IPv6協議,這會涉及到大量的改造工作,雖然得到各國政府和各大運營商的重視和推動,但是IPv4和IPv6仍將長期共存。

IP

D、IPV6兼容性問題

??? 現在我們大部分服務器都是使用IPv4接入互聯網的,我們要如何做兼容呢?也就是說如何做到IPv6和IPv4的兼容和相互訪問?

??? 要想使應用完全支持IPv6的環境,從協議到硬件,要做比較徹底的調整。不但客戶端要做IPv6的改造,服務器也要適配IPv6,主要有以下四種對應關系,必須做好以下每一種:

??? IPv4->IPv4

??? IPv4->IPv6

??? IPv6->IPv4

??? IPv6->IPv6

要做到IPv6和IPv4完全兼容需要做很大的修改,最簡單的協議上要兼容128位的IP地址,路由器,服務器等相關硬件也要升級。應蘋果公司的要求,我們重點關注客戶端從IPv6的網絡環境訪問IPv4的服務資源。

IPv6轉換機制有很多種,蘋果期望iOS

App能夠兼容DNS64/NAT64的方式。

(a) socket api支持RFC 4038—ApplicationAspects of IPv6 Transition

? v4 socket接口只能支持IPv4 stack

? v6 socket能支持IPv4 stack和IPv6 stack

(b) 服務器IP

? 返回v4 IP

? 返回v6 IP

(c) 用戶本地IP stack

? IPv4-only

? IPv6-only

? IPv4-IPv6 Dual stack

(d) 各種IPv6轉換機制

NAT64/DNS64

64:ff9b::/96用于v6的本地網絡通過NAT訪問v4的資源:RFC6146、RFC6147。

6to4 2002::/16用于兩個擁有v4公網地址的IPv6-only子網的互相訪問:RFC6343。

Teredo tunneling

2001::/32 通過隧道的方式讓兩個IPv6-only子網互相訪問,沒有NAT問題:RFC4380。

464XLAT用于程序只有v4地址(使用v4

socket),但是本地網絡是IPv6網絡,程序需要訪問v4資源,類似NAT64,不過區別在于服務器是運營商提供,手機上需要安裝CLAT服務:RFC6877。

還有很多兼容方案,復雜程度都很高。

??? 幸好,從一開始設計IPv6就考慮到了向后兼容的問題,運營商會提供一個中間節點,使用DNS64/NAT64等技術,負責協議的轉換,打通IPv6和IPv4之間的鏈路。(IPv6和IPv4互通技術有很多,這里只討論Apple要求的技術方案DNS64/NAT64)。


NAT64轉換原理

3.2不同IP stack組合的處理方式

A、v4 ip + IPv4-only or IPv4-IPv6 Dual stack

??? 在這樣的情況下,我們雖然用的是v6的socket,但是必須要讓socket走的是v4的協議。

??? ::ffff:0:0/96 — This prefix is designatedas anIPv4-mapped IPv6 address. With a few exceptions, this addresstype allows the transparent use of theTransport

Layer?protocols

over IPv4 through the IPv6 networking?application

programming interface. Server applications only need to open a single

listening?socket?to handle

connections from clients using IPv6 or IPv4 protocols. IPv6 clients will be

handled natively by default, and IPv4 clients appear as IPv6 clients at their

IPv4-mapped IPv6 address. Transmission is handled similarly; established

sockets may be used to transmit IPv4 or IPv6 datagram, based on the binding to

an IPv6 address, or an IPv4-mapped address. (See also?Transition

mechanisms.)

從上文可以看到如果服務器地址為128.0.0.128,我們轉換成IPv4-mapped

IPv6 address::ffff:128.0.0.128或者純16進制::ffff:ff00:00ff,然后賦值給sockaddr_in6.sin6_addr=”::ffff:128.0.0.128”;。這個socket雖然用了IPv6的sockaddr_in6,但實際上走的是IPv4

stack。

IPv4-mapped IPv6

address是讓用戶能夠使用一致的socket api來訪問IPv4和IPv6網絡。

B、v4 ip + IPv6-only

這里我們先看看Wikipedia對NAT64/DNS64的描述:

??? NAT64is a mechanism to allow IPv6 hosts to communicatewith IPv4 servers. The NAT64 server is the endpoint for at least one IPv4address and an IPv6 network segment of 32-bits, e.g.,64:ff9b::/96(RFC 6052,?RFC

6146). The

IPv6 client embeds the IPv4 address with which it wishes to communicate using

these bits, and sends its packets to the resulting address. The NAT64 server

then creates a?NAT-mapping between the IPv6

and the IPv4 address, allowing them to communicate.

??? DNS64?describes a?DNS server?that

when asked for a domain's?AAAA records,

but only finds?A records,

synthesizes the AAAA records from the A records. The first part of the

synthesized IPv6 address points to an IPv6/IPv4 translator and the second part

embeds the IPv4 address from the A record. The translator in question is

usually a NAT64 server. The standard-track specification of DNS64 is in?RFC 6147.[10]

??? There are two noticeable issues with thistransition mechanism:

[if !supportLists]l?? [endif]It only works for cases where DNS is usedto find the remote host address, if IPv4 literals are used the DNS64 serverwill never be involved.

[if !supportLists]l?? [endif]Because the DNS64 server needs to return

records not specified by the domain owner,?DNSSEC?validation against the?rootwill fail in cases where the DNS server doing thetranslation is not the domain owner's server.

NAT64是一種有狀態的網絡地址與協議轉換技術,一般只支持通過IPv6網絡側用戶發起連接訪問IPv4側網絡資源。但NAT64也支持通過手工配置靜態映射關系,實現IPv4網絡主動發起連接訪問IPv6網絡。NAT64可實現TCP、UDP、ICMP協議下的IPv6與IPv4網絡地址和協議轉換。

DNS64則主要是配合NAT64工作,主要是將DNS查詢信息中的A記錄(IPv4地址)合成到AAAA記錄(IPv6地址)中,返回合成的AAAA記錄給IPv6側用戶。NAT64一般與DNS64協同工作,而不需要在IPv6客戶端或IPv4服務器端做任何修改。

這里大概描述一下NAT64的工作流程,首先局域網內有一個NAT64的路由設備并且有DNS64的服務。

a、客戶端進行getaddrinfo的域名解析

b、DNS返回結果,如果返回的IP里面只有v4地址,并且當前網絡是IPv6-only網路,DNS64服務器會把v4地址加上64:ff9b::/96的前綴,例如64:ff9b::14.17.32.211。如果當前網絡是IPv4-only或IPv4-IPv6,DNS64不會做任何事情。

c、客戶端拿到IPv6地址進行connect。

d、路由器發現地址的前綴為64:ff9b::/96,知道這個是NAT64的映射,是需要訪問14.17.32.211。這個時候需要進行NAT64映射,因為到外網需要轉換成IPv4 stack。

e、當數據返回的時候,按照NAT映射,IPv4回包重新加上前綴64:ff9b::/96,然后返回給客戶端。

Apple的文檔里面也有很詳細的描述:


Apple IPv6描述01


Apple IPv6描述02

//NAT64 address sample

//address init

const char* ipv6_str = “64:ff9b::14.17.32.211”;

in6_addr ipv6_addr = {0};

int v6_r = inet_pton(AF_INET6, ipv6_str, &ipv6_addr);

sockaddr_in6 v6_addr = {0};

v6_addr.sin6_family = AF_INET6;

v6_addr.sin6_port = htons(80);

v6_addr.sin6_addr = ipv6_addr;


//socket connect

int v6_sock = socket(AF_INET6,SOCK_STREAM,IPPROTO_TCP);

std::string v6_error;

if (0 != connect(v6_sock, (sockaddr*)&v6_addr, 28))

{

????? v6_error =strerror(errno);

}


//get local ip

sockaddr_in6 v6_local_addr = {0};

socklen_t v6_local_addr_len = 28;

char v6_str_local_addr[64] = {0};

getpeername(v6_sock, (sockaddr*)&v6_local_addr,&v6_local_addr_len);

inet_ntop(v6_local_addr.sin_family, &v6_local_addr.sin6_addr,v6_str_local_addr, 64);

close(v6_sock);


舉個例子:

1、IPv6主機發起www.abc.com的AAAA域名解析到DNS64(主機配置的DNS地址是DNS64);

2、DNS64觸發AAAA到DNS AAAA中查詢;

3、DNS AAAA返回NULL的信息到DNS64;

4、 DNS64然后觸發A的申請到DNS A中查詢;

5、DNS A返回www.abc.com的A記錄(1.1.1.1);

6、 DNS64合成IPv6地址(64:ff9b:1.1.1.1),返回AAAA response給IPv6主機;

7、IPv6主機發起目的地址為64:ff9b:1.1.1.1的IPv6數據包,由于NAT64在IPv6域內通告配置的IPv6Prefix,因此這個數據包轉發到NAT64設備上;

8、NAT64執行地址轉換和協議轉換,目的地址轉換為192.0.2.1,源地址根據地址狀態轉換(64:ff9b:1.1.1.1,1500)>(1.1.1.1,2000)在IPv4域內路由到IPv4 Server;

9、 數據包返回,目的地址和端口為1.1.1.1,2000;

10、 NAT64根據已有記錄進行轉換,目的地址轉換為2001:db8::1,源地址為加了IPv6前綴的IPv4Server地址64:ff9b:1.1.1.1,發送到IPv6主機;

這里討論比較坑的地方,按照NAT64的規則,客戶端如果沒有做DNS域名解析的話,客戶端就需要完成DNS64的工作。這里的關鍵點是,發現網絡是IPv6-only的NAT64網絡的情況下,我們可以自己補充上前綴64:ff9b::/96,然后進行正常的訪問。

注:AAAA記錄(AAAA

Record)是用來將域名解析到IPv6地址的DNS記錄,用戶可以將一個域名解析到IPv6地址上,也可以將子域名解析到IPv6地址上。

C、v6 ip + IPv4-only

這里一般connect的時候會返回錯誤碼network

is unreachable,因為根本沒有v6的協議棧,就像沒有硬件設備一樣,但是不排除會有系統會返回no route to host。當然,如果服務器的地址是Teredo tunneling 2001::/32,可以客戶端直接做隧道。如果是6to4

2002::/16,并且客戶端有RAW socket權限加上非NAT網絡,這種情況下可以客戶端自己做6to4的路由。


D、v6 ip + IPv6-only or IPv4-IPv6


這里只要沒有配置上,是可以直接通訊的。當然這里會涉及到一個問題,如果DNS返回上文說的6to4或Teredo tunneling或pure native IPv6 address,這樣的情況下我們怎么做IP的選擇呢,可以參照RFC

3484 – Default Address Selection for Internet Protocol version 6(IPv6)。

[if !supportLists]三、??[endif]對開發同學的建議

4.1 不建議使用底層的網絡API

下圖展示的藍色部分的這些API都是不存在兼容性問題的,而我們平時自己用的包括那些第三方的網絡庫大部分都是用的這些API:


大部分情況下,我們用高級的API完全能夠實現我們的需求,而且高級API封裝得很便于使用,很多底層的像適配IPv6的工作都已經完成,而用底層API會有大量的工作要我們自己來做,更容易產生BUG,如果確實需要用底層的POSIX Socket API,請參照RFC 4038:Application Aspects of IPv6 Transition的指導。

4.2 不要用IP地址

SCNetworkReachabilityCreateWithName

比如這個API的nodename參數不要傳IP地址,而應該是域名;

4.3使用合適的數據容器

[if !vml]

[endif]

代碼中以上對應類型都要處理;

4.4檢查不兼容IPv6 DNS64/NAT64的代碼

?????????[if !vml]

[endif]

?????????這些API都是只針對IPv4做處理的,換用兼容IPv4及IPv6的API。

?????????判斷當前客戶端是處于IPv4-only、IPv6-only還是IPv4和IPv6并存的環境,然后分別使用不同的網絡API。

4.5使用系統API去合成IPv6地址

4.6使用socket及connect進行的聯網操作換用Apple提供的API

換用CoreFoundationframework及以上Apple提供的API,這樣,即使我們填的是IPv4的地址,系統會在不同的網絡環境自動幫我們進行地址的轉換,我們不需要額外的工作(例如CFStreamCreatePairWithSocketToHost、NSURLSession)。


四、 協議棧判別方法

5.1 判斷客戶端可用的IP stack

??? 客戶端做不同的處理的前提是需要知道客戶端可用的IP協議棧。可用的IP stack類型分別是IPv4-only、IPv6-only、IPv4-IPv6 Dual stack。

我們先定義客戶端可用的IP協議棧的含義:獲取客戶端當前能使用的IP協議棧。例如iOS在NAT64 Wi-Fi連接上的情況下,Mobile的網雖然存在IPv4的協議,但是系統是不允許使用的。iOS只能使用Wi-Fi的協議棧,在NAT64 Wi-Fi的情況下就是IPv6-only網絡了。如果遇到IPv6-only網絡,需要把它當作NAT64來處理,在v4 IP前添加前綴64:ff9b::/64。但是NAT64和IPv6-only不是等價的。IPv6-only網絡可能支持NAT64,能訪問v4的互聯網資源,但是IPv6-only能訪問v6的互聯網資源,不支持NAT64。這里假設IPv6-only的網絡都是支持NAT64的,對v4 IP進行64:ff9b::/96的處理。


5.2 DNS方案

??? 這里的方案是直接做DNS解析,然后判斷返回的IP有沒有帶上64:ff9b前綴來確定當前的IP協議棧。這也是唯一能夠判斷IPv6-only網絡是否支持NAT64的方案。


//gateway

in6_addr addr6_gateway = {0};

if (0 !=

getdefaultgateway6(&addr6_gateway))

?????? returnEIPv4;

if (IN6_IS_ADDR_UNSPECIFIED(&addr6_gateway))

?????? returnEIPv4;

in_addr addr_gateway = {0};

if (0 !=

getdefaultgateway(&addr_gateway))

return EIPv6;

if (INADDR_NONE ==

addr_gateway.s_addr || INADDR_ANY == addr_gateway.s_addr)

?????? returnEIPv6;

//getaddrinfo

struct addrinfo hints, *res, *res0;

memset(*hints, 0, sizeof(hints));

hints.ai_family

= PF_INET6;

六參考文獻

(1)wikipedia Transition from IPv4?:leftwards_arrow_with_hook:

(2) wikipedia NAT64?:leftwards_arrow_with_hook:

(3) wikipedia DNS64?:leftwards_arrow_with_hook:

(4)wikipedia IPv6 address?:leftwards_arrow_with_hook:

(5)https://developer.apple.com/library/prerelease/content/documentation/NetworkingInternetWeb/Conceptual/NetworkingOverview/UnderstandingandPreparingfortheIPv6Transition/UnderstandingandPreparingfortheIPv6Transition.html


IPv6審核互助群:112019540(群文件共享部分資料)

?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 230,501評論 6 544
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 99,673評論 3 429
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 178,610評論 0 383
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 63,939評論 1 318
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 72,668評論 6 412
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 56,004評論 1 329
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 44,001評論 3 449
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 43,173評論 0 290
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 49,705評論 1 336
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 41,426評論 3 359
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 43,656評論 1 374
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 39,139評論 5 364
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,833評論 3 350
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 35,247評論 0 28
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 36,580評論 1 295
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 52,371評論 3 400
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 48,621評論 2 380

推薦閱讀更多精彩內容