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 以上核心版本的 Android 裝置 (原先隨 P 版本出貨) 必須使用以 eBPF 為基礎的網路流量監控記錄,而非 xt_qtaguid。新的基礎架構更具彈性且更易於維護,且不需要任何樹外核心程式碼。

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

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

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

新的 trafficController 設計是以每個 cgroup eBPF 篩選器和核心內的 xt_bpf netfilter 模組為基礎。這些 eBPF 篩選器會在封包 tx/rx 通過篩選器時套用。cgroup eBPF 篩選器位於傳輸層,負責根據 Socket 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 來標記/取消標記一個 Socket。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 服務。NetworkManagement 服務會根據裝置核心版本和第一個 API 級別,判斷是否已啟用 eBPF 工具,並選擇取得每個應用程式網路用量統計資料的正確模組。如果應用程式 SDK 級別為 28 以上,則會遭到 sepolicy 封鎖,無法存取 xt_qtaguid proc 檔案。

在 9 之後的下一個 Android 版本中,應用程式將無法存取這些 xt_qtaguid 程序檔案,我們會開始從新的 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 測試會強制在所有裝置上實作新模組。不過,如果裝置的核心版本高於 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/+/main/net/test/bpf_test.py

單元測試位於: