xref: /openbmc/linux/net/sched/act_api.c (revision 55334a5db5cd32b207ac697cec3ec8e078f345d4)
11da177e4SLinus Torvalds /*
21da177e4SLinus Torvalds  * net/sched/act_api.c	Packet action API.
31da177e4SLinus Torvalds  *
41da177e4SLinus Torvalds  *		This program is free software; you can redistribute it and/or
51da177e4SLinus Torvalds  *		modify it under the terms of the GNU General Public License
61da177e4SLinus Torvalds  *		as published by the Free Software Foundation; either version
71da177e4SLinus Torvalds  *		2 of the License, or (at your option) any later version.
81da177e4SLinus Torvalds  *
91da177e4SLinus Torvalds  * Author:	Jamal Hadi Salim
101da177e4SLinus Torvalds  *
111da177e4SLinus Torvalds  *
121da177e4SLinus Torvalds  */
131da177e4SLinus Torvalds 
141da177e4SLinus Torvalds #include <linux/types.h>
151da177e4SLinus Torvalds #include <linux/kernel.h>
161da177e4SLinus Torvalds #include <linux/string.h>
171da177e4SLinus Torvalds #include <linux/errno.h>
185a0e3ad6STejun Heo #include <linux/slab.h>
191da177e4SLinus Torvalds #include <linux/skbuff.h>
201da177e4SLinus Torvalds #include <linux/init.h>
211da177e4SLinus Torvalds #include <linux/kmod.h>
22ab27cfb8SPatrick McHardy #include <linux/err.h>
233a9a231dSPaul Gortmaker #include <linux/module.h>
24b854272bSDenis V. Lunev #include <net/net_namespace.h>
25b854272bSDenis V. Lunev #include <net/sock.h>
261da177e4SLinus Torvalds #include <net/sch_generic.h>
271da177e4SLinus Torvalds #include <net/act_api.h>
28dc5fc579SArnaldo Carvalho de Melo #include <net/netlink.h>
291da177e4SLinus Torvalds 
3086062033SWANG Cong void tcf_hash_destroy(struct tc_action *a)
31e9ce1cd3SDavid S. Miller {
3286062033SWANG Cong 	struct tcf_common *p = a->priv;
3386062033SWANG Cong 	struct tcf_hashinfo *hinfo = a->ops->hinfo;
3486062033SWANG Cong 
3589819dc0SWANG Cong 	spin_lock_bh(&hinfo->lock);
3689819dc0SWANG Cong 	hlist_del(&p->tcfc_head);
3789819dc0SWANG Cong 	spin_unlock_bh(&hinfo->lock);
38e9ce1cd3SDavid S. Miller 	gen_kill_estimator(&p->tcfc_bstats,
39e9ce1cd3SDavid S. Miller 			   &p->tcfc_rate_est);
40c7de2cf0SEric Dumazet 	/*
41c7de2cf0SEric Dumazet 	 * gen_estimator est_timer() might access p->tcfc_lock
42c7de2cf0SEric Dumazet 	 * or bstats, wait a RCU grace period before freeing p
43c7de2cf0SEric Dumazet 	 */
44f5c8593cSLai Jiangshan 	kfree_rcu(p, tcfc_rcu);
45e9ce1cd3SDavid S. Miller }
46e9ce1cd3SDavid S. Miller EXPORT_SYMBOL(tcf_hash_destroy);
47e9ce1cd3SDavid S. Miller 
4886062033SWANG Cong int tcf_hash_release(struct tc_action *a, int bind)
49e9ce1cd3SDavid S. Miller {
5086062033SWANG Cong 	struct tcf_common *p = a->priv;
51e9ce1cd3SDavid S. Miller 	int ret = 0;
52e9ce1cd3SDavid S. Miller 
53e9ce1cd3SDavid S. Miller 	if (p) {
54e9ce1cd3SDavid S. Miller 		if (bind)
55e9ce1cd3SDavid S. Miller 			p->tcfc_bindcnt--;
56*55334a5dSWANG Cong 		else if (p->tcfc_bindcnt > 0)
57*55334a5dSWANG Cong 			return -EPERM;
58e9ce1cd3SDavid S. Miller 
59e9ce1cd3SDavid S. Miller 		p->tcfc_refcnt--;
60e9ce1cd3SDavid S. Miller 		if (p->tcfc_bindcnt <= 0 && p->tcfc_refcnt <= 0) {
61a5b5c958SWANG Cong 			if (a->ops->cleanup)
62a5b5c958SWANG Cong 				a->ops->cleanup(a, bind);
6386062033SWANG Cong 			tcf_hash_destroy(a);
64e9ce1cd3SDavid S. Miller 			ret = 1;
65e9ce1cd3SDavid S. Miller 		}
66e9ce1cd3SDavid S. Miller 	}
67e9ce1cd3SDavid S. Miller 	return ret;
68e9ce1cd3SDavid S. Miller }
69e9ce1cd3SDavid S. Miller EXPORT_SYMBOL(tcf_hash_release);
70e9ce1cd3SDavid S. Miller 
71e9ce1cd3SDavid S. Miller static int tcf_dump_walker(struct sk_buff *skb, struct netlink_callback *cb,
72c779f7afSWANG Cong 			   struct tc_action *a)
73e9ce1cd3SDavid S. Miller {
74c779f7afSWANG Cong 	struct tcf_hashinfo *hinfo = a->ops->hinfo;
7589819dc0SWANG Cong 	struct hlist_head *head;
76e9ce1cd3SDavid S. Miller 	struct tcf_common *p;
77e9ce1cd3SDavid S. Miller 	int err = 0, index = -1, i = 0, s_i = 0, n_i = 0;
784b3550efSPatrick McHardy 	struct nlattr *nest;
79e9ce1cd3SDavid S. Miller 
8089819dc0SWANG Cong 	spin_lock_bh(&hinfo->lock);
81e9ce1cd3SDavid S. Miller 
82e9ce1cd3SDavid S. Miller 	s_i = cb->args[0];
83e9ce1cd3SDavid S. Miller 
84e9ce1cd3SDavid S. Miller 	for (i = 0; i < (hinfo->hmask + 1); i++) {
8589819dc0SWANG Cong 		head = &hinfo->htab[tcf_hash(i, hinfo->hmask)];
86e9ce1cd3SDavid S. Miller 
8789819dc0SWANG Cong 		hlist_for_each_entry_rcu(p, head, tcfc_head) {
88e9ce1cd3SDavid S. Miller 			index++;
89e9ce1cd3SDavid S. Miller 			if (index < s_i)
90e9ce1cd3SDavid S. Miller 				continue;
91e9ce1cd3SDavid S. Miller 			a->priv = p;
92e9ce1cd3SDavid S. Miller 			a->order = n_i;
934b3550efSPatrick McHardy 
944b3550efSPatrick McHardy 			nest = nla_nest_start(skb, a->order);
954b3550efSPatrick McHardy 			if (nest == NULL)
964b3550efSPatrick McHardy 				goto nla_put_failure;
97e9ce1cd3SDavid S. Miller 			err = tcf_action_dump_1(skb, a, 0, 0);
98e9ce1cd3SDavid S. Miller 			if (err < 0) {
99e9ce1cd3SDavid S. Miller 				index--;
1004b3550efSPatrick McHardy 				nlmsg_trim(skb, nest);
101e9ce1cd3SDavid S. Miller 				goto done;
102e9ce1cd3SDavid S. Miller 			}
1034b3550efSPatrick McHardy 			nla_nest_end(skb, nest);
104e9ce1cd3SDavid S. Miller 			n_i++;
105e9ce1cd3SDavid S. Miller 			if (n_i >= TCA_ACT_MAX_PRIO)
106e9ce1cd3SDavid S. Miller 				goto done;
107e9ce1cd3SDavid S. Miller 		}
108e9ce1cd3SDavid S. Miller 	}
109e9ce1cd3SDavid S. Miller done:
11089819dc0SWANG Cong 	spin_unlock_bh(&hinfo->lock);
111e9ce1cd3SDavid S. Miller 	if (n_i)
112e9ce1cd3SDavid S. Miller 		cb->args[0] += n_i;
113e9ce1cd3SDavid S. Miller 	return n_i;
114e9ce1cd3SDavid S. Miller 
1157ba699c6SPatrick McHardy nla_put_failure:
1164b3550efSPatrick McHardy 	nla_nest_cancel(skb, nest);
117e9ce1cd3SDavid S. Miller 	goto done;
118e9ce1cd3SDavid S. Miller }
119e9ce1cd3SDavid S. Miller 
120c779f7afSWANG Cong static int tcf_del_walker(struct sk_buff *skb, struct tc_action *a)
121e9ce1cd3SDavid S. Miller {
122c779f7afSWANG Cong 	struct tcf_hashinfo *hinfo = a->ops->hinfo;
12389819dc0SWANG Cong 	struct hlist_head *head;
12489819dc0SWANG Cong 	struct hlist_node *n;
12589819dc0SWANG Cong 	struct tcf_common *p;
1264b3550efSPatrick McHardy 	struct nlattr *nest;
127e9ce1cd3SDavid S. Miller 	int i = 0, n_i = 0;
128*55334a5dSWANG Cong 	int ret = -EINVAL;
129e9ce1cd3SDavid S. Miller 
1304b3550efSPatrick McHardy 	nest = nla_nest_start(skb, a->order);
1314b3550efSPatrick McHardy 	if (nest == NULL)
1324b3550efSPatrick McHardy 		goto nla_put_failure;
1331b34ec43SDavid S. Miller 	if (nla_put_string(skb, TCA_KIND, a->ops->kind))
1341b34ec43SDavid S. Miller 		goto nla_put_failure;
135e9ce1cd3SDavid S. Miller 	for (i = 0; i < (hinfo->hmask + 1); i++) {
13689819dc0SWANG Cong 		head = &hinfo->htab[tcf_hash(i, hinfo->hmask)];
13789819dc0SWANG Cong 		hlist_for_each_entry_safe(p, n, head, tcfc_head) {
13886062033SWANG Cong 			a->priv = p;
139*55334a5dSWANG Cong 			ret = tcf_hash_release(a, 0);
140*55334a5dSWANG Cong 			if (ret == ACT_P_DELETED) {
141e9ce1cd3SDavid S. Miller 				module_put(a->ops->owner);
142e9ce1cd3SDavid S. Miller 				n_i++;
143*55334a5dSWANG Cong 			} else if (ret < 0)
144*55334a5dSWANG Cong 				goto nla_put_failure;
145e9ce1cd3SDavid S. Miller 		}
146805c1f4aSJamal Hadi Salim 	}
1471b34ec43SDavid S. Miller 	if (nla_put_u32(skb, TCA_FCNT, n_i))
1481b34ec43SDavid S. Miller 		goto nla_put_failure;
1494b3550efSPatrick McHardy 	nla_nest_end(skb, nest);
150e9ce1cd3SDavid S. Miller 
151e9ce1cd3SDavid S. Miller 	return n_i;
1527ba699c6SPatrick McHardy nla_put_failure:
1534b3550efSPatrick McHardy 	nla_nest_cancel(skb, nest);
154*55334a5dSWANG Cong 	return ret;
155e9ce1cd3SDavid S. Miller }
156e9ce1cd3SDavid S. Miller 
1579c75f402Sstephen hemminger static int tcf_generic_walker(struct sk_buff *skb, struct netlink_callback *cb,
158e9ce1cd3SDavid S. Miller 			      int type, struct tc_action *a)
159e9ce1cd3SDavid S. Miller {
160e9ce1cd3SDavid S. Miller 	if (type == RTM_DELACTION) {
161c779f7afSWANG Cong 		return tcf_del_walker(skb, a);
162e9ce1cd3SDavid S. Miller 	} else if (type == RTM_GETACTION) {
163c779f7afSWANG Cong 		return tcf_dump_walker(skb, cb, a);
164e9ce1cd3SDavid S. Miller 	} else {
1656ff9c364Sstephen hemminger 		WARN(1, "tcf_generic_walker: unknown action %d\n", type);
166e9ce1cd3SDavid S. Miller 		return -EINVAL;
167e9ce1cd3SDavid S. Miller 	}
168e9ce1cd3SDavid S. Miller }
169e9ce1cd3SDavid S. Miller 
1706e6a50c2SWANG Cong static struct tcf_common *tcf_hash_lookup(u32 index, struct tcf_hashinfo *hinfo)
171e9ce1cd3SDavid S. Miller {
17289819dc0SWANG Cong 	struct tcf_common *p = NULL;
17389819dc0SWANG Cong 	struct hlist_head *head;
174e9ce1cd3SDavid S. Miller 
17589819dc0SWANG Cong 	spin_lock_bh(&hinfo->lock);
17689819dc0SWANG Cong 	head = &hinfo->htab[tcf_hash(index, hinfo->hmask)];
17789819dc0SWANG Cong 	hlist_for_each_entry_rcu(p, head, tcfc_head)
178e9ce1cd3SDavid S. Miller 		if (p->tcfc_index == index)
179e9ce1cd3SDavid S. Miller 			break;
18089819dc0SWANG Cong 	spin_unlock_bh(&hinfo->lock);
181e9ce1cd3SDavid S. Miller 
182e9ce1cd3SDavid S. Miller 	return p;
183e9ce1cd3SDavid S. Miller }
184e9ce1cd3SDavid S. Miller 
185ddafd34fSWANG Cong u32 tcf_hash_new_index(struct tcf_hashinfo *hinfo)
186e9ce1cd3SDavid S. Miller {
187ddafd34fSWANG Cong 	u32 val = hinfo->index;
188e9ce1cd3SDavid S. Miller 
189e9ce1cd3SDavid S. Miller 	do {
190e9ce1cd3SDavid S. Miller 		if (++val == 0)
191e9ce1cd3SDavid S. Miller 			val = 1;
192e9ce1cd3SDavid S. Miller 	} while (tcf_hash_lookup(val, hinfo));
193e9ce1cd3SDavid S. Miller 
194ddafd34fSWANG Cong 	hinfo->index = val;
19517569faeSYang Yingliang 	return val;
196e9ce1cd3SDavid S. Miller }
197e9ce1cd3SDavid S. Miller EXPORT_SYMBOL(tcf_hash_new_index);
198e9ce1cd3SDavid S. Miller 
1996e6a50c2SWANG Cong int tcf_hash_search(struct tc_action *a, u32 index)
200e9ce1cd3SDavid S. Miller {
201e9ce1cd3SDavid S. Miller 	struct tcf_hashinfo *hinfo = a->ops->hinfo;
202e9ce1cd3SDavid S. Miller 	struct tcf_common *p = tcf_hash_lookup(index, hinfo);
203e9ce1cd3SDavid S. Miller 
204e9ce1cd3SDavid S. Miller 	if (p) {
205e9ce1cd3SDavid S. Miller 		a->priv = p;
206e9ce1cd3SDavid S. Miller 		return 1;
207e9ce1cd3SDavid S. Miller 	}
208e9ce1cd3SDavid S. Miller 	return 0;
209e9ce1cd3SDavid S. Miller }
2106e6a50c2SWANG Cong EXPORT_SYMBOL(tcf_hash_search);
211e9ce1cd3SDavid S. Miller 
21286062033SWANG Cong int tcf_hash_check(u32 index, struct tc_action *a, int bind)
213e9ce1cd3SDavid S. Miller {
214c779f7afSWANG Cong 	struct tcf_hashinfo *hinfo = a->ops->hinfo;
215e9ce1cd3SDavid S. Miller 	struct tcf_common *p = NULL;
216e9ce1cd3SDavid S. Miller 	if (index && (p = tcf_hash_lookup(index, hinfo)) != NULL) {
21776aab2c1SJamal Hadi Salim 		if (bind)
218e9ce1cd3SDavid S. Miller 			p->tcfc_bindcnt++;
219e9ce1cd3SDavid S. Miller 		p->tcfc_refcnt++;
220e9ce1cd3SDavid S. Miller 		a->priv = p;
22186062033SWANG Cong 		return 1;
222e9ce1cd3SDavid S. Miller 	}
22386062033SWANG Cong 	return 0;
224e9ce1cd3SDavid S. Miller }
225e9ce1cd3SDavid S. Miller EXPORT_SYMBOL(tcf_hash_check);
226e9ce1cd3SDavid S. Miller 
22786062033SWANG Cong void tcf_hash_cleanup(struct tc_action *a, struct nlattr *est)
22886062033SWANG Cong {
22986062033SWANG Cong 	struct tcf_common *pc = a->priv;
23086062033SWANG Cong 	if (est)
23186062033SWANG Cong 		gen_kill_estimator(&pc->tcfc_bstats,
23286062033SWANG Cong 				   &pc->tcfc_rate_est);
23386062033SWANG Cong 	kfree_rcu(pc, tcfc_rcu);
23486062033SWANG Cong }
23586062033SWANG Cong EXPORT_SYMBOL(tcf_hash_cleanup);
23686062033SWANG Cong 
23786062033SWANG Cong int tcf_hash_create(u32 index, struct nlattr *est, struct tc_action *a,
23886062033SWANG Cong 		    int size, int bind)
239e9ce1cd3SDavid S. Miller {
240c779f7afSWANG Cong 	struct tcf_hashinfo *hinfo = a->ops->hinfo;
241e9ce1cd3SDavid S. Miller 	struct tcf_common *p = kzalloc(size, GFP_KERNEL);
242e9ce1cd3SDavid S. Miller 
243e9ce1cd3SDavid S. Miller 	if (unlikely(!p))
24486062033SWANG Cong 		return -ENOMEM;
245e9ce1cd3SDavid S. Miller 	p->tcfc_refcnt = 1;
246e9ce1cd3SDavid S. Miller 	if (bind)
247e9ce1cd3SDavid S. Miller 		p->tcfc_bindcnt = 1;
248e9ce1cd3SDavid S. Miller 
249e9ce1cd3SDavid S. Miller 	spin_lock_init(&p->tcfc_lock);
25089819dc0SWANG Cong 	INIT_HLIST_NODE(&p->tcfc_head);
251ddafd34fSWANG Cong 	p->tcfc_index = index ? index : tcf_hash_new_index(hinfo);
252e9ce1cd3SDavid S. Miller 	p->tcfc_tm.install = jiffies;
253e9ce1cd3SDavid S. Miller 	p->tcfc_tm.lastuse = jiffies;
2540e991ec6SStephen Hemminger 	if (est) {
2550e991ec6SStephen Hemminger 		int err = gen_new_estimator(&p->tcfc_bstats, &p->tcfc_rate_est,
2567ba699c6SPatrick McHardy 					    &p->tcfc_lock, est);
2570e991ec6SStephen Hemminger 		if (err) {
2580e991ec6SStephen Hemminger 			kfree(p);
25986062033SWANG Cong 			return err;
2600e991ec6SStephen Hemminger 		}
2610e991ec6SStephen Hemminger 	}
2620e991ec6SStephen Hemminger 
263e9ce1cd3SDavid S. Miller 	a->priv = (void *) p;
26486062033SWANG Cong 	return 0;
265e9ce1cd3SDavid S. Miller }
266e9ce1cd3SDavid S. Miller EXPORT_SYMBOL(tcf_hash_create);
267e9ce1cd3SDavid S. Miller 
26886062033SWANG Cong void tcf_hash_insert(struct tc_action *a)
269e9ce1cd3SDavid S. Miller {
27086062033SWANG Cong 	struct tcf_common *p = a->priv;
27186062033SWANG Cong 	struct tcf_hashinfo *hinfo = a->ops->hinfo;
272e9ce1cd3SDavid S. Miller 	unsigned int h = tcf_hash(p->tcfc_index, hinfo->hmask);
273e9ce1cd3SDavid S. Miller 
27489819dc0SWANG Cong 	spin_lock_bh(&hinfo->lock);
27589819dc0SWANG Cong 	hlist_add_head(&p->tcfc_head, &hinfo->htab[h]);
27689819dc0SWANG Cong 	spin_unlock_bh(&hinfo->lock);
277e9ce1cd3SDavid S. Miller }
278e9ce1cd3SDavid S. Miller EXPORT_SYMBOL(tcf_hash_insert);
2791da177e4SLinus Torvalds 
2801f747c26SWANG Cong static LIST_HEAD(act_base);
2811da177e4SLinus Torvalds static DEFINE_RWLOCK(act_mod_lock);
2821da177e4SLinus Torvalds 
2834f1e9d89SWANG Cong int tcf_register_action(struct tc_action_ops *act, unsigned int mask)
2841da177e4SLinus Torvalds {
2851f747c26SWANG Cong 	struct tc_action_ops *a;
2864f1e9d89SWANG Cong 	int err;
2871da177e4SLinus Torvalds 
288a5b5c958SWANG Cong 	/* Must supply act, dump and init */
289a5b5c958SWANG Cong 	if (!act->act || !act->dump || !act->init)
29076c82d7aSJamal Hadi Salim 		return -EINVAL;
29176c82d7aSJamal Hadi Salim 
292382ca8a1SJamal Hadi Salim 	/* Supply defaults */
29363ef6174SJamal Hadi Salim 	if (!act->lookup)
29463ef6174SJamal Hadi Salim 		act->lookup = tcf_hash_search;
295382ca8a1SJamal Hadi Salim 	if (!act->walk)
296382ca8a1SJamal Hadi Salim 		act->walk = tcf_generic_walker;
29763ef6174SJamal Hadi Salim 
2984f1e9d89SWANG Cong 	act->hinfo = kmalloc(sizeof(struct tcf_hashinfo), GFP_KERNEL);
2994f1e9d89SWANG Cong 	if (!act->hinfo)
3004f1e9d89SWANG Cong 		return -ENOMEM;
3014f1e9d89SWANG Cong 	err = tcf_hashinfo_init(act->hinfo, mask);
3024f1e9d89SWANG Cong 	if (err) {
3034f1e9d89SWANG Cong 		kfree(act->hinfo);
3044f1e9d89SWANG Cong 		return err;
3054f1e9d89SWANG Cong 	}
3064f1e9d89SWANG Cong 
3071da177e4SLinus Torvalds 	write_lock(&act_mod_lock);
3081f747c26SWANG Cong 	list_for_each_entry(a, &act_base, head) {
3091da177e4SLinus Torvalds 		if (act->type == a->type || (strcmp(act->kind, a->kind) == 0)) {
3101da177e4SLinus Torvalds 			write_unlock(&act_mod_lock);
3114f1e9d89SWANG Cong 			tcf_hashinfo_destroy(act->hinfo);
3124f1e9d89SWANG Cong 			kfree(act->hinfo);
3131da177e4SLinus Torvalds 			return -EEXIST;
3141da177e4SLinus Torvalds 		}
3151da177e4SLinus Torvalds 	}
3161f747c26SWANG Cong 	list_add_tail(&act->head, &act_base);
3171da177e4SLinus Torvalds 	write_unlock(&act_mod_lock);
3181da177e4SLinus Torvalds 	return 0;
3191da177e4SLinus Torvalds }
32062e3ba1bSPatrick McHardy EXPORT_SYMBOL(tcf_register_action);
3211da177e4SLinus Torvalds 
3221da177e4SLinus Torvalds int tcf_unregister_action(struct tc_action_ops *act)
3231da177e4SLinus Torvalds {
3241f747c26SWANG Cong 	struct tc_action_ops *a;
3251da177e4SLinus Torvalds 	int err = -ENOENT;
3261da177e4SLinus Torvalds 
3271da177e4SLinus Torvalds 	write_lock(&act_mod_lock);
328a792866aSEric Dumazet 	list_for_each_entry(a, &act_base, head) {
329a792866aSEric Dumazet 		if (a == act) {
3301f747c26SWANG Cong 			list_del(&act->head);
3314f1e9d89SWANG Cong 			tcf_hashinfo_destroy(act->hinfo);
3324f1e9d89SWANG Cong 			kfree(act->hinfo);
3331da177e4SLinus Torvalds 			err = 0;
334a792866aSEric Dumazet 			break;
335a792866aSEric Dumazet 		}
3361da177e4SLinus Torvalds 	}
3371da177e4SLinus Torvalds 	write_unlock(&act_mod_lock);
3381da177e4SLinus Torvalds 	return err;
3391da177e4SLinus Torvalds }
34062e3ba1bSPatrick McHardy EXPORT_SYMBOL(tcf_unregister_action);
3411da177e4SLinus Torvalds 
3421da177e4SLinus Torvalds /* lookup by name */
3431da177e4SLinus Torvalds static struct tc_action_ops *tc_lookup_action_n(char *kind)
3441da177e4SLinus Torvalds {
345a792866aSEric Dumazet 	struct tc_action_ops *a, *res = NULL;
3461da177e4SLinus Torvalds 
3471da177e4SLinus Torvalds 	if (kind) {
3481da177e4SLinus Torvalds 		read_lock(&act_mod_lock);
3491f747c26SWANG Cong 		list_for_each_entry(a, &act_base, head) {
3501da177e4SLinus Torvalds 			if (strcmp(kind, a->kind) == 0) {
351a792866aSEric Dumazet 				if (try_module_get(a->owner))
352a792866aSEric Dumazet 					res = a;
3531da177e4SLinus Torvalds 				break;
3541da177e4SLinus Torvalds 			}
3551da177e4SLinus Torvalds 		}
3561da177e4SLinus Torvalds 		read_unlock(&act_mod_lock);
3571da177e4SLinus Torvalds 	}
358a792866aSEric Dumazet 	return res;
3591da177e4SLinus Torvalds }
3601da177e4SLinus Torvalds 
3617ba699c6SPatrick McHardy /* lookup by nlattr */
3627ba699c6SPatrick McHardy static struct tc_action_ops *tc_lookup_action(struct nlattr *kind)
3631da177e4SLinus Torvalds {
364a792866aSEric Dumazet 	struct tc_action_ops *a, *res = NULL;
3651da177e4SLinus Torvalds 
3661da177e4SLinus Torvalds 	if (kind) {
3671da177e4SLinus Torvalds 		read_lock(&act_mod_lock);
3681f747c26SWANG Cong 		list_for_each_entry(a, &act_base, head) {
3697ba699c6SPatrick McHardy 			if (nla_strcmp(kind, a->kind) == 0) {
370a792866aSEric Dumazet 				if (try_module_get(a->owner))
371a792866aSEric Dumazet 					res = a;
3721da177e4SLinus Torvalds 				break;
3731da177e4SLinus Torvalds 			}
3741da177e4SLinus Torvalds 		}
3751da177e4SLinus Torvalds 		read_unlock(&act_mod_lock);
3761da177e4SLinus Torvalds 	}
377a792866aSEric Dumazet 	return res;
3781da177e4SLinus Torvalds }
3791da177e4SLinus Torvalds 
38033be6271SWANG Cong int tcf_action_exec(struct sk_buff *skb, const struct list_head *actions,
3811da177e4SLinus Torvalds 		    struct tcf_result *res)
3821da177e4SLinus Torvalds {
383dc7f9f6eSEric Dumazet 	const struct tc_action *a;
3841da177e4SLinus Torvalds 	int ret = -1;
3851da177e4SLinus Torvalds 
3861da177e4SLinus Torvalds 	if (skb->tc_verd & TC_NCLS) {
3871da177e4SLinus Torvalds 		skb->tc_verd = CLR_TC_NCLS(skb->tc_verd);
3881da177e4SLinus Torvalds 		ret = TC_ACT_OK;
3891da177e4SLinus Torvalds 		goto exec_done;
3901da177e4SLinus Torvalds 	}
39133be6271SWANG Cong 	list_for_each_entry(a, actions, list) {
3921da177e4SLinus Torvalds repeat:
393f43c5a0dSPatrick McHardy 		ret = a->ops->act(skb, a, res);
3941da177e4SLinus Torvalds 		if (TC_MUNGED & skb->tc_verd) {
3951da177e4SLinus Torvalds 			/* copied already, allow trampling */
3961da177e4SLinus Torvalds 			skb->tc_verd = SET_TC_OK2MUNGE(skb->tc_verd);
3971da177e4SLinus Torvalds 			skb->tc_verd = CLR_TC_MUNGED(skb->tc_verd);
3981da177e4SLinus Torvalds 		}
3991da177e4SLinus Torvalds 		if (ret == TC_ACT_REPEAT)
4001da177e4SLinus Torvalds 			goto repeat;	/* we need a ttl - JHS */
40114d50e78SJ Hadi Salim 		if (ret != TC_ACT_PIPE)
40214d50e78SJ Hadi Salim 			goto exec_done;
4031da177e4SLinus Torvalds 	}
4041da177e4SLinus Torvalds exec_done:
4051da177e4SLinus Torvalds 	return ret;
4061da177e4SLinus Torvalds }
40762e3ba1bSPatrick McHardy EXPORT_SYMBOL(tcf_action_exec);
4081da177e4SLinus Torvalds 
409*55334a5dSWANG Cong int tcf_action_destroy(struct list_head *actions, int bind)
4101da177e4SLinus Torvalds {
41133be6271SWANG Cong 	struct tc_action *a, *tmp;
412*55334a5dSWANG Cong 	int ret = 0;
4131da177e4SLinus Torvalds 
41433be6271SWANG Cong 	list_for_each_entry_safe(a, tmp, actions, list) {
415*55334a5dSWANG Cong 		ret = tcf_hash_release(a, bind);
416*55334a5dSWANG Cong 		if (ret == ACT_P_DELETED)
4171da177e4SLinus Torvalds 			module_put(a->ops->owner);
418*55334a5dSWANG Cong 		else if (ret < 0)
419*55334a5dSWANG Cong 			return ret;
42033be6271SWANG Cong 		list_del(&a->list);
4211da177e4SLinus Torvalds 		kfree(a);
4221da177e4SLinus Torvalds 	}
423*55334a5dSWANG Cong 	return ret;
4241da177e4SLinus Torvalds }
4251da177e4SLinus Torvalds 
4261da177e4SLinus Torvalds int
4271da177e4SLinus Torvalds tcf_action_dump_old(struct sk_buff *skb, struct tc_action *a, int bind, int ref)
4281da177e4SLinus Torvalds {
4291da177e4SLinus Torvalds 	return a->ops->dump(skb, a, bind, ref);
4301da177e4SLinus Torvalds }
4311da177e4SLinus Torvalds 
4321da177e4SLinus Torvalds int
4331da177e4SLinus Torvalds tcf_action_dump_1(struct sk_buff *skb, struct tc_action *a, int bind, int ref)
4341da177e4SLinus Torvalds {
4351da177e4SLinus Torvalds 	int err = -EINVAL;
43627a884dcSArnaldo Carvalho de Melo 	unsigned char *b = skb_tail_pointer(skb);
4374b3550efSPatrick McHardy 	struct nlattr *nest;
4381da177e4SLinus Torvalds 
4391b34ec43SDavid S. Miller 	if (nla_put_string(skb, TCA_KIND, a->ops->kind))
4401b34ec43SDavid S. Miller 		goto nla_put_failure;
4411da177e4SLinus Torvalds 	if (tcf_action_copy_stats(skb, a, 0))
4427ba699c6SPatrick McHardy 		goto nla_put_failure;
4434b3550efSPatrick McHardy 	nest = nla_nest_start(skb, TCA_OPTIONS);
4444b3550efSPatrick McHardy 	if (nest == NULL)
4454b3550efSPatrick McHardy 		goto nla_put_failure;
446cc7ec456SEric Dumazet 	err = tcf_action_dump_old(skb, a, bind, ref);
447cc7ec456SEric Dumazet 	if (err > 0) {
4484b3550efSPatrick McHardy 		nla_nest_end(skb, nest);
4491da177e4SLinus Torvalds 		return err;
4501da177e4SLinus Torvalds 	}
4511da177e4SLinus Torvalds 
4527ba699c6SPatrick McHardy nla_put_failure:
453dc5fc579SArnaldo Carvalho de Melo 	nlmsg_trim(skb, b);
4541da177e4SLinus Torvalds 	return -1;
4551da177e4SLinus Torvalds }
45662e3ba1bSPatrick McHardy EXPORT_SYMBOL(tcf_action_dump_1);
4571da177e4SLinus Torvalds 
4581da177e4SLinus Torvalds int
45933be6271SWANG Cong tcf_action_dump(struct sk_buff *skb, struct list_head *actions, int bind, int ref)
4601da177e4SLinus Torvalds {
4611da177e4SLinus Torvalds 	struct tc_action *a;
4621da177e4SLinus Torvalds 	int err = -EINVAL;
4634b3550efSPatrick McHardy 	struct nlattr *nest;
4641da177e4SLinus Torvalds 
46533be6271SWANG Cong 	list_for_each_entry(a, actions, list) {
4664b3550efSPatrick McHardy 		nest = nla_nest_start(skb, a->order);
4674b3550efSPatrick McHardy 		if (nest == NULL)
4684b3550efSPatrick McHardy 			goto nla_put_failure;
4691da177e4SLinus Torvalds 		err = tcf_action_dump_1(skb, a, bind, ref);
4701da177e4SLinus Torvalds 		if (err < 0)
4714fe683f5SThomas Graf 			goto errout;
4724b3550efSPatrick McHardy 		nla_nest_end(skb, nest);
4731da177e4SLinus Torvalds 	}
4741da177e4SLinus Torvalds 
4751da177e4SLinus Torvalds 	return 0;
4761da177e4SLinus Torvalds 
4777ba699c6SPatrick McHardy nla_put_failure:
4784fe683f5SThomas Graf 	err = -EINVAL;
4794fe683f5SThomas Graf errout:
4804b3550efSPatrick McHardy 	nla_nest_cancel(skb, nest);
4814fe683f5SThomas Graf 	return err;
4821da177e4SLinus Torvalds }
4831da177e4SLinus Torvalds 
484c1b52739SBenjamin LaHaise struct tc_action *tcf_action_init_1(struct net *net, struct nlattr *nla,
485c1b52739SBenjamin LaHaise 				    struct nlattr *est, char *name, int ovr,
486c1b52739SBenjamin LaHaise 				    int bind)
4871da177e4SLinus Torvalds {
4881da177e4SLinus Torvalds 	struct tc_action *a;
4891da177e4SLinus Torvalds 	struct tc_action_ops *a_o;
4901da177e4SLinus Torvalds 	char act_name[IFNAMSIZ];
4917ba699c6SPatrick McHardy 	struct nlattr *tb[TCA_ACT_MAX + 1];
4927ba699c6SPatrick McHardy 	struct nlattr *kind;
493ab27cfb8SPatrick McHardy 	int err;
4941da177e4SLinus Torvalds 
4951da177e4SLinus Torvalds 	if (name == NULL) {
496cee63723SPatrick McHardy 		err = nla_parse_nested(tb, TCA_ACT_MAX, nla, NULL);
497cee63723SPatrick McHardy 		if (err < 0)
4981da177e4SLinus Torvalds 			goto err_out;
499cee63723SPatrick McHardy 		err = -EINVAL;
5007ba699c6SPatrick McHardy 		kind = tb[TCA_ACT_KIND];
5011da177e4SLinus Torvalds 		if (kind == NULL)
5021da177e4SLinus Torvalds 			goto err_out;
5037ba699c6SPatrick McHardy 		if (nla_strlcpy(act_name, kind, IFNAMSIZ) >= IFNAMSIZ)
5041da177e4SLinus Torvalds 			goto err_out;
5051da177e4SLinus Torvalds 	} else {
506cee63723SPatrick McHardy 		err = -EINVAL;
5071da177e4SLinus Torvalds 		if (strlcpy(act_name, name, IFNAMSIZ) >= IFNAMSIZ)
5081da177e4SLinus Torvalds 			goto err_out;
5091da177e4SLinus Torvalds 	}
5101da177e4SLinus Torvalds 
5111da177e4SLinus Torvalds 	a_o = tc_lookup_action_n(act_name);
5121da177e4SLinus Torvalds 	if (a_o == NULL) {
51395a5afcaSJohannes Berg #ifdef CONFIG_MODULES
5141da177e4SLinus Torvalds 		rtnl_unlock();
5154bba3925SPatrick McHardy 		request_module("act_%s", act_name);
5161da177e4SLinus Torvalds 		rtnl_lock();
5171da177e4SLinus Torvalds 
5181da177e4SLinus Torvalds 		a_o = tc_lookup_action_n(act_name);
5191da177e4SLinus Torvalds 
5201da177e4SLinus Torvalds 		/* We dropped the RTNL semaphore in order to
5211da177e4SLinus Torvalds 		 * perform the module load.  So, even if we
5221da177e4SLinus Torvalds 		 * succeeded in loading the module we have to
5231da177e4SLinus Torvalds 		 * tell the caller to replay the request.  We
5241da177e4SLinus Torvalds 		 * indicate this using -EAGAIN.
5251da177e4SLinus Torvalds 		 */
5261da177e4SLinus Torvalds 		if (a_o != NULL) {
527ab27cfb8SPatrick McHardy 			err = -EAGAIN;
5281da177e4SLinus Torvalds 			goto err_mod;
5291da177e4SLinus Torvalds 		}
5301da177e4SLinus Torvalds #endif
531ab27cfb8SPatrick McHardy 		err = -ENOENT;
5321da177e4SLinus Torvalds 		goto err_out;
5331da177e4SLinus Torvalds 	}
5341da177e4SLinus Torvalds 
535ab27cfb8SPatrick McHardy 	err = -ENOMEM;
5360da974f4SPanagiotis Issaris 	a = kzalloc(sizeof(*a), GFP_KERNEL);
5371da177e4SLinus Torvalds 	if (a == NULL)
5381da177e4SLinus Torvalds 		goto err_mod;
5391da177e4SLinus Torvalds 
540c779f7afSWANG Cong 	a->ops = a_o;
54133be6271SWANG Cong 	INIT_LIST_HEAD(&a->list);
5421da177e4SLinus Torvalds 	/* backward compatibility for policer */
5431da177e4SLinus Torvalds 	if (name == NULL)
544c1b52739SBenjamin LaHaise 		err = a_o->init(net, tb[TCA_ACT_OPTIONS], est, a, ovr, bind);
5451da177e4SLinus Torvalds 	else
546c1b52739SBenjamin LaHaise 		err = a_o->init(net, nla, est, a, ovr, bind);
547ab27cfb8SPatrick McHardy 	if (err < 0)
5481da177e4SLinus Torvalds 		goto err_free;
5491da177e4SLinus Torvalds 
5501da177e4SLinus Torvalds 	/* module count goes up only when brand new policy is created
551cc7ec456SEric Dumazet 	 * if it exists and is only bound to in a_o->init() then
552cc7ec456SEric Dumazet 	 * ACT_P_CREATED is not returned (a zero is).
5531da177e4SLinus Torvalds 	 */
554ab27cfb8SPatrick McHardy 	if (err != ACT_P_CREATED)
5551da177e4SLinus Torvalds 		module_put(a_o->owner);
5561da177e4SLinus Torvalds 
5571da177e4SLinus Torvalds 	return a;
5581da177e4SLinus Torvalds 
5591da177e4SLinus Torvalds err_free:
5601da177e4SLinus Torvalds 	kfree(a);
5611da177e4SLinus Torvalds err_mod:
5621da177e4SLinus Torvalds 	module_put(a_o->owner);
5631da177e4SLinus Torvalds err_out:
564ab27cfb8SPatrick McHardy 	return ERR_PTR(err);
5651da177e4SLinus Torvalds }
5661da177e4SLinus Torvalds 
56733be6271SWANG Cong int tcf_action_init(struct net *net, struct nlattr *nla,
568c1b52739SBenjamin LaHaise 				  struct nlattr *est, char *name, int ovr,
56933be6271SWANG Cong 				  int bind, struct list_head *actions)
5701da177e4SLinus Torvalds {
5717ba699c6SPatrick McHardy 	struct nlattr *tb[TCA_ACT_MAX_PRIO + 1];
57233be6271SWANG Cong 	struct tc_action *act;
573cee63723SPatrick McHardy 	int err;
5741da177e4SLinus Torvalds 	int i;
5751da177e4SLinus Torvalds 
576cee63723SPatrick McHardy 	err = nla_parse_nested(tb, TCA_ACT_MAX_PRIO, nla, NULL);
577cee63723SPatrick McHardy 	if (err < 0)
57833be6271SWANG Cong 		return err;
5791da177e4SLinus Torvalds 
5807ba699c6SPatrick McHardy 	for (i = 1; i <= TCA_ACT_MAX_PRIO && tb[i]; i++) {
581c1b52739SBenjamin LaHaise 		act = tcf_action_init_1(net, tb[i], est, name, ovr, bind);
58233be6271SWANG Cong 		if (IS_ERR(act)) {
58333be6271SWANG Cong 			err = PTR_ERR(act);
5841da177e4SLinus Torvalds 			goto err;
5851da177e4SLinus Torvalds 		}
58633be6271SWANG Cong 		act->order = i;
58733be6271SWANG Cong 		list_add_tail(&act->list, actions);
58833be6271SWANG Cong 	}
58933be6271SWANG Cong 	return 0;
5901da177e4SLinus Torvalds 
5911da177e4SLinus Torvalds err:
59233be6271SWANG Cong 	tcf_action_destroy(actions, bind);
59333be6271SWANG Cong 	return err;
5941da177e4SLinus Torvalds }
5951da177e4SLinus Torvalds 
5961da177e4SLinus Torvalds int tcf_action_copy_stats(struct sk_buff *skb, struct tc_action *a,
5971da177e4SLinus Torvalds 			  int compat_mode)
5981da177e4SLinus Torvalds {
5991da177e4SLinus Torvalds 	int err = 0;
6001da177e4SLinus Torvalds 	struct gnet_dump d;
6017eb8896dSWANG Cong 	struct tcf_common *p = a->priv;
6021da177e4SLinus Torvalds 
6037eb8896dSWANG Cong 	if (p == NULL)
6041da177e4SLinus Torvalds 		goto errout;
6051da177e4SLinus Torvalds 
6061da177e4SLinus Torvalds 	/* compat_mode being true specifies a call that is supposed
60706fe9fb4SDirk Hohndel 	 * to add additional backward compatibility statistic TLVs.
6081da177e4SLinus Torvalds 	 */
6091da177e4SLinus Torvalds 	if (compat_mode) {
6101da177e4SLinus Torvalds 		if (a->type == TCA_OLD_COMPAT)
6111da177e4SLinus Torvalds 			err = gnet_stats_start_copy_compat(skb, 0,
6127eb8896dSWANG Cong 				TCA_STATS, TCA_XSTATS, &p->tcfc_lock, &d);
6131da177e4SLinus Torvalds 		else
6141da177e4SLinus Torvalds 			return 0;
6151da177e4SLinus Torvalds 	} else
6161da177e4SLinus Torvalds 		err = gnet_stats_start_copy(skb, TCA_ACT_STATS,
6177eb8896dSWANG Cong 					    &p->tcfc_lock, &d);
6181da177e4SLinus Torvalds 
6191da177e4SLinus Torvalds 	if (err < 0)
6201da177e4SLinus Torvalds 		goto errout;
6211da177e4SLinus Torvalds 
6227eb8896dSWANG Cong 	if (gnet_stats_copy_basic(&d, &p->tcfc_bstats) < 0 ||
6237eb8896dSWANG Cong 	    gnet_stats_copy_rate_est(&d, &p->tcfc_bstats,
6247eb8896dSWANG Cong 				     &p->tcfc_rate_est) < 0 ||
6257eb8896dSWANG Cong 	    gnet_stats_copy_queue(&d, &p->tcfc_qstats) < 0)
6261da177e4SLinus Torvalds 		goto errout;
6271da177e4SLinus Torvalds 
6281da177e4SLinus Torvalds 	if (gnet_stats_finish_copy(&d) < 0)
6291da177e4SLinus Torvalds 		goto errout;
6301da177e4SLinus Torvalds 
6311da177e4SLinus Torvalds 	return 0;
6321da177e4SLinus Torvalds 
6331da177e4SLinus Torvalds errout:
6341da177e4SLinus Torvalds 	return -1;
6351da177e4SLinus Torvalds }
6361da177e4SLinus Torvalds 
6371da177e4SLinus Torvalds static int
63833be6271SWANG Cong tca_get_fill(struct sk_buff *skb, struct list_head *actions, u32 portid, u32 seq,
639e431b8c0SJamal Hadi Salim 	     u16 flags, int event, int bind, int ref)
6401da177e4SLinus Torvalds {
6411da177e4SLinus Torvalds 	struct tcamsg *t;
6421da177e4SLinus Torvalds 	struct nlmsghdr *nlh;
64327a884dcSArnaldo Carvalho de Melo 	unsigned char *b = skb_tail_pointer(skb);
6444b3550efSPatrick McHardy 	struct nlattr *nest;
6451da177e4SLinus Torvalds 
64615e47304SEric W. Biederman 	nlh = nlmsg_put(skb, portid, seq, event, sizeof(*t), flags);
6478b00a53cSDavid S. Miller 	if (!nlh)
6488b00a53cSDavid S. Miller 		goto out_nlmsg_trim;
6498b00a53cSDavid S. Miller 	t = nlmsg_data(nlh);
6501da177e4SLinus Torvalds 	t->tca_family = AF_UNSPEC;
6519ef1d4c7SPatrick McHardy 	t->tca__pad1 = 0;
6529ef1d4c7SPatrick McHardy 	t->tca__pad2 = 0;
6531da177e4SLinus Torvalds 
6544b3550efSPatrick McHardy 	nest = nla_nest_start(skb, TCA_ACT_TAB);
6554b3550efSPatrick McHardy 	if (nest == NULL)
6568b00a53cSDavid S. Miller 		goto out_nlmsg_trim;
6571da177e4SLinus Torvalds 
65833be6271SWANG Cong 	if (tcf_action_dump(skb, actions, bind, ref) < 0)
6598b00a53cSDavid S. Miller 		goto out_nlmsg_trim;
6601da177e4SLinus Torvalds 
6614b3550efSPatrick McHardy 	nla_nest_end(skb, nest);
6621da177e4SLinus Torvalds 
66327a884dcSArnaldo Carvalho de Melo 	nlh->nlmsg_len = skb_tail_pointer(skb) - b;
6641da177e4SLinus Torvalds 	return skb->len;
6651da177e4SLinus Torvalds 
6668b00a53cSDavid S. Miller out_nlmsg_trim:
667dc5fc579SArnaldo Carvalho de Melo 	nlmsg_trim(skb, b);
6681da177e4SLinus Torvalds 	return -1;
6691da177e4SLinus Torvalds }
6701da177e4SLinus Torvalds 
6711da177e4SLinus Torvalds static int
67215e47304SEric W. Biederman act_get_notify(struct net *net, u32 portid, struct nlmsghdr *n,
67333be6271SWANG Cong 	       struct list_head *actions, int event)
6741da177e4SLinus Torvalds {
6751da177e4SLinus Torvalds 	struct sk_buff *skb;
6761da177e4SLinus Torvalds 
6771da177e4SLinus Torvalds 	skb = alloc_skb(NLMSG_GOODSIZE, GFP_KERNEL);
6781da177e4SLinus Torvalds 	if (!skb)
6791da177e4SLinus Torvalds 		return -ENOBUFS;
68033be6271SWANG Cong 	if (tca_get_fill(skb, actions, portid, n->nlmsg_seq, 0, event, 0, 0) <= 0) {
6811da177e4SLinus Torvalds 		kfree_skb(skb);
6821da177e4SLinus Torvalds 		return -EINVAL;
6831da177e4SLinus Torvalds 	}
6842942e900SThomas Graf 
68515e47304SEric W. Biederman 	return rtnl_unicast(skb, net, portid);
6861da177e4SLinus Torvalds }
6871da177e4SLinus Torvalds 
6881da177e4SLinus Torvalds static struct tc_action *
68915e47304SEric W. Biederman tcf_action_get_1(struct nlattr *nla, struct nlmsghdr *n, u32 portid)
6901da177e4SLinus Torvalds {
6917ba699c6SPatrick McHardy 	struct nlattr *tb[TCA_ACT_MAX + 1];
6921da177e4SLinus Torvalds 	struct tc_action *a;
6931da177e4SLinus Torvalds 	int index;
694ab27cfb8SPatrick McHardy 	int err;
6951da177e4SLinus Torvalds 
696cee63723SPatrick McHardy 	err = nla_parse_nested(tb, TCA_ACT_MAX, nla, NULL);
697cee63723SPatrick McHardy 	if (err < 0)
698ab27cfb8SPatrick McHardy 		goto err_out;
6991da177e4SLinus Torvalds 
700cee63723SPatrick McHardy 	err = -EINVAL;
7017ba699c6SPatrick McHardy 	if (tb[TCA_ACT_INDEX] == NULL ||
7027ba699c6SPatrick McHardy 	    nla_len(tb[TCA_ACT_INDEX]) < sizeof(index))
703ab27cfb8SPatrick McHardy 		goto err_out;
7041587bac4SPatrick McHardy 	index = nla_get_u32(tb[TCA_ACT_INDEX]);
7051da177e4SLinus Torvalds 
706ab27cfb8SPatrick McHardy 	err = -ENOMEM;
7070da974f4SPanagiotis Issaris 	a = kzalloc(sizeof(struct tc_action), GFP_KERNEL);
7081da177e4SLinus Torvalds 	if (a == NULL)
709ab27cfb8SPatrick McHardy 		goto err_out;
7101da177e4SLinus Torvalds 
71133be6271SWANG Cong 	INIT_LIST_HEAD(&a->list);
712ab27cfb8SPatrick McHardy 	err = -EINVAL;
7137ba699c6SPatrick McHardy 	a->ops = tc_lookup_action(tb[TCA_ACT_KIND]);
71463acd680SJamal Hadi Salim 	if (a->ops == NULL) /* could happen in batch of actions */
7151da177e4SLinus Torvalds 		goto err_free;
716ab27cfb8SPatrick McHardy 	err = -ENOENT;
7171da177e4SLinus Torvalds 	if (a->ops->lookup(a, index) == 0)
7181da177e4SLinus Torvalds 		goto err_mod;
7191da177e4SLinus Torvalds 
7201da177e4SLinus Torvalds 	module_put(a->ops->owner);
7211da177e4SLinus Torvalds 	return a;
722ab27cfb8SPatrick McHardy 
7231da177e4SLinus Torvalds err_mod:
7241da177e4SLinus Torvalds 	module_put(a->ops->owner);
7251da177e4SLinus Torvalds err_free:
7261da177e4SLinus Torvalds 	kfree(a);
727ab27cfb8SPatrick McHardy err_out:
728ab27cfb8SPatrick McHardy 	return ERR_PTR(err);
7291da177e4SLinus Torvalds }
7301da177e4SLinus Torvalds 
73133be6271SWANG Cong static void cleanup_a(struct list_head *actions)
7321da177e4SLinus Torvalds {
73333be6271SWANG Cong 	struct tc_action *a, *tmp;
7341da177e4SLinus Torvalds 
73533be6271SWANG Cong 	list_for_each_entry_safe(a, tmp, actions, list) {
73633be6271SWANG Cong 		list_del(&a->list);
7371da177e4SLinus Torvalds 		kfree(a);
7381da177e4SLinus Torvalds 	}
7391da177e4SLinus Torvalds }
7401da177e4SLinus Torvalds 
7411da177e4SLinus Torvalds static struct tc_action *create_a(int i)
7421da177e4SLinus Torvalds {
7431da177e4SLinus Torvalds 	struct tc_action *act;
7441da177e4SLinus Torvalds 
7450da974f4SPanagiotis Issaris 	act = kzalloc(sizeof(*act), GFP_KERNEL);
7461da177e4SLinus Torvalds 	if (act == NULL) {
7476ff9c364Sstephen hemminger 		pr_debug("create_a: failed to alloc!\n");
7481da177e4SLinus Torvalds 		return NULL;
7491da177e4SLinus Torvalds 	}
7501da177e4SLinus Torvalds 	act->order = i;
75133be6271SWANG Cong 	INIT_LIST_HEAD(&act->list);
7521da177e4SLinus Torvalds 	return act;
7531da177e4SLinus Torvalds }
7541da177e4SLinus Torvalds 
7557316ae88STom Goff static int tca_action_flush(struct net *net, struct nlattr *nla,
75615e47304SEric W. Biederman 			    struct nlmsghdr *n, u32 portid)
7571da177e4SLinus Torvalds {
7581da177e4SLinus Torvalds 	struct sk_buff *skb;
7591da177e4SLinus Torvalds 	unsigned char *b;
7601da177e4SLinus Torvalds 	struct nlmsghdr *nlh;
7611da177e4SLinus Torvalds 	struct tcamsg *t;
7621da177e4SLinus Torvalds 	struct netlink_callback dcb;
7634b3550efSPatrick McHardy 	struct nlattr *nest;
7647ba699c6SPatrick McHardy 	struct nlattr *tb[TCA_ACT_MAX + 1];
7657ba699c6SPatrick McHardy 	struct nlattr *kind;
7661da177e4SLinus Torvalds 	struct tc_action *a = create_a(0);
76736723873SJamal Hadi Salim 	int err = -ENOMEM;
7681da177e4SLinus Torvalds 
7691da177e4SLinus Torvalds 	if (a == NULL) {
7706ff9c364Sstephen hemminger 		pr_debug("tca_action_flush: couldnt create tc_action\n");
7711da177e4SLinus Torvalds 		return err;
7721da177e4SLinus Torvalds 	}
7731da177e4SLinus Torvalds 
7741da177e4SLinus Torvalds 	skb = alloc_skb(NLMSG_GOODSIZE, GFP_KERNEL);
7751da177e4SLinus Torvalds 	if (!skb) {
7766ff9c364Sstephen hemminger 		pr_debug("tca_action_flush: failed skb alloc\n");
7771da177e4SLinus Torvalds 		kfree(a);
77836723873SJamal Hadi Salim 		return err;
7791da177e4SLinus Torvalds 	}
7801da177e4SLinus Torvalds 
78127a884dcSArnaldo Carvalho de Melo 	b = skb_tail_pointer(skb);
7821da177e4SLinus Torvalds 
783cee63723SPatrick McHardy 	err = nla_parse_nested(tb, TCA_ACT_MAX, nla, NULL);
784cee63723SPatrick McHardy 	if (err < 0)
7851da177e4SLinus Torvalds 		goto err_out;
7861da177e4SLinus Torvalds 
787cee63723SPatrick McHardy 	err = -EINVAL;
7887ba699c6SPatrick McHardy 	kind = tb[TCA_ACT_KIND];
7891da177e4SLinus Torvalds 	a->ops = tc_lookup_action(kind);
79063acd680SJamal Hadi Salim 	if (a->ops == NULL) /*some idjot trying to flush unknown action */
7911da177e4SLinus Torvalds 		goto err_out;
7921da177e4SLinus Torvalds 
79315e47304SEric W. Biederman 	nlh = nlmsg_put(skb, portid, n->nlmsg_seq, RTM_DELACTION, sizeof(*t), 0);
7948b00a53cSDavid S. Miller 	if (!nlh)
7958b00a53cSDavid S. Miller 		goto out_module_put;
7968b00a53cSDavid S. Miller 	t = nlmsg_data(nlh);
7971da177e4SLinus Torvalds 	t->tca_family = AF_UNSPEC;
7989ef1d4c7SPatrick McHardy 	t->tca__pad1 = 0;
7999ef1d4c7SPatrick McHardy 	t->tca__pad2 = 0;
8001da177e4SLinus Torvalds 
8014b3550efSPatrick McHardy 	nest = nla_nest_start(skb, TCA_ACT_TAB);
8024b3550efSPatrick McHardy 	if (nest == NULL)
8038b00a53cSDavid S. Miller 		goto out_module_put;
8041da177e4SLinus Torvalds 
8051da177e4SLinus Torvalds 	err = a->ops->walk(skb, &dcb, RTM_DELACTION, a);
8061da177e4SLinus Torvalds 	if (err < 0)
8078b00a53cSDavid S. Miller 		goto out_module_put;
808f97017cdSJamal Hadi Salim 	if (err == 0)
809f97017cdSJamal Hadi Salim 		goto noflush_out;
8101da177e4SLinus Torvalds 
8114b3550efSPatrick McHardy 	nla_nest_end(skb, nest);
8121da177e4SLinus Torvalds 
81327a884dcSArnaldo Carvalho de Melo 	nlh->nlmsg_len = skb_tail_pointer(skb) - b;
8141da177e4SLinus Torvalds 	nlh->nlmsg_flags |= NLM_F_ROOT;
8151da177e4SLinus Torvalds 	module_put(a->ops->owner);
8161da177e4SLinus Torvalds 	kfree(a);
81715e47304SEric W. Biederman 	err = rtnetlink_send(skb, net, portid, RTNLGRP_TC,
818cc7ec456SEric Dumazet 			     n->nlmsg_flags & NLM_F_ECHO);
8191da177e4SLinus Torvalds 	if (err > 0)
8201da177e4SLinus Torvalds 		return 0;
8211da177e4SLinus Torvalds 
8221da177e4SLinus Torvalds 	return err;
8231da177e4SLinus Torvalds 
8248b00a53cSDavid S. Miller out_module_put:
825ebbaeab1SThomas Graf 	module_put(a->ops->owner);
8261da177e4SLinus Torvalds err_out:
827f97017cdSJamal Hadi Salim noflush_out:
8281da177e4SLinus Torvalds 	kfree_skb(skb);
8291da177e4SLinus Torvalds 	kfree(a);
8301da177e4SLinus Torvalds 	return err;
8311da177e4SLinus Torvalds }
8321da177e4SLinus Torvalds 
8331da177e4SLinus Torvalds static int
834a56e1953SWANG Cong tcf_del_notify(struct net *net, struct nlmsghdr *n, struct list_head *actions,
835a56e1953SWANG Cong 	       u32 portid)
836a56e1953SWANG Cong {
837a56e1953SWANG Cong 	int ret;
838a56e1953SWANG Cong 	struct sk_buff *skb;
839a56e1953SWANG Cong 
840a56e1953SWANG Cong 	skb = alloc_skb(NLMSG_GOODSIZE, GFP_KERNEL);
841a56e1953SWANG Cong 	if (!skb)
842a56e1953SWANG Cong 		return -ENOBUFS;
843a56e1953SWANG Cong 
844a56e1953SWANG Cong 	if (tca_get_fill(skb, actions, portid, n->nlmsg_seq, 0, RTM_DELACTION,
845a56e1953SWANG Cong 			 0, 1) <= 0) {
846a56e1953SWANG Cong 		kfree_skb(skb);
847a56e1953SWANG Cong 		return -EINVAL;
848a56e1953SWANG Cong 	}
849a56e1953SWANG Cong 
850a56e1953SWANG Cong 	/* now do the delete */
851*55334a5dSWANG Cong 	ret = tcf_action_destroy(actions, 0);
852*55334a5dSWANG Cong 	if (ret < 0) {
853*55334a5dSWANG Cong 		kfree_skb(skb);
854*55334a5dSWANG Cong 		return ret;
855*55334a5dSWANG Cong 	}
856a56e1953SWANG Cong 
857a56e1953SWANG Cong 	ret = rtnetlink_send(skb, net, portid, RTNLGRP_TC,
858a56e1953SWANG Cong 			     n->nlmsg_flags & NLM_F_ECHO);
859a56e1953SWANG Cong 	if (ret > 0)
860a56e1953SWANG Cong 		return 0;
861a56e1953SWANG Cong 	return ret;
862a56e1953SWANG Cong }
863a56e1953SWANG Cong 
864a56e1953SWANG Cong static int
8657316ae88STom Goff tca_action_gd(struct net *net, struct nlattr *nla, struct nlmsghdr *n,
86615e47304SEric W. Biederman 	      u32 portid, int event)
8671da177e4SLinus Torvalds {
868cee63723SPatrick McHardy 	int i, ret;
8697ba699c6SPatrick McHardy 	struct nlattr *tb[TCA_ACT_MAX_PRIO + 1];
87033be6271SWANG Cong 	struct tc_action *act;
87133be6271SWANG Cong 	LIST_HEAD(actions);
8721da177e4SLinus Torvalds 
873cee63723SPatrick McHardy 	ret = nla_parse_nested(tb, TCA_ACT_MAX_PRIO, nla, NULL);
874cee63723SPatrick McHardy 	if (ret < 0)
875cee63723SPatrick McHardy 		return ret;
8761da177e4SLinus Torvalds 
8771da177e4SLinus Torvalds 	if (event == RTM_DELACTION && n->nlmsg_flags & NLM_F_ROOT) {
878f97017cdSJamal Hadi Salim 		if (tb[1] != NULL)
87915e47304SEric W. Biederman 			return tca_action_flush(net, tb[1], n, portid);
880f97017cdSJamal Hadi Salim 		else
881f97017cdSJamal Hadi Salim 			return -EINVAL;
8821da177e4SLinus Torvalds 	}
8831da177e4SLinus Torvalds 
8847ba699c6SPatrick McHardy 	for (i = 1; i <= TCA_ACT_MAX_PRIO && tb[i]; i++) {
88515e47304SEric W. Biederman 		act = tcf_action_get_1(tb[i], n, portid);
886ab27cfb8SPatrick McHardy 		if (IS_ERR(act)) {
887ab27cfb8SPatrick McHardy 			ret = PTR_ERR(act);
8881da177e4SLinus Torvalds 			goto err;
889ab27cfb8SPatrick McHardy 		}
8907ba699c6SPatrick McHardy 		act->order = i;
89133be6271SWANG Cong 		list_add_tail(&act->list, &actions);
8921da177e4SLinus Torvalds 	}
8931da177e4SLinus Torvalds 
8941da177e4SLinus Torvalds 	if (event == RTM_GETACTION)
89533be6271SWANG Cong 		ret = act_get_notify(net, portid, n, &actions, event);
8961da177e4SLinus Torvalds 	else { /* delete */
897a56e1953SWANG Cong 		ret = tcf_del_notify(net, n, &actions, portid);
898a56e1953SWANG Cong 		if (ret)
8991da177e4SLinus Torvalds 			goto err;
9001da177e4SLinus Torvalds 		return ret;
9011da177e4SLinus Torvalds 	}
9021da177e4SLinus Torvalds err:
90333be6271SWANG Cong 	cleanup_a(&actions);
9041da177e4SLinus Torvalds 	return ret;
9051da177e4SLinus Torvalds }
9061da177e4SLinus Torvalds 
907a56e1953SWANG Cong static int
908a56e1953SWANG Cong tcf_add_notify(struct net *net, struct nlmsghdr *n, struct list_head *actions,
909a56e1953SWANG Cong 	       u32 portid)
9101da177e4SLinus Torvalds {
9111da177e4SLinus Torvalds 	struct sk_buff *skb;
9121da177e4SLinus Torvalds 	int err = 0;
9131da177e4SLinus Torvalds 
9141da177e4SLinus Torvalds 	skb = alloc_skb(NLMSG_GOODSIZE, GFP_KERNEL);
9151da177e4SLinus Torvalds 	if (!skb)
9161da177e4SLinus Torvalds 		return -ENOBUFS;
9171da177e4SLinus Torvalds 
918a56e1953SWANG Cong 	if (tca_get_fill(skb, actions, portid, n->nlmsg_seq, n->nlmsg_flags,
919a56e1953SWANG Cong 			 RTM_NEWACTION, 0, 0) <= 0) {
920a56e1953SWANG Cong 		kfree_skb(skb);
921a56e1953SWANG Cong 		return -EINVAL;
922a56e1953SWANG Cong 	}
9231da177e4SLinus Torvalds 
924a56e1953SWANG Cong 	err = rtnetlink_send(skb, net, portid, RTNLGRP_TC,
925a56e1953SWANG Cong 			     n->nlmsg_flags & NLM_F_ECHO);
9261da177e4SLinus Torvalds 	if (err > 0)
9271da177e4SLinus Torvalds 		err = 0;
9281da177e4SLinus Torvalds 	return err;
9291da177e4SLinus Torvalds }
9301da177e4SLinus Torvalds 
9311da177e4SLinus Torvalds static int
9327316ae88STom Goff tcf_action_add(struct net *net, struct nlattr *nla, struct nlmsghdr *n,
93315e47304SEric W. Biederman 	       u32 portid, int ovr)
9341da177e4SLinus Torvalds {
9351da177e4SLinus Torvalds 	int ret = 0;
93633be6271SWANG Cong 	LIST_HEAD(actions);
9371da177e4SLinus Torvalds 
93833be6271SWANG Cong 	ret = tcf_action_init(net, nla, NULL, NULL, ovr, 0, &actions);
93933be6271SWANG Cong 	if (ret)
9401da177e4SLinus Torvalds 		goto done;
9411da177e4SLinus Torvalds 
9421da177e4SLinus Torvalds 	/* dump then free all the actions after update; inserted policy
9431da177e4SLinus Torvalds 	 * stays intact
944cc7ec456SEric Dumazet 	 */
945a56e1953SWANG Cong 	ret = tcf_add_notify(net, n, &actions, portid);
94633be6271SWANG Cong 	cleanup_a(&actions);
9471da177e4SLinus Torvalds done:
9481da177e4SLinus Torvalds 	return ret;
9491da177e4SLinus Torvalds }
9501da177e4SLinus Torvalds 
951661d2967SThomas Graf static int tc_ctl_action(struct sk_buff *skb, struct nlmsghdr *n)
9521da177e4SLinus Torvalds {
9533b1e0a65SYOSHIFUJI Hideaki 	struct net *net = sock_net(skb->sk);
9547ba699c6SPatrick McHardy 	struct nlattr *tca[TCA_ACT_MAX + 1];
95515e47304SEric W. Biederman 	u32 portid = skb ? NETLINK_CB(skb).portid : 0;
9561da177e4SLinus Torvalds 	int ret = 0, ovr = 0;
9571da177e4SLinus Torvalds 
958dfc47ef8SEric W. Biederman 	if ((n->nlmsg_type != RTM_GETACTION) && !capable(CAP_NET_ADMIN))
959dfc47ef8SEric W. Biederman 		return -EPERM;
960dfc47ef8SEric W. Biederman 
9617ba699c6SPatrick McHardy 	ret = nlmsg_parse(n, sizeof(struct tcamsg), tca, TCA_ACT_MAX, NULL);
9627ba699c6SPatrick McHardy 	if (ret < 0)
9637ba699c6SPatrick McHardy 		return ret;
9647ba699c6SPatrick McHardy 
9657ba699c6SPatrick McHardy 	if (tca[TCA_ACT_TAB] == NULL) {
9666ff9c364Sstephen hemminger 		pr_notice("tc_ctl_action: received NO action attribs\n");
9671da177e4SLinus Torvalds 		return -EINVAL;
9681da177e4SLinus Torvalds 	}
9691da177e4SLinus Torvalds 
970cc7ec456SEric Dumazet 	/* n->nlmsg_flags & NLM_F_CREATE */
9711da177e4SLinus Torvalds 	switch (n->nlmsg_type) {
9721da177e4SLinus Torvalds 	case RTM_NEWACTION:
9731da177e4SLinus Torvalds 		/* we are going to assume all other flags
97425985edcSLucas De Marchi 		 * imply create only if it doesn't exist
9751da177e4SLinus Torvalds 		 * Note that CREATE | EXCL implies that
9761da177e4SLinus Torvalds 		 * but since we want avoid ambiguity (eg when flags
9771da177e4SLinus Torvalds 		 * is zero) then just set this
9781da177e4SLinus Torvalds 		 */
9791da177e4SLinus Torvalds 		if (n->nlmsg_flags & NLM_F_REPLACE)
9801da177e4SLinus Torvalds 			ovr = 1;
9811da177e4SLinus Torvalds replay:
98215e47304SEric W. Biederman 		ret = tcf_action_add(net, tca[TCA_ACT_TAB], n, portid, ovr);
9831da177e4SLinus Torvalds 		if (ret == -EAGAIN)
9841da177e4SLinus Torvalds 			goto replay;
9851da177e4SLinus Torvalds 		break;
9861da177e4SLinus Torvalds 	case RTM_DELACTION:
9877316ae88STom Goff 		ret = tca_action_gd(net, tca[TCA_ACT_TAB], n,
98815e47304SEric W. Biederman 				    portid, RTM_DELACTION);
9891da177e4SLinus Torvalds 		break;
9901da177e4SLinus Torvalds 	case RTM_GETACTION:
9917316ae88STom Goff 		ret = tca_action_gd(net, tca[TCA_ACT_TAB], n,
99215e47304SEric W. Biederman 				    portid, RTM_GETACTION);
9931da177e4SLinus Torvalds 		break;
9941da177e4SLinus Torvalds 	default:
9951da177e4SLinus Torvalds 		BUG();
9961da177e4SLinus Torvalds 	}
9971da177e4SLinus Torvalds 
9981da177e4SLinus Torvalds 	return ret;
9991da177e4SLinus Torvalds }
10001da177e4SLinus Torvalds 
10017ba699c6SPatrick McHardy static struct nlattr *
10023a6c2b41SPatrick McHardy find_dump_kind(const struct nlmsghdr *n)
10031da177e4SLinus Torvalds {
10047ba699c6SPatrick McHardy 	struct nlattr *tb1, *tb2[TCA_ACT_MAX + 1];
10057ba699c6SPatrick McHardy 	struct nlattr *tb[TCA_ACT_MAX_PRIO + 1];
10067ba699c6SPatrick McHardy 	struct nlattr *nla[TCAA_MAX + 1];
10077ba699c6SPatrick McHardy 	struct nlattr *kind;
10081da177e4SLinus Torvalds 
1009c96c9471SPatrick McHardy 	if (nlmsg_parse(n, sizeof(struct tcamsg), nla, TCAA_MAX, NULL) < 0)
10101da177e4SLinus Torvalds 		return NULL;
10117ba699c6SPatrick McHardy 	tb1 = nla[TCA_ACT_TAB];
10121da177e4SLinus Torvalds 	if (tb1 == NULL)
10131da177e4SLinus Torvalds 		return NULL;
10141da177e4SLinus Torvalds 
10157ba699c6SPatrick McHardy 	if (nla_parse(tb, TCA_ACT_MAX_PRIO, nla_data(tb1),
10167ba699c6SPatrick McHardy 		      NLMSG_ALIGN(nla_len(tb1)), NULL) < 0)
10171da177e4SLinus Torvalds 		return NULL;
10181da177e4SLinus Torvalds 
10196d834e04SPatrick McHardy 	if (tb[1] == NULL)
10206d834e04SPatrick McHardy 		return NULL;
10216d834e04SPatrick McHardy 	if (nla_parse(tb2, TCA_ACT_MAX, nla_data(tb[1]),
10226d834e04SPatrick McHardy 		      nla_len(tb[1]), NULL) < 0)
10231da177e4SLinus Torvalds 		return NULL;
10247ba699c6SPatrick McHardy 	kind = tb2[TCA_ACT_KIND];
10251da177e4SLinus Torvalds 
102626dab893SThomas Graf 	return kind;
10271da177e4SLinus Torvalds }
10281da177e4SLinus Torvalds 
10291da177e4SLinus Torvalds static int
10301da177e4SLinus Torvalds tc_dump_action(struct sk_buff *skb, struct netlink_callback *cb)
10311da177e4SLinus Torvalds {
10321da177e4SLinus Torvalds 	struct nlmsghdr *nlh;
103327a884dcSArnaldo Carvalho de Melo 	unsigned char *b = skb_tail_pointer(skb);
10344b3550efSPatrick McHardy 	struct nlattr *nest;
10351da177e4SLinus Torvalds 	struct tc_action_ops *a_o;
10361da177e4SLinus Torvalds 	struct tc_action a;
10371da177e4SLinus Torvalds 	int ret = 0;
10388b00a53cSDavid S. Miller 	struct tcamsg *t = (struct tcamsg *) nlmsg_data(cb->nlh);
10397ba699c6SPatrick McHardy 	struct nlattr *kind = find_dump_kind(cb->nlh);
10401da177e4SLinus Torvalds 
10411da177e4SLinus Torvalds 	if (kind == NULL) {
10426ff9c364Sstephen hemminger 		pr_info("tc_dump_action: action bad kind\n");
10431da177e4SLinus Torvalds 		return 0;
10441da177e4SLinus Torvalds 	}
10451da177e4SLinus Torvalds 
104626dab893SThomas Graf 	a_o = tc_lookup_action(kind);
1047cc7ec456SEric Dumazet 	if (a_o == NULL)
10481da177e4SLinus Torvalds 		return 0;
10491da177e4SLinus Torvalds 
10501da177e4SLinus Torvalds 	memset(&a, 0, sizeof(struct tc_action));
10511da177e4SLinus Torvalds 	a.ops = a_o;
10521da177e4SLinus Torvalds 
105315e47304SEric W. Biederman 	nlh = nlmsg_put(skb, NETLINK_CB(cb->skb).portid, cb->nlh->nlmsg_seq,
10548b00a53cSDavid S. Miller 			cb->nlh->nlmsg_type, sizeof(*t), 0);
10558b00a53cSDavid S. Miller 	if (!nlh)
10568b00a53cSDavid S. Miller 		goto out_module_put;
10578b00a53cSDavid S. Miller 	t = nlmsg_data(nlh);
10581da177e4SLinus Torvalds 	t->tca_family = AF_UNSPEC;
10599ef1d4c7SPatrick McHardy 	t->tca__pad1 = 0;
10609ef1d4c7SPatrick McHardy 	t->tca__pad2 = 0;
10611da177e4SLinus Torvalds 
10624b3550efSPatrick McHardy 	nest = nla_nest_start(skb, TCA_ACT_TAB);
10634b3550efSPatrick McHardy 	if (nest == NULL)
10648b00a53cSDavid S. Miller 		goto out_module_put;
10651da177e4SLinus Torvalds 
10661da177e4SLinus Torvalds 	ret = a_o->walk(skb, cb, RTM_GETACTION, &a);
10671da177e4SLinus Torvalds 	if (ret < 0)
10688b00a53cSDavid S. Miller 		goto out_module_put;
10691da177e4SLinus Torvalds 
10701da177e4SLinus Torvalds 	if (ret > 0) {
10714b3550efSPatrick McHardy 		nla_nest_end(skb, nest);
10721da177e4SLinus Torvalds 		ret = skb->len;
10731da177e4SLinus Torvalds 	} else
10744b3550efSPatrick McHardy 		nla_nest_cancel(skb, nest);
10751da177e4SLinus Torvalds 
107627a884dcSArnaldo Carvalho de Melo 	nlh->nlmsg_len = skb_tail_pointer(skb) - b;
107715e47304SEric W. Biederman 	if (NETLINK_CB(cb->skb).portid && ret)
10781da177e4SLinus Torvalds 		nlh->nlmsg_flags |= NLM_F_MULTI;
10791da177e4SLinus Torvalds 	module_put(a_o->owner);
10801da177e4SLinus Torvalds 	return skb->len;
10811da177e4SLinus Torvalds 
10828b00a53cSDavid S. Miller out_module_put:
10831da177e4SLinus Torvalds 	module_put(a_o->owner);
1084dc5fc579SArnaldo Carvalho de Melo 	nlmsg_trim(skb, b);
10851da177e4SLinus Torvalds 	return skb->len;
10861da177e4SLinus Torvalds }
10871da177e4SLinus Torvalds 
10881da177e4SLinus Torvalds static int __init tc_action_init(void)
10891da177e4SLinus Torvalds {
1090c7ac8679SGreg Rose 	rtnl_register(PF_UNSPEC, RTM_NEWACTION, tc_ctl_action, NULL, NULL);
1091c7ac8679SGreg Rose 	rtnl_register(PF_UNSPEC, RTM_DELACTION, tc_ctl_action, NULL, NULL);
1092c7ac8679SGreg Rose 	rtnl_register(PF_UNSPEC, RTM_GETACTION, tc_ctl_action, tc_dump_action,
1093c7ac8679SGreg Rose 		      NULL);
10941da177e4SLinus Torvalds 
10951da177e4SLinus Torvalds 	return 0;
10961da177e4SLinus Torvalds }
10971da177e4SLinus Torvalds 
10981da177e4SLinus Torvalds subsys_initcall(tc_action_init);
1099