本系列不是介绍How to
配置iptables
的文章。由于网络上已经有不少这类型的教程了,其中一些还不错(好比连接).数组
本系列也不是通常意义上的Netfilter
源码分析文章。由于大段粘贴代码也会让人心生畏惧和厌烦!网络
本系列文章的目标是,用尽可能少的文字和图片讲明白How Netfilter work
框架
Netfilter
是一套融入在Linux
内核网络协议栈中的报文处理(过滤
或者修改
)框架。它在内核中报文的关键流动路径上定义了5
个HOOK
点(下图蓝色方框),各个协议(如IPv4
、IPv6
、ARP
)能够在这些HOOK
点安装钩子函数,报文流经此地,内核会按照优先级调用这些钩子函数,这些钩子函数最终会决定报文是被NF_ACCEPT
(放行)仍是NF_DROP
(丢弃)。函数
图中红色虚线表示内核最多见的报文流经的路径:本机接收、转发、本机发送。5
个HOOK
点分别是:路由前、本地上送、转发、本地发送、路由后1
源码分析
初次接触iptables
的同窗可能会被四表五链
这个名字吓到,特别是链
这个名字真的很容易使人困惑! 而当你了解了Netfilter
的实现细节后,才会发现:噢,原来链
就是HOOK
点,HOOK
点就是链
,由于有5
个HOOK
点,因此有五链
!spa
那么,为何要叫链
呢?.net
由于一个HOOK
点能够上能够安装多个钩子, 内核用“链条”将这些钩子串起来!设计
相比之下,四表(table)
就没那么神秘了: 起过滤做用的filter
表、起NAT
做用的nat
表,用于修改报文的mangle
表,用于取消链接跟踪的raw
表。code
Netfilter
设计多个表的目的,一方面是方便分类管理,另外一方面,更重要的是为了限定各个钩子(或者说用户规则)执行的顺序!教程
以PREROUTING
这个HOOK
点为例,用户使用iptables
设置的NAT
规则和mangle
会分别挂到nat hook
和mangle hook
,NAT
表的优先级天生比mangle
表低,所以报文必定会先执行mangle
表的规则。
这就是四表五链
的概念。我我的认为链
的比表
重要多了. 由于就算Netfilter
没有表的概念,那么经过当心翼翼地设置各个rule
的顺序其实也能够达到相同的效果。但链
(也就是HOOK
点)的做用是独一无二的。换个角度,用户在配置iptables
规则时,更多的精力也是放在 "应该在哪一个HOOK点进行操做",至于用的是filter
表、nat
表仍是其余表,其实都是瓜熟蒂落的事情。
用户经过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, }
hooks
的每一个元素都是链表头,链表上挂的元素类型是struct nf_hook_ops
,这些元素有两个来源,一类来自于Netfilter
初始化时各个表(如filter
)的初始化,另外一类来自于如链接跟踪这样的内部模块。下图展现了第一类来源的元素的挂接状况,它们按优先级排列(数字越小优先级越高),而.hook
就是报文到达对应的路径时会执行的钩子函数。
附:相关内核函数的例子
iptable_filter_init |--xt_hook_link |-- nf_register_hooks |-- nf_register_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
就会被执行。