Netfilter 之 連接跟蹤的helper


注冊helper

nf_conntrack_ftp_init是連接跟蹤ftp模塊的初始化函數,可以看到其調用了nf_conntrack_helpers_register來注冊helper;

 1 static int __init nf_conntrack_ftp_init(void)
 2 {
 3     int i, ret = 0;
 4 
 5     NF_CT_HELPER_BUILD_BUG_ON(sizeof(struct nf_ct_ftp_master));
 6 
 7     ftp_buffer = kmalloc(65536, GFP_KERNEL);
 8     if (!ftp_buffer)
 9         return -ENOMEM;
10 
11     if (ports_c == 0)
12         ports[ports_c++] = FTP_PORT;
13 
14     /* FIXME should be configurable whether IPv4 and IPv6 FTP connections
15          are tracked or not - YK */
16     /* 初始化helper */
17     for (i = 0; i < ports_c; i++) {
18         nf_ct_helper_init(&ftp[2 * i], AF_INET, IPPROTO_TCP, "ftp",
19                   FTP_PORT, ports[i], ports[i], &ftp_exp_policy,
20                   0, help, nf_ct_ftp_from_nlattr, THIS_MODULE);
21         nf_ct_helper_init(&ftp[2 * i + 1], AF_INET6, IPPROTO_TCP, "ftp",
22                   FTP_PORT, ports[i], ports[i], &ftp_exp_policy,
23                   0, help, nf_ct_ftp_from_nlattr, THIS_MODULE);
24     }
25 
26     /* 注冊helper */
27     ret = nf_conntrack_helpers_register(ftp, ports_c * 2);
28     if (ret < 0) {
29         pr_err("failed to register helpers\n");
30         kfree(ftp_buffer);
31         return ret;
32     }
33 
34     return 0;
35 }

 

模塊通過調用nf_conntrack_helper_register函數將helper添加到對應的hash中;函數首先會對是否有相同匹配的helper進行檢查,不存在才會存放到hash中;

 1 int nf_conntrack_helper_register(struct nf_conntrack_helper *me)
 2 {
 3     struct nf_conntrack_tuple_mask mask = { .src.u.all = htons(0xFFFF) };
 4     unsigned int h = helper_hash(&me->tuple);
 5     struct nf_conntrack_helper *cur;
 6     int ret = 0, i;
 7 
 8     BUG_ON(me->expect_policy == NULL);
 9     BUG_ON(me->expect_class_max >= NF_CT_MAX_EXPECT_CLASSES);
10     BUG_ON(strlen(me->name) > NF_CT_HELPER_NAME_LEN - 1);
11 
12     /* 允許的最大期望連接超額 */
13     if (me->expect_policy->max_expected > NF_CT_EXPECT_MAX_CNT)
14         return -EINVAL;
15 
16     mutex_lock(&nf_ct_helper_mutex);
17     /* 遍歷helper hash,查找是否已存在,條件(名稱相同 &&(三層協議為指定||三層協議相同)&&四層協議相同 */
18     for (i = 0; i < nf_ct_helper_hsize; i++) {
19         hlist_for_each_entry(cur, &nf_ct_helper_hash[i], hnode) {
20             if (!strcmp(cur->name, me->name) && /* 名稱相同 */
21                 (cur->tuple.src.l3num == NFPROTO_UNSPEC || /* 三層協議未指定 */
22                  cur->tuple.src.l3num == me->tuple.src.l3num) && /* 三層協議相同 */
23                 cur->tuple.dst.protonum == me->tuple.dst.protonum) { /* 四層協議相同 */
24                 ret = -EEXIST;
25                 goto out;
26             }
27         }
28     }
29 
30     /* avoid unpredictable behaviour for auto_assign_helper */
31     /* 不是userspace,遍歷hash,進行tuple和掩碼的比較,查看是否是已存在 */
32     if (!(me->flags & NF_CT_HELPER_F_USERSPACE)) {
33         hlist_for_each_entry(cur, &nf_ct_helper_hash[h], hnode) {
34             if (nf_ct_tuple_src_mask_cmp(&cur->tuple, &me->tuple,
35                              &mask)) {
36                 ret = -EEXIST;
37                 goto out;
38             }
39         }
40     }
41 
42     /* 將helper加入到helper hash */
43     refcount_set(&me->refcnt, 1);
44     hlist_add_head_rcu(&me->hnode, &nf_ct_helper_hash[h]);
45     nf_ct_helper_count++;
46 out:
47     mutex_unlock(&nf_ct_helper_mutex);
48     return ret;
49 }

 

關聯helper到conntrack

