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函数完成扩展功能;

猜你喜欢

转载自www.cnblogs.com/wanpengcoder/p/11755777.html