您的位置:首页 > 运维架构 > Linux

【Linux】Netfilter的使用和实现

2017-08-01 20:20 423 查看
概述
实现
使用


原文地址: http://blog.csdn.net/wh8_2011/article/details/49272911

概述

 Netfilter为多种网络协议(IPv4、IPv6、ARP等)各提供了一套钩子函数。

在IPv4中定义了5个钩子函数,这些钩子函数在数据包流经协议栈的5个关键点被调用。

这就像有5个钓鱼台,在每个钓鱼台放了一个鱼钩(钩子函数),把经过的数据包钓上来,

然后根据自定义的规则,来决定数据包的命运:

可以原封不动的放回IPv4协议,继续向上层递交;可以进行修改,再放回IPv4协议;也可以直接丢弃。

Netfilter主要采用连接跟踪(Connection Tracking)、包过滤(Packet Filtering)、地址转换(NAT)、包处理

(Packet Mangling)四种技术。

 

(1) IP层的5个钓鱼台



 

[java] view
plaincopy





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  

};  



支持的协议类型:

[java] view
plaincopy





enum {  

    NFPROTO_UNSPEC = 0,  

    NFPROTO_IPV4 = 2,  

    NFPROTO_ARP = 3,  

    NFPROTO_BRIDGE = 7,  

    NFPROTO_IPV6 = 10,  

    NFPROTO_DECNET = 12,  

    NFPROTO_NUMPROTO,  

};  



 

(2) 钩子函数

[java] view
plaincopy





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 *));  

  

/* 处理函数返回值 */  

#define NF_DROP 0 /* drop the packet, don't continue traversal */  

#define NF_ACCEPT 1 /* continue traversal as normal */  

#define NF_STOLEN 2 /* I've taken over the packet, don't continue traversal */  

#define NF_QUEUE 3 /* queue the packet (usually for userspace handling) */  

#define NF_REPEAT 4 /* call this hook again */  

#define NF_STOP 5  

#define NF_MAX_VERDICT NF_STOP  



 

(3) Netfilter实体

在使用Netfilter时,需要定义一个nf_hook_ops实例。

[java] view
plaincopy





struct nf_hook_ops {  

    struct list_head list;  

    /* User fills in from here down. */  

    nf_hookfn *hook; /* 要注册的钩子函数 */  

    struct module *owner;  

    u_int8_t pf; /* 协议类型 */  

    unsigned int hooknum; /* 哪个钓鱼台 */  

    /* Hooks are ordered in asending priority. */  

    int priority; /* 数值越小,优先级越高 */  

};  

typedef __u8 u_int8_t;  



 

(4) 注册与注销

[java] view
plaincopy





/* Functions to register/unregister hook points. */  

int nf_register_hook(struct nf_hook_ops *reg);  

void nf_unregister_hook(struct nf_hook_ops *reg);  



 

实现

 

Netfilter定义了一个全局链表:

[java] view
plaincopy





struct list_head nf_hooks[NFPROTO_NUMPROTO][NF_MAX_HOOKS];  

EXPORT_SYMBOL(nf_hooks);  

static DEFINE_MUTEX(nf_hook_mutex);  



 

(1) 注册函数

注册函数会把nf_hook_ops放入nf_hooks相应的位置中。

[java] view
plaincopy





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); /* 把netfilter实例添加到队列中 */  

    mutex_unlock(&nf_hook_mutex);  

    return 0;  

}  



 

(2) 注销函数

[java] view
plaincopy





void nf_unregister_hook(struct nf_hook_ops *reg)  

{  

    mutex_lock(&nf_hook_mutex);  

    list_del_rcu(®->list); /* 把netfilter实例从队列中删除 */  

    mutex_unlock(&nf_hook_mutex);  

    synchronize_net();  

}  



 

(3) 内核接口

内核的Netfilter钩子函数调用:

NF_HOOK

    |--> NF_HOOK_THRESH

               |--> nf_hook_thresh

                         |--> nf_hook_slow

                                   |--> nf_iterate

[java] view
plaincopy





static inline int NF_HOOK(uint8_t pf, unsigned int hook, struct sk_buff *skb,  

    struct net_device *in, struct net_device *out, int (*okfn)(struct sk_buff *))  

{  

    /* INT_MIN表示要调用钓鱼台的所有钩子函数 */  

    return NF_HOOK_THRESH(pf, hook, skb, in, out, okfn, INT_MIN);   

}  

  

static inline int NF_HOOK_THRESH(uint8_t pf, unsigned int hook, struct sk_buff *skb,  

    struct net_device *in, struct net_device *out, int (*okfn)(struct sk_buff *), int thresh)  

{  

    int ret = nf_hook_thresh(pf, hook, skb, in, out, okfn, thresh);  

