eBPF トラフィック モニタリング

eBPF ネットワーク トラフィック ツールは、カーネルとユーザー空間の実装の組み合わせを使用して、前回のデバイスの起動以降のデバイスにおけるネットワーク使用量を監視します。ソケットのタグ付け、フォアグラウンド / バックグラウンド トラフィックの分離、UID ごとのファイアウォールなどの追加機能を備え、スマートフォンのステータスに応じてアプリをネットワーク アクセスからブロックします。ツールから収集された統計情報は、eBPF maps というカーネルデータ構造に格納されます。NetworkStatsService などのサービスがこの結果を使用して、前回の起動以降の永続的なトラフィック統計情報を提供します。

例とソース

ユーザー空間の変更は主に system/netd プロジェクトと framework/base プロジェクトで行われます。開発は AOSP で行われているため、AOSP コードは常に最新のものになります。ソースは主に system/netd/server/TrafficController*system/netd/bpfloadersystem/netd/libbpf/ にあります。一部の必要なフレームワークの変更は framework/base/system/core にもあります。

実装

Android 9 以降、カーネル 4.9 以上で動作し元々 P リリースで出荷された Android デバイスは、xt_qtaguid ではなく eBPF ベースのネットワーク トラフィック モニタリング アカウンティングを使用する必要があります。新しいインフラストラクチャは柔軟性とメンテナンス性が高く、ツリー外のカーネルコードを必要としません。

以前のトラフィック モニタリングと eBPF トラフィック モニタリングの設計上の主な違いを図 1 に示します。

以前のトラフィック モニタリングと eBPF トラフィック モニタリングの設計上の違い

図 1. 以前のトラフィック モニタリング(左)と eBPF トラフィック モニタリング(右)の設計上の違い

新しい trafficController 設計は、cgroup ごとの eBPF フィルタと、カーネル内部の xt_bpf netfilter モジュールに基づいています。これらの eBPF フィルタは、フィルタを通過するときにパケット tx/rx に適用されます。cgroup eBPF フィルタはトランスポート層にあり、ソケット UID とユーザー空間の設定に応じて、正しい UID に対するトラフィックをカウントします。xt_bpf netfilter は、bw_raw_PREROUTINGbw_mangle_POSTROUTING のチェーンでフックされ、正しいインターフェースに対するトラフィックをカウントします。

起動時に、ユーザー空間プロセス trafficController はデータ収集に使用する eBPF マップを作成し、sys/fs/bpf ですべてのマップを仮想ファイルとして固定します。次に、特権プロセス bpfloader は、プリコンパイルされた eBPF プログラムをカーネルに読み込んで、正しい cgroup にアタッチします。すべてのトラフィックに 1 つのルート cgroup が存在するため、デフォルトでは、すべてのプロセスをその cgroup に含める必要があります。

実行時に、trafficControllertraffic_cookie_tag_maptraffic_uid_counterSet_map に書き込むことでソケットのタグ付け / タグ解除を行うことができます。NetworkStatsService は、traffic_tag_stats_maptraffic_uid_stats_maptraffic_iface_stats_map からトラフィック統計データを読み取ることができます。トラフィック統計情報収集機能の他に、trafficControllercgroup の eBPF フィルタは、スマートフォンの設定に応じて特定の UID からのトラフィックをブロックします。UID ベースのネットワーキング トラフィックのブロック機能はカーネル内部の xt_owner モジュールの置き換えであり、詳細モードは、traffic_powersave_uid_maptraffic_standby_uid_maptraffic_dozable_uid_map に書き込むことで構成できます。

新しい実装は以前の xt_qtaguid モジュール実装に準拠しているため、TrafficControllerNetworkStatsService は以前の実装か新しい実装のいずれかで実行されます。アプリが公開 API を使用している場合は、バックグラウンドで xt_qtaguid ツールと eBPF ツールのいずれを使用しても違いは生じません。

デバイス カーネルが Android 共通カーネル 4.9(SHA 39c856663dcc81739e52b02b77d6af259eb838f6 以上)に基づいている場合、新しい eBPF ツールを実装するために、HAL、ドライバ、またはカーネルコードを変更する必要はありません。

