先日ルート化せずにパケットキャプチャが可能なtPacketCaptureをリリースしました。
tPacketCaptureは、Android4.0で追加されたVPNに関する機能を利用しています。しかしながらこのVPNに関する技術的な説明は、VpnServiceクラスに書いてあるのみです。
少しでも参考になればと思い、和訳+αとしてVpnServiceについての解説をします。

vpsservice_notification.png

和訳の前に、VpnServiceを使うことで実現できできそうな機能を考えてみます。
そもそも、VPN(Virtual Private Network)自体はICS以前のAndroidでも利用可能でした。
しかし、システムの設定として利用するVPNでは、アプリケーションごとに合わせたカスタマイズができませんでした。
そこでICSから、VpnServiceクラスが提供され、VPNを利用するアプリケーションを作成できるようになりました。
特に企業向けアプリケーションで利用することを想定しているようです。

VpnServiceを利用すれば、パケットを全て監視することが可能になります。
そのため、以下の様な機能も作れます。

  • 通信先IPによって、VPNサーバを利用するか自動で切り替える(VPN経由でアクセスできないサーバへは直接接続する)
  • 通信先IPによるフィルタ機能の実現(マルウェアの通信を完全にシャットアウト)


その他にもいろいろ発展しそうです。

以下、リファレンスの和訳になります。

Android Developers VpnService:
http://developer.android.com/intl/ja/reference/android/net/VpnService.html

VpnServiceは、独自にVPN機能を拡張したり、構築するときに利用できるベースクラスです。
一般的に、VpnServiceが、Vertual Networkインタフェースの作成、アドレス設定、ルーティング設定、そしてディスクリプタの返却を実施します。
ディスクリプタから読み出すことにより、インタフェースに送られた送信パケットを取り扱えます。
ディスクリプタに書き込むことにより、インタフェースが受信したかのように、パケットを追加することができます。
インタフェースはInternet Protocol(IP)上で動作していますので、全てのパケットはIPヘッダーで始まっています。
トンネルを通して遠隔サーバとパケットを交換し処理を行うことで、アプリケーションはVPN接続を実現できます。

アプリケーションにパケットを渡すことで、セキュリティ的な危険が出てきます。
VPNアプリケーションは簡単にネットワークを切断することができます。
加えて、2つのVPNアプリケーションが存在する場合、お互いに干渉するでしょう。
システム側ではこれらの問題に対応する為、いくつかの動作を行います。
ポイントを以下に示します。

  • VPN接続を行うためには、ユーザの操作が必要です(図1
  • 一度に1つのVPN接続しか存在できません。新しい接続が作られるときには、古い接続が無効にされます
  • VPN接続を行っている間は、システムがノーティフィケーションを表示します(図2
  • 現在のVPN接続の情報を提供するダイアログを、システムが提供します。切断のボタンもついています(図3
  • ファイルディスクリプタが閉じられた時でも、ネットワークは自動的に再接続されます。これは、VPNアプリケーションがシステムにkillされた場合も同様になります

図1

vpsservice_dialog_start.png

図2

vpsservice_notification.png

図3

vpsservice_dialog_disconnect.png

このVpnServiceクラスには、2つの主要なメソッドがあります。
prepare(Context) と establish()です。
前者は、ユーザの操作を取り扱ったり、他のアプリケーションによって生成され
たVPN接続を停止します。
後者は、VpnService.Builderにパラメータを渡すことで、VPNインタフェースを
生成します。
アプリケーションは、必ずprepare(Context)を呼ばなければなりません。
このことにより、本クラスの他のメソッドを使用することを許可でき、また、い
つでも許可を無効にすることができます。
VPN接続を生成するための一般的な手順を以下に示します。

  1. 接続のボタンをユーザが押した時、prepare(Context)が呼ばれ返されたintentで起動します
  2. アプリケーションの準備ができた時点でServiceが開始されます
  3. 遠隔サーバに呈してトンネルが生成され、VPN接続のためにネットワークパラメータをやり取りします
  4. これらのパラメータをVpnService.Builderに渡し、establish()を呼び出すことでVPNインタフェースが生成されます
  5. トンネルと返されたファイルディスクリプタの間で、処理やパケットの交換を実施してください
  6. onRevoke()が呼び出された時、ファイルディスクリプタはcloseされ、トンネルは直ぐに切断されます

このクラスを継承したServiceはパーミッションとintent-filterを必要とします。
BIND_VPN_SERVICE パーミッションによりServiceは保護されなければならないし、intent-filterはSERVICE_INTERFACEアクションに一致しなければいけません。
AndroidManifest.xmlに記載するVPNサービスの定義例を以下に示します。

 <service android:name=".ExampleVpnService"
android:permission="android.permission.BIND_VPN_SERVICE">
<intent-filter>
<action android:name="android.net.VpnService"/>
</intent-filter>
</service>

このVpnServiceは4.0リリース直後のエミュレータだと動作しませんでしたが、最新のエミュレータでは動作させる事が可能です。

ブログ内の関連する記事