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. Legacy(左)和 eBPF(右)流量监控设计差异
新的trafficController
设计基于cgroup
eBPF 过滤器以及内核中的xt_bpf
netfilter 模块。这些 eBPF 过滤器在通过过滤器时应用于数据包 tx/rx。 cgroup
eBPF 过滤器位于传输层,负责根据套接字 UID 和用户空间设置对正确 UID 的流量进行计数。 xt_bpf
过滤器与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 内核配置测试有助于验证是否打开了正确的配置。
-
设备
MEM_LOCK
rlimit 必须设置为 8 MB 或更大。
旧版 xt_qtaguid 弃用流程
新的 eBPF 工具正在替换xt_qtaguid
模块和它所基于的xt_owner
模块。我们将开始从 Android 内核中删除xt_qtaguid
模块并禁用其不必要的配置。
在 Android 9 版本中,所有设备都开启了xt_qtaguid
模块,但是所有直接读取xt_qtaguid
模块 proc 文件的公共 API 都被移到了NetworkManagement
Service 中。根据设备内核版本和第一个 API 级别,网络管理服务知道是否打开了NetworkManagement
工具,并选择正确的模块来获取每个应用程序的网络使用统计信息。具有 SDK 级别 28 及更高级别的应用程序被 sepolicy 阻止访问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 master 获取补丁。确保您的实现通过了适用的 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/+/master/tests/tests/net/src/android/net/cts/TrafficStatsTest.java {: .external}
- https://android.googlesource.com/platform/cts/+/master/tests/tests/app.usage/src/android/app/usage/cts/NetworkUsageStatsTest.java {: .external}
- https://android.googlesource.com/platform/system/netd/+/master/tests/bpf_base_test.cpp {: .external}
VTS 测试位于https://android.googlesource.com/kernel/tests/+/master/net/test/bpf_test.py 。
单元测试位于: