PawseySC/ebpf-traffic-meter

GitHub: PawseySC/ebpf-traffic-meter

基于eBPF的按用户网络流量计量工具,支持IPv4/IPv6双栈,按UID记录流量方向、字节量和通信IP。

Stars: 11 | Forks: 1

# eBPF 流量计量器 - [eBPF 流量计量器](#ebpf-traffic-meter) - [eBPF](#ebpf) - [环境要求](#requirements) - [Fedora/RHEL](#fedorarhel) - [Ubuntu/Debian](#ubuntudebian) - [SLES](#sles) - [构建](#building) - [用法](#usage) - [参数](#arguments) - [示例](#examples) - [输出](#output) - [字段](#fields) - [输出示例](#example-output) - [非跟踪 IP 过滤](#untracked-ip-filtering) - [架构](#architecture) - [许可证](#license) - [参考](#references) 一款使用 eBPF 的按用户网络流量监控工具。捕获所有 IPv4 和 IPv6 网络流量,并将其记录到按用户区分的文件中,包含方向、字节数和 IP 地址。 可以指定要忽略的网络地址,例如仅捕获外部网络流量。 ## eBPF eBPF 是一项源于 Linux 内核的技术,可以在特权上下文(如操作系统内核)中运行沙盒程序。 它用于安全高效地扩展内核的功能,而无需更改内核源代码或加载内核模块。 它可以跟踪事件并在任何级别与操作系统交互,包括文件系统、GPU、网络和任何系统调用,参见[参考](#references)。 ## 环境要求 - Linux 内核 5.8+(用于 ring buffer 支持) - clang(用于 BPF 编译) - libbpf、libelf、libz 开发头文件 - **Root 权限**(用于加载 eBPF 程序) ### Fedora/RHEL ``` sudo dnf install clang libbpf-devel elfutils-libelf-devel zlib-devel ``` ### Ubuntu/Debian ``` sudo apt install clang libbpf-dev libelf-dev zlib1g-dev ``` ### SLES **警告**:待确认 ``` sudo zypper install clang libbpf-devel libelf-devel zlib-devel ``` ## 构建 ``` make clean && make ``` 这将生成: - `traffic_meter.bpf.o` - eBPF 程序对象 - `traffic_meter_user` - 用户空间加载器 - `ipmask_tool` - 生成网络字节序的非跟踪网络的位掩码,参见[下方](#untracked-ip-filtering) ## 用法 ``` sudo ./traffic_meter_user [cgroup_path] [nic_id] ``` ### 参数 | 参数 | 默认值 | 描述 | |----------|---------|-------------| | `cgroup_path` | `/sys/fs/cgroup/user.slice` | 要附加的 Cgroup(回退到 `/sys/fs/cgroup`) | | `nic_id` | `unknown` | 日志文件名的标识符 | ### 示例 ``` # 默认 cgroup,指定文件名的 NIC sudo ./traffic_meter_user "" eno1 # 指定 cgroup 路径 sudo ./traffic_meter_user /sys/fs/cgroup/user.slice/user-1000.slice eth0 # 监控所有流量 (root cgroup) sudo ./traffic_meter_user /sys/fs/cgroup wlan0 ``` 按 `Ctrl+C` 停止。 ## 输出 日志文件以 CSV 格式写入 `/tmp/traffic_user__.log`: ``` direction,bytes,src_ip,dst_ip,timestamp,total_in,total_out ``` ### 字段 | 字段 | 描述 | |-------|-------------| | `direction` | `in`(接收)或 `out`(发送) | | `bytes` | 数据包大小(字节) | | `src_ip` | 源 IP 地址(IPv4 或 IPv6) | | `dst_ip` | 目的 IP 地址(IPv4 或 IPv6) | | `timestamp` | 纳秒精度的 Unix 时间戳(秒.纳秒) | | `total_in` | 此 UID 目前累计接收的字节数 | | `total_out` | 此 UID 目前累计发送的字节数 | ### 输出示例 ``` out,52,192.168.1.100,8.8.8.8,1732985432.123456789,0,52 in,84,8.8.8.8,192.168.1.100,1732985432.234567890,84,52 out,1500,192.168.1.100,142.250.185.78,1732985433.345678901,84,1552 in,1500,142.250.185.78,192.168.1.100,1732985433.456789012,1584,1552 out,80,2001:db8::1,2607:f8b0:4004:800::200e,1732985434.567890123,1584,1632 in,1280,2607:f8b0:4004:800::200e,2001:db8::1,1732985434.678901234,2864,1632 ``` ## 非跟踪 IP 过滤 eBPF 程序包含一个静态的 IPv4 和 IPv6 网络掩码列表,在监控期间这些掩码会被**忽略**。 这些掩码定义在 `untracked_masks.h` 文件中,由 `traffic_meter.bpf.c` 作为 `untracked_ipv4` 和 `untracked_ipv6` 数组包含。 对于每个数据包,程序会检查源地址**和**目的地址是否匹配任何已配置的网络。 如果找到匹配项,则使用 `bpf_ringbuf_discard()` 丢弃该事件,并允许数据包继续传输而不被记录。 * **IPv4** – 每个条目以网络字节序存储网络地址和子网掩码。示例条目包括 `10.0.0.0/8` 和 `192.168.1.0/24`。 * **IPv6** – 每个条目存储一个 16 字节的网络前缀和前缀长度。辅助函数 `ipv6_is_untracked()` 根据前缀长度执行逐字节比较。 要修改被忽略的网络,请编辑 `untracked_masks.h` 中的静态数组并重新构建项目(`make clean && make`)。 这允许您定制监控以排除内部或其他无关流量。 文件 `untracked_masks.h` 的内容 ``` static const struct ipv4_mask untracked_ipv4[] = { { __builtin_bswap32(0x0a000000), __builtin_bswap32(0xff000000) }, // 10.0.0.0/8 { __builtin_bswap32(0xc0a80100), __builtin_bswap32(0xffffff00) }, // 192.168.1.0/24 }; static const struct ipv6_mask untracked_ipv6[] = { { { 0x20, 0x01, 0x0d, 0xb8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, 32 }, { { 0x20, 0x01, 0x0d, 0xb8, 0xab, 0xcd, 0x12, 0x34, 0x56, 0x78, 0x9a, 0xbc, 0xde, 0xf0, 0x00, 0x00 }, 112 }, }; ``` 使用随附的 `ipmask_tool` 从存储在文件中的 IP 地址列表生成 `untracked_masks.h` 文件,使用 `*` 作为通配符;例如: `ip_list.txt`: ``` 10.0.*.* 192.168.1.* 2001:db8::* 2001:db8:abcd:1234:5678:9abc:def0:* ``` **用法** ``` ./ipmask_tool ip_list.txt > untracked_masks.h ``` 该工具对于维护非跟踪网络非常有用,无需手动计算位掩码。 请注意,由于 eBPF 内核中禁止循环,因此使用 `#pragma unroll` 指令展开循环,并且数组长度是一个静态常量。完全展开所需考虑的最大迭代次数因编译器和编译器版本而异,在某些情况下,可能基于编译器对代码的分析而动态变化。 ## 架构 1. **eBPF 程序** (`traffic_meter.bpf.c`): - 附加到 IPv4 和 IPv6 的 `cgroup_skb/egress` 和 `cgroup_skb/ingress` 钩子 - 使用 `bpf_get_socket_uid()` 识别套接字所有者 (UID) - 从 IP 头提取源/目的 IP - 通过 IPv4 和 IPv6 各自独立的 ring buffer 将事件发送到用户空间 2. **用户空间加载器** (`traffic_meter_user.c`): - 加载并附加 eBPF 程序(共 4 个:IPv4 和 IPv6 的 ingress/egress) - 轮询两个 ring buffer 以获取事件 - 跟踪每个 UID 在入/出方向上的累计字节数 - 将事件传递给可插拔的输出后端 - 默认后端将 CSV 写入按用户区分的日志文件 输出系统使用可插拔的后端接口: ``` struct output_backend { int (*init)(void *config); int (*write)(const struct traffic_record *rec); void (*close)(void); }; ``` 要添加新的输出目标(syslog、网络套接字、数据库等),请实现此接口并将 `g_backend` 设置为您的实现。 ## 许可证 GPL(对于使用仅限 GPL 辅助函数的 eBPF 程序是必需的) ## 参考 - [官方网站](https://ebpf.io/) - [eBPF 文档](https://docs.ebpf.io/) - [教程](https://eunomia.dev/tutorials/)
标签:C/C++, certspotter, Docker镜像, IPv4/IPv6, Linux内核, 事务性I/O, 二进制发布, 内核编程, 安全渗透, 客户端加密, 开源工具, 用户行为审计, 系统性能监控, 网络可见性, 网络安全, 进程隐藏, 防御绕过, 隐私保护