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 篩選器通過篩選器後,就會套用至封包 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 來標記/取消標記一個 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 模組處理檔案的所有公開 API 會移至 NetworkManagement Service。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

單元測試位於: