测试基础 iOS 应用必须支持 IPv6,否则审核将被拒

恒温 · 2016年06月15日 · 最后由 王静淑 回复于 2016年08月24日 · 2549 次阅读

从2016年6月1日起,iOS 应用必须支持 IPv6,否则审核将被拒。详见 Supporting IPv6 DNS64/NAT64 Networks。本文是翻译稿。从本文中可以学到有关 IPv6 过度时期的网络架构和具体 IOS 应用如何兼容的知识。

原文链接: http://www.pchou.info/ios/2016/06/05/ios-supporting-ipv6.html?utm_source=tuicool&utm_medium=referral

Supporting IPv6 DNS64/NAT64 Networks

IPV4 地址枯竭迫在眉睫,企业和移动运营商都纷纷部署 IPv6 DNS64 和 NAT64 网络。所谓 DNS64/NAT64 网络其实就是一个 IPv6-only 的网络,通过转换,我们可以继续访问 IPv4 的内容。 根据应用的不同,我们需要不同的处理方式来适配 IPv6:

  • 如果你在使用 high-level 的 networking API,比如 NSURLSession ,或者你使用 CFNetwork 框架,使用 hostname 来连接的话,你就不需要特意为 IPv6 做什么。但是如果你没有使用 hostname 来连接的话,那就要做下处理,具体参见 Avoid Resolving DNS Names Before Connecting to a Host 。
  • 如果你在写服务端应用或者底层网络应用,那么你需要确保你的代码可以在 IPv4 和 IPv6 的地址工作。参考 RFC4038: Application Aspects of IPv6 Transition

What’s Driving IPv6 Adoption

一些主要的网络服务提供商,包括美国主要的移动运营商,都在积极推动和部署 IPv6 网络。主要原因如下:

Note: World IPv6 Launch is an organization that tracks deployment activity at a global scale. To see recent trends, visit the World IPv6 Launch website.

IPv4 地址耗尽

几十年来,众所周知,IPv4 地址最终会被耗尽。 我们通过技术手段,比如无类别域间路由(CIDR)和 网络地址转换(NAT)推迟耗尽的时间。然而在2011年1月31日,IANA(The Internet Assigned Numbers Authority,互联网数字分配机构)可分配 IPv4 地址耗尽。 美国 Internet 号码注册中心 (ARIN) 也预计在 2015 年夏天用完 IPv4 地址—去这里看倒数!(特么都 2016 年了。。)

IPv6 比 IPv4 更加高效

除了要解决 IPv4 耗尽的问题,IPv6 也比 IPv4 更加高效。比如:

  • 不需要 NAT 了。(IPv6 可以做到每一粒沙子都有一个唯一的 IP,这样当然可以去掉 NAT)
  • 通过简化报头提供更快的路由
  • 防止网络碎片
  • 相邻地址解析时避免使用广播

4G 开发

第四代移动通信技术 (4G) 仅基于包交换,由于 IPv4 地址的限制,为了保证 4G 开发的扩展性,需要 IPv6 的支持

多媒体服务兼容性

IP Multimedia Core Network Subsystem (IMS) 允许一些服务通过 IP 传输,例如多媒体 SMS 消息和 VoLTE。 有些服务提供商使用 IMS 时仅支持 IPv6。

成本

业界在向 IPv6 迁移的过程中,需要继续支持古老的 IPv4 网络,这使运营商产生了额外的操作和维护成本。

DNS64/NAT64 转换流程

为了缓解 IPv4 地址的耗尽,许多 IPv4 网络采用 NAT 技术。尽管这种方案临时奏效,但是实践证明耗资巨大并且不够可靠。如今,随着越来越多的设备使用 IPv6,运营商必须同时支持 IPv4 和 IPv6,这种努力却是花费巨大的。

图 10-1 蜂窝移动网络分别提供 IPv4 和 IPv6 链接

img

理想情况下,运营商希望丢掉对 IPv4 的支持。然而,这么做会导致客户端无法访问基于 IPv4 的服务器,而 IPv4 的服务器依然是网络的重要组成部分。为了解决这个问题,大多数的网络供应商实现了一个叫 DNS64/NAT64 的转换流程。这是个纯 IPv6 网络,并通过转换也可继续访问 IPv4 的内容。

图 10-2 蜂窝移动网络用 DNS64 和 NAT64 来部署一个 IPv6 网络

img

在这个流程中,如果客户端向 DNS64 服务器发起一个 DNS 查询,当 DNS 找到一个基于 IPv6 的地址后,立刻返回客户端。如果无法找到对应的 IPv6 地址,DNS64 服务器将请求 IPv4 地址,然后 DNS64 服务器将 IPv4 作为前缀合成一个 IPv6 地址,并且将其返回给客户端。这样,客户端将总是获得一个 IPv6 目标地址,见图 10-3。

