基础参数初始化

nf_conntrack_init_start函数完成连接跟踪基础参数的初始化,包括了hash,slab,扩展项,GC任务等;

  1 int nf_conntrack_init_start(void)
  2 {
  3     int max_factor = 8;
  4     int ret = -ENOMEM;
  5     int i;
  6 
  7     /* struct nf_ct_ext uses u8 to store offsets/size */
  8     BUILD_BUG_ON(total_extension_size() > 255u);
  9 
 10     seqcount_init(&nf_conntrack_generation);
 11 
 12     for (i = 0; i < CONNTRACK_LOCKS; i++)
 13         spin_lock_init(&nf_conntrack_locks[i]);
 14 
 15     /* 根据内存大小,初始化htable_size */
 16     if (!nf_conntrack_htable_size) {
 17         /* Idea from tcp.c: use 1/16384 of memory.
 18          * On i386: 32MB machine has 512 buckets.
 19          * >= 1GB machines have 16384 buckets.
 20          * >= 4GB machines have 65536 buckets.
 21          */
 22         nf_conntrack_htable_size
 23             = (((totalram_pages << PAGE_SHIFT) / 16384)
 24                / sizeof(struct hlist_head));
 25         if (totalram_pages > (4 * (1024 * 1024 * 1024 / PAGE_SIZE)))
 26             nf_conntrack_htable_size = 65536;
 27         else if (totalram_pages > (1024 * 1024 * 1024 / PAGE_SIZE))
 28             nf_conntrack_htable_size = 16384;
 29         if (nf_conntrack_htable_size < 32)
 30             nf_conntrack_htable_size = 32;
 31 
 32         /* Use a max. factor of four by default to get the same max as
 33          * with the old struct list_heads. When a table size is given
 34          * we use the old value of 8 to avoid reducing the max.
 35          * entries. */
 36         max_factor = 4;
 37     }
 38 
 39     /* 分配hash */
 40     nf_conntrack_hash = nf_ct_alloc_hashtable(&nf_conntrack_htable_size, 1);
 41     if (!nf_conntrack_hash)
 42         return -ENOMEM;
 43 
 44     /* 设置连接跟踪项的最大值 */
 45     nf_conntrack_max = max_factor * nf_conntrack_htable_size;
 46 
 47     /* 创建nf_conn连接跟踪slab */
 48     nf_conntrack_cachep = kmem_cache_create("nf_conntrack",
 49                         sizeof(struct nf_conn),
 50                         NFCT_INFOMASK + 1,
 51                         SLAB_TYPESAFE_BY_RCU | SLAB_HWCACHE_ALIGN, NULL);
 52     if (!nf_conntrack_cachep)
 53         goto err_cachep;
 54 
 55     printk(KERN_INFO "nf_conntrack version %s (%u buckets, %d max)\n",
 56            NF_CONNTRACK_VERSION, nf_conntrack_htable_size,
 57            nf_conntrack_max);
 58 
 59     /* 各个扩展项的初始化 */
 60     ret = nf_conntrack_expect_init();
 61     if (ret < 0)
 62         goto err_expect;
 63 
 64     ret = nf_conntrack_acct_init();
 65     if (ret < 0)
 66         goto err_acct;
 67 
 68     ret = nf_conntrack_tstamp_init();
 69     if (ret < 0)
 70         goto err_tstamp;
 71 
 72     ret = nf_conntrack_ecache_init();
 73     if (ret < 0)
 74         goto err_ecache;
 75 
 76     ret = nf_conntrack_timeout_init();
 77     if (ret < 0)
 78         goto err_timeout;
 79 
 80     ret = nf_conntrack_helper_init();
 81     if (ret < 0)
 82         goto err_helper;
 83 
 84     ret = nf_conntrack_labels_init();
 85     if (ret < 0)
 86         goto err_labels;
 87 
 88     ret = nf_conntrack_seqadj_init();
 89     if (ret < 0)
 90         goto err_seqadj;
 91 
 92     /* 初始化nf_ct_l3protos[]初始化为nf_conntrack_l3proto_generic */
 93     ret = nf_conntrack_proto_init();
 94     if (ret < 0)
 95         goto err_proto;
 96 
 97     /* 初始化连接跟踪gc任务 */
 98     conntrack_gc_work_init(&conntrack_gc_work);
 99     queue_delayed_work(system_long_wq, &conntrack_gc_work.dwork, HZ);
100 
101     return 0;
102 
103 err_proto:
104     nf_conntrack_seqadj_fini();
105 err_seqadj:
106     nf_conntrack_labels_fini();
107 err_labels:
108     nf_conntrack_helper_fini();
109 err_helper:
110     nf_conntrack_timeout_fini();
111 err_timeout:
112     nf_conntrack_ecache_fini();
113 err_ecache:
114     nf_conntrack_tstamp_fini();
115 err_tstamp:
116     nf_conntrack_acct_fini();
117 err_acct:
118     nf_conntrack_expect_fini();
119 err_expect:
120     kmem_cache_destroy(nf_conntrack_cachep);
121 err_cachep:
122     nf_ct_free_hashtable(nf_conntrack_hash, nf_conntrack_htable_size);
123     return ret;
124 }

 