    if (ret == 1)  

        ret = okfn(skb); /* 如果skb没被处理掉,调用此函数 */  

  

    return ret;  

}  



[java] view
plaincopy





/** 

 * nf_hook_thresh - call a netfilter hook 

 * Returns 1 if the hook has allowed the packet to pass. 

 * The function okfn must be invoked by the caller in this case. 

 * Any other return value indicates the packet has been consumed by the hook. 

 */  

static inline int nf_hook_thresh(u_int8_t pf, unsigned int hook, struct sk_buff *skb,  

    struct net_device *indev, struct net_device *outdev, int (*okfn)(struct sk_buff *), int thresh)  

{  

#ifndef CONFIG_NETFILTER_DEBUG  

    /* 如果协议pf的hook点上没有已注册的nf_hook_ops实例,直接返回1 */  

    if (list_empty(&nf_hooks[pf][hook]))  

        return 1;  

#endif  

  

    return nf_hook_slow(pf, hook, skb, indev, outdev, okfn, thresh);  

}  

   

/* Returns 1 if okfn() needs to be executed by the caller, -EPERM for NF_DROP, 0 otherwise. */  

int nf_hook_slow(u_int8_t 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;  

  

    /* We may already have this, but read-locks nest anyway */  

    rcu_read_lock();  

  

    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;  

    } else if (verdict == NF_DROP) {  

        kfree_skb(skb);  

        ret = -EPERM;  

    } else if ((verdict & NF_VERDICT_MASK) == NF_QUEUE) {  

        if (! nf_queue(skb, elem, ph, hook, indev, outdev, okfn, verdict >> NF_VERDICT_BITS))  

            goto next_hook;  

    }  

  

    rcu_read_unlock();  

  

    return ret;  

}  



[java] view
plaincopy





unsigned int nf_iterate(struct list_head *head, struct sk_buff *skb, unsigned 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;  

  

        /* 优先级>=hook_thresh的都会被执行 */  

        if (hook_thresh > elem_priority)  

            continue;  

  

        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;  

}  



 

使用

 

以下是一个简单的模块,加载到一个HTTP服务器上。

通过在PRE_ROUTING处注册my_hookfn,改变接收数据包的源IP为8.8.8.8(Google DNS server)。

当客户端向服务器发送一个请求时,肯定收不到服务器的响应:)

[java] view
plaincopy





#include <linux/netfilter.h>  

#include <linux/init.h>  

#include <linux/module.h>  

#include <linux/netfilter_ipv4.h>  

#include <linux/ip.h>  

#include <linux/inet.h>  

  

/** 

 * Hook function to be called. 

 * We modify the packet's src IP. 

 */  

unsigned int my_hookfn(unsigned int hooknum,  

    struct sk_buff *skb,  

    const struct net_device *in,  

    const struct net_device *out,  

    int (*okfn)(struct sk_buff *))  

{  

    struct iphdr *iph;  

    iph = ip_hdr(skb);  

  

    /* log the original src IP */  

    printk(KERN_INFO"src IP %pI4\n", &iph->saddr);  

  

    /* modify the packet's src IP */  

    iph->saddr = in_aton("8.8.8.8");  

  

    return NF_ACCEPT;  

}  

  

/* A netfilter instance to use */  

static struct nf_hook_ops nfho = {  

    .hook = my_hookfn,  

    .pf = PF_INET,  

    .hooknum = NF_INET_PRE_ROUTING,  

    .priority = NF_IP_PRI_FIRST,  

    .owner = THIS_MODULE,  

};  

  

static int __init sknf_init(void)  

{  

    if (nf_register_hook(&nfho)) {  

        printk(KERN_ERR"nf_register_hook() failed\n");  

        return -1;  

    }  

    return 0;  

}  

  

static void __exit sknf_exit(void)  

{  

    nf_unregister_hook(&nfho);  

}  

  

module_init(sknf_init);  

module_exit(sknf_exit);  

MODULE_AUTHOR("zhangsk");  

MODULE_LICENSE("GPL");
 





顶 0 踩 0
 
 
上一篇数据包接收系列 — IP协议处理流程(二)
下一篇不错的网络协议栈测试工具 — Packetdrill

  相关文章推荐


Linux Netfilter实现机制和扩展技术

Netfilter框架的设计与实现

Netfilter的使用和实现

netfilter中iptables表的实现

如何使用netfilter/iptables构建防火墙

netfilter:Linux 防火墙在内核中的实现

用Netfilter模块实现基于令牌桶的每IP地址流量控制

Netfilter实现机制分析

netfilter中对多连接协议跟踪和NAT实现

Netfilter 连接跟踪与状态检测的实现
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: