netfilter框架概述

1 Netfilter

1.1 netfilter概述

Netfilter是linux 2.4.x引入的一个子系统,它是一个通用的、抽象的框架。其不依赖于具体的协议,而是为每种网络协议定义一套HOOK函数。这些HOOK函数在数据报经过协议栈的几个关键点被调用。 内核的任何模块可以对每种协议的一个或多个HOOK进行注册,实现挂接。这样当某个数据报被传递给Netfilter框架时,内核能检测到是否有任何模块对该协议和HOOK函数进行了注册。若注册了,则调用该模块的注册时使用的回调函数,这样这些模块就有机会检查、修改、丢弃该数据报及指示Netfilter将该数据报传入用户空间的队列。

1.2 Netfilter框架

1.2.1 Netfilter钩子点

netfilter五个钩子点在协议栈的位置:

在这里插入图片描述

netfilter分别在这五个钩子点嵌入NF_HOOK函数,然后通过NF_HOOK函数进入netfilter框架

1.2.2 不同方向报文所经过的钩子点

本机往外发的报文:

在这里插入图片描述

发往本机的报文:
在这里插入图片描述

由本机转发的报文:

在这里插入图片描述

1.2.3 钩子的入口函数
PREROUTINGINPUTFORWARDOUTPUTPOSTROUTING
ip_rcvip_local_deliverip_forward__ip_local_outip_output

1.3 Netfilter核心代码分析

1.3.1 钩子函数结构体

钩子函数封装在结构体nf_hook_ops里,nf_hook_ops结构体定义如下:

struct nf_hook_ops {
	/* User fills in from here down. */
	nf_hookfn		*hook;        //钩子函数,各模块自定义
	struct net_device	*dev;
	void			*priv;        
	u_int8_t		pf;//Protocol families
	unsigned int		hooknum;       //钩子点  
	/* Hooks are ordered in ascending priority. */
	int			priority;              //钩子函数执行优先级
};

钩子函数nf_hookfn的定义:

typedef unsigned int nf_hookfn(void *priv,struct sk_buff *skb,const struct nf_hook_state *state);

netfilter的钩子函数的存储结构图:
在这里插入图片描述注册钩子函数实际就是把每个nf_hook_ops变量的成员hook和priv赋值给每个nf_hook_entry变量,并且根据priority把nf_hook_entry变量按从小到大的顺序存储在nf_hook_entries的hooks[]数组里。并且为了能按priority存储在数组里需要知道已注册所有钩子函数的priority,因此在hooks[]的末尾按priority存储指向每个nf_hook_ops变量的指针。

1.3.2 钩子函数的注册和注销

注册和注销多个钩子函数:

int nf_register_net_hooks(struct net*net,struct nf_hook_ops *reg,unsigned int n);
void nf_unregister_net_hooks(struct net*net,struct nf_hook_ops *reg,unsigned int n);

注册和注销单个钩子函数:

int nf_register_net_hook(struct net*net,struct nf_hook_ops *reg);
void nf_unregister_net_hook(struct net*net,struct nf_hook_ops *reg);

nf_register_net_hook核心函数分析:

static int __nf_register_net_hook(struct net *net, int pf,const struct nf_hook_ops *reg)
{                                                                                                                                                                       
        struct nf_hook_entries *p, *new_hooks;
        struct nf_hook_entries __rcu **pp;

        if (pf == NFPROTO_NETDEV) {
#ifndef CONFIG_NETFILTER_INGRESS
                if (reg->hooknum == NF_NETDEV_INGRESS)
                        return -EOPNOTSUPP;
#endif
                if (reg->hooknum != NF_NETDEV_INGRESS ||
                    !reg->dev || dev_net(reg->dev) != net)
                        return -EINVAL;
        }
	
    	//根据待注册的钩子函数结构体里的pf和hooknum找出存储同类钩子函数的结构体数组指针
        pp = nf_hook_entry_head(net, pf, reg->hooknum, reg->dev);
        if (!pp)
                return -EINVAL;

        mutex_lock(&nf_hook_mutex);

        p = nf_entry_dereference(*pp);
    //申请新的内存按优先级从小到大的顺序存储原有的结构体数组成员和待注册的新成员
        new_hooks = nf_hook_entries_grow(p, reg);

        if (!IS_ERR(new_hooks))
                rcu_assign_pointer(*pp, new_hooks);

        mutex_unlock(&nf_hook_mutex);
        if (IS_ERR(new_hooks))
                return PTR_ERR(new_hooks);

        hooks_validate(new_hooks);
#ifdef CONFIG_NETFILTER_INGRESS
        if (pf == NFPROTO_NETDEV && reg->hooknum == NF_NETDEV_INGRESS)
                net_inc_ingress_queue();
#endif
#ifdef CONFIG_JUMP_LABEL
        static_key_slow_inc(&nf_hooks_needed[pf][reg->hooknum]);
#endif
        BUG_ON(p == new_hooks);
    //释放原有的钩子函数结构体数组nf_hook_entrie[]的内存,包括存储钩子函数原有的结构体nf_hook_ops地址的内存
        nf_hook_entries_free(p);
        return 0;                                                                                                                                                       
}

static struct nf_hook_entries *
nf_hook_entries_grow(const struct nf_hook_entries *old,                                                                                                                 
                     const struct nf_hook_ops *reg)
{
        unsigned int i, alloc_entries, nhooks, old_entries;
        struct nf_hook_ops **orig_ops = NULL;
        struct nf_hook_ops **new_ops;
        struct nf_hook_entries *new;
        bool inserted = false;

        alloc_entries = 1;
        old_entries = old ? old->num_hook_entries : 0;

        if (old) {
            	//获取已注册的所有钩子函数原有的结构体nf_hook_ops地址列表
            	//位于钩子函数结构体数组nf_hook_entrie[]的后面
                orig_ops = nf_hook_entries_get_hook_ops(old);

                for (i = 0; i < old_entries; i++) {
                        if (orig_ops[i] != &dummy_ops)
                                alloc_entries++;
                }
        }

        if (alloc_entries > MAX_HOOK_COUNT)
                return ERR_PTR(-E2BIG);

        new = allocate_hook_entries_size(alloc_entries);
        if (!new)
                return ERR_PTR(-ENOMEM);

    	//new_ops用来存储所有已注册以及待注册的钩子函数原有的结构体nf_hook_ops地址
        new_ops = nf_hook_entries_get_hook_ops(new);

        i = 0;
        nhooks = 0;
        while (i < old_entries) {
                if (orig_ops[i] == &dummy_ops) {
                        ++i;
                        continue;
                }
				//优先级比新成员小的原有成员赋值到新申请的内存
                if (inserted || reg->priority > orig_ops[i]->priority) {
                        new_ops[nhooks] = (void *)orig_ops[i];
                        new->hooks[nhooks] = old->hooks[i];
                        i++;
                } else {
                        new_ops[nhooks] = (void *)reg;
                        new->hooks[nhooks].hook = reg->hook;
                        new->hooks[nhooks].priv = reg->priv;
                        inserted = true;
                }
                nhooks++;
        }
		//如果新成员优先级最大或者是第一个钩子函数
        if (!inserted) {
                new_ops[nhooks] = (void *)reg;
                new->hooks[nhooks].hook = reg->hook;
                new->hooks[nhooks].priv = reg->priv;
        }

        return new;
}

1.3.3 钩子函数的执行

netfilter在各个钩子点执行NF_HOOK函数,一旦报文经过这些钩子点,触发注册在这些钩子点的钩子函数的执行

NF_HOOK_COND同NF_HOOK,区别在于其有条件执行。

NF_HOOK的调用关系图:

在这里插入图片描述

NF_HOOK的核心函数nf_hook_show分析:

/* Returns 1 if okfn() needs to be executed by the caller,
 * -EPERM for NF_DROP, 0 otherwise.  Caller must hold rcu_read_lock. */
int nf_hook_slow(struct sk_buff *skb, struct nf_hook_state *state,                                                                                                      
                 const struct nf_hook_entries *e, unsigned int s)
{
        unsigned int verdict;
        int ret;
		//从优先级为s的钩子函数开始执行
        for (; s < e->num_hook_entries; s++) {
                verdict = nf_hook_entry_hookfn(&e->hooks[s], skb, state);
                switch (verdict & NF_VERDICT_MASK) {
                case NF_ACCEPT://继续执行下一个钩子函数
                        break;
                case NF_DROP://报文释放,其后的钩子函数不再执行
                        kfree_skb(skb);
                        ret = NF_DROP_GETERR(verdict);
                        if (ret == 0)
                                ret = -EPERM;
                        return ret;
                case NF_QUEUE://把报文入队,交给用户程序
                        ret = nf_queue(skb, state, e, s, verdict);
                        if (ret == 1)
                                continue;
                        return ret;
                default://NF_STOLEN、NF_REPEAT、NF_STOP等其他返回值状态,其后的钩子函数都不再执行,NF_STOP在4.4返回1
                        /* Implicit handling for NF_STOLEN, as well as any other
                         * non conventional verdicts.
                         */
                        return 0;
                }
        }

        return 1;                                                                                                                                                       
}
  • 0
    点赞
  • 18
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值