写在前面
本系列 不是 介绍 How to
配置 iptables
的文章。因为网络上已经有很多这类型的教程了,其中一些还不错(比如链接).
本系列也 不是 一般意义上的 Netfilter
源码分析文章。因为大段粘贴代码也会让人心生畏惧和厌烦!
本系列文章的目标是,用尽量少的文字和图片讲明白How Netfilter work
Netfilter 的基本概念
Netfilter
是一套 融入 在Linux
内核网络协议栈中的报文处理 ( 过滤
或者 修改
) 框架。它在内核中报文的关键流动路径上定义了 5
个HOOK
点 (下图蓝色方框),各个协议(如IPv4
、IPv6
、ARP
) 可以在这些 HOOK
点安装钩子函数,报文流经此地,内核会按照优先级调用这些钩子函数,这些钩子函数最终会决定报文是被 NF_ACCEPT
(放行) 还是NF_DROP
(丢弃)。
图中红色虚线表示内核最常见的报文流经的路径:本机接收 、 转发 、 本机发送 。5
个HOOK
点分别是:路由前 、 本地上送 、 转发 、 本地发送 、 路由后1
链(chain) & 表(table)
初次接触 iptables
的同学可能会被 四表五链
这个名字吓到, 特别是 链
这个名字真的很容易令人困惑! 而当你了解了 Netfilter
的实现细节后,才会发现:噢,原来 链
就是 HOOK
点,HOOK
点就是 链
,因为有 5
个HOOK
点,所以有 五链
!
那么,为什么要叫 链
呢?
因为一个 HOOK
点可以上可以安装多个钩子, 内核用“链条”将这些钩子串起来!
相比之下,四表 (table)
就没那么神秘了: 起过滤作用的 filter
表、起 NAT
作用的 nat
表,用于修改报文的 mangle
表, 用于取消连接跟踪的 raw
表。
Netfilter
设计多个表的目的,一方面是方便分类管理,另一方面,更重要的是为了限定各个钩子 (或者说用户规则) 执行的顺序!
以 PREROUTING
这个 HOOK
点为例,用户使用 iptables
设置的 NAT
规则和 mangle
会分别挂到 nat hook
和mangle hook
,NAT
表的优先级天生比 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_IPV6
的hook
;第二个维度是 链
, 对于 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 namespace 为net
的 hooks[NFPROTO_IPV4][NF_INET_PRE_ROUTING]
链表上的元素, 也就是上面图中的第一行的链表。如果三个钩子函数执行的结果 (verdict
) 都是 NF_ACCEPT
,那么NF_HOOK
指定的 ip_rcv_finish
就会被执行。