Netfilter笔记-02

Netfilter说白了就是针对不同的协议(协议类型和hook节点我们上一章已经讲过)在kernel中放置了不同的hook节点,等数据包sk_buff,到来的时候,要给hook节点进行过滤,下图为IP层的五个hook点的位置:

Netfilter笔记-02

 

在讲具体流程之前我们要介绍一个结构体: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(&reg->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接收数据包的流程。