eBPF 網路流量工具使用核心和用戶空間實現的組合來監視自上次裝置啟動以來裝置上的網路使用情況。它提供了附加功能,例如套接字標記、分離前台/後台流量以及按 UID 防火牆,以根據手機狀態阻止應用程式存取網路。從該工具收集的統計資料儲存在稱為eBPF maps
的核心資料結構中,結果由NetworkStatsService
等服務使用,以提供自上次啟動以來的持久流量統計資料。
範例和來源
使用者空間的變化主要是在system/netd
和framework/base
專案。開發是在 AOSP 中完成的,因此 AOSP 程式碼將始終是最新的。原始碼主要位於system/netd/server/TrafficController*
、 system/netd/bpfloader
和system/netd/libbpf/
。一些必要的框架變更也在framework/base/
和system/core
中。
執行
從 Android 9 開始,運行在內核 4.9 或更高版本上且最初隨 P 版本一起提供的 Android 設備必須使用基於 eBPF 的網路流量監控計費,而不是xt_qtaguid
。新的基礎設施更加靈活,更易於維護,並且不需要任何樹外核心程式碼。
傳統流量監控和 eBPF 流量監控之間的主要設計差異如圖 1 所示。
圖 1.傳統(左)與 eBPF(右)流量監控設計差異
新的trafficController
設計基於cgroup
eBPF過濾器以及核心內的xt_bpf
netfilter模組。當封包 tx/rx 通過過濾器時,這些 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
來標記/取消標記套接字。 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
模組 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_test
和libbpf_test
。
測試
核心 net_tests可確保您開啟所需的功能並向後移植所需的核心修補程式。這些測試已集成為 Android 9 版本 VTS 測試的一部分。 system/netd/
中有一些單元測試( netd_unit_test
和libbpf_test
)。 netd_integration_test
中有一些測試來驗證新工具的整體行為。
CTS 和 CTS 驗證器
由於 Android 9 版本支援這兩個流量監控模組,因此沒有 CTS 測試來強制在所有裝置上實施新模組。但對於最初隨 Android 9 版本一起發布的核心版本高於 4.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