要件

  1. カーネル設定では、次の設定を有効にしておく必要があります。

    1. CONFIG_CGROUP_BPF=y
    2. CONFIG_BPF=y
    3. CONFIG_BPF_SYSCALL=y
    4. CONFIG_NETFILTER_XT_MATCH_BPF=y
    5. CONFIG_INET_UDP_DIAG=y

    VTS カーネル構成テストは、正しい構成が有効になっていることを確認する際に有用です。

  2. デバイス MEM_LOCK rlimit を 8 MB 以上に設定する必要があります。

以前の xt_qtaguid のサポート終了プロセス

新しい eBPF ツールは、xt_qtaguid モジュールとベースになっている xt_owner モジュールを置き換えます。まず、Android カーネルから xt_qtaguid モジュールを削除し、必要のない構成を無効にします。

Android 9 リリースでは、xt_qtaguid モジュールがすべてのデバイスでオンになりますが、xt_qtaguid モジュール proc ファイルを読み取るすべての公開 API が NetworkManagement Service に移行されます。NetworkManagement Service は、デバイスのカーネル バージョンと初期 API レベルに応じて eBPF ツールがオンになっているかどうかを把握し、アプリのネットワーク使用状況の統計情報ごとに適切なモジュールを選択します。SDK レベル 28 以上のアプリは、sepolicy によって xt_qtaguid proc ファイルに対するアクセスがブロックされます。

9 以降の Android リリースでは、このような xt_qtaguid proc ファイルに対するアプリからのアクセスが完全にブロックされます。新しい Android 共通カーネルから xt_qtaguid モジュールが削除されるようになります。削除した後、そのカーネル バージョン用の Android ベース構成を更新して xt_qtaguid モジュールを明示的にオフにします。xt_qtaguid モジュールは、Android リリースの最小カーネル バージョン要件が 4.9 以上に達した段階で、完全にサポートを終了します。

Android 9 リリースでは、Android 9 リリースを搭載するデバイスのみが新しい eBPF 機能を必要とします。eBPF ツールをサポートできるカーネルが搭載されているデバイスでは、Android 9 リリースにアップグレードする際に、新しい eBPF 機能にアップデートすることをおすすめします。このアップデートを適用する CTS テストはありません。

検証

Android 共通カーネルと Android AOSP マスターからパッチを定期的に取得する必要があります。実装が、該当する VTS テストと CTS テスト、netd_unit_testlibbpf_test に合格していることを確認します。

テスト

必要な機能が有効になっており、必要なカーネルパッチがバックポートされていることを確認する、カーネル net_tests があります。テストは、Android 9 リリース VTS テストの一部として統合されています。単体テストは、system/netd/netd_unit_testlibbpf_test)にあります。netd_integration_test には、新しいツールの全体的な動作を検証するためのテストがあります。

CTS と CTS 検証ツール

Android 9 リリースでは両方のトラフィック モニタリング モジュールがサポートされているため、新しいモジュールの実装をすべてのデバイスに強制する CTS テストはありません。ただし、元々 Android 9 リリース(すなわち初期 API レベルが 28 以上)が搭載された、カーネル バージョン 4.9 以降のデバイスの場合、新しいモジュールが正しく構成されていることを確認する CTS テストが GSI にあります。TrafficStatsTestNetworkUsageStatsTestCtsNativeNetTestCases などの古い CTS テストを使用して、古い UID モジュールとの動作の整合性を確認できます。

手動テスト

system/netd/ には、単体テストがあります(netd_unit_testnetd_integration_testlibbpf_test)。ステータスを手動で確認するための dumpsys サポートがあります。コマンド dumpsys netd は、trafficController モジュールの基本ステータスと、eBPF が正しくオンになっているかどうかを表示します。eBPF がオンの場合、コマンド dumpsys netd trafficcontroller は、タグ付きのソケット情報、タグごとの統計情報、UID と iface、所有者 UID の一致など、各 eBPF マップの詳細を表示します。

テストの場所

CTS テストは次の場所にあります。

VTS テストは https://android.googlesource.com/kernel/tests/+/master/net/test/bpf_test.py にあります。

単体テストは次の場所にあります。