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 };

注册流程到此为止;

原文地址:https://www.cnblogs.com/wanpengcoder/p/11755574.html