协议与tuple操作初始化

nf_conntrack_l3proto_ipv4_init函数完成了协议和tuple操作函数相关的初始化;

 1 static int __init nf_conntrack_l3proto_ipv4_init(void)
 2 {
 3     int ret = 0;
 4 
 5     need_conntrack();
 6 
 7     ret = nf_register_sockopt(&so_getorigdst);
 8     if (ret < 0) {
 9         pr_err("Unable to register netfilter socket option\n");
10         return ret;
11     }
12 
13     ret = register_pernet_subsys(&ipv4_net_ops);
14     if (ret < 0) {
15         pr_err("nf_conntrack_ipv4: can't register pernet ops\n");
16         goto cleanup_sockopt;
17     }
18 
19     /* nf_conntrack_l4proto相关初始化 */
20     ret = nf_ct_l4proto_register(builtin_l4proto4,
21                      ARRAY_SIZE(builtin_l4proto4));
22     if (ret < 0)
23         goto cleanup_pernet;
24 
25     /* nf_conntrack_l3proto相关初始化 */
26     ret = nf_ct_l3proto_register(&nf_conntrack_l3proto_ipv4);
27     if (ret < 0) {
28         pr_err("nf_conntrack_ipv4: can't register ipv4 proto.\n");
29         goto cleanup_l4proto;
30     }
31 
32     return ret;
33 cleanup_l4proto:
34     nf_ct_l4proto_unregister(builtin_l4proto4,
35                  ARRAY_SIZE(builtin_l4proto4));
36  cleanup_pernet:
37     unregister_pernet_subsys(&ipv4_net_ops);
38  cleanup_sockopt:
39     nf_unregister_sockopt(&so_getorigdst);
40     return ret;
41 }

 

nf_conntrack_l3proto_ipv4

nf_conntrack_l3proto_ipv4 结构成员初始化包括了基础信息,tuple的相关操作,钩子函数的注册,注销等,每个函数的作用如下;

 1 struct nf_conntrack_l3proto nf_conntrack_l3proto_ipv4 __read_mostly = {
 2     .l3proto     = PF_INET,
 3     .name         = "ipv4",
 4     .pkt_to_tuple     = ipv4_pkt_to_tuple,
 5     .invert_tuple     = ipv4_invert_tuple,
 6     .print_tuple     = ipv4_print_tuple,
 7     .get_l4proto     = ipv4_get_l4proto,
 8 #if IS_ENABLED(CONFIG_NF_CT_NETLINK)
 9     .tuple_to_nlattr = ipv4_tuple_to_nlattr,
10     .nlattr_tuple_size = ipv4_nlattr_tuple_size,
11     .nlattr_to_tuple = ipv4_nlattr_to_tuple,
12     .nla_policy     = ipv4_nla_policy,
13 #endif
14     .net_ns_get     = ipv4_hooks_register,
15     .net_ns_put     = ipv4_hooks_unregister,
16     .me         = THIS_MODULE,
17 };

 

tuple相关操作
 1 /* 从ip头中获取源目的地址,存入tuple */
 2 static bool ipv4_pkt_to_tuple(const struct sk_buff *skb, unsigned int nhoff,
 3                   struct nf_conntrack_tuple *tuple)
 4 {
 5     const __be32 *ap;
 6     __be32 _addrs[2];
 7     ap = skb_header_pointer(skb, nhoff + offsetof(struct iphdr, saddr),
 8                 sizeof(u_int32_t) * 2, _addrs);
 9     if (ap == NULL)
10         return false;
11 
12     tuple->src.u3.ip = ap[0];
13     tuple->dst.u3.ip = ap[1];
14 
15     return true;
16 }
17 
18 /* 根据原tuple地址设置新tuple,源目的地址均相反 */
19 static bool ipv4_invert_tuple(struct nf_conntrack_tuple *tuple,
20                   const struct nf_conntrack_tuple *orig)
21 {
22     tuple->src.u3.ip = orig->dst.u3.ip;
23     tuple->dst.u3.ip = orig->src.u3.ip;
24 
25     return true;
26 }
27 
28 /* 打印tuple的源目的地址 */
29 static void ipv4_print_tuple(struct seq_file *s,
30                 const struct nf_conntrack_tuple *tuple)
31 {
32     seq_printf(s, "src=%pI4 dst=%pI4 ",
33            &tuple->src.u3.ip, &tuple->dst.u3.ip);
34 }
35 
36 /* 获取ip头中的协议 */
37 static int ipv4_get_l4proto(const struct sk_buff *skb, unsigned int nhoff,
38                 unsigned int *dataoff, u_int8_t *protonum)
39 {
40     const struct iphdr *iph;
41     struct iphdr _iph;
42 
43     
44     iph = skb_header_pointer(skb, nhoff, sizeof(_iph), &_iph);
45     if (iph == NULL)
46         return -NF_ACCEPT;
47 
48     /* Conntrack defragments packets, we might still see fragments
49      * inside ICMP packets though. */
50     if (iph->frag_off & htons(IP_OFFSET))
51         return -NF_ACCEPT;
52 
53     *dataoff = nhoff + (iph->ihl << 2);
54     *protonum = iph->protocol;
55 
56     /* Check bogus IP headers */
57     if (*dataoff > skb->len) {
58         pr_debug("nf_conntrack_ipv4: bogus IPv4 packet: "
59              "nhoff %u, ihl %u, skblen %u\n",
60              nhoff, iph->ihl << 2, skb->len);
61         return -NF_ACCEPT;
62     }
63 
64     return NF_ACCEPT;
65 }

 

