blob: 0f31f09946ab0df45a443a413867a30cc5ac36c4 [file] [log] [blame]
Linus Torvalds1da177e2005-04-16 15:20:36 -07001/*
2 * net/sched/act_api.c Packet action API.
3 *
4 * This program is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU General Public License
6 * as published by the Free Software Foundation; either version
7 * 2 of the License, or (at your option) any later version.
8 *
9 * Author: Jamal Hadi Salim
10 *
11 *
12 */
13
Linus Torvalds1da177e2005-04-16 15:20:36 -070014#include <linux/types.h>
15#include <linux/kernel.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070016#include <linux/string.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070017#include <linux/errno.h>
Tejun Heo5a0e3ad2010-03-24 17:04:11 +090018#include <linux/slab.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070019#include <linux/skbuff.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070020#include <linux/init.h>
21#include <linux/kmod.h>
Patrick McHardyab27cfb2008-01-23 20:33:13 -080022#include <linux/err.h>
Paul Gortmaker3a9a2312011-05-27 09:12:25 -040023#include <linux/module.h>
Jiri Pirkob3f55bd2017-10-11 09:41:08 +020024#include <linux/rhashtable.h>
25#include <linux/list.h>
Denis V. Lunevb8542722007-12-01 00:21:31 +110026#include <net/net_namespace.h>
27#include <net/sock.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070028#include <net/sch_generic.h>
Jamal Hadi Salim1045ba72017-01-24 07:02:41 -050029#include <net/pkt_cls.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070030#include <net/act_api.h>
Arnaldo Carvalho de Melodc5fc572007-03-25 23:06:12 -070031#include <net/netlink.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070032
Jiri Pirkodb505142017-05-17 11:08:03 +020033static int tcf_action_goto_chain_init(struct tc_action *a, struct tcf_proto *tp)
34{
35 u32 chain_index = a->tcfa_action & TC_ACT_EXT_VAL_MASK;
36
37 if (!tp)
38 return -EINVAL;
WANG Cong367a8ce2017-05-23 09:42:37 -070039 a->goto_chain = tcf_chain_get(tp->chain->block, chain_index, true);
Jiri Pirkodb505142017-05-17 11:08:03 +020040 if (!a->goto_chain)
41 return -ENOMEM;
42 return 0;
43}
44
45static void tcf_action_goto_chain_fini(struct tc_action *a)
46{
47 tcf_chain_put(a->goto_chain);
48}
49
50static void tcf_action_goto_chain_exec(const struct tc_action *a,
51 struct tcf_result *res)
52{
53 const struct tcf_chain *chain = a->goto_chain;
54
55 res->goto_tp = rcu_dereference_bh(chain->filter_chain);
56}
57
Vlad Busloveec94fd2018-07-05 17:24:23 +030058static void tcf_free_cookie_rcu(struct rcu_head *p)
59{
60 struct tc_cookie *cookie = container_of(p, struct tc_cookie, rcu);
61
62 kfree(cookie->data);
63 kfree(cookie);
64}
65
66static void tcf_set_action_cookie(struct tc_cookie __rcu **old_cookie,
67 struct tc_cookie *new_cookie)
68{
69 struct tc_cookie *old;
70
71 old = xchg(old_cookie, new_cookie);
72 if (old)
73 call_rcu(&old->rcu, tcf_free_cookie_rcu);
74}
75
Cong Wangd7fb60b2017-09-11 16:33:30 -070076/* XXX: For standalone actions, we don't need a RCU grace period either, because
77 * actions are always connected to filters and filters are already destroyed in
78 * RCU callbacks, so after a RCU grace period actions are already disconnected
79 * from filters. Readers later can not find us.
80 */
81static void free_tcf(struct tc_action *p)
Eric Dumazet519c8182015-07-06 05:18:04 -070082{
Eric Dumazet519c8182015-07-06 05:18:04 -070083 free_percpu(p->cpu_bstats);
84 free_percpu(p->cpu_qstats);
Jamal Hadi Salim1045ba72017-01-24 07:02:41 -050085
Vlad Busloveec94fd2018-07-05 17:24:23 +030086 tcf_set_action_cookie(&p->act_cookie, NULL);
Jiri Pirkodb505142017-05-17 11:08:03 +020087 if (p->goto_chain)
88 tcf_action_goto_chain_fini(p);
Jamal Hadi Salim1045ba72017-01-24 07:02:41 -050089
Eric Dumazet519c8182015-07-06 05:18:04 -070090 kfree(p);
91}
92
Chris Mi65a206c2017-08-30 02:31:59 -040093static void tcf_idr_remove(struct tcf_idrinfo *idrinfo, struct tc_action *p)
David S. Millere9ce1cd2006-08-21 23:54:55 -070094{
Vlad Buslov290aa0a2018-05-21 23:03:04 +030095 spin_lock(&idrinfo->lock);
Matthew Wilcox9c160942017-11-28 09:48:43 -050096 idr_remove(&idrinfo->action_idr, p->tcfa_index);
Vlad Buslov290aa0a2018-05-21 23:03:04 +030097 spin_unlock(&idrinfo->lock);
Eric Dumazet1c0d32f2016-12-04 09:48:16 -080098 gen_kill_estimator(&p->tcfa_rate_est);
Cong Wangd7fb60b2017-09-11 16:33:30 -070099 free_tcf(p);
David S. Millere9ce1cd2006-08-21 23:54:55 -0700100}
David S. Millere9ce1cd2006-08-21 23:54:55 -0700101
Chris Mi65a206c2017-08-30 02:31:59 -0400102int __tcf_idr_release(struct tc_action *p, bool bind, bool strict)
David S. Millere9ce1cd2006-08-21 23:54:55 -0700103{
104 int ret = 0;
105
Cong Wanga159d3c2017-11-01 10:23:49 -0700106 ASSERT_RTNL();
107
Vlad Buslov036bb442018-07-05 17:24:24 +0300108 /* Release with strict==1 and bind==0 is only called through act API
109 * interface (classifiers always bind). Only case when action with
110 * positive reference count and zero bind count can exist is when it was
111 * also created with act API (unbinding last classifier will destroy the
112 * action if it was created by classifier). So only case when bind count
113 * can be changed after initial check is when unbound action is
114 * destroyed by act API while classifier binds to action with same id
115 * concurrently. This result either creation of new action(same behavior
116 * as before), or reusing existing action if concurrent process
117 * increments reference count before action is deleted. Both scenarios
118 * are acceptable.
119 */
David S. Millere9ce1cd2006-08-21 23:54:55 -0700120 if (p) {
121 if (bind)
Vlad Buslov036bb442018-07-05 17:24:24 +0300122 atomic_dec(&p->tcfa_bindcnt);
123 else if (strict && atomic_read(&p->tcfa_bindcnt) > 0)
WANG Cong55334a52014-02-11 17:07:34 -0800124 return -EPERM;
David S. Millere9ce1cd2006-08-21 23:54:55 -0700125
Vlad Buslov036bb442018-07-05 17:24:24 +0300126 if (atomic_read(&p->tcfa_bindcnt) <= 0 &&
127 refcount_dec_and_test(&p->tcfa_refcnt)) {
WANG Congec0595c2016-07-25 16:09:42 -0700128 if (p->ops->cleanup)
Cong Wang9a63b252017-12-05 12:53:07 -0800129 p->ops->cleanup(p);
Chris Mi65a206c2017-08-30 02:31:59 -0400130 tcf_idr_remove(p->idrinfo, p);
WANG Cong1d4150c2016-02-22 15:57:52 -0800131 ret = ACT_P_DELETED;
David S. Millere9ce1cd2006-08-21 23:54:55 -0700132 }
133 }
Daniel Borkmann28e6b672015-07-29 23:35:25 +0200134
David S. Millere9ce1cd2006-08-21 23:54:55 -0700135 return ret;
136}
Chris Mi65a206c2017-08-30 02:31:59 -0400137EXPORT_SYMBOL(__tcf_idr_release);
David S. Millere9ce1cd2006-08-21 23:54:55 -0700138
Roman Mashak4e76e752018-03-08 16:59:19 -0500139static size_t tcf_action_shared_attrs_size(const struct tc_action *act)
140{
141 u32 cookie_len = 0;
142
143 if (act->act_cookie)
144 cookie_len = nla_total_size(act->act_cookie->len);
145
146 return nla_total_size(0) /* action number nested */
147 + nla_total_size(IFNAMSIZ) /* TCA_ACT_KIND */
148 + cookie_len /* TCA_ACT_COOKIE */
149 + nla_total_size(0) /* TCA_ACT_STATS nested */
150 /* TCA_STATS_BASIC */
151 + nla_total_size_64bit(sizeof(struct gnet_stats_basic))
152 /* TCA_STATS_QUEUE */
153 + nla_total_size_64bit(sizeof(struct gnet_stats_queue))
154 + nla_total_size(0) /* TCA_OPTIONS nested */
155 + nla_total_size(sizeof(struct tcf_t)); /* TCA_GACT_TM */
156}
157
158static size_t tcf_action_full_attrs_size(size_t sz)
159{
160 return NLMSG_HDRLEN /* struct nlmsghdr */
161 + sizeof(struct tcamsg)
162 + nla_total_size(0) /* TCA_ACT_TAB nested */
163 + sz;
164}
165
166static size_t tcf_action_fill_size(const struct tc_action *act)
167{
168 size_t sz = tcf_action_shared_attrs_size(act);
169
170 if (act->ops->get_fill_size)
171 return act->ops->get_fill_size(act) + sz;
172 return sz;
173}
174
Chris Mi65a206c2017-08-30 02:31:59 -0400175static int tcf_dump_walker(struct tcf_idrinfo *idrinfo, struct sk_buff *skb,
WANG Conga85a9702016-07-25 16:09:41 -0700176 struct netlink_callback *cb)
David S. Millere9ce1cd2006-08-21 23:54:55 -0700177{
Chris Mi65a206c2017-08-30 02:31:59 -0400178 int err = 0, index = -1, s_i = 0, n_i = 0;
Jamal Hadi Salim90825b22017-07-30 13:24:51 -0400179 u32 act_flags = cb->args[2];
Jamal Hadi Salime62e4842017-07-30 13:24:52 -0400180 unsigned long jiffy_since = cb->args[3];
Patrick McHardy4b3550ef2008-01-23 20:34:11 -0800181 struct nlattr *nest;
Chris Mi65a206c2017-08-30 02:31:59 -0400182 struct idr *idr = &idrinfo->action_idr;
183 struct tc_action *p;
184 unsigned long id = 1;
David S. Millere9ce1cd2006-08-21 23:54:55 -0700185
Vlad Buslov290aa0a2018-05-21 23:03:04 +0300186 spin_lock(&idrinfo->lock);
David S. Millere9ce1cd2006-08-21 23:54:55 -0700187
188 s_i = cb->args[0];
189
Matthew Wilcox7a457572017-11-28 15:39:51 -0500190 idr_for_each_entry_ul(idr, p, id) {
Chris Mi65a206c2017-08-30 02:31:59 -0400191 index++;
192 if (index < s_i)
193 continue;
WANG Conga85a9702016-07-25 16:09:41 -0700194
Chris Mi65a206c2017-08-30 02:31:59 -0400195 if (jiffy_since &&
196 time_after(jiffy_since,
197 (unsigned long)p->tcfa_tm.lastuse))
198 continue;
David S. Millere9ce1cd2006-08-21 23:54:55 -0700199
Chris Mi65a206c2017-08-30 02:31:59 -0400200 nest = nla_nest_start(skb, n_i);
Craig Dillabaugh734549e2018-03-26 14:58:32 -0400201 if (!nest) {
202 index--;
Chris Mi65a206c2017-08-30 02:31:59 -0400203 goto nla_put_failure;
Craig Dillabaugh734549e2018-03-26 14:58:32 -0400204 }
Chris Mi65a206c2017-08-30 02:31:59 -0400205 err = tcf_action_dump_1(skb, p, 0, 0);
206 if (err < 0) {
207 index--;
208 nlmsg_trim(skb, nest);
209 goto done;
David S. Millere9ce1cd2006-08-21 23:54:55 -0700210 }
Chris Mi65a206c2017-08-30 02:31:59 -0400211 nla_nest_end(skb, nest);
212 n_i++;
213 if (!(act_flags & TCA_FLAG_LARGE_DUMP_ON) &&
214 n_i >= TCA_ACT_MAX_PRIO)
215 goto done;
David S. Millere9ce1cd2006-08-21 23:54:55 -0700216 }
217done:
Jamal Hadi Salime62e4842017-07-30 13:24:52 -0400218 if (index >= 0)
219 cb->args[0] = index + 1;
220
Vlad Buslov290aa0a2018-05-21 23:03:04 +0300221 spin_unlock(&idrinfo->lock);
Jamal Hadi Salim90825b22017-07-30 13:24:51 -0400222 if (n_i) {
Jamal Hadi Salim90825b22017-07-30 13:24:51 -0400223 if (act_flags & TCA_FLAG_LARGE_DUMP_ON)
224 cb->args[1] = n_i;
225 }
David S. Millere9ce1cd2006-08-21 23:54:55 -0700226 return n_i;
227
Patrick McHardy7ba699c2008-01-22 22:11:50 -0800228nla_put_failure:
Patrick McHardy4b3550ef2008-01-23 20:34:11 -0800229 nla_nest_cancel(skb, nest);
David S. Millere9ce1cd2006-08-21 23:54:55 -0700230 goto done;
231}
232
Chris Mi65a206c2017-08-30 02:31:59 -0400233static int tcf_del_walker(struct tcf_idrinfo *idrinfo, struct sk_buff *skb,
WANG Conga85a9702016-07-25 16:09:41 -0700234 const struct tc_action_ops *ops)
David S. Millere9ce1cd2006-08-21 23:54:55 -0700235{
Patrick McHardy4b3550ef2008-01-23 20:34:11 -0800236 struct nlattr *nest;
Chris Mi65a206c2017-08-30 02:31:59 -0400237 int n_i = 0;
WANG Cong55334a52014-02-11 17:07:34 -0800238 int ret = -EINVAL;
Chris Mi65a206c2017-08-30 02:31:59 -0400239 struct idr *idr = &idrinfo->action_idr;
240 struct tc_action *p;
241 unsigned long id = 1;
David S. Millere9ce1cd2006-08-21 23:54:55 -0700242
WANG Conga85a9702016-07-25 16:09:41 -0700243 nest = nla_nest_start(skb, 0);
Patrick McHardy4b3550ef2008-01-23 20:34:11 -0800244 if (nest == NULL)
245 goto nla_put_failure;
WANG Conga85a9702016-07-25 16:09:41 -0700246 if (nla_put_string(skb, TCA_KIND, ops->kind))
David S. Miller1b34ec42012-03-29 05:11:39 -0400247 goto nla_put_failure;
WANG Conga85a9702016-07-25 16:09:41 -0700248
Matthew Wilcox7a457572017-11-28 15:39:51 -0500249 idr_for_each_entry_ul(idr, p, id) {
Chris Mi65a206c2017-08-30 02:31:59 -0400250 ret = __tcf_idr_release(p, false, true);
251 if (ret == ACT_P_DELETED) {
Jiri Pirko255cd502017-09-13 17:32:37 +0200252 module_put(ops->owner);
Chris Mi65a206c2017-08-30 02:31:59 -0400253 n_i++;
254 } else if (ret < 0) {
255 goto nla_put_failure;
David S. Millere9ce1cd2006-08-21 23:54:55 -0700256 }
257 }
David S. Miller1b34ec42012-03-29 05:11:39 -0400258 if (nla_put_u32(skb, TCA_FCNT, n_i))
259 goto nla_put_failure;
Patrick McHardy4b3550ef2008-01-23 20:34:11 -0800260 nla_nest_end(skb, nest);
David S. Millere9ce1cd2006-08-21 23:54:55 -0700261
262 return n_i;
Patrick McHardy7ba699c2008-01-22 22:11:50 -0800263nla_put_failure:
Patrick McHardy4b3550ef2008-01-23 20:34:11 -0800264 nla_nest_cancel(skb, nest);
WANG Cong55334a52014-02-11 17:07:34 -0800265 return ret;
David S. Millere9ce1cd2006-08-21 23:54:55 -0700266}
267
WANG Congddf97cc2016-02-22 15:57:53 -0800268int tcf_generic_walker(struct tc_action_net *tn, struct sk_buff *skb,
269 struct netlink_callback *cb, int type,
Alexander Aringb3620142018-02-15 10:54:59 -0500270 const struct tc_action_ops *ops,
271 struct netlink_ext_ack *extack)
David S. Millere9ce1cd2006-08-21 23:54:55 -0700272{
Chris Mi65a206c2017-08-30 02:31:59 -0400273 struct tcf_idrinfo *idrinfo = tn->idrinfo;
WANG Congddf97cc2016-02-22 15:57:53 -0800274
David S. Millere9ce1cd2006-08-21 23:54:55 -0700275 if (type == RTM_DELACTION) {
Chris Mi65a206c2017-08-30 02:31:59 -0400276 return tcf_del_walker(idrinfo, skb, ops);
David S. Millere9ce1cd2006-08-21 23:54:55 -0700277 } else if (type == RTM_GETACTION) {
Chris Mi65a206c2017-08-30 02:31:59 -0400278 return tcf_dump_walker(idrinfo, skb, cb);
David S. Millere9ce1cd2006-08-21 23:54:55 -0700279 } else {
Alexander Aringb3620142018-02-15 10:54:59 -0500280 WARN(1, "tcf_generic_walker: unknown command %d\n", type);
281 NL_SET_ERR_MSG(extack, "tcf_generic_walker: unknown command");
David S. Millere9ce1cd2006-08-21 23:54:55 -0700282 return -EINVAL;
283 }
284}
WANG Congddf97cc2016-02-22 15:57:53 -0800285EXPORT_SYMBOL(tcf_generic_walker);
David S. Millere9ce1cd2006-08-21 23:54:55 -0700286
Vlad Buslov3f7c72b2018-07-05 17:24:26 +0300287static bool __tcf_idr_check(struct tc_action_net *tn, u32 index,
288 struct tc_action **a, int bind)
David S. Millere9ce1cd2006-08-21 23:54:55 -0700289{
Vlad Buslov3f7c72b2018-07-05 17:24:26 +0300290 struct tcf_idrinfo *idrinfo = tn->idrinfo;
291 struct tc_action *p;
David S. Millere9ce1cd2006-08-21 23:54:55 -0700292
Vlad Buslov290aa0a2018-05-21 23:03:04 +0300293 spin_lock(&idrinfo->lock);
Matthew Wilcox322d8842017-11-28 10:01:24 -0500294 p = idr_find(&idrinfo->action_idr, index);
Vlad Buslov3f7c72b2018-07-05 17:24:26 +0300295 if (p) {
296 refcount_inc(&p->tcfa_refcnt);
297 if (bind)
298 atomic_inc(&p->tcfa_bindcnt);
299 }
Vlad Buslov290aa0a2018-05-21 23:03:04 +0300300 spin_unlock(&idrinfo->lock);
David S. Millere9ce1cd2006-08-21 23:54:55 -0700301
Vlad Buslov3f7c72b2018-07-05 17:24:26 +0300302 if (p) {
303 *a = p;
304 return true;
305 }
306 return false;
David S. Millere9ce1cd2006-08-21 23:54:55 -0700307}
David S. Millere9ce1cd2006-08-21 23:54:55 -0700308
Chris Mi65a206c2017-08-30 02:31:59 -0400309int tcf_idr_search(struct tc_action_net *tn, struct tc_action **a, u32 index)
David S. Millere9ce1cd2006-08-21 23:54:55 -0700310{
Vlad Buslov3f7c72b2018-07-05 17:24:26 +0300311 return __tcf_idr_check(tn, index, a, 0);
David S. Millere9ce1cd2006-08-21 23:54:55 -0700312}
Chris Mi65a206c2017-08-30 02:31:59 -0400313EXPORT_SYMBOL(tcf_idr_search);
David S. Millere9ce1cd2006-08-21 23:54:55 -0700314
Chris Mi65a206c2017-08-30 02:31:59 -0400315bool tcf_idr_check(struct tc_action_net *tn, u32 index, struct tc_action **a,
316 int bind)
David S. Millere9ce1cd2006-08-21 23:54:55 -0700317{
Vlad Buslov3f7c72b2018-07-05 17:24:26 +0300318 return __tcf_idr_check(tn, index, a, bind);
David S. Millere9ce1cd2006-08-21 23:54:55 -0700319}
Chris Mi65a206c2017-08-30 02:31:59 -0400320EXPORT_SYMBOL(tcf_idr_check);
David S. Millere9ce1cd2006-08-21 23:54:55 -0700321
Vlad Buslov2a2ea342018-07-05 17:24:27 +0300322int tcf_idr_delete_index(struct tc_action_net *tn, u32 index)
323{
324 struct tcf_idrinfo *idrinfo = tn->idrinfo;
325 struct tc_action *p;
326 int ret = 0;
327
328 spin_lock(&idrinfo->lock);
329 p = idr_find(&idrinfo->action_idr, index);
330 if (!p) {
331 spin_unlock(&idrinfo->lock);
332 return -ENOENT;
333 }
334
335 if (!atomic_read(&p->tcfa_bindcnt)) {
336 if (refcount_dec_and_test(&p->tcfa_refcnt)) {
337 struct module *owner = p->ops->owner;
338
339 WARN_ON(p != idr_remove(&idrinfo->action_idr,
340 p->tcfa_index));
341 spin_unlock(&idrinfo->lock);
342
343 if (p->ops->cleanup)
344 p->ops->cleanup(p);
345
346 gen_kill_estimator(&p->tcfa_rate_est);
347 free_tcf(p);
348 module_put(owner);
349 return 0;
350 }
351 ret = 0;
352 } else {
353 ret = -EPERM;
354 }
355
356 spin_unlock(&idrinfo->lock);
357 return ret;
358}
359EXPORT_SYMBOL(tcf_idr_delete_index);
360
Chris Mi65a206c2017-08-30 02:31:59 -0400361int tcf_idr_create(struct tc_action_net *tn, u32 index, struct nlattr *est,
362 struct tc_action **a, const struct tc_action_ops *ops,
363 int bind, bool cpustats)
David S. Millere9ce1cd2006-08-21 23:54:55 -0700364{
WANG Congec0595c2016-07-25 16:09:42 -0700365 struct tc_action *p = kzalloc(ops->size, GFP_KERNEL);
Chris Mi65a206c2017-08-30 02:31:59 -0400366 struct tcf_idrinfo *idrinfo = tn->idrinfo;
367 struct idr *idr = &idrinfo->action_idr;
Eric Dumazet519c8182015-07-06 05:18:04 -0700368 int err = -ENOMEM;
David S. Millere9ce1cd2006-08-21 23:54:55 -0700369
370 if (unlikely(!p))
WANG Cong86062032014-02-11 17:07:31 -0800371 return -ENOMEM;
Vlad Buslov036bb442018-07-05 17:24:24 +0300372 refcount_set(&p->tcfa_refcnt, 1);
David S. Millere9ce1cd2006-08-21 23:54:55 -0700373 if (bind)
Vlad Buslov036bb442018-07-05 17:24:24 +0300374 atomic_set(&p->tcfa_bindcnt, 1);
David S. Millere9ce1cd2006-08-21 23:54:55 -0700375
Eric Dumazet519c8182015-07-06 05:18:04 -0700376 if (cpustats) {
377 p->cpu_bstats = netdev_alloc_pcpu_stats(struct gnet_stats_basic_cpu);
Matthew Wilcox339913a2017-11-28 10:28:15 -0500378 if (!p->cpu_bstats)
Eric Dumazet519c8182015-07-06 05:18:04 -0700379 goto err1;
Matthew Wilcox339913a2017-11-28 10:28:15 -0500380 p->cpu_qstats = alloc_percpu(struct gnet_stats_queue);
381 if (!p->cpu_qstats)
382 goto err2;
Eric Dumazet519c8182015-07-06 05:18:04 -0700383 }
WANG Congec0595c2016-07-25 16:09:42 -0700384 spin_lock_init(&p->tcfa_lock);
Matthew Wilcox339913a2017-11-28 10:28:15 -0500385 idr_preload(GFP_KERNEL);
Vlad Buslov290aa0a2018-05-21 23:03:04 +0300386 spin_lock(&idrinfo->lock);
Chris Mi65a206c2017-08-30 02:31:59 -0400387 /* user doesn't specify an index */
388 if (!index) {
Matthew Wilcox339913a2017-11-28 10:28:15 -0500389 index = 1;
390 err = idr_alloc_u32(idr, NULL, &index, UINT_MAX, GFP_ATOMIC);
Chris Mi65a206c2017-08-30 02:31:59 -0400391 } else {
Matthew Wilcox339913a2017-11-28 10:28:15 -0500392 err = idr_alloc_u32(idr, NULL, &index, index, GFP_ATOMIC);
Chris Mi65a206c2017-08-30 02:31:59 -0400393 }
Vlad Buslov290aa0a2018-05-21 23:03:04 +0300394 spin_unlock(&idrinfo->lock);
Matthew Wilcox339913a2017-11-28 10:28:15 -0500395 idr_preload_end();
396 if (err)
397 goto err3;
Chris Mi65a206c2017-08-30 02:31:59 -0400398
Matthew Wilcox339913a2017-11-28 10:28:15 -0500399 p->tcfa_index = index;
WANG Congec0595c2016-07-25 16:09:42 -0700400 p->tcfa_tm.install = jiffies;
401 p->tcfa_tm.lastuse = jiffies;
402 p->tcfa_tm.firstuse = 0;
Stephen Hemminger0e991ec2008-11-25 21:12:32 -0800403 if (est) {
WANG Congec0595c2016-07-25 16:09:42 -0700404 err = gen_new_estimator(&p->tcfa_bstats, p->cpu_bstats,
405 &p->tcfa_rate_est,
406 &p->tcfa_lock, NULL, est);
Matthew Wilcox339913a2017-11-28 10:28:15 -0500407 if (err)
408 goto err4;
Stephen Hemminger0e991ec2008-11-25 21:12:32 -0800409 }
410
Chris Mi65a206c2017-08-30 02:31:59 -0400411 p->idrinfo = idrinfo;
WANG Congec0595c2016-07-25 16:09:42 -0700412 p->ops = ops;
413 INIT_LIST_HEAD(&p->list);
414 *a = p;
WANG Cong86062032014-02-11 17:07:31 -0800415 return 0;
Matthew Wilcox339913a2017-11-28 10:28:15 -0500416err4:
417 idr_remove(idr, index);
418err3:
419 free_percpu(p->cpu_qstats);
420err2:
421 free_percpu(p->cpu_bstats);
422err1:
423 kfree(p);
424 return err;
David S. Millere9ce1cd2006-08-21 23:54:55 -0700425}
Chris Mi65a206c2017-08-30 02:31:59 -0400426EXPORT_SYMBOL(tcf_idr_create);
David S. Millere9ce1cd2006-08-21 23:54:55 -0700427
Chris Mi65a206c2017-08-30 02:31:59 -0400428void tcf_idr_insert(struct tc_action_net *tn, struct tc_action *a)
David S. Millere9ce1cd2006-08-21 23:54:55 -0700429{
Chris Mi65a206c2017-08-30 02:31:59 -0400430 struct tcf_idrinfo *idrinfo = tn->idrinfo;
David S. Millere9ce1cd2006-08-21 23:54:55 -0700431
Vlad Buslov290aa0a2018-05-21 23:03:04 +0300432 spin_lock(&idrinfo->lock);
Matthew Wilcox234a4622017-11-28 09:56:36 -0500433 idr_replace(&idrinfo->action_idr, a, a->tcfa_index);
Vlad Buslov290aa0a2018-05-21 23:03:04 +0300434 spin_unlock(&idrinfo->lock);
David S. Millere9ce1cd2006-08-21 23:54:55 -0700435}
Chris Mi65a206c2017-08-30 02:31:59 -0400436EXPORT_SYMBOL(tcf_idr_insert);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700437
Chris Mi65a206c2017-08-30 02:31:59 -0400438void tcf_idrinfo_destroy(const struct tc_action_ops *ops,
439 struct tcf_idrinfo *idrinfo)
WANG Cong1d4150c2016-02-22 15:57:52 -0800440{
Chris Mi65a206c2017-08-30 02:31:59 -0400441 struct idr *idr = &idrinfo->action_idr;
442 struct tc_action *p;
443 int ret;
444 unsigned long id = 1;
WANG Cong1d4150c2016-02-22 15:57:52 -0800445
Matthew Wilcox7a457572017-11-28 15:39:51 -0500446 idr_for_each_entry_ul(idr, p, id) {
Chris Mi65a206c2017-08-30 02:31:59 -0400447 ret = __tcf_idr_release(p, false, true);
448 if (ret == ACT_P_DELETED)
449 module_put(ops->owner);
450 else if (ret < 0)
451 return;
WANG Cong1d4150c2016-02-22 15:57:52 -0800452 }
Chris Mi65a206c2017-08-30 02:31:59 -0400453 idr_destroy(&idrinfo->action_idr);
WANG Cong1d4150c2016-02-22 15:57:52 -0800454}
Chris Mi65a206c2017-08-30 02:31:59 -0400455EXPORT_SYMBOL(tcf_idrinfo_destroy);
WANG Cong1d4150c2016-02-22 15:57:52 -0800456
WANG Cong1f747c22013-12-15 20:15:10 -0800457static LIST_HEAD(act_base);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700458static DEFINE_RWLOCK(act_mod_lock);
459
WANG Congddf97cc2016-02-22 15:57:53 -0800460int tcf_register_action(struct tc_action_ops *act,
461 struct pernet_operations *ops)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700462{
WANG Cong1f747c22013-12-15 20:15:10 -0800463 struct tc_action_ops *a;
WANG Congddf97cc2016-02-22 15:57:53 -0800464 int ret;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700465
WANG Congddf97cc2016-02-22 15:57:53 -0800466 if (!act->act || !act->dump || !act->init || !act->walk || !act->lookup)
Jamal Hadi Salim76c82d72013-12-04 09:26:52 -0500467 return -EINVAL;
468
WANG Congab102b82016-10-11 10:56:45 -0700469 /* We have to register pernet ops before making the action ops visible,
470 * otherwise tcf_action_init_1() could get a partially initialized
471 * netns.
472 */
473 ret = register_pernet_subsys(ops);
474 if (ret)
475 return ret;
476
Linus Torvalds1da177e2005-04-16 15:20:36 -0700477 write_lock(&act_mod_lock);
WANG Cong1f747c22013-12-15 20:15:10 -0800478 list_for_each_entry(a, &act_base, head) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700479 if (act->type == a->type || (strcmp(act->kind, a->kind) == 0)) {
480 write_unlock(&act_mod_lock);
WANG Congab102b82016-10-11 10:56:45 -0700481 unregister_pernet_subsys(ops);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700482 return -EEXIST;
483 }
484 }
WANG Cong1f747c22013-12-15 20:15:10 -0800485 list_add_tail(&act->head, &act_base);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700486 write_unlock(&act_mod_lock);
WANG Congddf97cc2016-02-22 15:57:53 -0800487
Linus Torvalds1da177e2005-04-16 15:20:36 -0700488 return 0;
489}
Patrick McHardy62e3ba12008-01-22 22:10:23 -0800490EXPORT_SYMBOL(tcf_register_action);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700491
WANG Congddf97cc2016-02-22 15:57:53 -0800492int tcf_unregister_action(struct tc_action_ops *act,
493 struct pernet_operations *ops)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700494{
WANG Cong1f747c22013-12-15 20:15:10 -0800495 struct tc_action_ops *a;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700496 int err = -ENOENT;
497
498 write_lock(&act_mod_lock);
Eric Dumazeta7928662013-12-20 12:32:32 -0800499 list_for_each_entry(a, &act_base, head) {
500 if (a == act) {
501 list_del(&act->head);
502 err = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700503 break;
Eric Dumazeta7928662013-12-20 12:32:32 -0800504 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700505 }
506 write_unlock(&act_mod_lock);
WANG Congab102b82016-10-11 10:56:45 -0700507 if (!err)
508 unregister_pernet_subsys(ops);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700509 return err;
510}
Patrick McHardy62e3ba12008-01-22 22:10:23 -0800511EXPORT_SYMBOL(tcf_unregister_action);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700512
513/* lookup by name */
514static struct tc_action_ops *tc_lookup_action_n(char *kind)
515{
Eric Dumazeta7928662013-12-20 12:32:32 -0800516 struct tc_action_ops *a, *res = NULL;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700517
518 if (kind) {
519 read_lock(&act_mod_lock);
WANG Cong1f747c22013-12-15 20:15:10 -0800520 list_for_each_entry(a, &act_base, head) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700521 if (strcmp(kind, a->kind) == 0) {
Eric Dumazeta7928662013-12-20 12:32:32 -0800522 if (try_module_get(a->owner))
523 res = a;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700524 break;
525 }
526 }
527 read_unlock(&act_mod_lock);
528 }
Eric Dumazeta7928662013-12-20 12:32:32 -0800529 return res;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700530}
531
Patrick McHardy7ba699c2008-01-22 22:11:50 -0800532/* lookup by nlattr */
533static struct tc_action_ops *tc_lookup_action(struct nlattr *kind)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700534{
Eric Dumazeta7928662013-12-20 12:32:32 -0800535 struct tc_action_ops *a, *res = NULL;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700536
537 if (kind) {
538 read_lock(&act_mod_lock);
WANG Cong1f747c22013-12-15 20:15:10 -0800539 list_for_each_entry(a, &act_base, head) {
Patrick McHardy7ba699c2008-01-22 22:11:50 -0800540 if (nla_strcmp(kind, a->kind) == 0) {
Eric Dumazeta7928662013-12-20 12:32:32 -0800541 if (try_module_get(a->owner))
542 res = a;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700543 break;
544 }
545 }
546 read_unlock(&act_mod_lock);
547 }
Eric Dumazeta7928662013-12-20 12:32:32 -0800548 return res;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700549}
550
Jamal Hadi Salime0ee84d2017-04-23 13:17:28 -0400551/*TCA_ACT_MAX_PRIO is 32, there count upto 32 */
552#define TCA_ACT_MAX_PRIO_MASK 0x1FF
WANG Cong22dc13c2016-08-13 22:35:00 -0700553int tcf_action_exec(struct sk_buff *skb, struct tc_action **actions,
554 int nr_actions, struct tcf_result *res)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700555{
Jamal Hadi Salime0ee84d2017-04-23 13:17:28 -0400556 u32 jmp_prgcnt = 0;
557 u32 jmp_ttl = TCA_ACT_MAX_PRIO; /*matches actions per filter */
Jiri Pirkoec1a9cc2017-08-04 14:29:02 +0200558 int i;
559 int ret = TC_ACT_OK;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700560
Willem de Bruijne7246e12017-01-07 17:06:35 -0500561 if (skb_skip_tc_classify(skb))
562 return TC_ACT_OK;
563
Jamal Hadi Salime0ee84d2017-04-23 13:17:28 -0400564restart_act_graph:
WANG Cong22dc13c2016-08-13 22:35:00 -0700565 for (i = 0; i < nr_actions; i++) {
566 const struct tc_action *a = actions[i];
567
Jamal Hadi Salime0ee84d2017-04-23 13:17:28 -0400568 if (jmp_prgcnt > 0) {
569 jmp_prgcnt -= 1;
570 continue;
571 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700572repeat:
Jamal Hadi Salim63acd682013-12-23 08:02:12 -0500573 ret = a->ops->act(skb, a, res);
Jamal Hadi Salim63acd682013-12-23 08:02:12 -0500574 if (ret == TC_ACT_REPEAT)
575 goto repeat; /* we need a ttl - JHS */
Jamal Hadi Salime0ee84d2017-04-23 13:17:28 -0400576
Jiri Pirko9da32422017-05-02 10:12:00 +0200577 if (TC_ACT_EXT_CMP(ret, TC_ACT_JUMP)) {
Jamal Hadi Salime0ee84d2017-04-23 13:17:28 -0400578 jmp_prgcnt = ret & TCA_ACT_MAX_PRIO_MASK;
579 if (!jmp_prgcnt || (jmp_prgcnt > nr_actions)) {
580 /* faulty opcode, stop pipeline */
581 return TC_ACT_OK;
582 } else {
583 jmp_ttl -= 1;
584 if (jmp_ttl > 0)
585 goto restart_act_graph;
586 else /* faulty graph, stop pipeline */
587 return TC_ACT_OK;
588 }
Jiri Pirkodb505142017-05-17 11:08:03 +0200589 } else if (TC_ACT_EXT_CMP(ret, TC_ACT_GOTO_CHAIN)) {
590 tcf_action_goto_chain_exec(a, res);
Jamal Hadi Salime0ee84d2017-04-23 13:17:28 -0400591 }
592
Jamal Hadi Salim63acd682013-12-23 08:02:12 -0500593 if (ret != TC_ACT_PIPE)
Willem de Bruijne7246e12017-01-07 17:06:35 -0500594 break;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700595 }
Jamal Hadi Salime0ee84d2017-04-23 13:17:28 -0400596
Linus Torvalds1da177e2005-04-16 15:20:36 -0700597 return ret;
598}
Patrick McHardy62e3ba12008-01-22 22:10:23 -0800599EXPORT_SYMBOL(tcf_action_exec);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700600
WANG Cong55334a52014-02-11 17:07:34 -0800601int tcf_action_destroy(struct list_head *actions, int bind)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700602{
Jiri Pirko255cd502017-09-13 17:32:37 +0200603 const struct tc_action_ops *ops;
WANG Cong33be6272013-12-15 20:15:05 -0800604 struct tc_action *a, *tmp;
WANG Cong55334a52014-02-11 17:07:34 -0800605 int ret = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700606
WANG Cong33be6272013-12-15 20:15:05 -0800607 list_for_each_entry_safe(a, tmp, actions, list) {
Jiri Pirko255cd502017-09-13 17:32:37 +0200608 ops = a->ops;
Chris Mi65a206c2017-08-30 02:31:59 -0400609 ret = __tcf_idr_release(a, bind, true);
WANG Cong55334a52014-02-11 17:07:34 -0800610 if (ret == ACT_P_DELETED)
Jiri Pirko255cd502017-09-13 17:32:37 +0200611 module_put(ops->owner);
WANG Cong55334a52014-02-11 17:07:34 -0800612 else if (ret < 0)
613 return ret;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700614 }
WANG Cong55334a52014-02-11 17:07:34 -0800615 return ret;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700616}
617
618int
619tcf_action_dump_old(struct sk_buff *skb, struct tc_action *a, int bind, int ref)
620{
Linus Torvalds1da177e2005-04-16 15:20:36 -0700621 return a->ops->dump(skb, a, bind, ref);
622}
623
624int
625tcf_action_dump_1(struct sk_buff *skb, struct tc_action *a, int bind, int ref)
626{
627 int err = -EINVAL;
Arnaldo Carvalho de Melo27a884d2007-04-19 20:29:13 -0700628 unsigned char *b = skb_tail_pointer(skb);
Patrick McHardy4b3550ef2008-01-23 20:34:11 -0800629 struct nlattr *nest;
Vlad Busloveec94fd2018-07-05 17:24:23 +0300630 struct tc_cookie *cookie;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700631
David S. Miller1b34ec42012-03-29 05:11:39 -0400632 if (nla_put_string(skb, TCA_KIND, a->ops->kind))
633 goto nla_put_failure;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700634 if (tcf_action_copy_stats(skb, a, 0))
Patrick McHardy7ba699c2008-01-22 22:11:50 -0800635 goto nla_put_failure;
Vlad Busloveec94fd2018-07-05 17:24:23 +0300636
637 rcu_read_lock();
638 cookie = rcu_dereference(a->act_cookie);
639 if (cookie) {
640 if (nla_put(skb, TCA_ACT_COOKIE, cookie->len, cookie->data)) {
641 rcu_read_unlock();
Jamal Hadi Salim1045ba72017-01-24 07:02:41 -0500642 goto nla_put_failure;
Vlad Busloveec94fd2018-07-05 17:24:23 +0300643 }
Jamal Hadi Salim1045ba72017-01-24 07:02:41 -0500644 }
Vlad Busloveec94fd2018-07-05 17:24:23 +0300645 rcu_read_unlock();
Jamal Hadi Salim1045ba72017-01-24 07:02:41 -0500646
Patrick McHardy4b3550ef2008-01-23 20:34:11 -0800647 nest = nla_nest_start(skb, TCA_OPTIONS);
648 if (nest == NULL)
649 goto nla_put_failure;
Eric Dumazetcc7ec452011-01-19 19:26:56 +0000650 err = tcf_action_dump_old(skb, a, bind, ref);
651 if (err > 0) {
Patrick McHardy4b3550ef2008-01-23 20:34:11 -0800652 nla_nest_end(skb, nest);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700653 return err;
654 }
655
Patrick McHardy7ba699c2008-01-22 22:11:50 -0800656nla_put_failure:
Arnaldo Carvalho de Melodc5fc572007-03-25 23:06:12 -0700657 nlmsg_trim(skb, b);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700658 return -1;
659}
Patrick McHardy62e3ba12008-01-22 22:10:23 -0800660EXPORT_SYMBOL(tcf_action_dump_1);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700661
Jamal Hadi Salim0b0f43f2016-06-05 10:41:32 -0400662int tcf_action_dump(struct sk_buff *skb, struct list_head *actions,
663 int bind, int ref)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700664{
665 struct tc_action *a;
666 int err = -EINVAL;
Patrick McHardy4b3550ef2008-01-23 20:34:11 -0800667 struct nlattr *nest;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700668
WANG Cong33be6272013-12-15 20:15:05 -0800669 list_for_each_entry(a, actions, list) {
Patrick McHardy4b3550ef2008-01-23 20:34:11 -0800670 nest = nla_nest_start(skb, a->order);
671 if (nest == NULL)
672 goto nla_put_failure;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700673 err = tcf_action_dump_1(skb, a, bind, ref);
674 if (err < 0)
Thomas Graf4fe683f2006-07-05 20:47:28 -0700675 goto errout;
Patrick McHardy4b3550ef2008-01-23 20:34:11 -0800676 nla_nest_end(skb, nest);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700677 }
678
679 return 0;
680
Patrick McHardy7ba699c2008-01-22 22:11:50 -0800681nla_put_failure:
Thomas Graf4fe683f2006-07-05 20:47:28 -0700682 err = -EINVAL;
683errout:
Patrick McHardy4b3550ef2008-01-23 20:34:11 -0800684 nla_nest_cancel(skb, nest);
Thomas Graf4fe683f2006-07-05 20:47:28 -0700685 return err;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700686}
687
Wolfgang Bumillere0535ce2017-04-20 14:08:26 +0200688static struct tc_cookie *nla_memdup_cookie(struct nlattr **tb)
Jamal Hadi Salim1045ba72017-01-24 07:02:41 -0500689{
Wolfgang Bumillere0535ce2017-04-20 14:08:26 +0200690 struct tc_cookie *c = kzalloc(sizeof(*c), GFP_KERNEL);
691 if (!c)
692 return NULL;
Jamal Hadi Salim1045ba72017-01-24 07:02:41 -0500693
Wolfgang Bumillere0535ce2017-04-20 14:08:26 +0200694 c->data = nla_memdup(tb[TCA_ACT_COOKIE], GFP_KERNEL);
695 if (!c->data) {
696 kfree(c);
697 return NULL;
Jamal Hadi Salim1045ba72017-01-24 07:02:41 -0500698 }
Wolfgang Bumillere0535ce2017-04-20 14:08:26 +0200699 c->len = nla_len(tb[TCA_ACT_COOKIE]);
Jamal Hadi Salim1045ba72017-01-24 07:02:41 -0500700
Wolfgang Bumillere0535ce2017-04-20 14:08:26 +0200701 return c;
Jamal Hadi Salim1045ba72017-01-24 07:02:41 -0500702}
703
Jiri Pirko9fb9f252017-05-17 11:08:02 +0200704struct tc_action *tcf_action_init_1(struct net *net, struct tcf_proto *tp,
705 struct nlattr *nla, struct nlattr *est,
Alexander Aringaea0d722018-02-15 10:54:54 -0500706 char *name, int ovr, int bind,
Vlad Buslov789871b2018-07-05 17:24:25 +0300707 bool rtnl_held,
Alexander Aringaea0d722018-02-15 10:54:54 -0500708 struct netlink_ext_ack *extack)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700709{
710 struct tc_action *a;
711 struct tc_action_ops *a_o;
Wolfgang Bumillere0535ce2017-04-20 14:08:26 +0200712 struct tc_cookie *cookie = NULL;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700713 char act_name[IFNAMSIZ];
Eric Dumazetcc7ec452011-01-19 19:26:56 +0000714 struct nlattr *tb[TCA_ACT_MAX + 1];
Patrick McHardy7ba699c2008-01-22 22:11:50 -0800715 struct nlattr *kind;
Patrick McHardyab27cfb2008-01-23 20:33:13 -0800716 int err;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700717
Linus Torvalds1da177e2005-04-16 15:20:36 -0700718 if (name == NULL) {
Alexander Aring84ae0172018-02-15 10:54:55 -0500719 err = nla_parse_nested(tb, TCA_ACT_MAX, nla, NULL, extack);
Patrick McHardycee63722008-01-23 20:33:32 -0800720 if (err < 0)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700721 goto err_out;
Patrick McHardycee63722008-01-23 20:33:32 -0800722 err = -EINVAL;
Patrick McHardy7ba699c2008-01-22 22:11:50 -0800723 kind = tb[TCA_ACT_KIND];
Alexander Aring84ae0172018-02-15 10:54:55 -0500724 if (!kind) {
725 NL_SET_ERR_MSG(extack, "TC action kind must be specified");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700726 goto err_out;
Alexander Aring84ae0172018-02-15 10:54:55 -0500727 }
728 if (nla_strlcpy(act_name, kind, IFNAMSIZ) >= IFNAMSIZ) {
729 NL_SET_ERR_MSG(extack, "TC action name too long");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700730 goto err_out;
Alexander Aring84ae0172018-02-15 10:54:55 -0500731 }
Wolfgang Bumillere0535ce2017-04-20 14:08:26 +0200732 if (tb[TCA_ACT_COOKIE]) {
733 int cklen = nla_len(tb[TCA_ACT_COOKIE]);
734
Alexander Aring84ae0172018-02-15 10:54:55 -0500735 if (cklen > TC_COOKIE_MAX_SIZE) {
736 NL_SET_ERR_MSG(extack, "TC cookie size above the maximum");
Wolfgang Bumillere0535ce2017-04-20 14:08:26 +0200737 goto err_out;
Alexander Aring84ae0172018-02-15 10:54:55 -0500738 }
Wolfgang Bumillere0535ce2017-04-20 14:08:26 +0200739
740 cookie = nla_memdup_cookie(tb);
741 if (!cookie) {
Alexander Aring84ae0172018-02-15 10:54:55 -0500742 NL_SET_ERR_MSG(extack, "No memory to generate TC cookie");
Wolfgang Bumillere0535ce2017-04-20 14:08:26 +0200743 err = -ENOMEM;
744 goto err_out;
745 }
746 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700747 } else {
Alexander Aring84ae0172018-02-15 10:54:55 -0500748 if (strlcpy(act_name, name, IFNAMSIZ) >= IFNAMSIZ) {
749 NL_SET_ERR_MSG(extack, "TC action name too long");
750 err = -EINVAL;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700751 goto err_out;
Alexander Aring84ae0172018-02-15 10:54:55 -0500752 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700753 }
754
755 a_o = tc_lookup_action_n(act_name);
756 if (a_o == NULL) {
Johannes Berg95a5afc2008-10-16 15:24:51 -0700757#ifdef CONFIG_MODULES
Vlad Buslov789871b2018-07-05 17:24:25 +0300758 if (rtnl_held)
759 rtnl_unlock();
Patrick McHardy4bba3922006-01-08 22:22:14 -0800760 request_module("act_%s", act_name);
Vlad Buslov789871b2018-07-05 17:24:25 +0300761 if (rtnl_held)
762 rtnl_lock();
Linus Torvalds1da177e2005-04-16 15:20:36 -0700763
764 a_o = tc_lookup_action_n(act_name);
765
766 /* We dropped the RTNL semaphore in order to
767 * perform the module load. So, even if we
768 * succeeded in loading the module we have to
769 * tell the caller to replay the request. We
770 * indicate this using -EAGAIN.
771 */
772 if (a_o != NULL) {
Patrick McHardyab27cfb2008-01-23 20:33:13 -0800773 err = -EAGAIN;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700774 goto err_mod;
775 }
776#endif
Alexander Aring84ae0172018-02-15 10:54:55 -0500777 NL_SET_ERR_MSG(extack, "Failed to load TC action module");
Patrick McHardyab27cfb2008-01-23 20:33:13 -0800778 err = -ENOENT;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700779 goto err_out;
780 }
781
Linus Torvalds1da177e2005-04-16 15:20:36 -0700782 /* backward compatibility for policer */
783 if (name == NULL)
Alexander Aring589dad62018-02-15 10:54:56 -0500784 err = a_o->init(net, tb[TCA_ACT_OPTIONS], est, &a, ovr, bind,
Vlad Buslov789871b2018-07-05 17:24:25 +0300785 rtnl_held, extack);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700786 else
Vlad Buslov789871b2018-07-05 17:24:25 +0300787 err = a_o->init(net, nla, est, &a, ovr, bind, rtnl_held,
788 extack);
Patrick McHardyab27cfb2008-01-23 20:33:13 -0800789 if (err < 0)
WANG Conga85a9702016-07-25 16:09:41 -0700790 goto err_mod;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700791
Vlad Busloveec94fd2018-07-05 17:24:23 +0300792 if (!name && tb[TCA_ACT_COOKIE])
793 tcf_set_action_cookie(&a->act_cookie, cookie);
Jamal Hadi Salim1045ba72017-01-24 07:02:41 -0500794
Linus Torvalds1da177e2005-04-16 15:20:36 -0700795 /* module count goes up only when brand new policy is created
Eric Dumazetcc7ec452011-01-19 19:26:56 +0000796 * if it exists and is only bound to in a_o->init() then
797 * ACT_P_CREATED is not returned (a zero is).
798 */
Patrick McHardyab27cfb2008-01-23 20:33:13 -0800799 if (err != ACT_P_CREATED)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700800 module_put(a_o->owner);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700801
Jiri Pirkodb505142017-05-17 11:08:03 +0200802 if (TC_ACT_EXT_CMP(a->tcfa_action, TC_ACT_GOTO_CHAIN)) {
803 err = tcf_action_goto_chain_init(a, tp);
804 if (err) {
805 LIST_HEAD(actions);
806
807 list_add_tail(&a->list, &actions);
808 tcf_action_destroy(&actions, bind);
Alexander Aring84ae0172018-02-15 10:54:55 -0500809 NL_SET_ERR_MSG(extack, "Failed to init TC action chain");
Jiri Pirkodb505142017-05-17 11:08:03 +0200810 return ERR_PTR(err);
811 }
812 }
813
Linus Torvalds1da177e2005-04-16 15:20:36 -0700814 return a;
815
Linus Torvalds1da177e2005-04-16 15:20:36 -0700816err_mod:
817 module_put(a_o->owner);
818err_out:
Wolfgang Bumillere0535ce2017-04-20 14:08:26 +0200819 if (cookie) {
820 kfree(cookie->data);
821 kfree(cookie);
822 }
Patrick McHardyab27cfb2008-01-23 20:33:13 -0800823 return ERR_PTR(err);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700824}
825
Jamal Hadi Salimaecc5ce2016-09-19 19:02:51 -0400826static void cleanup_a(struct list_head *actions, int ovr)
827{
828 struct tc_action *a;
829
830 if (!ovr)
831 return;
832
833 list_for_each_entry(a, actions, list)
Vlad Buslov036bb442018-07-05 17:24:24 +0300834 refcount_dec(&a->tcfa_refcnt);
Jamal Hadi Salimaecc5ce2016-09-19 19:02:51 -0400835}
836
Jiri Pirko9fb9f252017-05-17 11:08:02 +0200837int tcf_action_init(struct net *net, struct tcf_proto *tp, struct nlattr *nla,
838 struct nlattr *est, char *name, int ovr, int bind,
Roman Mashakd04e6992018-03-08 16:59:17 -0500839 struct list_head *actions, size_t *attr_size,
Vlad Buslov789871b2018-07-05 17:24:25 +0300840 bool rtnl_held, struct netlink_ext_ack *extack)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700841{
Eric Dumazetcc7ec452011-01-19 19:26:56 +0000842 struct nlattr *tb[TCA_ACT_MAX_PRIO + 1];
WANG Cong33be6272013-12-15 20:15:05 -0800843 struct tc_action *act;
Roman Mashak4e76e752018-03-08 16:59:19 -0500844 size_t sz = 0;
Patrick McHardycee63722008-01-23 20:33:32 -0800845 int err;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700846 int i;
847
Alexander Aring84ae0172018-02-15 10:54:55 -0500848 err = nla_parse_nested(tb, TCA_ACT_MAX_PRIO, nla, NULL, extack);
Patrick McHardycee63722008-01-23 20:33:32 -0800849 if (err < 0)
WANG Cong33be6272013-12-15 20:15:05 -0800850 return err;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700851
Patrick McHardy7ba699c2008-01-22 22:11:50 -0800852 for (i = 1; i <= TCA_ACT_MAX_PRIO && tb[i]; i++) {
Alexander Aringaea0d722018-02-15 10:54:54 -0500853 act = tcf_action_init_1(net, tp, tb[i], est, name, ovr, bind,
Vlad Buslov789871b2018-07-05 17:24:25 +0300854 rtnl_held, extack);
WANG Cong33be6272013-12-15 20:15:05 -0800855 if (IS_ERR(act)) {
856 err = PTR_ERR(act);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700857 goto err;
WANG Cong33be6272013-12-15 20:15:05 -0800858 }
Patrick McHardy7ba699c2008-01-22 22:11:50 -0800859 act->order = i;
Roman Mashak4e76e752018-03-08 16:59:19 -0500860 sz += tcf_action_fill_size(act);
Jamal Hadi Salimaecc5ce2016-09-19 19:02:51 -0400861 if (ovr)
Vlad Buslov036bb442018-07-05 17:24:24 +0300862 refcount_inc(&act->tcfa_refcnt);
WANG Cong33be6272013-12-15 20:15:05 -0800863 list_add_tail(&act->list, actions);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700864 }
Jamal Hadi Salimaecc5ce2016-09-19 19:02:51 -0400865
Roman Mashak4e76e752018-03-08 16:59:19 -0500866 *attr_size = tcf_action_full_attrs_size(sz);
867
Jamal Hadi Salimaecc5ce2016-09-19 19:02:51 -0400868 /* Remove the temp refcnt which was necessary to protect against
869 * destroying an existing action which was being replaced
870 */
871 cleanup_a(actions, ovr);
WANG Cong33be6272013-12-15 20:15:05 -0800872 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700873
874err:
WANG Cong33be6272013-12-15 20:15:05 -0800875 tcf_action_destroy(actions, bind);
876 return err;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700877}
878
WANG Congec0595c2016-07-25 16:09:42 -0700879int tcf_action_copy_stats(struct sk_buff *skb, struct tc_action *p,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700880 int compat_mode)
881{
882 int err = 0;
883 struct gnet_dump d;
YOSHIFUJI Hideaki10297b92007-02-09 23:25:16 +0900884
WANG Cong7eb88962014-01-09 16:14:05 -0800885 if (p == NULL)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700886 goto errout;
887
888 /* compat_mode being true specifies a call that is supposed
Dirk Hohndel06fe9fb2009-09-28 21:43:57 -0400889 * to add additional backward compatibility statistic TLVs.
Linus Torvalds1da177e2005-04-16 15:20:36 -0700890 */
891 if (compat_mode) {
WANG Congec0595c2016-07-25 16:09:42 -0700892 if (p->type == TCA_OLD_COMPAT)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700893 err = gnet_stats_start_copy_compat(skb, 0,
Nicolas Dichtel98545182016-04-26 10:06:18 +0200894 TCA_STATS,
895 TCA_XSTATS,
WANG Congec0595c2016-07-25 16:09:42 -0700896 &p->tcfa_lock, &d,
Nicolas Dichtel98545182016-04-26 10:06:18 +0200897 TCA_PAD);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700898 else
899 return 0;
900 } else
901 err = gnet_stats_start_copy(skb, TCA_ACT_STATS,
WANG Congec0595c2016-07-25 16:09:42 -0700902 &p->tcfa_lock, &d, TCA_ACT_PAD);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700903
904 if (err < 0)
905 goto errout;
906
WANG Congec0595c2016-07-25 16:09:42 -0700907 if (gnet_stats_copy_basic(NULL, &d, p->cpu_bstats, &p->tcfa_bstats) < 0 ||
Eric Dumazet1c0d32f2016-12-04 09:48:16 -0800908 gnet_stats_copy_rate_est(&d, &p->tcfa_rate_est) < 0 ||
Eric Dumazet519c8182015-07-06 05:18:04 -0700909 gnet_stats_copy_queue(&d, p->cpu_qstats,
WANG Congec0595c2016-07-25 16:09:42 -0700910 &p->tcfa_qstats,
911 p->tcfa_qstats.qlen) < 0)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700912 goto errout;
913
914 if (gnet_stats_finish_copy(&d) < 0)
915 goto errout;
916
917 return 0;
918
919errout:
920 return -1;
921}
922
Jamal Hadi Salim0b0f43f2016-06-05 10:41:32 -0400923static int tca_get_fill(struct sk_buff *skb, struct list_head *actions,
924 u32 portid, u32 seq, u16 flags, int event, int bind,
925 int ref)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700926{
927 struct tcamsg *t;
928 struct nlmsghdr *nlh;
Arnaldo Carvalho de Melo27a884d2007-04-19 20:29:13 -0700929 unsigned char *b = skb_tail_pointer(skb);
Patrick McHardy4b3550ef2008-01-23 20:34:11 -0800930 struct nlattr *nest;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700931
Eric W. Biederman15e47302012-09-07 20:12:54 +0000932 nlh = nlmsg_put(skb, portid, seq, event, sizeof(*t), flags);
David S. Miller8b00a532012-06-26 21:39:32 -0700933 if (!nlh)
934 goto out_nlmsg_trim;
935 t = nlmsg_data(nlh);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700936 t->tca_family = AF_UNSPEC;
Patrick McHardy9ef1d4c2005-06-28 12:55:30 -0700937 t->tca__pad1 = 0;
938 t->tca__pad2 = 0;
YOSHIFUJI Hideaki10297b92007-02-09 23:25:16 +0900939
Patrick McHardy4b3550ef2008-01-23 20:34:11 -0800940 nest = nla_nest_start(skb, TCA_ACT_TAB);
Alexander Aring1af8515582018-02-15 10:54:53 -0500941 if (!nest)
David S. Miller8b00a532012-06-26 21:39:32 -0700942 goto out_nlmsg_trim;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700943
WANG Cong33be6272013-12-15 20:15:05 -0800944 if (tcf_action_dump(skb, actions, bind, ref) < 0)
David S. Miller8b00a532012-06-26 21:39:32 -0700945 goto out_nlmsg_trim;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700946
Patrick McHardy4b3550ef2008-01-23 20:34:11 -0800947 nla_nest_end(skb, nest);
YOSHIFUJI Hideaki10297b92007-02-09 23:25:16 +0900948
Arnaldo Carvalho de Melo27a884d2007-04-19 20:29:13 -0700949 nlh->nlmsg_len = skb_tail_pointer(skb) - b;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700950 return skb->len;
951
David S. Miller8b00a532012-06-26 21:39:32 -0700952out_nlmsg_trim:
Arnaldo Carvalho de Melodc5fc572007-03-25 23:06:12 -0700953 nlmsg_trim(skb, b);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700954 return -1;
955}
956
957static int
Roman Mashakc4c42902017-07-13 13:12:18 -0400958tcf_get_notify(struct net *net, u32 portid, struct nlmsghdr *n,
Alexander Aring84ae0172018-02-15 10:54:55 -0500959 struct list_head *actions, int event,
960 struct netlink_ext_ack *extack)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700961{
962 struct sk_buff *skb;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700963
964 skb = alloc_skb(NLMSG_GOODSIZE, GFP_KERNEL);
965 if (!skb)
966 return -ENOBUFS;
Jamal Hadi Salim0b0f43f2016-06-05 10:41:32 -0400967 if (tca_get_fill(skb, actions, portid, n->nlmsg_seq, 0, event,
Vlad Buslov3f7c72b2018-07-05 17:24:26 +0300968 0, 1) <= 0) {
Alexander Aring84ae0172018-02-15 10:54:55 -0500969 NL_SET_ERR_MSG(extack, "Failed to fill netlink attributes while adding TC action");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700970 kfree_skb(skb);
971 return -EINVAL;
972 }
Thomas Graf2942e902006-08-15 00:30:25 -0700973
Eric W. Biederman15e47302012-09-07 20:12:54 +0000974 return rtnl_unicast(skb, net, portid);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700975}
976
WANG Congddf97cc2016-02-22 15:57:53 -0800977static struct tc_action *tcf_action_get_1(struct net *net, struct nlattr *nla,
Alexander Aring84ae0172018-02-15 10:54:55 -0500978 struct nlmsghdr *n, u32 portid,
979 struct netlink_ext_ack *extack)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700980{
Eric Dumazetcc7ec452011-01-19 19:26:56 +0000981 struct nlattr *tb[TCA_ACT_MAX + 1];
WANG Conga85a9702016-07-25 16:09:41 -0700982 const struct tc_action_ops *ops;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700983 struct tc_action *a;
984 int index;
Patrick McHardyab27cfb2008-01-23 20:33:13 -0800985 int err;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700986
Alexander Aring84ae0172018-02-15 10:54:55 -0500987 err = nla_parse_nested(tb, TCA_ACT_MAX, nla, NULL, extack);
Patrick McHardycee63722008-01-23 20:33:32 -0800988 if (err < 0)
Patrick McHardyab27cfb2008-01-23 20:33:13 -0800989 goto err_out;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700990
Patrick McHardycee63722008-01-23 20:33:32 -0800991 err = -EINVAL;
Patrick McHardy7ba699c2008-01-22 22:11:50 -0800992 if (tb[TCA_ACT_INDEX] == NULL ||
Alexander Aring84ae0172018-02-15 10:54:55 -0500993 nla_len(tb[TCA_ACT_INDEX]) < sizeof(index)) {
994 NL_SET_ERR_MSG(extack, "Invalid TC action index value");
Patrick McHardyab27cfb2008-01-23 20:33:13 -0800995 goto err_out;
Alexander Aring84ae0172018-02-15 10:54:55 -0500996 }
Patrick McHardy1587bac2008-01-23 20:35:03 -0800997 index = nla_get_u32(tb[TCA_ACT_INDEX]);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700998
Patrick McHardyab27cfb2008-01-23 20:33:13 -0800999 err = -EINVAL;
WANG Conga85a9702016-07-25 16:09:41 -07001000 ops = tc_lookup_action(tb[TCA_ACT_KIND]);
Alexander Aring84ae0172018-02-15 10:54:55 -05001001 if (!ops) { /* could happen in batch of actions */
1002 NL_SET_ERR_MSG(extack, "Specified TC action not found");
WANG Conga85a9702016-07-25 16:09:41 -07001003 goto err_out;
Alexander Aring84ae0172018-02-15 10:54:55 -05001004 }
Patrick McHardyab27cfb2008-01-23 20:33:13 -08001005 err = -ENOENT;
Alexander Aring331a9292018-02-15 10:54:57 -05001006 if (ops->lookup(net, &a, index, extack) == 0)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001007 goto err_mod;
1008
WANG Conga85a9702016-07-25 16:09:41 -07001009 module_put(ops->owner);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001010 return a;
Patrick McHardyab27cfb2008-01-23 20:33:13 -08001011
Linus Torvalds1da177e2005-04-16 15:20:36 -07001012err_mod:
WANG Conga85a9702016-07-25 16:09:41 -07001013 module_put(ops->owner);
Patrick McHardyab27cfb2008-01-23 20:33:13 -08001014err_out:
1015 return ERR_PTR(err);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001016}
1017
Tom Goff7316ae82010-03-19 15:40:13 +00001018static int tca_action_flush(struct net *net, struct nlattr *nla,
Alexander Aring84ae0172018-02-15 10:54:55 -05001019 struct nlmsghdr *n, u32 portid,
1020 struct netlink_ext_ack *extack)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001021{
1022 struct sk_buff *skb;
1023 unsigned char *b;
1024 struct nlmsghdr *nlh;
1025 struct tcamsg *t;
1026 struct netlink_callback dcb;
Patrick McHardy4b3550ef2008-01-23 20:34:11 -08001027 struct nlattr *nest;
Eric Dumazetcc7ec452011-01-19 19:26:56 +00001028 struct nlattr *tb[TCA_ACT_MAX + 1];
WANG Conga85a9702016-07-25 16:09:41 -07001029 const struct tc_action_ops *ops;
Patrick McHardy7ba699c2008-01-22 22:11:50 -08001030 struct nlattr *kind;
Jamal Hadi Salim36723872008-08-13 02:41:45 -07001031 int err = -ENOMEM;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001032
Linus Torvalds1da177e2005-04-16 15:20:36 -07001033 skb = alloc_skb(NLMSG_GOODSIZE, GFP_KERNEL);
Alexander Aring84ae0172018-02-15 10:54:55 -05001034 if (!skb)
Jamal Hadi Salim36723872008-08-13 02:41:45 -07001035 return err;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001036
Arnaldo Carvalho de Melo27a884d2007-04-19 20:29:13 -07001037 b = skb_tail_pointer(skb);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001038
Alexander Aring84ae0172018-02-15 10:54:55 -05001039 err = nla_parse_nested(tb, TCA_ACT_MAX, nla, NULL, extack);
Patrick McHardycee63722008-01-23 20:33:32 -08001040 if (err < 0)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001041 goto err_out;
1042
Patrick McHardycee63722008-01-23 20:33:32 -08001043 err = -EINVAL;
Patrick McHardy7ba699c2008-01-22 22:11:50 -08001044 kind = tb[TCA_ACT_KIND];
WANG Conga85a9702016-07-25 16:09:41 -07001045 ops = tc_lookup_action(kind);
Alexander Aring84ae0172018-02-15 10:54:55 -05001046 if (!ops) { /*some idjot trying to flush unknown action */
1047 NL_SET_ERR_MSG(extack, "Cannot flush unknown TC action");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001048 goto err_out;
Alexander Aring84ae0172018-02-15 10:54:55 -05001049 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001050
Jamal Hadi Salim0b0f43f2016-06-05 10:41:32 -04001051 nlh = nlmsg_put(skb, portid, n->nlmsg_seq, RTM_DELACTION,
1052 sizeof(*t), 0);
Alexander Aring84ae0172018-02-15 10:54:55 -05001053 if (!nlh) {
1054 NL_SET_ERR_MSG(extack, "Failed to create TC action flush notification");
David S. Miller8b00a532012-06-26 21:39:32 -07001055 goto out_module_put;
Alexander Aring84ae0172018-02-15 10:54:55 -05001056 }
David S. Miller8b00a532012-06-26 21:39:32 -07001057 t = nlmsg_data(nlh);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001058 t->tca_family = AF_UNSPEC;
Patrick McHardy9ef1d4c2005-06-28 12:55:30 -07001059 t->tca__pad1 = 0;
1060 t->tca__pad2 = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001061
Patrick McHardy4b3550ef2008-01-23 20:34:11 -08001062 nest = nla_nest_start(skb, TCA_ACT_TAB);
Alexander Aring84ae0172018-02-15 10:54:55 -05001063 if (!nest) {
1064 NL_SET_ERR_MSG(extack, "Failed to add new netlink message");
David S. Miller8b00a532012-06-26 21:39:32 -07001065 goto out_module_put;
Alexander Aring84ae0172018-02-15 10:54:55 -05001066 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001067
Alexander Aring41780102018-02-15 10:54:58 -05001068 err = ops->walk(net, skb, &dcb, RTM_DELACTION, ops, extack);
Davide Caratti66dede22018-02-15 15:50:57 +01001069 if (err <= 0) {
1070 nla_nest_cancel(skb, nest);
David S. Miller8b00a532012-06-26 21:39:32 -07001071 goto out_module_put;
Davide Caratti66dede22018-02-15 15:50:57 +01001072 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001073
Patrick McHardy4b3550ef2008-01-23 20:34:11 -08001074 nla_nest_end(skb, nest);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001075
Arnaldo Carvalho de Melo27a884d2007-04-19 20:29:13 -07001076 nlh->nlmsg_len = skb_tail_pointer(skb) - b;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001077 nlh->nlmsg_flags |= NLM_F_ROOT;
WANG Conga85a9702016-07-25 16:09:41 -07001078 module_put(ops->owner);
Eric W. Biederman15e47302012-09-07 20:12:54 +00001079 err = rtnetlink_send(skb, net, portid, RTNLGRP_TC,
Eric Dumazetcc7ec452011-01-19 19:26:56 +00001080 n->nlmsg_flags & NLM_F_ECHO);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001081 if (err > 0)
1082 return 0;
Alexander Aring84ae0172018-02-15 10:54:55 -05001083 if (err < 0)
1084 NL_SET_ERR_MSG(extack, "Failed to send TC action flush notification");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001085
1086 return err;
1087
David S. Miller8b00a532012-06-26 21:39:32 -07001088out_module_put:
WANG Conga85a9702016-07-25 16:09:41 -07001089 module_put(ops->owner);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001090err_out:
1091 kfree_skb(skb);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001092 return err;
1093}
1094
1095static int
WANG Conga56e1952014-01-09 16:14:00 -08001096tcf_del_notify(struct net *net, struct nlmsghdr *n, struct list_head *actions,
Roman Mashakd04e6992018-03-08 16:59:17 -05001097 u32 portid, size_t attr_size, struct netlink_ext_ack *extack)
WANG Conga56e1952014-01-09 16:14:00 -08001098{
1099 int ret;
1100 struct sk_buff *skb;
1101
Roman Mashakd04e6992018-03-08 16:59:17 -05001102 skb = alloc_skb(attr_size <= NLMSG_GOODSIZE ? NLMSG_GOODSIZE : attr_size,
1103 GFP_KERNEL);
WANG Conga56e1952014-01-09 16:14:00 -08001104 if (!skb)
1105 return -ENOBUFS;
1106
1107 if (tca_get_fill(skb, actions, portid, n->nlmsg_seq, 0, RTM_DELACTION,
Vlad Buslov3f7c72b2018-07-05 17:24:26 +03001108 0, 2) <= 0) {
Alexander Aring84ae0172018-02-15 10:54:55 -05001109 NL_SET_ERR_MSG(extack, "Failed to fill netlink TC action attributes");
WANG Conga56e1952014-01-09 16:14:00 -08001110 kfree_skb(skb);
1111 return -EINVAL;
1112 }
1113
1114 /* now do the delete */
WANG Cong55334a52014-02-11 17:07:34 -08001115 ret = tcf_action_destroy(actions, 0);
1116 if (ret < 0) {
Alexander Aring84ae0172018-02-15 10:54:55 -05001117 NL_SET_ERR_MSG(extack, "Failed to delete TC action");
WANG Cong55334a52014-02-11 17:07:34 -08001118 kfree_skb(skb);
1119 return ret;
1120 }
WANG Conga56e1952014-01-09 16:14:00 -08001121
1122 ret = rtnetlink_send(skb, net, portid, RTNLGRP_TC,
1123 n->nlmsg_flags & NLM_F_ECHO);
1124 if (ret > 0)
1125 return 0;
1126 return ret;
1127}
1128
1129static int
Tom Goff7316ae82010-03-19 15:40:13 +00001130tca_action_gd(struct net *net, struct nlattr *nla, struct nlmsghdr *n,
Alexander Aring84ae0172018-02-15 10:54:55 -05001131 u32 portid, int event, struct netlink_ext_ack *extack)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001132{
Patrick McHardycee63722008-01-23 20:33:32 -08001133 int i, ret;
Eric Dumazetcc7ec452011-01-19 19:26:56 +00001134 struct nlattr *tb[TCA_ACT_MAX_PRIO + 1];
WANG Cong33be6272013-12-15 20:15:05 -08001135 struct tc_action *act;
Roman Mashakd04e6992018-03-08 16:59:17 -05001136 size_t attr_size = 0;
WANG Cong33be6272013-12-15 20:15:05 -08001137 LIST_HEAD(actions);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001138
Alexander Aring84ae0172018-02-15 10:54:55 -05001139 ret = nla_parse_nested(tb, TCA_ACT_MAX_PRIO, nla, NULL, extack);
Patrick McHardycee63722008-01-23 20:33:32 -08001140 if (ret < 0)
1141 return ret;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001142
Eric Dumazetcc7ec452011-01-19 19:26:56 +00001143 if (event == RTM_DELACTION && n->nlmsg_flags & NLM_F_ROOT) {
Alexander Aring1af8515582018-02-15 10:54:53 -05001144 if (tb[1])
Alexander Aring84ae0172018-02-15 10:54:55 -05001145 return tca_action_flush(net, tb[1], n, portid, extack);
Alexander Aring1af8515582018-02-15 10:54:53 -05001146
Alexander Aring84ae0172018-02-15 10:54:55 -05001147 NL_SET_ERR_MSG(extack, "Invalid netlink attributes while flushing TC action");
Alexander Aring1af8515582018-02-15 10:54:53 -05001148 return -EINVAL;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001149 }
1150
Patrick McHardy7ba699c2008-01-22 22:11:50 -08001151 for (i = 1; i <= TCA_ACT_MAX_PRIO && tb[i]; i++) {
Alexander Aring84ae0172018-02-15 10:54:55 -05001152 act = tcf_action_get_1(net, tb[i], n, portid, extack);
Patrick McHardyab27cfb2008-01-23 20:33:13 -08001153 if (IS_ERR(act)) {
1154 ret = PTR_ERR(act);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001155 goto err;
Patrick McHardyab27cfb2008-01-23 20:33:13 -08001156 }
Patrick McHardy7ba699c2008-01-22 22:11:50 -08001157 act->order = i;
Roman Mashak4e76e752018-03-08 16:59:19 -05001158 attr_size += tcf_action_fill_size(act);
WANG Cong33be6272013-12-15 20:15:05 -08001159 list_add_tail(&act->list, &actions);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001160 }
1161
Roman Mashak4e76e752018-03-08 16:59:19 -05001162 attr_size = tcf_action_full_attrs_size(attr_size);
1163
Linus Torvalds1da177e2005-04-16 15:20:36 -07001164 if (event == RTM_GETACTION)
Alexander Aring84ae0172018-02-15 10:54:55 -05001165 ret = tcf_get_notify(net, portid, n, &actions, event, extack);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001166 else { /* delete */
Vlad Buslov3f7c72b2018-07-05 17:24:26 +03001167 cleanup_a(&actions, 1); /* lookup took reference */
Roman Mashakd04e6992018-03-08 16:59:17 -05001168 ret = tcf_del_notify(net, n, &actions, portid, attr_size, extack);
WANG Conga56e1952014-01-09 16:14:00 -08001169 if (ret)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001170 goto err;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001171 return ret;
1172 }
1173err:
Vlad Buslov3f7c72b2018-07-05 17:24:26 +03001174 tcf_action_destroy(&actions, 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001175 return ret;
1176}
1177
WANG Conga56e1952014-01-09 16:14:00 -08001178static int
1179tcf_add_notify(struct net *net, struct nlmsghdr *n, struct list_head *actions,
Roman Mashakd04e6992018-03-08 16:59:17 -05001180 u32 portid, size_t attr_size, struct netlink_ext_ack *extack)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001181{
Linus Torvalds1da177e2005-04-16 15:20:36 -07001182 struct sk_buff *skb;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001183 int err = 0;
1184
Roman Mashakd04e6992018-03-08 16:59:17 -05001185 skb = alloc_skb(attr_size <= NLMSG_GOODSIZE ? NLMSG_GOODSIZE : attr_size,
1186 GFP_KERNEL);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001187 if (!skb)
1188 return -ENOBUFS;
1189
WANG Conga56e1952014-01-09 16:14:00 -08001190 if (tca_get_fill(skb, actions, portid, n->nlmsg_seq, n->nlmsg_flags,
1191 RTM_NEWACTION, 0, 0) <= 0) {
Roman Mashakd143b9e2018-03-02 20:52:01 -05001192 NL_SET_ERR_MSG(extack, "Failed to fill netlink attributes while adding TC action");
WANG Conga56e1952014-01-09 16:14:00 -08001193 kfree_skb(skb);
1194 return -EINVAL;
1195 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001196
WANG Conga56e1952014-01-09 16:14:00 -08001197 err = rtnetlink_send(skb, net, portid, RTNLGRP_TC,
1198 n->nlmsg_flags & NLM_F_ECHO);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001199 if (err > 0)
1200 err = 0;
1201 return err;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001202}
1203
Jamal Hadi Salim5a7a5552016-09-18 08:45:33 -04001204static int tcf_action_add(struct net *net, struct nlattr *nla,
Alexander Aringaea0d722018-02-15 10:54:54 -05001205 struct nlmsghdr *n, u32 portid, int ovr,
1206 struct netlink_ext_ack *extack)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001207{
Roman Mashakd04e6992018-03-08 16:59:17 -05001208 size_t attr_size = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001209 int ret = 0;
WANG Cong33be6272013-12-15 20:15:05 -08001210 LIST_HEAD(actions);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001211
Alexander Aringaea0d722018-02-15 10:54:54 -05001212 ret = tcf_action_init(net, NULL, nla, NULL, NULL, ovr, 0, &actions,
Vlad Buslov789871b2018-07-05 17:24:25 +03001213 &attr_size, true, extack);
WANG Cong33be6272013-12-15 20:15:05 -08001214 if (ret)
WANG Congf07fed82016-08-13 22:34:56 -07001215 return ret;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001216
Roman Mashakd04e6992018-03-08 16:59:17 -05001217 return tcf_add_notify(net, n, &actions, portid, attr_size, extack);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001218}
1219
Jamal Hadi Salim90825b22017-07-30 13:24:51 -04001220static u32 tcaa_root_flags_allowed = TCA_FLAG_LARGE_DUMP_ON;
1221static const struct nla_policy tcaa_policy[TCA_ROOT_MAX + 1] = {
1222 [TCA_ROOT_FLAGS] = { .type = NLA_BITFIELD32,
1223 .validation_data = &tcaa_root_flags_allowed },
Jamal Hadi Salime62e4842017-07-30 13:24:52 -04001224 [TCA_ROOT_TIME_DELTA] = { .type = NLA_U32 },
Jamal Hadi Salim90825b22017-07-30 13:24:51 -04001225};
1226
David Ahernc21ef3e2017-04-16 09:48:24 -07001227static int tc_ctl_action(struct sk_buff *skb, struct nlmsghdr *n,
1228 struct netlink_ext_ack *extack)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001229{
YOSHIFUJI Hideaki3b1e0a62008-03-26 02:26:21 +09001230 struct net *net = sock_net(skb->sk);
Jamal Hadi Salim90825b22017-07-30 13:24:51 -04001231 struct nlattr *tca[TCA_ROOT_MAX + 1];
Eric W. Biederman15e47302012-09-07 20:12:54 +00001232 u32 portid = skb ? NETLINK_CB(skb).portid : 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001233 int ret = 0, ovr = 0;
1234
Jamal Hadi Salim0b0f43f2016-06-05 10:41:32 -04001235 if ((n->nlmsg_type != RTM_GETACTION) &&
1236 !netlink_capable(skb, CAP_NET_ADMIN))
Eric W. Biedermandfc47ef2012-11-16 03:03:00 +00001237 return -EPERM;
1238
Jamal Hadi Salim90825b22017-07-30 13:24:51 -04001239 ret = nlmsg_parse(n, sizeof(struct tcamsg), tca, TCA_ROOT_MAX, NULL,
David Ahernc21ef3e2017-04-16 09:48:24 -07001240 extack);
Patrick McHardy7ba699c2008-01-22 22:11:50 -08001241 if (ret < 0)
1242 return ret;
1243
1244 if (tca[TCA_ACT_TAB] == NULL) {
Alexander Aring84ae0172018-02-15 10:54:55 -05001245 NL_SET_ERR_MSG(extack, "Netlink action attributes missing");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001246 return -EINVAL;
1247 }
1248
Eric Dumazetcc7ec452011-01-19 19:26:56 +00001249 /* n->nlmsg_flags & NLM_F_CREATE */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001250 switch (n->nlmsg_type) {
1251 case RTM_NEWACTION:
1252 /* we are going to assume all other flags
Lucas De Marchi25985ed2011-03-30 22:57:33 -03001253 * imply create only if it doesn't exist
Linus Torvalds1da177e2005-04-16 15:20:36 -07001254 * Note that CREATE | EXCL implies that
1255 * but since we want avoid ambiguity (eg when flags
1256 * is zero) then just set this
1257 */
Eric Dumazetcc7ec452011-01-19 19:26:56 +00001258 if (n->nlmsg_flags & NLM_F_REPLACE)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001259 ovr = 1;
1260replay:
Alexander Aringaea0d722018-02-15 10:54:54 -05001261 ret = tcf_action_add(net, tca[TCA_ACT_TAB], n, portid, ovr,
1262 extack);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001263 if (ret == -EAGAIN)
1264 goto replay;
1265 break;
1266 case RTM_DELACTION:
Tom Goff7316ae82010-03-19 15:40:13 +00001267 ret = tca_action_gd(net, tca[TCA_ACT_TAB], n,
Alexander Aring84ae0172018-02-15 10:54:55 -05001268 portid, RTM_DELACTION, extack);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001269 break;
1270 case RTM_GETACTION:
Tom Goff7316ae82010-03-19 15:40:13 +00001271 ret = tca_action_gd(net, tca[TCA_ACT_TAB], n,
Alexander Aring84ae0172018-02-15 10:54:55 -05001272 portid, RTM_GETACTION, extack);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001273 break;
1274 default:
1275 BUG();
1276 }
1277
1278 return ret;
1279}
1280
Jamal Hadi Salim90825b22017-07-30 13:24:51 -04001281static struct nlattr *find_dump_kind(struct nlattr **nla)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001282{
Eric Dumazetcc7ec452011-01-19 19:26:56 +00001283 struct nlattr *tb1, *tb2[TCA_ACT_MAX + 1];
Patrick McHardy7ba699c2008-01-22 22:11:50 -08001284 struct nlattr *tb[TCA_ACT_MAX_PRIO + 1];
Patrick McHardy7ba699c2008-01-22 22:11:50 -08001285 struct nlattr *kind;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001286
Patrick McHardy7ba699c2008-01-22 22:11:50 -08001287 tb1 = nla[TCA_ACT_TAB];
Linus Torvalds1da177e2005-04-16 15:20:36 -07001288 if (tb1 == NULL)
1289 return NULL;
1290
Patrick McHardy7ba699c2008-01-22 22:11:50 -08001291 if (nla_parse(tb, TCA_ACT_MAX_PRIO, nla_data(tb1),
Johannes Bergfceb6432017-04-12 14:34:07 +02001292 NLMSG_ALIGN(nla_len(tb1)), NULL, NULL) < 0)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001293 return NULL;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001294
Patrick McHardy6d834e02008-01-23 20:32:42 -08001295 if (tb[1] == NULL)
1296 return NULL;
Johannes Bergfceb6432017-04-12 14:34:07 +02001297 if (nla_parse_nested(tb2, TCA_ACT_MAX, tb[1], NULL, NULL) < 0)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001298 return NULL;
Patrick McHardy7ba699c2008-01-22 22:11:50 -08001299 kind = tb2[TCA_ACT_KIND];
Linus Torvalds1da177e2005-04-16 15:20:36 -07001300
Thomas Graf26dab892006-07-05 20:45:06 -07001301 return kind;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001302}
1303
Jamal Hadi Salim5a7a5552016-09-18 08:45:33 -04001304static int tc_dump_action(struct sk_buff *skb, struct netlink_callback *cb)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001305{
WANG Congddf97cc2016-02-22 15:57:53 -08001306 struct net *net = sock_net(skb->sk);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001307 struct nlmsghdr *nlh;
Arnaldo Carvalho de Melo27a884d2007-04-19 20:29:13 -07001308 unsigned char *b = skb_tail_pointer(skb);
Patrick McHardy4b3550ef2008-01-23 20:34:11 -08001309 struct nlattr *nest;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001310 struct tc_action_ops *a_o;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001311 int ret = 0;
David S. Miller8b00a532012-06-26 21:39:32 -07001312 struct tcamsg *t = (struct tcamsg *) nlmsg_data(cb->nlh);
Jamal Hadi Salim90825b22017-07-30 13:24:51 -04001313 struct nlattr *tb[TCA_ROOT_MAX + 1];
1314 struct nlattr *count_attr = NULL;
Jamal Hadi Salime62e4842017-07-30 13:24:52 -04001315 unsigned long jiffy_since = 0;
Jamal Hadi Salim90825b22017-07-30 13:24:51 -04001316 struct nlattr *kind = NULL;
1317 struct nla_bitfield32 bf;
Jamal Hadi Salime62e4842017-07-30 13:24:52 -04001318 u32 msecs_since = 0;
Jamal Hadi Salim90825b22017-07-30 13:24:51 -04001319 u32 act_count = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001320
Jamal Hadi Salim90825b22017-07-30 13:24:51 -04001321 ret = nlmsg_parse(cb->nlh, sizeof(struct tcamsg), tb, TCA_ROOT_MAX,
1322 tcaa_policy, NULL);
1323 if (ret < 0)
1324 return ret;
1325
1326 kind = find_dump_kind(tb);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001327 if (kind == NULL) {
stephen hemminger6ff9c362010-05-12 06:37:05 +00001328 pr_info("tc_dump_action: action bad kind\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001329 return 0;
1330 }
1331
Thomas Graf26dab892006-07-05 20:45:06 -07001332 a_o = tc_lookup_action(kind);
Eric Dumazetcc7ec452011-01-19 19:26:56 +00001333 if (a_o == NULL)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001334 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001335
Jamal Hadi Salim90825b22017-07-30 13:24:51 -04001336 cb->args[2] = 0;
1337 if (tb[TCA_ROOT_FLAGS]) {
1338 bf = nla_get_bitfield32(tb[TCA_ROOT_FLAGS]);
1339 cb->args[2] = bf.value;
1340 }
1341
Jamal Hadi Salime62e4842017-07-30 13:24:52 -04001342 if (tb[TCA_ROOT_TIME_DELTA]) {
1343 msecs_since = nla_get_u32(tb[TCA_ROOT_TIME_DELTA]);
1344 }
1345
Eric W. Biederman15e47302012-09-07 20:12:54 +00001346 nlh = nlmsg_put(skb, NETLINK_CB(cb->skb).portid, cb->nlh->nlmsg_seq,
David S. Miller8b00a532012-06-26 21:39:32 -07001347 cb->nlh->nlmsg_type, sizeof(*t), 0);
1348 if (!nlh)
1349 goto out_module_put;
Jamal Hadi Salim90825b22017-07-30 13:24:51 -04001350
Jamal Hadi Salime62e4842017-07-30 13:24:52 -04001351 if (msecs_since)
1352 jiffy_since = jiffies - msecs_to_jiffies(msecs_since);
1353
David S. Miller8b00a532012-06-26 21:39:32 -07001354 t = nlmsg_data(nlh);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001355 t->tca_family = AF_UNSPEC;
Patrick McHardy9ef1d4c2005-06-28 12:55:30 -07001356 t->tca__pad1 = 0;
1357 t->tca__pad2 = 0;
Jamal Hadi Salime62e4842017-07-30 13:24:52 -04001358 cb->args[3] = jiffy_since;
Jamal Hadi Salim90825b22017-07-30 13:24:51 -04001359 count_attr = nla_reserve(skb, TCA_ROOT_COUNT, sizeof(u32));
1360 if (!count_attr)
1361 goto out_module_put;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001362
Patrick McHardy4b3550ef2008-01-23 20:34:11 -08001363 nest = nla_nest_start(skb, TCA_ACT_TAB);
1364 if (nest == NULL)
David S. Miller8b00a532012-06-26 21:39:32 -07001365 goto out_module_put;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001366
Alexander Aring41780102018-02-15 10:54:58 -05001367 ret = a_o->walk(net, skb, cb, RTM_GETACTION, a_o, NULL);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001368 if (ret < 0)
David S. Miller8b00a532012-06-26 21:39:32 -07001369 goto out_module_put;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001370
1371 if (ret > 0) {
Patrick McHardy4b3550ef2008-01-23 20:34:11 -08001372 nla_nest_end(skb, nest);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001373 ret = skb->len;
Jamal Hadi Salim90825b22017-07-30 13:24:51 -04001374 act_count = cb->args[1];
1375 memcpy(nla_data(count_attr), &act_count, sizeof(u32));
1376 cb->args[1] = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001377 } else
Jamal Hadi Salimebecaa62016-06-13 18:08:42 -04001378 nlmsg_trim(skb, b);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001379
Arnaldo Carvalho de Melo27a884d2007-04-19 20:29:13 -07001380 nlh->nlmsg_len = skb_tail_pointer(skb) - b;
Eric W. Biederman15e47302012-09-07 20:12:54 +00001381 if (NETLINK_CB(cb->skb).portid && ret)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001382 nlh->nlmsg_flags |= NLM_F_MULTI;
1383 module_put(a_o->owner);
1384 return skb->len;
1385
David S. Miller8b00a532012-06-26 21:39:32 -07001386out_module_put:
Linus Torvalds1da177e2005-04-16 15:20:36 -07001387 module_put(a_o->owner);
Arnaldo Carvalho de Melodc5fc572007-03-25 23:06:12 -07001388 nlmsg_trim(skb, b);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001389 return skb->len;
1390}
1391
Jiri Pirkob3f55bd2017-10-11 09:41:08 +02001392struct tcf_action_net {
1393 struct rhashtable egdev_ht;
1394};
1395
1396static unsigned int tcf_action_net_id;
1397
1398struct tcf_action_egdev_cb {
1399 struct list_head list;
1400 tc_setup_cb_t *cb;
1401 void *cb_priv;
1402};
1403
1404struct tcf_action_egdev {
1405 struct rhash_head ht_node;
1406 const struct net_device *dev;
1407 unsigned int refcnt;
1408 struct list_head cb_list;
1409};
1410
1411static const struct rhashtable_params tcf_action_egdev_ht_params = {
1412 .key_offset = offsetof(struct tcf_action_egdev, dev),
1413 .head_offset = offsetof(struct tcf_action_egdev, ht_node),
1414 .key_len = sizeof(const struct net_device *),
1415};
1416
1417static struct tcf_action_egdev *
1418tcf_action_egdev_lookup(const struct net_device *dev)
1419{
1420 struct net *net = dev_net(dev);
1421 struct tcf_action_net *tan = net_generic(net, tcf_action_net_id);
1422
1423 return rhashtable_lookup_fast(&tan->egdev_ht, &dev,
1424 tcf_action_egdev_ht_params);
1425}
1426
1427static struct tcf_action_egdev *
1428tcf_action_egdev_get(const struct net_device *dev)
1429{
1430 struct tcf_action_egdev *egdev;
1431 struct tcf_action_net *tan;
1432
1433 egdev = tcf_action_egdev_lookup(dev);
1434 if (egdev)
1435 goto inc_ref;
1436
1437 egdev = kzalloc(sizeof(*egdev), GFP_KERNEL);
1438 if (!egdev)
1439 return NULL;
1440 INIT_LIST_HEAD(&egdev->cb_list);
Or Gerlitz0843c092017-10-18 18:38:08 +03001441 egdev->dev = dev;
Jiri Pirkob3f55bd2017-10-11 09:41:08 +02001442 tan = net_generic(dev_net(dev), tcf_action_net_id);
1443 rhashtable_insert_fast(&tan->egdev_ht, &egdev->ht_node,
1444 tcf_action_egdev_ht_params);
1445
1446inc_ref:
1447 egdev->refcnt++;
1448 return egdev;
1449}
1450
1451static void tcf_action_egdev_put(struct tcf_action_egdev *egdev)
1452{
1453 struct tcf_action_net *tan;
1454
1455 if (--egdev->refcnt)
1456 return;
1457 tan = net_generic(dev_net(egdev->dev), tcf_action_net_id);
1458 rhashtable_remove_fast(&tan->egdev_ht, &egdev->ht_node,
1459 tcf_action_egdev_ht_params);
1460 kfree(egdev);
1461}
1462
1463static struct tcf_action_egdev_cb *
1464tcf_action_egdev_cb_lookup(struct tcf_action_egdev *egdev,
1465 tc_setup_cb_t *cb, void *cb_priv)
1466{
1467 struct tcf_action_egdev_cb *egdev_cb;
1468
1469 list_for_each_entry(egdev_cb, &egdev->cb_list, list)
1470 if (egdev_cb->cb == cb && egdev_cb->cb_priv == cb_priv)
1471 return egdev_cb;
1472 return NULL;
1473}
1474
1475static int tcf_action_egdev_cb_call(struct tcf_action_egdev *egdev,
1476 enum tc_setup_type type,
1477 void *type_data, bool err_stop)
1478{
1479 struct tcf_action_egdev_cb *egdev_cb;
1480 int ok_count = 0;
1481 int err;
1482
1483 list_for_each_entry(egdev_cb, &egdev->cb_list, list) {
1484 err = egdev_cb->cb(type, type_data, egdev_cb->cb_priv);
1485 if (err) {
1486 if (err_stop)
1487 return err;
1488 } else {
1489 ok_count++;
1490 }
1491 }
1492 return ok_count;
1493}
1494
1495static int tcf_action_egdev_cb_add(struct tcf_action_egdev *egdev,
1496 tc_setup_cb_t *cb, void *cb_priv)
1497{
1498 struct tcf_action_egdev_cb *egdev_cb;
1499
1500 egdev_cb = tcf_action_egdev_cb_lookup(egdev, cb, cb_priv);
1501 if (WARN_ON(egdev_cb))
1502 return -EEXIST;
1503 egdev_cb = kzalloc(sizeof(*egdev_cb), GFP_KERNEL);
1504 if (!egdev_cb)
1505 return -ENOMEM;
1506 egdev_cb->cb = cb;
1507 egdev_cb->cb_priv = cb_priv;
1508 list_add(&egdev_cb->list, &egdev->cb_list);
1509 return 0;
1510}
1511
1512static void tcf_action_egdev_cb_del(struct tcf_action_egdev *egdev,
1513 tc_setup_cb_t *cb, void *cb_priv)
1514{
1515 struct tcf_action_egdev_cb *egdev_cb;
1516
1517 egdev_cb = tcf_action_egdev_cb_lookup(egdev, cb, cb_priv);
1518 if (WARN_ON(!egdev_cb))
1519 return;
1520 list_del(&egdev_cb->list);
1521 kfree(egdev_cb);
1522}
1523
1524static int __tc_setup_cb_egdev_register(const struct net_device *dev,
1525 tc_setup_cb_t *cb, void *cb_priv)
1526{
1527 struct tcf_action_egdev *egdev = tcf_action_egdev_get(dev);
1528 int err;
1529
1530 if (!egdev)
1531 return -ENOMEM;
1532 err = tcf_action_egdev_cb_add(egdev, cb, cb_priv);
1533 if (err)
1534 goto err_cb_add;
1535 return 0;
1536
1537err_cb_add:
1538 tcf_action_egdev_put(egdev);
1539 return err;
1540}
1541int tc_setup_cb_egdev_register(const struct net_device *dev,
1542 tc_setup_cb_t *cb, void *cb_priv)
1543{
1544 int err;
1545
1546 rtnl_lock();
1547 err = __tc_setup_cb_egdev_register(dev, cb, cb_priv);
1548 rtnl_unlock();
1549 return err;
1550}
1551EXPORT_SYMBOL_GPL(tc_setup_cb_egdev_register);
1552
1553static void __tc_setup_cb_egdev_unregister(const struct net_device *dev,
1554 tc_setup_cb_t *cb, void *cb_priv)
1555{
1556 struct tcf_action_egdev *egdev = tcf_action_egdev_lookup(dev);
1557
1558 if (WARN_ON(!egdev))
1559 return;
1560 tcf_action_egdev_cb_del(egdev, cb, cb_priv);
1561 tcf_action_egdev_put(egdev);
1562}
1563void tc_setup_cb_egdev_unregister(const struct net_device *dev,
1564 tc_setup_cb_t *cb, void *cb_priv)
1565{
1566 rtnl_lock();
1567 __tc_setup_cb_egdev_unregister(dev, cb, cb_priv);
1568 rtnl_unlock();
1569}
1570EXPORT_SYMBOL_GPL(tc_setup_cb_egdev_unregister);
1571
1572int tc_setup_cb_egdev_call(const struct net_device *dev,
1573 enum tc_setup_type type, void *type_data,
1574 bool err_stop)
1575{
1576 struct tcf_action_egdev *egdev = tcf_action_egdev_lookup(dev);
1577
1578 if (!egdev)
1579 return 0;
1580 return tcf_action_egdev_cb_call(egdev, type, type_data, err_stop);
1581}
1582EXPORT_SYMBOL_GPL(tc_setup_cb_egdev_call);
1583
1584static __net_init int tcf_action_net_init(struct net *net)
1585{
1586 struct tcf_action_net *tan = net_generic(net, tcf_action_net_id);
1587
1588 return rhashtable_init(&tan->egdev_ht, &tcf_action_egdev_ht_params);
1589}
1590
1591static void __net_exit tcf_action_net_exit(struct net *net)
1592{
1593 struct tcf_action_net *tan = net_generic(net, tcf_action_net_id);
1594
1595 rhashtable_destroy(&tan->egdev_ht);
1596}
1597
1598static struct pernet_operations tcf_action_net_ops = {
1599 .init = tcf_action_net_init,
1600 .exit = tcf_action_net_exit,
1601 .id = &tcf_action_net_id,
1602 .size = sizeof(struct tcf_action_net),
1603};
1604
Linus Torvalds1da177e2005-04-16 15:20:36 -07001605static int __init tc_action_init(void)
1606{
Jiri Pirkob3f55bd2017-10-11 09:41:08 +02001607 int err;
1608
1609 err = register_pernet_subsys(&tcf_action_net_ops);
1610 if (err)
1611 return err;
1612
Florian Westphalb97bac62017-08-09 20:41:48 +02001613 rtnl_register(PF_UNSPEC, RTM_NEWACTION, tc_ctl_action, NULL, 0);
1614 rtnl_register(PF_UNSPEC, RTM_DELACTION, tc_ctl_action, NULL, 0);
Greg Rosec7ac8672011-06-10 01:27:09 +00001615 rtnl_register(PF_UNSPEC, RTM_GETACTION, tc_ctl_action, tc_dump_action,
Florian Westphalb97bac62017-08-09 20:41:48 +02001616 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001617
Linus Torvalds1da177e2005-04-16 15:20:36 -07001618 return 0;
1619}
1620
1621subsys_initcall(tc_action_init);