深入理解 netfilter 和 iptables!

↓推荐关注↓
来源:云原生实验室
Netfilter (配合 iptables)使得用户空间应用程序可以注册内核网络栈在处理数据包时应用的处理规则,实现高效的网络转发和过滤 。很多常见的主机防火墙程序以及 Kubernetes 的 Service 转发都是通过 iptables 来实现的 。
关于 netfilter 的介绍文章大部分只描述了抽象的概念,实际上其内核代码的基本实现不算复杂,本文主要参考 Linux 内核 2.6 版本代码(早期版本较为简单),与最新的 5.x 版本在实现上可能有较大差异,但基本设计变化不大,不影响理解其原理 。
本文假设读者已对 TCP/IP 协议有基本了解 。
Netfilter 的设计与实现
netfilter 的定义是一个工作在 Linux 内核的网络数据包处理框架,为了彻底理解 netfilter 的工作方式,我们首先需要对数据包在 Linux 内核中的处理路径建立基本认识 。
数据包的内核之旅
数据包在内核中的处理路径,也就是处理网络数据包的内核代码调用链,大体上也可按 TCP/IP 模型分为多个层级,以接收一个 IPv4 的 tcp 数据包为例:
在物理-网络设备层,网卡通过 DMA 将接收到的数据包写入内存中的 ring buffer ,经过一系列中断和调度后,操作系统内核调用 __skb_dequeue 将数据包加入对应设备的处理队列中,并转换成 sk_buffer 类型(即 socket buffer - 将在整个内核调用栈中持续作为参数传递的基础数据结构,下文指称的数据包都可以认为是 sk_buffer ),最后调用 netif_receive_skb 函数按协议类型对数据包进行分类,并跳转到对应的处理函数 。如下图所示:

深入理解 netfilter 和 iptables!

文章插图
network-path
假设该数据包为 IP 协议包,对应的接收包处理函数 ip_rcv 将被调用,数据包处理进入网络(IP)层 。ip_rcv 检查数据包的 IP 首部并丢弃出错的包,必要时还会聚合被分片的 IP 包 。然后执行 ip_rcv_finish 函数,对数据包进行路由查询并决定是将数据包交付本机还是转发其他主机 。假设数据包的目的地址是本主机,接着执行的 dst_input 函数将调用 ip_local_deliver 函数 。ip_local_deliver 函数中将根据 IP 首部中的协议号判断载荷数据的协议类型,最后调用对应类型的包处理函数 。本例中将调用 TCP 协议对应的 tcp_v4_rcv 函数,之后数据包处理进入传输层 。
tcp_v4_rcv 函数同样读取数据包的 TCP 首部并计算校验和,然后在数据包对应的 TCP control buffer 中维护一些必要状态包括 TCP 序列号以及 SACK 号等 。该函数下一步将调用 __tcp_v4_lookup 查询数据包对应的 socket,如果没找到或 socket 的连接状态处于 TCP_TIME_WAIT,数据包将被丢弃 。如果 socket 处于未加锁状态,数据包将通过调用 tcp_prequeue 函数进入 prequeue 队列,之后数据包将可被用户态的用户程序所处理 。传输层的处理流程超出本文讨论范围,实际上还要复杂很多 。
netfilter hooks
接下来我们正式进入主题 。netfilter 的首要组成部分是 netfilter hooks 。
hook 触发点
对于不同的协议(IPv4、IPv6 或 ARP 等),Linux 内核网络栈会在该协议栈数据包处理路径上的预设位置触发对应的 hook 。在不同协议处理流程中的触发点位置以及对应的 hook 名称(蓝色矩形外部的黑体字)如下,本文仅重点关注 IPv4 协议:
深入理解 netfilter 和 iptables!

文章插图
netfilter-flow
所谓的 hook 实质上是代码中的枚举对象(值为从 0 开始递增的整型):
enum nf_inet_hooks { NF_INET_PRE_ROUTING, NF_INET_LOCAL_IN, NF_INET_FORWARD, NF_INET_LOCAL_OUT, NF_INET_POST_ROUTING, NF_INET_NUMHOOKS };
每个 hook 在内核网络栈中对应特定的触发点位置,以 IPv4 协议栈为例,有以下 netfilter hooks 定义:


以上关于本文的内容,仅作参考!温馨提示:如遇专业性较强的问题(如:疾病、健康、理财等),还请咨询专业人士给予相关指导!

「辽宁龙网」www.liaoninglong.com小编还为您精选了以下内容,希望对您有所帮助: