BPF_PROG_TYPE_FLOW_DISSECTOR¶
概述¶
流分解器(Flow dissector)是一个从数据包中解析元数据的例程。它在网络子系统的各个地方(RFS、流哈希等)都有使用。
BPF 流分解器试图在 BPF 中重新实现基于 C 的流分解器逻辑,以获得 BPF 验证器的所有好处(即指令数量和尾调用的限制)。
API¶
BPF 流分解器程序在 __sk_buff
上操作。然而,只允许使用有限的字段集:data
、data_end
和 flow_keys
。flow_keys
是 struct bpf_flow_keys
,包含流分解器的输入和输出参数。
- 输入参数为:
nhoff
- 网络头部的初始偏移量thoff
- 传输头部的初始偏移量,初始化为 nhoffn_proto
- L3 协议类型,从 L2 头部解析flags
- 可选标志
流分解器 BPF 程序应填充 struct bpf_flow_keys
的其余字段。输入参数 nhoff/thoff/n_proto
也应进行相应调整。
BPF 程序的返回码要么是 BPF_OK 表示成功分解,要么是 BPF_DROP 表示解析错误。
__sk_buff->data¶
在没有 VLAN 的情况下,BPF 流分解器的初始状态如下:
+------+------+------------+-----------+
| DMAC | SMAC | ETHER_TYPE | L3_HEADER |
+------+------+------------+-----------+
^
|
+-- flow dissector starts here
skb->data + flow_keys->nhoff point to the first byte of L3_HEADER
flow_keys->thoff = nhoff
flow_keys->n_proto = ETHER_TYPE
在 VLAN 的情况下,流分解器可以以两种不同的状态被调用。
VLAN 解析前
+------+------+------+-----+-----------+-----------+
| DMAC | SMAC | TPID | TCI |ETHER_TYPE | L3_HEADER |
+------+------+------+-----+-----------+-----------+
^
|
+-- flow dissector starts here
skb->data + flow_keys->nhoff point the to first byte of TCI
flow_keys->thoff = nhoff
flow_keys->n_proto = TPID
请注意,TPID 可以是 802.1AD,因此 BPF 程序对于双标签数据包可能需要解析两次 VLAN 信息。
VLAN 解析后
+------+------+------+-----+-----------+-----------+
| DMAC | SMAC | TPID | TCI |ETHER_TYPE | L3_HEADER |
+------+------+------+-----+-----------+-----------+
^
|
+-- flow dissector starts here
skb->data + flow_keys->nhoff point the to first byte of L3_HEADER
flow_keys->thoff = nhoff
flow_keys->n_proto = ETHER_TYPE
在这种情况下,VLAN 信息已在流分解器之前处理,BPF 流分解器无需处理它。
这里的要点如下:BPF 流分解器程序可以在包含可选 VLAN 头的情况下被调用,并且应该优雅地处理两种情况:存在单个或双个 VLAN 以及不存在 VLAN。同一个程序可以用于这两种情况,并且必须仔细编写以处理这两种情况。
标志¶
flow_keys->flags
可能包含可选的输入标志,其作用如下:
BPF_FLOW_DISSECTOR_F_PARSE_1ST_FRAG
- 告诉 BPF 流分解器继续解析第一个分片;默认的预期行为是流分解器在发现数据包已分片后立即返回;由eth_get_headlen
用于估算 GRO 的所有头部的长度。BPF_FLOW_DISSECTOR_F_STOP_AT_FLOW_LABEL
- 告诉 BPF 流分解器在到达 IPv6 流标签时停止解析;由___skb_get_hash
用于获取流哈希。BPF_FLOW_DISSECTOR_F_STOP_AT_ENCAP
- 告诉 BPF 流分解器在到达封装头部时停止解析;由路由基础设施使用。
参考实现¶
请参阅 tools/testing/selftests/bpf/progs/bpf_flow.c
获取参考实现,并参阅 tools/testing/selftests/bpf/flow_dissector_load.[hc]
获取加载器。bpftool 也可以用于加载 BPF 流分解器程序。
- 参考实现组织如下:
jmp_table
映射,包含每个支持的 L3 协议的子程序。_dissect
例程 - 入口点;它进行输入n_proto
解析并执行bpf_tail_call
到相应的 L3 处理器。
由于 BPF 目前不支持循环(或任何跳回),因此使用 jmp_table 来处理多层封装(和 IPv6 选项)。
当前限制¶
BPF 流分解器不支持导出所有内核中基于 C 的实现可以导出的元数据。值得注意的例子是单 VLAN (802.1Q) 和双 VLAN (802.1AD) 标签。请参阅 struct bpf_flow_keys
以获取当前可以从 BPF 上下文导出的信息集。
当 BPF 流分解器附加到根网络命名空间(机器范围策略)时,用户无法在其子网络命名空间中覆盖它。