• linux netfilter 五个钩子点


    参考http://www.linuxtcpipstack.com/685.html#NF_INET_PRE_ROUTING

    https://opengers.github.io/openstack/openstack-base-netfilter-framework-overview/

    http://blog.chinaunix.net/uid-26517122-id-4293010.html

    https://netfilter.org/documentation/HOWTO/netfilter-hacking-HOWTO-3.html

    https://en.wikipedia.org/wiki/Netfilter

    在三层IPv4数据包的处理过程中,可能经过Netfilter的五个钩子点,分别为NF_INET_PRE_ROUTING、NF_INET_LOCAL_IN、NF_INET_FORWARD、NF_INET_LOCAL_OUT、NF_INET_POST_ROUTING,在每个点都可以设置一些规则,来对数据包进行匹配检查处理,这些规则的配置、布局和匹配流程。

     

     Netfilter 提供了一个基本的报文拦截框架,即hook机制;

    Netfilter 中定义了一个全局二维数组,来存放注册了的处理函数。

    struct list_head hooks[NFPROTO_NUMPROTO][NF_MAX_HOOKS] __read_mostly;

     在每个关键点上,有很多已经按照优先级预先注册了的回调函数;

    每个钩子函数最后必须向Netfilter框架返回下列几个值其中之一

    #define NF_DROP 0 /* 丢包,不再传输 */
    #define NF_ACCEPT 1 /* 接受数据包,继续正常传输 这个返回值告诉 Netfilter:到目前为止,该数据包还是被接受的并且该数据包应当被递交到网络协议栈的下一个阶段 */
    #define NF_STOLEN 2 /* 数据包已经被接管,回调函数处理该包,NF不再处理,该回调函数将从此开始对数据包的处理,并且Netfilter应当放弃对该数据包做任何的处理。但是,这并不意味着该数据包的资源已经被释放。这个数据包以及它独自的sk_buff数据结构仍然有效,只是回调函数从Netfilter 获取了该数据包的所有权 */
    #define NF_QUEUE 3 /*对该数据报进行排队 ,通常用于将数据包交给用户空间的进程处理 */
    #define NF_REPEAT 4 /* 再次调用该回调函数,应当谨慎使用这个值,以免造成死循环*/
    #define NF_STOP 5    /*结束执行, 数据包通过了挂载点的所有规则。但与NF_ACCEPT不同的一点时,当某条规则的判定结果为NF_STOP,那么可以直接返回结果NF_STOP,无需进行后面的判定了。而NF_ACCEPT需要所以的规则都为ACCEPT,才能返回NF_ACCEPT */
    #define NF_MAX_VERDICT NF_STOP
     

    netfilter 回调函数注册

    nf_hook_ops是注册的钩子函数的核心结构,字段含义如下所示,一般待注册的钩子函数会组成一个nf_hook_ops数组,在注册过程中调用nf_register_net_hooks将所有规则加入到指定的钩子点;

    /*
    list:因为在一个HOOK点有可能注册多个钩子函数,因此这个变量用来将某个HOOK点所注册的所有钩子函数组织
    成一个双向链表;
    hook:该参数是一个指向nf_hookfn类型的函数的指针,由该函数指针所指向的回调函数在该hook被激活时调用【nf_hookfn在后面做解释】;
    owner:表示这个hook是属于哪个模块的
    pf:该hook函数所处理的协议。目前我们主要处理IPv4,所以该参数总是PF_INET;
    hooknum:钩子函数的挂载点,即HOOK点;
    priority:优先级。前面也说过,一个HOOK点可能挂载了多个钩子函数,当Netfilter在这些HOOK点上遍历查找所注册的钩子函数时,这些钩子函数的先后执行顺序便由该参数来制定。
    */
    struct nf_hook_ops {
        struct list_head    list;
     
        /* User fills in from here down. */
        nf_hookfn        *hook; /* 钩子函数 */
        struct net_device    *dev; /* 设备 */
        void            *priv; /* 私有数据 */
        u_int8_t        pf; /* 协议族 */
        unsigned int        hooknum; /* 钩子点 */
        /* Hooks are ordered in ascending priority. */
        int            priority; /* 优先级 */
    };

    钩子函数原型为:

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

    nf_register_net_hook为钩子函数注册的主流程,首先找到钩子点函数的入口,然后根据优先级将当前注册的钩子函数插入到链表中;

    int nf_register_net_hook(struct net *net, const struct nf_hook_ops *reg)
    {
        struct list_head *hook_list;
        struct nf_hook_entry *entry;
        struct nf_hook_ops *elem;
     /* 分配钩子入口结构 */
        entry = kmalloc(sizeof(*entry), GFP_KERNEL);
        if (!entry)
            return -ENOMEM;
    
        entry->orig_ops    = reg;
        entry->ops    = *reg;
    /* 找到钩子点链表头部 */
        hook_list = nf_find_hook_list(net, reg);
        if (!hook_list) {
            kfree(entry);
            return -ENOENT;
        }
    
        mutex_lock(&nf_hook_mutex);
         /* 找到钩子应该插入的位置 */
        list_for_each_entry(elem, hook_list, list) {
            if (reg->priority < elem->priority)
                break;
        }
         /* 插入钩子点 */
        list_add_rcu(&entry->ops.list, elem->list.prev);
        mutex_unlock(&nf_hook_mutex);
    #ifdef CONFIG_NETFILTER_INGRESS
        if (reg->pf == NFPROTO_NETDEV && reg->hooknum == NF_NETDEV_INGRESS)
            net_inc_ingress_queue();
    #endif
    #ifdef HAVE_JUMP_LABEL
        static_key_slow_inc(&nf_hooks_needed[reg->pf][reg->hooknum]);
    #endif
        return 0;
    }
    View Code
    /*
    对于nf
    其钩子函数入口形式为hooks[协议族][钩子点],在二维数组的每个节点都对应着一个钩子函数链表,内部多个nf_hook_entry通过优先级从小到大排列;
    */
    struct netns_nf {
    #if defined CONFIG_PROC_FS
        struct proc_dir_entry *proc_netfilter;
    #endif
        const struct nf_queue_handler __rcu *queue_handler;
        const struct nf_logger __rcu *nf_loggers[NFPROTO_NUMPROTO];
    #ifdef CONFIG_SYSCTL
        struct ctl_table_header *nf_log_dir_header;
    #endif
        struct list_head hooks[NFPROTO_NUMPROTO][NF_MAX_HOOKS];
    };
    
    enum {
        NFPROTO_UNSPEC =  0,
        NFPROTO_INET   =  1,
        NFPROTO_IPV4   =  2,
        NFPROTO_ARP    =  3,
        NFPROTO_NETDEV =  5,
        NFPROTO_BRIDGE =  7,
        NFPROTO_IPV6   = 10,
        NFPROTO_DECNET = 12,
        NFPROTO_NUMPROTO,
    };
    //IPv4钩子点的定义如下:
    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
    };
    http代理服务器(3-4-7层代理)-网络事件库公共组件、内核kernel驱动 摄像头驱动 tcpip网络协议栈、netfilter、bridge 好像看过!!!! 但行好事 莫问前程 --身高体重180的胖子
  • 相关阅读:
    在Linux上使用C语言编程获取IPv4地址及子网掩码
    使用gdb进行写操作
    [中英对照]The Art Of Reporting Bugs | 报bug的艺术
    [中英对照]Introduction to Remote Direct Memory Access (RDMA) | RDMA概述
    Intel万兆网卡背靠背连接ping不通那点事儿
    [中英对照]The sysfs Filesystem | sysfs文件系统
    图说单播,组播,广播,选播和地域播
    Ubuntu双网卡不双待攻略
    反汇编容易反编译难
    PHP之路——微信公众号授权获取用户信息
  • 原文地址:https://www.cnblogs.com/codestack/p/10850642.html
Copyright © 2020-2023  润新知