eBPF 流量監控

eBPF 網絡流量工具結合使用內核和用戶空間實現來監控自上次設備啟動以來設備上的網絡使用情況。它提供了附加功能,例如套接字標記、分離前台/後台流量和按 UID 的防火牆,以根據手機狀態阻止應用程序訪問網絡。從該工具收集的統計信息存儲在稱為eBPF maps的內核數據結構中,並且NetworkStatsService等服務使用結果來提供自上次啟動以來的持久流量統計信息。

示例和來源

用戶空間的變化主要在system/netdframework/base項目中。在 AOSP 中進行開發,因此 AOSP 代碼將始終是最新的。源碼主要位於system/netd/server/TrafficController*system/netd/bpfloadersystem/netd/libbpf/ 。一些必要的框架更改也在framework/base/system/core中。

執行

從 Android 9 開始,在內核 4.9 或更高版本上運行且最初附帶 P 版本的 Android 設備必須使用基於 eBPF 的網絡流量監控記帳而不是xt_qtaguid 。新的基礎架構更靈活、更易於維護,並且不需要任何樹外內核代碼。

傳統和 eBPF 流量監控之間的主要設計差異如圖 1 所示。

Legacy 和 eBPF 流量監控設計差異

圖 1. Legacy(左)和 eBPF(右)流量監控設計差異

新的trafficController設計基於cgroup eBPF 過濾器以及內核中的xt_bpf netfilter 模塊。這些 eBPF 過濾器在通過過濾器時應用於數據包 tx/rx。 cgroup eBPF 過濾器位於傳輸層,負責根據套接字 UID 和用戶空間設置對正確 UID 的流量進行計數。 xt_bpf過濾器與bw_raw_PREROUTINGbw_mangle_POSTROUTING鏈掛鉤,並負責對正確接口的流量進行計數。

在啟動時,用戶空間進程trafficController創建用於數據收集的 eBPF 映射,並將所有映射作為虛擬文件固定在sys/fs/bpf中。然後特權進程bpfloader將預編譯的 eBPF 程序加載到內核中,並將其附加到正確的cgroup中。所有流量都有一個根cgroup ,因此默認情況下所有進程都應包含在該cgroup中。

在運行時, trafficController可以通過寫入traffic_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 或更高版本),則無需修改 HAL、驅動程序或內核代碼即可實現新的 eBPF 工具。

要求

  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 中。根據設備內核版本和第一個 API 級別,網絡管理服務知道是否打開了NetworkManagement工具,並選擇正確的模塊來獲取每個應用程序的網絡使用統計信息。具有 SDK 級別 28 及更高級別的應用程序被 sepolicy 阻止訪問xt_qtaguid proc 文件。

在 9 之後的下一個 Android 版本中,應用程序對這些xt_qtaguid proc 文件的訪問將被完全阻止,我們將開始從新的 Android 通用內核中刪除xt_qtaguid模塊。刪除後,我們將更新該內核版本的 Android 基本配置,以明確關閉xt_qtaguid模塊。當 Android 版本的最低內核版本要求為 4.9 或更高版本時,將完全棄用xt_qtaguid模塊。

在 Android 9 版本中,只有搭載 Android 9 版本的設備才需要具有新的 eBPF 功能。對於帶有可支持 eBPF 工具的內核的設備,我們建議在升級到 Android 9 版本時將其更新為新的 eBPF 功能。沒有 CTS 測試來強制執行該更新。

驗證

您應該定期從 Android 通用內核和 Android AOSP master 獲取補丁。確保您的實現通過了適用的 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 測試強制在所有設備上實施新模塊。但對於內核版本高於 4.9 且最初隨 Android 9 發行版(即第一個 API 級別 >= 28)的設備,在 GSI 上進行 CTS 測試以驗證新模塊是否配置正確。舊的 CTS 測試,例如TrafficStatsTestNetworkUsageStatsTestCtsNativeNetTestCases可用於驗證行為是否與舊的 UID 模塊一致。

手動測試

system/netd/中有一些單元測試( netd_unit_testnetd_integration_testlibbpf_test )。 dumpsys 支持手動檢查狀態。命令dumpsys netd顯示trafficController模塊的基本狀態以及 eBPF 是否正確開啟。如果打開了 eBPF,命令dumpsys netd trafficcontroller顯示每個 eBPF 映射的詳細內容,包括標記的套接字信息、每個標記的統計信息、UID 和 iface 以及所有者 UID 匹配。

測試地點

CTS 測試位於:

VTS 測試位於https://android.googlesource.com/kernel/tests/+/master/net/test/bpf_test.py

單元測試位於: