上面代码中的常量与 图1 中挂载钩子函数的位置一一对应,如常量 NF_IP_PRE_ROUTING 对应着 图1 的 PRE_ROUTING 处。
二、Netfilter 钩子函数链
前面说过,Netfilter 是通过在网络协议中的不同位置挂载钩子函数来对数据包进行过滤和处理,而且每个挂载点能够挂载多个钩子函数,所以 Netfilter 使用链表结构来存储这些钩子函数,如 图2 所示:struct list_head 结构是内核的通用链表结构。
从 nf_hooks 变量定义为一个二维数组,第一维是用来表示不同的协议(如 IPv4 或者 IPv6,本文只讨论 IPv4,所以可以把 nf_hooks 当成是一维数组),而第二维用于表示不同的挂载点,如 图2 中的 5 个挂载点。
三、钩子函数
接下来我们介绍一下钩子函数在 Netfilter 中的存储方式。
前面我们介绍过,Netfilter 通过链表来存储钩子函数,而钩子函数是通过结构 nf_hook_ops 来描述的,其定义如下:面我们对 nf_hook_ops 结构的各个字段进行说明:
-
list:用于把处于相同挂载点的钩子函数链接起来。
-
hook:钩子函数指针,就是用于处理或者过滤数据包的函数。
-
pf:协议类型,用于指定钩子函数挂载在 nf_hooks 数组第一维的位置,如 IPv4 协议设置为 PF_INET。
-
hooknum:钩子函数所在链(挂载点),如 NF_IP_PRE_ROUTING。
-
priority:钩子函数的优先级,用于管理钩子函数的调用顺序。
其中 hook 字段的类型为 nf_hookfn,nf_hookfn 类型的定义如下:们也介绍一下 nf_hookfn 函数的各个参数的作用:
-
hooknum:钩子函数所在链(挂载点),如 NF_IP_PRE_ROUTING。
-
skb:数据包对象,就是要处理或者过滤的数据包。
-
in:接收数据包的设备对象。
-
out:发送数据包的设备对象。
-
okfn:当挂载点上所有的钩子函数都处理过数据包后,将会调用这个函数来对数据包进行下一步处理。
四、注册钩子函数
当定义好一个钩子函数结构后,需要调用 nf_register_hook 函数来将其注册到 nf_hooks 数组中,nf_register_hook 函数的实现如下:_register_hook 函数的实现比较简单,步骤如下:
-
对 nf_hooks 进行上锁操作,用于保护 nf_hooks 变量不受并发竞争。
-
通过钩子函数的优先级来找到其在钩子函数链表中的正确位置。
-
把钩子函数插入到链表中。
-
对 nf_hooks 进行解锁操作。
插入过程如 图3 所示: 图3 所示,我们要把优先级为 20 的钩子函数插入到 PRE_ROUTING 这个链中,而 PRE_ROUTING 链已经存在两个钩子函数,一个优先级为 10, 另外一个优先级为 30。
通过与链表中的钩子函数的优先级进行对比,发现新的钩子函数应该插入到优先级为 10 的钩子函数后面,所以就 如图3 所示就把新的钩子函数插入到优先级为 10 的钩子函数后面。
五、触发调用钩子函数
钩子函数已经被保存到不同的链上,那么什么时候才会触发调用这些钩子函数来处理数据包呢?
要触发调用某个挂载点上(链)的所有钩子函数,需要使用 NF_HOOK 宏来实现,其定义如下:先介绍一下 NF_HOOK 宏的各个参数的作用:
-
pf:协议类型,就是 nf_hooks 数组的第一个维度,如 IPv4 协议就是 PF_INET。
-
hook:要调用哪一条链(挂载点)上的钩子函数,如 NF_IP_PRE_ROUTING。
-
indev:接收数据包的设备对象。
-
outdev:发送数据包的设备对象。
-
okfn:当链上的所有钩子函数都处理完成,将会调用此函数继续对数据包进行处理。
而 NF_HOOK 宏的实现也比较简单,首先判断一下钩子函数链表是否为空,如果是空的话,就直接调用 okfn 函数来处理数据包,否则就调用 nf_hook_slow 函数来处理数据包。我们来看看 nf_hook_slow 函数的实现:
(编辑:宁德站长网)
【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容!
|