IPv6协议定义了一下五个钩子,它们的名字(名字后面的是它们对应的值)告诉我们它们所处的位置,如图所示:
1. NF_IP6_PRE_ROUTING 0:数据包在抵达路由之前经过这个钩子。目前,在这个钩子上只对数据包作包头检测处理,一般应用于防止拒绝服务攻击和NAT;
2. NF_IP6_LOCAL_IN 1:目的地为本地主机的数据包经过这个钩子。防火墙一般建立在这个钩子上;
3. NF_IP6_FORWARD 2:目的地非本地主机的数据包经过这个钩子;
4. NF_IP6_LOCAL_OUT 3:本地主机发出的数据包经过这个钩子;
5. NF_IP6_POST_ROUTING 4:数据包在离开本地主机之前经过这个钩子,包括源地址为本地主机和非本地主机的。
对照图片,我们分析数据报经过Netfilter机制的过程。数据报从左边进入系统,进行IP校验以后,数据报经过第一个钩子 NF_IP6_PRE_ROUTING注册函数进行处理;然后就进入路由代码,其决定该数据包是需要转发还是发给本机的;若该数据包是发被本机的,则该数据经过钩子NF_IP6_LOCAL_IN注册函数处理以后然后传递给上层协议;若该数据包应该被转发则它被NF_IP6_FORWARD注册函数处理;经过转发的数据报经过最后一个钩子NF_IP6_POST_ROUTING注册函数处理以后,再传输到网络上。
本地产生的数据经过钩子函数NF_IP6_LOCAL_OUT注册函数处理以后,进行路由选择处理,然后经过NF_IP6_POST_ROUTING注册函数处理以后发送到网络上。
注册函数处理完后,将返回一个整形常量,内核根据这个返回值随数据报作下一步的处理。目前内核定义了一下四个常量:
1. NF_DROP 0:丢弃此数据报,而不进入此后的处理;
2. NF_ACCEPT 1:接受此数据报,进入下一步的处理;
3. NF_STOLEN 2:表示异常分组;
4. NF_QUEUE 3:排队到用户空间,等待用户处理;
5. NF_REPEAT 4:进入此函数再作处理。
为了是其它内核模块能够操作数据报,Netfilter为我们提供了接口nf_register_hook(struct nf_hook_ops *reg),函数原型在netfilter.h,定义在netfilter.c,返回值为int类型,注册成功返回零,失败则返回一个负值,参数reg为 nf_hook_ops结构体类型指针。我们在内核模块初始化的时候调用这个接口来注册处理函数,而我们的函数指针则保存在reg指针所指向的 nf_hook_ops。nf_hook_ops定义在netfilter.h:
struct nf_hook_ops
{
struct list_head list;
nf_hookfn *hook;
int pf;
int hooknum;
int priority;
};
list:链表头,用来把各个处理函数组织成一个表,初始化为{NULL,NULL};
hook:我们定义的处理函数的指针,返回值必须为前面所说的几个常量之一;
pf:协议族,表示这个HOOK属于哪个协议族;
hooknum:我们想要注册的钩子,取值为五个钩子之一;
priority:优先级,目前Netfilter定义了一下几个优先级,取值也小优先级也高,我们可以根据需要对各个优先级加减一个常量得到符合我们需要的优先级。
NF_IP6_PRI_FIRST = INT_MIN
NF_IP6_PRI_CONNTRACK = -200
NF_IP6_PRI_MANGLE = -150
NF_IP6_PRI_NAT_DST = -100
NF_IP6_PRI_FILTER = 0
NF_IP6_PRI_NAT_SRC = 100
NF_IP6_PRI_LAST = INT_MAX
接下来我们谈谈处理函数的定义,函数指针的类型为nf_hookfn,它的定义为:
typedef unsigned int nf_hookfn(unsigned int hooknum,
struct sk_buff **skb,
const struct net_device *in,
const struct net_device *out,
int (*okfn)(struct sk_buff *));
所有的这些参数都是由Netfilter传递给我们的处理函数的。除了okfn其它用途都比较易懂,okfn是当对应的钩子的注册函数为空时, Netfilter调用的处理函数,它就是如果我们的处理函数返回Accept时Netfilter调用的处理函数。
