Android の VPN ファイアウォールでアプリをブロックすると、アプリは動かなくなり、その使用量の行は上がり続ける。そこで「ブロックが漏れた」あるいは「カウンターが壊れている」と考えたくなる。どちらも導けない。使用量の記録は、ある観測点でその ID にバイトが帰属された、と述べているだけで、遠隔のサービスがリクエストを受け付けたかどうかは何も語らない。どの境界が測られ、どの ID に課されたのかが分かれば、二つの観測は矛盾しなくなる。

カウンターが測るのはバイト数であり、配信ではない

Android は通信を UID に帰属させ、両方向で数える。それをアプリのリクエストが成功した瞬間ではなく、インターフェースの境界で行う。

拒否された接続試行もパケットを動かす。SYN、リセット、それに続く再送、そして両方に先立つ DNS 問い合わせである。何も配信されなくてもこれらのバイトは存在し、経路によってはどのフィルターが判断するよりも前に数えられる。

だからこそ、すべてのリクエストが失敗していても、ブロックしたアプリの行は増えうる。カウンターが答えるのは「この ID に何バイトが帰属したか」であり、「この ID がインターネットに到達したか」ではない。グラフは受け入れられたストリームと丁重に拒否されたハンドシェイクを区別できず、両方向を合計した総量も区別できない。

帰属はアイコンではなく UID に従う

行は UID ごとであり、一つの UID を複数のパッケージが共有することがあり、可視のアプリが自分の ID で動く別のコンポーネントに処理を渡すこともある。共有の WebView プロセスが描画するブラウザのタブ、バックグラウンド同期サービス、主要アプリと並んで導入されたコンパニオンパッケージは、いずれも使用量画面が予想外の名前の下で、あるいはブロックしたはずのアプリの下で表示しうる通信を生む。ブロックされたパッケージとそのリクエストを運ぶヘルパーは別の主体であり、規則はその一方しか覆っていなかった。

数値がおかしいときは、ラベルではなく UID を比べる。使用量 API は UID で問い合わせるものであり、adb shell dumpsys package <package> | grep userId= が比較すべき数値を出力する。同じ UID の二つの項目は、カウンターから見れば同じ ID である。

時間枠が規則より前かもしれない

使用量データはライブで流れるのではなく、バケットに集められて定期的に問い合わせられる。日次の合計には、ブロックを有効にする前の数分、前の時間のバックグラウンド同期、すでに進行中だった通信が含まれうる。累積の合計を一つの拒否されたリクエストと比べ、その差を漏れと呼ぶのは測定の誤りであり、ファイアウォールの所見ではない。

もっと短い期間と新しい開始点を使う。規則を変えた時刻を記録し、収集間隔を一つ待ってから、それ以降のデータだけを見る。規則の後に始まる期間で合計が上がり続けるなら、調べるべきものがある。上がらなくなれば、以前の増加は表示されていた履歴だった。

VPN サービスは設定したものではないかもしれない

VPN ファイアウォールは自分のサービスが運ぶ通信しか見えない。別の VPN アプリが起動して有効な接続を奪えば、そのインターフェースが設定に残っていても、フィルターは経路から外れる。アプリごとのバイパスリストも意図的に同じことをするし、アプリを除外したプロファイルは、そのアプリが動くなら漏れではない。

IPv4、IPv6、DNS も食い違うことがある。IPv4 を扱うフィルターが IPv6 やプライベート DNS リゾルバーを対象外にすれば、アプリの通信の一部は規則がまったく記述していない経路を通る。何かを書き換える前に、いま VPN を所有しているアプリと除外リストの内容を確認する。

adb shell settings get secure always_on_vpn_app
adb shell dumpsys connectivity
adb shell dumpsys netstats detail

新しいリクエストを一つ試し、四つの事実を書き留める

無害なリクエストを一つ選び、一度だけ実行して、四つの事実を書き留める。有効な規則、アプリの UID、ネットワークの状態、時刻である。次に、フィルターがそのフローを記録したか、宛先やゲートウェイが同じ接続を見たかを確かめる。両側で同一と確認された一つのフローが証拠であり、集計値は証拠ではない。

擁護できる結論は、何が拒否され、どの観測がそれを裏づけたかを名指しする。「カウンターが上がった」は所見ではなく、「使用量カウンターはすべて偽物だ」も所見ではない。OSI 層のスローガンでは決着しない。正直な答えは、カウンターとブロックが別のものを語りながら両方とも正しいということだ。Android の NetworkStatsManager のリファレンスは、これらのバケットがどう問い合わせられ、帰属されるかを記述している。

関連記事