在(一)中说到,报文在内核协议栈中会途经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.
快速访问到具体的表。