图 10-3 DNS64 IPv4 到 IPv6 转换过程

img

当客户端向服务端发送请求时,目标地址为合成后的 IPv6 地址会自动由 NAT64 网关路由过去。对于请求,网关作的是 IPv6 到 IPv4 的转换。同样的,对于服务器响应,网关作的是 IPv4 到 IPv6 的转换。见图 10-4

图 10-4 DNS64/NAT64 转化方案的流程

img

IPv6 和 App Store 的要求

对 IPv6 DNS64/NAT64 网络的兼容性,将是 App Store 的提交时的必须条件,所以兼容对于 app 来说是相当重要的。好消息是,大多数 app 已经是 IPv6 兼容的了。对于这些 app,进行定期的回归测试依旧是必要的。对于那些 IPv6 不兼容的应用在面对 DNS64/NAT64 网路时可能遇到麻烦。幸运的是,解决问题通常很简单,下面章节会讨论这个问题。

常见的阻碍 IPv6 支持的行为

有几个导致应用无法支持 IPv6 的场景。本节描述如何解决这些问题。

  • 嵌入 IP 地址的协议。许多通信协议,像SIP,FTP,WebSockets,P2PP,都可能在协议的报文中包含了 IP 地址。例如,FTP参数命令DATA PORT PASSIVE的交换信息中包含了 IP 地址。类似的,IP 地址值可能出现在SIP的头部,像To FROM Contact Record-Route以及Via。参见Use High-Level Networking FrameworksDon’t Use IP Address Literals
  • 配置文件中使用 IP 地址。参见Don’t Use IP Address Literals
  • 网络状态监测。许多 app 试图主动的监测网络连接和 wifi 连接,却将 IP 地址作为参数而调用网络可达性相关的 API。参见Connect Without Preflight
  • 使用底层网络接口。一些 app 直接使用socket和其他的低层次网络 API,比如gethostbyname gethostbyname2inet_aton。这些 API 很容易因为错误使用而仅支持 IPv4。比如,域名解析时使用AF_INET地址簇,而不是AF_UNSPEC地址簇。参见Use High-Level Networking Frameworks
  • 使用了小的地址簇存储容器。一些 app 和网络库,使用了例如unit32,in_addr,sockaddr_in这种 32 位或更小的容器来存储地址。参见Use Appropriately Sized Storage Containers

确保 IPv6 DNS64/NAT64 兼容性

附上下面的指导来确保 IPv6 DNS64/NAT64 的兼容性。

使用高层次的网络框架

app 请求网络时,可以构建在高层次的网络框架上,也可以使用底层的POSIX兼容的socket接口。在多数情况下,相比底层接口,高层次的接口效率高一些,兼容性好,容易使用,不容易掉入通常的编程错误陷阱中。

图 10-5 网络框架和 API 层次

img

  • WebKit。此框架提供一系列的类用来在窗口上显示 web 内容,而且实现了浏览器特性,诸如:链接、前进后退管理、最近访问历史。WebKit 将加载网页的流程简化了,包括异步地从 HTTP 服务器上请求网页内容,这些服务器响应的数据包可能一点点送达,也可能以随机的顺序到达,甚至可能由于网络错误收不全。详见WebKit Framework Reference
  • Cocoa URL loading system。这个系统用于简单地通过网络发送和接收数据,却不需要提供显示的 IP 地址。数据的发送和接收使用这几个类中的一个:NSURLSession NSURLRequest NSURLConnection,这些类使用NSURL对象。NSURL对象允许你操作 URL。创建一个NSURL对象时使用initWithString:方法,并传入一个指定的 URL。调用NSURL类的checkResourceIsReachableAndReturnError:方法检测目标主机的可达性。详见URL Session Programming Guide
  • CFNetwork。这个核心服务框架提供了一个抽象网络协议的库。这个库提供了大量易用的网络操作,比如 BSD socket,DNS 解析,处理 HTTP/HTTPS。调动CFHostCreateWithName方法,避免显示的使用 IP 地址来标识主机。调用CFStreamCreatePairWithSocketToCFHost与主机建立 TCP 链接。详见CFNetwork Programming Guide中的CFNetwork Concepts

如果你需要使用低层次的 socket 接口,参看如下指导:RFC4038: Application Aspects of IPv6 Transition

Getting Started with Networking, Internet, and WebNetworking Overview提供详细的网络框架 API 的说明

不要使用 IP 地址

