150978462SPablo Neira Ayuso /*
250978462SPablo Neira Ayuso  * (C) 2012 by Pablo Neira Ayuso <pablo@netfilter.org>
350978462SPablo Neira Ayuso  * (C) 2012 by Vyatta Inc. <http://www.vyatta.com>
450978462SPablo Neira Ayuso  *
550978462SPablo Neira Ayuso  * This program is free software; you can redistribute it and/or modify
650978462SPablo Neira Ayuso  * it under the terms of the GNU General Public License version 2 as
750978462SPablo Neira Ayuso  * published by the Free Software Foundation (or any later at your option).
850978462SPablo Neira Ayuso  */
950978462SPablo Neira Ayuso #include <linux/init.h>
1050978462SPablo Neira Ayuso #include <linux/module.h>
1150978462SPablo Neira Ayuso #include <linux/kernel.h>
1250978462SPablo Neira Ayuso #include <linux/rculist.h>
1350978462SPablo Neira Ayuso #include <linux/rculist_nulls.h>
1450978462SPablo Neira Ayuso #include <linux/types.h>
1550978462SPablo Neira Ayuso #include <linux/timer.h>
1650978462SPablo Neira Ayuso #include <linux/security.h>
1750978462SPablo Neira Ayuso #include <linux/skbuff.h>
1850978462SPablo Neira Ayuso #include <linux/errno.h>
1950978462SPablo Neira Ayuso #include <linux/netlink.h>
2050978462SPablo Neira Ayuso #include <linux/spinlock.h>
2150978462SPablo Neira Ayuso #include <linux/interrupt.h>
2250978462SPablo Neira Ayuso #include <linux/slab.h>
2350978462SPablo Neira Ayuso 
2450978462SPablo Neira Ayuso #include <linux/netfilter.h>
2550978462SPablo Neira Ayuso #include <net/netlink.h>
2650978462SPablo Neira Ayuso #include <net/sock.h>
2750978462SPablo Neira Ayuso #include <net/netfilter/nf_conntrack.h>
2850978462SPablo Neira Ayuso #include <net/netfilter/nf_conntrack_core.h>
2950978462SPablo Neira Ayuso #include <net/netfilter/nf_conntrack_l3proto.h>
3050978462SPablo Neira Ayuso #include <net/netfilter/nf_conntrack_l4proto.h>
3150978462SPablo Neira Ayuso #include <net/netfilter/nf_conntrack_tuple.h>
3250978462SPablo Neira Ayuso 
3350978462SPablo Neira Ayuso #include <linux/netfilter/nfnetlink.h>
3450978462SPablo Neira Ayuso #include <linux/netfilter/nfnetlink_cttimeout.h>
3550978462SPablo Neira Ayuso 
3650978462SPablo Neira Ayuso MODULE_LICENSE("GPL");
3750978462SPablo Neira Ayuso MODULE_AUTHOR("Pablo Neira Ayuso <pablo@netfilter.org>");
3850978462SPablo Neira Ayuso MODULE_DESCRIPTION("cttimeout: Extended Netfilter Connection Tracking timeout tuning");
3950978462SPablo Neira Ayuso 
4050978462SPablo Neira Ayuso struct ctnl_timeout {
4150978462SPablo Neira Ayuso 	struct list_head	head;
4250978462SPablo Neira Ayuso 	struct rcu_head		rcu_head;
4350978462SPablo Neira Ayuso 	atomic_t		refcnt;
4450978462SPablo Neira Ayuso 	char			name[CTNL_TIMEOUT_NAME_MAX];
4550978462SPablo Neira Ayuso 	__u16			l3num;
4650978462SPablo Neira Ayuso 	__u8			l4num;
4750978462SPablo Neira Ayuso 	char			data[0];
4850978462SPablo Neira Ayuso };
4950978462SPablo Neira Ayuso 
5050978462SPablo Neira Ayuso static LIST_HEAD(cttimeout_list);
5150978462SPablo Neira Ayuso 
5250978462SPablo Neira Ayuso static const struct nla_policy cttimeout_nla_policy[CTA_TIMEOUT_MAX+1] = {
5350978462SPablo Neira Ayuso 	[CTA_TIMEOUT_NAME]	= { .type = NLA_NUL_STRING },
5450978462SPablo Neira Ayuso 	[CTA_TIMEOUT_L3PROTO]	= { .type = NLA_U16 },
5550978462SPablo Neira Ayuso 	[CTA_TIMEOUT_L4PROTO]	= { .type = NLA_U8 },
5650978462SPablo Neira Ayuso 	[CTA_TIMEOUT_DATA]	= { .type = NLA_NESTED },
5750978462SPablo Neira Ayuso };
5850978462SPablo Neira Ayuso 
5950978462SPablo Neira Ayuso static int
6050978462SPablo Neira Ayuso ctnl_timeout_parse_policy(struct ctnl_timeout *timeout,
6150978462SPablo Neira Ayuso 			       struct nf_conntrack_l4proto *l4proto,
6250978462SPablo Neira Ayuso 			       const struct nlattr *attr)
6350978462SPablo Neira Ayuso {
6450978462SPablo Neira Ayuso 	int ret = 0;
6550978462SPablo Neira Ayuso 
6650978462SPablo Neira Ayuso 	if (likely(l4proto->ctnl_timeout.nlattr_to_obj)) {
6750978462SPablo Neira Ayuso 		struct nlattr *tb[l4proto->ctnl_timeout.nlattr_max+1];
6850978462SPablo Neira Ayuso 
6950978462SPablo Neira Ayuso 		nla_parse_nested(tb, l4proto->ctnl_timeout.nlattr_max,
7050978462SPablo Neira Ayuso 				 attr, l4proto->ctnl_timeout.nla_policy);
7150978462SPablo Neira Ayuso 
7250978462SPablo Neira Ayuso 		ret = l4proto->ctnl_timeout.nlattr_to_obj(tb, &timeout->data);
7350978462SPablo Neira Ayuso 	}
7450978462SPablo Neira Ayuso 	return ret;
7550978462SPablo Neira Ayuso }
7650978462SPablo Neira Ayuso 
7750978462SPablo Neira Ayuso static int
7850978462SPablo Neira Ayuso cttimeout_new_timeout(struct sock *ctnl, struct sk_buff *skb,
7950978462SPablo Neira Ayuso 		      const struct nlmsghdr *nlh,
8050978462SPablo Neira Ayuso 		      const struct nlattr * const cda[])
8150978462SPablo Neira Ayuso {
8250978462SPablo Neira Ayuso 	__u16 l3num;
8350978462SPablo Neira Ayuso 	__u8 l4num;
8450978462SPablo Neira Ayuso 	struct nf_conntrack_l4proto *l4proto;
8550978462SPablo Neira Ayuso 	struct ctnl_timeout *timeout, *matching = NULL;
8650978462SPablo Neira Ayuso 	char *name;
8750978462SPablo Neira Ayuso 	int ret;
8850978462SPablo Neira Ayuso 
8950978462SPablo Neira Ayuso 	if (!cda[CTA_TIMEOUT_NAME] ||
9050978462SPablo Neira Ayuso 	    !cda[CTA_TIMEOUT_L3PROTO] ||
9150978462SPablo Neira Ayuso 	    !cda[CTA_TIMEOUT_L4PROTO] ||
9250978462SPablo Neira Ayuso 	    !cda[CTA_TIMEOUT_DATA])
9350978462SPablo Neira Ayuso 		return -EINVAL;
9450978462SPablo Neira Ayuso 
9550978462SPablo Neira Ayuso 	name = nla_data(cda[CTA_TIMEOUT_NAME]);
9650978462SPablo Neira Ayuso 	l3num = ntohs(nla_get_be16(cda[CTA_TIMEOUT_L3PROTO]));
9750978462SPablo Neira Ayuso 	l4num = nla_get_u8(cda[CTA_TIMEOUT_L4PROTO]);
9850978462SPablo Neira Ayuso 
9950978462SPablo Neira Ayuso 	list_for_each_entry(timeout, &cttimeout_list, head) {
10050978462SPablo Neira Ayuso 		if (strncmp(timeout->name, name, CTNL_TIMEOUT_NAME_MAX) != 0)
10150978462SPablo Neira Ayuso 			continue;
10250978462SPablo Neira Ayuso 
10350978462SPablo Neira Ayuso 		if (nlh->nlmsg_flags & NLM_F_EXCL)
10450978462SPablo Neira Ayuso 			return -EEXIST;
10550978462SPablo Neira Ayuso 
10650978462SPablo Neira Ayuso 		matching = timeout;
10750978462SPablo Neira Ayuso 		break;
10850978462SPablo Neira Ayuso 	}
10950978462SPablo Neira Ayuso 
11050978462SPablo Neira Ayuso 	l4proto = __nf_ct_l4proto_find(l3num, l4num);
11150978462SPablo Neira Ayuso 
11250978462SPablo Neira Ayuso 	/* This protocol is not supportted, skip. */
11350978462SPablo Neira Ayuso 	if (l4proto->l4proto != l4num)
11450978462SPablo Neira Ayuso 		return -EOPNOTSUPP;
11550978462SPablo Neira Ayuso 
11650978462SPablo Neira Ayuso 	if (matching) {
11750978462SPablo Neira Ayuso 		if (nlh->nlmsg_flags & NLM_F_REPLACE) {
11850978462SPablo Neira Ayuso 			/* You cannot replace one timeout policy by another of
11950978462SPablo Neira Ayuso 			 * different kind, sorry.
12050978462SPablo Neira Ayuso 			 */
12150978462SPablo Neira Ayuso 			if (matching->l3num != l3num ||
12250978462SPablo Neira Ayuso 			    matching->l4num != l4num)
12350978462SPablo Neira Ayuso 				return -EINVAL;
12450978462SPablo Neira Ayuso 
12550978462SPablo Neira Ayuso 			ret = ctnl_timeout_parse_policy(matching, l4proto,
12650978462SPablo Neira Ayuso 							cda[CTA_TIMEOUT_DATA]);
12750978462SPablo Neira Ayuso 			return ret;
12850978462SPablo Neira Ayuso 		}
12950978462SPablo Neira Ayuso 		return -EBUSY;
13050978462SPablo Neira Ayuso 	}
13150978462SPablo Neira Ayuso 
13250978462SPablo Neira Ayuso 	timeout = kzalloc(sizeof(struct ctnl_timeout) +
13350978462SPablo Neira Ayuso 			  l4proto->ctnl_timeout.obj_size, GFP_KERNEL);
13450978462SPablo Neira Ayuso 	if (timeout == NULL)
13550978462SPablo Neira Ayuso 		return -ENOMEM;
13650978462SPablo Neira Ayuso 
13750978462SPablo Neira Ayuso 	ret = ctnl_timeout_parse_policy(timeout, l4proto,
13850978462SPablo Neira Ayuso 					cda[CTA_TIMEOUT_DATA]);
13950978462SPablo Neira Ayuso 	if (ret < 0)
14050978462SPablo Neira Ayuso 		goto err;
14150978462SPablo Neira Ayuso 
14250978462SPablo Neira Ayuso 	strcpy(timeout->name, nla_data(cda[CTA_TIMEOUT_NAME]));
14350978462SPablo Neira Ayuso 	timeout->l3num = l3num;
14450978462SPablo Neira Ayuso 	timeout->l4num = l4num;
14550978462SPablo Neira Ayuso 	atomic_set(&timeout->refcnt, 1);
14650978462SPablo Neira Ayuso 	list_add_tail_rcu(&timeout->head, &cttimeout_list);
14750978462SPablo Neira Ayuso 
14850978462SPablo Neira Ayuso 	return 0;
14950978462SPablo Neira Ayuso err:
15050978462SPablo Neira Ayuso 	kfree(timeout);
15150978462SPablo Neira Ayuso 	return ret;
15250978462SPablo Neira Ayuso }
15350978462SPablo Neira Ayuso 
15450978462SPablo Neira Ayuso static int
15550978462SPablo Neira Ayuso ctnl_timeout_fill_info(struct sk_buff *skb, u32 pid, u32 seq, u32 type,
15650978462SPablo Neira Ayuso 		       int event, struct ctnl_timeout *timeout)
15750978462SPablo Neira Ayuso {
15850978462SPablo Neira Ayuso 	struct nlmsghdr *nlh;
15950978462SPablo Neira Ayuso 	struct nfgenmsg *nfmsg;
16050978462SPablo Neira Ayuso 	unsigned int flags = pid ? NLM_F_MULTI : 0;
16150978462SPablo Neira Ayuso 	struct nf_conntrack_l4proto *l4proto;
16250978462SPablo Neira Ayuso 
16350978462SPablo Neira Ayuso 	event |= NFNL_SUBSYS_CTNETLINK_TIMEOUT << 8;
16450978462SPablo Neira Ayuso 	nlh = nlmsg_put(skb, pid, seq, event, sizeof(*nfmsg), flags);
16550978462SPablo Neira Ayuso 	if (nlh == NULL)
16650978462SPablo Neira Ayuso 		goto nlmsg_failure;
16750978462SPablo Neira Ayuso 
16850978462SPablo Neira Ayuso 	nfmsg = nlmsg_data(nlh);
16950978462SPablo Neira Ayuso 	nfmsg->nfgen_family = AF_UNSPEC;
17050978462SPablo Neira Ayuso 	nfmsg->version = NFNETLINK_V0;
17150978462SPablo Neira Ayuso 	nfmsg->res_id = 0;
17250978462SPablo Neira Ayuso 
17350978462SPablo Neira Ayuso 	NLA_PUT_STRING(skb, CTA_TIMEOUT_NAME, timeout->name);
17450978462SPablo Neira Ayuso 	NLA_PUT_BE16(skb, CTA_TIMEOUT_L3PROTO, htons(timeout->l3num));
17550978462SPablo Neira Ayuso 	NLA_PUT_U8(skb, CTA_TIMEOUT_L4PROTO, timeout->l4num);
17650978462SPablo Neira Ayuso 	NLA_PUT_BE32(skb, CTA_TIMEOUT_USE,
17750978462SPablo Neira Ayuso 			htonl(atomic_read(&timeout->refcnt)));
17850978462SPablo Neira Ayuso 
17950978462SPablo Neira Ayuso 	l4proto = __nf_ct_l4proto_find(timeout->l3num, timeout->l4num);
18050978462SPablo Neira Ayuso 
18150978462SPablo Neira Ayuso 	/* If the timeout object does not match the layer 4 protocol tracker,
18250978462SPablo Neira Ayuso 	 * then skip dumping the data part since we don't know how to
18350978462SPablo Neira Ayuso 	 * interpret it. This may happen for UPDlite, SCTP and DCCP since
18450978462SPablo Neira Ayuso 	 * you can unload the module.
18550978462SPablo Neira Ayuso 	 */
18650978462SPablo Neira Ayuso 	if (timeout->l4num != l4proto->l4proto)
18750978462SPablo Neira Ayuso 		goto out;
18850978462SPablo Neira Ayuso 
18950978462SPablo Neira Ayuso 	if (likely(l4proto->ctnl_timeout.obj_to_nlattr)) {
19050978462SPablo Neira Ayuso 		struct nlattr *nest_parms;
19150978462SPablo Neira Ayuso 		int ret;
19250978462SPablo Neira Ayuso 
19350978462SPablo Neira Ayuso 		nest_parms = nla_nest_start(skb,
19450978462SPablo Neira Ayuso 					    CTA_TIMEOUT_DATA | NLA_F_NESTED);
19550978462SPablo Neira Ayuso 		if (!nest_parms)
19650978462SPablo Neira Ayuso 			goto nla_put_failure;
19750978462SPablo Neira Ayuso 
19850978462SPablo Neira Ayuso 		ret = l4proto->ctnl_timeout.obj_to_nlattr(skb, &timeout->data);
19950978462SPablo Neira Ayuso 		if (ret < 0)
20050978462SPablo Neira Ayuso 			goto nla_put_failure;
20150978462SPablo Neira Ayuso 
20250978462SPablo Neira Ayuso 		nla_nest_end(skb, nest_parms);
20350978462SPablo Neira Ayuso 	}
20450978462SPablo Neira Ayuso out:
20550978462SPablo Neira Ayuso 	nlmsg_end(skb, nlh);
20650978462SPablo Neira Ayuso 	return skb->len;
20750978462SPablo Neira Ayuso 
20850978462SPablo Neira Ayuso nlmsg_failure:
20950978462SPablo Neira Ayuso nla_put_failure:
21050978462SPablo Neira Ayuso 	nlmsg_cancel(skb, nlh);
21150978462SPablo Neira Ayuso 	return -1;
21250978462SPablo Neira Ayuso }
21350978462SPablo Neira Ayuso 
21450978462SPablo Neira Ayuso static int
21550978462SPablo Neira Ayuso ctnl_timeout_dump(struct sk_buff *skb, struct netlink_callback *cb)
21650978462SPablo Neira Ayuso {
21750978462SPablo Neira Ayuso 	struct ctnl_timeout *cur, *last;
21850978462SPablo Neira Ayuso 
21950978462SPablo Neira Ayuso 	if (cb->args[2])
22050978462SPablo Neira Ayuso 		return 0;
22150978462SPablo Neira Ayuso 
22250978462SPablo Neira Ayuso 	last = (struct ctnl_timeout *)cb->args[1];
22350978462SPablo Neira Ayuso 	if (cb->args[1])
22450978462SPablo Neira Ayuso 		cb->args[1] = 0;
22550978462SPablo Neira Ayuso 
22650978462SPablo Neira Ayuso 	rcu_read_lock();
22750978462SPablo Neira Ayuso 	list_for_each_entry_rcu(cur, &cttimeout_list, head) {
22850978462SPablo Neira Ayuso 		if (last && cur != last)
22950978462SPablo Neira Ayuso 			continue;
23050978462SPablo Neira Ayuso 
23150978462SPablo Neira Ayuso 		if (ctnl_timeout_fill_info(skb, NETLINK_CB(cb->skb).pid,
23250978462SPablo Neira Ayuso 					   cb->nlh->nlmsg_seq,
23350978462SPablo Neira Ayuso 					   NFNL_MSG_TYPE(cb->nlh->nlmsg_type),
23450978462SPablo Neira Ayuso 					   IPCTNL_MSG_TIMEOUT_NEW, cur) < 0) {
23550978462SPablo Neira Ayuso 			cb->args[1] = (unsigned long)cur;
23650978462SPablo Neira Ayuso 			break;
23750978462SPablo Neira Ayuso 		}
23850978462SPablo Neira Ayuso 	}
23950978462SPablo Neira Ayuso 	if (!cb->args[1])
24050978462SPablo Neira Ayuso 		cb->args[2] = 1;
24150978462SPablo Neira Ayuso 	rcu_read_unlock();
24250978462SPablo Neira Ayuso 	return skb->len;
24350978462SPablo Neira Ayuso }
24450978462SPablo Neira Ayuso 
24550978462SPablo Neira Ayuso static int
24650978462SPablo Neira Ayuso cttimeout_get_timeout(struct sock *ctnl, struct sk_buff *skb,
24750978462SPablo Neira Ayuso 		      const struct nlmsghdr *nlh,
24850978462SPablo Neira Ayuso 		      const struct nlattr * const cda[])
24950978462SPablo Neira Ayuso {
25050978462SPablo Neira Ayuso 	int ret = -ENOENT;
25150978462SPablo Neira Ayuso 	char *name;
25250978462SPablo Neira Ayuso 	struct ctnl_timeout *cur;
25350978462SPablo Neira Ayuso 
25450978462SPablo Neira Ayuso 	if (nlh->nlmsg_flags & NLM_F_DUMP) {
25550978462SPablo Neira Ayuso 		struct netlink_dump_control c = {
25650978462SPablo Neira Ayuso 			.dump = ctnl_timeout_dump,
25750978462SPablo Neira Ayuso 		};
25850978462SPablo Neira Ayuso 		return netlink_dump_start(ctnl, skb, nlh, &c);
25950978462SPablo Neira Ayuso 	}
26050978462SPablo Neira Ayuso 
26150978462SPablo Neira Ayuso 	if (!cda[CTA_TIMEOUT_NAME])
26250978462SPablo Neira Ayuso 		return -EINVAL;
26350978462SPablo Neira Ayuso 	name = nla_data(cda[CTA_TIMEOUT_NAME]);
26450978462SPablo Neira Ayuso 
26550978462SPablo Neira Ayuso 	list_for_each_entry(cur, &cttimeout_list, head) {
26650978462SPablo Neira Ayuso 		struct sk_buff *skb2;
26750978462SPablo Neira Ayuso 
26850978462SPablo Neira Ayuso 		if (strncmp(cur->name, name, CTNL_TIMEOUT_NAME_MAX) != 0)
26950978462SPablo Neira Ayuso 			continue;
27050978462SPablo Neira Ayuso 
27150978462SPablo Neira Ayuso 		skb2 = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
27250978462SPablo Neira Ayuso 		if (skb2 == NULL) {
27350978462SPablo Neira Ayuso 			ret = -ENOMEM;
27450978462SPablo Neira Ayuso 			break;
27550978462SPablo Neira Ayuso 		}
27650978462SPablo Neira Ayuso 
27750978462SPablo Neira Ayuso 		ret = ctnl_timeout_fill_info(skb2, NETLINK_CB(skb).pid,
27850978462SPablo Neira Ayuso 					     nlh->nlmsg_seq,
27950978462SPablo Neira Ayuso 					     NFNL_MSG_TYPE(nlh->nlmsg_type),
28050978462SPablo Neira Ayuso 					     IPCTNL_MSG_TIMEOUT_NEW, cur);
28150978462SPablo Neira Ayuso 		if (ret <= 0) {
28250978462SPablo Neira Ayuso 			kfree_skb(skb2);
28350978462SPablo Neira Ayuso 			break;
28450978462SPablo Neira Ayuso 		}
28550978462SPablo Neira Ayuso 		ret = netlink_unicast(ctnl, skb2, NETLINK_CB(skb).pid,
28650978462SPablo Neira Ayuso 					MSG_DONTWAIT);
28750978462SPablo Neira Ayuso 		if (ret > 0)
28850978462SPablo Neira Ayuso 			ret = 0;
28950978462SPablo Neira Ayuso 
29050978462SPablo Neira Ayuso 		/* this avoids a loop in nfnetlink. */
29150978462SPablo Neira Ayuso 		return ret == -EAGAIN ? -ENOBUFS : ret;
29250978462SPablo Neira Ayuso 	}
29350978462SPablo Neira Ayuso 	return ret;
29450978462SPablo Neira Ayuso }
29550978462SPablo Neira Ayuso 
29650978462SPablo Neira Ayuso /* try to delete object, fail if it is still in use. */
29750978462SPablo Neira Ayuso static int ctnl_timeout_try_del(struct ctnl_timeout *timeout)
29850978462SPablo Neira Ayuso {
29950978462SPablo Neira Ayuso 	int ret = 0;
30050978462SPablo Neira Ayuso 
30150978462SPablo Neira Ayuso 	/* we want to avoid races with nf_ct_timeout_find_get. */
30250978462SPablo Neira Ayuso 	if (atomic_dec_and_test(&timeout->refcnt)) {
30350978462SPablo Neira Ayuso 		/* We are protected by nfnl mutex. */
30450978462SPablo Neira Ayuso 		list_del_rcu(&timeout->head);
30550978462SPablo Neira Ayuso 		kfree_rcu(timeout, rcu_head);
30650978462SPablo Neira Ayuso 	} else {
30750978462SPablo Neira Ayuso 		/* still in use, restore reference counter. */
30850978462SPablo Neira Ayuso 		atomic_inc(&timeout->refcnt);
30950978462SPablo Neira Ayuso 		ret = -EBUSY;
31050978462SPablo Neira Ayuso 	}
31150978462SPablo Neira Ayuso 	return ret;
31250978462SPablo Neira Ayuso }
31350978462SPablo Neira Ayuso 
31450978462SPablo Neira Ayuso static int
31550978462SPablo Neira Ayuso cttimeout_del_timeout(struct sock *ctnl, struct sk_buff *skb,
31650978462SPablo Neira Ayuso 		      const struct nlmsghdr *nlh,
31750978462SPablo Neira Ayuso 		      const struct nlattr * const cda[])
31850978462SPablo Neira Ayuso {
31950978462SPablo Neira Ayuso 	char *name;
32050978462SPablo Neira Ayuso 	struct ctnl_timeout *cur;
32150978462SPablo Neira Ayuso 	int ret = -ENOENT;
32250978462SPablo Neira Ayuso 
32350978462SPablo Neira Ayuso 	if (!cda[CTA_TIMEOUT_NAME]) {
32450978462SPablo Neira Ayuso 		list_for_each_entry(cur, &cttimeout_list, head)
32550978462SPablo Neira Ayuso 			ctnl_timeout_try_del(cur);
32650978462SPablo Neira Ayuso 
32750978462SPablo Neira Ayuso 		return 0;
32850978462SPablo Neira Ayuso 	}
32950978462SPablo Neira Ayuso 	name = nla_data(cda[CTA_TIMEOUT_NAME]);
33050978462SPablo Neira Ayuso 
33150978462SPablo Neira Ayuso 	list_for_each_entry(cur, &cttimeout_list, head) {
33250978462SPablo Neira Ayuso 		if (strncmp(cur->name, name, CTNL_TIMEOUT_NAME_MAX) != 0)
33350978462SPablo Neira Ayuso 			continue;
33450978462SPablo Neira Ayuso 
33550978462SPablo Neira Ayuso 		ret = ctnl_timeout_try_del(cur);
33650978462SPablo Neira Ayuso 		if (ret < 0)
33750978462SPablo Neira Ayuso 			return ret;
33850978462SPablo Neira Ayuso 
33950978462SPablo Neira Ayuso 		break;
34050978462SPablo Neira Ayuso 	}
34150978462SPablo Neira Ayuso 	return ret;
34250978462SPablo Neira Ayuso }
34350978462SPablo Neira Ayuso 
34450978462SPablo Neira Ayuso static const struct nfnl_callback cttimeout_cb[IPCTNL_MSG_TIMEOUT_MAX] = {
34550978462SPablo Neira Ayuso 	[IPCTNL_MSG_TIMEOUT_NEW]	= { .call = cttimeout_new_timeout,
34650978462SPablo Neira Ayuso 					    .attr_count = CTA_TIMEOUT_MAX,
34750978462SPablo Neira Ayuso 					    .policy = cttimeout_nla_policy },
34850978462SPablo Neira Ayuso 	[IPCTNL_MSG_TIMEOUT_GET]	= { .call = cttimeout_get_timeout,
34950978462SPablo Neira Ayuso 					    .attr_count = CTA_TIMEOUT_MAX,
35050978462SPablo Neira Ayuso 					    .policy = cttimeout_nla_policy },
35150978462SPablo Neira Ayuso 	[IPCTNL_MSG_TIMEOUT_DELETE]	= { .call = cttimeout_del_timeout,
35250978462SPablo Neira Ayuso 					    .attr_count = CTA_TIMEOUT_MAX,
35350978462SPablo Neira Ayuso 					    .policy = cttimeout_nla_policy },
35450978462SPablo Neira Ayuso };
35550978462SPablo Neira Ayuso 
35650978462SPablo Neira Ayuso static const struct nfnetlink_subsystem cttimeout_subsys = {
35750978462SPablo Neira Ayuso 	.name				= "conntrack_timeout",
35850978462SPablo Neira Ayuso 	.subsys_id			= NFNL_SUBSYS_CTNETLINK_TIMEOUT,
35950978462SPablo Neira Ayuso 	.cb_count			= IPCTNL_MSG_TIMEOUT_MAX,
36050978462SPablo Neira Ayuso 	.cb				= cttimeout_cb,
36150978462SPablo Neira Ayuso };
36250978462SPablo Neira Ayuso 
36350978462SPablo Neira Ayuso MODULE_ALIAS_NFNL_SUBSYS(NFNL_SUBSYS_CTNETLINK_TIMEOUT);
36450978462SPablo Neira Ayuso 
36550978462SPablo Neira Ayuso static int __init cttimeout_init(void)
36650978462SPablo Neira Ayuso {
36750978462SPablo Neira Ayuso 	int ret;
36850978462SPablo Neira Ayuso 
36950978462SPablo Neira Ayuso 	ret = nfnetlink_subsys_register(&cttimeout_subsys);
37050978462SPablo Neira Ayuso 	if (ret < 0) {
37150978462SPablo Neira Ayuso 		pr_err("cttimeout_init: cannot register cttimeout with "
37250978462SPablo Neira Ayuso 			"nfnetlink.\n");
37350978462SPablo Neira Ayuso 		goto err_out;
37450978462SPablo Neira Ayuso 	}
37550978462SPablo Neira Ayuso 	return 0;
37650978462SPablo Neira Ayuso 
37750978462SPablo Neira Ayuso err_out:
37850978462SPablo Neira Ayuso 	return ret;
37950978462SPablo Neira Ayuso }
38050978462SPablo Neira Ayuso 
38150978462SPablo Neira Ayuso static void __exit cttimeout_exit(void)
38250978462SPablo Neira Ayuso {
38350978462SPablo Neira Ayuso 	struct ctnl_timeout *cur, *tmp;
38450978462SPablo Neira Ayuso 
38550978462SPablo Neira Ayuso 	pr_info("cttimeout: unregistering from nfnetlink.\n");
38650978462SPablo Neira Ayuso 
38750978462SPablo Neira Ayuso 	nfnetlink_subsys_unregister(&cttimeout_subsys);
38850978462SPablo Neira Ayuso 	list_for_each_entry_safe(cur, tmp, &cttimeout_list, head) {
38950978462SPablo Neira Ayuso 		list_del_rcu(&cur->head);
39050978462SPablo Neira Ayuso 		/* We are sure that our objects have no clients at this point,
39150978462SPablo Neira Ayuso 		 * it's safe to release them all without checking refcnt.
39250978462SPablo Neira Ayuso 		 */
39350978462SPablo Neira Ayuso 		kfree_rcu(cur, rcu_head);
39450978462SPablo Neira Ayuso 	}
39550978462SPablo Neira Ayuso }
39650978462SPablo Neira Ayuso 
39750978462SPablo Neira Ayuso module_init(cttimeout_init);
39850978462SPablo Neira Ayuso module_exit(cttimeout_exit);
399