xref: /openbmc/linux/net/netfilter/xt_CT.c (revision eeb4cb95)
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>
20c1ebd7dfSPablo Neira Ayuso #include <net/netfilter/nf_conntrack_l4proto.h>
2124de58f4SPablo Neira Ayuso #include <net/netfilter/nf_conntrack_timeout.h>
225d0aa2ccSPatrick McHardy #include <net/netfilter/nf_conntrack_zones.h>
2384f3bb9aSPatrick McHardy 
2424de58f4SPablo Neira Ayuso static unsigned int xt_ct_target_v0(struct sk_buff *skb,
254b560b44SJan Engelhardt 				    const struct xt_action_param *par)
2684f3bb9aSPatrick McHardy {
2784f3bb9aSPatrick McHardy 	const struct xt_ct_target_info *info = par->targinfo;
2884f3bb9aSPatrick McHardy 	struct nf_conn *ct = info->ct;
2984f3bb9aSPatrick McHardy 
3084f3bb9aSPatrick McHardy 	/* Previously seen (loopback)? Ignore. */
3184f3bb9aSPatrick McHardy 	if (skb->nfct != NULL)
3284f3bb9aSPatrick McHardy 		return XT_CONTINUE;
3384f3bb9aSPatrick McHardy 
3484f3bb9aSPatrick McHardy 	atomic_inc(&ct->ct_general.use);
3584f3bb9aSPatrick McHardy 	skb->nfct = &ct->ct_general;
3684f3bb9aSPatrick McHardy 	skb->nfctinfo = IP_CT_NEW;
3784f3bb9aSPatrick McHardy 
3884f3bb9aSPatrick McHardy 	return XT_CONTINUE;
3984f3bb9aSPatrick McHardy }
4084f3bb9aSPatrick McHardy 
4124de58f4SPablo Neira Ayuso static unsigned int xt_ct_target_v1(struct sk_buff *skb,
4224de58f4SPablo Neira Ayuso 				    const struct xt_action_param *par)
4324de58f4SPablo Neira Ayuso {
4424de58f4SPablo Neira Ayuso 	const struct xt_ct_target_info_v1 *info = par->targinfo;
4524de58f4SPablo Neira Ayuso 	struct nf_conn *ct = info->ct;
4624de58f4SPablo Neira Ayuso 
4724de58f4SPablo Neira Ayuso 	/* Previously seen (loopback)? Ignore. */
4824de58f4SPablo Neira Ayuso 	if (skb->nfct != NULL)
4924de58f4SPablo Neira Ayuso 		return XT_CONTINUE;
5024de58f4SPablo Neira Ayuso 
5124de58f4SPablo Neira Ayuso 	atomic_inc(&ct->ct_general.use);
5224de58f4SPablo Neira Ayuso 	skb->nfct = &ct->ct_general;
5324de58f4SPablo Neira Ayuso 	skb->nfctinfo = IP_CT_NEW;
5424de58f4SPablo Neira Ayuso 
5524de58f4SPablo Neira Ayuso 	return XT_CONTINUE;
5624de58f4SPablo Neira Ayuso }
5724de58f4SPablo Neira Ayuso 
5884f3bb9aSPatrick McHardy static u8 xt_ct_find_proto(const struct xt_tgchk_param *par)
5984f3bb9aSPatrick McHardy {
60076f7839SJan Engelhardt 	if (par->family == NFPROTO_IPV4) {
6184f3bb9aSPatrick McHardy 		const struct ipt_entry *e = par->entryinfo;
6284f3bb9aSPatrick McHardy 
6384f3bb9aSPatrick McHardy 		if (e->ip.invflags & IPT_INV_PROTO)
6484f3bb9aSPatrick McHardy 			return 0;
6584f3bb9aSPatrick McHardy 		return e->ip.proto;
66076f7839SJan Engelhardt 	} else if (par->family == NFPROTO_IPV6) {
6784f3bb9aSPatrick McHardy 		const struct ip6t_entry *e = par->entryinfo;
6884f3bb9aSPatrick McHardy 
6984f3bb9aSPatrick McHardy 		if (e->ipv6.invflags & IP6T_INV_PROTO)
7084f3bb9aSPatrick McHardy 			return 0;
7184f3bb9aSPatrick McHardy 		return e->ipv6.proto;
7284f3bb9aSPatrick McHardy 	} else
7384f3bb9aSPatrick McHardy 		return 0;
7484f3bb9aSPatrick McHardy }
7584f3bb9aSPatrick McHardy 
7624de58f4SPablo Neira Ayuso static int xt_ct_tg_check_v0(const struct xt_tgchk_param *par)
7784f3bb9aSPatrick McHardy {
7884f3bb9aSPatrick McHardy 	struct xt_ct_target_info *info = par->targinfo;
7984f3bb9aSPatrick McHardy 	struct nf_conntrack_tuple t;
8084f3bb9aSPatrick McHardy 	struct nf_conn_help *help;
8184f3bb9aSPatrick McHardy 	struct nf_conn *ct;
824a5a5c73SJan Engelhardt 	int ret = 0;
8384f3bb9aSPatrick McHardy 	u8 proto;
8484f3bb9aSPatrick McHardy 
859bf04646SPablo Neira Ayuso 	if (info->flags & ~XT_CT_NOTRACK)
869bf04646SPablo Neira Ayuso 		return -EINVAL;
8784f3bb9aSPatrick McHardy 
8884f3bb9aSPatrick McHardy 	if (info->flags & XT_CT_NOTRACK) {
895bfddbd4SEric Dumazet 		ct = nf_ct_untracked_get();
9084f3bb9aSPatrick McHardy 		atomic_inc(&ct->ct_general.use);
9184f3bb9aSPatrick McHardy 		goto out;
9284f3bb9aSPatrick McHardy 	}
9384f3bb9aSPatrick McHardy 
945d0aa2ccSPatrick McHardy #ifndef CONFIG_NF_CONNTRACK_ZONES
955d0aa2ccSPatrick McHardy 	if (info->zone)
965d0aa2ccSPatrick McHardy 		goto err1;
975d0aa2ccSPatrick McHardy #endif
985d0aa2ccSPatrick McHardy 
994a5a5c73SJan Engelhardt 	ret = nf_ct_l3proto_try_module_get(par->family);
1004a5a5c73SJan Engelhardt 	if (ret < 0)
10184f3bb9aSPatrick McHardy 		goto err1;
10284f3bb9aSPatrick McHardy 
10384f3bb9aSPatrick McHardy 	memset(&t, 0, sizeof(t));
1045d0aa2ccSPatrick McHardy 	ct = nf_conntrack_alloc(par->net, info->zone, &t, &t, GFP_KERNEL);
1054a5a5c73SJan Engelhardt 	ret = PTR_ERR(ct);
10684f3bb9aSPatrick McHardy 	if (IS_ERR(ct))
10784f3bb9aSPatrick McHardy 		goto err2;
10884f3bb9aSPatrick McHardy 
1094a5a5c73SJan Engelhardt 	ret = 0;
11084f3bb9aSPatrick McHardy 	if ((info->ct_events || info->exp_events) &&
11184f3bb9aSPatrick McHardy 	    !nf_ct_ecache_ext_add(ct, info->ct_events, info->exp_events,
11284f3bb9aSPatrick McHardy 				  GFP_KERNEL))
11384f3bb9aSPatrick McHardy 		goto err3;
11484f3bb9aSPatrick McHardy 
1159bf04646SPablo Neira Ayuso 	if (info->helper[0]) {
1164a5a5c73SJan Engelhardt 		ret = -ENOENT;
11784f3bb9aSPatrick McHardy 		proto = xt_ct_find_proto(par);
118a7fed762SJan Engelhardt 		if (!proto) {
119a7fed762SJan Engelhardt 			pr_info("You must specify a L4 protocol, "
120a7fed762SJan Engelhardt 				"and not use inversions on it.\n");
12184f3bb9aSPatrick McHardy 			goto err3;
122a7fed762SJan Engelhardt 		}
12384f3bb9aSPatrick McHardy 
1244a5a5c73SJan Engelhardt 		ret = -ENOMEM;
12584f3bb9aSPatrick McHardy 		help = nf_ct_helper_ext_add(ct, GFP_KERNEL);
12684f3bb9aSPatrick McHardy 		if (help == NULL)
12784f3bb9aSPatrick McHardy 			goto err3;
12884f3bb9aSPatrick McHardy 
1294a5a5c73SJan Engelhardt 		ret = -ENOENT;
13084f3bb9aSPatrick McHardy 		help->helper = nf_conntrack_helper_try_module_get(info->helper,
13184f3bb9aSPatrick McHardy 								  par->family,
13284f3bb9aSPatrick McHardy 								  proto);
133a7fed762SJan Engelhardt 		if (help->helper == NULL) {
134a7fed762SJan Engelhardt 			pr_info("No such helper \"%s\"\n", info->helper);
13584f3bb9aSPatrick McHardy 			goto err3;
13684f3bb9aSPatrick McHardy 		}
137a7fed762SJan Engelhardt 	}
13884f3bb9aSPatrick McHardy 
13984f3bb9aSPatrick McHardy 	__set_bit(IPS_TEMPLATE_BIT, &ct->status);
14084f3bb9aSPatrick McHardy 	__set_bit(IPS_CONFIRMED_BIT, &ct->status);
14184f3bb9aSPatrick McHardy out:
14284f3bb9aSPatrick McHardy 	info->ct = ct;
143d6b00a53SJan Engelhardt 	return 0;
14484f3bb9aSPatrick McHardy 
14584f3bb9aSPatrick McHardy err3:
14684f3bb9aSPatrick McHardy 	nf_conntrack_free(ct);
14784f3bb9aSPatrick McHardy err2:
14884f3bb9aSPatrick McHardy 	nf_ct_l3proto_module_put(par->family);
14984f3bb9aSPatrick McHardy err1:
1504a5a5c73SJan Engelhardt 	return ret;
15184f3bb9aSPatrick McHardy }
15284f3bb9aSPatrick McHardy 
15324de58f4SPablo Neira Ayuso static int xt_ct_tg_check_v1(const struct xt_tgchk_param *par)
15424de58f4SPablo Neira Ayuso {
15524de58f4SPablo Neira Ayuso 	struct xt_ct_target_info_v1 *info = par->targinfo;
15624de58f4SPablo Neira Ayuso 	struct nf_conntrack_tuple t;
15724de58f4SPablo Neira Ayuso 	struct nf_conn_help *help;
15824de58f4SPablo Neira Ayuso 	struct nf_conn *ct;
15924de58f4SPablo Neira Ayuso 	int ret = 0;
16024de58f4SPablo Neira Ayuso 	u8 proto;
16124de58f4SPablo Neira Ayuso 
16224de58f4SPablo Neira Ayuso 	if (info->flags & ~XT_CT_NOTRACK)
16324de58f4SPablo Neira Ayuso 		return -EINVAL;
16424de58f4SPablo Neira Ayuso 
16524de58f4SPablo Neira Ayuso 	if (info->flags & XT_CT_NOTRACK) {
16624de58f4SPablo Neira Ayuso 		ct = nf_ct_untracked_get();
16724de58f4SPablo Neira Ayuso 		atomic_inc(&ct->ct_general.use);
16824de58f4SPablo Neira Ayuso 		goto out;
16924de58f4SPablo Neira Ayuso 	}
17024de58f4SPablo Neira Ayuso 
17124de58f4SPablo Neira Ayuso #ifndef CONFIG_NF_CONNTRACK_ZONES
17224de58f4SPablo Neira Ayuso 	if (info->zone)
17324de58f4SPablo Neira Ayuso 		goto err1;
17424de58f4SPablo Neira Ayuso #endif
17524de58f4SPablo Neira Ayuso 
17624de58f4SPablo Neira Ayuso 	ret = nf_ct_l3proto_try_module_get(par->family);
17724de58f4SPablo Neira Ayuso 	if (ret < 0)
17824de58f4SPablo Neira Ayuso 		goto err1;
17924de58f4SPablo Neira Ayuso 
18024de58f4SPablo Neira Ayuso 	memset(&t, 0, sizeof(t));
18124de58f4SPablo Neira Ayuso 	ct = nf_conntrack_alloc(par->net, info->zone, &t, &t, GFP_KERNEL);
18224de58f4SPablo Neira Ayuso 	ret = PTR_ERR(ct);
18324de58f4SPablo Neira Ayuso 	if (IS_ERR(ct))
18424de58f4SPablo Neira Ayuso 		goto err2;
18524de58f4SPablo Neira Ayuso 
18624de58f4SPablo Neira Ayuso 	ret = 0;
18724de58f4SPablo Neira Ayuso 	if ((info->ct_events || info->exp_events) &&
18824de58f4SPablo Neira Ayuso 	    !nf_ct_ecache_ext_add(ct, info->ct_events, info->exp_events,
18924de58f4SPablo Neira Ayuso 				  GFP_KERNEL))
19024de58f4SPablo Neira Ayuso 		goto err3;
19124de58f4SPablo Neira Ayuso 
19224de58f4SPablo Neira Ayuso 	if (info->helper[0]) {
19324de58f4SPablo Neira Ayuso 		ret = -ENOENT;
19424de58f4SPablo Neira Ayuso 		proto = xt_ct_find_proto(par);
19524de58f4SPablo Neira Ayuso 		if (!proto) {
19624de58f4SPablo Neira Ayuso 			pr_info("You must specify a L4 protocol, "
19724de58f4SPablo Neira Ayuso 				"and not use inversions on it.\n");
19824de58f4SPablo Neira Ayuso 			goto err3;
19924de58f4SPablo Neira Ayuso 		}
20024de58f4SPablo Neira Ayuso 
20124de58f4SPablo Neira Ayuso 		ret = -ENOMEM;
20224de58f4SPablo Neira Ayuso 		help = nf_ct_helper_ext_add(ct, GFP_KERNEL);
20324de58f4SPablo Neira Ayuso 		if (help == NULL)
20424de58f4SPablo Neira Ayuso 			goto err3;
20524de58f4SPablo Neira Ayuso 
20624de58f4SPablo Neira Ayuso 		ret = -ENOENT;
20724de58f4SPablo Neira Ayuso 		help->helper = nf_conntrack_helper_try_module_get(info->helper,
20824de58f4SPablo Neira Ayuso 								  par->family,
20924de58f4SPablo Neira Ayuso 								  proto);
21024de58f4SPablo Neira Ayuso 		if (help->helper == NULL) {
21124de58f4SPablo Neira Ayuso 			pr_info("No such helper \"%s\"\n", info->helper);
21224de58f4SPablo Neira Ayuso 			goto err3;
21324de58f4SPablo Neira Ayuso 		}
21424de58f4SPablo Neira Ayuso 	}
21524de58f4SPablo Neira Ayuso 
21624de58f4SPablo Neira Ayuso #ifdef CONFIG_NF_CONNTRACK_TIMEOUT
21724de58f4SPablo Neira Ayuso 	if (info->timeout) {
21824de58f4SPablo Neira Ayuso 		typeof(nf_ct_timeout_find_get_hook) timeout_find_get;
21924de58f4SPablo Neira Ayuso 		struct ctnl_timeout *timeout;
22024de58f4SPablo Neira Ayuso 		struct nf_conn_timeout *timeout_ext;
22124de58f4SPablo Neira Ayuso 
2221ac0bf99SPablo Neira Ayuso 		rcu_read_lock();
22324de58f4SPablo Neira Ayuso 		timeout_find_get =
22424de58f4SPablo Neira Ayuso 			rcu_dereference(nf_ct_timeout_find_get_hook);
22524de58f4SPablo Neira Ayuso 
22624de58f4SPablo Neira Ayuso 		if (timeout_find_get) {
22724de58f4SPablo Neira Ayuso 			const struct ipt_entry *e = par->entryinfo;
228eeb4cb95SPablo Neira Ayuso 			struct nf_conntrack_l4proto *l4proto;
22924de58f4SPablo Neira Ayuso 
23024de58f4SPablo Neira Ayuso 			if (e->ip.invflags & IPT_INV_PROTO) {
23124de58f4SPablo Neira Ayuso 				ret = -EINVAL;
23224de58f4SPablo Neira Ayuso 				pr_info("You cannot use inversion on "
23324de58f4SPablo Neira Ayuso 					 "L4 protocol\n");
2341ac0bf99SPablo Neira Ayuso 				goto err4;
23524de58f4SPablo Neira Ayuso 			}
23624de58f4SPablo Neira Ayuso 			timeout = timeout_find_get(info->timeout);
23724de58f4SPablo Neira Ayuso 			if (timeout == NULL) {
23824de58f4SPablo Neira Ayuso 				ret = -ENOENT;
23924de58f4SPablo Neira Ayuso 				pr_info("No such timeout policy \"%s\"\n",
24024de58f4SPablo Neira Ayuso 					info->timeout);
2411ac0bf99SPablo Neira Ayuso 				goto err4;
24224de58f4SPablo Neira Ayuso 			}
24324de58f4SPablo Neira Ayuso 			if (timeout->l3num != par->family) {
24424de58f4SPablo Neira Ayuso 				ret = -EINVAL;
24524de58f4SPablo Neira Ayuso 				pr_info("Timeout policy `%s' can only be "
24624de58f4SPablo Neira Ayuso 					"used by L3 protocol number %d\n",
24724de58f4SPablo Neira Ayuso 					info->timeout, timeout->l3num);
2481ac0bf99SPablo Neira Ayuso 				goto err4;
24924de58f4SPablo Neira Ayuso 			}
250eeb4cb95SPablo Neira Ayuso 			/* Make sure the timeout policy matches any existing
251eeb4cb95SPablo Neira Ayuso 			 * protocol tracker, otherwise default to generic.
252eeb4cb95SPablo Neira Ayuso 			 */
253eeb4cb95SPablo Neira Ayuso 			l4proto = __nf_ct_l4proto_find(par->family,
254eeb4cb95SPablo Neira Ayuso 						       e->ip.proto);
255eeb4cb95SPablo Neira Ayuso 			if (timeout->l4proto->l4proto != l4proto->l4proto) {
25624de58f4SPablo Neira Ayuso 				ret = -EINVAL;
25724de58f4SPablo Neira Ayuso 				pr_info("Timeout policy `%s' can only be "
25824de58f4SPablo Neira Ayuso 					"used by L4 protocol number %d\n",
259c1ebd7dfSPablo Neira Ayuso 					info->timeout,
260c1ebd7dfSPablo Neira Ayuso 					timeout->l4proto->l4proto);
2611ac0bf99SPablo Neira Ayuso 				goto err4;
26224de58f4SPablo Neira Ayuso 			}
26324de58f4SPablo Neira Ayuso 			timeout_ext = nf_ct_timeout_ext_add(ct, timeout,
26424de58f4SPablo Neira Ayuso 							    GFP_KERNEL);
26524de58f4SPablo Neira Ayuso 			if (timeout_ext == NULL) {
26624de58f4SPablo Neira Ayuso 				ret = -ENOMEM;
2671ac0bf99SPablo Neira Ayuso 				goto err4;
26824de58f4SPablo Neira Ayuso 			}
26924de58f4SPablo Neira Ayuso 		} else {
27024de58f4SPablo Neira Ayuso 			ret = -ENOENT;
27124de58f4SPablo Neira Ayuso 			pr_info("Timeout policy base is empty\n");
2721ac0bf99SPablo Neira Ayuso 			goto err4;
27324de58f4SPablo Neira Ayuso 		}
2741ac0bf99SPablo Neira Ayuso 		rcu_read_unlock();
27524de58f4SPablo Neira Ayuso 	}
27624de58f4SPablo Neira Ayuso #endif
27724de58f4SPablo Neira Ayuso 
27824de58f4SPablo Neira Ayuso 	__set_bit(IPS_TEMPLATE_BIT, &ct->status);
27924de58f4SPablo Neira Ayuso 	__set_bit(IPS_CONFIRMED_BIT, &ct->status);
28024de58f4SPablo Neira Ayuso out:
28124de58f4SPablo Neira Ayuso 	info->ct = ct;
28224de58f4SPablo Neira Ayuso 	return 0;
28324de58f4SPablo Neira Ayuso 
2841ac0bf99SPablo Neira Ayuso err4:
2851ac0bf99SPablo Neira Ayuso 	rcu_read_unlock();
28624de58f4SPablo Neira Ayuso err3:
28724de58f4SPablo Neira Ayuso 	nf_conntrack_free(ct);
28824de58f4SPablo Neira Ayuso err2:
28924de58f4SPablo Neira Ayuso 	nf_ct_l3proto_module_put(par->family);
29024de58f4SPablo Neira Ayuso err1:
29124de58f4SPablo Neira Ayuso 	return ret;
29224de58f4SPablo Neira Ayuso }
29324de58f4SPablo Neira Ayuso 
29424de58f4SPablo Neira Ayuso static void xt_ct_tg_destroy_v0(const struct xt_tgdtor_param *par)
29584f3bb9aSPatrick McHardy {
29684f3bb9aSPatrick McHardy 	struct xt_ct_target_info *info = par->targinfo;
29784f3bb9aSPatrick McHardy 	struct nf_conn *ct = info->ct;
29884f3bb9aSPatrick McHardy 	struct nf_conn_help *help;
29984f3bb9aSPatrick McHardy 
3005bfddbd4SEric Dumazet 	if (!nf_ct_is_untracked(ct)) {
30184f3bb9aSPatrick McHardy 		help = nfct_help(ct);
30284f3bb9aSPatrick McHardy 		if (help)
30384f3bb9aSPatrick McHardy 			module_put(help->helper->me);
30484f3bb9aSPatrick McHardy 
30584f3bb9aSPatrick McHardy 		nf_ct_l3proto_module_put(par->family);
30684f3bb9aSPatrick McHardy 	}
30784f3bb9aSPatrick McHardy 	nf_ct_put(info->ct);
30884f3bb9aSPatrick McHardy }
30984f3bb9aSPatrick McHardy 
31024de58f4SPablo Neira Ayuso static void xt_ct_tg_destroy_v1(const struct xt_tgdtor_param *par)
31124de58f4SPablo Neira Ayuso {
31224de58f4SPablo Neira Ayuso 	struct xt_ct_target_info_v1 *info = par->targinfo;
31324de58f4SPablo Neira Ayuso 	struct nf_conn *ct = info->ct;
31424de58f4SPablo Neira Ayuso 	struct nf_conn_help *help;
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 #endif
31924de58f4SPablo Neira Ayuso 	if (!nf_ct_is_untracked(ct)) {
32024de58f4SPablo Neira Ayuso 		help = nfct_help(ct);
32124de58f4SPablo Neira Ayuso 		if (help)
32224de58f4SPablo Neira Ayuso 			module_put(help->helper->me);
32324de58f4SPablo Neira Ayuso 
32424de58f4SPablo Neira Ayuso 		nf_ct_l3proto_module_put(par->family);
32524de58f4SPablo Neira Ayuso 
32624de58f4SPablo Neira Ayuso #ifdef CONFIG_NF_CONNTRACK_TIMEOUT
3271ac0bf99SPablo Neira Ayuso 		rcu_read_lock();
32824de58f4SPablo Neira Ayuso 		timeout_put = rcu_dereference(nf_ct_timeout_put_hook);
32924de58f4SPablo Neira Ayuso 
33024de58f4SPablo Neira Ayuso 		if (timeout_put) {
33124de58f4SPablo Neira Ayuso 			timeout_ext = nf_ct_timeout_find(ct);
33224de58f4SPablo Neira Ayuso 			if (timeout_ext)
33324de58f4SPablo Neira Ayuso 				timeout_put(timeout_ext->timeout);
33424de58f4SPablo Neira Ayuso 		}
3351ac0bf99SPablo Neira Ayuso 		rcu_read_unlock();
33624de58f4SPablo Neira Ayuso #endif
33724de58f4SPablo Neira Ayuso 	}
33824de58f4SPablo Neira Ayuso 	nf_ct_put(info->ct);
33924de58f4SPablo Neira Ayuso }
34024de58f4SPablo Neira Ayuso 
34124de58f4SPablo Neira Ayuso static struct xt_target xt_ct_tg_reg[] __read_mostly = {
34224de58f4SPablo Neira Ayuso 	{
34384f3bb9aSPatrick McHardy 		.name		= "CT",
34484f3bb9aSPatrick McHardy 		.family		= NFPROTO_UNSPEC,
3457d5f7ed8SJan Engelhardt 		.targetsize	= sizeof(struct xt_ct_target_info),
34624de58f4SPablo Neira Ayuso 		.checkentry	= xt_ct_tg_check_v0,
34724de58f4SPablo Neira Ayuso 		.destroy	= xt_ct_tg_destroy_v0,
34824de58f4SPablo Neira Ayuso 		.target		= xt_ct_target_v0,
34984f3bb9aSPatrick McHardy 		.table		= "raw",
35084f3bb9aSPatrick McHardy 		.me		= THIS_MODULE,
35124de58f4SPablo Neira Ayuso 	},
35224de58f4SPablo Neira Ayuso 	{
35324de58f4SPablo Neira Ayuso 		.name		= "CT",
35424de58f4SPablo Neira Ayuso 		.family		= NFPROTO_UNSPEC,
35524de58f4SPablo Neira Ayuso 		.revision	= 1,
35624de58f4SPablo Neira Ayuso 		.targetsize	= sizeof(struct xt_ct_target_info_v1),
35724de58f4SPablo Neira Ayuso 		.checkentry	= xt_ct_tg_check_v1,
35824de58f4SPablo Neira Ayuso 		.destroy	= xt_ct_tg_destroy_v1,
35924de58f4SPablo Neira Ayuso 		.target		= xt_ct_target_v1,
36024de58f4SPablo Neira Ayuso 		.table		= "raw",
36124de58f4SPablo Neira Ayuso 		.me		= THIS_MODULE,
36224de58f4SPablo Neira Ayuso 	},
36384f3bb9aSPatrick McHardy };
36484f3bb9aSPatrick McHardy 
36584f3bb9aSPatrick McHardy static int __init xt_ct_tg_init(void)
36684f3bb9aSPatrick McHardy {
36724de58f4SPablo Neira Ayuso 	return xt_register_targets(xt_ct_tg_reg, ARRAY_SIZE(xt_ct_tg_reg));
36884f3bb9aSPatrick McHardy }
36984f3bb9aSPatrick McHardy 
37084f3bb9aSPatrick McHardy static void __exit xt_ct_tg_exit(void)
37184f3bb9aSPatrick McHardy {
37224de58f4SPablo Neira Ayuso 	xt_unregister_targets(xt_ct_tg_reg, ARRAY_SIZE(xt_ct_tg_reg));
37384f3bb9aSPatrick McHardy }
37484f3bb9aSPatrick McHardy 
37584f3bb9aSPatrick McHardy module_init(xt_ct_tg_init);
37684f3bb9aSPatrick McHardy module_exit(xt_ct_tg_exit);
37784f3bb9aSPatrick McHardy 
37884f3bb9aSPatrick McHardy MODULE_LICENSE("GPL");
37984f3bb9aSPatrick McHardy MODULE_DESCRIPTION("Xtables: connection tracking target");
38084f3bb9aSPatrick McHardy MODULE_ALIAS("ipt_CT");
38184f3bb9aSPatrick McHardy MODULE_ALIAS("ip6t_CT");
382