HID: hid-lg4ff: remove unused label in !LEDS_CLASS case
[firefly-linux-kernel-4.4.55.git] / net / netfilter / xt_CT.c
1 /*
2  * Copyright (c) 2010 Patrick McHardy <kaber@trash.net>
3  *
4  * This program is free software; you can redistribute it and/or modify
5  * it under the terms of the GNU General Public License version 2 as
6  * published by the Free Software Foundation.
7  */
8 #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
9 #include <linux/module.h>
10 #include <linux/gfp.h>
11 #include <linux/skbuff.h>
12 #include <linux/netfilter_ipv4/ip_tables.h>
13 #include <linux/netfilter_ipv6/ip6_tables.h>
14 #include <linux/netfilter/x_tables.h>
15 #include <linux/netfilter/xt_CT.h>
16 #include <net/netfilter/nf_conntrack.h>
17 #include <net/netfilter/nf_conntrack_helper.h>
18 #include <net/netfilter/nf_conntrack_ecache.h>
19 #include <net/netfilter/nf_conntrack_timeout.h>
20 #include <net/netfilter/nf_conntrack_zones.h>
21
22 static unsigned int xt_ct_target_v0(struct sk_buff *skb,
23                                     const struct xt_action_param *par)
24 {
25         const struct xt_ct_target_info *info = par->targinfo;
26         struct nf_conn *ct = info->ct;
27
28         /* Previously seen (loopback)? Ignore. */
29         if (skb->nfct != NULL)
30                 return XT_CONTINUE;
31
32         atomic_inc(&ct->ct_general.use);
33         skb->nfct = &ct->ct_general;
34         skb->nfctinfo = IP_CT_NEW;
35
36         return XT_CONTINUE;
37 }
38
39 static unsigned int xt_ct_target_v1(struct sk_buff *skb,
40                                     const struct xt_action_param *par)
41 {
42         const struct xt_ct_target_info_v1 *info = par->targinfo;
43         struct nf_conn *ct = info->ct;
44
45         /* Previously seen (loopback)? Ignore. */
46         if (skb->nfct != NULL)
47                 return XT_CONTINUE;
48
49         atomic_inc(&ct->ct_general.use);
50         skb->nfct = &ct->ct_general;
51         skb->nfctinfo = IP_CT_NEW;
52
53         return XT_CONTINUE;
54 }
55
56 static u8 xt_ct_find_proto(const struct xt_tgchk_param *par)
57 {
58         if (par->family == NFPROTO_IPV4) {
59                 const struct ipt_entry *e = par->entryinfo;
60
61                 if (e->ip.invflags & IPT_INV_PROTO)
62                         return 0;
63                 return e->ip.proto;
64         } else if (par->family == NFPROTO_IPV6) {
65                 const struct ip6t_entry *e = par->entryinfo;
66
67                 if (e->ipv6.invflags & IP6T_INV_PROTO)
68                         return 0;
69                 return e->ipv6.proto;
70         } else
71                 return 0;
72 }
73
74 static int xt_ct_tg_check_v0(const struct xt_tgchk_param *par)
75 {
76         struct xt_ct_target_info *info = par->targinfo;
77         struct nf_conntrack_tuple t;
78         struct nf_conn_help *help;
79         struct nf_conn *ct;
80         int ret = 0;
81         u8 proto;
82
83         if (info->flags & ~XT_CT_NOTRACK)
84                 return -EINVAL;
85
86         if (info->flags & XT_CT_NOTRACK) {
87                 ct = nf_ct_untracked_get();
88                 atomic_inc(&ct->ct_general.use);
89                 goto out;
90         }
91
92 #ifndef CONFIG_NF_CONNTRACK_ZONES
93         if (info->zone)
94                 goto err1;
95 #endif
96
97         ret = nf_ct_l3proto_try_module_get(par->family);
98         if (ret < 0)
99                 goto err1;
100
101         memset(&t, 0, sizeof(t));
102         ct = nf_conntrack_alloc(par->net, info->zone, &t, &t, GFP_KERNEL);
103         ret = PTR_ERR(ct);
104         if (IS_ERR(ct))
105                 goto err2;
106
107         ret = 0;
108         if ((info->ct_events || info->exp_events) &&
109             !nf_ct_ecache_ext_add(ct, info->ct_events, info->exp_events,
110                                   GFP_KERNEL))
111                 goto err3;
112
113         if (info->helper[0]) {
114                 ret = -ENOENT;
115                 proto = xt_ct_find_proto(par);
116                 if (!proto) {
117                         pr_info("You must specify a L4 protocol, "
118                                 "and not use inversions on it.\n");
119                         goto err3;
120                 }
121
122                 ret = -ENOMEM;
123                 help = nf_ct_helper_ext_add(ct, GFP_KERNEL);
124                 if (help == NULL)
125                         goto err3;
126
127                 ret = -ENOENT;
128                 help->helper = nf_conntrack_helper_try_module_get(info->helper,
129                                                                   par->family,
130                                                                   proto);
131                 if (help->helper == NULL) {
132                         pr_info("No such helper \"%s\"\n", info->helper);
133                         goto err3;
134                 }
135         }
136
137         __set_bit(IPS_TEMPLATE_BIT, &ct->status);
138         __set_bit(IPS_CONFIRMED_BIT, &ct->status);
139 out:
140         info->ct = ct;
141         return 0;
142
143 err3:
144         nf_conntrack_free(ct);
145 err2:
146         nf_ct_l3proto_module_put(par->family);
147 err1:
148         return ret;
149 }
150
151 static int xt_ct_tg_check_v1(const struct xt_tgchk_param *par)
152 {
153         struct xt_ct_target_info_v1 *info = par->targinfo;
154         struct nf_conntrack_tuple t;
155         struct nf_conn_help *help;
156         struct nf_conn *ct;
157         int ret = 0;
158         u8 proto;
159
160         if (info->flags & ~XT_CT_NOTRACK)
161                 return -EINVAL;
162
163         if (info->flags & XT_CT_NOTRACK) {
164                 ct = nf_ct_untracked_get();
165                 atomic_inc(&ct->ct_general.use);
166                 goto out;
167         }
168
169 #ifndef CONFIG_NF_CONNTRACK_ZONES
170         if (info->zone)
171                 goto err1;
172 #endif
173
174         ret = nf_ct_l3proto_try_module_get(par->family);
175         if (ret < 0)
176                 goto err1;
177
178         memset(&t, 0, sizeof(t));
179         ct = nf_conntrack_alloc(par->net, info->zone, &t, &t, GFP_KERNEL);
180         ret = PTR_ERR(ct);
181         if (IS_ERR(ct))
182                 goto err2;
183
184         ret = 0;
185         if ((info->ct_events || info->exp_events) &&
186             !nf_ct_ecache_ext_add(ct, info->ct_events, info->exp_events,
187                                   GFP_KERNEL))
188                 goto err3;
189
190         if (info->helper[0]) {
191                 ret = -ENOENT;
192                 proto = xt_ct_find_proto(par);
193                 if (!proto) {
194                         pr_info("You must specify a L4 protocol, "
195                                 "and not use inversions on it.\n");
196                         goto err3;
197                 }
198
199                 ret = -ENOMEM;
200                 help = nf_ct_helper_ext_add(ct, GFP_KERNEL);
201                 if (help == NULL)
202                         goto err3;
203
204                 ret = -ENOENT;
205                 help->helper = nf_conntrack_helper_try_module_get(info->helper,
206                                                                   par->family,
207                                                                   proto);
208                 if (help->helper == NULL) {
209                         pr_info("No such helper \"%s\"\n", info->helper);
210                         goto err3;
211                 }
212         }
213
214 #ifdef CONFIG_NF_CONNTRACK_TIMEOUT
215         if (info->timeout) {
216                 typeof(nf_ct_timeout_find_get_hook) timeout_find_get;
217                 struct ctnl_timeout *timeout;
218                 struct nf_conn_timeout *timeout_ext;
219
220                 timeout_find_get =
221                         rcu_dereference(nf_ct_timeout_find_get_hook);
222
223                 if (timeout_find_get) {
224                         const struct ipt_entry *e = par->entryinfo;
225
226                         if (e->ip.invflags & IPT_INV_PROTO) {
227                                 ret = -EINVAL;
228                                 pr_info("You cannot use inversion on "
229                                          "L4 protocol\n");
230                                 goto err3;
231                         }
232                         timeout = timeout_find_get(info->timeout);
233                         if (timeout == NULL) {
234                                 ret = -ENOENT;
235                                 pr_info("No such timeout policy \"%s\"\n",
236                                         info->timeout);
237                                 goto err3;
238                         }
239                         if (timeout->l3num != par->family) {
240                                 ret = -EINVAL;
241                                 pr_info("Timeout policy `%s' can only be "
242                                         "used by L3 protocol number %d\n",
243                                         info->timeout, timeout->l3num);
244                                 goto err3;
245                         }
246                         if (timeout->l4num != e->ip.proto) {
247                                 ret = -EINVAL;
248                                 pr_info("Timeout policy `%s' can only be "
249                                         "used by L4 protocol number %d\n",
250                                         info->timeout, timeout->l4num);
251                                 goto err3;
252                         }
253                         timeout_ext = nf_ct_timeout_ext_add(ct, timeout,
254                                                             GFP_KERNEL);
255                         if (timeout_ext == NULL) {
256                                 ret = -ENOMEM;
257                                 goto err3;
258                         }
259                 } else {
260                         ret = -ENOENT;
261                         pr_info("Timeout policy base is empty\n");
262                         goto err3;
263                 }
264         }
265 #endif
266
267         __set_bit(IPS_TEMPLATE_BIT, &ct->status);
268         __set_bit(IPS_CONFIRMED_BIT, &ct->status);
269 out:
270         info->ct = ct;
271         return 0;
272
273 err3:
274         nf_conntrack_free(ct);
275 err2:
276         nf_ct_l3proto_module_put(par->family);
277 err1:
278         return ret;
279 }
280
281 static void xt_ct_tg_destroy_v0(const struct xt_tgdtor_param *par)
282 {
283         struct xt_ct_target_info *info = par->targinfo;
284         struct nf_conn *ct = info->ct;
285         struct nf_conn_help *help;
286
287         if (!nf_ct_is_untracked(ct)) {
288                 help = nfct_help(ct);
289                 if (help)
290                         module_put(help->helper->me);
291
292                 nf_ct_l3proto_module_put(par->family);
293         }
294         nf_ct_put(info->ct);
295 }
296
297 static void xt_ct_tg_destroy_v1(const struct xt_tgdtor_param *par)
298 {
299         struct xt_ct_target_info_v1 *info = par->targinfo;
300         struct nf_conn *ct = info->ct;
301         struct nf_conn_help *help;
302 #ifdef CONFIG_NF_CONNTRACK_TIMEOUT
303         struct nf_conn_timeout *timeout_ext;
304         typeof(nf_ct_timeout_put_hook) timeout_put;
305 #endif
306         if (!nf_ct_is_untracked(ct)) {
307                 help = nfct_help(ct);
308                 if (help)
309                         module_put(help->helper->me);
310
311                 nf_ct_l3proto_module_put(par->family);
312
313 #ifdef CONFIG_NF_CONNTRACK_TIMEOUT
314                 timeout_put = rcu_dereference(nf_ct_timeout_put_hook);
315
316                 if (timeout_put) {
317                         timeout_ext = nf_ct_timeout_find(ct);
318                         if (timeout_ext)
319                                 timeout_put(timeout_ext->timeout);
320                 }
321 #endif
322         }
323         nf_ct_put(info->ct);
324 }
325
326 static struct xt_target xt_ct_tg_reg[] __read_mostly = {
327         {
328                 .name           = "CT",
329                 .family         = NFPROTO_UNSPEC,
330                 .targetsize     = sizeof(struct xt_ct_target_info),
331                 .checkentry     = xt_ct_tg_check_v0,
332                 .destroy        = xt_ct_tg_destroy_v0,
333                 .target         = xt_ct_target_v0,
334                 .table          = "raw",
335                 .me             = THIS_MODULE,
336         },
337         {
338                 .name           = "CT",
339                 .family         = NFPROTO_UNSPEC,
340                 .revision       = 1,
341                 .targetsize     = sizeof(struct xt_ct_target_info_v1),
342                 .checkentry     = xt_ct_tg_check_v1,
343                 .destroy        = xt_ct_tg_destroy_v1,
344                 .target         = xt_ct_target_v1,
345                 .table          = "raw",
346                 .me             = THIS_MODULE,
347         },
348 };
349
350 static int __init xt_ct_tg_init(void)
351 {
352         return xt_register_targets(xt_ct_tg_reg, ARRAY_SIZE(xt_ct_tg_reg));
353 }
354
355 static void __exit xt_ct_tg_exit(void)
356 {
357         xt_unregister_targets(xt_ct_tg_reg, ARRAY_SIZE(xt_ct_tg_reg));
358 }
359
360 module_init(xt_ct_tg_init);
361 module_exit(xt_ct_tg_exit);
362
363 MODULE_LICENSE("GPL");
364 MODULE_DESCRIPTION("Xtables: connection tracking target");
365 MODULE_ALIAS("ipt_CT");
366 MODULE_ALIAS("ip6t_CT");