Netfilter 之 鉤子函數註冊

經過註冊流程代碼的分析,可以明確鉤子函數的註冊流程,理解存儲鉤子函數的數據結構,以下圖(點擊圖片可查看原圖);linux

 

廢話很少說,開始分析;數組

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的原型爲:tcp

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實例的鏈表的形式存在的,其成員以下;spa

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爲鉤子函數註冊的主流程,首先找到鉤子點函數的入口,而後根據優先級將當前註冊的鉤子函數插入到鏈表中;nuxt

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

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

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

 

註冊流程到此爲止;

相關文章
相關標籤/搜索