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

 

注冊流程到此為止;

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM