连接跟踪子系统之helper
在連接跟蹤子系統之AF_INET協議族鉤子函數
中有看到,在連接跟蹤子系統的出口,第一個鉤子是ipv4_conntrack_help()(以AF_INET協議族為例),之后才是對新連接的確認鉤子。并且help鉤子的邏輯就是調用連接跟蹤信息塊中的help()回調函數。這篇筆記就來看看連接跟蹤子系統對helper的管理,相關代碼文件為:
| net/netfilter/nf_conntrack_helper.c | helper框架管理實現文件 |
| include/net/netfilter/nf_conntrack_helper.h | helper頭文件 |
1. helper定義
協議是否需要實現helper是可選的,如果需要實現,那么協議需要首先實例化struct nf_conntrack_helper對象,然后將其注冊到系統中才行。helper定義如下:
struct nf_conntrack_helper {struct hlist_node hnode; /* Internal use. *///每個helper模塊可以有一個名字const char *name; /* name of the module */struct module *me; /* pointer to self *///一個連接允許同時存在的最大期望連接數unsigned int max_expected;//屬于該helper的期望連接的保活定時器超時時間unsigned int timeout;/* Tuple of things we will help (compared against server response) *///指明helper模塊關心什么樣的數據包。該字段很關鍵,它決定了新連接建立時//能否匹配到該helper對象struct nf_conntrack_tuple tuple;/* Function to call when data passes; return verdict, or -1 to invalidate. *///help()回調int (*help)(struct sk_buff *skb, unsigned int protoff,struct nf_conn *ct, enum ip_conntrack_info conntrackinfo);void (*destroy)(struct nf_conn *ct);int (*to_nlattr)(struct sk_buff *skb, const struct nf_conn *ct); };從helper對象的定義來看,helper和期望連接是緊密相關的。
此外,helper會使用連接跟蹤的擴展部分,擴展部分保存的實際上是sturct nf_conn_help,該結構定義如下:
//當前內核版本有如下4個用戶使用helper union nf_conntrack_help {/* insert conntrack helper private data (master) here */struct nf_ct_ftp_master ct_ftp_info;struct nf_ct_pptp_master ct_pptp_info;struct nf_ct_h323_master ct_h323_info;struct nf_ct_sane_master ct_sane_info; }; /* nf_conn feature for connections that have a helper */ struct nf_conn_help {//指向創建該擴展的helperstruct nf_conntrack_helper *helper;//help私有信息union nf_conntrack_help help;//一個連接可以用多個期望連接,這些同屬一個連接的期望連接被組織成一個鏈表struct hlist_head expectations;//expectations鏈表的長度unsigned int expecting; };2. helper子模塊初始化
helper是連接跟蹤子系統必備的一個子模塊,它在連接跟蹤子系統的初始化過程中被初始化。
static DEFINE_MUTEX(nf_ct_helper_mutex); static struct hlist_head *nf_ct_helper_hash __read_mostly; //哈希桶大小 static unsigned int nf_ct_helper_hsize __read_mostly; //哈希表中已保存helper對象的個數 static unsigned int nf_ct_helper_count __read_mostly; static int nf_ct_helper_vmalloc; //對于help類型擴展,連接跟蹤信息塊的擴展字段中真正保存的的struct nf_conn_help static struct nf_ct_ext_type helper_extend __read_mostly = {.len = sizeof(struct nf_conn_help),.align = __alignof__(struct nf_conn_help),.id = NF_CT_EXT_HELPER, };int nf_conntrack_helper_init(void) {int err;//分配一個哈希表用于保存注冊的helper模塊nf_ct_helper_hsize = 1; /* gets rounded up to use one page */nf_ct_helper_hash = nf_ct_alloc_hashtable(&nf_ct_helper_hsize, &nf_ct_helper_vmalloc);if (!nf_ct_helper_hash)return -ENOMEM;//helper需要使用連接跟蹤信息塊的擴展字段,該擴展字段的實際內容由各個使用擴展的模塊決定,//所以這里需要先注冊一個擴展err = nf_ct_extend_register(&helper_extend);if (err < 0)goto err1;return 0; ... }對于擴展,見筆記連接跟蹤子系統之extend,其中有以helper為例介紹的擴展的注冊。
注意:helper子模塊負責的是管理系統中所有已經注冊的helper,為ftp這類用戶使用helper提供便利,它本身并不注冊任何的helper對象。
3. helper的注冊
連接跟蹤子系統對于helper提供的是一種框架上的支持,它僅僅是管理系統中已注冊的helper對象,并且為helper在Netfilter中發揮作用提供一種執行機制,具體每個helper模塊干些什么,框架并不操心。
所以,如果有協議要使用helper,必須先向連接跟蹤子系統注冊一個struct nf_conntrack_helper對象,通過該對象,說明自己想要處理的數據包(tuple成員)類型、以及匹配到該數據包后需要執行的help()回調。
int nf_conntrack_helper_register(struct nf_conntrack_helper *me) {//根據tuple計算哈希值unsigned int h = helper_hash(&me->tuple);BUG_ON(me->timeout == 0);//將helper模塊添加到全局的哈希表中并遞增已注冊helper模塊的個數mutex_lock(&nf_ct_helper_mutex);hlist_add_head_rcu(&me->hnode, &nf_ct_helper_hash[h]);nf_ct_helper_count++;mutex_unlock(&nf_ct_helper_mutex);return 0; } EXPORT_SYMBOL_GPL(nf_conntrack_helper_register);去注冊是相反的操作,只不過它需要做更多的同步與檢查工作,因為去注冊時,當前可能有連接正在使用該helper模塊,所以需要同步這些連接的狀態。
4. helper的查找
在連接跟蹤子系統之核心實現中有看到,連接跟蹤子系統在檢測到一個新的連接時,會通過調用init_conntrack()為其創建連接跟蹤信息塊。之前的博客并沒有仔細分析其中關于期望連接和helper相關的處理邏輯,這里我們重點來看helper的處理,期望連接的處理見連接跟蹤子系統之期望連接。
4.1 init_conntrack()
/* Allocate a new conntrack: we return -ENOMEM if classificationfailed due to stress. Otherwise it really is unclassifiable. */ static struct nf_conntrack_tuple_hash * init_conntrack(const struct nf_conntrack_tuple *tuple,struct nf_conntrack_l3proto *l3proto, struct nf_conntrack_l4proto *l4proto,struct sk_buff *skb, unsigned int dataoff) {struct nf_conn *ct;struct nf_conn_help *help;struct nf_conntrack_tuple repl_tuple;struct nf_conntrack_expect *exp; ...spin_lock_bh(&nf_conntrack_lock);//根據skb的tuple搜索期望連接鏈表,檢查該新的連接是否是某個已有連接的期望連接exp = nf_ct_find_expectation(tuple);if (exp) {//新連接時某個連接的期望連接,見筆記"連接跟蹤子系統之期望連接"...} else {//沒有找到期望連接,檢查系統中是否有helper模塊想處理這種連接,//為什么這里要用Reply方向的tuple查找helper呢?struct nf_conntrack_helper *helper;//這里為什么是使用repl_tuple來查找helper,不理解!!!helper = __nf_ct_helper_find(&repl_tuple);if (helper) {//找到了處理該連接的helper對象,那么在連接信息塊的擴展字段中添加一個help類型的擴展,//help類型的擴展的實際類型是struct nf_conn_help,這里返回分配的help對象,然后建//立help和helper之間的關系,因為通過連接跟蹤信息塊能夠拿到的實際上是help對象help = nf_ct_helper_ext_add(ct, GFP_ATOMIC);if (help)rcu_assign_pointer(help->helper, helper);}NF_CT_STAT_INC(new);} ... }4.2 查找helper:__nf_ct_helper_find()
根據參數指定的tuple,查找全局的helper哈希表nf_ct_helper_hash,返回對應的helper對象。
struct nf_conntrack_helper * __nf_ct_helper_find(const struct nf_conntrack_tuple *tuple) {struct nf_conntrack_helper *helper;struct nf_conntrack_tuple_mask mask = { .src.u.all = htons(0xFFFF) };struct hlist_node *n;unsigned int h;//如果當前系統中根本就沒有注冊的helper,當然也就沒有helper對該連接感興趣了if (!nf_ct_helper_count)return NULL;//根據tuple計算hash值h = helper_hash(tuple);//遍歷哈希表中h對應的沖突鏈,尋找helper->tuple與入參tuple一致的helperhlist_for_each_entry_rcu(helper, n, &nf_ct_helper_hash[h], hnode) {//匹配原則就是檢查L3協議地址、L3協議號、L4協議地址是否相等if (nf_ct_tuple_src_mask_cmp(tuple, &helper->tuple, &mask))return helper;}return NULL; } EXPORT_SYMBOL_GPL(__nf_ct_helper_find);4.3 添加help擴展: nf_ct_helper_ext_add()
struct nf_conn_help *nf_ct_helper_ext_add(struct nf_conn *ct, gfp_t gfp) {struct nf_conn_help *help;//調用extend子模塊的接口添加一個help類型的擴展,見"連接跟蹤子系統之extend"help = nf_ct_ext_add(ct, NF_CT_EXT_HELPER, gfp);if (help)INIT_HLIST_HEAD(&help->expectations);elsepr_debug("failed to add helper extension area");return help; } EXPORT_SYMBOL_GPL(nf_ct_helper_ext_add);5. help()回調的調用
以AF_INET協議族的help鉤子函數為例,來看看help()回調函數是如何被執行的。help鉤子以較低優先級工作在連接跟蹤子系統的出口處。
static unsigned int ipv4_conntrack_help(unsigned int hooknum, struct sk_buff *skb,const struct net_device *in, const struct net_device *out, int (*okfn)(struct sk_buff *)) {struct nf_conn *ct;enum ip_conntrack_info ctinfo;const struct nf_conn_help *help;const struct nf_conntrack_helper *helper;//在入口處,skb應該已經找到了它的連接跟蹤信息塊ct = nf_ct_get(skb, &ctinfo);//沒有ct,說明該skb沒有被跟蹤。或者skb屬于一個特殊狀態(不理解何時會是這個狀態)。//這兩種情況,不會對該skb執行help()回調if (!ct || ctinfo == IP_CT_RELATED + IP_CT_IS_REPLY)return NF_ACCEPT;//獲取skb的help信息,該信息在連接跟蹤的入口處就已經指定了(如果有的話)help = nfct_help(ct);if (!help)return NF_ACCEPT;/* rcu_read_lock()ed by nf_hook_slow */helper = rcu_dereference(help->helper);if (!helper)return NF_ACCEPT;//執行help()回調,回調函數的返回值將作為給Netfilter框架的返回值return helper->help(skb, skb_network_offset(skb) + ip_hdrlen(skb), ct, ctinfo); }總結
以上是生活随笔為你收集整理的连接跟踪子系统之helper的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 如何从被领导到领导别人
- 下一篇: java信息管理系统总结_java实现科