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 所示。

傳統流量監控與 eBPF 流量監控設計差異

圖 1.傳統(左)與 eBPF(右)流量監控設計差異

新的trafficController設計基於cgroup eBPF過濾器以及核心內的xt_bpf netfilter模組。當封包 tx/rx 通過過濾器時,這些 eBPF 過濾器將應用於封包 tx/rx。 cgroup eBPF 過濾器位於傳輸層,負責根據套接字 UID 以及用戶空間設置,根據正確的 UID 計算流量。 xt_bpf netfilter 掛在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 核心配置測試非常有用。

舊版 xt_qtaguid 棄用流程

新的 eBPF 工具正在取代xt_qtaguid模組及其所基於的xt_owner模組。我們將開始從 Android 核心中刪除xt_qtaguid模組並停用其不必要的配置。

在 Android 9 版本中, xt_qtaguid模組在所有裝置中開啟,但所有直接讀取xt_qtaguid模組 proc 檔案的公共 API 均移至NetworkManagement Service 中。根據裝置核心版本和第一個 API 級別, NetworkManagement服務知道 eBPF 工具是否已打開,並選擇正確的模組來取得每個應用程式的網路使用統計資料。 SEPolicy 會阻止 SDK 等級 28 及更高等級的應用程式存取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 主核心取得修補程式。確保您的實作通過適用的 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 版本一起發布的核心版本高於 4.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/+/main/net/test/bpf_test.py

單元測試位於: