【协议森林】详解Netfilter(一)

1、Netfilter介绍

Netfilter是2.4.x内核引入的,工作在内核空间中,通常结合ip_table内核模块一起使用以构造linux下的防火墙。Linux内核中,netfilter是一个包过滤框架,默认地,它在这个框架上实现了包过滤、状态检测、网络地址转换和包标记等多种功能。因为它设计的开放性,任何有内核开发经验的开发人员,也可以很容易地利用它提供接口,在内核的数据链路层、网络层,实现自己的功能模块。

2、Hook点

Netfilter的整体思想为:嵌入到内核IP协议栈的一系列调用入口,通过挂钩,将钩子函数设置在报文处理的路径上,以达到控制数据包流通的作用。这些路径可以分为三类:流入的、流经的和流出的,具体为PRE_ROUTING,LOCAL_IN,FORWARD,LOCAL_OUT,POST_ROUTING,这些点,简单称之为Hook点。
Netfilter定义了一个二维数组structlist_headnf_hooks[NPROTO][NF_MAX_HOOKS]来保存各个协议的各个挂载点。每一个nf_hooks[协议号][hook点]元素都是一个链表头,所以链表中可以存在多个元素,每个元素都是一个包含有处理函数的结构体。
在这里插入图片描述
由于包总是从PRE_ROUTING/LOCAL_OUT两个钩子进入Netfilter,本文中把这两个钩子叫做入口。包总是从LOCAL_IN/POST_ROUTING两个钩子点走完Netfilter,把这两个钩子叫做出口。
在这里插入图片描述

3、Netfilter原理

Netfilter使用宏NF_HOOK在协议栈内部切入到Netfilter框架中,定义如下:

#defineNF_HOOK(pf,hook,skb,indev,outdev,okfn)\
NF_HOOK_THRESH(pf,hook,skb,indev,outdev,okfn,INT_MIN)

关于宏NF_HOOK各个参数的解释说明:
1)pf:协议族名,Netfilter架构同样可以用于IP层之外,因此这个变量还可以有诸如PF_INET6,PF_DECnet等名字。
2)hook:HOOK点的名字,对于IP层,就是取上面的五个值;
3)skb:不解释;
4)indev:数据包进来的设备,以structnet_device结构表示;
5)outdev:数据包出去的设备,以structnet_device结构表示;
(后面可以看到,以上五个参数将传递给nf_register_hook中注册的处理函数。)
6)okfn:是个函数指针,当所有的该HOOK点的所有登记函数调用完后,转而走此流程。
而NF_HOOK_THRESH又是一个宏:

#defineNF_HOOK_THRESH(pf,hook,skb,indev,outdev,okfn,thresh)\
({int__ret;\
if((__ret=nf_hook_thresh(pf,hook,&(skb),indev,outdev,okfn,thresh,1))==1)\
__ret=(okfn)(skb);\
__ret;})

NF_HOOK_THRESH宏内部调用了nf_hook_thresh函数。nf_hook_thresh通过传入的参数pf协议号和hook点,找到链表头指针nf_hooks[协议号][hook点],如图2-4所示,然后遍历调用在链表上注册过的函数hookfn,并根据hookfn的调用返回值的情况来进行相应处理。
在这里插入图片描述
由于多个函数都会被调用,并且被调用的函数还能决定该数据包的去留,所以存在一个优先级thresh(宏定义中),thresh来规定,哪个函数先被调用,哪个函数后被调用。优先级数值越小表示优先级越高,如果优先级一样,则按照注册的时候,挂载的先后顺序依次处理。见下面部分代码:

list_for_each_continue_rcu(*i,head){	/*遍历*/
		structnf_hook_ops*elem=(structnf_hook_ops*)*i;			/*获取元素*/

		if(hook_thresh>elem->priority)	/*优先级比hook_thresh高(值小)的执行*/
			continue;
		verdict=elem->hook(hook,skb,indev,outdev,okfn);/*调用hook函数*/
		……
}

上述的元素是用到了下面这个结构体:

structnf_hook_ops{
	structlist_headlist;
	nf_hookfn*hook;			/*回调函数*/
	structmodule*owner;		/*归属模块*/
	u_int8_tpf;				/*协议号*/
	unsignedinthooknum;		/*Hook点*/
	intpriority;				/*调用的优先级*/
};

注册一个钩子回调函数通过调用注册函数nf_register_hook来实现,nf_register_hook内部实现上插入到链表的时候,根据优先级priority进行排序插入,递归调用的时候,就直接使高优先级的先被调用,低优先级后被调用。

4、Netfilter切入点

1.net/ipv4/ip_input.c里的ip_rcv函数。该函数主要用来处理网络层的IP报文的入口函数,它到Netfilter框架的切入点为:

NF_HOOK(PF_INET,NF_IP_PRE_ROUTING,skb,dev,NULL,ip_rcv_finish)

如果协议栈当前收到了一个IP报文(PF_INET),那么就把这个报文传到Netfilter的NF_IP_PRE_ROUTING过滤点,去检查在过滤点(nf_hooks[2][0])是否已经有人注册了相关的用于处理数据包的钩子函数。如果有,则挨个去遍历链表nf_hooks[2][0]去寻找匹配的match和相应的target,根据返回到Netfilter框架中的值来进一步决定该如何处理该数据包(由钩子模块处理还是交由ip_rcv_finish函数继续处理)。
2.net/ipv4/ip_forward.c中的ip_forward函数,它的切入点为:

NF_HOOK(PF_INET,NF_IP_FORWARD,skb,skb->dev,rt->u.dst.dev,ip_forward_finish);

在经过路由抉择后,所有需要本机转发的报文都会交由ip_forward函数进行处理。这里,该函数由NF_IP_FOWARD过滤点切入到Netfilter框架,在nf_hooks[2][2]过滤点执行匹配查找。最后根据返回值来确定ip_forward_finish函数的执行情况。
3.net/ipv4/ip_output.c中的ip_output函数,它切入Netfilter框架的形式为:

NF_HOOK_COND(PF_INET,NF_IP_POST_ROUTING,skb,NULL,dev,ip_finish_output,!(IPCB(skb)->flags&IPSKB_REROUTED));

这里我们看到切入点从无条件宏NF_HOOK改成了有条件宏NF_HOOK_COND,调用该宏的条件是:如果协议栈当前所处理的数据包skb中没有重新路由的标记,数据包才会进入Netfilter框架。否则直接调用ip_finish_output函数走协议栈去处理。同样,如果需要陷入Netfilter框架则数据包会在nf_hooks[2][4]过滤点去进行匹配查找。
4.在net/ipv4/ip_input.c中的ip_local_deliver函数。该函数处理所有目的地址是本机的数据包,其切入函数为:

NF_HOOK(PF_INET,NF_IP_LOCAL_IN,skb,skb->dev,NULL,ip_local_deliver_finish);

发给本机的数据包,首先全部会去nf_hooks[2][1]过滤点上检测是否有相关数据包的回调处理函数,如果有则执行匹配和动作,最后根据返回值执行ip_local_deliver_finish函数。
5.net/ipv4/ip_output.c中的ip_push_pending_frames函数。该函数是将IP分片重组成完整的IP报文,然后发送出去。进入Netfilter框架的切入点为:

NF_HOOK(PF_INET,NF_IP_LOCAL_OUT,skb,NULL,skb->dst->dev,dst_output);

对于所有从本机发出去的报文都会首先去Netfilter的nf_hooks[2][3]过滤点去过滤。

加入我们

在这里插入图片描述

  • 3
    点赞
  • 28
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值