3. 网络观测数据采集:网络数据包捕获原理、XDP与TC程序、套接字过滤、网络延迟与吞吐量指标
网络观测是 eBPF 最成熟、应用最广泛的能力领域。本章深入解析 eBPF 在网络数据包捕获路径上的核心技术原理,涵盖从硬件中断到用户态套接字的完整链路,并重点阐述 XDP、TC 以及套接字过滤三种编程模型的适用场景与性能差异。同时,定义网络延迟与吞吐量的关键观测指标,为后续构建自动化运维制品库奠定数据采集基础。
3.1 网络数据包捕获原理与内核路径
理解 eBPF 网络观测,首先需要掌握数据包从网卡到用户态应用程序的完整内核路径。eBPF 程序可以挂载到该路径的不同“钩子”点,从而实现对网络流量的观测、过滤或修改。
下表对比了不同捕获点的关键特性:
| 捕获点 | 内核层级 | 触发时机 | 性能开销 | 典型应用 |
|---|---|---|---|---|
| XDP | 驱动层 (RX 队列) | 最早,分配 skb 之前 | 极低 (无协议栈开销) | DDoS 防御、负载均衡、高速包过滤 |
| TC (ingress) | 协议栈入口 (dev_queue_xmit) | 分配 skb 后,协议栈处理前 | 低 | 流量整形、QoS、容器网络策略 |
| 套接字过滤 (BPF_PROG_TYPE_SOCKET_FILTER) | 套接字层 | 数据到达套接字接收队列前 | 中等 | tcpdump、应用层协议过滤、安全审计 |
下图展示了 eBPF 程序在数据包内核路径上的挂载位置:
3.2 XDP 与 TC 程序:内核态网络处理
XDP (eXpress Data Path) 和 TC (Traffic Control) 是 eBPF 在网络观测与处理中最核心的两个程序类型。它们运行在内核态,能够以极低的开销处理高速网络流量。
3.2.1 XDP 程序
XDP 程序挂载在网卡驱动层,在数据包被分配为 sk_buff 结构体之前执行。这意味着 XDP 可以访问原始的、未经内核协议栈处理的网络帧数据。XDP 程序通过返回一个动作码来决定数据包的命运:
- XDP_PASS:将数据包传递给内核协议栈继续处理。
- XDP_DROP:直接丢弃数据包,性能极高,适用于 DDoS 防御。
- XDP_TX:将数据包从接收到的网卡原路转发出去,适用于负载均衡。
- XDP_REDIRECT:将数据包重定向到其他网卡或 CPU,适用于高级转发场景。
- XDP_ABORTED:程序出错,丢弃数据包并记录错误。
3.2.2 TC 程序
TC 程序挂载在 Linux 流量控制子系统的入口(ingress)或出口(egress)处。与 XDP 不同,TC 程序在数据包已被分配 sk_buff 结构体后执行,因此可以访问更丰富的协议元数据。TC 程序常用于:
- 流量整形与 QoS(服务质量)
- 容器网络策略(如 Cilium 网络策略)
- 网络地址转换(NAT)
- 更复杂的包修改与封装/解封装
下表对比了 XDP 与 TC 的核心差异:
| 特性 | XDP | TC (ingress) |
|---|---|---|
| 执行位置 | 驱动层,skb 分配前 | 协议栈入口,skb 分配后 |
| 数据访问 | 原始网络帧 (xdp_md) | sk_buff 结构体 |
| 性能 | 极高 (无协议栈开销) | 高 (少量协议栈开销) |
| 可编程性 | 有限 (不能直接修改 skb) | 丰富 (可修改 skb 元数据) |
| 典型场景 | DDoS 防御、负载均衡 | 容器网络策略、QoS |
| 硬件依赖 | 需要网卡驱动支持 | 无特殊硬件依赖 |
3.3 套接字过滤:用户态视角的观测
套接字过滤(Socket Filter)是 eBPF 最早的应用之一,其程序类型为 BPF_PROG_TYPE_SOCKET_FILTER。它允许用户将 eBPF 程序附加到特定的套接字上,对进出该套接字的数据包进行过滤和观测。
与 XDP 和 TC 不同,套接字过滤工作在协议栈之上、用户态应用之下。这意味着它看到的是已经经过内核协议栈处理的数据,例如已经完成 TCP 重组、IP 分片重组的流数据。套接字过滤的典型应用是 tcpdump 和 seccomp。
一个简单的套接字过滤 eBPF 程序示例(仅捕获 TCP 数据包):
#include <linux/bpf.h>
#include <linux/if_ether.h>
#include <linux/ip.h>
#include <linux/tcp.h>
SEC("socket")
int tcp_filter(struct __sk_buff *skb) {
// 获取网络层头部偏移
void *data = (void *)(long)skb->data;
void *data_end = (void *)(long)skb->data_end;
struct ethhdr *eth = data;
if (eth + 1 > data_end)
return 0;
// 仅处理 IPv4 数据包
if (eth->h_proto != __constant_htons(ETH_P_IP))
return 0;
struct iphdr *ip = data + sizeof(*eth);
if (ip + 1 > data_end)
return 0;
// 仅处理 TCP 数据包
if (ip->protocol != IPPROTO_TCP)
return 0;
// 捕获 TCP 数据包,返回非零值
return -1;
}
3.4 网络延迟与吞吐量指标
网络观测的最终目的是量化网络性能。eBPF 能够以纳秒级精度采集关键指标,为自动化运维提供数据支撑。以下是核心的网络性能指标:
3.4.1 网络延迟指标
延迟衡量数据包从源端到目的端所花费的时间。eBPF 可以测量路径上不同阶段的延迟:
- RTT (Round-Trip Time): 数据包从发送到收到 ACK 确认的总时间。eBPF 可以通过跟踪 TCP 套接字的
tcp_rcv_established等函数来精确计算 RTT。 - 队列延迟 (Queueing Delay): 数据包在内核队列中等待处理的时间。通过 TC 或 XDP 程序可以测量数据包到达与开始处理的时间差。
- 处理延迟 (Processing Delay): 内核协议栈处理数据包所花费的时间。eBPF 可以在协议栈关键函数(如
ip_rcv、tcp_v4_rcv)的入口和出口处打点,计算处理耗时。 - 应用延迟 (Application Delay): 数据从套接字传递到用户态应用并被应用处理的时间。这通常需要结合用户态探针 (uprobe) 来测量。
3.4.2 吞吐量指标
吞吐量衡量单位时间内成功传输的数据量。eBPF 可以按流、按协议、按容器粒度进行统计:
- PPS (Packets Per Second): 每秒处理的数据包数量。XDP 程序可以直接在驱动层统计,性能损耗极小。
- BPS (Bytes Per Second): 每秒处理的字节数,即带宽。TC 程序可以方便地统计每个
sk_buff的长度。 - 连接数 (Connections Per Second): 每秒新建的 TCP 连接数。通过跟踪
tcp_connect或inet_csk_accept函数可以精确统计。 - 重传率 (Retransmission Rate): 重传数据包占总发送数据包的比例。高重传率通常意味着网络拥塞或链路质量问题。
BPF_MAP_TYPE_PERCPU_HASH 或 BPF_MAP_TYPE_PERCPU_ARRAY 来存储计数器,以避免多核竞争。用户态程序定期(如每 1 秒)读取这些 percpu map 并汇总,即可得到全局的延迟和吞吐量指标。
下表总结了关键指标及其 eBPF 采集方法:
| 指标类别 | 指标名称 | eBPF 采集方法 | 挂载点 |
|---|---|---|---|
| 延迟 | RTT | 跟踪 tcp_rcv_established,计算时间戳差 | kprobe / fentry |
| 队列延迟 | 在 TC 入口记录时间戳,与 XDP 时间戳对比 | XDP + TC | |
| 吞吐量 | PPS / BPS | 在 XDP 或 TC 程序中递增 percpu 计数器 | XDP / TC |
| 连接数 | 跟踪 tcp_connect / inet_csk_accept | kprobe / tracepoint | |
| 可靠性 | 重传率 | 跟踪 tcp_retransmit_skb | kprobe |
下图展示了 eBPF 如何从不同层级采集延迟与吞吐量指标: