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
23static inline int xt_ct_target(struct sk_buff *skb, struct nf_conn *ct)
24{
25	/* Previously seen (loopback)? Ignore. */
26	if (skb->nfct != NULL)
27		return XT_CONTINUE;
28
29	/* special case the untracked ct : we want the percpu object */
30	if (!ct)
31		ct = nf_ct_untracked_get();
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
39static 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
48static 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
57static 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
75static int
76xt_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("You must specify a L4 protocol, and not use "
86			"inversions on it.\n");
87		return -ENOENT;
88	}
89
90	helper = nf_conntrack_helper_try_module_get(helper_name, par->family,
91						    proto);
92	if (helper == NULL) {
93		pr_info("No such helper \"%s\"\n", helper_name);
94		return -ENOENT;
95	}
96
97	help = nf_ct_helper_ext_add(ct, helper, GFP_KERNEL);
98	if (help == NULL) {
99		module_put(helper->me);
100		return -ENOMEM;
101	}
102
103	help->helper = helper;
104	return 0;
105}
106
107#ifdef CONFIG_NF_CONNTRACK_TIMEOUT
108static void __xt_ct_tg_timeout_put(struct ctnl_timeout *timeout)
109{
110	typeof(nf_ct_timeout_put_hook) timeout_put;
111
112	timeout_put = rcu_dereference(nf_ct_timeout_put_hook);
113	if (timeout_put)
114		timeout_put(timeout);
115}
116#endif
117
118static int
119xt_ct_set_timeout(struct nf_conn *ct, const struct xt_tgchk_param *par,
120		  const char *timeout_name)
121{
122#ifdef CONFIG_NF_CONNTRACK_TIMEOUT
123	typeof(nf_ct_timeout_find_get_hook) timeout_find_get;
124	struct ctnl_timeout *timeout;
125	struct nf_conn_timeout *timeout_ext;
126	struct nf_conntrack_l4proto *l4proto;
127	int ret = 0;
128	u8 proto;
129
130	rcu_read_lock();
131	timeout_find_get = rcu_dereference(nf_ct_timeout_find_get_hook);
132	if (timeout_find_get == NULL) {
133		ret = -ENOENT;
134		pr_info("Timeout policy base is empty\n");
135		goto out;
136	}
137
138	proto = xt_ct_find_proto(par);
139	if (!proto) {
140		ret = -EINVAL;
141		pr_info("You must specify a L4 protocol, and not use "
142			"inversions on it.\n");
143		goto out;
144	}
145
146	timeout = timeout_find_get(timeout_name);
147	if (timeout == NULL) {
148		ret = -ENOENT;
149		pr_info("No such timeout policy \"%s\"\n", timeout_name);
150		goto out;
151	}
152
153	if (timeout->l3num != par->family) {
154		ret = -EINVAL;
155		pr_info("Timeout policy `%s' can only be used by L3 protocol "
156			"number %d\n", timeout_name, timeout->l3num);
157		goto err_put_timeout;
158	}
159	/* Make sure the timeout policy matches any existing protocol tracker,
160	 * otherwise default to generic.
161	 */
162	l4proto = __nf_ct_l4proto_find(par->family, proto);
163	if (timeout->l4proto->l4proto != l4proto->l4proto) {
164		ret = -EINVAL;
165		pr_info("Timeout policy `%s' can only be used by L4 protocol "
166			"number %d\n",
167			timeout_name, timeout->l4proto->l4proto);
168		goto err_put_timeout;
169	}
170	timeout_ext = nf_ct_timeout_ext_add(ct, timeout, GFP_ATOMIC);
171	if (timeout_ext == NULL)
172		ret = -ENOMEM;
173
174err_put_timeout:
175	__xt_ct_tg_timeout_put(timeout);
176out:
177	rcu_read_unlock();
178	return ret;
179#else
180	return -EOPNOTSUPP;
181#endif
182}
183
184static int xt_ct_tg_check(const struct xt_tgchk_param *par,
185			  struct xt_ct_target_info_v1 *info)
186{
187	struct nf_conntrack_tuple t;
188	struct nf_conn *ct;
189	int ret = -EOPNOTSUPP;
190
191	if (info->flags & XT_CT_NOTRACK) {
192		ct = NULL;
193		goto out;
194	}
195
196#ifndef CONFIG_NF_CONNTRACK_ZONES
197	if (info->zone)
198		goto err1;
199#endif
200
201	ret = nf_ct_l3proto_try_module_get(par->family);
202	if (ret < 0)
203		goto err1;
204
205	memset(&t, 0, sizeof(t));
206	ct = nf_conntrack_alloc(par->net, info->zone, &t, &t, GFP_KERNEL);
207	ret = PTR_ERR(ct);
208	if (IS_ERR(ct))
209		goto err2;
210
211	ret = 0;
212	if ((info->ct_events || info->exp_events) &&
213	    !nf_ct_ecache_ext_add(ct, info->ct_events, info->exp_events,
214				  GFP_KERNEL)) {
215		ret = -EINVAL;
216		goto err3;
217	}
218
219	if (info->helper[0]) {
220		ret = xt_ct_set_helper(ct, info->helper, par);
221		if (ret < 0)
222			goto err3;
223	}
224
225	if (info->timeout[0]) {
226		ret = xt_ct_set_timeout(ct, par, info->timeout);
227		if (ret < 0)
228			goto err3;
229	}
230
231	nf_conntrack_tmpl_insert(par->net, ct);
232out:
233	info->ct = ct;
234	return 0;
235
236err3:
237	nf_conntrack_free(ct);
238err2:
239	nf_ct_l3proto_module_put(par->family);
240err1:
241	return ret;
242}
243
244static int xt_ct_tg_check_v0(const struct xt_tgchk_param *par)
245{
246	struct xt_ct_target_info *info = par->targinfo;
247	struct xt_ct_target_info_v1 info_v1 = {
248		.flags 		= info->flags,
249		.zone		= info->zone,
250		.ct_events	= info->ct_events,
251		.exp_events	= info->exp_events,
252	};
253	int ret;
254
255	if (info->flags & ~XT_CT_NOTRACK)
256		return -EINVAL;
257
258	memcpy(info_v1.helper, info->helper, sizeof(info->helper));
259
260	ret = xt_ct_tg_check(par, &info_v1);
261	if (ret < 0)
262		return ret;
263
264	info->ct = info_v1.ct;
265
266	return ret;
267}
268
269static int xt_ct_tg_check_v1(const struct xt_tgchk_param *par)
270{
271	struct xt_ct_target_info_v1 *info = par->targinfo;
272
273	if (info->flags & ~XT_CT_NOTRACK)
274		return -EINVAL;
275
276	return xt_ct_tg_check(par, par->targinfo);
277}
278
279static int xt_ct_tg_check_v2(const struct xt_tgchk_param *par)
280{
281	struct xt_ct_target_info_v1 *info = par->targinfo;
282
283	if (info->flags & ~XT_CT_MASK)
284		return -EINVAL;
285
286	return xt_ct_tg_check(par, par->targinfo);
287}
288
289static void xt_ct_destroy_timeout(struct nf_conn *ct)
290{
291#ifdef CONFIG_NF_CONNTRACK_TIMEOUT
292	struct nf_conn_timeout *timeout_ext;
293	typeof(nf_ct_timeout_put_hook) timeout_put;
294
295	rcu_read_lock();
296	timeout_put = rcu_dereference(nf_ct_timeout_put_hook);
297
298	if (timeout_put) {
299		timeout_ext = nf_ct_timeout_find(ct);
300		if (timeout_ext)
301			timeout_put(timeout_ext->timeout);
302	}
303	rcu_read_unlock();
304#endif
305}
306
307static void xt_ct_tg_destroy(const struct xt_tgdtor_param *par,
308			     struct xt_ct_target_info_v1 *info)
309{
310	struct nf_conn *ct = info->ct;
311	struct nf_conn_help *help;
312
313	if (ct && !nf_ct_is_untracked(ct)) {
314		help = nfct_help(ct);
315		if (help)
316			module_put(help->helper->me);
317
318		nf_ct_l3proto_module_put(par->family);
319
320		xt_ct_destroy_timeout(ct);
321		nf_ct_put(info->ct);
322	}
323}
324
325static void xt_ct_tg_destroy_v0(const struct xt_tgdtor_param *par)
326{
327	struct xt_ct_target_info *info = par->targinfo;
328	struct xt_ct_target_info_v1 info_v1 = {
329		.flags 		= info->flags,
330		.zone		= info->zone,
331		.ct_events	= info->ct_events,
332		.exp_events	= info->exp_events,
333		.ct		= info->ct,
334	};
335	memcpy(info_v1.helper, info->helper, sizeof(info->helper));
336
337	xt_ct_tg_destroy(par, &info_v1);
338}
339
340static void xt_ct_tg_destroy_v1(const struct xt_tgdtor_param *par)
341{
342	xt_ct_tg_destroy(par, par->targinfo);
343}
344
345static struct xt_target xt_ct_tg_reg[] __read_mostly = {
346	{
347		.name		= "CT",
348		.family		= NFPROTO_UNSPEC,
349		.targetsize	= sizeof(struct xt_ct_target_info),
350		.checkentry	= xt_ct_tg_check_v0,
351		.destroy	= xt_ct_tg_destroy_v0,
352		.target		= xt_ct_target_v0,
353		.table		= "raw",
354		.me		= THIS_MODULE,
355	},
356	{
357		.name		= "CT",
358		.family		= NFPROTO_UNSPEC,
359		.revision	= 1,
360		.targetsize	= sizeof(struct xt_ct_target_info_v1),
361		.checkentry	= xt_ct_tg_check_v1,
362		.destroy	= xt_ct_tg_destroy_v1,
363		.target		= xt_ct_target_v1,
364		.table		= "raw",
365		.me		= THIS_MODULE,
366	},
367	{
368		.name		= "CT",
369		.family		= NFPROTO_UNSPEC,
370		.revision	= 2,
371		.targetsize	= sizeof(struct xt_ct_target_info_v1),
372		.checkentry	= xt_ct_tg_check_v2,
373		.destroy	= xt_ct_tg_destroy_v1,
374		.target		= xt_ct_target_v1,
375		.table		= "raw",
376		.me		= THIS_MODULE,
377	},
378};
379
380static unsigned int
381notrack_tg(struct sk_buff *skb, const struct xt_action_param *par)
382{
383	/* Previously seen (loopback)? Ignore. */
384	if (skb->nfct != NULL)
385		return XT_CONTINUE;
386
387	skb->nfct = &nf_ct_untracked_get()->ct_general;
388	skb->nfctinfo = IP_CT_NEW;
389	nf_conntrack_get(skb->nfct);
390
391	return XT_CONTINUE;
392}
393
394static int notrack_chk(const struct xt_tgchk_param *par)
395{
396	if (!par->net->xt.notrack_deprecated_warning) {
397		pr_info("netfilter: NOTRACK target is deprecated, "
398			"use CT instead or upgrade iptables\n");
399		par->net->xt.notrack_deprecated_warning = true;
400	}
401	return 0;
402}
403
404static struct xt_target notrack_tg_reg __read_mostly = {
405	.name		= "NOTRACK",
406	.revision	= 0,
407	.family		= NFPROTO_UNSPEC,
408	.checkentry	= notrack_chk,
409	.target		= notrack_tg,
410	.table		= "raw",
411	.me		= THIS_MODULE,
412};
413
414static int __init xt_ct_tg_init(void)
415{
416	int ret;
417
418	ret = xt_register_target(&notrack_tg_reg);
419	if (ret < 0)
420		return ret;
421
422	ret = xt_register_targets(xt_ct_tg_reg, ARRAY_SIZE(xt_ct_tg_reg));
423	if (ret < 0) {
424		xt_unregister_target(&notrack_tg_reg);
425		return ret;
426	}
427	return 0;
428}
429
430static void __exit xt_ct_tg_exit(void)
431{
432	xt_unregister_targets(xt_ct_tg_reg, ARRAY_SIZE(xt_ct_tg_reg));
433	xt_unregister_target(&notrack_tg_reg);
434}
435
436module_init(xt_ct_tg_init);
437module_exit(xt_ct_tg_exit);
438
439MODULE_LICENSE("GPL");
440MODULE_DESCRIPTION("Xtables: connection tracking target");
441MODULE_ALIAS("ipt_CT");
442MODULE_ALIAS("ip6t_CT");
443MODULE_ALIAS("ipt_NOTRACK");
444MODULE_ALIAS("ip6t_NOTRACK");
445