第三部分
Netfilter核心
10 Netfilter钩子函数
网络代码中有若干调用是涉及到Netfilter的,并且所有的东西都和它绑定(也就是模块被加载)到netfilter框架中。最普遍的钩子组成部分是xtables(防火墙)、连接跟踪、IPv4的NAT引擎和IPVS的虚拟服务器。Ip6_input.c里面就调用了这个特定的钩子函数:
return NF_HOOK(NFPROTO_IPV6, NF_INET_PRE_ROUTING, skb,
dev, NULL, ip6_rcv_finish);
这里调用了所有的prerouting的钩子。NF_HOOK是一个大的宏,最后它将进入实际的函数nf_hook_slow,我将这些留给特殊的开发者,让你们自己重新编写查看这部分。(不用紧张,这里只是训练你。)
10.1 框架结构体
在struct nf_hook_ops中为每个钩子包含有vtable(虚函数表?)和元数据,例如名字和关联的协议。这个结构体的定义可以通过包含
struct nf_hook_ops {
unsigned int pf;
unsigned int priority;
unsigned int hooknum;
unsigned int (*hookfn)(unsigned int hooknum, struct sk_buff *skb,
const struct net_device *in,
const struct net_device *out,
int (*okfn)(struct sk_buff *));
struct module *owner;
}
10.2 初始化
结构体成员pf关系到这个钩子属于哪一个函数回调组。可能的值都列在netfilter.h中,最常见的值是NFPROTO_IPV6、NFPROTO_IPV4、NFPROTO_ARP和 NFPROTO_BRIDGE。在net/ipv4/目录中,你只能找到NFPROTO_IPV4,在net/ipv6/中,只能找到NFPROTO_IPV6。
成员priority则标示了这个钩子函数的调用顺序。一些标志性的静态变量定义在
因为有Xtables模块,这个注册结构体一定不能使const的,因为对应的区域可能会随着实现被修改。
static struct nf_hook_ops myhook_ops __read_mostly = {
.pf = PF_INET6,
.priority = 1,
.hooknum = NF_INET_LOCAL_OUT,
.hookfn = myhook_fn,
};
static int __init myhook_init(void)
{
return nf_register_hook(&myhook_ops);
}
static void __exit myhook_exit(void)
{
nf_unregister_hook(&myhook_ops);
}
module_init(myhook_init);
module_exit(myhook_exit);
这个钩子框架自己本身也不关注hooknum。一个钩子调用指定了一个特定的hooknum,hook模块可能会根据它来做一些事情。它通常是用于标示这个调用是从哪个位置来的:在前面(5.3节)提到Xtables、NAT和连接跟踪有五个静态变量,NF_INET_{PRE,POST}_ROUTING、NF_INET_LOCAL_{IN,OUT}和NF_INET_LOCAL_OUT。
10.3 主要函数
static unsigned int myhook_fn(unsigned int hooknum, struct sk_buff *skb,
const struct net_device *in, const struct net_device *out,
int (*okfn)(struct sk_buff *))
{
pr_info("Arr matey! - Captain Hook approves your packet!\n");
return NF_ACCEPT;
}
一个钩子函数的可能的返回值和target的返回值非常相似。不过没有XT_RETURN,因为没有可以跳转的链我们可以返回,所以这个返回值在这里是没有意义的。同样也没有XT_CONTINUE,在最好的情况下等于NF_ACCEPT,意味着这个钩子函数允许这个包通过。
其他值都意味这个包被消化了,在头文件linux/netfilter.h中定义。(所以所有的NF_*静态变量都可以被使用,参考5.4节)
有很多的返回值定义都不是普遍被使用,更多的是被netfilter内部使用,例如
NF_QUEUE,被xt_NFQUEUE使用,用于将包中继到用户空间处理。
NF_STOLEN,相对于NF_DROP将数据包丢弃并且释放掉skb,NF_STOLEN只是表示这个钩子函数接管了这个skb数据包,netfilter只需要丢弃。Netfilter忘记了这个数据包,并不意味这这个数据包丢失了—这个钩子函数可能将会重新发送或者延长发送。
NF_REPEAT,这个数据包将会重新执行这个钩子函数。连接跟踪使用这个返回值用于简化代码路径。
NF_STOP,在功能上和NF_ACCEPT相同。