Netfilter笔记-02
Netfilter说白了就是针对不同的协议(协议类型和hook节点我们上一章已经讲过)在kernel中放置了不同的hook节点,等数据包sk_buff,到来的时候,要给hook节点进行过滤,下图为IP层的五个hook点的位置:
在讲具体流程之前我们要介绍一个结构体:struct nf_hook_ops
struct nf_hook_ops {
struct list_head list; //链表
/* User fills in from here down. */
nf_hookfn *hook; //hook的处理函数
struct module *owner;
u_int8_t pf; //协议类型
unsigned int hooknum; //hook点,也就是上图的5个hook点
/* Hooks are ordered in ascending priority. */
int priority; //优先级(越小优先级越高)
};
从结构体的内容可以看书,它是双向循环链表的数据结构,在kernel代码中很常见到list_head。
下面我们分析hooks的注册流程。
首先我们要注册hook节点,使用函数int nf_register_hook(struct nf_hook_ops *reg),函数实现是在net/netfilter/core.c中
在net/ipv4/netfilter/iptable_nat.c文件中,可以看到内核使用module_init加载模块iptable_nat_init,进一步调用nf_register_hooks函数就行ipv4hook节点的注册,具体代码如下:
static int __init iptable_nat_init(void)
{
int err;
err = register_pernet_subsys(&iptable_nat_net_ops);
if (err < 0)
goto err1;
err = nf_register_hooks(nf_nat_ipv4_ops, ARRAY_SIZE(nf_nat_ipv4_ops));//注册hooks函数,nf_nat_ipv4_ops为一个nf_hook_ops结构体
if (err < 0)
goto err2;
return 0;
err2:
unregister_pernet_subsys(&iptable_nat_net_ops);
err1:
return err;
}
static void __exit iptable_nat_exit(void)
{
nf_unregister_hooks(nf_nat_ipv4_ops, ARRAY_SIZE(nf_nat_ipv4_ops));
unregister_pernet_subsys(&iptable_nat_net_ops);
}
module_init(iptable_nat_init);
module_exit(iptable_nat_exit);
nf_nat_ipv4_ops为nf_hook_ops结构体类型的实现,具体如下:
static struct nf_hook_ops nf_nat_ipv4_ops[] __read_mostly = {
/* Before packet filtering, change destination */
{
.hook = nf_nat_ipv4_in, //hook的调用函数
.owner = THIS_MODULE, //拥有者
.pf = NFPROTO_IPV4, //协议
.hooknum = NF_INET_PRE_ROUTING, //hook节点
.priority = NF_IP_PRI_NAT_DST, //优先级
},
/* After packet filtering, change source */
{
.hook = nf_nat_ipv4_out,
.owner = THIS_MODULE,
.pf = NFPROTO_IPV4,
.hooknum = NF_INET_POST_ROUTING,
.priority = NF_IP_PRI_NAT_SRC,
},
/* Before packet filtering, change destination */
{
.hook = nf_nat_ipv4_local_fn,
.owner = THIS_MODULE,
.pf = NFPROTO_IPV4,
.hooknum = NF_INET_LOCAL_OUT,
.priority = NF_IP_PRI_NAT_DST,
},
/* After packet filtering, change source */
{
.hook = nf_nat_ipv4_fn,
.owner = THIS_MODULE,
.pf = NFPROTO_IPV4,
.hooknum = NF_INET_LOCAL_IN,
.priority = NF_IP_PRI_NAT_SRC,
},
};
我们可以看到nf_nat_ipv4_ops没有对NF_INET_FORWARD进行初始化,这个我们后续讲解。
我们来看一下nf_register_hook函数的具体实现
int nf_register_hook(struct nf_hook_ops *reg)
{
struct nf_hook_ops *elem;
int err;
err = mutex_lock_interruptible(&nf_hook_mutex);
if (err < 0)
return err;
list_for_each_entry(elem, &nf_hooks[reg->pf][reg->hooknum], list) {
if (reg->priority < elem->priority)
break;
}
list_add_rcu(®->list, elem->list.prev);
mutex_unlock(&nf_hook_mutex);
#if defined(CONFIG_JUMP_LABEL)
static_key_slow_inc(&nf_hooks_needed[reg->pf][reg->hooknum]);
#endif
return 0;
}
EXPORT_SYMBOL(nf_register_hook);
nf_hooks为存放hooks的链表,内核使用list_for_each_entry方法遍历nf_hooks,如果新来的reg hook < elem ,那么在elem之前把reg插入到nf_hooks链表中,因为优先级值越小越高,所以链表中的优先级顺序由高到低。
运行完nf_register_hook函数之后,内核是把Ipv4的 5个hooks在kernel中已经注册完成。等待数据包来触发这些hook,下一章我们进行hook接收数据包的流程。