Chinaunix首页 | 论坛 | 博客
  • 博客访问: 162601
  • 博文数量: 115
  • 博客积分: 0
  • 博客等级: 民兵
  • 技术积分: 10
  • 用 户 组: 普通用户
  • 注册时间: 2016-11-28 14:16
文章分类

全部博文(115)

文章存档

2017年(36)

2016年(79)

我的朋友

分类: LINUX

2017-03-12 17:08:06

原文地址:netfilter 源码分析 作者:zghover

                   netfilter 源码分析

1, 定义

enum nf_inet_hooks {
    NF_INET_PRE_ROUTING,
    NF_INET_LOCAL_IN,
    NF_INET_FORWARD,
    NF_INET_LOCAL_OUT,
    NF_INET_POST_ROUTING,
    NF_INET_NUMHOOKS
};


// NF_HOOK()函数宏定义
#define NF_HOOK(pf, hook, skb, indev, outdev, okfn) \
    NF_HOOK_THRESH(pf, hook, skb, indev, outdev, okfn, INT_MIN)

// NF_HOOK_THRESH()函数宏定义
#define NF_HOOK_THRESH(pf, hook, skb, indev, outdev, okfn, thresh)         \
({int __ret;                                       \
if ((__ret=nf_hook_thresh(pf, hook, (skb), indev, outdev, okfn, thresh, 1)) == 1)\
    __ret = (okfn)(skb);                               \
__ret;})



2, 调用关系

2.1 函数调用

//netfilter共定义了5个挂载点,每个挂载点用在数据包接收的不同的阶段,这5个挂载点的调用关系如下:   

NF_IP_PRE_ROUTING(ip_rcv)-------->NF_IP_FORWARD(ip_forward)--------->NF_IP_POST_ROUTING(ip_output)
                     |                    ^
                     |                    |
                     V                    |
                 NF_IP_LOCAL_IN       NF_IP_LOCAL_OUT
        (ip_local_deliver)    (ip_local_out)


NF_INET_PRE_ROUTING : 用于在sk_buff成功接收,并对sk的内容进行成功检查后调用:
//ip_rcv()函数中调用:
int ip_rcv(struct sk_buff *skb, struct net_device *dev, struct packet_type *pt, struct net_device *orig_dev)
    return NF_HOOK(PF_INET, NF_INET_PRE_ROUTING, skb, dev, NULL,
        ip_rcv_finish);

NF_INET_LOCAL_OUT : 在ip包接收到本地后调用:
//ip_local_deliver()函数中的调用:
int ip_local_deliver(struct sk_buff *skb)
    return NF_HOOK(PF_INET, NF_INET_LOCAL_IN, skb, skb->dev, NULL,
        ip_local_deliver_finish);

//NF_INET_FORWARD: 在对接收的sk_buff包完成路由分类判断是需要进行转发的包进行处理后调用。
//在ip_forward()函数中的调用:
int ip_forward(struct sk_buff *skb)
    return NF_HOOK(PF_INET, NF_INET_FORWARD, skb, skb->dev, rt->u.dst.dev,
               ip_forward_finish);
       
//在对自身发出的包进行处理调用。
// _ip_local_out()函数中的调用:
调用关系:ip_build_and_send_pkt()->ip_local_out()->_ip_local_out()
//int __ip_local_out(struct sk_buff *skb)
    return nf_hook(PF_INET, NF_INET_LOCAL_OUT, skb, NULL, skb->dst->dev,
               dst_output);
   
//在IP栈成功接收sk_buff包后处理调用。
// ip_output()函数中的调用:
int ip_output(struct sk_buff *skb)
    return NF_HOOK_COND(PF_INET, NF_INET_POST_ROUTING, skb, NULL, dev,
                ip_finish_output,
                !(IPCB(skb)->flags & IPSKB_REROUTED));

3, netfilter 数据结构

3.1 nf_hooks链表
netfilter定义了一个二维的链表头数组struct list_head nf_hooks[NPROTO][NF_MAX_HOOKS]来表示所有协议族的各个挂接点,NPROTO值为32,可表示linux所支持所有32个协议族(include/linux/socket.h文件中定义),也就是使用socket(2)函数的第一个参数的值,如互联网的TCP/IP协议族PF_INET(2)。每个协议族有NF_MAX_HOOKS(8)个挂接点,但实际只用了如上所述的5个,数组中每个元素表示一个协议族在一个挂接点的处理链表头,其定义如下:

struct list_head nf_hooks[NPROTO][NF_MAX_HOOKS] __read_mostly;

3.2 netfilter 挂载函数注册结构

//netfilter 操作结构
struct nf_hook_ops
{
    struct list_head list;

    /* User fills in from here down. */
    nf_hookfn *hook;        //挂载函数
    struct module *owner;    //模块
    int pf;             //pf与hooknum一起索引到特定协议特定编号的挂载函数队列,用于索引nf_hooks
    int hooknum;
    /* Hooks are ordered in ascending priority. */
    int priority;         //priority决定在同一队列(pf与hooknum相同)的顺序,priority越小则排列越靠前。
};

//挂载函数的原型
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 *));


4, netfilter的注册

4.1 注册和注销
函数调用关系:
iptable_filter_init()->nf_register_hooks()->nf_register_hook()

