linux 内核 netfilter 网络过滤模块 (1)-框架 怼烎@ 2021-07-28 15:25 743阅读 0赞 ### 1. netfilter框架 ### Netfilter 是Linux内核中进行数据包过滤、连接跟踪、地址转换等的主要实现框架。当我们希望过滤特定的数据包或者需要修改数据包的内容再发送出去,这些动作主要都在netfilter中完成。 iptables工具就是用户空间和内核的Netfilter模块通信的手段,iptables命令提供很多选项来实现过滤数据包的各种操作,所以,我们在定义数据包过滤规则时,并不需要去直接修改内核中的netfilter模块,后面会讲到iptables命令如何作用于内核中的netfilter。 Netfilter的实质就是定义一系列的hook点(挂钩),每个hook点上可以挂载多个hook函数,hook函数中就实现了我们要对数据包的内容做怎样的修改、以及要将数据包放行还是过滤掉。数据包进入netfilter框架后,实际上就是依次经过所有hook函数的处理,数据包的命运就掌握在这些hook函数的手里。 本文基于内核版本2.6.31。 所有的hook点都放在一个全局的二维数组,每个hook点上的hook函数按照优先级顺序注册到一个链表中,注册的接口为nf\_register\_hook()。这个二维数组的定义如下: struct list_head nf_hooks[NFPROTO_NUMPROTO][NF_MAX_HOOKS]__read_mostly; 其中NFPROTO\_NUMPROTO 为netfilter支持的协议类型: enum { NFPROTO_UNSPEC= 0, NFPROTO_IPV4 = 2, NFPROTO_ARP = 3, NFPROTO_BRIDGE= 7, NFPROTO_IPV6 = 10, NFPROTO_DECNET= 12, NFPROTO_NUMPROTO, }; 我们看到枚举值并不连续,这是为了和协议族定义的值保持一致,因为注册hook函数的时候是根据协议族来注册的,如PF\_INET=2,则对应的NFPROTO\_IPV4=2。 NF\_MAX\_HOOKS是每种协议最多可挂的hook点的个数,它的值是8。如IPv4挂了5个,分别为: 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 }; 也就是说,对于IPv4协议来讲,一共有5个hook点,这5个hook点上注册的hook函数放在下列链表中: <table> <tbody> <tr> <td style="vertical-align:top;"> <p>Hook点</p> </td> <td style="vertical-align:top;"> <p>该hook点上注册的hook函数链表的表头</p> </td> </tr> <tr> <td style="vertical-align:top;"> <p>NF_INET_PRE_ROUTING</p> </td> <td style="vertical-align:top;"> <p>nf_hooks[NFPROTO_IPV4][ NF_INET_PRE_ROUTING]</p> </td> </tr> <tr> <td style="vertical-align:top;"> <p>NF_INET_LOCAL_IN</p> </td> <td style="vertical-align:top;"> <p>nf_hooks[NFPROTO_IPV4][ NF_INET_LOCAL_IN]</p> </td> </tr> <tr> <td style="vertical-align:top;"> <p>NF_INET_FORWARD</p> </td> <td style="vertical-align:top;"> <p>nf_hooks[NFPROTO_IPV4][ NF_INET_FORWARD]</p> </td> </tr> <tr> <td style="vertical-align:top;"> <p>NF_INET_LOCAL_OUT</p> </td> <td style="vertical-align:top;"> <p>nf_hooks[NFPROTO_IPV4][ NF_INET_LOCAL_OUT]</p> </td> </tr> <tr> <td style="vertical-align:top;"> <p>NF_INET_POST_ROUTING</p> </td> <td style="vertical-align:top;"> <p>nf_hooks[NFPROTO_IPV4][ NF_INET_POST_ROUTING]</p> </td> </tr> </tbody> </table> 对于发往本地的数据包,会依次经过NF\_INET\_PRE\_ROUTING和NF\_INET\_LOCAL\_IN两个hook点的处理。 对于本地向外发出去的数据包,会依次经过NF\_INET\_LOCAL\_OUT和NF\_INET\_POST\_ROUTING两个hook点的处理。 对于经过本机转发的数据包,会依次经过F\_INET\_PRE\_ROUTING、NF\_INET\_FORWARD和NF\_INET\_POST\_ROUTING三个hook点的处理。 ![Center][] ### 2. netfilter中的表 ### 为了便于和用户态的iptables命令交互,引入了表的概念,iptables命令定义了各种规则,这些规则的目的就是对数据包进行过滤和处理。为了将规则分类,在内核中,用户设置的规则被放到了不同的表中。同时也方便管理和查找规则。 netfilter中的表存放在***&init\_net***\->xt.tables\[NFPROTO\_NUMPROTO\]链表数组中,tables是一个list\_head类型的链表,NFPROTO\_NUMPROTO是一个枚举类型,包括各协议族,上面已经提到过。 可以看出每个协议有单独的链表来存放自己所有的netfilter的表,例如IPv4一共有5张表: filter表:对数据包进行过滤。 NAT表:对数据包进行地址转换。 mangle表:主要用来修改数据包。 security表:用于实现强制访问控制安全模型。 raw表:其他各种用途。 常用的两个表就是filter表(用来进行数据包过滤)和NAT表(NAT转换规则以及作用于连接跟踪)。 而hook函数就是去遍历这些表中的规则,并根据这些规则去处理数据包。 这些表都是struct xt\_table类型的。例如ipv4的netfilter表称为iptables,可以在***&init\_net*** ->ipv4中找到,如下: struct xt_table *iptable_filter; struct xt_table *iptable_mangle; struct xt_table *iptable_raw; struct xt_table *arptable_filter; struct xt_table *iptable_security; struct xt_table *nat_table; 表结构的注册通过xt\_register\_table()函数完成,注册的表都放在***&init\_net*** ->xt.tables\[\]链表数组中。 而查找和执行表的规则是通过xxt\_do\_table()完成的,如ipt\_do\_table()、ip6t\_do\_table()、arpt\_do\_table()。 我们暂时只关心ipv4,通过ipt\_do\_table()函数的调用者我们可以看到哪些hook点会去查找哪些表: <table> <tbody> <tr> <td style="vertical-align:top;"> <p>caller</p> </td> <td style="vertical-align:top;"> <p>module</p> </td> <td style="vertical-align:top;"> <p>hook点</p> </td> <td style="vertical-align:top;"> <p>priority</p> </td> <td style="vertical-align:top;"> <p>table</p> </td> </tr> <tr> <td style="vertical-align:top;"> <p>ipt_local_in_hook</p> </td> <td style="vertical-align:top;"> <p>iptable filter</p> </td> <td style="vertical-align:top;"> <p>NF_INET_LOCAL_IN</p> </td> <td style="vertical-align:top;"> <p>NF_IP_PRI_FILTER</p> </td> <td style="vertical-align:top;"> <p>filter</p> </td> </tr> <tr> <td style="vertical-align:top;"> <p>ipt_hook</p> </td> <td style="vertical-align:top;"> <p>iptable filter</p> </td> <td style="vertical-align:top;"> <p>NF_INET_FORWARD</p> </td> <td style="vertical-align:top;"> <p>NF_IP_PRI_FILTER</p> </td> <td style="vertical-align:top;"> <p>filter</p> </td> </tr> <tr> <td style="vertical-align:top;"> <p>ipt_local_out_hook</p> </td> <td style="vertical-align:top;"> <p>iptable filter</p> </td> <td style="vertical-align:top;"> <p>NF_INET_LOCAL_OUT</p> </td> <td style="vertical-align:top;"> <p>NF_IP_PRI_FILTER</p> </td> <td style="vertical-align:top;"> <p>filter</p> </td> </tr> <tr> <td style="vertical-align:top;"> <p>ipt_pre_routing_hook</p> </td> <td style="vertical-align:top;"> <p>iptables mangle</p> </td> <td style="vertical-align:top;"> <p>NF_INET_PRE_ROUTING</p> </td> <td style="vertical-align:top;"> <p>NF_IP_PRI_MANGLE</p> </td> <td style="vertical-align:top;"> <p>mangle</p> </td> </tr> <tr> <td style="vertical-align:top;"> <p>ipt_local_in_hook</p> </td> <td style="vertical-align:top;"> <p>iptables mangle</p> </td> <td style="vertical-align:top;"> <p>NF_INET_LOCAL_IN</p> </td> <td style="vertical-align:top;"> <p>NF_IP_PRI_MANGLE</p> </td> <td style="vertical-align:top;"> <p>mangle</p> </td> </tr> <tr> <td style="vertical-align:top;"> <p>ipt_forward_hook</p> </td> <td style="vertical-align:top;"> <p>iptables mangle</p> </td> <td style="vertical-align:top;"> <p>NF_INET_FORWARD</p> </td> <td style="vertical-align:top;"> <p>NF_IP_PRI_MANGLE</p> </td> <td style="vertical-align:top;"> <p>mangle</p> </td> </tr> <tr> <td style="vertical-align:top;"> <p>ipt_local_hook</p> </td> <td style="vertical-align:top;"> <p>iptables mangle</p> </td> <td style="vertical-align:top;"> <p>NF_INET_LOCAL_OUT</p> </td> <td style="vertical-align:top;"> <p>NF_IP_PRI_MANGLE</p> </td> <td style="vertical-align:top;"> <p>mangle</p> </td> </tr> <tr> <td style="vertical-align:top;"> <p>ipt_post_routing_hook</p> </td> <td style="vertical-align:top;"> <p>iptables mangle</p> </td> <td style="vertical-align:top;"> <p>NF_INET_POST_ROUTING</p> </td> <td style="vertical-align:top;"> <p>NF_IP_PRI_MANGLE</p> </td> <td style="vertical-align:top;"> <p>mangle</p> </td> </tr> <tr> <td style="vertical-align:top;"> <p>ipt_local_in_hook</p> </td> <td style="vertical-align:top;"> <p>iptables security</p> </td> <td style="vertical-align:top;"> <p>NF_INET_LOCAL_IN</p> </td> <td style="vertical-align:top;"> <p>NF_IP_PRI_SECURITY</p> </td> <td style="vertical-align:top;"> <p>security</p> </td> </tr> <tr> <td style="vertical-align:top;"> <p>ipt_forward_hook</p> </td> <td style="vertical-align:top;"> <p>iptables security</p> </td> <td style="vertical-align:top;"> <p>NF_INET_FORWARD</p> </td> <td style="vertical-align:top;"> <p>NF_IP_PRI_SECURITY</p> </td> <td style="vertical-align:top;"> <p>security</p> </td> </tr> <tr> <td style="vertical-align:top;"> <p>ipt_local_out_hook</p> </td> <td style="vertical-align:top;"> <p>iptables security</p> </td> <td style="vertical-align:top;"> <p>NF_INET_LOCAL_OUT</p> </td> <td style="vertical-align:top;"> <p>NF_IP_PRI_SECURITY</p> </td> <td style="vertical-align:top;"> <p>security</p> </td> </tr> <tr> <td style="vertical-align:top;"> <p>ipt_hook</p> </td> <td style="vertical-align:top;"> <p>iptables raw</p> </td> <td style="vertical-align:top;"> <p>NF_INET_PRE_ROUTING</p> </td> <td style="vertical-align:top;"> <p>NF_IP_PRI_RAW</p> </td> <td style="vertical-align:top;"> <p>raw</p> </td> </tr> <tr> <td style="vertical-align:top;"> <p>ipt_local_hook</p> </td> <td style="vertical-align:top;"> <p>iptables raw</p> </td> <td style="vertical-align:top;"> <p>NF_INET_LOCAL_OUT</p> </td> <td style="vertical-align:top;"> <p>NF_IP_PRI_RAW</p> </td> <td style="vertical-align:top;"> <p>raw</p> </td> </tr> <tr> <td style="vertical-align:top;"> <p>nf_nat_rule_find</p> </td> <td style="vertical-align:top;"> <p>nat</p> </td> <td style="vertical-align:top;"> <p>NAT的4个hook点</p> </td> <td style="vertical-align:top;"> <p>NF_IP_PRI_NAT_DST/</p> <p>NF_IP_PRI_NAT_SRC</p> </td> <td style="vertical-align:top;"> <p>nat</p> </td> </tr> </tbody> </table> 可以看到iptable有5张表:filter、mangle、security、raw和nat表。这些表中的规则可以通过用户态的iptables程序来配置,netfilter中的hook函数会去根据用户配置的iptables规则来处理数据包。 ![Center 1][] 上图是IPv4协议中每个hook点上注册的hook函数(虚线框中的函数,按照箭头方向的优先级顺序被调用),注意,图中假设security/mangle/raw这三个表中没有规则,所以没有相应的hook函数。netfilter的入口点为ip\_rcv()函数。图中有四种hook函数: 1. 紫色的两个函数用来对分片包进行重组。 2. 蓝色的四个函数用来实现数据包的连接跟踪(conntrack)模块。 3. 绿色的三个函数用来用来进行数据包的过滤(查找filter表中的规则)。 4. 粉色的四个函数用来实现NAT地址转换(查找NAT表中的规则)。 ### 3. 遍历hook函数 ### 上一节我们看到了每个hook点上都注册了哪些hook函数,每当数据包经过某个hook点时,netfilter就遍历这个hook上的所有hook函数来处理数据包。遍历的宏为: NF_HOOK(pf, hook, skb, indev, outdev, okfn) 参数解释: pf:协议族,如PF\_INET对应IPv4。 hook:指明要遍历那个hook点,如NF\_INET\_PRE\_ROUTING。 skb:待处理的数据包的sk\_buff结构指针。 indev:数据包的来源设备。 outdev:数据包的目的去向设备。 okfn:函数指针。如果数据包成功走完hook点上的所有hook函数,接下来执行okfn函数,函数名一般名为xxx\_finish。 NF\_HOOK()调用nf\_hook\_slow()来执行遍历动作,其实现就是去遍历注册到nf\_hooks\[pf\]\[hook\]链表中的hook函数并执行,根据返回结果来决定数据包的去向。 hook函数返回值的说明: * NF\_ACCEPT: 接受分组,使之穿过网络实现中剩余的协议层(或该hook点上后续的hook函数)。 * NF\_STOLEN: 挂钩函数窃取了一个分组,并处理了该分组,此时,该分组已与内核无关,不必再调用其他挂钩,还必须取消其他协议层的处理。 * NF\_DROP: 丢弃分组,其中的数据可以释放了。 * NF\_QUEUE: 将分组置于一个等待队列上,以便其数据可以由用户空间代码处理。不会执行其他hook函数。 * NF\_REPEAT: 再次调用该hook函数。 ### 4. netfilter的一些补充 ### **※. **每个hook代码都分为两部分,即hook函数和xx\_finish()函数,这样做,是因为编译内核时可以选择不编译netfilter模块,xx\_finish函数的内容是即使没有netfilter模块,内核也要必须对数据包做的事情。 **※. ** netfilter可以指定一个优先级,低于这个优先级的hook函数不被执行,nf\_hook\_thresh(..., int thresh, int cond)函数的thresh参数就是优先级。同时还可以通过cond参数(取0或1)更干脆的取消整个链的遍历。 **※. **我们发现遍历hook的代码都放在函数的最后,如下面代码中的ip\_forward\_finish函数, NF_HOOK(PF_INET, NF_INET_FORWARD, skb, skb->dev,rt->u.dst.dev, ip_forward_finish); 这样的话,ip\_forward\_finish函数即使不被声明为inline,其执行效率也挺高,因为GNU C有一个尾部过程调用的优化,省去了函数返回的开销。 **※. ** hook点NF\_INET\_PRE\_ROUTING在协议收包时进入。而NF\_INET\_LOCAL\_OUT在上层发包的时候进入,在ip\_push\_pending\_frames(),ip\_queue\_xmit()和raw\_send\_hdrine()等函数会去走该hook点。 后面几篇文章会依次介绍netfilter中的连接跟踪和NAT模块,并分析用户态的iptables工具是如何作用于netfilter的,为了方便和netfilter交互,iptables工具中也有表(table)的概念,并将hook点称为链(chain)。 [Center]: /images/20210728/8ced2d83e74240be94a8710289352e3d.png [Center 1]: /images/20210728/054c83687fb744d9b1495f6bd0a56b73.png
相关 Linux 网络层收发包流程及 Netfilter 框架浅析 1. 前言 本文主要对 Linux 系统内核协议栈中网络层接收,发送以及转发数据包的流程进行简要介绍,同时对 Netfilter 数据包过滤框架的基本原理以及使用方式进行简 朱雀/ 2023年07月17日 07:08/ 0 赞/ 178 阅读
相关 走进Linux内核之Netfilter框架 走进Linux内核之Netfilter框架 > 初次发表[掘金][Link 1] > 笔者此前对Linux内核相关模块稍有研究,实现内核级通信加密、视频流加密等,涉及: 水深无声/ 2022年09月12日 04:55/ 0 赞/ 226 阅读
相关 西安邮电1-linux内核编程模块 西安邮电1-linux内核编程模块 内核模块,全称为动态可加载内核模块,LKM 。不可独立运行,可单独编译,运行时被链接到内核作为内核的一部分在内核空间运行。模块通常由一 淩亂°似流年/ 2022年09月10日 04:22/ 0 赞/ 220 阅读
相关 内核之旅 --- 内核模块学习1---内核模块参数传递 内核模块的参数传递: 内核模块在加载时是可以添加参数的,但是支持类型有所改变。 首先,内核模块中的变量如果需要使用外界传递的参数需要使用特定的宏 module\_par 不念不忘少年蓝@/ 2022年08月05日 08:50/ 0 赞/ 499 阅读
相关 linux 内核 netfilter 网络过滤模块 (3)-NAT 本文对netfilter中NAT部分的源码进行分析,读者需要先对NAT的基本概念有一个大致了解。 1. NAT模块的初始化 NAT模块的初始化过程主要是初始化一些 我会带着你远行/ 2021年07月29日 00:48/ 0 赞/ 661 阅读
相关 linux 内核 netfilter 网络过滤模块 (1)-框架 1. netfilter框架 Netfilter 是Linux内核中进行数据包过滤、连接跟踪、地址转换等的主要实现框架。当我们希望过滤特定的数据包或者需要修改数据包的内容 怼烎@/ 2021年07月28日 15:25/ 0 赞/ 744 阅读
相关 linux 内核 netfilter 网络过滤模块 (4)-期望连接 传统的conntrack和NAT处理只对IP层和传输层头部进行转换处理,但是一些应用层协议,在协议数据报文中包含了地址信息。为了使得这些应用也能透明地完成NAT转换,NAT使用 谁借莪1个温暖的怀抱¢/ 2021年07月28日 15:13/ 0 赞/ 461 阅读
相关 linux 内核中基于netfilter的编译选项 在内核配置文件中要启用一些较要的选项包括Netfilter连接跟踪、日志记录和包过滤。iptables 是通过使用由Netfilter提供的内核中的框架来建立一个策略的。 绝地灬酷狼/ 2021年07月28日 12:22/ 0 赞/ 779 阅读
相关 linux 内核 netfilter 网络过滤模块 (5)-iptables iptables是用户态的配置工具,用于实现网络层的防火墙,用户可以通过iptables命令设置一系列的过滤规则,来截获特定的数据包并进行过滤或其他处理。 iptables命 谁践踏了优雅/ 2021年07月28日 11:32/ 0 赞/ 402 阅读
还没有评论,来说两句吧...