eBPF 流量監控

eBPF 網路流量工具會結合核心和使用者空間實作項目,監控裝置自上次啟動以來的網路用量。這項功能提供額外功能,例如插座標記、區分前景/背景流量,以及根據手機狀態,透過每個 UID 的防火牆封鎖應用程式的網路存取權。工具收集的統計資料會儲存在名為 eBPF maps 的核心資料結構中,而 NetworkStatsService 等服務會使用這些結果,提供自上次啟動以來持續性的流量統計資料。

範例和來源

使用者空間的變更主要在 system/netdframework/base 專案中。開發作業是在 Android 開放原始碼計畫中進行,因此 Android 開放原始碼計畫程式碼一律會保持最新狀態。來源主要位於 system/netd/server/TrafficController*system/netd/bpfloadersystem/netd/libbpf/framework/base/system/core 也進行了必要的架構變更。

實作

從 Android 9 開始,搭載核心 4.9 以上版本且原先隨附 P 版本的 Android 裝置,必須使用 eBPF 型網路流量監控會計系統,而非 xt_qtaguid。新基礎架構更具彈性,也更容易維護,而且不需要任何樹外核心程式碼。

圖 1 說明舊版和 eBPF 流量監控之間的主要設計差異。

舊版和 eBPF 流量監控設計差異

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

新的 trafficController 設計是以每個 cgroup eBPF 篩選器為基礎,以及核心內的 xt_bpf netfilter 模組。這些 eBPF 篩選器會在封包通過篩選器時套用至封包傳輸/接收。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 以上版本) 為基礎,則實作新的 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 核心設定測試有助於確認是否已啟用正確的設定。

舊版 xt_qtaguid 淘汰程序

新的 eBPF 工具將取代 xt_qtaguid 模組和以該模組為基礎的 xt_owner 模組。我們將開始從 Android 核心移除 xt_qtaguid 模組,並停用不必要的設定。

在 Android 9 版本中,所有裝置都會開啟 xt_qtaguid 模組,但直接讀取 xt_qtaguid 模組 proc 檔案的所有公開 API 都會移至 NetworkManagement 服務。NetworkManagement 服務會根據裝置核心版本和第一個 API 級別,判斷 eBPF 工具是否已開啟,並選擇正確的模組來取得每個應用程式的網路用量統計資料。SDK 級別 28 以上的應用程式會受到 sepolicy 阻擋,無法存取 xt_qtaguid proc 檔案。

在 Android 9 之後的下一個版本中,應用程式將完全無法存取這些 xt_qtaguid proc 檔案,我們也會開始從新的 Android 通用核心中移除 xt_qtaguid 模組。移除後,我們會更新該核心版本的 Android 基本設定,明確關閉 xt_qtaguid 模組。當 Android 版本的最低核心版本需求為 4.9 以上時,xt_qtaguid 模組將完全淘汰。

在 Android 9 版本中,只有隨 Android 9 版本推出的裝置才需要具備新的 eBPF 功能。如果裝置出廠時搭載的 Kernel 支援 eBPF 工具,建議您在升級至 Android 9 時,更新至新的 eBPF 功能。沒有 CTS 測試可強制執行這項更新。

驗證

您應定期從 Android 通用核心和 Android AOSP main 取得修補程式。請確保導入作業通過適用的 VTS 和 CTS 測試、netd_unit_test,以及 libbpf_test

測試

請使用核心 net_tests,確保已啟用必要功能,並回溯移植必要的核心修補程式。這些測試已整合為 Android 9 版本 VTS 測試的一部分。system/netd/ 中有一些單元測試 (netd_unit_testlibbpf_test)。netd_integration_test 中有一些測試,可驗證新工具的整體行為。

CTS 和 CTS 驗證器

由於 Android 9 版本支援這兩個流量監控模組,因此沒有 CTS 測試會強制在所有裝置上實作新模組。不過,如果裝置的 Kernel 版本高於 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 和介面,以及擁有者 UID 相符情形。

測試地點

CTS 測試位於:

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

單元測試位於: