【BPF入门系列-11】使用 eBPF 技术跟踪 Netfilter 数据流过滤结果
1. 网络层数据流向与 Netfilter 体系
图 1-1 为网络层内核收发核心流程图,在函数流程图中我们可以看到 Netfliter
在其中的位置(图中深色底纹圆角矩形)。图中对应的 hook
点有 5 个,每个hook
点中保存一组按照优先级排序的函数列表:
NF_IP_PREROUTING
:接收到的包进入协议栈后立即触发此hook
中注册的对应函数列表,在进行任何路由判断 (将包发往哪里)之前;NF_IP_LOCAL_IN
:接收到的包经过路由判断,如果目的是本机,将触发此hook
中注册的对应函数列表;NF_IP_FORWARD
:接收到的包经过路由判断,如果目的是其他机器,将触发此hook
中注册的对应函数列表;NF_IP_LOCAL_OUT
:本机产生的准备发送的包,在进入协议栈后立即触发此hook
中注册的对应函数列表;NF_IP_POST_ROUTING
:本机产生的准备发送的包或者转发的包,在经过路由判断之后, 将触发此hook
中注册的对应函数列表;
图 1-1 网络层内核收发核心流程图
从图 1-1 的数据流分为三类,分别用不同的颜色标注,因此我们可以得知:
- 本地处理的数据包,在
Netfliter
体系中会依次流经NF_IP_PREROUTING
和NF_IP_LOCAL_IN
; - 转发的数据包,在
Netfliter
体系中会依次流经NF_IP_FORWARD
和NF_IP_POST_ROUTING
; - 本地发送的数据包,
在 Netfliter
体系中会依次流经NF_IP_LOCAL_OUT
和NF_IP_POST_ROUTING
;
2. Netfilter 与 IPtables
2.1 Netfilter 数据结构
Netfilter
架构中对于 hook
点中注册的函数管理,采用二维数组的方式进行组织,纵轴为协议,横轴为 hook
点,每个 Network Namespace
对应一个此种格式的二维数组,详见图 2-1。数组中保存的为 nf_hook_entries
结构,对应保存了该 hook
点中注册的 hook
函数,函数按照优先级的方式进行管理,调用时也是按照优先级进行过滤。
图 2-1 Netfilter hook 点函数数据结构
其中 hooks_ipv4[NF_INET_NUMHOOKS]
位于 net->nf
变量中。 hook
函数的原型定义如下:
|
|
以 table nat
定义的 hook
函数为例, struct nf_hook_ops nf_nat_ipv4_ops
如下:
|
|
nf_nat_ipv4_ops
结构在函数 iptable_nat_table_init
中初始化,最终通过 nf_register_net_hook
函数注册到对应 hook 点的函数列表中。
2.2 iptabes
iptables
是运行在用户空间的应用软件,通过控制 Linux
内核 中 Netfilter
模块,来管理网络数据包的处理和转发。iptables
使用 table
来组织规则,根据用来做什么类型的判断标准,将规则分为不同 table
,当前支持的 table
有 raw/mangle/nat/filter/security
等。在 table
内部采用链 (chain
)进行组织,其中系统内置的 chain
与 Netfilter
中的 hook
点一一对应,例如 chain PREROUTING
对应于 NF_IP_PRE_ROUTING hook
,用户自定义 chain
没有对应的 Netfilter hook
对应,因此必须通过 jump
跳转的方式进行关联。
iptables
的整体组织如下表,纵轴代表的是 table
名,横轴是 chain
的名字,与 Netfilter hook
点一一对应。纵轴的方向代表了在某个 chain
上调用的顺序,优先级自上而下。
Tables↓ /Chains→ | PREROUTING | INPUT | FORWARD | OUTPUT | POSTROUTING |
---|---|---|---|---|---|
(routing decision) | ✓ | ||||
raw | ✓ | ✓ | |||
(connection tracking enabled) | ✓ | ✓ | |||
mangle | ✓ | ✓ | ✓ | ✓ | ✓ |
nat (DNAT) | ✓ | ✓ | |||
(routing decision) | ✓ | ✓ | |||
filter | ✓ | ✓ | ✓ | ||
security | ✓ | ✓ | ✓ | ||
nat (SNAT) | ✓ | ✓ |
表 2-1 iptables 规则组织
2.3 内核代码实现
此处以 ip_rcv
函数为例,简单讨论在代码层面的实现:
|
|
NF_HOOK
宏在启用 Netfilter
的条件编译下,会首先调用 nf_hook
函数,在该函数中会根据传入的协议和 hook
点,获取到对应的 hook
函数列表头(例如 IPv4
协议中的 net->nf.hooks_ipv4[hook]
),然后在 nf_hook_slow
中循环调用列表中的 hook
函数(hook
函数按照优先级组织),并基于 hook
函数返回的结果决定继续调用列表中后续的 hook
函数,还是直接返回。
Netfilter
中 hook
函数的格式基本如下,直接调用 ipt_do_table
函数,最后的参数传入对应的 table
字段。
|
|
所以,如果我们想要获取到 Netfilter hook
点中对应函数的过滤的结果,则需要跟踪 ipt_do_table
函数的入参和返回结果即可。
|
|
3. 使用 eBPF 技术跟踪
经过上述分析,我们了解到对于 Netfilter
的底层函数为 ipt_do_table
,那么我们只需要使用 kprobe
和 kretprobe
获取到入参和返回结果,即可以获取到对应的过滤结果,这对于我们分析采用 iptables 管理流量的场景下定位问题非常方便。
图 3-1 程序架构
运行效果图:
|
|
完整代码如下:
|
|
可以在样例程序的基础上通过 skb
读取对应的 IP
和端口信息(包括源和目的),这可以实现对于 Netfilter
中的 hook
点跟踪。完整的可使用代码参见 skbtracer.py,使用帮助如下:
|
|
查看 iptables 数据流程,需要添加 –iptable 标记。
4. 参考资料
- 原文作者:DavidDi
- 原文链接:https://www.ebpf.top/post/iptalbes_ebpf/
- 版权声明:本作品采用知识共享署名-非商业性使用-禁止演绎 4.0 国际许可协议. 进行许可,非商业转载请注明出处(作者,原文链接),商业转载请联系作者获得授权。
- 最后更新时间:2022-11-05 21:36:52.101058914 +0800 CST