xref: /openbmc/linux/net/netfilter/xt_CT.c (revision cc41c84b)
184f3bb9aSPatrick McHardy /*
284f3bb9aSPatrick McHardy  * Copyright (c) 2010 Patrick McHardy <kaber@trash.net>
384f3bb9aSPatrick McHardy  *
484f3bb9aSPatrick McHardy  * This program is free software; you can redistribute it and/or modify
584f3bb9aSPatrick McHardy  * it under the terms of the GNU General Public License version 2 as
684f3bb9aSPatrick McHardy  * published by the Free Software Foundation.
784f3bb9aSPatrick McHardy  */
8a7fed762SJan Engelhardt #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
984f3bb9aSPatrick McHardy #include <linux/module.h>
105a0e3ad6STejun Heo #include <linux/gfp.h>
1184f3bb9aSPatrick McHardy #include <linux/skbuff.h>
1284f3bb9aSPatrick McHardy #include <linux/netfilter_ipv4/ip_tables.h>
1384f3bb9aSPatrick McHardy #include <linux/netfilter_ipv6/ip6_tables.h>
1484f3bb9aSPatrick McHardy #include <linux/netfilter/x_tables.h>
1584f3bb9aSPatrick McHardy #include <linux/netfilter/xt_CT.h>
1684f3bb9aSPatrick McHardy #include <net/netfilter/nf_conntrack.h>
17eeb4cb95SPablo Neira Ayuso #include <net/netfilter/nf_conntrack_l4proto.h>
1884f3bb9aSPatrick McHardy #include <net/netfilter/nf_conntrack_helper.h>
1984f3bb9aSPatrick McHardy #include <net/netfilter/nf_conntrack_ecache.h>
2024de58f4SPablo Neira Ayuso #include <net/netfilter/nf_conntrack_timeout.h>
215d0aa2ccSPatrick McHardy #include <net/netfilter/nf_conntrack_zones.h>
2284f3bb9aSPatrick McHardy 
23d52ed437SPablo Neira Ayuso static inline int xt_ct_target(struct sk_buff *skb, struct nf_conn *ct)
2484f3bb9aSPatrick McHardy {
2584f3bb9aSPatrick McHardy 	/* Previously seen (loopback)? Ignore. */
26a9e419dcSFlorian Westphal 	if (skb->_nfct != 0)
2784f3bb9aSPatrick McHardy 		return XT_CONTINUE;
2884f3bb9aSPatrick McHardy 
29cc41c84bSFlorian Westphal 	if (ct) {
3084f3bb9aSPatrick McHardy 		atomic_inc(&ct->ct_general.use);
31c74454faSFlorian Westphal 		nf_ct_set(skb, ct, IP_CT_NEW);
32cc41c84bSFlorian Westphal 	} else {
33cc41c84bSFlorian Westphal 		nf_ct_set(skb, ct, IP_CT_UNTRACKED);
34cc41c84bSFlorian Westphal 	}
3584f3bb9aSPatrick McHardy 
3684f3bb9aSPatrick McHardy 	return XT_CONTINUE;
3784f3bb9aSPatrick McHardy }
3884f3bb9aSPatrick McHardy 
39d52ed437SPablo Neira Ayuso static unsigned int xt_ct_target_v0(struct sk_buff *skb,
40d52ed437SPablo Neira Ayuso 				    const struct xt_action_param *par)
41d52ed437SPablo Neira Ayuso {
42d52ed437SPablo Neira Ayuso 	const struct xt_ct_target_info *info = par->targinfo;
43d52ed437SPablo Neira Ayuso 	struct nf_conn *ct = info->ct;
44d52ed437SPablo Neira Ayuso 
45d52ed437SPablo Neira Ayuso 	return xt_ct_target(skb, ct);
46d52ed437SPablo Neira Ayuso }
47d52ed437SPablo Neira Ayuso 
4824de58f4SPablo Neira Ayuso static unsigned int xt_ct_target_v1(struct sk_buff *skb,
4924de58f4SPablo Neira Ayuso 				    const struct xt_action_param *par)
5024de58f4SPablo Neira Ayuso {
5124de58f4SPablo Neira Ayuso 	const struct xt_ct_target_info_v1 *info = par->targinfo;
5224de58f4SPablo Neira Ayuso 	struct nf_conn *ct = info->ct;
5324de58f4SPablo Neira Ayuso 
54d52ed437SPablo Neira Ayuso 	return xt_ct_target(skb, ct);
5524de58f4SPablo Neira Ayuso }
5624de58f4SPablo Neira Ayuso 
5784f3bb9aSPatrick McHardy static u8 xt_ct_find_proto(const struct xt_tgchk_param *par)
5884f3bb9aSPatrick McHardy {
59076f7839SJan Engelhardt 	if (par->family == NFPROTO_IPV4) {
6084f3bb9aSPatrick McHardy 		const struct ipt_entry *e = par->entryinfo;
6184f3bb9aSPatrick McHardy 
6284f3bb9aSPatrick McHardy 		if (e->ip.invflags & IPT_INV_PROTO)
6384f3bb9aSPatrick McHardy 			return 0;
6484f3bb9aSPatrick McHardy 		return e->ip.proto;
65076f7839SJan Engelhardt 	} else if (par->family == NFPROTO_IPV6) {
6684f3bb9aSPatrick McHardy 		const struct ip6t_entry *e = par->entryinfo;
6784f3bb9aSPatrick McHardy 
6884f3bb9aSPatrick McHardy 		if (e->ipv6.invflags & IP6T_INV_PROTO)
6984f3bb9aSPatrick McHardy 			return 0;
7084f3bb9aSPatrick McHardy 		return e->ipv6.proto;
7184f3bb9aSPatrick McHardy 	} else
7284f3bb9aSPatrick McHardy 		return 0;
7384f3bb9aSPatrick McHardy }
7484f3bb9aSPatrick McHardy 
75236df005SPablo Neira Ayuso static int
76236df005SPablo Neira Ayuso xt_ct_set_helper(struct nf_conn *ct, const char *helper_name,
77236df005SPablo Neira Ayuso 		 const struct xt_tgchk_param *par)
78236df005SPablo Neira Ayuso {
79236df005SPablo Neira Ayuso 	struct nf_conntrack_helper *helper;
80236df005SPablo Neira Ayuso 	struct nf_conn_help *help;
81236df005SPablo Neira Ayuso 	u8 proto;
82236df005SPablo Neira Ayuso 
83236df005SPablo Neira Ayuso 	proto = xt_ct_find_proto(par);
84236df005SPablo Neira Ayuso 	if (!proto) {
85236df005SPablo Neira Ayuso 		pr_info("You must specify a L4 protocol, and not use "
86236df005SPablo Neira Ayuso 			"inversions on it.\n");
87236df005SPablo Neira Ayuso 		return -ENOENT;
88236df005SPablo Neira Ayuso 	}
89236df005SPablo Neira Ayuso 
90236df005SPablo Neira Ayuso 	helper = nf_conntrack_helper_try_module_get(helper_name, par->family,
91236df005SPablo Neira Ayuso 						    proto);
92236df005SPablo Neira Ayuso 	if (helper == NULL) {
93236df005SPablo Neira Ayuso 		pr_info("No such helper \"%s\"\n", helper_name);
94236df005SPablo Neira Ayuso 		return -ENOENT;
95236df005SPablo Neira Ayuso 	}
96236df005SPablo Neira Ayuso 
97236df005SPablo Neira Ayuso 	help = nf_ct_helper_ext_add(ct, helper, GFP_KERNEL);
98236df005SPablo Neira Ayuso 	if (help == NULL) {
99236df005SPablo Neira Ayuso 		module_put(helper->me);
100236df005SPablo Neira Ayuso 		return -ENOMEM;
101236df005SPablo Neira Ayuso 	}
102236df005SPablo Neira Ayuso 
103236df005SPablo Neira Ayuso 	help->helper = helper;
104236df005SPablo Neira Ayuso 	return 0;
105236df005SPablo Neira Ayuso }
106236df005SPablo Neira Ayuso 
107ee14186fSPablo Neira Ayuso #ifdef CONFIG_NF_CONNTRACK_TIMEOUT
108ee14186fSPablo Neira Ayuso static void __xt_ct_tg_timeout_put(struct ctnl_timeout *timeout)
109ee14186fSPablo Neira Ayuso {
110ee14186fSPablo Neira Ayuso 	typeof(nf_ct_timeout_put_hook) timeout_put;
111ee14186fSPablo Neira Ayuso 
112ee14186fSPablo Neira Ayuso 	timeout_put = rcu_dereference(nf_ct_timeout_put_hook);
113ee14186fSPablo Neira Ayuso 	if (timeout_put)
114ee14186fSPablo Neira Ayuso 		timeout_put(timeout);
115ee14186fSPablo Neira Ayuso }
116ee14186fSPablo Neira Ayuso #endif
117ee14186fSPablo Neira Ayuso 
118236df005SPablo Neira Ayuso static int
119236df005SPablo Neira Ayuso xt_ct_set_timeout(struct nf_conn *ct, const struct xt_tgchk_param *par,
120236df005SPablo Neira Ayuso 		  const char *timeout_name)
121236df005SPablo Neira Ayuso {
122236df005SPablo Neira Ayuso #ifdef CONFIG_NF_CONNTRACK_TIMEOUT
123236df005SPablo Neira Ayuso 	typeof(nf_ct_timeout_find_get_hook) timeout_find_get;
124236df005SPablo Neira Ayuso 	struct ctnl_timeout *timeout;
125236df005SPablo Neira Ayuso 	struct nf_conn_timeout *timeout_ext;
126236df005SPablo Neira Ayuso 	struct nf_conntrack_l4proto *l4proto;
127236df005SPablo Neira Ayuso 	int ret = 0;
1280153d5a8SPablo Neira Ayuso 	u8 proto;
129236df005SPablo Neira Ayuso 
130236df005SPablo Neira Ayuso 	rcu_read_lock();
131236df005SPablo Neira Ayuso 	timeout_find_get = rcu_dereference(nf_ct_timeout_find_get_hook);
132236df005SPablo Neira Ayuso 	if (timeout_find_get == NULL) {
133236df005SPablo Neira Ayuso 		ret = -ENOENT;
134236df005SPablo Neira Ayuso 		pr_info("Timeout policy base is empty\n");
135236df005SPablo Neira Ayuso 		goto out;
136236df005SPablo Neira Ayuso 	}
137236df005SPablo Neira Ayuso 
1380153d5a8SPablo Neira Ayuso 	proto = xt_ct_find_proto(par);
1390153d5a8SPablo Neira Ayuso 	if (!proto) {
140236df005SPablo Neira Ayuso 		ret = -EINVAL;
1410153d5a8SPablo Neira Ayuso 		pr_info("You must specify a L4 protocol, and not use "
1420153d5a8SPablo Neira Ayuso 			"inversions on it.\n");
143236df005SPablo Neira Ayuso 		goto out;
144236df005SPablo Neira Ayuso 	}
145236df005SPablo Neira Ayuso 
14619576c94SPablo Neira 	timeout = timeout_find_get(par->net, timeout_name);
147236df005SPablo Neira Ayuso 	if (timeout == NULL) {
148236df005SPablo Neira Ayuso 		ret = -ENOENT;
149236df005SPablo Neira Ayuso 		pr_info("No such timeout policy \"%s\"\n", timeout_name);
150236df005SPablo Neira Ayuso 		goto out;
151236df005SPablo Neira Ayuso 	}
152236df005SPablo Neira Ayuso 
153236df005SPablo Neira Ayuso 	if (timeout->l3num != par->family) {
154236df005SPablo Neira Ayuso 		ret = -EINVAL;
155236df005SPablo Neira Ayuso 		pr_info("Timeout policy `%s' can only be used by L3 protocol "
156236df005SPablo Neira Ayuso 			"number %d\n", timeout_name, timeout->l3num);
157236df005SPablo Neira Ayuso 		goto err_put_timeout;
158236df005SPablo Neira Ayuso 	}
159236df005SPablo Neira Ayuso 	/* Make sure the timeout policy matches any existing protocol tracker,
160236df005SPablo Neira Ayuso 	 * otherwise default to generic.
161236df005SPablo Neira Ayuso 	 */
1620153d5a8SPablo Neira Ayuso 	l4proto = __nf_ct_l4proto_find(par->family, proto);
163236df005SPablo Neira Ayuso 	if (timeout->l4proto->l4proto != l4proto->l4proto) {
164236df005SPablo Neira Ayuso 		ret = -EINVAL;
165236df005SPablo Neira Ayuso 		pr_info("Timeout policy `%s' can only be used by L4 protocol "
166236df005SPablo Neira Ayuso 			"number %d\n",
167236df005SPablo Neira Ayuso 			timeout_name, timeout->l4proto->l4proto);
168236df005SPablo Neira Ayuso 		goto err_put_timeout;
169236df005SPablo Neira Ayuso 	}
170236df005SPablo Neira Ayuso 	timeout_ext = nf_ct_timeout_ext_add(ct, timeout, GFP_ATOMIC);
171236df005SPablo Neira Ayuso 	if (timeout_ext == NULL)
172236df005SPablo Neira Ayuso 		ret = -ENOMEM;
173236df005SPablo Neira Ayuso 
174403d89adSPablo Neira Ayuso 	rcu_read_unlock();
175403d89adSPablo Neira Ayuso 	return ret;
176403d89adSPablo Neira Ayuso 
177236df005SPablo Neira Ayuso err_put_timeout:
178236df005SPablo Neira Ayuso 	__xt_ct_tg_timeout_put(timeout);
179236df005SPablo Neira Ayuso out:
180236df005SPablo Neira Ayuso 	rcu_read_unlock();
181236df005SPablo Neira Ayuso 	return ret;
182236df005SPablo Neira Ayuso #else
183236df005SPablo Neira Ayuso 	return -EOPNOTSUPP;
184236df005SPablo Neira Ayuso #endif
185236df005SPablo Neira Ayuso }
186236df005SPablo Neira Ayuso 
187deedb590SDaniel Borkmann static u16 xt_ct_flags_to_dir(const struct xt_ct_target_info_v1 *info)
188deedb590SDaniel Borkmann {
189deedb590SDaniel Borkmann 	switch (info->flags & (XT_CT_ZONE_DIR_ORIG |
190deedb590SDaniel Borkmann 			       XT_CT_ZONE_DIR_REPL)) {
191deedb590SDaniel Borkmann 	case XT_CT_ZONE_DIR_ORIG:
192deedb590SDaniel Borkmann 		return NF_CT_ZONE_DIR_ORIG;
193deedb590SDaniel Borkmann 	case XT_CT_ZONE_DIR_REPL:
194deedb590SDaniel Borkmann 		return NF_CT_ZONE_DIR_REPL;
195deedb590SDaniel Borkmann 	default:
196deedb590SDaniel Borkmann 		return NF_CT_DEFAULT_ZONE_DIR;
197deedb590SDaniel Borkmann 	}
198deedb590SDaniel Borkmann }
199deedb590SDaniel Borkmann 
200d52ed437SPablo Neira Ayuso static int xt_ct_tg_check(const struct xt_tgchk_param *par,
201d52ed437SPablo Neira Ayuso 			  struct xt_ct_target_info_v1 *info)
20224de58f4SPablo Neira Ayuso {
203308ac914SDaniel Borkmann 	struct nf_conntrack_zone zone;
20424de58f4SPablo Neira Ayuso 	struct nf_conn *ct;
2054610476dSPablo Neira Ayuso 	int ret = -EOPNOTSUPP;
206236df005SPablo Neira Ayuso 
20724de58f4SPablo Neira Ayuso 	if (info->flags & XT_CT_NOTRACK) {
20827e7190eSEric Dumazet 		ct = NULL;
20924de58f4SPablo Neira Ayuso 		goto out;
21024de58f4SPablo Neira Ayuso 	}
21124de58f4SPablo Neira Ayuso 
21224de58f4SPablo Neira Ayuso #ifndef CONFIG_NF_CONNTRACK_ZONES
213deedb590SDaniel Borkmann 	if (info->zone || info->flags & (XT_CT_ZONE_DIR_ORIG |
2145e8018fcSDaniel Borkmann 					 XT_CT_ZONE_DIR_REPL |
2155e8018fcSDaniel Borkmann 					 XT_CT_ZONE_MARK))
21624de58f4SPablo Neira Ayuso 		goto err1;
21724de58f4SPablo Neira Ayuso #endif
21824de58f4SPablo Neira Ayuso 
219ecb2421bSFlorian Westphal 	ret = nf_ct_netns_get(par->net, par->family);
22024de58f4SPablo Neira Ayuso 	if (ret < 0)
22124de58f4SPablo Neira Ayuso 		goto err1;
22224de58f4SPablo Neira Ayuso 
223308ac914SDaniel Borkmann 	memset(&zone, 0, sizeof(zone));
224308ac914SDaniel Borkmann 	zone.id = info->zone;
225deedb590SDaniel Borkmann 	zone.dir = xt_ct_flags_to_dir(info);
2265e8018fcSDaniel Borkmann 	if (info->flags & XT_CT_ZONE_MARK)
2275e8018fcSDaniel Borkmann 		zone.flags |= NF_CT_FLAG_MARK;
228308ac914SDaniel Borkmann 
229308ac914SDaniel Borkmann 	ct = nf_ct_tmpl_alloc(par->net, &zone, GFP_KERNEL);
2301a727c63SDan Carpenter 	if (!ct) {
2311a727c63SDan Carpenter 		ret = -ENOMEM;
23224de58f4SPablo Neira Ayuso 		goto err2;
2331a727c63SDan Carpenter 	}
23424de58f4SPablo Neira Ayuso 
23524de58f4SPablo Neira Ayuso 	ret = 0;
23624de58f4SPablo Neira Ayuso 	if ((info->ct_events || info->exp_events) &&
23724de58f4SPablo Neira Ayuso 	    !nf_ct_ecache_ext_add(ct, info->ct_events, info->exp_events,
23814abfa16SEric Leblond 				  GFP_KERNEL)) {
23914abfa16SEric Leblond 		ret = -EINVAL;
24024de58f4SPablo Neira Ayuso 		goto err3;
24114abfa16SEric Leblond 	}
24224de58f4SPablo Neira Ayuso 
24324de58f4SPablo Neira Ayuso 	if (info->helper[0]) {
244236df005SPablo Neira Ayuso 		ret = xt_ct_set_helper(ct, info->helper, par);
245236df005SPablo Neira Ayuso 		if (ret < 0)
24624de58f4SPablo Neira Ayuso 			goto err3;
24724de58f4SPablo Neira Ayuso 	}
24824de58f4SPablo Neira Ayuso 
2496cf51852SPablo Neira Ayuso 	if (info->timeout[0]) {
250236df005SPablo Neira Ayuso 		ret = xt_ct_set_timeout(ct, par, info->timeout);
251236df005SPablo Neira Ayuso 		if (ret < 0)
252236df005SPablo Neira Ayuso 			goto err3;
25324de58f4SPablo Neira Ayuso 	}
2540838aa7fSPablo Neira Ayuso 	__set_bit(IPS_CONFIRMED_BIT, &ct->status);
2550838aa7fSPablo Neira Ayuso 	nf_conntrack_get(&ct->ct_general);
25624de58f4SPablo Neira Ayuso out:
25724de58f4SPablo Neira Ayuso 	info->ct = ct;
25824de58f4SPablo Neira Ayuso 	return 0;
25924de58f4SPablo Neira Ayuso 
26024de58f4SPablo Neira Ayuso err3:
2619cf94eabSDaniel Borkmann 	nf_ct_tmpl_free(ct);
26224de58f4SPablo Neira Ayuso err2:
263ecb2421bSFlorian Westphal 	nf_ct_netns_put(par->net, par->family);
26424de58f4SPablo Neira Ayuso err1:
26524de58f4SPablo Neira Ayuso 	return ret;
26624de58f4SPablo Neira Ayuso }
26724de58f4SPablo Neira Ayuso 
268d52ed437SPablo Neira Ayuso static int xt_ct_tg_check_v0(const struct xt_tgchk_param *par)
26984f3bb9aSPatrick McHardy {
27084f3bb9aSPatrick McHardy 	struct xt_ct_target_info *info = par->targinfo;
271d52ed437SPablo Neira Ayuso 	struct xt_ct_target_info_v1 info_v1 = {
272d52ed437SPablo Neira Ayuso 		.flags 		= info->flags,
273d52ed437SPablo Neira Ayuso 		.zone		= info->zone,
274d52ed437SPablo Neira Ayuso 		.ct_events	= info->ct_events,
275d52ed437SPablo Neira Ayuso 		.exp_events	= info->exp_events,
276d52ed437SPablo Neira Ayuso 	};
277d52ed437SPablo Neira Ayuso 	int ret;
27884f3bb9aSPatrick McHardy 
2795474f57fSPablo Neira Ayuso 	if (info->flags & ~XT_CT_NOTRACK)
2805474f57fSPablo Neira Ayuso 		return -EINVAL;
2815474f57fSPablo Neira Ayuso 
282d52ed437SPablo Neira Ayuso 	memcpy(info_v1.helper, info->helper, sizeof(info->helper));
28384f3bb9aSPatrick McHardy 
284d52ed437SPablo Neira Ayuso 	ret = xt_ct_tg_check(par, &info_v1);
285d52ed437SPablo Neira Ayuso 	if (ret < 0)
286d52ed437SPablo Neira Ayuso 		return ret;
287d52ed437SPablo Neira Ayuso 
288d52ed437SPablo Neira Ayuso 	info->ct = info_v1.ct;
289d52ed437SPablo Neira Ayuso 
290d52ed437SPablo Neira Ayuso 	return ret;
29184f3bb9aSPatrick McHardy }
292d52ed437SPablo Neira Ayuso 
293d52ed437SPablo Neira Ayuso static int xt_ct_tg_check_v1(const struct xt_tgchk_param *par)
294d52ed437SPablo Neira Ayuso {
2955474f57fSPablo Neira Ayuso 	struct xt_ct_target_info_v1 *info = par->targinfo;
2965474f57fSPablo Neira Ayuso 
2975474f57fSPablo Neira Ayuso 	if (info->flags & ~XT_CT_NOTRACK)
2985474f57fSPablo Neira Ayuso 		return -EINVAL;
2995474f57fSPablo Neira Ayuso 
3005474f57fSPablo Neira Ayuso 	return xt_ct_tg_check(par, par->targinfo);
3015474f57fSPablo Neira Ayuso }
3025474f57fSPablo Neira Ayuso 
3035474f57fSPablo Neira Ayuso static int xt_ct_tg_check_v2(const struct xt_tgchk_param *par)
3045474f57fSPablo Neira Ayuso {
3055474f57fSPablo Neira Ayuso 	struct xt_ct_target_info_v1 *info = par->targinfo;
3065474f57fSPablo Neira Ayuso 
3075474f57fSPablo Neira Ayuso 	if (info->flags & ~XT_CT_MASK)
3085474f57fSPablo Neira Ayuso 		return -EINVAL;
3095474f57fSPablo Neira Ayuso 
310d52ed437SPablo Neira Ayuso 	return xt_ct_tg_check(par, par->targinfo);
31184f3bb9aSPatrick McHardy }
31284f3bb9aSPatrick McHardy 
313236df005SPablo Neira Ayuso static void xt_ct_destroy_timeout(struct nf_conn *ct)
31424de58f4SPablo Neira Ayuso {
31524de58f4SPablo Neira Ayuso #ifdef CONFIG_NF_CONNTRACK_TIMEOUT
31624de58f4SPablo Neira Ayuso 	struct nf_conn_timeout *timeout_ext;
31724de58f4SPablo Neira Ayuso 	typeof(nf_ct_timeout_put_hook) timeout_put;
31824de58f4SPablo Neira Ayuso 
3191ac0bf99SPablo Neira Ayuso 	rcu_read_lock();
32024de58f4SPablo Neira Ayuso 	timeout_put = rcu_dereference(nf_ct_timeout_put_hook);
32124de58f4SPablo Neira Ayuso 
32224de58f4SPablo Neira Ayuso 	if (timeout_put) {
32324de58f4SPablo Neira Ayuso 		timeout_ext = nf_ct_timeout_find(ct);
324ae2d708eSPablo Neira Ayuso 		if (timeout_ext) {
32524de58f4SPablo Neira Ayuso 			timeout_put(timeout_ext->timeout);
326ae2d708eSPablo Neira Ayuso 			RCU_INIT_POINTER(timeout_ext->timeout, NULL);
327ae2d708eSPablo Neira Ayuso 		}
32824de58f4SPablo Neira Ayuso 	}
3291ac0bf99SPablo Neira Ayuso 	rcu_read_unlock();
33024de58f4SPablo Neira Ayuso #endif
33124de58f4SPablo Neira Ayuso }
332236df005SPablo Neira Ayuso 
333d52ed437SPablo Neira Ayuso static void xt_ct_tg_destroy(const struct xt_tgdtor_param *par,
334d52ed437SPablo Neira Ayuso 			     struct xt_ct_target_info_v1 *info)
335236df005SPablo Neira Ayuso {
336236df005SPablo Neira Ayuso 	struct nf_conn *ct = info->ct;
337236df005SPablo Neira Ayuso 	struct nf_conn_help *help;
338236df005SPablo Neira Ayuso 
339cc41c84bSFlorian Westphal 	if (ct) {
340236df005SPablo Neira Ayuso 		help = nfct_help(ct);
341236df005SPablo Neira Ayuso 		if (help)
342236df005SPablo Neira Ayuso 			module_put(help->helper->me);
343236df005SPablo Neira Ayuso 
344ecb2421bSFlorian Westphal 		nf_ct_netns_put(par->net, par->family);
345236df005SPablo Neira Ayuso 
346236df005SPablo Neira Ayuso 		xt_ct_destroy_timeout(ct);
34724de58f4SPablo Neira Ayuso 		nf_ct_put(info->ct);
34824de58f4SPablo Neira Ayuso 	}
34927e7190eSEric Dumazet }
35024de58f4SPablo Neira Ayuso 
351d52ed437SPablo Neira Ayuso static void xt_ct_tg_destroy_v0(const struct xt_tgdtor_param *par)
352d52ed437SPablo Neira Ayuso {
353d52ed437SPablo Neira Ayuso 	struct xt_ct_target_info *info = par->targinfo;
354d52ed437SPablo Neira Ayuso 	struct xt_ct_target_info_v1 info_v1 = {
355d52ed437SPablo Neira Ayuso 		.flags 		= info->flags,
356d52ed437SPablo Neira Ayuso 		.zone		= info->zone,
357d52ed437SPablo Neira Ayuso 		.ct_events	= info->ct_events,
358d52ed437SPablo Neira Ayuso 		.exp_events	= info->exp_events,
359d52ed437SPablo Neira Ayuso 		.ct		= info->ct,
360d52ed437SPablo Neira Ayuso 	};
361d52ed437SPablo Neira Ayuso 	memcpy(info_v1.helper, info->helper, sizeof(info->helper));
362d52ed437SPablo Neira Ayuso 
363d52ed437SPablo Neira Ayuso 	xt_ct_tg_destroy(par, &info_v1);
364d52ed437SPablo Neira Ayuso }
365d52ed437SPablo Neira Ayuso 
366d52ed437SPablo Neira Ayuso static void xt_ct_tg_destroy_v1(const struct xt_tgdtor_param *par)
367d52ed437SPablo Neira Ayuso {
368d52ed437SPablo Neira Ayuso 	xt_ct_tg_destroy(par, par->targinfo);
369d52ed437SPablo Neira Ayuso }
370d52ed437SPablo Neira Ayuso 
37124de58f4SPablo Neira Ayuso static struct xt_target xt_ct_tg_reg[] __read_mostly = {
37224de58f4SPablo Neira Ayuso 	{
37384f3bb9aSPatrick McHardy 		.name		= "CT",
37484f3bb9aSPatrick McHardy 		.family		= NFPROTO_UNSPEC,
3757d5f7ed8SJan Engelhardt 		.targetsize	= sizeof(struct xt_ct_target_info),
376ec231890SWillem de Bruijn 		.usersize	= offsetof(struct xt_ct_target_info, ct),
37724de58f4SPablo Neira Ayuso 		.checkentry	= xt_ct_tg_check_v0,
37824de58f4SPablo Neira Ayuso 		.destroy	= xt_ct_tg_destroy_v0,
37924de58f4SPablo Neira Ayuso 		.target		= xt_ct_target_v0,
38084f3bb9aSPatrick McHardy 		.table		= "raw",
38184f3bb9aSPatrick McHardy 		.me		= THIS_MODULE,
38224de58f4SPablo Neira Ayuso 	},
38324de58f4SPablo Neira Ayuso 	{
38424de58f4SPablo Neira Ayuso 		.name		= "CT",
38524de58f4SPablo Neira Ayuso 		.family		= NFPROTO_UNSPEC,
38624de58f4SPablo Neira Ayuso 		.revision	= 1,
38724de58f4SPablo Neira Ayuso 		.targetsize	= sizeof(struct xt_ct_target_info_v1),
388ec231890SWillem de Bruijn 		.usersize	= offsetof(struct xt_ct_target_info, ct),
38924de58f4SPablo Neira Ayuso 		.checkentry	= xt_ct_tg_check_v1,
39024de58f4SPablo Neira Ayuso 		.destroy	= xt_ct_tg_destroy_v1,
39124de58f4SPablo Neira Ayuso 		.target		= xt_ct_target_v1,
39224de58f4SPablo Neira Ayuso 		.table		= "raw",
39324de58f4SPablo Neira Ayuso 		.me		= THIS_MODULE,
39424de58f4SPablo Neira Ayuso 	},
3955474f57fSPablo Neira Ayuso 	{
3965474f57fSPablo Neira Ayuso 		.name		= "CT",
3975474f57fSPablo Neira Ayuso 		.family		= NFPROTO_UNSPEC,
3985474f57fSPablo Neira Ayuso 		.revision	= 2,
3995474f57fSPablo Neira Ayuso 		.targetsize	= sizeof(struct xt_ct_target_info_v1),
400ec231890SWillem de Bruijn 		.usersize	= offsetof(struct xt_ct_target_info, ct),
4015474f57fSPablo Neira Ayuso 		.checkentry	= xt_ct_tg_check_v2,
4025474f57fSPablo Neira Ayuso 		.destroy	= xt_ct_tg_destroy_v1,
4035474f57fSPablo Neira Ayuso 		.target		= xt_ct_target_v1,
4045474f57fSPablo Neira Ayuso 		.table		= "raw",
4055474f57fSPablo Neira Ayuso 		.me		= THIS_MODULE,
4065474f57fSPablo Neira Ayuso 	},
40784f3bb9aSPatrick McHardy };
40884f3bb9aSPatrick McHardy 
40910db9069SPablo Neira Ayuso static unsigned int
41010db9069SPablo Neira Ayuso notrack_tg(struct sk_buff *skb, const struct xt_action_param *par)
41110db9069SPablo Neira Ayuso {
41210db9069SPablo Neira Ayuso 	/* Previously seen (loopback)? Ignore. */
413a9e419dcSFlorian Westphal 	if (skb->_nfct != 0)
41410db9069SPablo Neira Ayuso 		return XT_CONTINUE;
41510db9069SPablo Neira Ayuso 
416cc41c84bSFlorian Westphal 	nf_ct_set(skb, NULL, IP_CT_UNTRACKED);
41710db9069SPablo Neira Ayuso 
41810db9069SPablo Neira Ayuso 	return XT_CONTINUE;
41910db9069SPablo Neira Ayuso }
42010db9069SPablo Neira Ayuso 
42110db9069SPablo Neira Ayuso static int notrack_chk(const struct xt_tgchk_param *par)
42210db9069SPablo Neira Ayuso {
42310db9069SPablo Neira Ayuso 	if (!par->net->xt.notrack_deprecated_warning) {
42410db9069SPablo Neira Ayuso 		pr_info("netfilter: NOTRACK target is deprecated, "
42510db9069SPablo Neira Ayuso 			"use CT instead or upgrade iptables\n");
42610db9069SPablo Neira Ayuso 		par->net->xt.notrack_deprecated_warning = true;
42710db9069SPablo Neira Ayuso 	}
42810db9069SPablo Neira Ayuso 	return 0;
42910db9069SPablo Neira Ayuso }
43010db9069SPablo Neira Ayuso 
43110db9069SPablo Neira Ayuso static struct xt_target notrack_tg_reg __read_mostly = {
43210db9069SPablo Neira Ayuso 	.name		= "NOTRACK",
43310db9069SPablo Neira Ayuso 	.revision	= 0,
43410db9069SPablo Neira Ayuso 	.family		= NFPROTO_UNSPEC,
43510db9069SPablo Neira Ayuso 	.checkentry	= notrack_chk,
43610db9069SPablo Neira Ayuso 	.target		= notrack_tg,
43710db9069SPablo Neira Ayuso 	.table		= "raw",
43810db9069SPablo Neira Ayuso 	.me		= THIS_MODULE,
43910db9069SPablo Neira Ayuso };
44010db9069SPablo Neira Ayuso 
44184f3bb9aSPatrick McHardy static int __init xt_ct_tg_init(void)
44284f3bb9aSPatrick McHardy {
44310db9069SPablo Neira Ayuso 	int ret;
44410db9069SPablo Neira Ayuso 
44510db9069SPablo Neira Ayuso 	ret = xt_register_target(&notrack_tg_reg);
44610db9069SPablo Neira Ayuso 	if (ret < 0)
44710db9069SPablo Neira Ayuso 		return ret;
44810db9069SPablo Neira Ayuso 
44910db9069SPablo Neira Ayuso 	ret = xt_register_targets(xt_ct_tg_reg, ARRAY_SIZE(xt_ct_tg_reg));
45010db9069SPablo Neira Ayuso 	if (ret < 0) {
45110db9069SPablo Neira Ayuso 		xt_unregister_target(&notrack_tg_reg);
45210db9069SPablo Neira Ayuso 		return ret;
45310db9069SPablo Neira Ayuso 	}
45410db9069SPablo Neira Ayuso 	return 0;
45584f3bb9aSPatrick McHardy }
45684f3bb9aSPatrick McHardy 
45784f3bb9aSPatrick McHardy static void __exit xt_ct_tg_exit(void)
45884f3bb9aSPatrick McHardy {
45924de58f4SPablo Neira Ayuso 	xt_unregister_targets(xt_ct_tg_reg, ARRAY_SIZE(xt_ct_tg_reg));
46010db9069SPablo Neira Ayuso 	xt_unregister_target(&notrack_tg_reg);
46184f3bb9aSPatrick McHardy }
46284f3bb9aSPatrick McHardy 
46384f3bb9aSPatrick McHardy module_init(xt_ct_tg_init);
46484f3bb9aSPatrick McHardy module_exit(xt_ct_tg_exit);
46584f3bb9aSPatrick McHardy 
46684f3bb9aSPatrick McHardy MODULE_LICENSE("GPL");
46784f3bb9aSPatrick McHardy MODULE_DESCRIPTION("Xtables: connection tracking target");
46884f3bb9aSPatrick McHardy MODULE_ALIAS("ipt_CT");
46984f3bb9aSPatrick McHardy MODULE_ALIAS("ip6t_CT");
47010db9069SPablo Neira Ayuso MODULE_ALIAS("ipt_NOTRACK");
47110db9069SPablo Neira Ayuso MODULE_ALIAS("ip6t_NOTRACK");
472