• Netfilter 之 钩子函数注册


    通过注册流程代码的分析,能够明确钩子函数的注册流程,理解存储钩子函数的数据结构,如下图(点击图片可查看原图);

    废话不多说,开始分析;

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

     1 struct nf_hook_ops {
     2     struct list_head    list;
     3 
     4     /* User fills in from here down. */
     5     nf_hookfn        *hook; /* 钩子函数 */
     6     struct net_device    *dev; /* 设备 */
     7     void            *priv; /* 私有数据 */
     8     u_int8_t        pf; /* 协议族 */
     9     unsigned int        hooknum; /* 钩子点 */
    10     /* Hooks are ordered in ascending priority. */
    11     int            priority; /* 优先级 */
    12 };

    钩子函数nf_hookfn的原型为:

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

    nf_register_net_hooks在注册多个钩子函数时使用,它对多个函数顺序调用nf_register_net_hook进行注册,并且在注册失败时进行回滚;

     1 int nf_register_net_hooks(struct net *net, const struct nf_hook_ops *reg,
     2               unsigned int n)
     3 {
     4     unsigned int i;
     5     int err = 0;
     6 
     7     /* 循环注册钩子函数 */
     8     for (i = 0; i < n; i++) {
     9         err = nf_register_net_hook(net, &reg[i]);
    10         /* 失败 */
    11         if (err)
    12             goto err;
    13     }
    14     return err;
    15 
    16 err:
    17     /* 注销本次已注册的钩子函数 */
    18     if (i > 0)
    19         nf_unregister_net_hooks(net, reg, i);
    20     return err;
    21 }

    多个钩子函数在注册之后,是以多个nf_hook_entry实例的链表的形式存在的,其成员如下;

    1 struct nf_hook_entry {
    2     struct nf_hook_entry __rcu    *next; /* 下一节点 */
    3     nf_hookfn            *hook; /* 钩子函数 */
    4     void                *priv; /* 私有数据 */
    5     const struct nf_hook_ops    *orig_ops; /* 钩子操作 */
    6 };

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

     1 int nf_register_net_hook(struct net *net, const struct nf_hook_ops *reg)
     2 {
     3     struct nf_hook_entry __rcu **pp;
     4     struct nf_hook_entry *entry, *p;
     5 
     6     if (reg->pf == NFPROTO_NETDEV) {
     7 #ifndef CONFIG_NETFILTER_INGRESS
     8         if (reg->hooknum == NF_NETDEV_INGRESS)
     9             return -EOPNOTSUPP;
    10 #endif
    11         if (reg->hooknum != NF_NETDEV_INGRESS ||
    12             !reg->dev || dev_net(reg->dev) != net)
    13             return -EINVAL;
    14     }
    15 
    16     /* 找到钩子点链表头部 */
    17     pp = nf_hook_entry_head(net, reg);
    18     if (!pp)
    19         return -EINVAL;
    20 
    21     /* 分配钩子入口结构 */
    22     entry = kmalloc(sizeof(*entry), GFP_KERNEL);
    23     if (!entry)
    24         return -ENOMEM;
    25 
    26     /* 初始化 */
    27     nf_hook_entry_init(entry, reg);
    28 
    29     mutex_lock(&nf_hook_mutex);
    30 
    31     /* Find the spot in the list */
    32     /* 找到钩子应该插入的位置 */
    33     for (; (p = nf_entry_dereference(*pp)) != NULL; pp = &p->next) {
    34         if (reg->priority < nf_hook_entry_priority(p))
    35             break;
    36     }
    37 
    38     /* 插入钩子点 */
    39     rcu_assign_pointer(entry->next, p);
    40     rcu_assign_pointer(*pp, entry);
    41 
    42     mutex_unlock(&nf_hook_mutex);
    43 #ifdef CONFIG_NETFILTER_INGRESS
    44     if (reg->pf == NFPROTO_NETDEV && reg->hooknum == NF_NETDEV_INGRESS)
    45         net_inc_ingress_queue();
    46 #endif
    47 #ifdef HAVE_JUMP_LABEL
    48     static_key_slow_inc(&nf_hooks_needed[reg->pf][reg->hooknum]);
    49 #endif
    50     return 0;
    51 }

    nf_hook_entry_head的作用为查找钩子点函数入口,从这个函数中,我们可以看到,钩子函数存放位置为net->nf.hooks[pf] + hooknum;

     1 static struct nf_hook_entry __rcu **nf_hook_entry_head(struct net *net, const struct nf_hook_ops *reg)
     2 {
     3     if (reg->pf != NFPROTO_NETDEV)
     4         return net->nf.hooks[reg->pf]+reg->hooknum;
     5 
     6 #ifdef CONFIG_NETFILTER_INGRESS
     7     if (reg->hooknum == NF_NETDEV_INGRESS) {
     8         if (reg->dev && dev_net(reg->dev) == net)
     9             return &reg->dev->nf_hooks_ingress;
    10     }
    11 #endif
    12     return NULL;
    13 }

    进一步查看net结构,其成员为struct netns_nf nf;

     1 struct net {
     2 
     3 #ifdef CONFIG_NETFILTER
     4     struct netns_nf        nf;
     5     struct netns_xt        xt;
     6 #if defined(CONFIG_NF_CONNTRACK) || defined(CONFIG_NF_CONNTRACK_MODULE)
     7     struct netns_ct        ct;
     8 #endif
     9 #if defined(CONFIG_NF_TABLES) || defined(CONFIG_NF_TABLES_MODULE)
    10     struct netns_nftables    nft;
    11 #endif
    12 #if IS_ENABLED(CONFIG_NF_DEFRAG_IPV6)
    13     struct netns_nf_frag    nf_frag;
    14 #endif
    15     struct sock        *nfnl;
    16     struct sock        *nfnl_stash;
    17 #if IS_ENABLED(CONFIG_NETFILTER_NETLINK_ACCT)
    18     struct list_head        nfnl_acct_list;
    19 #endif
    20 #if IS_ENABLED(CONFIG_NF_CT_NETLINK_TIMEOUT)
    21     struct list_head    nfct_timeout_list;
    22 #endif
    23 
    24 };

    进一步查看netns_nf结构,其中有如下成员,struct nf_hook_entry __rcu *hooks[NFPROTO_NUMPROTO][NF_MAX_HOOKS]; 可见,其钩子函数入口形式为hooks[协议族][钩子点],在二维数组的每个节点都对应着一个钩子函数链表,内部多个nf_hook_entry通过优先级从小到大排列;

     1 struct netns_nf {
     2 #if defined CONFIG_PROC_FS
     3     struct proc_dir_entry *proc_netfilter;
     4 #endif
     5     const struct nf_queue_handler __rcu *queue_handler;
     6     const struct nf_logger __rcu *nf_loggers[NFPROTO_NUMPROTO];
     7 #ifdef CONFIG_SYSCTL
     8     struct ctl_table_header *nf_log_dir_header;
     9 #endif
    10     struct nf_hook_entry __rcu *hooks[NFPROTO_NUMPROTO][NF_MAX_HOOKS];
    11 #if IS_ENABLED(CONFIG_NF_DEFRAG_IPV4)
    12     bool            defrag_ipv4;
    13 #endif
    14 #if IS_ENABLED(CONFIG_NF_DEFRAG_IPV6)
    15     bool            defrag_ipv6;
    16 #endif
    17 };

    协议的定义如下:

     1 enum {
     2     NFPROTO_UNSPEC =  0,
     3     NFPROTO_INET   =  1,
     4     NFPROTO_IPV4   =  2,
     5     NFPROTO_ARP    =  3,
     6     NFPROTO_NETDEV =  5,
     7     NFPROTO_BRIDGE =  7,
     8     NFPROTO_IPV6   = 10,
     9     NFPROTO_DECNET = 12,
    10     NFPROTO_NUMPROTO,
    11 };

    IPv4钩子点的定义如下:

    1 enum nf_inet_hooks {
    2     NF_INET_PRE_ROUTING,
    3     NF_INET_LOCAL_IN,
    4     NF_INET_FORWARD,
    5     NF_INET_LOCAL_OUT,
    6     NF_INET_POST_ROUTING,
    7     NF_INET_NUMHOOKS
    8 };

    注册流程到此为止;

  • 相关阅读:
    线程
    unix架构
    Unix命令
    可重入函数reentrant function
    Eclipse 中 program arguments 与 VM arguments 的区别
    Java中Generics的使用
    Java的Reflection机制
    Java按值传递、按引用传递
    Java label
    LeetCode Merge Intervals
  • 原文地址:https://www.cnblogs.com/wanpengcoder/p/11755574.html
Copyright © 2020-2023  润新知