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
。新的基础架构更灵活、更易于维护,并且不需要任何树外内核代码。
图 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
来标记/取消标记套接字。 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
服务。根据设备内核版本和首次 API 级别,NetworkManagement
服务知道是否已启用 eBPF 工具,并选择正确的模块来获取每个应用的联网使用情况统计信息。SDK 级别为 28 及更高级别的应用被 sepolicy 阻止访问 xt_qtaguid
proc 文件。
在 Android 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 测试强制在所有设备上实施新模块。但是,对于内核版本高于 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。
单元测试位于