在有新數據包進入的時候,如果沒有對應連接跟蹤,需要調用init_conntrack新建一個連接跟蹤,其中會設置連接跟蹤的helper;

 1 static int
 2 resolve_normal_ct(struct net *net, struct nf_conn *tmpl,
 3           struct sk_buff *skb,
 4           unsigned int dataoff,
 5           u_int16_t l3num,
 6           u_int8_t protonum,
 7           struct nf_conntrack_l3proto *l3proto,
 8           struct nf_conntrack_l4proto *l4proto)
 9 {
10     const struct nf_conntrack_zone *zone;
11     struct nf_conntrack_tuple tuple;
12     struct nf_conntrack_tuple_hash *h;
13     enum ip_conntrack_info ctinfo;
14     struct nf_conntrack_zone tmp;
15     struct nf_conn *ct;
16     u32 hash;
17 
18     /* 將源目的地址端口協議方向等字段設置到tuple */
19     if (!nf_ct_get_tuple(skb, skb_network_offset(skb),
20                  dataoff, l3num, protonum, net, &tuple, l3proto,
21                  l4proto)) {
22         pr_debug("Can't get tuple\n");
23         return 0;
24     }
25 
26     /* look for tuple match */
27     /* 從hash中查找tuple */
28     zone = nf_ct_zone_tmpl(tmpl, skb, &tmp);
29     hash = hash_conntrack_raw(&tuple, net);
30     h = __nf_conntrack_find_get(net, zone, &tuple, hash);
31 
32     /* 未找到該tuple */
33     if (!h) {
34         /* 創建一個節點 */
35         h = init_conntrack(net, tmpl, &tuple, l3proto, l4proto,
36                    skb, dataoff, hash);
37         if (!h)
38             return 0;
39         if (IS_ERR(h))
40             return PTR_ERR(h);
41     }
42 
43     /* 獲取到nf_conn */
44     ct = nf_ct_tuplehash_to_ctrack(h);
45 
46     /* It exists; we have (non-exclusive) reference. */
47     /* 應答方向,已建立連接應答 */
48     if (NF_CT_DIRECTION(h) == IP_CT_DIR_REPLY) {
49         ctinfo = IP_CT_ESTABLISHED_REPLY;
50     } 
51     /* 原始方向 */
52     else {
53         /* Once we've had two way comms, always ESTABLISHED. */
54         /* 已經見過應答了,那么是已連接狀態 */
55         if (test_bit(IPS_SEEN_REPLY_BIT, &ct->status)) {
56             pr_debug("normal packet for %p\n", ct);
57             ctinfo = IP_CT_ESTABLISHED;
58         } 
59         /* 有期望連接標記,則設置關聯字段 */
60         else if (test_bit(IPS_EXPECTED_BIT, &ct->status)) {
61             pr_debug("related packet for %p\n", ct);
62             ctinfo = IP_CT_RELATED;
63         } 
64         /* 其他情況,新連接 */
65         else {
66             pr_debug("new packet for %p\n", ct);
67             ctinfo = IP_CT_NEW;
68         }
69     }
70 
71     /* skb關聯nf_conn */
72     nf_ct_set(skb, ct, ctinfo);
73     return 0;
74 }

 

init_conntrack函數調用__nf_ct_try_assign_helper來對helper進行賦值;

 1 static noinline struct nf_conntrack_tuple_hash *
 2 init_conntrack(struct net *net, struct nf_conn *tmpl,
 3            const struct nf_conntrack_tuple *tuple,
 4            struct nf_conntrack_l3proto *l3proto,
 5            struct nf_conntrack_l4proto *l4proto,
 6            struct sk_buff *skb,
 7            unsigned int dataoff, u32 hash)
 8 {
 9         /*...*/
10 
11     /* 嘗試設置helper */
12     if (!exp)
13         __nf_ct_try_assign_helper(ct, tmpl, GFP_ATOMIC);
14 
15     /*...*/
16 
17     return &ct->tuplehash[IP_CT_DIR_ORIGINAL];
18 }

 

__nf_ct_try_assign_helper函數完成對helper的設置,其中在helper為空的時候調用nf_ct_lookup_helper查找helper;

 1 int __nf_ct_try_assign_helper(struct nf_conn *ct, struct nf_conn *tmpl,
 2                   gfp_t flags)
 3 {
 4     struct nf_conntrack_helper *helper = NULL;
 5     struct nf_conn_help *help;
 6     struct net *net = nf_ct_net(ct);
 7 
 8     /* We already got a helper explicitly attached. The function
 9      * nf_conntrack_alter_reply - in case NAT is in use - asks for looking
10      * the helper up again. Since now the user is in full control of
11      * making consistent helper configurations, skip this automatic
12      * re-lookup, otherwise we'll lose the helper.
13      */
14     if (test_bit(IPS_HELPER_BIT, &ct->status))
15         return 0;
16 
17     /* 原關聯存在,記錄helper為原關聯的helper */
18     if (tmpl != NULL) {
19         help = nfct_help(tmpl);
20         if (help != NULL) {
21             helper = help->helper;
22             set_bit(IPS_HELPER_BIT, &ct->status);
23         }
24     }
25 
26     /* 新連接跟蹤的help */
27     help = nfct_help(ct);
28 
29     /* helper為空 */
30     if (helper == NULL) {
31         /* 根據tuple和mask查找helper */
32         helper = nf_ct_lookup_helper(ct, net);
33 
34         /* 沒找到,賦值為NULL */
35         if (helper == NULL) {
36             if (help)
37                 RCU_INIT_POINTER(help->helper, NULL);
38             return 0;
39         }
40     }
41 
42     /* help為空 */
43     if (help == NULL) {
44         /* 為連接跟蹤添加help擴展 */
45         help = nf_ct_helper_ext_add(ct, helper, flags);
46         if (help == NULL)
47             return -ENOMEM;
48     } 
49     /* 擴展不為空 */
50     else {
51         /* We only allow helper re-assignment of the same sort since
52          * we cannot reallocate the helper extension area.
53          */
54         struct nf_conntrack_helper *tmp = rcu_dereference(help->helper);
55 
56         /* 已有的help和新的helper所屬的help不是同一個擴展help */
57         if (tmp && tmp->help != helper->help) {
58             RCU_INIT_POINTER(help->helper, NULL);
59             return 0;
60         }
61     }
62 
63     /* 設置helper */
64     rcu_assign_pointer(help->helper, helper);
65 
66     return 0;
67 }

 

 1 static struct nf_conntrack_helper *
 2 nf_ct_lookup_helper(struct nf_conn *ct, struct net *net)
 3 {
 4     if (!net->ct.sysctl_auto_assign_helper) {
 5         if (net->ct.auto_assign_helper_warned)
 6             return NULL;
 7         if (!__nf_ct_helper_find(&ct->tuplehash[IP_CT_DIR_REPLY].tuple))
 8             return NULL;
 9         pr_info("nf_conntrack: default automatic helper assignment "
10             "has been turned off for security reasons and CT-based "
11             " firewall rule not found. Use the iptables CT target "
12             "to attach helpers instead.\n");
13         net->ct.auto_assign_helper_warned = 1;
14         return NULL;
15     }
16 
17     /* 根據tuple查找 */
18     return __nf_ct_helper_find(&ct->tuplehash[IP_CT_DIR_REPLY].tuple);
19 }

 

__nf_ct_helper_find會遍歷第一部分講到的保存已注冊helper的hash表,通過tuple和mask來查找對應helper;

 1 static struct nf_conntrack_helper *
 2 __nf_ct_helper_find(const struct nf_conntrack_tuple *tuple)
 3 {
 4     struct nf_conntrack_helper *helper;
 5     struct nf_conntrack_tuple_mask mask = { .src.u.all = htons(0xFFFF) };
 6     unsigned int h;
 7 
 8     if (!nf_ct_helper_count)
 9         return NULL;
10 
11     h = helper_hash(tuple);
12 
13     /* 遍歷對應hash,根據tuple和mask查找helper */
14     hlist_for_each_entry_rcu(helper, &nf_ct_helper_hash[h], hnode) {
15         if (nf_ct_tuple_src_mask_cmp(tuple, &helper->tuple, &mask))
16             return helper;
17     }
18     return NULL;
19 }

 

對比過程,在三層地址,四層端口,協議均相同的情況,認為找到helper;

 1 static inline bool
 2 nf_ct_tuple_src_mask_cmp(const struct nf_conntrack_tuple *t1,
 3              const struct nf_conntrack_tuple *t2,
 4              const struct nf_conntrack_tuple_mask *mask)
 5 {
 6     int count;
 7 
 8     /* 判斷三層地址是否相同 */
 9     for (count = 0; count < NF_CT_TUPLE_L3SIZE; count++) {
10         if ((t1->src.u3.all[count] ^ t2->src.u3.all[count]) &
11             mask->src.u3.all[count])
12             return false;
13     }
14 
15     /* 判斷四層端口是否相同 */
16     if ((t1->src.u.all ^ t2->src.u.all) & mask->src.u.all)
17         return false;
18 
19     /* 判斷協議是否相同 */
20     if (t1->src.l3num != t2->src.l3num ||
21         t1->dst.protonum != t2->dst.protonum)
22         return false;
23 
24 
25     /* 地址+端口+協議都相同,已存在,返回true */
26     return true;
27 }

 

調用helper

在連接跟蹤的ipv4_helper鈎子函數中,會查找連接跟蹤的對應的helper,並執行helper的help函數完成擴展功能;


免責聲明!

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



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