乐趣区

Netfilter-是如何工作的一HOOK点

写在前面

本系列 不是 介绍 How to 配置 iptables 的文章。因为网络上已经有很多这类型的教程了,其中一些还不错(比如链接).

本系列也 不是 一般意义上的 Netfilter 源码分析文章。因为大段粘贴代码也会让人心生畏惧和厌烦!

本系列文章的目标是,用尽量少的文字和图片讲明白How Netfilter work

Netfilter 的基本概念

Netfilter是一套 融入 Linux内核网络协议栈中的报文处理 ( 过滤 或者 修改 ) 框架。它在内核中报文的关键流动路径上定义了 5HOOK点 (下图蓝色方框),各个协议(如IPv4IPv6ARP) 可以在这些 HOOK 点安装钩子函数,报文流经此地,内核会按照优先级调用这些钩子函数,这些钩子函数最终会决定报文是被 NF_ACCEPT(放行) 还是NF_DROP(丢弃)。

图中红色虚线表示内核最常见的报文流经的路径:本机接收 转发 本机发送
5HOOK点分别是:路由前 本地上送 转发 本地发送 路由后1

链(chain) & 表(table)

初次接触 iptables 的同学可能会被 四表五链 这个名字吓到, 特别是 这个名字真的很容易令人困惑! 而当你了解了 Netfilter 的实现细节后,才会发现:噢,原来 就是 HOOK 点,HOOK点就是 ,因为有 5HOOK点,所以有 五链

那么,为什么要叫 呢?

因为一个 HOOK 点可以上可以安装多个钩子, 内核用“链条”将这些钩子串起来!


相比之下,四表 (table) 就没那么神秘了: 起过滤作用的 filter 表、起 NAT 作用的 nat 表,用于修改报文的 mangle 表, 用于取消连接跟踪的 raw 表。

Netfilter设计多个表的目的,一方面是方便分类管理,另一方面,更重要的是为了限定各个钩子 (或者说用户规则) 执行的顺序!

PREROUTING 这个 HOOK 点为例,用户使用 iptables 设置的 NAT 规则和 mangle 会分别挂到 nat hookmangle hookNAT表的优先级天生比 mangle 表低,因此报文一定会先执行 mangle 表的规则。

这就是 四表五链 的概念。我个人认为的比 重要多了. 因为就算 Netfilter 没有表的概念,那么通过小心翼翼地设置各个 rule 的顺序其实也可以达到相同的效果。但 (也就是 HOOK 点)的作用是独一无二的。换个角度,用户在配置 iptables 规则时,更多的精力也是放在 “ 应该在哪个 HOOK 点进行操作 ”,至于用的是filter 表、nat表还是其他表,其实都是顺理成章的事情。

Hook

HOOK 点的位置

用户通过 iptables 配置的规则最终会记录在 HOOK 点。HOOK点定义在 struct net 结构中,即 HOOK 点是各个 net namespace 中独立的。所以,在使用容器的场景中,每个容器的防火墙规则是独立的。

struct net {
    /* code omitted */
    struct netns_nf        nf;
    /* code omitted */
}

struct netns_nf {
    /* code omitted */
    struct list_head hooks[NFPROTO_NUMPROTO][NF_MAX_HOOKS];
};

从上面的定义可以看到,HOOK点是一个二维数组,每个元素都是一个 链表头 。它的第一个维度是协议类型,其中最常用的NFPROTO_IPV4,我们使用的iptables 命令都是将这个钩子安装到这个协议的 hook,而使用ip6tables 就是将钩子安装到 NFPROTO_IPV6hook;第二个维度是 , 对于 IPV4 来说,它的取值范围如下:

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 点的元素

hooks的每个元素都是链表头,链表上挂的元素类型是 struct nf_hook_ops,这些元素有两个来源,一类来自于Netfilter 初始化时各个表 (如filter) 的初始化,另一类来自于如连接跟踪这样的内部模块。下图展示了 第一类 来源的元素的挂接情况,它们按优先级排列 (数字越小优先级越高),而.hook 就是报文到达对应的路径时会执行的钩子函数。

附:相关内核函数的例子

iptable_filter_init 
  |--xt_hook_link
    |-- nf_register_hooks
       |-- nf_register_hook

HOOK 点的调用

Netfilter框架已经完全融入内核协议栈了,所以在协议栈代码中常常可以看到 NF_HOOK 宏的调用,这个宏的参数指定了 HOOK 点。

以本机收到 IPv4 报文为例

int ip_rcv(struct sk_buff* skb,...)
{
   // code omitted
   return NF_HOOK(NFPROTO_IPV4, NF_INET_PRE_ROUTING, net, NULL, skb, dev, NULL, ip_rcv_finish); 
   // code omitted
}

它指定要遍历的钩子函数是 net namespacenethooks[NFPROTO_IPV4][NF_INET_PRE_ROUTING] 链表上的元素, 也就是上面图中的第一行的链表。如果三个钩子函数执行的结果 (verdict) 都是 NF_ACCEPT,那么NF_HOOK 指定的 ip_rcv_finish 就会被执行。

退出移动版