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