eBPF 網(wǎng)絡(luò)流量工具結(jié)合使用內(nèi)核與用戶空間實(shí)現(xiàn)來監(jiān)控設(shè)備自上次啟動(dòng)以來的網(wǎng)絡(luò)使用情況。它提供了額外的功能(如套接字標(biāo)記、分離前臺(tái)/后臺(tái)流量,以及按 UID 劃分的防火墻),以根據(jù)手機(jī)狀態(tài)阻止應(yīng)用訪問網(wǎng)絡(luò)。從該工具收集的統(tǒng)計(jì)數(shù)據(jù)存儲(chǔ)在稱為 eBPF maps
的內(nèi)核數(shù)據(jù)結(jié)構(gòu)中,并且相應(yīng)結(jié)果由NetworkStatsService
等服務(wù)用來提供自設(shè)備上次啟動(dòng)以來的持久流量統(tǒng)計(jì)數(shù)據(jù)。
示例和來源
用戶空間更改主要在 system/netd
和framework/base
項(xiàng)目中。開發(fā)工作在 AOSP 中完成,因此 AOSP 代碼將始終保持最新狀態(tài)。源代碼主要位于system/netd/server/TrafficController*
、system/netd/bpfloader
和system/netd/libbpf/
中。此外,一些必要的框架變更也在framework/base/
和system/core
中。
實(shí)現(xiàn)
從 Android 9 開始,內(nèi)核版本為 4.9 或更高且最初搭載了 Android P 版本的 Android 設(shè)備必須使用基于 eBPF 的網(wǎng)絡(luò)流量監(jiān)控記帳模塊,而不是 xt_qtaguid
。新的基礎(chǔ)架構(gòu)更靈活且更易于維護(hù),并且不需要任何外部內(nèi)核代碼。
舊版流量監(jiān)控和 eBPF 流量監(jiān)控之間的主要設(shè)計(jì)差異如圖 1 所示。
圖 1.舊版流量監(jiān)控(左)和 eBPF 流量監(jiān)控(右)的設(shè)計(jì)差異
新的 trafficController
設(shè)計(jì)基于cgroup
級(jí)的 eBPF 過濾器以及內(nèi)核中的xt_bpf
netfilter 模塊。這些 eBPF 過濾器在收發(fā)數(shù)據(jù)包時(shí)應(yīng)用,數(shù)據(jù)包需要通過這些過濾器。cgroup
eBPF 過濾器位于傳輸層,負(fù)責(zé)根據(jù)套接字 UID 以及用戶空間設(shè)置對(duì)正確的 UID 計(jì)算流量。xt_bpf
netfilter 掛接在bw_raw_PREROUTING
和bw_mangle_POSTROUTING
鏈上,負(fù)責(zé)對(duì)正確的接口計(jì)算流量。
在啟動(dòng)時(shí),用戶空間進(jìn)程 trafficController
會(huì)創(chuàng)建用于收集數(shù)據(jù)的 eBPF 映射,并將所有映射作為虛擬文件固定在sys/fs/bpf
。然后,特權(quán)進(jìn)程bpfloader
將預(yù)編譯的 eBPF 程序加載到內(nèi)核中,并將其附加到正確的cgroup
。所有流量都對(duì)應(yīng)于同一個(gè)根cgroup
,因此默認(rèn)情況下,所有進(jìn)程都應(yīng)包含在該cgroup
中。
在運(yùn)行時(shí),trafficController
可以通過將數(shù)據(jù)寫入traffic_cookie_tag_map
和traffic_uid_counterSet_map
來標(biāo)記/取消標(biāo)記套接字。NetworkStatsService
可以從traffic_tag_stats_map
、traffic_uid_stats_map
和traffic_iface_stats_map
中讀取流量統(tǒng)計(jì)數(shù)據(jù)。除了流量統(tǒng)計(jì)數(shù)據(jù)收集功能之外,trafficController
和cgroup
eBPF 過濾器還負(fù)責(zé)根據(jù)手機(jī)設(shè)置屏蔽來自某些 UID 的流量。基于 UID 的網(wǎng)絡(luò)流量屏蔽功能取代了內(nèi)核中的xt_owner
模塊,并且可以通過將數(shù)據(jù)寫入traffic_powersave_uid_map
、traffic_standby_uid_map
和traffic_dozable_uid_map
來配置詳細(xì)模式。
新實(shí)現(xiàn)遵循舊版 xt_qtaguid
模塊實(shí)現(xiàn),因此TrafficController
和NetworkStatsService
將使用舊版實(shí)現(xiàn)或新實(shí)現(xiàn)運(yùn)行。如果應(yīng)用使用公共 API,那么無論在后臺(tái)使用xt_qtaguid
還是 eBPF 工具,應(yīng)該沒有任何區(qū)別。
如果設(shè)備內(nèi)核基于 Android 通用內(nèi)核 4.9(
SHA39c856663dcc81739e52b02b77d6af259eb838f6 或更高版本),則無需修改 HAL、驅(qū)動(dòng)程序或內(nèi)核代碼,即可實(shí)現(xiàn)新的 eBPF 工具。
要求
-
內(nèi)核配置必須開啟以下配置:
驗(yàn)證是否已開啟正確配置時(shí),VTS 內(nèi)核配置測試非常有用。
-
CONFIG_CGROUP_BPF=y
-
CONFIG_BPF=y
-
CONFIG_BPF_SYSCALL=y
-
CONFIG_NETFILTER_XT_MATCH_BPF=y
-
CONFIG_INET_UDP_DIAG=y
-
-
設(shè)備
MEM_LOCK
資源限制必須設(shè)為 8 MB 或更多。
舊版 xt_qtaguid 棄用過程
新的 eBPF 工具正在逐步取代 xt_qtaguid
模塊以及它所基于的xt_owner
模塊。我們將開始從 Android 內(nèi)核中移除xt_qtaguid
模塊,并停用不必要的配置。
在 Android 9 版本中,xt_qtaguid
模塊在所有設(shè)備上都處于開啟狀態(tài),但直接讀取xt_qtaguid
模塊 proc 文件的所有公共 API 都移到了NetworkManagement
服務(wù)中。根據(jù)設(shè)備內(nèi)核版本和初始 API 級(jí)別,NetworkManagement
服務(wù)能夠知道 eBPF 工具是否處于開啟狀態(tài),并選擇正確的模塊來獲取每個(gè)應(yīng)用的網(wǎng)絡(luò)使用情況統(tǒng)計(jì)數(shù)據(jù)。sepolicy 會(huì)阻止 SDK 級(jí)別為 28 及以上的應(yīng)用訪問xt_qtaguid
proc 文件。
在 Android 9 之后的下一個(gè)版本中,我們將完全阻止應(yīng)用訪問這些 xt_qtaguid
proc 文件,并開始從新的 Android 通用內(nèi)核中移除xt_qtaguid
模塊。移除該模塊后,我們將更新相應(yīng)內(nèi)核版本的 Android 基礎(chǔ)配置,以明確關(guān)閉xt_qtaguid
模塊。當(dāng) Android 版本的最低內(nèi)核版本要求為 4.9 或更高時(shí),我們將徹底棄用xt_qtaguid
模塊。
在 Android 9 版本中,只有搭載 Android 9 版本的設(shè)備才需要具備新的 eBPF 功能。如果設(shè)備搭載的內(nèi)核可以支持 eBPF 工具,我們建議在升級(jí)到 Android 9 版本時(shí),將設(shè)備更新為采用新的 eBPF 功能。沒有強(qiáng)制執(zhí)行該更新的 CTS 測試。
驗(yàn)證
您應(yīng)該定期從 Android 通用內(nèi)核和 Android AOSP 主分支獲取補(bǔ)丁程序。請(qǐng)確保您的實(shí)現(xiàn)通過適用的 VTS 和 CTS 測試,即 netd_unit_test
和libbpf_test
。
測試
提供了內(nèi)核 net_tests,用來確保您開啟了必需的功能,并向后移植了必需的內(nèi)核補(bǔ)丁程序。這些測試已集成到 Android 9 版本 VTS 測試中。system/netd/
中有一些單元測試(netd_unit_test
和libbpf_test
)。netd_integration_test
中有一些驗(yàn)證新工具整體行為的測試。
CTS 和 CTS 驗(yàn)證程序
由于這兩個(gè)流量監(jiān)控模塊在 Android 9 版本中都得到支持,因此沒有強(qiáng)制在所有設(shè)備上實(shí)現(xiàn)新模塊的 CTS 測試。不過,對(duì)于內(nèi)核版本高于 4.9 且最初搭載了 Android 9 版本(即,初始 API 級(jí)別大于等于 28)的設(shè)備,提供了基于 GSI 的 CTS 測試,用于驗(yàn)證是否正確配置了新模塊。舊的 CTS 測試(如TrafficStatsTest
、NetworkUsageStatsTest
和CtsNativeNetTestCases
)可用于驗(yàn)證新模塊的行為是否與舊的 UID 模塊一致。
手動(dòng)測試
system/netd/
中有一些單元測試(netd_unit_test
、netd_integration_test
和libbpf_test
)。此外,還提供了 dumpsys 支持,以便手動(dòng)檢查狀態(tài)。dumpsys netd
命令可顯示trafficController
模塊的基本狀態(tài)以及是否正確開啟了 eBPF。如果 eBPF 處于開啟狀態(tài),dumpsys netd trafficcontroller
命令會(huì)顯示每個(gè) eBPF 映射的詳細(xì)內(nèi)容,包括帶標(biāo)記的套接字信息、每個(gè)標(biāo)記的統(tǒng)計(jì)數(shù)據(jù)、UID 和 iface,以及所有者 UID 匹配項(xiàng)。
測試所在位置
CTS 測試位于以下位置:
-
https://android.googlesource.com/platform/cts/+/master/tests/tests/net/src/android/net/cts/TrafficStatsTest.JAVA
-
https://android.googlesource.com/platform/cts/+/master/tests/tests/App.usage/src/android/app/usage/cts/NetworkUsageStatsTest.java
-
https://android.googlesource.com/platform/system/netd/+/master/tests/bpf_base_test.cpp
VTS 測試位于
https://android.googlesource.com/kernel/tests/+
/master/net/test/bpf_test.py。
單元測試位于以下位置:
-
https://android.googlesource.com/platform/system/netd/+/master/libbpf/BpfNetworkStatsTest.cpp
-
https://android.googlesource.com/platform/system/netd/+/master/server/TrafficControllerTest.cpp