在许多 API 中请确保不再使用点分十进制表示的 IPv4 地址,例如getaddrinfoSCNetworkReachabilityCreateWithName。取而代之,应该使用高层次网络框架和地址无关的 API,例如在使用getaddrinfogetnameinfo时,传入主机名或域名。详见:getaddrinfo(3) Mac OS X Developer Tools Manual Page 和 getnameinfo(3) Mac OS X Developer Tools Manual Page。

从 IOS9 何 OSX10.11 开始,NSURLSessionCFNetwork会在本地自动将 IPv4 的地址合成 IPv6 地址,便于与 DNS64/NAT64 通信。不过,你依旧不该使用 IP 地址串。

连接时无需网络预检

检测网络可达性的 API(参见SCNetworkReachability Reference) 用来在遇到连接异常时进行诊断。许多 app 错误的使用了 API,它们往往通过调用SCNetworkReachabilityCreateWithAddress方法,并将 IPv4 地址0.0.0.0作为参数传入,来不断检查网络连接,实际表示是否至少可达一个路由 (which indicates that there is a router on the network)。然而,即使有这样的路由也不保证互联网的连接存在。总之,避免进行网络可达性的检测。只需要直接进行连接,并且优雅的处理失败的情况。如果你确实需要检测网络可用性,需避免使用SCNetworkReachabilityCreateWithAddress,而是调用SCNetworkReachabilityCreateWithName,并传入主机名。

有些 app 还在调用SCNetworkReachabilityCreateWithAddress的时候传入 IPv4 地址169.254.0.0(一个自动分配的本地 IP),试图检测 Wi-Fi 连接。若要检测 Wi-Fi 或蜂窝移动网络连接,参见网络可达标识kSCNetworkReachabilityFlagsIsWWAN

使用合适的 Storage Container 大小

使用 Storage Container 结构,如sockaddr_storage,用以有足够的空间存放 IPv6 地址。

检查代码不兼容 IPv6 DNS64/NAT64 的代码

查找并删除 IPv4 相关的 API,如:

  • inet_addr()
  • inet_aton()
  • inet_lnaof()
  • inet_makeaddr()
  • inet_netof()
  • inet_network()
  • inet_ntoa()
  • inet_ntoa_r()
  • bindresvport()
  • getipv4sourcefilter()
  • setipv4sourcefitler()

如果你处理的 IPv4 的类型,去报同时处理对应的 IPv6 类型

IPv4 IPv6
AF_INET AF_INET6
PF_INET PF_INET6
struct in_addr struct in_addr6
struct sockaddr_in struct sockaddr_in6
kDNSServiceProtocol_IPv4 kDNSServiceProtocol_IPv6

使用系统 API 合成 IPv6 地址

如果你的 app 需要连接到仅支持 IPv4 的服务器,且不使用 DNS 域名解析,请使用getaddrinfo处理 IPv4 地址串 (译注:getaddrinfo 可通过传入一个 IPv4 或 IPv6 地址,得到一个 sockaddr 结构链表)。如果当前的网络接口不支持 IPv4,仅支持 IPv6,NAT64 和 DNS64,这样做可以得到一个合成的 IPv6 地址。

代码 10-1 展示了如何用getaddrinfo处理 IPv4 地址串。假设你内存中有一个 4 个字节的 IPv4 地址串 (如{192,0,2,1}),这个示例代码将之转化为字符串 (“192.0.2.1”),使用getaddrinfo合成一个 IPv6 地址结构 (struct sockaddr_in6包含 IPv6 地址串为” 64:ff9b::192.0.2.1”),然后尝试连接到这个 IPv6 地址。

代码 10-1 使用getaddrinfo处理 IPv4 地址串

#include <sys/socket.h>
#include <netdb.h>
#include <arpa/inet.h>
#include <err.h>

uint8_t ipv4[4] = {192, 0, 2, 1};
struct addrinfo hints, *res, *res0;
int error, s;
const char *cause = NULL;

char ipv4_str_buf[INET_ADDRSTRLEN] = { 0 };
const char *ipv4_str = inet_ntop(AF_INET, &ipv4, ipv4_str_buf, sizeof(ipv4_str_buf));

memset(&hints, 0, sizeof(hints));
hints.ai_family = PF_UNSPEC;
hints.ai_socktype = SOCK_STREAM;
hints.ai_flags = AI_DEFAULT;
error = getaddrinfo(ipv4_str, "http", &hints, &res0);
if (error) {
    errx(1, "%s", gai_strerror(error));
    /*NOTREACHED*/
}
s = -1;
for (res = res0; res; res = res->ai_next) {
    s = socket(res->ai_family, res->ai_socktype,
               res->ai_protocol);
    if (s < 0) {
        cause = "socket";
        continue;
    }

    if (connect(s, res->ai_addr, res->ai_addrlen) < 0) {
        cause = "connect";
        close(s);
        s = -1;
        continue;
    }

    break;  /* okay we got one */
}
if (s < 0) {
    err(1, "%s", cause);
    /*NOTREACHED*/
}
freeaddrinfo(res0);