netlink与tuple之间的操作
 1 /* 填充tuple的源目的地址到netlink */
 2 static int ipv4_tuple_to_nlattr(struct sk_buff *skb,
 3                 const struct nf_conntrack_tuple *tuple)
 4 {
 5     if (nla_put_in_addr(skb, CTA_IP_V4_SRC, tuple->src.u3.ip) ||
 6         nla_put_in_addr(skb, CTA_IP_V4_DST, tuple->dst.u3.ip))
 7         goto nla_put_failure;
 8     return 0;
 9 
10 nla_put_failure:
11     return -1;
12 }
13 
14 static const struct nla_policy ipv4_nla_policy[CTA_IP_MAX+1] = {
15     [CTA_IP_V4_SRC]    = { .type = NLA_U32 },
16     [CTA_IP_V4_DST]    = { .type = NLA_U32 },
17 };
18 
19 /* 将netlink中的源目的地址填充到tuple */
20 static int ipv4_nlattr_to_tuple(struct nlattr *tb[],
21                 struct nf_conntrack_tuple *t)
22 {
23     if (!tb[CTA_IP_V4_SRC] || !tb[CTA_IP_V4_DST])
24         return -EINVAL;
25 
26     t->src.u3.ip = nla_get_in_addr(tb[CTA_IP_V4_SRC]);
27     t->dst.u3.ip = nla_get_in_addr(tb[CTA_IP_V4_DST]);
28 
29     return 0;
30 }
31 
32 /* 获取netlink中的tuple大小 */
33 static int ipv4_nlattr_tuple_size(void)
34 {
35     return nla_policy_len(ipv4_nla_policy, CTA_IP_MAX + 1);
36 }

 

钩子函数的注册

ipv4_hooks_register钩子函数注册回调,其中分为两步,defrag钩子注册和其他钩子注册;

 1 static int ipv4_hooks_register(struct net *net)
 2 {
 3     struct conntrack4_net *cnet = net_generic(net, conntrack4_net_id);
 4     int err = 0;
 5 
 6     mutex_lock(&register_ipv4_hooks);
 7 
 8     cnet->users++;
 9     if (cnet->users > 1)
10         goto out_unlock;
11 
12     /* defrag钩子函数注册 */
13     err = nf_defrag_ipv4_enable(net);
14     if (err) {
15         cnet->users = 0;
16         goto out_unlock;
17     }
18 
19     /* 注册netfilter钩子函数 */
20     err = nf_register_net_hooks(net, ipv4_conntrack_ops,
21                     ARRAY_SIZE(ipv4_conntrack_ops));
22 
23     if (err)
24         cnet->users = 0;
25  out_unlock:
26     mutex_unlock(&register_ipv4_hooks);
27     return err;
28 }

 

nf_defrag_ipv4_enable为defrag钩子注册流程;

 1 int nf_defrag_ipv4_enable(struct net *net)
 2 {
 3     int err = 0;
 4 
 5     might_sleep();
 6 
 7     if (net->nf.defrag_ipv4)
 8         return 0;
 9 
10     mutex_lock(&defrag4_mutex);
11     if (net->nf.defrag_ipv4)
12         goto out_unlock;
13 
14     err = nf_register_net_hooks(net, ipv4_defrag_ops,
15                     ARRAY_SIZE(ipv4_defrag_ops));
16     if (err == 0)
17         net->nf.defrag_ipv4 = true;
18 
19  out_unlock:
20     mutex_unlock(&defrag4_mutex);
21     return err;
22 }

 

defrag钩子函数;

 1 static struct nf_hook_ops ipv4_defrag_ops[] = {
 2     {
 3         .hook        = ipv4_conntrack_defrag,
 4         .pf        = NFPROTO_IPV4,
 5         .hooknum    = NF_INET_PRE_ROUTING,
 6         .priority    = NF_IP_PRI_CONNTRACK_DEFRAG,
 7     },
 8     {
 9         .hook           = ipv4_conntrack_defrag,
10         .pf             = NFPROTO_IPV4,
11         .hooknum        = NF_INET_LOCAL_OUT,
12         .priority       = NF_IP_PRI_CONNTRACK_DEFRAG,
13     },
14 };

 

conntrack_in,helper,confirm钩子函数;

 1 static struct nf_hook_ops ipv4_conntrack_ops[] __read_mostly = {
 2     {
 3         .hook        = ipv4_conntrack_in,
 4         .pf        = NFPROTO_IPV4,
 5         .hooknum    = NF_INET_PRE_ROUTING,
 6         .priority    = NF_IP_PRI_CONNTRACK,
 7     },
 8     {
 9         .hook        = ipv4_conntrack_local,
10         .pf        = NFPROTO_IPV4,
11         .hooknum    = NF_INET_LOCAL_OUT,
12         .priority    = NF_IP_PRI_CONNTRACK,
13     },
14     {
15         .hook        = ipv4_helper,
16         .pf        = NFPROTO_IPV4,
17         .hooknum    = NF_INET_POST_ROUTING,
18         .priority    = NF_IP_PRI_CONNTRACK_HELPER,
19     },
20     {
21         .hook        = ipv4_confirm,
22         .pf        = NFPROTO_IPV4,
23         .hooknum    = NF_INET_POST_ROUTING,
24         .priority    = NF_IP_PRI_CONNTRACK_CONFIRM,
25     },
26     {
27         .hook        = ipv4_helper,
28         .pf        = NFPROTO_IPV4,
29         .hooknum    = NF_INET_LOCAL_IN,
30         .priority    = NF_IP_PRI_CONNTRACK_HELPER,
31     },
32     {
33         .hook        = ipv4_confirm,
34         .pf        = NFPROTO_IPV4,
35         .hooknum    = NF_INET_LOCAL_IN,
36         .priority    = NF_IP_PRI_CONNTRACK_CONFIRM,
37     },
38 };

 

nf_conntrack_l4proto_tcp4

多种协议会实现自己的nf_conntrack_l4proto,这里只列出tcp的实现,简要看下;

nf_conntrack_l4proto_tcp4结构中的函数与nf_conntrack_l3proto_ipv4 结构类似,其中多数函数是将源目的ip的操作换成了源目的端口的操作,此处不重复分析,另外增加了几个连接相关的操作函数packet,new等,遇到的时候在展开分析;

 1 struct nf_conntrack_l4proto nf_conntrack_l4proto_tcp4 __read_mostly =
 2 {
 3     .l3proto        = PF_INET,
 4     .l4proto         = IPPROTO_TCP,
 5     .name             = "tcp",
 6     .pkt_to_tuple         = tcp_pkt_to_tuple,
 7     .invert_tuple         = tcp_invert_tuple,
 8     .print_tuple         = tcp_print_tuple,
 9     .print_conntrack     = tcp_print_conntrack,
10     .packet         = tcp_packet,
11     .get_timeouts        = tcp_get_timeouts,
12     .new             = tcp_new,
13     .error            = tcp_error,
14     .can_early_drop        = tcp_can_early_drop,
15 #if IS_ENABLED(CONFIG_NF_CT_NETLINK)
16     .to_nlattr        = tcp_to_nlattr,
17     .nlattr_size        = tcp_nlattr_size,
18     .from_nlattr        = nlattr_to_tcp,
19     .tuple_to_nlattr    = nf_ct_port_tuple_to_nlattr,
20     .nlattr_to_tuple    = nf_ct_port_nlattr_to_tuple,
21     .nlattr_tuple_size    = tcp_nlattr_tuple_size,
22     .nla_policy        = nf_ct_port_nla_policy,
23 #endif
24 #if IS_ENABLED(CONFIG_NF_CT_NETLINK_TIMEOUT)
25     .ctnl_timeout        = {
26         .nlattr_to_obj    = tcp_timeout_nlattr_to_obj,
27         .obj_to_nlattr    = tcp_timeout_obj_to_nlattr,
28         .nlattr_max    = CTA_TIMEOUT_TCP_MAX,
29         .obj_size    = sizeof(unsigned int) *
30                     TCP_CONNTRACK_TIMEOUT_MAX,
31         .nla_policy    = tcp_timeout_nla_policy,
32     },
33 #endif /* CONFIG_NF_CT_NETLINK_TIMEOUT */
34     .init_net        = tcp_init_net,
35     .get_net_proto        = tcp_get_net_proto,
36 };

 

 

相关文章: