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 6052RFC

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阅读 161,780评论 4 369
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 68,424评论 1 305
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 111,397评论 0 254
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 44,576评论 0 218
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,997评论 3 295
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,945评论 1 224
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 32,107评论 2 317
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,850评论 0 208
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,625评论 1 250
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,804评论 2 253
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,285评论 1 265
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,613评论 3 261
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,291评论 3 242
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,164评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,963评论 0 201
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 36,096评论 2 285
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,886评论 2 278

推荐阅读更多精彩内容