从 IOS9.2 和 OSX10.11.2 开始合成 IPv6 地址的功能才被加入到getaddrinfo。不过,这么用不会对旧的系统产生兼容性问题。参见 getaddrinfo(3) Mac OS X Developer Tools Manual Page.

测试 IPv6 DNS64/NAT64 兼容性

大多数蜂窝移动供应商已经开始部署 IPv6 DNS64/NAT64 网络,测试这种网络最简单的的方法是用 Mac 建立一个本地的 IPv6 DNS64/NAT64 网络。你可以将其他设备链接到这个网络来测试。见图 10-6

提示:IPv6 DNS64/NAT64 网络仅在 OSX 10.11 及更高版本上可以设置。除此之外,基于 Mac 来建立的 IPv6 DNS64/NAT64 网络仅与支持RFC6106: IPv6 Router Advertisement Options for DNS Configuration的客户端设备兼容。如果你的设备不是 iOS 或 OSX 设备,请确保它支持 RFC。还需注意的是:不同于运营商提供的 DNS64/NAT64 网络,基于 Mac 系统的 IPv6 DNS64/NAT64 总是返回合成后的 IPv6 地址。因此,它不能用于访问你本地网络以外的纯 IPv6 网络。

图 10-6 本地的基于 Mac 的 IPv6 DNS64/NAT64 网络

img

使用你的 Mac 建立本地的 IPv6 Wi-Fi 网络

  1. 确保你的 Mac 连接到互联网,但不是通过Wi-Fi

  2. 启动System Preferences

  3. 按住Option键 (标准键盘是 Alt 键) 点击Sharing,不要放开 Option 键。

    图 10-7 打开 Sharing preferences

    img

  4. 在共享列表中选择Internet Sharing

    图 10-8 配置 Internet Sharing

    img

  5. 放开Option

  6. 勾选Create NAT64 Network复选框

    图 10-9 启用一个本地 IPv6 NAT64 网络

    img

  7. 选择你用于互联连接的网络接口,例如蓝牙局域网 (译者注:通常这里 mac 用以太网连接互联网,很少有用蓝牙的)

    图 10-10 选择共享的网络接口

    img

  8. 选择 Wi-Fi 复选框

    图 10-11 通过 Wi-Fi 开启共享

    img

  9. 点击Wi-Fi Options,配置你网络的网络名和安全选项

    图 10-12 设置 Wi-Fi 网络选项

    img

  10. 勾选Internet Sharing复选框启动你的本地网络

    图 10-14 启动网络共享

    img

  11. 当弹出确认是否开启共享时,点击Start

    图 10-15 开启网络共享

    img

    一旦共享启动后,你应该可以看到一个绿色的状态指示灯和一段话说明共享已开启。在 Wi-Fi 菜单中,你同样将看到一个小的向上的箭头,表示网络共享已经开启。现在你拥有了一个 IPv6 NAT64 的网络,其他设备可以连接这个网络来测试 app。

    图 10-16 网络共享开启图标

    img

提示:为了确保测试时严格使用本地的 IPv6 网络,请确认测试设备没有其他的网络接口正在使用。例如,如果你在测试 iOS 设备,确保蜂窝移动网络服务是禁用的,这样才能确保通过 Wi-Fi 连接。

如果觉得我的文章对您有用,请随意打赏。您的支持将鼓励我继续创作!
共收到 5 条回复 时间 点赞

来顶帖了,学习下 ipv6 怎么搞

前半个月,我们提交审核的时候也遇到了,最后还是需要支持 IPV6 才过审,坑多的很

相信苹果

说到审核被拒真心一个头两个大,这大热天心情更烦躁了!!!但是我的审核通过了,这可比吃雪糕降温速度快多了,第一天提交,第二天通过,这速度杠杠哒,其实就是给 APP 服务器域名添加 IPv6 解析,添加一条 4A 记录,超级 easy,无意中加入的干货群(140755394)帮我解决了苹果审核头疼的问题,推荐一试!

苹果最近太任性了,这不我的 APP 审核被拒了 N 回,大热天我这暴脾气真是蹭蹭的往上涨。还好有大神鼎力相助,发挥洪荒之力让我 easy 通过审核,这不呼吸也顺畅了,赶快让我嘚瑟一会,其实就是给 APP 服务器域名添加 IPv6 解析,添加一条 4A 记录。这么好的消息必须雨露均沾,无意中加入微博:6Box 网络加速器,强推给大家,审核通过分分钟的事!!!!!!

需要 登录 后方可回复, 如果你还没有账号请点击这里 注册