关于本文
Linux 现在已经支持了很多种和网络相关的 eBPF 程序了,这些程序可以附加到任何套接字上,可以提取流经数据包的相关信息,可以检测和控制系统的网络流量,可以对网络接口的数据包进行过滤,可以对数据包进行放行、禁止或重定向等,可以修改网络连接状态…这些能力在 service mesh,云原生[2][3]等场景下有极大的作用,本文会介绍一些 eBPF 程序在网络上的应用。
如何实现
从网络的角度来看,使用 BPF 程序主要有两个用途: 数据包捕获 和 过滤 [1]。
它这些能力的实现主要取决与以下两种类型的程序:
- 套接字相关程序
- 基于 BPF 分类器和流量控制程序
网络相关的 eBPF 程序通常会需要传一个参数 struct __sk_buff
,我们先来看看他的结构[4][5]:
struct __sk_buff {
__u32 len; /* 整个数据区域的长度, 这个 len 只计算有效的协议长度,如果在 l3 时, 不会计算 l2 的协议头长度*/
__u32 pkt_type; /*标记帧的类型*/
__u32 mark;
__u32 queue_mapping;
__u32 protocol; /*协议类型*/
__u32 vlan_present;
__u32 vlan_tci;
__u32 vlan_proto;
__u32 priority;
__u32 ingress_ifindex;
__u32 ifindex;
__u32 tc_index; /* traffic control index */
__u32 cb[5]; /* 保存与协议相关的控制信息, 每个协议可以独立使用这些信息*/
__u32 hash;
__u32 tc_classid;
__u32 data; // 数据开始指针
__u32 data_end; // 数据结束指针
__u32 napi_id;
/* Accessed by BPF_PROG_TYPE_sk_skb types from here to ... */
__u32 family;
__u32 remote_ip4; /* Stored in network byte order */
__u32 local_ip4; /* Stored in network byte order */
__u32 remote_ip6[4]; /* Stored in network byte order */
__u32 local_ip6[4]; /* Stored in network byte order */
__u32 remote_port; /* Stored in network byte order */
__u32 local_port; /* stored in host byte order */
/* ... here. */
__u32 data_meta;
__bpf_md_ptr(struct bpf_flow_keys *, flow_keys);
__u64 tstamp;
__u32 wire_len;
__u32 gso_segs;
__bpf_md_ptr(struct bpf_sock *, sk);
__u32 gso_size;
__u32 :32; /* Padding, future use. */
__u64 hwtstamp;
};
它是对内核网络核心结构 struct sk_buff
的用户可访问字段的镜像。BPF 程序中对 struct __sk_buff
字段的访问,将会被 BPF 校验器转换成对相应的 struct sk_buff
字段的访问[7]。而 sk_buff
是 Linux 网络协议栈里最重要的结构体,它用来管理和控制接受或发送数据包,在内核中各个协议之间传输时,不需要拷贝 sk_buff
的数据,只需要改协议头和移动数据指针,当数据从 L4 到 L2 时,数据段只用移动 sk_buff
里的 data
指针即可 。
BPF 程序通过访问 __sk_buff
中暴露的这些数据 ,可以选择放行(PASS)或丢弃(DROP)数据包; 可以将当前流量信息保存到 BPF 映射
来统计流量数据。甚至在一部分 BPF 程序里,可以对这些数据包进行重定向或改变其基本结构[1]。
可以说 __sk_buff
是 eBPF 程序可以在网络上大方异彩的核心,那下面我来介绍一下这些 BPF 程序都实现了什么强大的功能。