共计 1522 个字符,预计需要花费 4 分钟才能阅读完成。
在 (一) 中说到,报文在内核协议栈中会途经 5
个HOOK
点,在每个 HOOK
点上会依次执行链表上的钩子函数,那么这些钩子函数是如何与用户使用 iptables
下发的各个 rule
联系起来的呢?这些 rule
又是如何存储的呢?本文详细描述这个问题。
表(table)
内核使用 struct xt_table
这个结构来表示一个表 (table
)。结构中记录了这个表在哪些HOOK
点有效,它的 private
指针保存了这个表包含的所有规则。
每个新的 net namespace
创建时,Netfitler
会创建所有属于当前 net namespace
的表,并将其记录到自己的 net namespace
中。
从图中可以看出,创建一个包含默认 rule
的table
分两步
- 根据已有的模板,生成默认
rule
- 注册
table
到Netfilter
框架中
生成默认 rule
如果你使用过 iptables
, 可能会知道每张table
的每条 chain
都有一个默认 POLICY
, 它表示chain
中的所有的 rule
都不匹配,则执行默认的行为。这个默认行为可以通过 iptables
配置,在系统启动时默认行为都被设置为 NF_ACCEPT
. 而这里的默认rule
就是这个意思,Netfilter
会为每个 chain
创建一条默认的rule
,并将它们加入到table
ipt_replace
上图创建默认 rule
的时候使用了一个 struct ipt_replace
的结构,这个结构的来历和 iptables
的配置方法是紧密相连的:iptables
采用的是 读 - 修改 - 写
的方式进行配置的!
举个例子,当你使用 iptables
向Netfilter
的 filter
表添加一条新的 rule
时,iptables
会将 filter
整个表读取到用户空间,在用户空间修改完成后,再将其重新设置到 Netfilter
。所以,这是一个替换(Replace
) 的过程。
因此,ipt_replace
一般就是指用户使用 iptables
下发的表。典型的结构如下:
hook_entry
和 underflow
分别表示各个 HOOK
上的第一条 rule
和最后一条 rule
的偏移 (具体的规则在最后一个ipt_entry
的柔性数组里!)
但在这里,由于还处于初始化阶段,所以这里的 repl
是内核自己由 packet_filter
模板生成的,它会为 filter
所在的 LOCAL_IN
、FORWARD
、LOCAL_OUT
这几个 HOOK
点各创建一条默认rule
.
ipt_entry
内核使用 struct ipt_entry
表示 一条 用户使用 iptables
配置的 rule
. 这个结构后面会紧跟若干个ipt_entry_match
结构和 1 个ipt_standard_target
结构。它与一条用户配置的 iptables
的关系如下:
其中
ipt_entry
: 表示标准匹配结构。包括报文的源 IP、目的 IP、入接口、出接口等条件ipt_entry_match
: 表示扩展匹配。一条完整的rule
包含零个到多个该结构ipt_standard_target
: 表示匹配后的动作
如果对 iptables
的匹配规则不太熟悉,建议点此扩展匹配了解一下
注册 table 到 Netfilter 框架
在完成了默认规则的创建之后(保存在ipt_replace
),接下来就是应该注册新的表了。
xt_table
中保存规则的 private
指针类型是 struct xt_table_info
,因此这里第一部就是把repl
转换为 newinfo
。接下来就是调用xt_register_table
创建新的表并注册到框架了
注册完成之后,Netfilter
可以通过 net->xt.tables[af]
链表遍历到所有注册的表,也可以通过 net->ipv4.
快速访问到具体的表。