Merge tag 'linux-kselftest-5.2-rc1-2' of git://git.kernel.org/pub/scm/linux/kernel...
[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_timeout.h>
21 #include <net/netfilter/nf_conntrack_zones.h>
22
23 static inline int xt_ct_target(struct sk_buff *skb, struct nf_conn *ct)
24 {
25         /* Previously seen (loopback)? Ignore. */
26         if (skb->_nfct != 0)
27                 return XT_CONTINUE;
28
29         if (ct) {
30                 atomic_inc(&ct->ct_general.use);
31                 nf_ct_set(skb, ct, IP_CT_NEW);
32         } else {
33                 nf_ct_set(skb, ct, IP_CT_UNTRACKED);
34         }
35
36         return XT_CONTINUE;
37 }
38
39 static unsigned int xt_ct_target_v0(struct sk_buff *skb,
40                                     const struct xt_action_param *par)
41 {
42         const struct xt_ct_target_info *info = par->targinfo;
43         struct nf_conn *ct = info->ct;
44
45         return xt_ct_target(skb, ct);
46 }
47
48 static unsigned int xt_ct_target_v1(struct sk_buff *skb,
49                                     const struct xt_action_param *par)
50 {
51         const struct xt_ct_target_info_v1 *info = par->targinfo;
52         struct nf_conn *ct = info->ct;
53
54         return xt_ct_target(skb, ct);
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
76 xt_ct_set_helper(struct nf_conn *ct, const char *helper_name,
77                  const struct xt_tgchk_param *par)
78 {
79         struct nf_conntrack_helper *helper;
80         struct nf_conn_help *help;
81         u8 proto;
82
83         proto = xt_ct_find_proto(par);
84         if (!proto) {
85                 pr_info_ratelimited("You must specify a L4 protocol and not use inversions on it\n");
86                 return -ENOENT;
87         }
88
89         helper = nf_conntrack_helper_try_module_get(helper_name, par->family,
90                                                     proto);
91         if (helper == NULL) {
92                 pr_info_ratelimited("No such helper \"%s\"\n", helper_name);
93                 return -ENOENT;
94         }
95
96         help = nf_ct_helper_ext_add(ct, GFP_KERNEL);
97         if (help == NULL) {
98                 nf_conntrack_helper_put(helper);
99                 return -ENOMEM;
100         }
101
102         help->helper = helper;
103         return 0;
104 }
105
106 static int
107 xt_ct_set_timeout(struct nf_conn *ct, const struct xt_tgchk_param *par,
108                   const char *timeout_name)
109 {
110 #ifdef CONFIG_NF_CONNTRACK_TIMEOUT
111         const struct nf_conntrack_l4proto *l4proto;
112         u8 proto;
113
114         proto = xt_ct_find_proto(par);
115         if (!proto) {
116                 pr_info_ratelimited("You must specify a L4 protocol and not "
117                                     "use inversions on it");
118                 return -EINVAL;
119         }
120         l4proto = nf_ct_l4proto_find(proto);
121         return nf_ct_set_timeout(par->net, ct, par->family, l4proto->l4proto,
122                                  timeout_name);
123
124 #else
125         return -EOPNOTSUPP;
126 #endif
127 }
128
129 static u16 xt_ct_flags_to_dir(const struct xt_ct_target_info_v1 *info)
130 {
131         switch (info->flags & (XT_CT_ZONE_DIR_ORIG |
132                                XT_CT_ZONE_DIR_REPL)) {
133         case XT_CT_ZONE_DIR_ORIG:
134                 return NF_CT_ZONE_DIR_ORIG;
135         case XT_CT_ZONE_DIR_REPL:
136                 return NF_CT_ZONE_DIR_REPL;
137         default:
138                 return NF_CT_DEFAULT_ZONE_DIR;
139         }
140 }
141
142 static int xt_ct_tg_check(const struct xt_tgchk_param *par,
143                           struct xt_ct_target_info_v1 *info)
144 {
145         struct nf_conntrack_zone zone;
146         struct nf_conn_help *help;
147         struct nf_conn *ct;
148         int ret = -EOPNOTSUPP;
149
150         if (info->flags & XT_CT_NOTRACK) {
151                 ct = NULL;
152                 goto out;
153         }
154
155 #ifndef CONFIG_NF_CONNTRACK_ZONES
156         if (info->zone || info->flags & (XT_CT_ZONE_DIR_ORIG |
157                                          XT_CT_ZONE_DIR_REPL |
158                                          XT_CT_ZONE_MARK))
159                 goto err1;
160 #endif
161
162         ret = nf_ct_netns_get(par->net, par->family);
163         if (ret < 0)
164                 goto err1;
165
166         memset(&zone, 0, sizeof(zone));
167         zone.id = info->zone;
168         zone.dir = xt_ct_flags_to_dir(info);
169         if (info->flags & XT_CT_ZONE_MARK)
170                 zone.flags |= NF_CT_FLAG_MARK;
171
172         ct = nf_ct_tmpl_alloc(par->net, &zone, GFP_KERNEL);
173         if (!ct) {
174                 ret = -ENOMEM;
175                 goto err2;
176         }
177
178         ret = 0;
179         if ((info->ct_events || info->exp_events) &&
180             !nf_ct_ecache_ext_add(ct, info->ct_events, info->exp_events,
181                                   GFP_KERNEL)) {
182                 ret = -EINVAL;
183                 goto err3;
184         }
185
186         if (info->helper[0]) {
187                 if (strnlen(info->helper, sizeof(info->helper)) == sizeof(info->helper)) {
188                         ret = -ENAMETOOLONG;
189                         goto err3;
190                 }
191
192                 ret = xt_ct_set_helper(ct, info->helper, par);
193                 if (ret < 0)
194                         goto err3;
195         }
196
197         if (info->timeout[0]) {
198                 if (strnlen(info->timeout, sizeof(info->timeout)) == sizeof(info->timeout)) {
199                         ret = -ENAMETOOLONG;
200                         goto err4;
201                 }
202
203                 ret = xt_ct_set_timeout(ct, par, info->timeout);
204                 if (ret < 0)
205                         goto err4;
206         }
207         __set_bit(IPS_CONFIRMED_BIT, &ct->status);
208         nf_conntrack_get(&ct->ct_general);
209 out:
210         info->ct = ct;
211         return 0;
212
213 err4:
214         help = nfct_help(ct);
215         if (help)
216                 nf_conntrack_helper_put(help->helper);
217 err3:
218         nf_ct_tmpl_free(ct);
219 err2:
220         nf_ct_netns_put(par->net, par->family);
221 err1:
222         return ret;
223 }
224
225 static int xt_ct_tg_check_v0(const struct xt_tgchk_param *par)
226 {
227         struct xt_ct_target_info *info = par->targinfo;
228         struct xt_ct_target_info_v1 info_v1 = {
229                 .flags          = info->flags,
230                 .zone           = info->zone,
231                 .ct_events      = info->ct_events,
232                 .exp_events     = info->exp_events,
233         };
234         int ret;
235
236         if (info->flags & ~XT_CT_NOTRACK)
237                 return -EINVAL;
238
239         memcpy(info_v1.helper, info->helper, sizeof(info->helper));
240
241         ret = xt_ct_tg_check(par, &info_v1);
242         if (ret < 0)
243                 return ret;
244
245         info->ct = info_v1.ct;
246
247         return ret;
248 }
249
250 static int xt_ct_tg_check_v1(const struct xt_tgchk_param *par)
251 {
252         struct xt_ct_target_info_v1 *info = par->targinfo;
253
254         if (info->flags & ~XT_CT_NOTRACK)
255                 return -EINVAL;
256
257         return xt_ct_tg_check(par, par->targinfo);
258 }
259
260 static int xt_ct_tg_check_v2(const struct xt_tgchk_param *par)
261 {
262         struct xt_ct_target_info_v1 *info = par->targinfo;
263
264         if (info->flags & ~XT_CT_MASK)
265                 return -EINVAL;
266
267         return xt_ct_tg_check(par, par->targinfo);
268 }
269
270 static void xt_ct_tg_destroy(const struct xt_tgdtor_param *par,
271                              struct xt_ct_target_info_v1 *info)
272 {
273         struct nf_conn *ct = info->ct;
274         struct nf_conn_help *help;
275
276         if (ct) {
277                 help = nfct_help(ct);
278                 if (help)
279                         nf_conntrack_helper_put(help->helper);
280
281                 nf_ct_netns_put(par->net, par->family);
282
283                 nf_ct_destroy_timeout(ct);
284                 nf_ct_put(info->ct);
285         }
286 }
287
288 static void xt_ct_tg_destroy_v0(const struct xt_tgdtor_param *par)
289 {
290         struct xt_ct_target_info *info = par->targinfo;
291         struct xt_ct_target_info_v1 info_v1 = {
292                 .flags          = info->flags,
293                 .zone           = info->zone,
294                 .ct_events      = info->ct_events,
295                 .exp_events     = info->exp_events,
296                 .ct             = info->ct,
297         };
298         memcpy(info_v1.helper, info->helper, sizeof(info->helper));
299
300         xt_ct_tg_destroy(par, &info_v1);
301 }
302
303 static void xt_ct_tg_destroy_v1(const struct xt_tgdtor_param *par)
304 {
305         xt_ct_tg_destroy(par, par->targinfo);
306 }
307
308 static struct xt_target xt_ct_tg_reg[] __read_mostly = {
309         {
310                 .name           = "CT",
311                 .family         = NFPROTO_UNSPEC,
312                 .targetsize     = sizeof(struct xt_ct_target_info),
313                 .usersize       = offsetof(struct xt_ct_target_info, ct),
314                 .checkentry     = xt_ct_tg_check_v0,
315                 .destroy        = xt_ct_tg_destroy_v0,
316                 .target         = xt_ct_target_v0,
317                 .table          = "raw",
318                 .me             = THIS_MODULE,
319         },
320         {
321                 .name           = "CT",
322                 .family         = NFPROTO_UNSPEC,
323                 .revision       = 1,
324                 .targetsize     = sizeof(struct xt_ct_target_info_v1),
325                 .usersize       = offsetof(struct xt_ct_target_info, ct),
326                 .checkentry     = xt_ct_tg_check_v1,
327                 .destroy        = xt_ct_tg_destroy_v1,
328                 .target         = xt_ct_target_v1,
329                 .table          = "raw",
330                 .me             = THIS_MODULE,
331         },
332         {
333                 .name           = "CT",
334                 .family         = NFPROTO_UNSPEC,
335                 .revision       = 2,
336                 .targetsize     = sizeof(struct xt_ct_target_info_v1),
337                 .usersize       = offsetof(struct xt_ct_target_info, ct),
338                 .checkentry     = xt_ct_tg_check_v2,
339                 .destroy        = xt_ct_tg_destroy_v1,
340                 .target         = xt_ct_target_v1,
341                 .table          = "raw",
342                 .me             = THIS_MODULE,
343         },
344 };
345
346 static unsigned int
347 notrack_tg(struct sk_buff *skb, const struct xt_action_param *par)
348 {
349         /* Previously seen (loopback)? Ignore. */
350         if (skb->_nfct != 0)
351                 return XT_CONTINUE;
352
353         nf_ct_set(skb, NULL, IP_CT_UNTRACKED);
354
355         return XT_CONTINUE;
356 }
357
358 static int notrack_chk(const struct xt_tgchk_param *par)
359 {
360         if (!par->net->xt.notrack_deprecated_warning) {
361                 pr_info("netfilter: NOTRACK target is deprecated, "
362                         "use CT instead or upgrade iptables\n");
363                 par->net->xt.notrack_deprecated_warning = true;
364         }
365         return 0;
366 }
367
368 static struct xt_target notrack_tg_reg __read_mostly = {
369         .name           = "NOTRACK",
370         .revision       = 0,
371         .family         = NFPROTO_UNSPEC,
372         .checkentry     = notrack_chk,
373         .target         = notrack_tg,
374         .table          = "raw",
375         .me             = THIS_MODULE,
376 };
377
378 static int __init xt_ct_tg_init(void)
379 {
380         int ret;
381
382         ret = xt_register_target(&notrack_tg_reg);
383         if (ret < 0)
384                 return ret;
385
386         ret = xt_register_targets(xt_ct_tg_reg, ARRAY_SIZE(xt_ct_tg_reg));
387         if (ret < 0) {
388                 xt_unregister_target(&notrack_tg_reg);
389                 return ret;
390         }
391         return 0;
392 }
393
394 static void __exit xt_ct_tg_exit(void)
395 {
396         xt_unregister_targets(xt_ct_tg_reg, ARRAY_SIZE(xt_ct_tg_reg));
397         xt_unregister_target(&notrack_tg_reg);
398 }
399
400 module_init(xt_ct_tg_init);
401 module_exit(xt_ct_tg_exit);
402
403 MODULE_LICENSE("GPL");
404 MODULE_DESCRIPTION("Xtables: connection tracking target");
405 MODULE_ALIAS("ipt_CT");
406 MODULE_ALIAS("ip6t_CT");
407 MODULE_ALIAS("ipt_NOTRACK");
408 MODULE_ALIAS("ip6t_NOTRACK");