1e97150dfSThomas Gleixner // SPDX-License-Identifier: GPL-2.0-or-later
250978462SPablo Neira Ayuso /*
350978462SPablo Neira Ayuso  * (C) 2012 by Pablo Neira Ayuso <pablo@netfilter.org>
450978462SPablo Neira Ayuso  * (C) 2012 by Vyatta Inc. <http://www.vyatta.com>
550978462SPablo Neira Ayuso  */
650978462SPablo Neira Ayuso #include <linux/init.h>
750978462SPablo Neira Ayuso #include <linux/module.h>
850978462SPablo Neira Ayuso #include <linux/kernel.h>
950978462SPablo Neira Ayuso #include <linux/rculist.h>
1050978462SPablo Neira Ayuso #include <linux/rculist_nulls.h>
1150978462SPablo Neira Ayuso #include <linux/types.h>
1250978462SPablo Neira Ayuso #include <linux/timer.h>
1350978462SPablo Neira Ayuso #include <linux/security.h>
1450978462SPablo Neira Ayuso #include <linux/skbuff.h>
1550978462SPablo Neira Ayuso #include <linux/errno.h>
1650978462SPablo Neira Ayuso #include <linux/netlink.h>
1750978462SPablo Neira Ayuso #include <linux/spinlock.h>
1850978462SPablo Neira Ayuso #include <linux/interrupt.h>
1950978462SPablo Neira Ayuso #include <linux/slab.h>
2050978462SPablo Neira Ayuso 
2150978462SPablo Neira Ayuso #include <linux/netfilter.h>
2250978462SPablo Neira Ayuso #include <net/netlink.h>
23ebfbe675SFlorian Westphal #include <net/netns/generic.h>
2450978462SPablo Neira Ayuso #include <net/sock.h>
2550978462SPablo Neira Ayuso #include <net/netfilter/nf_conntrack.h>
2650978462SPablo Neira Ayuso #include <net/netfilter/nf_conntrack_core.h>
2750978462SPablo Neira Ayuso #include <net/netfilter/nf_conntrack_l4proto.h>
2850978462SPablo Neira Ayuso #include <net/netfilter/nf_conntrack_tuple.h>
2924de58f4SPablo Neira Ayuso #include <net/netfilter/nf_conntrack_timeout.h>
3050978462SPablo Neira Ayuso 
3150978462SPablo Neira Ayuso #include <linux/netfilter/nfnetlink.h>
3250978462SPablo Neira Ayuso #include <linux/netfilter/nfnetlink_cttimeout.h>
3350978462SPablo Neira Ayuso 
34ebfbe675SFlorian Westphal static unsigned int nfct_timeout_id __read_mostly;
35ebfbe675SFlorian Westphal 
3678222bacSFlorian Westphal struct ctnl_timeout {
3778222bacSFlorian Westphal 	struct list_head	head;
38aeed55a0SFlorian Westphal 	struct list_head	free_head;
3978222bacSFlorian Westphal 	struct rcu_head		rcu_head;
4078222bacSFlorian Westphal 	refcount_t		refcnt;
4178222bacSFlorian Westphal 	char			name[CTNL_TIMEOUT_NAME_MAX];
4278222bacSFlorian Westphal 
43aeed55a0SFlorian Westphal 	/* must be at the end */
44aeed55a0SFlorian Westphal 	struct nf_ct_timeout	timeout;
4578222bacSFlorian Westphal };
4678222bacSFlorian Westphal 
47ebfbe675SFlorian Westphal struct nfct_timeout_pernet {
48ebfbe675SFlorian Westphal 	struct list_head	nfct_timeout_list;
4978222bacSFlorian Westphal 	struct list_head	nfct_timeout_freelist;
50ebfbe675SFlorian Westphal };
51ebfbe675SFlorian Westphal 
5250978462SPablo Neira Ayuso MODULE_LICENSE("GPL");
5350978462SPablo Neira Ayuso MODULE_AUTHOR("Pablo Neira Ayuso <pablo@netfilter.org>");
5450978462SPablo Neira Ayuso MODULE_DESCRIPTION("cttimeout: Extended Netfilter Connection Tracking timeout tuning");
5550978462SPablo Neira Ayuso 
5650978462SPablo Neira Ayuso static const struct nla_policy cttimeout_nla_policy[CTA_TIMEOUT_MAX+1] = {
57e93b5f9fSFlorian Westphal 	[CTA_TIMEOUT_NAME]	= { .type = NLA_NUL_STRING,
58e93b5f9fSFlorian Westphal 				    .len  = CTNL_TIMEOUT_NAME_MAX - 1},
5950978462SPablo Neira Ayuso 	[CTA_TIMEOUT_L3PROTO]	= { .type = NLA_U16 },
6050978462SPablo Neira Ayuso 	[CTA_TIMEOUT_L4PROTO]	= { .type = NLA_U8 },
6150978462SPablo Neira Ayuso 	[CTA_TIMEOUT_DATA]	= { .type = NLA_NESTED },
6250978462SPablo Neira Ayuso };
6350978462SPablo Neira Ayuso 
nfct_timeout_pernet(struct net * net)64ebfbe675SFlorian Westphal static struct nfct_timeout_pernet *nfct_timeout_pernet(struct net *net)
65ebfbe675SFlorian Westphal {
66ebfbe675SFlorian Westphal 	return net_generic(net, nfct_timeout_id);
67ebfbe675SFlorian Westphal }
68ebfbe675SFlorian Westphal 
6950978462SPablo Neira Ayuso static int
ctnl_timeout_parse_policy(void * timeout,const struct nf_conntrack_l4proto * l4proto,struct net * net,const struct nlattr * attr)70c779e849SFlorian Westphal ctnl_timeout_parse_policy(void *timeout,
712a04aabfSJulia Lawall 			  const struct nf_conntrack_l4proto *l4proto,
7291cb498eSPablo Neira Ayuso 			  struct net *net, const struct nlattr *attr)
7350978462SPablo Neira Ayuso {
748039ab43SGustavo A. R. Silva 	struct nlattr **tb;
7550978462SPablo Neira Ayuso 	int ret = 0;
7650978462SPablo Neira Ayuso 
778039ab43SGustavo A. R. Silva 	tb = kcalloc(l4proto->ctnl_timeout.nlattr_max + 1, sizeof(*tb),
788039ab43SGustavo A. R. Silva 		     GFP_KERNEL);
798039ab43SGustavo A. R. Silva 
808039ab43SGustavo A. R. Silva 	if (!tb)
818039ab43SGustavo A. R. Silva 		return -ENOMEM;
828039ab43SGustavo A. R. Silva 
838cb08174SJohannes Berg 	ret = nla_parse_nested_deprecated(tb,
848cb08174SJohannes Berg 					  l4proto->ctnl_timeout.nlattr_max,
858cb08174SJohannes Berg 					  attr,
868cb08174SJohannes Berg 					  l4proto->ctnl_timeout.nla_policy,
878cb08174SJohannes Berg 					  NULL);
88130ffbc2SDaniel Borkmann 	if (ret < 0)
898039ab43SGustavo A. R. Silva 		goto err;
9050978462SPablo Neira Ayuso 
91c779e849SFlorian Westphal 	ret = l4proto->ctnl_timeout.nlattr_to_obj(tb, net, timeout);
928039ab43SGustavo A. R. Silva 
938039ab43SGustavo A. R. Silva err:
948039ab43SGustavo A. R. Silva 	kfree(tb);
9550978462SPablo Neira Ayuso 	return ret;
9650978462SPablo Neira Ayuso }
9750978462SPablo Neira Ayuso 
cttimeout_new_timeout(struct sk_buff * skb,const struct nfnl_info * info,const struct nlattr * const cda[])98a6555365SPablo Neira Ayuso static int cttimeout_new_timeout(struct sk_buff *skb,
99a6555365SPablo Neira Ayuso 				 const struct nfnl_info *info,
100a6555365SPablo Neira Ayuso 				 const struct nlattr * const cda[])
10150978462SPablo Neira Ayuso {
102a6555365SPablo Neira Ayuso 	struct nfct_timeout_pernet *pernet = nfct_timeout_pernet(info->net);
10350978462SPablo Neira Ayuso 	__u16 l3num;
10450978462SPablo Neira Ayuso 	__u8 l4num;
105b3480fe0SFlorian Westphal 	const struct nf_conntrack_l4proto *l4proto;
10650978462SPablo Neira Ayuso 	struct ctnl_timeout *timeout, *matching = NULL;
10750978462SPablo Neira Ayuso 	char *name;
10850978462SPablo Neira Ayuso 	int ret;
10950978462SPablo Neira Ayuso 
11050978462SPablo Neira Ayuso 	if (!cda[CTA_TIMEOUT_NAME] ||
11150978462SPablo Neira Ayuso 	    !cda[CTA_TIMEOUT_L3PROTO] ||
11250978462SPablo Neira Ayuso 	    !cda[CTA_TIMEOUT_L4PROTO] ||
11350978462SPablo Neira Ayuso 	    !cda[CTA_TIMEOUT_DATA])
11450978462SPablo Neira Ayuso 		return -EINVAL;
11550978462SPablo Neira Ayuso 
11650978462SPablo Neira Ayuso 	name = nla_data(cda[CTA_TIMEOUT_NAME]);
11750978462SPablo Neira Ayuso 	l3num = ntohs(nla_get_be16(cda[CTA_TIMEOUT_L3PROTO]));
11850978462SPablo Neira Ayuso 	l4num = nla_get_u8(cda[CTA_TIMEOUT_L4PROTO]);
11950978462SPablo Neira Ayuso 
120ebfbe675SFlorian Westphal 	list_for_each_entry(timeout, &pernet->nfct_timeout_list, head) {
12150978462SPablo Neira Ayuso 		if (strncmp(timeout->name, name, CTNL_TIMEOUT_NAME_MAX) != 0)
12250978462SPablo Neira Ayuso 			continue;
12350978462SPablo Neira Ayuso 
124a6555365SPablo Neira Ayuso 		if (info->nlh->nlmsg_flags & NLM_F_EXCL)
12550978462SPablo Neira Ayuso 			return -EEXIST;
12650978462SPablo Neira Ayuso 
12750978462SPablo Neira Ayuso 		matching = timeout;
12850978462SPablo Neira Ayuso 		break;
12950978462SPablo Neira Ayuso 	}
13050978462SPablo Neira Ayuso 
13150978462SPablo Neira Ayuso 	if (matching) {
132a6555365SPablo Neira Ayuso 		if (info->nlh->nlmsg_flags & NLM_F_REPLACE) {
13350978462SPablo Neira Ayuso 			/* You cannot replace one timeout policy by another of
13450978462SPablo Neira Ayuso 			 * different kind, sorry.
13550978462SPablo Neira Ayuso 			 */
1366c1fd7dcSPablo Neira Ayuso 			if (matching->timeout.l3num != l3num ||
1376c1fd7dcSPablo Neira Ayuso 			    matching->timeout.l4proto->l4proto != l4num)
13823aaba5aSLiping Zhang 				return -EINVAL;
13923aaba5aSLiping Zhang 
1406c1fd7dcSPablo Neira Ayuso 			return ctnl_timeout_parse_policy(&matching->timeout.data,
1416c1fd7dcSPablo Neira Ayuso 							 matching->timeout.l4proto,
142a6555365SPablo Neira Ayuso 							 info->net,
143a6555365SPablo Neira Ayuso 							 cda[CTA_TIMEOUT_DATA]);
144c1ebd7dfSPablo Neira Ayuso 		}
14550978462SPablo Neira Ayuso 
14623aaba5aSLiping Zhang 		return -EBUSY;
14750978462SPablo Neira Ayuso 	}
14823aaba5aSLiping Zhang 
1494a60dc74SFlorian Westphal 	l4proto = nf_ct_l4proto_find(l4num);
15023aaba5aSLiping Zhang 
15123aaba5aSLiping Zhang 	/* This protocol is not supportted, skip. */
15223aaba5aSLiping Zhang 	if (l4proto->l4proto != l4num) {
15323aaba5aSLiping Zhang 		ret = -EOPNOTSUPP;
154c1ebd7dfSPablo Neira Ayuso 		goto err_proto_put;
15550978462SPablo Neira Ayuso 	}
15650978462SPablo Neira Ayuso 
15750978462SPablo Neira Ayuso 	timeout = kzalloc(sizeof(struct ctnl_timeout) +
15850978462SPablo Neira Ayuso 			  l4proto->ctnl_timeout.obj_size, GFP_KERNEL);
159c1ebd7dfSPablo Neira Ayuso 	if (timeout == NULL) {
160c1ebd7dfSPablo Neira Ayuso 		ret = -ENOMEM;
161c1ebd7dfSPablo Neira Ayuso 		goto err_proto_put;
162c1ebd7dfSPablo Neira Ayuso 	}
16350978462SPablo Neira Ayuso 
164a6555365SPablo Neira Ayuso 	ret = ctnl_timeout_parse_policy(&timeout->timeout.data, l4proto,
165a6555365SPablo Neira Ayuso 					info->net, cda[CTA_TIMEOUT_DATA]);
16650978462SPablo Neira Ayuso 	if (ret < 0)
16750978462SPablo Neira Ayuso 		goto err;
16850978462SPablo Neira Ayuso 
16950978462SPablo Neira Ayuso 	strcpy(timeout->name, nla_data(cda[CTA_TIMEOUT_NAME]));
1706c1fd7dcSPablo Neira Ayuso 	timeout->timeout.l3num = l3num;
1716c1fd7dcSPablo Neira Ayuso 	timeout->timeout.l4proto = l4proto;
172b54ab92bSReshetova, Elena 	refcount_set(&timeout->refcnt, 1);
173523895e5SFlorian Westphal 	__module_get(THIS_MODULE);
174ebfbe675SFlorian Westphal 	list_add_tail_rcu(&timeout->head, &pernet->nfct_timeout_list);
17550978462SPablo Neira Ayuso 
17650978462SPablo Neira Ayuso 	return 0;
17750978462SPablo Neira Ayuso err:
17850978462SPablo Neira Ayuso 	kfree(timeout);
179c1ebd7dfSPablo Neira Ayuso err_proto_put:
18050978462SPablo Neira Ayuso 	return ret;
18150978462SPablo Neira Ayuso }
18250978462SPablo Neira Ayuso 
18350978462SPablo Neira Ayuso static int
ctnl_timeout_fill_info(struct sk_buff * skb,u32 portid,u32 seq,u32 type,int event,struct ctnl_timeout * timeout)18415e47304SEric W. Biederman ctnl_timeout_fill_info(struct sk_buff *skb, u32 portid, u32 seq, u32 type,
18550978462SPablo Neira Ayuso 		       int event, struct ctnl_timeout *timeout)
18650978462SPablo Neira Ayuso {
18750978462SPablo Neira Ayuso 	struct nlmsghdr *nlh;
18815e47304SEric W. Biederman 	unsigned int flags = portid ? NLM_F_MULTI : 0;
1896c1fd7dcSPablo Neira Ayuso 	const struct nf_conntrack_l4proto *l4proto = timeout->timeout.l4proto;
1904430b897SPablo Neira Ayuso 	struct nlattr *nest_parms;
1914430b897SPablo Neira Ayuso 	int ret;
19250978462SPablo Neira Ayuso 
193dedb67c4SPablo Neira Ayuso 	event = nfnl_msg_type(NFNL_SUBSYS_CTNETLINK_TIMEOUT, event);
19419c28b13SPablo Neira Ayuso 	nlh = nfnl_msg_put(skb, portid, seq, event, flags, AF_UNSPEC,
19519c28b13SPablo Neira Ayuso 			   NFNETLINK_V0, 0);
19619c28b13SPablo Neira Ayuso 	if (!nlh)
19750978462SPablo Neira Ayuso 		goto nlmsg_failure;
19850978462SPablo Neira Ayuso 
19948f03bdaSDavid S. Miller 	if (nla_put_string(skb, CTA_TIMEOUT_NAME, timeout->name) ||
2006c1fd7dcSPablo Neira Ayuso 	    nla_put_be16(skb, CTA_TIMEOUT_L3PROTO,
2016c1fd7dcSPablo Neira Ayuso 			 htons(timeout->timeout.l3num)) ||
2026c1fd7dcSPablo Neira Ayuso 	    nla_put_u8(skb, CTA_TIMEOUT_L4PROTO, l4proto->l4proto) ||
20348f03bdaSDavid S. Miller 	    nla_put_be32(skb, CTA_TIMEOUT_USE,
204b54ab92bSReshetova, Elena 			 htonl(refcount_read(&timeout->refcnt))))
20548f03bdaSDavid S. Miller 		goto nla_put_failure;
20650978462SPablo Neira Ayuso 
207ae0be8deSMichal Kubecek 	nest_parms = nla_nest_start(skb, CTA_TIMEOUT_DATA);
20850978462SPablo Neira Ayuso 	if (!nest_parms)
20950978462SPablo Neira Ayuso 		goto nla_put_failure;
21050978462SPablo Neira Ayuso 
2114430b897SPablo Neira Ayuso 	ret = l4proto->ctnl_timeout.obj_to_nlattr(skb, &timeout->timeout.data);
21250978462SPablo Neira Ayuso 	if (ret < 0)
21350978462SPablo Neira Ayuso 		goto nla_put_failure;
21450978462SPablo Neira Ayuso 
21550978462SPablo Neira Ayuso 	nla_nest_end(skb, nest_parms);
216c1ebd7dfSPablo Neira Ayuso 
21750978462SPablo Neira Ayuso 	nlmsg_end(skb, nlh);
21850978462SPablo Neira Ayuso 	return skb->len;
21950978462SPablo Neira Ayuso 
22050978462SPablo Neira Ayuso nlmsg_failure:
22150978462SPablo Neira Ayuso nla_put_failure:
22250978462SPablo Neira Ayuso 	nlmsg_cancel(skb, nlh);
22350978462SPablo Neira Ayuso 	return -1;
22450978462SPablo Neira Ayuso }
22550978462SPablo Neira Ayuso 
22650978462SPablo Neira Ayuso static int
ctnl_timeout_dump(struct sk_buff * skb,struct netlink_callback * cb)22750978462SPablo Neira Ayuso ctnl_timeout_dump(struct sk_buff *skb, struct netlink_callback *cb)
22850978462SPablo Neira Ayuso {
229ebfbe675SFlorian Westphal 	struct nfct_timeout_pernet *pernet;
23019576c94SPablo Neira 	struct net *net = sock_net(skb->sk);
23150978462SPablo Neira Ayuso 	struct ctnl_timeout *cur, *last;
23250978462SPablo Neira Ayuso 
23350978462SPablo Neira Ayuso 	if (cb->args[2])
23450978462SPablo Neira Ayuso 		return 0;
23550978462SPablo Neira Ayuso 
23650978462SPablo Neira Ayuso 	last = (struct ctnl_timeout *)cb->args[1];
23750978462SPablo Neira Ayuso 	if (cb->args[1])
23850978462SPablo Neira Ayuso 		cb->args[1] = 0;
23950978462SPablo Neira Ayuso 
24050978462SPablo Neira Ayuso 	rcu_read_lock();
241ebfbe675SFlorian Westphal 	pernet = nfct_timeout_pernet(net);
242ebfbe675SFlorian Westphal 	list_for_each_entry_rcu(cur, &pernet->nfct_timeout_list, head) {
24337bc4f8dSPablo Neira Ayuso 		if (last) {
24437bc4f8dSPablo Neira Ayuso 			if (cur != last)
24550978462SPablo Neira Ayuso 				continue;
24650978462SPablo Neira Ayuso 
24737bc4f8dSPablo Neira Ayuso 			last = NULL;
24837bc4f8dSPablo Neira Ayuso 		}
24915e47304SEric W. Biederman 		if (ctnl_timeout_fill_info(skb, NETLINK_CB(cb->skb).portid,
25050978462SPablo Neira Ayuso 					   cb->nlh->nlmsg_seq,
25150978462SPablo Neira Ayuso 					   NFNL_MSG_TYPE(cb->nlh->nlmsg_type),
25250978462SPablo Neira Ayuso 					   IPCTNL_MSG_TIMEOUT_NEW, cur) < 0) {
25350978462SPablo Neira Ayuso 			cb->args[1] = (unsigned long)cur;
25450978462SPablo Neira Ayuso 			break;
25550978462SPablo Neira Ayuso 		}
25650978462SPablo Neira Ayuso 	}
25750978462SPablo Neira Ayuso 	if (!cb->args[1])
25850978462SPablo Neira Ayuso 		cb->args[2] = 1;
25950978462SPablo Neira Ayuso 	rcu_read_unlock();
26050978462SPablo Neira Ayuso 	return skb->len;
26150978462SPablo Neira Ayuso }
26250978462SPablo Neira Ayuso 
cttimeout_get_timeout(struct sk_buff * skb,const struct nfnl_info * info,const struct nlattr * const cda[])263a6555365SPablo Neira Ayuso static int cttimeout_get_timeout(struct sk_buff *skb,
264a6555365SPablo Neira Ayuso 				 const struct nfnl_info *info,
265a6555365SPablo Neira Ayuso 				 const struct nlattr * const cda[])
26650978462SPablo Neira Ayuso {
267a6555365SPablo Neira Ayuso 	struct nfct_timeout_pernet *pernet = nfct_timeout_pernet(info->net);
26850978462SPablo Neira Ayuso 	int ret = -ENOENT;
26950978462SPablo Neira Ayuso 	char *name;
27050978462SPablo Neira Ayuso 	struct ctnl_timeout *cur;
27150978462SPablo Neira Ayuso 
272a6555365SPablo Neira Ayuso 	if (info->nlh->nlmsg_flags & NLM_F_DUMP) {
27350978462SPablo Neira Ayuso 		struct netlink_dump_control c = {
27450978462SPablo Neira Ayuso 			.dump = ctnl_timeout_dump,
27550978462SPablo Neira Ayuso 		};
276a6555365SPablo Neira Ayuso 		return netlink_dump_start(info->sk, skb, info->nlh, &c);
27750978462SPablo Neira Ayuso 	}
27850978462SPablo Neira Ayuso 
27950978462SPablo Neira Ayuso 	if (!cda[CTA_TIMEOUT_NAME])
28050978462SPablo Neira Ayuso 		return -EINVAL;
28150978462SPablo Neira Ayuso 	name = nla_data(cda[CTA_TIMEOUT_NAME]);
28250978462SPablo Neira Ayuso 
283ebfbe675SFlorian Westphal 	list_for_each_entry(cur, &pernet->nfct_timeout_list, head) {
28450978462SPablo Neira Ayuso 		struct sk_buff *skb2;
28550978462SPablo Neira Ayuso 
28650978462SPablo Neira Ayuso 		if (strncmp(cur->name, name, CTNL_TIMEOUT_NAME_MAX) != 0)
28750978462SPablo Neira Ayuso 			continue;
28850978462SPablo Neira Ayuso 
28950978462SPablo Neira Ayuso 		skb2 = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
29050978462SPablo Neira Ayuso 		if (skb2 == NULL) {
29150978462SPablo Neira Ayuso 			ret = -ENOMEM;
29250978462SPablo Neira Ayuso 			break;
29350978462SPablo Neira Ayuso 		}
29450978462SPablo Neira Ayuso 
29515e47304SEric W. Biederman 		ret = ctnl_timeout_fill_info(skb2, NETLINK_CB(skb).portid,
296a6555365SPablo Neira Ayuso 					     info->nlh->nlmsg_seq,
297a6555365SPablo Neira Ayuso 					     NFNL_MSG_TYPE(info->nlh->nlmsg_type),
29850978462SPablo Neira Ayuso 					     IPCTNL_MSG_TIMEOUT_NEW, cur);
29950978462SPablo Neira Ayuso 		if (ret <= 0) {
30050978462SPablo Neira Ayuso 			kfree_skb(skb2);
30150978462SPablo Neira Ayuso 			break;
30250978462SPablo Neira Ayuso 		}
30350978462SPablo Neira Ayuso 
304e0241ae6SPablo Neira Ayuso 		ret = nfnetlink_unicast(skb2, info->net, NETLINK_CB(skb).portid);
305e0241ae6SPablo Neira Ayuso 		break;
30650978462SPablo Neira Ayuso 	}
307e0241ae6SPablo Neira Ayuso 
30850978462SPablo Neira Ayuso 	return ret;
30950978462SPablo Neira Ayuso }
31050978462SPablo Neira Ayuso 
31150978462SPablo Neira Ayuso /* try to delete object, fail if it is still in use. */
ctnl_timeout_try_del(struct net * net,struct ctnl_timeout * timeout)31219576c94SPablo Neira static int ctnl_timeout_try_del(struct net *net, struct ctnl_timeout *timeout)
31350978462SPablo Neira Ayuso {
31450978462SPablo Neira Ayuso 	int ret = 0;
31550978462SPablo Neira Ayuso 
316b75911b6SLiping Zhang 	/* We want to avoid races with ctnl_timeout_put. So only when the
317b75911b6SLiping Zhang 	 * current refcnt is 1, we decrease it to 0.
318b75911b6SLiping Zhang 	 */
319b54ab92bSReshetova, Elena 	if (refcount_dec_if_one(&timeout->refcnt)) {
32050978462SPablo Neira Ayuso 		/* We are protected by nfnl mutex. */
32150978462SPablo Neira Ayuso 		list_del_rcu(&timeout->head);
3226c1fd7dcSPablo Neira Ayuso 		nf_ct_untimeout(net, &timeout->timeout);
32350978462SPablo Neira Ayuso 		kfree_rcu(timeout, rcu_head);
32450978462SPablo Neira Ayuso 	} else {
32550978462SPablo Neira Ayuso 		ret = -EBUSY;
32650978462SPablo Neira Ayuso 	}
32750978462SPablo Neira Ayuso 	return ret;
32850978462SPablo Neira Ayuso }
32950978462SPablo Neira Ayuso 
cttimeout_del_timeout(struct sk_buff * skb,const struct nfnl_info * info,const struct nlattr * const cda[])330a6555365SPablo Neira Ayuso static int cttimeout_del_timeout(struct sk_buff *skb,
331a6555365SPablo Neira Ayuso 				 const struct nfnl_info *info,
332a6555365SPablo Neira Ayuso 				 const struct nlattr * const cda[])
33350978462SPablo Neira Ayuso {
334a6555365SPablo Neira Ayuso 	struct nfct_timeout_pernet *pernet = nfct_timeout_pernet(info->net);
33593fac10bSLiping Zhang 	struct ctnl_timeout *cur, *tmp;
33650978462SPablo Neira Ayuso 	int ret = -ENOENT;
3377b8002a1SPablo Neira Ayuso 	char *name;
33850978462SPablo Neira Ayuso 
33950978462SPablo Neira Ayuso 	if (!cda[CTA_TIMEOUT_NAME]) {
340ebfbe675SFlorian Westphal 		list_for_each_entry_safe(cur, tmp, &pernet->nfct_timeout_list,
34193fac10bSLiping Zhang 					 head)
342a6555365SPablo Neira Ayuso 			ctnl_timeout_try_del(info->net, cur);
34350978462SPablo Neira Ayuso 
34450978462SPablo Neira Ayuso 		return 0;
34550978462SPablo Neira Ayuso 	}
34650978462SPablo Neira Ayuso 	name = nla_data(cda[CTA_TIMEOUT_NAME]);
34750978462SPablo Neira Ayuso 
348ebfbe675SFlorian Westphal 	list_for_each_entry(cur, &pernet->nfct_timeout_list, head) {
34950978462SPablo Neira Ayuso 		if (strncmp(cur->name, name, CTNL_TIMEOUT_NAME_MAX) != 0)
35050978462SPablo Neira Ayuso 			continue;
35150978462SPablo Neira Ayuso 
352a6555365SPablo Neira Ayuso 		ret = ctnl_timeout_try_del(info->net, cur);
35350978462SPablo Neira Ayuso 		if (ret < 0)
35450978462SPablo Neira Ayuso 			return ret;
35550978462SPablo Neira Ayuso 
35650978462SPablo Neira Ayuso 		break;
35750978462SPablo Neira Ayuso 	}
35850978462SPablo Neira Ayuso 	return ret;
35950978462SPablo Neira Ayuso }
36050978462SPablo Neira Ayuso 
cttimeout_default_set(struct sk_buff * skb,const struct nfnl_info * info,const struct nlattr * const cda[])361a6555365SPablo Neira Ayuso static int cttimeout_default_set(struct sk_buff *skb,
362a6555365SPablo Neira Ayuso 				 const struct nfnl_info *info,
363a6555365SPablo Neira Ayuso 				 const struct nlattr * const cda[])
36491cb498eSPablo Neira Ayuso {
365b3480fe0SFlorian Westphal 	const struct nf_conntrack_l4proto *l4proto;
36691cb498eSPablo Neira Ayuso 	__u8 l4num;
36791cb498eSPablo Neira Ayuso 	int ret;
36891cb498eSPablo Neira Ayuso 
36991cb498eSPablo Neira Ayuso 	if (!cda[CTA_TIMEOUT_L3PROTO] ||
37091cb498eSPablo Neira Ayuso 	    !cda[CTA_TIMEOUT_L4PROTO] ||
37191cb498eSPablo Neira Ayuso 	    !cda[CTA_TIMEOUT_DATA])
37291cb498eSPablo Neira Ayuso 		return -EINVAL;
37391cb498eSPablo Neira Ayuso 
37491cb498eSPablo Neira Ayuso 	l4num = nla_get_u8(cda[CTA_TIMEOUT_L4PROTO]);
3754a60dc74SFlorian Westphal 	l4proto = nf_ct_l4proto_find(l4num);
37691cb498eSPablo Neira Ayuso 
37791cb498eSPablo Neira Ayuso 	/* This protocol is not supported, skip. */
37891cb498eSPablo Neira Ayuso 	if (l4proto->l4proto != l4num) {
37991cb498eSPablo Neira Ayuso 		ret = -EOPNOTSUPP;
38091cb498eSPablo Neira Ayuso 		goto err;
38191cb498eSPablo Neira Ayuso 	}
38291cb498eSPablo Neira Ayuso 
383a6555365SPablo Neira Ayuso 	ret = ctnl_timeout_parse_policy(NULL, l4proto, info->net,
38491cb498eSPablo Neira Ayuso 					cda[CTA_TIMEOUT_DATA]);
38591cb498eSPablo Neira Ayuso 	if (ret < 0)
38691cb498eSPablo Neira Ayuso 		goto err;
38791cb498eSPablo Neira Ayuso 
38891cb498eSPablo Neira Ayuso 	return 0;
38991cb498eSPablo Neira Ayuso err:
39091cb498eSPablo Neira Ayuso 	return ret;
39191cb498eSPablo Neira Ayuso }
39291cb498eSPablo Neira Ayuso 
39391cb498eSPablo Neira Ayuso static int
cttimeout_default_fill_info(struct net * net,struct sk_buff * skb,u32 portid,u32 seq,u32 type,int event,u16 l3num,const struct nf_conntrack_l4proto * l4proto,const unsigned int * timeouts)39491cb498eSPablo Neira Ayuso cttimeout_default_fill_info(struct net *net, struct sk_buff *skb, u32 portid,
395dd2934a9SFlorian Westphal 			    u32 seq, u32 type, int event, u16 l3num,
3968866df92SPablo Neira Ayuso 			    const struct nf_conntrack_l4proto *l4proto,
3978866df92SPablo Neira Ayuso 			    const unsigned int *timeouts)
39891cb498eSPablo Neira Ayuso {
39991cb498eSPablo Neira Ayuso 	struct nlmsghdr *nlh;
40091cb498eSPablo Neira Ayuso 	unsigned int flags = portid ? NLM_F_MULTI : 0;
4014430b897SPablo Neira Ayuso 	struct nlattr *nest_parms;
4024430b897SPablo Neira Ayuso 	int ret;
40391cb498eSPablo Neira Ayuso 
404dedb67c4SPablo Neira Ayuso 	event = nfnl_msg_type(NFNL_SUBSYS_CTNETLINK_TIMEOUT, event);
40519c28b13SPablo Neira Ayuso 	nlh = nfnl_msg_put(skb, portid, seq, event, flags, AF_UNSPEC,
40619c28b13SPablo Neira Ayuso 			   NFNETLINK_V0, 0);
40719c28b13SPablo Neira Ayuso 	if (!nlh)
40891cb498eSPablo Neira Ayuso 		goto nlmsg_failure;
40991cb498eSPablo Neira Ayuso 
410dd2934a9SFlorian Westphal 	if (nla_put_be16(skb, CTA_TIMEOUT_L3PROTO, htons(l3num)) ||
41191cb498eSPablo Neira Ayuso 	    nla_put_u8(skb, CTA_TIMEOUT_L4PROTO, l4proto->l4proto))
41291cb498eSPablo Neira Ayuso 		goto nla_put_failure;
41391cb498eSPablo Neira Ayuso 
414ae0be8deSMichal Kubecek 	nest_parms = nla_nest_start(skb, CTA_TIMEOUT_DATA);
41591cb498eSPablo Neira Ayuso 	if (!nest_parms)
41691cb498eSPablo Neira Ayuso 		goto nla_put_failure;
41791cb498eSPablo Neira Ayuso 
4188866df92SPablo Neira Ayuso 	ret = l4proto->ctnl_timeout.obj_to_nlattr(skb, timeouts);
41991cb498eSPablo Neira Ayuso 	if (ret < 0)
42091cb498eSPablo Neira Ayuso 		goto nla_put_failure;
42191cb498eSPablo Neira Ayuso 
42291cb498eSPablo Neira Ayuso 	nla_nest_end(skb, nest_parms);
42391cb498eSPablo Neira Ayuso 
42491cb498eSPablo Neira Ayuso 	nlmsg_end(skb, nlh);
42591cb498eSPablo Neira Ayuso 	return skb->len;
42691cb498eSPablo Neira Ayuso 
42791cb498eSPablo Neira Ayuso nlmsg_failure:
42891cb498eSPablo Neira Ayuso nla_put_failure:
42991cb498eSPablo Neira Ayuso 	nlmsg_cancel(skb, nlh);
43091cb498eSPablo Neira Ayuso 	return -1;
43191cb498eSPablo Neira Ayuso }
43291cb498eSPablo Neira Ayuso 
cttimeout_default_get(struct sk_buff * skb,const struct nfnl_info * info,const struct nlattr * const cda[])433a6555365SPablo Neira Ayuso static int cttimeout_default_get(struct sk_buff *skb,
434a6555365SPablo Neira Ayuso 				 const struct nfnl_info *info,
435a6555365SPablo Neira Ayuso 				 const struct nlattr * const cda[])
43691cb498eSPablo Neira Ayuso {
437b3480fe0SFlorian Westphal 	const struct nf_conntrack_l4proto *l4proto;
4388866df92SPablo Neira Ayuso 	unsigned int *timeouts = NULL;
43991cb498eSPablo Neira Ayuso 	struct sk_buff *skb2;
440b3480fe0SFlorian Westphal 	__u16 l3num;
441b3480fe0SFlorian Westphal 	__u8 l4num;
442e0241ae6SPablo Neira Ayuso 	int ret;
44391cb498eSPablo Neira Ayuso 
44491cb498eSPablo Neira Ayuso 	if (!cda[CTA_TIMEOUT_L3PROTO] || !cda[CTA_TIMEOUT_L4PROTO])
44591cb498eSPablo Neira Ayuso 		return -EINVAL;
44691cb498eSPablo Neira Ayuso 
44791cb498eSPablo Neira Ayuso 	l3num = ntohs(nla_get_be16(cda[CTA_TIMEOUT_L3PROTO]));
44891cb498eSPablo Neira Ayuso 	l4num = nla_get_u8(cda[CTA_TIMEOUT_L4PROTO]);
4494a60dc74SFlorian Westphal 	l4proto = nf_ct_l4proto_find(l4num);
45091cb498eSPablo Neira Ayuso 
4518866df92SPablo Neira Ayuso 	if (l4proto->l4proto != l4num)
452e0241ae6SPablo Neira Ayuso 		return -EOPNOTSUPP;
4538866df92SPablo Neira Ayuso 
4548866df92SPablo Neira Ayuso 	switch (l4proto->l4proto) {
4558866df92SPablo Neira Ayuso 	case IPPROTO_ICMP:
456a6555365SPablo Neira Ayuso 		timeouts = &nf_icmp_pernet(info->net)->timeout;
4578866df92SPablo Neira Ayuso 		break;
4588866df92SPablo Neira Ayuso 	case IPPROTO_TCP:
459a6555365SPablo Neira Ayuso 		timeouts = nf_tcp_pernet(info->net)->timeouts;
4608866df92SPablo Neira Ayuso 		break;
461954d8297SGustavo A. R. Silva 	case IPPROTO_UDP:
46289259088SFlorian Westphal 	case IPPROTO_UDPLITE:
463a6555365SPablo Neira Ayuso 		timeouts = nf_udp_pernet(info->net)->timeouts;
4648866df92SPablo Neira Ayuso 		break;
4658866df92SPablo Neira Ayuso 	case IPPROTO_DCCP:
4668866df92SPablo Neira Ayuso #ifdef CONFIG_NF_CT_PROTO_DCCP
467a6555365SPablo Neira Ayuso 		timeouts = nf_dccp_pernet(info->net)->dccp_timeout;
4688866df92SPablo Neira Ayuso #endif
4698866df92SPablo Neira Ayuso 		break;
4708866df92SPablo Neira Ayuso 	case IPPROTO_ICMPV6:
471a6555365SPablo Neira Ayuso 		timeouts = &nf_icmpv6_pernet(info->net)->timeout;
4728866df92SPablo Neira Ayuso 		break;
4738866df92SPablo Neira Ayuso 	case IPPROTO_SCTP:
4748866df92SPablo Neira Ayuso #ifdef CONFIG_NF_CT_PROTO_SCTP
475a6555365SPablo Neira Ayuso 		timeouts = nf_sctp_pernet(info->net)->timeouts;
4768866df92SPablo Neira Ayuso #endif
4778866df92SPablo Neira Ayuso 		break;
47889259088SFlorian Westphal 	case IPPROTO_GRE:
47989259088SFlorian Westphal #ifdef CONFIG_NF_CT_PROTO_GRE
480a6555365SPablo Neira Ayuso 		timeouts = nf_gre_pernet(info->net)->timeouts;
48189259088SFlorian Westphal #endif
48289259088SFlorian Westphal 		break;
4838866df92SPablo Neira Ayuso 	case 255:
484a6555365SPablo Neira Ayuso 		timeouts = &nf_generic_pernet(info->net)->timeout;
4858866df92SPablo Neira Ayuso 		break;
4868866df92SPablo Neira Ayuso 	default:
48789259088SFlorian Westphal 		WARN_ONCE(1, "Missing timeouts for proto %d", l4proto->l4proto);
4888866df92SPablo Neira Ayuso 		break;
48991cb498eSPablo Neira Ayuso 	}
49091cb498eSPablo Neira Ayuso 
4918866df92SPablo Neira Ayuso 	if (!timeouts)
492e0241ae6SPablo Neira Ayuso 		return -EOPNOTSUPP;
4938866df92SPablo Neira Ayuso 
49491cb498eSPablo Neira Ayuso 	skb2 = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
495e0241ae6SPablo Neira Ayuso 	if (!skb2)
496e0241ae6SPablo Neira Ayuso 		return -ENOMEM;
49791cb498eSPablo Neira Ayuso 
498a6555365SPablo Neira Ayuso 	ret = cttimeout_default_fill_info(info->net, skb2,
499a6555365SPablo Neira Ayuso 					  NETLINK_CB(skb).portid,
500a6555365SPablo Neira Ayuso 					  info->nlh->nlmsg_seq,
501a6555365SPablo Neira Ayuso 					  NFNL_MSG_TYPE(info->nlh->nlmsg_type),
50291cb498eSPablo Neira Ayuso 					  IPCTNL_MSG_TIMEOUT_DEFAULT_SET,
5038866df92SPablo Neira Ayuso 					  l3num, l4proto, timeouts);
50491cb498eSPablo Neira Ayuso 	if (ret <= 0) {
50591cb498eSPablo Neira Ayuso 		kfree_skb(skb2);
506e0241ae6SPablo Neira Ayuso 		return -ENOMEM;
50791cb498eSPablo Neira Ayuso 	}
50891cb498eSPablo Neira Ayuso 
509e0241ae6SPablo Neira Ayuso 	return nfnetlink_unicast(skb2, info->net, NETLINK_CB(skb).portid);
51091cb498eSPablo Neira Ayuso }
51191cb498eSPablo Neira Ayuso 
ctnl_timeout_find_get(struct net * net,const char * name)51299e25d07SPablo Neira Ayuso static struct nf_ct_timeout *ctnl_timeout_find_get(struct net *net,
51399e25d07SPablo Neira Ayuso 						   const char *name)
51424de58f4SPablo Neira Ayuso {
515ebfbe675SFlorian Westphal 	struct nfct_timeout_pernet *pernet = nfct_timeout_pernet(net);
51624de58f4SPablo Neira Ayuso 	struct ctnl_timeout *timeout, *matching = NULL;
51724de58f4SPablo Neira Ayuso 
518ebfbe675SFlorian Westphal 	list_for_each_entry_rcu(timeout, &pernet->nfct_timeout_list, head) {
51924de58f4SPablo Neira Ayuso 		if (strncmp(timeout->name, name, CTNL_TIMEOUT_NAME_MAX) != 0)
52024de58f4SPablo Neira Ayuso 			continue;
52124de58f4SPablo Neira Ayuso 
522523895e5SFlorian Westphal 		if (!refcount_inc_not_zero(&timeout->refcnt))
52324de58f4SPablo Neira Ayuso 			goto err;
52424de58f4SPablo Neira Ayuso 		matching = timeout;
52524de58f4SPablo Neira Ayuso 		break;
52624de58f4SPablo Neira Ayuso 	}
52724de58f4SPablo Neira Ayuso err:
52899e25d07SPablo Neira Ayuso 	return matching ? &matching->timeout : NULL;
52924de58f4SPablo Neira Ayuso }
53024de58f4SPablo Neira Ayuso 
ctnl_timeout_put(struct nf_ct_timeout * t)5316c1fd7dcSPablo Neira Ayuso static void ctnl_timeout_put(struct nf_ct_timeout *t)
53224de58f4SPablo Neira Ayuso {
5336c1fd7dcSPablo Neira Ayuso 	struct ctnl_timeout *timeout =
5346c1fd7dcSPablo Neira Ayuso 		container_of(t, struct ctnl_timeout, timeout);
5356c1fd7dcSPablo Neira Ayuso 
536523895e5SFlorian Westphal 	if (refcount_dec_and_test(&timeout->refcnt)) {
537b75911b6SLiping Zhang 		kfree_rcu(timeout, rcu_head);
53824de58f4SPablo Neira Ayuso 		module_put(THIS_MODULE);
53924de58f4SPablo Neira Ayuso 	}
540523895e5SFlorian Westphal }
54124de58f4SPablo Neira Ayuso 
54250978462SPablo Neira Ayuso static const struct nfnl_callback cttimeout_cb[IPCTNL_MSG_TIMEOUT_MAX] = {
54350f2db9eSPablo Neira Ayuso 	[IPCTNL_MSG_TIMEOUT_NEW] = {
54450f2db9eSPablo Neira Ayuso 		.call		= cttimeout_new_timeout,
54550f2db9eSPablo Neira Ayuso 		.type		= NFNL_CB_MUTEX,
54650978462SPablo Neira Ayuso 		.attr_count	= CTA_TIMEOUT_MAX,
54750f2db9eSPablo Neira Ayuso 		.policy		= cttimeout_nla_policy
54850f2db9eSPablo Neira Ayuso 	},
54950f2db9eSPablo Neira Ayuso 	[IPCTNL_MSG_TIMEOUT_GET] = {
55050f2db9eSPablo Neira Ayuso 		.call		= cttimeout_get_timeout,
55150f2db9eSPablo Neira Ayuso 		.type		= NFNL_CB_MUTEX,
55250978462SPablo Neira Ayuso 		.attr_count	= CTA_TIMEOUT_MAX,
55350f2db9eSPablo Neira Ayuso 		.policy		= cttimeout_nla_policy
55450f2db9eSPablo Neira Ayuso 	},
55550f2db9eSPablo Neira Ayuso 	[IPCTNL_MSG_TIMEOUT_DELETE] = {
55650f2db9eSPablo Neira Ayuso 		.call		= cttimeout_del_timeout,
55750f2db9eSPablo Neira Ayuso 		.type		= NFNL_CB_MUTEX,
55850978462SPablo Neira Ayuso 		.attr_count	= CTA_TIMEOUT_MAX,
55950f2db9eSPablo Neira Ayuso 		.policy		= cttimeout_nla_policy
56050f2db9eSPablo Neira Ayuso 	},
56150f2db9eSPablo Neira Ayuso 	[IPCTNL_MSG_TIMEOUT_DEFAULT_SET] = {
56250f2db9eSPablo Neira Ayuso 		.call		= cttimeout_default_set,
56350f2db9eSPablo Neira Ayuso 		.type		= NFNL_CB_MUTEX,
56491cb498eSPablo Neira Ayuso 		.attr_count	= CTA_TIMEOUT_MAX,
56550f2db9eSPablo Neira Ayuso 		.policy		= cttimeout_nla_policy
56650f2db9eSPablo Neira Ayuso 	},
56750f2db9eSPablo Neira Ayuso 	[IPCTNL_MSG_TIMEOUT_DEFAULT_GET] = {
56850f2db9eSPablo Neira Ayuso 		.call		= cttimeout_default_get,
56950f2db9eSPablo Neira Ayuso 		.type		= NFNL_CB_MUTEX,
57091cb498eSPablo Neira Ayuso 		.attr_count	= CTA_TIMEOUT_MAX,
57150f2db9eSPablo Neira Ayuso 		.policy		= cttimeout_nla_policy
57250f2db9eSPablo Neira Ayuso 	},
57350978462SPablo Neira Ayuso };
57450978462SPablo Neira Ayuso 
57550978462SPablo Neira Ayuso static const struct nfnetlink_subsystem cttimeout_subsys = {
57650978462SPablo Neira Ayuso 	.name				= "conntrack_timeout",
57750978462SPablo Neira Ayuso 	.subsys_id			= NFNL_SUBSYS_CTNETLINK_TIMEOUT,
57850978462SPablo Neira Ayuso 	.cb_count			= IPCTNL_MSG_TIMEOUT_MAX,
57950978462SPablo Neira Ayuso 	.cb				= cttimeout_cb,
58050978462SPablo Neira Ayuso };
58150978462SPablo Neira Ayuso 
58250978462SPablo Neira Ayuso MODULE_ALIAS_NFNL_SUBSYS(NFNL_SUBSYS_CTNETLINK_TIMEOUT);
58350978462SPablo Neira Ayuso 
cttimeout_net_init(struct net * net)58419576c94SPablo Neira static int __net_init cttimeout_net_init(struct net *net)
58519576c94SPablo Neira {
586ebfbe675SFlorian Westphal 	struct nfct_timeout_pernet *pernet = nfct_timeout_pernet(net);
587ebfbe675SFlorian Westphal 
588ebfbe675SFlorian Westphal 	INIT_LIST_HEAD(&pernet->nfct_timeout_list);
58978222bacSFlorian Westphal 	INIT_LIST_HEAD(&pernet->nfct_timeout_freelist);
59019576c94SPablo Neira 
59119576c94SPablo Neira 	return 0;
59219576c94SPablo Neira }
59319576c94SPablo Neira 
cttimeout_net_pre_exit(struct net * net)59478222bacSFlorian Westphal static void __net_exit cttimeout_net_pre_exit(struct net *net)
59578222bacSFlorian Westphal {
59678222bacSFlorian Westphal 	struct nfct_timeout_pernet *pernet = nfct_timeout_pernet(net);
59778222bacSFlorian Westphal 	struct ctnl_timeout *cur, *tmp;
59878222bacSFlorian Westphal 
59978222bacSFlorian Westphal 	list_for_each_entry_safe(cur, tmp, &pernet->nfct_timeout_list, head) {
60078222bacSFlorian Westphal 		list_del_rcu(&cur->head);
60178222bacSFlorian Westphal 		list_add(&cur->free_head, &pernet->nfct_timeout_freelist);
60278222bacSFlorian Westphal 	}
60378222bacSFlorian Westphal 
60478222bacSFlorian Westphal 	/* core calls synchronize_rcu() after this */
60578222bacSFlorian Westphal }
60678222bacSFlorian Westphal 
cttimeout_net_exit(struct net * net)60719576c94SPablo Neira static void __net_exit cttimeout_net_exit(struct net *net)
60819576c94SPablo Neira {
609ebfbe675SFlorian Westphal 	struct nfct_timeout_pernet *pernet = nfct_timeout_pernet(net);
61019576c94SPablo Neira 	struct ctnl_timeout *cur, *tmp;
61119576c94SPablo Neira 
61217438b42SFlorian Westphal 	if (list_empty(&pernet->nfct_timeout_freelist))
61317438b42SFlorian Westphal 		return;
61417438b42SFlorian Westphal 
6154e665afbSHarsha Sharma 	nf_ct_untimeout(net, NULL);
61619576c94SPablo Neira 
617*394e7716SFlorian Westphal 	list_for_each_entry_safe(cur, tmp, &pernet->nfct_timeout_freelist, free_head) {
61878222bacSFlorian Westphal 		list_del(&cur->free_head);
619b75911b6SLiping Zhang 
620b54ab92bSReshetova, Elena 		if (refcount_dec_and_test(&cur->refcnt))
62119576c94SPablo Neira 			kfree_rcu(cur, rcu_head);
62219576c94SPablo Neira 	}
62319576c94SPablo Neira }
62419576c94SPablo Neira 
62519576c94SPablo Neira static struct pernet_operations cttimeout_ops = {
62619576c94SPablo Neira 	.init	= cttimeout_net_init,
62778222bacSFlorian Westphal 	.pre_exit = cttimeout_net_pre_exit,
62819576c94SPablo Neira 	.exit	= cttimeout_net_exit,
629ebfbe675SFlorian Westphal 	.id     = &nfct_timeout_id,
630ebfbe675SFlorian Westphal 	.size   = sizeof(struct nfct_timeout_pernet),
63119576c94SPablo Neira };
63219576c94SPablo Neira 
6337afa3883SFlorian Westphal static const struct nf_ct_timeout_hooks hooks = {
6347afa3883SFlorian Westphal 	.timeout_find_get = ctnl_timeout_find_get,
6357afa3883SFlorian Westphal 	.timeout_put = ctnl_timeout_put,
6367afa3883SFlorian Westphal };
6377afa3883SFlorian Westphal 
cttimeout_init(void)63850978462SPablo Neira Ayuso static int __init cttimeout_init(void)
63950978462SPablo Neira Ayuso {
64050978462SPablo Neira Ayuso 	int ret;
64150978462SPablo Neira Ayuso 
64219576c94SPablo Neira 	ret = register_pernet_subsys(&cttimeout_ops);
64319576c94SPablo Neira 	if (ret < 0)
64419576c94SPablo Neira 		return ret;
64519576c94SPablo Neira 
64650978462SPablo Neira Ayuso 	ret = nfnetlink_subsys_register(&cttimeout_subsys);
64750978462SPablo Neira Ayuso 	if (ret < 0) {
64850978462SPablo Neira Ayuso 		pr_err("cttimeout_init: cannot register cttimeout with "
64950978462SPablo Neira Ayuso 			"nfnetlink.\n");
65050978462SPablo Neira Ayuso 		goto err_out;
65150978462SPablo Neira Ayuso 	}
6527afa3883SFlorian Westphal 	RCU_INIT_POINTER(nf_ct_timeout_hook, &hooks);
65350978462SPablo Neira Ayuso 	return 0;
65450978462SPablo Neira Ayuso 
65550978462SPablo Neira Ayuso err_out:
65619576c94SPablo Neira 	unregister_pernet_subsys(&cttimeout_ops);
65750978462SPablo Neira Ayuso 	return ret;
65850978462SPablo Neira Ayuso }
65950978462SPablo Neira Ayuso 
untimeout(struct nf_conn * ct,void * timeout)66042df4fb9SFlorian Westphal static int untimeout(struct nf_conn *ct, void *timeout)
66142df4fb9SFlorian Westphal {
66242df4fb9SFlorian Westphal 	struct nf_conn_timeout *timeout_ext = nf_ct_timeout_find(ct);
66342df4fb9SFlorian Westphal 
66442df4fb9SFlorian Westphal 	if (timeout_ext)
66542df4fb9SFlorian Westphal 		RCU_INIT_POINTER(timeout_ext->timeout, NULL);
66642df4fb9SFlorian Westphal 
66742df4fb9SFlorian Westphal 	return 0;
66842df4fb9SFlorian Westphal }
66942df4fb9SFlorian Westphal 
cttimeout_exit(void)67050978462SPablo Neira Ayuso static void __exit cttimeout_exit(void)
67150978462SPablo Neira Ayuso {
67250978462SPablo Neira Ayuso 	nfnetlink_subsys_unregister(&cttimeout_subsys);
673ae2d708eSPablo Neira Ayuso 
67419576c94SPablo Neira 	unregister_pernet_subsys(&cttimeout_ops);
6757afa3883SFlorian Westphal 	RCU_INIT_POINTER(nf_ct_timeout_hook, NULL);
67642df4fb9SFlorian Westphal 
67742df4fb9SFlorian Westphal 	nf_ct_iterate_destroy(untimeout, NULL);
67850978462SPablo Neira Ayuso }
67950978462SPablo Neira Ayuso 
68050978462SPablo Neira Ayuso module_init(cttimeout_init);
68150978462SPablo Neira Ayuso module_exit(cttimeout_exit);
682