随着IPv4地址池的耗尽,企业和蜂窝网络提供商正越来越多的部署IPv6 DNS64和NAT64网络。DNS64/NAT64网络是一个纯IPv6网络,通过转换,它也可以继续支持访问IPv4内容。基于应用的本质不同,转换的方式也不同:
- 如果你正在使用高级网络API,例如NSURLSession和CFNetwork框架,编写客户端应用,并且你通过名称进行连接,那么你无需为适应IPv6做任何改变。如果你不是通过名称进行连接,那你需要做出相应改变。参见Avoid Resolving DNS Names Before Connecting to a Host来了解如何做到,CFNetwork的信息,参见CFNetwork Framework Reference。
- 如果你正在写服务器端的应用,或者其他低级别网络应用,你应该确保你的socket代码正确的使用了IPv4和IPv6地址。参考RFC4038: Application Aspects of IPv6 Transition。
什么推动了IPv6
主要的网络服务提供商,包括美国的主要蜂窝网络运营商,正积极推广和部署IPv6。这是由多种因素导致的。
注意:World IPv6 Launch是一个全球性的跟踪部署活动的组织。想查看最近的趋势,请访问World IPv6 Launch website。
IPv4地址耗尽
几十年来,大家已经知道IP v4地址将最终耗尽。类似CIDR(Classless Inter-Domain Routing,无类别域间路由)和NAT(network address translation,网络地址转换)技术帮助延缓了这种趋势。但是,在2011年1月31日,IANA(Internet Assigned Numbers Authority,互联网号码分配机构)的顶级池正式耗尽。ARIN(American Registry for Internet Numbers,美国互联网号码注册机构)预计2015年夏天IP v4地址将耗尽。(原文写于2012年。)
IP v6比IP v4更高效
除了解决IP v4耗尽的问题,IP v6比IP v4更高效。例如,IP v6:
- 避免进行网络地址转换(NAT)
- 通过使用简化的报头,可以更快的通过路由
- 防止网络数据包碎片
- 避免相邻地址解析的广播(Avoids broadcasting for neighbor address resolution)
4G部署
第四代移动电信技术(4G)仅基于数据包交换。由于IP v4地址的限制,为了扩展4G的部署,需要得到IP v6的支持。
多媒体服务兼容性
IMS(IP Multimedia Service Compatibility,IP多媒体核心网络子系统)允许服务(例如多媒体SMS信息和LTE语音)通过IP传送。某些服务提供商使用IMS只兼容IP v6。
成本
当产业持续迁移到IP v6的时候,继续支持旧有的IP v4网络会导致服务提供商增加额外的运营和管理成本。
DNS64.NAT64转换流程
为了帮助减缓IP v4地址的消耗,NAT被应用于IP v4网络中。尽管这是一种临时的解决方案,但却被证明非常昂贵且脆弱。今天,随着越来越多的客户端使用IP v6,供应商现在不需同时支持IP v4和IP v6。这种努力成本巨大。
图10-1 提供IP v4和IP v6分开连接的蜂窝网络
理想情况下,提供商想舍弃掉对IP v4网络的支持。但是,很多IP v4服务器依然是网络中重要的组成部分。为了解决这个问题,大多数主要的网络提供商采用了DNS64/NAT64转换流程。这是一种通过转换继续提供对IP v4内容的IP v6网络。
图10-2 使用DNS64 和 NAT64部署IP v6网络的蜂窝网络
这这类流程中,客户端发送DNS查询到一个DNS64服务器,从DNS请求一个IP v6地址。当IP v6地址被找到时,它就会立即传回给客户端。但是,如果IP v6地址没有找到,DNS64服务器就会转而请求一个IP v4地址。然后DNS64服务器使用IP v4地址的前缀合成一个IP v6地址,并传回给客户端。这样,客户端始终会收到一个IP v6地址。查看图10-3。
图10-3 DNS64 IP v4到IP v6的转换过程
当客户端发送一个请求到服务器,任何为合成地址指定的IP v6数据包,都会自动的通过NAT64网关进行路由。该网关执行IP v6到IP v4地址的转换,以及对请求的协议转换。它还为了服务器的的响应,执行IP v4到IPv6的转换。参见图10-4。
图10-4 DNS64/NAT64转换解决方案的流程
IPv6和App Store要求
兼容 IPv6 DNS64/NAT64网络将是App Store提交的要求,所以确保兼容对于应用来说至关重要。好消息是,大多数应用已经IP v6兼容了。对于这些应用,定期测试来回顾是非常重要的。不兼容IP v6的应用在DNS64/NAT64网络上操作的时候可能会遇到麻烦。幸运的是,通过本章的介绍,解决这些问题通常都很简单。
对支持IP v6的常见阻碍
几种情况能阻止应用支持IP v6。下面这部分就是描述如何解决这些问题。
- IP地址字面量嵌入到了协议中。很多通信协议,例如SIP(Session Initiation Protocol,会话发起协议)、FTP(File Transfer Protocol,文本传输协议)、WebSockets、以及P2PP(Peer-to-Peer Protocol,点对点协议)等,在协议消息中都包含了IP地址字面量。例如,FTP参数命令DATA PORT 和 PASSIVE交换信息包含了IP 地址字面量。类似地,IP地址字面量可以出现在SIP头部的值中,例如To, From, Contact, Record-Route, 和 Via。参见 Use High-Level Networking Frameworks 和 Don’t Use IP Address Literals。
- IP地址字面量嵌入到了配置文件中。配置文件经常包含IP 地址字面量。参见Don’t Use IP Address Literals。
- 网络预检。很多应用试图通过把IP地址字面量传递给网络可达性API,来主动检查网络连接或活动Wi-Fi连接。参见 Connect Without Preflight。
- 使用低等级网络API。一些应用直接使用socket和其他原始的网络API,例如gethostbyname, gethostbyname2, 和 inet_aton。这些API容易被滥用或只支持IP v4;例如,为AF_INET地址族,而不是 AF_UNSPEC地址族解析主机名。参见Use High-Level Networking Frameworks。
- 使用小地址族存储容器。一些应用和网络库使用地址存储容器,例如uint32_t、in_addr、 和 sockaddr_in,它们是32bit或更小。参见Use Appropriately Sized Storage Containers。
确保IPv6 DNS64/NAT64兼容性
遵守以下准则,来确保应用应用中的IPv6 DNS64/NAT64兼容性。
使用高等级网络框架
需要联网的应用可以构建在高级网络框架或者低级POSIX socket APIs之上。在大多数情况下,使用高级框架就足够了。它们功能强大、易于使用、且不易出错。
图10-5 网络框架和API层
- WebKit。这个框架提供一系列针对在视窗上显示网页内容、以及实现浏览器功能(例如链接、管理前进后退列表、和管理最近访问过的网页历史)的类。WebKit简化了加载网页的复杂过程,——也就是异步从HTTP服务器请求网页内容,响应的数据可能随机的逐渐到达,或者由于网络错误只到达一部分。更多信息,参见WebKit Framework Reference。
- Cocoa URL加载系统。这个系统是通过没有提供显式IP 地址的网络发送数据的最简单方式。发送和接收数据使用几个类(例如NSURLSession, NSURLRequest, 和 NSURLConnection)中的一个,这些类都使用了NSURL对象。NSURL对象让你的应用操作URL以及它们指向的资源。通过调用initWithString: 方法并传入指定的URL来创建NSURL对象。调用NSURL类的checkResourceIsReachableAndReturnError:方法来检查主机的可达性。更多信息,参见URL Session Programming Guide。
- CFNetwork。这个Core Services框架提供一个针对网络协议的抽象库,可以使用它来轻松的执行各种网络任务,例如使用BSD sockets、解析DNS主机、以及使用HTTP/HTTPS。为了不使用显式的IP地址来标识主机,请调用CFHostCreateWithName方法。为了与TCP socket主机建立连接,请调用CFStreamCreatePairWithSocketToCFHost方法。更多信息,参见CFNetwork Programming Guide中的CFNetwork Concepts方法。
如果你需要使用低层次的socket API,需要参考RFC4038: Application Aspects of IPv6 Transition中的指南。
注意:Getting Started with Networking, Internet, and Web 和 Networking Overview提供网络框架和API的详细信息。
不要使用IP地址字面量
确保你没有把点分十进制的IP v4地址字面量传递给API,例如getaddrinfo和SCNetworkReachabilityCreateWithName。而是应该使用高级网络框架和地址无关的API,例如getaddrinfo 和 getnameinfo,并传递主机名或完整的域名给它。参见getaddrinfo(3) Mac OS X Developer Tools Manual Page 和 getnameinfo(3) Mac OS X Developer Tools Manual Page.
注意:在iOS9和OS X10.11及更高级的版本,从IOS9何OSX10.11开始,NSURLSession和CFNetwork会在本地自动将IPv4的地址合成IPv6地址,便于与DNS64/NAT64通信。但是,你仍不应该在代码中使用IP地址字面量。
无预检连接
可达性API(参见SCNetworkReachability Reference)是用于在识别出连接问题之后进行诊断的。很多应用错误地使用这些API来预检网络连接,它们通过调用SCNetworkReachabilityCreateWithAddress方法并将IP v4地址0.0.0.0作为参数传入,以表明网络中有路由。但是显示的路由并不保证实际上存在一个网络连接。总之,避免预检网络可达性。只需要尝试建立连接,并优雅的处理失败情况即可。如果你必须检查网络可达性,那也要避免调用SCNetworkReachabilityCreateWithAddress方法。而是调用SCNetworkReachabilityCreateWithName方法,并传入主机名。
一些应用还给SCNetworkReachabilityCreateWithAddress方法传入一个IP v4地址169.254.0.0(自动分配的本地IP),视图检查激活的Wi-Fi连接。想要检查Wi-Fi或蜂窝网络连接,应该查看网络可达标识kSCNetworkReachabilityFlagsIsWWAN。
采用合适尺寸的存储容器
使用足够大的地址存储容器,例如sockaddr_storage,来存储IP v6地址。
检查IPv6 DNS64/NAT64不兼容性的源代码
检查并排出特定于IP v4的API。例如:
- inet_addr()
- inet_aton()
- inet_lnaof()
- inet_makeaddr()
- inet_netof()
- inet_network()
- inet_ntoa()
- inet_ntoa_r()
- bindresvport()
- getipv4sourcefilter()
- setipv4sourcefilter()
如果你的代码要处理IPv4类型,请确保IP v6也能被处理。
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来合成IP v6地址
如果应用需要连接到一个仅IP v4且没有主机名的服务器,请使用getaddrinfo来解析IP v4地址字面量。如果当前网络接口不支持IP v4,但却支持IP v6、NAT64、以及DNS64,执行这个任务将会得到一个合成的IP v6地址。
代码清单10-1 展示了如何使用getaddrinfo来解析一个IP v4字面量。假设你有一个IP v4地址存储在内存中,占4个字节(例如{192, 0, 2, 1}),这个例子代码会把它转换为字符串(例如”192.0.2.1"),使用getaddrinfo来合并为一个IP v6地址(例如一个struct sockaddr_in6包含IP v6地址”64:ff9b::192.0.2.1"),并尝试连接这个IP v6地址。
代码清单 10-1 使用getaddrinfo来解析IP v4地址字面量
#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);
注意:合并IP v6地址的功能是在 iOS 9.2 和 OS X 10.11.2开始添加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网络设置选项在OS X10.11及更高版本中可用。另外,基于Mac的IPv6 DNS64/NAT64网络和支持RFC6106: IPv6 Router Advertisement Options for DNS Configuration的客户端设备是兼容的。如果你测试的设备不是iOS或OS X设备,请确保它支持这个RFC。请注意,不同于服务提供商部署的DNS64/NAT64流程,基于Mac的DNS64/NAT64始终生成合成的IP v6地址。因此,它不提供对本地网络之外的IP v6服务器的访问。详情参见 Limitations of Local Testing。
图10-6 基于Mac的本地 IPv6 DNS64/NAT64网络
想要使用Mac来设置一个本地的IP v6 Wi-Fi网络
- 确保Mac已通过非Wi-Fi的方式连接到网络。
- 从Dock、LaunchPad或Apple菜单启动系统偏好。
- 按住Option键并点击Sharing(分享)。不要放开Option键。
图10-7 打开分享偏好
- 在分享服务列表中选择Internet Sharing(网络分享)。
图10-8 配置网络分享
- 放开Option键。
- 选择 Create NAT64 Network复选框。
图10-9 启用 Create NAT64 Network。
- 选择用于互联网连接的网络接口,例如Thunderbolt Ethernet(雷电以太网接口)。
图10-10 选择用于分享的网络接口
- 选中Wi-Fi复选框。
图10-11 允许通过Wi-Fi分享
- 点击Wi-Fi选项,并为你的网络配置网络名和安全选项。
图10-12 访问Wi-Fi网络选项。
图10-13 设置本地Wi-Fi网络选项
- 选择Internet Sharing(互联网分享)复选框来启用你的本地网络。
图10-14 启用互联网分享
- 当提示确认是否开启共享时,点击开始(Start)。
图10-15 开始网络共享
一旦共享被激活,你应该可以看到一个绿色状态等以及一段说明网络共享开启的标签。在Wi-Fi菜单中,你还将看到一个向上的小尖头,表明网络共享已经被启用。现在你有了IPv6 NAT64网络,其他神杯可以连接到这个网络来测试你的应用。
图10-16 网络共享指示器
重要:为了确保测试严格的使用本地IP v6网络,要保证测试设备不要打开其他网络接口。例如,如果测试一个iOS设备,就禁用蜂窝网络服务,确保只在Wi-Fi环境下进行测试。
本地测试的局限性
基于Mac的IPv6 DNS64/NAT64网络是一个在IP v6环境中测试应用的有效工具。但是,但因为它始终生成合成的IP v6地址,并使用IP v4在WAN侧发送数据,所以它不是和服务提供商提供的网络有所区别。这个网络允许IP v6之间的直连。如果你的服务器配置错误,它或许会导致应用在使用和测试时有不同的行为。甚至导致在你自己的环境中难以复现的App Review失败。
特别是,如果你的服务声称支持IP v6但是在运行时却有问题。这种情况下,在最初测试的时候,应用可以通过IP v6路径和服务器进行通信,因此认为测试通过。但是,实际上你的测试网络是把IP v6在WAN侧转换成了IP v4。所以你使用的实际上是你的服务器的IP v4数据路径。稍后,在应用程序审查(或在真实世界)期间,应用相同的操作,与服务器的连接是直接使用IP v6。如果服务器无法正常响应IP v流量,应用就无法按预期运行。
为了避免这种情况,除了使用基于Mac的 IPv6 DNS64/NAT64测试网络来验证应用外,还要独立的验证你的服务器是否为IP v6服务器。例如,确保服务器:
- 有正确的DNS信息。除了检查服务器自身,你可以从Mac上使用命令行工具dig(1)来查看服务器如何报告其AAAA记录。
- 正在监听IP v6.使用类似ipv6-test.com的工具来测试web服务(HTTP或HTTPS)。对于其他协议,你需要从其所在IP v6网络进行验证。
- 正确的响应IP v6请求。如果你有访问权限,查看服务器日志,以验证IP v6流量被正确处理。如果不是,你需要从其所在IP v6网络进行测试。