3. 流量可视化基础:网络数据包在内核中的路径、eBPF挂载点选择策略

要实现节点池的流量可视化,首先需要理解网络数据包在Linux内核中的完整旅程。eBPF技术之所以强大,是因为它能在数据包路径的关键节点上“挂载”观测程序,从而以极低的开销捕获流量元数据。本章将深入剖析数据包的内核路径,并对比XDP、TC、Socket Filter三大挂载点的适用场景与选择策略。

3.1 网络数据包在内核中的路径

一个数据包从网卡到达用户态应用,需要经过多个内核子系统。下图展示了标准路径上的关键阶段与eBPF挂载点位置:

网络数据包内核路径与eBPF挂载点 网络数据包内核路径与eBPF挂载点 网卡 (NIC) XDP 挂载点 内核协议栈 TC Ingress TC Egress Socket Filter 用户态应用 说明:XDP在驱动层最早介入,TC在协议栈前后处理,Socket Filter在socket层过滤。越早挂载,性能越好,但可获取的上下文越少。

数据包从网卡接收后,依次经过以下关键阶段:

  1. 网卡驱动层:数据包通过DMA写入内存环形缓冲区,触发硬中断。
  2. XDP(eXpress Data Path):在驱动层之后、协议栈之前执行,是eBPF最早介入点。
  3. 内核协议栈:进行IP路由、TCP/UDP解包、连接跟踪等处理。
  4. TC(Traffic Control)Ingress:协议栈处理前,可对入站流量进行整形或过滤。
  5. TC Egress:协议栈处理后、出站前,可对出站流量进行控制。
  6. Socket Filter:在socket层,数据包交付给用户态应用前,可进行过滤或观测。

3.2 eBPF挂载点选择策略

不同的挂载点决定了eBPF程序能获取的数据粒度、性能开销以及操作能力。下表对比了三大核心挂载点的关键差异:

特性 XDP TC (Ingress/Egress) Socket Filter
介入时机 驱动层,协议栈之前 协议栈前后 Socket层,交付用户态前
性能开销 极低(无协议栈开销) 低(需经过协议栈部分处理) 中等(需完整协议栈处理)
可操作动作 丢弃、重定向、透传 丢弃、重定向、整形、镜像 过滤、复制、统计
数据可见性 原始L2/L3帧 sk_buff结构(含L4信息) 已解析的socket数据
适用场景 DDoS防御、负载均衡、高速抓包 流量镜像、QoS、容器网络策略 应用层监控、协议分析、审计
编程接口 BPF_PROG_TYPE_XDP BPF_PROG_TYPE_SCHED_CLS BPF_PROG_TYPE_SOCKET_FILTER

3.3 挂载点选择决策流程

在实际的节点池巡检场景中,选择挂载点需要根据观测目标和性能要求进行权衡。以下决策树可以帮助快速定位:

eBPF挂载点选择决策树 eBPF挂载点选择决策树 观测目标是什么? 需要线速处理? 选择 XDP 需要流量整形? 选择 TC 需要应用层数据? 选择 Socket Filter 实践建议: • 节点池流量可视化首选 TC Ingress:兼顾性能与数据完整性,可获取sk_buff中的L4信息。 • 若需捕获所有包(含被DDoS丢弃的),使用 XDP 并配合 perf_event 输出。

3.4 实战:在节点池中部署TC Ingress观测点

对于大多数节点池巡检场景,TC Ingress 是最佳平衡点。它既能获取完整的L4元数据(源IP、目的IP、端口、协议),又不会像Socket Filter那样增加协议栈尾部延迟。以下是一个挂载TC Ingress eBPF程序的示例:

// tc_ingress_kern.c - 挂载到 TC Ingress 的 eBPF 程序
#include <linux/bpf.h>
#include <linux/pkt_cls.h>
#include <bpf/bpf_helpers.h>

SEC("classifier")
int tc_ingress_func(struct __sk_buff *skb) {
    // 提取 L3/L4 元数据
    __u32 src_ip = skb->remote_ip4;
    __u32 dst_ip = skb->local_ip4;
    __u16 src_port = skb->remote_port;
    __u16 dst_port = skb->local_port;
    __u8  protocol = skb->protocol;

    // 将元数据发送到 perf ring buffer 或 map
    // ... (此处省略具体输出逻辑)

    return TC_ACT_OK;  // 放行数据包
}

char _license[] SEC("license") = "GPL";
# 加载 TC Ingress eBPF 程序到 eth0
tc qdisc add dev eth0 clsact
tc filter add dev eth0 ingress bpf da obj tc_ingress_kern.o sec classifier

# 验证挂载状态
tc filter show dev eth0 ingress
最佳实践:在节点池中,建议为每个节点的物理网卡和容器veth对分别挂载TC Ingress程序。物理网卡捕获南北向流量,veth对捕获东西向流量,从而实现全量流量可视化。

3.5 挂载点选择避坑指南

  • XDP 不支持虚拟网卡:XDP需要原生驱动支持,在veth、tun等虚拟设备上无法使用。容器场景下,东西向流量需依赖TC。
  • Socket Filter 无法捕获被丢弃的包:如果数据包在协议栈中被iptables或策略路由丢弃,Socket Filter将无法感知。此时应使用XDP或TC。
  • TC Egress 的流量顺序:出站流量在TC Egress阶段已经过协议栈完整处理,适合做流量整形和镜像,但不适合做原始包捕获。
  • 性能与数据完整性的权衡:XDP性能最好但只能看到L2/L3头部;TC可看到L4;Socket Filter可看到应用层数据。根据巡检需求选择合适粒度。
核心结论:节点池流量可视化的挂载点选择应遵循“先TC Ingress,不足再XDP,需要应用层再Socket Filter”的原则。TC Ingress在绝大多数场景下提供了性能与数据完整性的最佳平衡。