//对挂载函数的注册是在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;

    //从指定pf和hooknum的nf_hooks队列遍历,按priority从小到大顺序,将reg插入相应位置。
    list_for_each_entry(elem, &nf_hooks[reg->pf][reg->hooknum], list) {
        if (reg->priority < elem->priority)    //要注册的reg要比存在的优先级数字小,放到前面
            break;
    }  
    list_add_rcu(®->list, elem->list.prev);
    mutex_unlock(&nf_hook_mutex);
    return 0;
}

//注销挂载函数
void nf_unregister_hook(struct nf_hook_ops *reg)
{
    mutex_lock(&nf_hook_mutex);
    list_del_rcu(®->list);        //把reg从对应的链表中删除
    mutex_unlock(&nf_hook_mutex);

    synchronize_net();
}

//向链表中注册n次
int nf_register_hooks(struct nf_hook_ops *reg, unsigned int n)

//注销n次
void nf_unregister_hooks(struct nf_hook_ops *reg, unsigned int n)


4.2 调用netfilter函数

4.2.1 函数调用关系

ip_rcv        |        NF_HOOK()->NF_HOOK_THRESH()
ip_forward  | --->            ->nf_hook_thresh()
...        |                    ->nf_hook_slow()
                            ->nf_iterate()


4.2.2 数据结构

// 函数nf_iterate()函数返回值
/* Responses from hook functions. */
#define NF_DROP 0
#define NF_ACCEPT 1
#define NF_STOLEN 2
#define NF_QUEUE 3
#define NF_REPEAT 4
#define NF_STOP 5
#define NF_MAX_VERDICT NF_STOP

NF_ACCEPT: 表示勾子函数允许报文继续向下处理,此时应该继续执行队列上的下一个勾子函数,因为这些勾子函数都是对同一类报文在相同位置的过滤,前一个通后,并不能返回,而要所有函数都执行完,结果仍为NF_ACCEPT时,则可返回它;
NF_REPEAT: 表示要重复执行勾子函数一次;所以勾子函数要编写得当,否则报文会一直执行一个返回NF_REPEAET的勾子函数,当返回值为NF_REPEAT时,不会返回;
NF_STOP : 表示停止执行队列上的勾子函数,直接返回;
NF_DROP: 表示丢弃掉报文;
NF_STOLEN: 表示报文不再往上传递,与NF_DROP不同的是,它没有调用kfree_skb()释放掉skb;
NF_QUEUE: 检查给定协议(pf)是否有队列处理函数,有则进行处理,否则丢掉。



4.2.3 实现
/* Returns 1 if okfn() needs to be executed by the caller,
 * -EPERM for NF_DROP, 0 otherwise. */
int nf_hook_slow(int pf, unsigned int hook, struct sk_buff *skb,
         struct net_device *indev,
         struct net_device *outdev,
         int (*okfn)(struct sk_buff *),
         int hook_thresh)
{
    struct list_head *elem;
    unsigned int verdict;
    int ret = 0;

#ifdef CONFIG_NET_NS
    struct net *net;

    net = indev == NULL ? dev_net(outdev) : dev_net(indev);
    if (net != &init_net)
        return 1;
#endif            

    /* We may already have this, but read-locks nest anyway */
    rcu_read_lock();

    //通过pf协议类型,和hook类型,找到挂载函数结构的链表头
    elem = &nf_hooks[pf][hook];   

next_hook:
    verdict = nf_iterate(&nf_hooks[pf][hook], skb, hook, indev,
                 outdev, &elem, okfn, hook_thresh);

    if (verdict == NF_ACCEPT || verdict == NF_STOP) { //若接收规则,或规则停止可以继续处理数据包
        ret = 1;
        goto unlock;
    } else if (verdict == NF_DROP) {  //若丢弃包,则直接丢弃
        kfree_skb(skb);
        ret = -EPERM;
    } else if ((verdict & NF_VERDICT_MASK) == NF_QUEUE) {    //检查是否有队列处理函数,若有调用之
        if (!nf_queue(skb, elem, pf, hook, indev, outdev, okfn,
                  verdict >> NF_VERDICT_BITS))
            goto next_hook;
    }
unlock:
    rcu_read_unlock();
    return ret;
}




unsigned int nf_iterate(struct list_head *head,
            struct sk_buff *skb,
            int hook,
            const struct net_device *indev,
            const struct net_device *outdev,
            struct list_head **i,
            int (*okfn)(struct sk_buff *),
            int hook_thresh)
{
    unsigned int verdict;

    /*
     * The caller must not block between calls to this
     * function because of risk of continuing from deleted element.
     */
    list_for_each_continue_rcu(*i, head) {
        struct nf_hook_ops *elem = (struct nf_hook_ops *)*i;

        if (hook_thresh > elem->priority)
            continue;

        /* Optimization: we don't need to hold module
           reference here, since function can't sleep. --RR */
        verdict = elem->hook(hook, skb, indev, outdev, okfn);
        if (verdict != NF_ACCEPT) {
#ifdef CONFIG_NETFILTER_DEBUG
            if (unlikely((verdict & NF_VERDICT_MASK)
                            > NF_MAX_VERDICT)) {
                NFDEBUG("Evil return from %p(%u).\n",
                    elem->hook, hook);
                continue;
            }
#endif

            if (verdict != NF_REPEAT)
                return verdict;
            *i = (*i)->prev;
        }
    }
    return NF_ACCEPT;
}





#说明
kernel版本2.6.27

阅读(1105) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~