eBPF 網路流量工具會結合核心和使用者空間實作,監控裝置自上次開機以來的網路用量。它提供額外功能,例如通訊端點標記、分隔前景/背景流量,以及依 UID 設定的防火牆,可根據手機狀態封鎖應用程式的網路存取權。從這項工具收集的統計資料會儲存在名為 eBPF maps
的核心資料結構中,而結果會由 NetworkStatsService
等服務使用,用於提供自上次啟動後持續累積的流量統計資料。
範例和來源
使用者空間的變更主要發生在 system/netd
和 framework/base
專案中。開發作業會在 Android 開放原始碼計畫中進行,因此 Android 開放原始碼計畫的程式碼一律都是最新版本。來源主要位於 system/netd/server/TrafficController*
、system/netd/bpfloader
和 system/netd/libbpf/
。framework/base/
和 system/core
也需要進行一些必要的架構變更。
實作
自 Android 9 起,在核心 4.9 以上版本中且最初隨附於 P 版本的 Android 裝置,「必須」使用以 eBPF 為基礎的網路流量監控計算功能,而不是使用 xt_qtaguid
。新的基礎架構更有彈性、更容易維護,也不需要任何樹狀核心程式碼。
圖 1 說明舊版和 eBPF 流量監控之間的主要設計差異。
圖 1. 舊版 (左) 和 eBPF (右) 流量監控設計的差異
新的 trafficController
設計是以每個 cgroup
eBPF 篩選器以及核心內的 xt_bpf
netfilter 模組為基礎。這些 eBPF 篩選器通過篩選器後,就會套用至封包 tx/rx。cgroup
eBPF 篩選器位於傳輸層,負責根據通訊端 UID 和使用者空間設定,計算正確的 UID 的流量。xt_bpf
netfilter 會連結至 bw_raw_PREROUTING
和 bw_mangle_POSTROUTING
鏈結,並負責針對正確介面計算流量。
在啟動期間,使用者空間程序 trafficController
會建立用於資料收集的 eBPF 對應項目,並將所有對應項目釘選為 sys/fs/bpf
中的虛擬檔案。接著,特權程序 bpfloader
會將預先編譯的 eBPF 程式載入至核心,並將其附加至正確的 cgroup
。所有流量都有一個根目錄 cgroup
,因此根據預設,所有程序都應包含在該 cgroup
中。
在執行期間,trafficController
可以透過寫入 traffic_cookie_tag_map
和 traffic_uid_counterSet_map
來標記/取消標記一個 Socket。NetworkStatsService
可讀取 traffic_tag_stats_map
、traffic_uid_stats_map
和 traffic_iface_stats_map
的流量統計資料。除了流量統計資料收集函式,trafficController
和 cgroup
eBPF 篩選器也負責根據手機設定封鎖特定 UID 的流量。以 UID 為基礎的網路流量封鎖功能可取代核心中的 xt_owner
模組,您可以透過寫入 traffic_powersave_uid_map
、traffic_standby_uid_map
和 traffic_dozable_uid_map
來設定詳細模式。
新實作項目遵循舊版 xt_qtaguid
模組實作項目,因此 TrafficController
和 NetworkStatsService
會搭配舊版或新版實作項目執行。如果應用程式使用公用 API,無論在背景使用 xt_qtaguid
或 eBPF 工具,都不會發生任何差異。
如果裝置核心是以 Android 通用核心 4.9 (SHA 39c856663dcc81739e52b02b77d6af259eb838f6 以上版本) 為基礎,則不必修改 HAL、驅動程式或核心程式碼,即可實作新的 eBPF 工具。
需求條件
核心設定「必須」啟用下列設定:
CONFIG_CGROUP_BPF=y
CONFIG_BPF=y
CONFIG_BPF_SYSCALL=y
CONFIG_NETFILTER_XT_MATCH_BPF=y
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_test
和 libbpf_test
。
測試
核心 net_tests 可確保您已啟用必要功能,並回移必要的核心修補程式。這些測試已整合至 Android 9 發布版本的 VTS 測試。system/netd/
中有一些單元測試 (netd_unit_test
和 libbpf_test
)。netd_integration_test
中有一些測試,可驗證新工具的整體行為。
CTS 和 CTS 驗證工具
由於 Android 9 版本支援兩種流量監控模組,因此目前沒有 CTS 測試可強制在所有裝置上實作新模組。不過,如果裝置的核心版本高於 4.9,且最初隨 Android 9 版本發布 (也就是第一個 API 級別 >= 28),則 GSI 會進行 CTS 測試,驗證新模組是否正確設定。您可以使用舊版 CTS 測試 (例如 TrafficStatsTest
、NetworkUsageStatsTest
和 CtsNativeNetTestCases
) 驗證行為是否與舊版 UID 模組一致。
手動測試
system/netd/
中有一些單元測試 (netd_unit_test
、netd_integration_test
和 libbpf_test
)。dumpsys 支援手動檢查狀態。dumpsys netd
指令會顯示 trafficController
模組的基本狀態,以及 eBPF 是否已正確開啟。如果 eBPF 已開啟,指令 dumpsys netd trafficcontroller
會顯示每個 eBPF 地圖的詳細內容,包括標記通訊端資訊、每個標記的統計資料、UID 和 iface,以及擁有者 UID 比對。
測試地點
CTS 測試位於:
- https://android.googlesource.com/platform/cts/+/main/tests/tests/net/src/android/net/cts/TrafficStatsTest.java
- https://android.googlesource.com/platform/cts/+/main/tests/tests/app.usage/src/android/app/usage/cts/NetworkUsageStatsTest.java
- https://android.googlesource.com/platform/system/netd/+/main/tests/bpf_base_test.cpp
VTS 測試位於 https://android.googlesource.com/kernel/tests/+/main/net/test/bpf_test.py。
單元測試位於:
- https://android.googlesource.com/platform/system/netd/+/main/libbpf/BpfNetworkStatsTest.cpp
- https://android.googlesource.com/platform/system/netd/+/main/server/TrafficControllerTest.cpp