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