AppleのIPv6検収を通過する

WWDC15以降にAppleの開発者ページに掲載されたSupporting IPv6 in iOS 9というニュースで、IPv6-only network serviceに対応するというポリシーが発表され、2016年から、その点をアプリレビュー時に適用すると公示しています。ニュースを見るや、頭が真っ白になりましたが、この時IPv6 Only Networkを一般ユーザーに提供している国内ISPが存在しなかったため、まだしっくりきていませんでした。Appleが何をしたいのか、どのレベルがアプリレビュー時においてReject基準になるのか、頭の中で混乱しました。

テスト方法はAppleのガイドラインがあったので、そのドキュメントを見て、自分が担当しているSDKのテストを進行してみましたが、やはりIPv6 Only networkでは通信が行われていません。

最初は社内ネットワーク環境からSDKのソースコードなどを調べてみましたが、大きな問題は見つかりませんでした。しかし、コンピュータは嘘をつかないので、SDK内で使用しているOpen Sourceライブラリを調べてみると、IPv6をサポートできない状態になっていました。該当の問題を解決すると、正常にIPv6環境でのテストが成功しました。

この記事では、私がIPv6の問題を処理する際に調べた基本的な内容について紹介したいと思います。

IPv6で最小限度知っておくこと

アドレスの長さが128bitに急増しました。(インターネット接続機器とそれに伴うIPv4アドレスの枯渇に対応するため提案されました)IPv6は、既存のIpv4との互換性を最大限に維持できる方向で設計され、ほとんどの既存プロトコルは修正することなく、IPv6上で動作できます。

アドレス表現方法

アドレスの表現は、128bitのアドレス空間で、16bit(2octet)を16進数で表現して、8桁に設定します。

2001:0db8:85a3:0000:0000:8a2e:0370:7334

上記のAddress Literalは以下のように表現できます。
0000は0に省略ができ、以下のように省略できます。

2001:0db8:85a3:8a2e:0370:7334

IPv6の特徴

IPv6の特性について簡単に説明します。

  • IPアドレスの拡張:IPv4での32ビットのアドレス空間から、IPv6では128ビットのアドレス空間を提供する。
  • ホストアドレス自動設定:IPv6ホストは、IPv6ネットワークに接続した瞬間に自動でネットワークアドレスが付与される。これは、ネットワーク管理者からIPアドレスを与えられ、手動設定する必要があったIPv4に比べて重要な事項である。
  • パケットサイズの拡張:IPv4では64KBに制限されていたが、IPv6のジャンボグラムオプションを使用すると、特定のホスト間では、任意で大きいサイズのパケットを送受信できる。これにより帯域幅が広いネットワークをより効率的に使用できる。
  • 効率的なルーティング:IPパケットの処理を迅速に行えるように、固定サイズの単純なヘッダーを使用するとともに、拡張ヘッダを通じてネットワーク機能の拡張やオプション機能の拡張が容易な構造に定義された。
  • フローラベル(Flow Labeling):フローラベル(flow label)概念を導入して、特定のトラフィックは別途特別な処理(リアルタイム通信など)で、高品質のサービスを提供できるようになった。
  • 認証とセキュリティ機能:パケット送信元の認証とデータの整合性と機密性の機能をIPプロトコル体系に反映した。IPv6拡張ヘッダーから適用できる。
  • モビリティ:IPv6ホストは、ネットワークの物理的な場所に制限を受けずに同じアドレスを維持しながら自由に移動ができる。このようなモバイルIPv6は、RFC 3775とRFC 3776に記述されている。(IPv4にもモバイルIPが定義されているが、まだ汎用化されていないと思われる)

参照:https://jp.wikipedia.org/wiki/IPv6

Appleが公示したiOSアプリの検収内容を発見

iPhoneアプリの検収

Appleの公示内容:https://developer.apple.com/news/?id=05042016a

内容は非常に簡単で、IPv6 Onlyネットワークでもアプリが正常に動作しなければならないということです。これはIPv4基盤のAPIは動作しないことを意味します。一般的にネットワーク通信時、NSURLSessionのCFNetworkのようなHigh Level APIの場合では、すでにIPv6に対するサポートに問題はありませんが、Low-Level socket APIsを直接使用する場合は、構造体と関数などにIPv4のみの関数を使用していないか確認が必要です。

また、アプリでネットワーク通信時に、ハードコーディングされたIPv4アドレスを使用したり、サーバーから伝達されるアドレスがIPv4アドレスを使ってサーバーにアクセスするときもアプリの修正が必要です。

したがって、下記のように、テスト環境を構築して正常にネットワーク通信できるか確認しましょう。

IPv6テスト方法

一般的には、IPv6 Onlyネットワーク環境を準備してテストできないので、AppleではIPv6テスト用にNAT64 / DNS64環境を構成できるようにガイドを設けています。

テスト環境

[https://developer.apple.com/library/mac/documentation/NetworkingInternetWeb/Conceptual/NetworkingOverview/UnderstandingandPreparingfortheIPv6Transition/UnderstandingandPreparingfortheIPv6Transition.html#//apple_ref/doc/uid/TP40010220-CH213-SW16]から抜粋

テスト環境を構築するには、以下のような機器が必要です。

  1. 有線、無線に対応したMacOSがインストールされている機器
  2. iPhone、MacOS機器

MacOS機器の場合、DNS64、NAT64の役割をして接続したiOS DeviceにIPv6アドレスを割り当て、外部ネットワーク(IPv4)と通信できます。(NAT64は、IPv6とIPv4ホスト間の通信を可能にする技術で、DNS64は、DNSサーバーにAAAAレコードを要請したとき、Aレコードだけが存在する場合、AレコードからAAAAレコードに合成(変換)して要請されたサーバーのIPアドレスを取得できるようにします)

iPhoneは、インターネット共有(NAT64)を活性化したiMac(上記の1項目を満たす場合、他の機器でも可)機器に接続し、iMacは有線LANで既存のインターネット環境に接続します。

したがってiPhoneはNAT64環境で提供されるIPv6アドレスが割り当てられます。詳細は下記で確認できます。
[https://developer.apple.com/library/mac/documentation/NetworkingInternetWeb/Conceptual/NetworkingOverview/UnderstandingandPreparingfortheIPv6Transition/UnderstandingandPreparingfortheIPv6Transition.html#//apple_ref/doc/uid/TP40010220-CH213-SW16]

万が一、上記の方法でテスト時にネットワーク通信がうまくいかなかった場合は、下記を確認してみよう。

  1. IP literalをハードコーディングして、プログラムで使用している。またはIP Addressを設定ファイルやサーバーから取得してsocket接続している。
  2. Network Preflight(通信前に、Wi-Fi、Cellularなどの接続テスト)をZeroAddress(0.0.0.0)でReachability Testしている。
  3. Low-Level Networking APIを使用している。(ソースコードおよび外部ライブラリ)
  4. IP Addressを保存するためにuint32_tin_addrsockaddr_inのような変数タイプ(32bit以下のサイズ)を使用している。

次章で詳しく見てみよう。

開発者が注意すべき点

IP literalをプログラム上でハードコーディングした場合

一般的には、IP literalを直接使用する場合はありませんが、一部のシステムでは、最初にサーバーにアクセスした後、当該サーバーからIPアドレスを割り当てられ、再び割り当てられたIPで接続するようなものがあります。または設定ファイルからIPアドレスを取得して使用している場合もあります。この場合は、IPv6 onlyネットワーク上で正常に通信が行われず、Apple検収でRejectの理由になります。したがって、必ずDNSを利用してIPアドレスを取得できるようにする必要があります。

Network Preflightをする場合

iPhoneやiPadなどのMobile Deviceは、ネットワーク通信が常に接続されることを保証できないので、通信前に、Wi-FiやCellular接続状態を確認した上で、通信するように実装されています。従来のZero-Address(0.0.0.0)でReachability Testを多用していました。

しかし、IPv6 Onlyネットワークの場合、Zero-AddressでReachability Testした場合、通常の接続状態を確認できません。
したがって、以下のように変更する必要があります。

一般的にIPv4ネットワーク環境では、下記APIにZero-Addressを割り当てて、接続テストをします。

SCNetworkReachabilityCreateWithAddress()

IPv4とIPv6ネットワーク環境を同時にサポートするには、以下のAPIを通じて実際のDNSに存在するHost名を記入し、接続テストをする必要があります。

reachability = SCNetworkReachabilityCreateWithName(kCFAllocatorDefault, @"www.google.com" UTF8String]);

SCNetworkReachabilityCreateWithName()  関数を用いて接続テストをすると、実際に当該Domainを持つサーバーには接続負荷がかからず、ネットワークの接続状態だけを確認します。

Low-Level Networking APIを使用している場合

iOS Foundation FrameworkでサポートしているHigh-Level APIだけを使って通信する場合、別途処理は不要ですが、一般的にはWebsocketなどの通信に外部ライブラリを使用したり、通信性能を向上させるためにOpen Sourceネットワークライブラリを使用している場合があります。このような場合は、Open Sourceで以下のような関数を使用していないか確認が必要です。

以下のようなIPv4専用APIがあれば、削除(または変更)が必要です。

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

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

IPv6によるアプリのReject事例と参考資料

アプリのReject事例

2016/6/1以降に検収申請した一部アプリで、特に変更していないのにRejectされてしまうケースが発生し、Review Rejectから、IPv6 only networkで正常にゲームが実行されないことが、その理由だと分かりました。

ゲームとゲームサーバーの通信上では、すでにIPv6に対応したAPIを使用するように措置してあり、アプリで使用するすべてのサーバーアドレスをDomain登録するように変更していたので、問題なく検収を通過できると思っていましたが、先ほどの理由でパスできませんでした。確認したところ、サーバーからIPv4アドレスを受け取り、クライアントが当該アドレスに基づいて接続を試みるときに問題が発生していました。当該アドレスがIPv4 literalで構成されており、IPv6 only network環境では正常にConnectionを結ぶことができない状況になっていました。

最近では、アプリを開発する際にOpen Sourceや外部のソリューションを多用する傾向があるため、アプリで使用している外部ソリューションでIPv6に対応しているか明確に確認する必要があります。

参考資料

TOAST Meetup 編集部

TOASTの技術ナレッジやお得なイベント情報を発信していきます
pagetop