一归苍、背景
? ? WWDC2015蘋果宣布在iOS9支持純IPv6的網(wǎng)絡(luò)服務(wù),并且要求2016年提交到AppStore的應(yīng)用必須兼容純IPv6的網(wǎng)絡(luò)麸恍,要求適配的系統(tǒng)版本是iOS9以上(包括iOS9)灵巧,否則會有過審被拒的可能,貼別是子2016年6月1日起抹沪,國內(nèi)陸陸續(xù)續(xù)出現(xiàn)了大量App無法通過IPv6審核孩等;
二、簡單結(jié)論
由于目前大部分服務(wù)器(包括阿里云)都沒有Internet類型的IPv6地址采够,所以我們現(xiàn)在遇到的問題主要是App在IPv6-only網(wǎng)絡(luò)下去訪問IPv4-only網(wǎng)絡(luò)的服務(wù)端,如果服務(wù)器需要兼容IPv6需要做很多軟件和硬件上的全面升級冰垄。
幸好蹬癌,從一開始設(shè)計IPv6就考慮到了向后兼容的問題,運營商會提供一個中間節(jié)點虹茶,使用DNS64/NAT64等技術(shù)逝薪,負(fù)責(zé)協(xié)議的轉(zhuǎn)換和地址的轉(zhuǎn)換,打通IPv6和IPv4之間的鏈路蝴罪。這樣我們在IPv6的環(huán)境下也是可以訪問IPv4的后臺資源的董济,我們的后臺也就暫時不需要做什么變動。
對于使用域名方式訪問服務(wù)端的情況要门,App
Client端只需將網(wǎng)絡(luò)層通信接口改造成兼容IPv6即可虏肾,App Client在IPv6-Only網(wǎng)絡(luò)下會按照兩種方式進(jìn)行域名解析廓啊,如果域名配置了AAAA記錄,則直接返回配置的IPv6地址封豪,如果只有A記錄谴轮,則DNS64會合成IPv6地址后返回給App Client,大部分非分區(qū)類型手機(jī)游戲和非游戲類App適合此場景吹埠。
對于使用IP方式訪問服務(wù)端的情況第步,App
Client端既需要將網(wǎng)絡(luò)層通信接口改造成兼容IPv6,又需要實現(xiàn)IPv4和IPv6間的地址轉(zhuǎn)換(App Client從服務(wù)器拉取的地址列表一般是IPv4類型的缘琅,連接服務(wù)端時需要用IPv6類型的IP粘都,服務(wù)端返回包時不需要轉(zhuǎn)換,因為NAT64服務(wù)已經(jīng)自動做了轉(zhuǎn)換)刷袍,對于分區(qū)或分服類手機(jī)游戲適合此場景翩隧。
兼容IPv6的網(wǎng)絡(luò)接口可以直接使用蘋果官方提供的版本,逐一在App Client端代碼中做替換即可做个。
可以參考如下文檔鸽心,示例了如果在App
Client中進(jìn)行代碼修改以適應(yīng)這個問題。
http://www.atatech.org/articles/54871
因為上述鏈接文檔是從代碼和協(xié)議層面對原理進(jìn)行深入剖析居暖,可能不太適合一些部署類的用戶顽频,因此接下來將從相對概要的層面對這個問題進(jìn)行分析。
三太闺、方案分析
3.1 IPV6概述
A糯景、什么是IPV6
IPv6是Internet
Protocol Version 6的縮寫,簡單的概括IPv6就是現(xiàn)行的互聯(lián)網(wǎng)協(xié)議(IPv4)的下一代IP協(xié)議省骂。IPv6由128位二進(jìn)制數(shù)組成蟀淮,可提供龐大的IP地址資源,足以讓地球上每個生物乃至每厘米都能分配到一個或多個IP地址钞澳。將這128位的地址按每16位劃分為一個段怠惶,將每個段轉(zhuǎn)換成十六進(jìn)制數(shù)字,并用冒號隔開轧粟。
??? IPv4地址示例:192.168.1.1
??? IPv6地址示例:2001:0db8:85a3:08d3:1319:8a2e:0370:7344
B策治、為什么要接入IPv6
??? 目前互聯(lián)網(wǎng)廣泛應(yīng)用的IPv4技術(shù),理論上IPv4是一個32位的二進(jìn)制數(shù)的地址兰吟,可編址1600萬個網(wǎng)絡(luò)通惫、40億臺主機(jī)。但在采用了A混蔼、B履腋、C三類編址方式后,可用的網(wǎng)絡(luò)地址和主機(jī)地址數(shù)目大打折扣,歐美國家掌握著核心技術(shù)遵湖,且互聯(lián)網(wǎng)發(fā)展較早悔政,因此擁有約3/4的IP資源。造成我國及其他發(fā)展中國家的IP地址資源不足的困局奄侠,隨著中國互聯(lián)網(wǎng)用戶的不斷增加和電子卓箫、網(wǎng)絡(luò)技術(shù)的蓬勃發(fā)展,缺乏IP地址資源垄潮,將嚴(yán)重制約我國及其他發(fā)展中國家互聯(lián)網(wǎng)的應(yīng)用和發(fā)展烹卒。
C、IPv6發(fā)展現(xiàn)狀
??? 由于從IPv4網(wǎng)絡(luò)完全過渡到IPv6網(wǎng)絡(luò)需要全球互聯(lián)網(wǎng)基礎(chǔ)設(shè)施中的網(wǎng)絡(luò)軟件和網(wǎng)絡(luò)硬件設(shè)備以及終端設(shè)備都支持IPv6協(xié)議弯洗,這會涉及到大量的改造工作旅急,雖然得到各國政府和各大運營商的重視和推動,但是IPv4和IPv6仍將長期共存牡整。
IP
D藐吮、IPV6兼容性問題
??? 現(xiàn)在我們大部分服務(wù)器都是使用IPv4接入互聯(lián)網(wǎng)的,我們要如何做兼容呢逃贝?也就是說如何做到IPv6和IPv4的兼容和相互訪問谣辞?
??? 要想使應(yīng)用完全支持IPv6的環(huán)境,從協(xié)議到硬件沐扳,要做比較徹底的調(diào)整泥从。不但客戶端要做IPv6的改造,服務(wù)器也要適配IPv6沪摄,主要有以下四種對應(yīng)關(guān)系躯嫉,必須做好以下每一種:
??? IPv4->IPv4
??? IPv4->IPv6
??? IPv6->IPv4
??? IPv6->IPv6
要做到IPv6和IPv4完全兼容需要做很大的修改,最簡單的協(xié)議上要兼容128位的IP地址杨拐,路由器祈餐,服務(wù)器等相關(guān)硬件也要升級。應(yīng)蘋果公司的要求哄陶,我們重點關(guān)注客戶端從IPv6的網(wǎng)絡(luò)環(huán)境訪問IPv4的服務(wù)資源帆阳。
IPv6轉(zhuǎn)換機(jī)制有很多種,蘋果期望iOS
App能夠兼容DNS64/NAT64的方式屋吨。
(a) socket api支持RFC 4038—ApplicationAspects of IPv6 Transition
? v4 socket接口只能支持IPv4 stack
? v6 socket能支持IPv4 stack和IPv6 stack
(b) 服務(wù)器IP
? 返回v4 IP
? 返回v6 IP
(c) 用戶本地IP stack
? IPv4-only
? IPv6-only
? IPv4-IPv6 Dual stack
(d) 各種IPv6轉(zhuǎn)換機(jī)制
NAT64/DNS64
64:ff9b::/96用于v6的本地網(wǎng)絡(luò)通過NAT訪問v4的資源:RFC6146舱痘、RFC6147。
6to4 2002::/16用于兩個擁有v4公網(wǎng)地址的IPv6-only子網(wǎng)的互相訪問:RFC6343离赫。
Teredo tunneling
2001::/32 通過隧道的方式讓兩個IPv6-only子網(wǎng)互相訪問,沒有NAT問題:RFC4380塌碌。
464XLAT用于程序只有v4地址(使用v4
socket)渊胸,但是本地網(wǎng)絡(luò)是IPv6網(wǎng)絡(luò),程序需要訪問v4資源台妆,類似NAT64翎猛,不過區(qū)別在于服務(wù)器是運營商提供胖翰,手機(jī)上需要安裝CLAT服務(wù):RFC6877。
還有很多兼容方案切厘,復(fù)雜程度都很高萨咳。
??? 幸好,從一開始設(shè)計IPv6就考慮到了向后兼容的問題疫稿,運營商會提供一個中間節(jié)點培他,使用DNS64/NAT64等技術(shù),負(fù)責(zé)協(xié)議的轉(zhuǎn)換遗座,打通IPv6和IPv4之間的鏈路舀凛。(IPv6和IPv4互通技術(shù)有很多,這里只討論Apple要求的技術(shù)方案DNS64/NAT64)途蒋。
3.2不同IP stack組合的處理方式
A猛遍、v4 ip + IPv4-only or IPv4-IPv6 Dual stack
??? 在這樣的情況下,我們雖然用的是v6的socket号坡,但是必須要讓socket走的是v4的協(xié)議懊烤。
??? ::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.)
從上文可以看到如果服務(wù)器地址為128.0.0.128,我們轉(zhuǎn)換成IPv4-mapped
IPv6 address::ffff:128.0.0.128或者純16進(jìn)制::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網(wǎng)絡(luò)寄啼。
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是一種有狀態(tài)的網(wǎng)絡(luò)地址與協(xié)議轉(zhuǎn)換技術(shù)代箭,一般只支持通過IPv6網(wǎng)絡(luò)側(cè)用戶發(fā)起連接訪問IPv4側(cè)網(wǎng)絡(luò)資源墩划。但NAT64也支持通過手工配置靜態(tài)映射關(guān)系,實現(xiàn)IPv4網(wǎng)絡(luò)主動發(fā)起連接訪問IPv6網(wǎng)絡(luò)嗡综。NAT64可實現(xiàn)TCP乙帮、UDP、ICMP協(xié)議下的IPv6與IPv4網(wǎng)絡(luò)地址和協(xié)議轉(zhuǎn)換极景。
DNS64則主要是配合NAT64工作察净,主要是將DNS查詢信息中的A記錄(IPv4地址)合成到AAAA記錄(IPv6地址)中,返回合成的AAAA記錄給IPv6側(cè)用戶盼樟。NAT64一般與DNS64協(xié)同工作氢卡,而不需要在IPv6客戶端或IPv4服務(wù)器端做任何修改。
這里大概描述一下NAT64的工作流程晨缴,首先局域網(wǎng)內(nèi)有一個NAT64的路由設(shè)備并且有DNS64的服務(wù)译秦。
a、客戶端進(jìn)行g(shù)etaddrinfo的域名解析
b、DNS返回結(jié)果筑悴,如果返回的IP里面只有v4地址们拙,并且當(dāng)前網(wǎng)絡(luò)是IPv6-only網(wǎng)路,DNS64服務(wù)器會把v4地址加上64:ff9b::/96的前綴阁吝,例如64:ff9b::14.17.32.211砚婆。如果當(dāng)前網(wǎng)絡(luò)是IPv4-only或IPv4-IPv6,DNS64不會做任何事情突勇。
c装盯、客戶端拿到IPv6地址進(jìn)行connect兵琳。
d窍箍、路由器發(fā)現(xiàn)地址的前綴為64:ff9b::/96琳骡,知道這個是NAT64的映射芬探,是需要訪問14.17.32.211彤侍。這個時候需要進(jìn)行NAT64映射俄认,因為到外網(wǎng)需要轉(zhuǎn)換成IPv4 stack兜看。
e钥组、當(dāng)數(shù)據(jù)返回的時候共屈,按照NAT映射绑谣,IPv4回包重新加上前綴64:ff9b::/96,然后返回給客戶端拗引。
Apple的文檔里面也有很詳細(xì)的描述:
//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主機(jī)發(fā)起www.abc.com的AAAA域名解析到DNS64(主機(jī)配置的DNS地址是DNS64);
2哼凯、DNS64觸發(fā)AAAA到DNS AAAA中查詢欲间;
3、DNS AAAA返回NULL的信息到DNS64断部;
4、 DNS64然后觸發(fā)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主機(jī);
7儡遮、IPv6主機(jī)發(fā)起目的地址為64:ff9b:1.1.1.1的IPv6數(shù)據(jù)包,由于NAT64在IPv6域內(nèi)通告配置的IPv6Prefix,因此這個數(shù)據(jù)包轉(zhuǎn)發(fā)到NAT64設(shè)備上蹂随;
8十嘿、NAT64執(zhí)行地址轉(zhuǎn)換和協(xié)議轉(zhuǎn)換,目的地址轉(zhuǎn)換為192.0.2.1岳锁,源地址根據(jù)地址狀態(tài)轉(zhuǎn)換(64:ff9b:1.1.1.1绩衷,1500)>(1.1.1.1,2000)在IPv4域內(nèi)路由到IPv4 Server激率;
9咳燕、 數(shù)據(jù)包返回,目的地址和端口為1.1.1.1,2000乒躺;
10招盲、 NAT64根據(jù)已有記錄進(jìn)行轉(zhuǎn)換,目的地址轉(zhuǎn)換為2001:db8::1嘉冒,源地址為加了IPv6前綴的IPv4Server地址64:ff9b:1.1.1.1曹货,發(fā)送到IPv6主機(jī);
這里討論比較坑的地方讳推,按照NAT64的規(guī)則顶籽,客戶端如果沒有做DNS域名解析的話,客戶端就需要完成DNS64的工作娜遵。這里的關(guān)鍵點是蜕衡,發(fā)現(xiàn)網(wǎng)絡(luò)是IPv6-only的NAT64網(wǎng)絡(luò)的情況下,我們可以自己補充上前綴64:ff9b::/96设拟,然后進(jìn)行正常的訪問慨仿。
注:AAAA記錄(AAAA
Record)是用來將域名解析到IPv6地址的DNS記錄,用戶可以將一個域名解析到IPv6地址上纳胧,也可以將子域名解析到IPv6地址上镰吆。
C、v6 ip + IPv4-only
這里一般connect的時候會返回錯誤碼network
is unreachable跑慕,因為根本沒有v6的協(xié)議棧万皿,就像沒有硬件設(shè)備一樣摧找,但是不排除會有系統(tǒng)會返回no route to host。當(dāng)然牢硅,如果服務(wù)器的地址是Teredo tunneling 2001::/32蹬耘,可以客戶端直接做隧道。如果是6to4
2002::/16减余,并且客戶端有RAW socket權(quán)限加上非NAT網(wǎng)絡(luò)综苔,這種情況下可以客戶端自己做6to4的路由。
D位岔、v6 ip + IPv6-only or IPv4-IPv6
這里只要沒有配置上如筛,是可以直接通訊的。當(dāng)然這里會涉及到一個問題抒抬,如果DNS返回上文說的6to4或Teredo tunneling或pure native IPv6 address杨刨,這樣的情況下我們怎么做IP的選擇呢,可以參照RFC
3484 – Default Address Selection for Internet Protocol version 6(IPv6)擦剑。
[if !supportLists]三妖胀、??[endif]對開發(fā)同學(xué)的建議
4.1 不建議使用底層的網(wǎng)絡(luò)API
下圖展示的藍(lán)色部分的這些API都是不存在兼容性問題的,而我們平時自己用的包括那些第三方的網(wǎng)絡(luò)庫大部分都是用的這些API:
大部分情況下抓于,我們用高級的API完全能夠?qū)崿F(xiàn)我們的需求做粤,而且高級API封裝得很便于使用,很多底層的像適配IPv6的工作都已經(jīng)完成捉撮,而用底層API會有大量的工作要我們自己來做怕品,更容易產(chǎn)生BUG,如果確實需要用底層的POSIX Socket API巾遭,請參照RFC 4038:Application Aspects of IPv6 Transition的指導(dǎo)肉康。
4.2 不要用IP地址
SCNetworkReachabilityCreateWithName
比如這個API的nodename參數(shù)不要傳IP地址,而應(yīng)該是域名灼舍;
4.3使用合適的數(shù)據(jù)容器
[if !vml]
[endif]
代碼中以上對應(yīng)類型都要處理吼和;
4.4檢查不兼容IPv6 DNS64/NAT64的代碼
?????????[if !vml]
[endif]
?????????這些API都是只針對IPv4做處理的,換用兼容IPv4及IPv6的API骑素。
?????????判斷當(dāng)前客戶端是處于IPv4-only炫乓、IPv6-only還是IPv4和IPv6并存的環(huán)境,然后分別使用不同的網(wǎng)絡(luò)API献丑。
4.5使用系統(tǒng)API去合成IPv6地址
4.6使用socket及connect進(jìn)行的聯(lián)網(wǎng)操作換用Apple提供的API
換用CoreFoundationframework及以上Apple提供的API末捣,這樣,即使我們填的是IPv4的地址创橄,系統(tǒng)會在不同的網(wǎng)絡(luò)環(huán)境自動幫我們進(jìn)行地址的轉(zhuǎn)換箩做,我們不需要額外的工作(例如CFStreamCreatePairWithSocketToHost、NSURLSession)妥畏。
四邦邦、 協(xié)議棧判別方法
5.1 判斷客戶端可用的IP stack
??? 客戶端做不同的處理的前提是需要知道客戶端可用的IP協(xié)議棧安吁。可用的IP stack類型分別是IPv4-only燃辖、IPv6-only鬼店、IPv4-IPv6 Dual stack。
我們先定義客戶端可用的IP協(xié)議棧的含義:獲取客戶端當(dāng)前能使用的IP協(xié)議棧黔龟。例如iOS在NAT64 Wi-Fi連接上的情況下薪韩,Mobile的網(wǎng)雖然存在IPv4的協(xié)議,但是系統(tǒng)是不允許使用的捌锭。iOS只能使用Wi-Fi的協(xié)議棧,在NAT64 Wi-Fi的情況下就是IPv6-only網(wǎng)絡(luò)了罗捎。如果遇到IPv6-only網(wǎng)絡(luò)观谦,需要把它當(dāng)作NAT64來處理,在v4 IP前添加前綴64:ff9b::/64桨菜。但是NAT64和IPv6-only不是等價的豁状。IPv6-only網(wǎng)絡(luò)可能支持NAT64,能訪問v4的互聯(lián)網(wǎng)資源倒得,但是IPv6-only能訪問v6的互聯(lián)網(wǎng)資源泻红,不支持NAT64。這里假設(shè)IPv6-only的網(wǎng)絡(luò)都是支持NAT64的霞掺,對v4 IP進(jìn)行64:ff9b::/96的處理谊路。
5.2 DNS方案
??? 這里的方案是直接做DNS解析,然后判斷返回的IP有沒有帶上64:ff9b前綴來確定當(dāng)前的IP協(xié)議棧菩彬。這也是唯一能夠判斷IPv6-only網(wǎng)絡(luò)是否支持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惨恭;
六參考文獻(xiàn)
(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(群文件共享部分資料)