blob: c1a4b5d30814af521bc7f0102304e62b9d9db06c [file] [log] [blame]
Linus Torvalds1da177e2005-04-16 15:20:36 -07001/*
2 * net/sched/cls_api.c Packet classifier 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 * Authors: Alexey Kuznetsov, <kuznet@ms2.inr.ac.ru>
10 *
11 * Changes:
12 *
13 * Eduardo J. Blanco <ejbs@netlabs.com.uy> :990222: kmod support
14 *
15 */
16
Linus Torvalds1da177e2005-04-16 15:20:36 -070017#include <linux/module.h>
18#include <linux/types.h>
19#include <linux/kernel.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070020#include <linux/string.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070021#include <linux/errno.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070022#include <linux/skbuff.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070023#include <linux/init.h>
24#include <linux/kmod.h>
Patrick McHardyab27cfb2008-01-23 20:33:13 -080025#include <linux/err.h>
Tejun Heo5a0e3ad2010-03-24 17:04:11 +090026#include <linux/slab.h>
Denis V. Lunevb8542722007-12-01 00:21:31 +110027#include <net/net_namespace.h>
28#include <net/sock.h>
Arnaldo Carvalho de Melodc5fc572007-03-25 23:06:12 -070029#include <net/netlink.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070030#include <net/pkt_sched.h>
31#include <net/pkt_cls.h>
32
Linus Torvalds1da177e2005-04-16 15:20:36 -070033/* The list of all installed classifier types */
WANG Cong36272872013-12-15 20:15:11 -080034static LIST_HEAD(tcf_proto_base);
Linus Torvalds1da177e2005-04-16 15:20:36 -070035
36/* Protects list of registered TC modules. It is pure SMP lock. */
37static DEFINE_RWLOCK(cls_mod_lock);
38
39/* Find classifier type by string name */
40
Eric Dumazetdc7f9f62011-07-05 23:25:42 +000041static const struct tcf_proto_ops *tcf_proto_lookup_ops(struct nlattr *kind)
Linus Torvalds1da177e2005-04-16 15:20:36 -070042{
Eric Dumazetdcd76082013-12-20 10:04:18 -080043 const struct tcf_proto_ops *t, *res = NULL;
Linus Torvalds1da177e2005-04-16 15:20:36 -070044
45 if (kind) {
46 read_lock(&cls_mod_lock);
WANG Cong36272872013-12-15 20:15:11 -080047 list_for_each_entry(t, &tcf_proto_base, head) {
Patrick McHardyadd93b62008-01-22 22:11:33 -080048 if (nla_strcmp(kind, t->kind) == 0) {
Eric Dumazetdcd76082013-12-20 10:04:18 -080049 if (try_module_get(t->owner))
50 res = t;
Linus Torvalds1da177e2005-04-16 15:20:36 -070051 break;
52 }
53 }
54 read_unlock(&cls_mod_lock);
55 }
Eric Dumazetdcd76082013-12-20 10:04:18 -080056 return res;
Linus Torvalds1da177e2005-04-16 15:20:36 -070057}
58
59/* Register(unregister) new classifier type */
60
61int register_tcf_proto_ops(struct tcf_proto_ops *ops)
62{
WANG Cong36272872013-12-15 20:15:11 -080063 struct tcf_proto_ops *t;
Linus Torvalds1da177e2005-04-16 15:20:36 -070064 int rc = -EEXIST;
65
66 write_lock(&cls_mod_lock);
WANG Cong36272872013-12-15 20:15:11 -080067 list_for_each_entry(t, &tcf_proto_base, head)
Linus Torvalds1da177e2005-04-16 15:20:36 -070068 if (!strcmp(ops->kind, t->kind))
69 goto out;
70
WANG Cong36272872013-12-15 20:15:11 -080071 list_add_tail(&ops->head, &tcf_proto_base);
Linus Torvalds1da177e2005-04-16 15:20:36 -070072 rc = 0;
73out:
74 write_unlock(&cls_mod_lock);
75 return rc;
76}
Stephen Hemmingeraa767bf2008-01-21 02:26:41 -080077EXPORT_SYMBOL(register_tcf_proto_ops);
Linus Torvalds1da177e2005-04-16 15:20:36 -070078
79int unregister_tcf_proto_ops(struct tcf_proto_ops *ops)
80{
WANG Cong36272872013-12-15 20:15:11 -080081 struct tcf_proto_ops *t;
Linus Torvalds1da177e2005-04-16 15:20:36 -070082 int rc = -ENOENT;
83
Daniel Borkmannc78e1742015-05-20 17:13:33 +020084 /* Wait for outstanding call_rcu()s, if any, from a
85 * tcf_proto_ops's destroy() handler.
86 */
87 rcu_barrier();
88
Linus Torvalds1da177e2005-04-16 15:20:36 -070089 write_lock(&cls_mod_lock);
Eric Dumazetdcd76082013-12-20 10:04:18 -080090 list_for_each_entry(t, &tcf_proto_base, head) {
91 if (t == ops) {
92 list_del(&t->head);
93 rc = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -070094 break;
Eric Dumazetdcd76082013-12-20 10:04:18 -080095 }
96 }
Linus Torvalds1da177e2005-04-16 15:20:36 -070097 write_unlock(&cls_mod_lock);
98 return rc;
99}
Stephen Hemmingeraa767bf2008-01-21 02:26:41 -0800100EXPORT_SYMBOL(unregister_tcf_proto_ops);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700101
Tom Goff7316ae82010-03-19 15:40:13 +0000102static int tfilter_notify(struct net *net, struct sk_buff *oskb,
103 struct nlmsghdr *n, struct tcf_proto *tp,
Eric Dumazetfa59b272016-10-09 20:25:55 -0700104 unsigned long fh, int event, bool unicast);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700105
Daniel Borkmannea7f8272016-06-10 23:10:22 +0200106static void tfilter_notify_chain(struct net *net, struct sk_buff *oskb,
107 struct nlmsghdr *n,
108 struct tcf_proto __rcu **chain, int event)
109{
110 struct tcf_proto __rcu **it_chain;
111 struct tcf_proto *tp;
112
113 for (it_chain = chain; (tp = rtnl_dereference(*it_chain)) != NULL;
114 it_chain = &tp->next)
Roman Mashak19a8bb22016-11-22 20:57:04 -0500115 tfilter_notify(net, oskb, n, tp, 0, event, false);
Daniel Borkmannea7f8272016-06-10 23:10:22 +0200116}
Linus Torvalds1da177e2005-04-16 15:20:36 -0700117
118/* Select new prio value from the range, managed by kernel. */
119
Stephen Hemmingeraa767bf2008-01-21 02:26:41 -0800120static inline u32 tcf_auto_prio(struct tcf_proto *tp)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700121{
Stephen Hemmingeraa767bf2008-01-21 02:26:41 -0800122 u32 first = TC_H_MAKE(0xC0000000U, 0U);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700123
124 if (tp)
Eric Dumazetcc7ec452011-01-19 19:26:56 +0000125 first = tp->prio - 1;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700126
127 return first;
128}
129
130/* Add/change/delete/get a filter node */
131
Thomas Graf661d2962013-03-21 07:45:29 +0000132static int tc_ctl_tfilter(struct sk_buff *skb, struct nlmsghdr *n)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700133{
YOSHIFUJI Hideaki3b1e0a62008-03-26 02:26:21 +0900134 struct net *net = sock_net(skb->sk);
Patrick McHardyadd93b62008-01-22 22:11:33 -0800135 struct nlattr *tca[TCA_MAX + 1];
Linus Torvalds1da177e2005-04-16 15:20:36 -0700136 struct tcmsg *t;
137 u32 protocol;
138 u32 prio;
139 u32 nprio;
140 u32 parent;
141 struct net_device *dev;
142 struct Qdisc *q;
John Fastabend25d8c0d2014-09-12 20:05:27 -0700143 struct tcf_proto __rcu **back;
144 struct tcf_proto __rcu **chain;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700145 struct tcf_proto *tp;
Eric Dumazetdc7f9f62011-07-05 23:25:42 +0000146 const struct tcf_proto_ops *tp_ops;
Eric Dumazet20fea082007-11-14 01:44:41 -0800147 const struct Qdisc_class_ops *cops;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700148 unsigned long cl;
149 unsigned long fh;
150 int err;
Daniel Borkmann09babe42016-12-21 18:04:11 +0100151 int tp_created;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700152
Stéphane Graber4e8bbb82014-04-30 11:25:43 -0400153 if ((n->nlmsg_type != RTM_GETTFILTER) &&
David S. Miller5f013c9b2014-05-12 13:19:14 -0400154 !netlink_ns_capable(skb, net->user_ns, CAP_NET_ADMIN))
Eric W. Biedermandfc47ef2012-11-16 03:03:00 +0000155 return -EPERM;
Hong zhi guode179c82013-03-25 17:36:33 +0000156
Linus Torvalds1da177e2005-04-16 15:20:36 -0700157replay:
Daniel Borkmann09babe42016-12-21 18:04:11 +0100158 tp_created = 0;
159
Hong zhi guode179c82013-03-25 17:36:33 +0000160 err = nlmsg_parse(n, sizeof(*t), tca, TCA_MAX, NULL);
161 if (err < 0)
162 return err;
163
David S. Miller942b8162012-06-26 21:48:50 -0700164 t = nlmsg_data(n);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700165 protocol = TC_H_MIN(t->tcm_info);
166 prio = TC_H_MAJ(t->tcm_info);
167 nprio = prio;
168 parent = t->tcm_parent;
169 cl = 0;
170
171 if (prio == 0) {
Daniel Borkmannea7f8272016-06-10 23:10:22 +0200172 switch (n->nlmsg_type) {
173 case RTM_DELTFILTER:
Daniel Borkmann9f6ed032016-06-16 23:19:29 +0200174 if (protocol || t->tcm_handle || tca[TCA_KIND])
Daniel Borkmannea7f8272016-06-10 23:10:22 +0200175 return -ENOENT;
176 break;
177 case RTM_NEWTFILTER:
178 /* If no priority is provided by the user,
179 * we allocate one.
180 */
181 if (n->nlmsg_flags & NLM_F_CREATE) {
182 prio = TC_H_MAKE(0x80000000U, 0U);
183 break;
184 }
185 /* fall-through */
186 default:
Linus Torvalds1da177e2005-04-16 15:20:36 -0700187 return -ENOENT;
Daniel Borkmannea7f8272016-06-10 23:10:22 +0200188 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700189 }
190
191 /* Find head of filter chain. */
192
193 /* Find link */
Tom Goff7316ae82010-03-19 15:40:13 +0000194 dev = __dev_get_by_index(net, t->tcm_ifindex);
Stephen Hemmingeraa767bf2008-01-21 02:26:41 -0800195 if (dev == NULL)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700196 return -ENODEV;
197
198 /* Find qdisc */
199 if (!parent) {
Patrick McHardyaf356af2009-09-04 06:41:18 +0000200 q = dev->qdisc;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700201 parent = q->handle;
Stephen Hemmingeraa767bf2008-01-21 02:26:41 -0800202 } else {
203 q = qdisc_lookup(dev, TC_H_MAJ(t->tcm_parent));
204 if (q == NULL)
205 return -EINVAL;
206 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700207
208 /* Is it classful? */
Eric Dumazetcc7ec452011-01-19 19:26:56 +0000209 cops = q->ops->cl_ops;
210 if (!cops)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700211 return -EINVAL;
212
Patrick McHardy71ebe5e2009-09-04 06:41:15 +0000213 if (cops->tcf_chain == NULL)
214 return -EOPNOTSUPP;
215
Linus Torvalds1da177e2005-04-16 15:20:36 -0700216 /* Do we search for filter, attached to class? */
217 if (TC_H_MIN(parent)) {
218 cl = cops->get(q, parent);
219 if (cl == 0)
220 return -ENOENT;
221 }
222
223 /* And the last stroke */
224 chain = cops->tcf_chain(q, cl);
225 err = -EINVAL;
226 if (chain == NULL)
227 goto errout;
Daniel Borkmannea7f8272016-06-10 23:10:22 +0200228 if (n->nlmsg_type == RTM_DELTFILTER && prio == 0) {
229 tfilter_notify_chain(net, skb, n, chain, RTM_DELTFILTER);
230 tcf_destroy_chain(chain);
231 err = 0;
232 goto errout;
233 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700234
235 /* Check the chain for existence of proto-tcf with this priority */
John Fastabend25d8c0d2014-09-12 20:05:27 -0700236 for (back = chain;
237 (tp = rtnl_dereference(*back)) != NULL;
238 back = &tp->next) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700239 if (tp->prio >= prio) {
240 if (tp->prio == prio) {
Eric Dumazetcc7ec452011-01-19 19:26:56 +0000241 if (!nprio ||
242 (tp->protocol != protocol && protocol))
Linus Torvalds1da177e2005-04-16 15:20:36 -0700243 goto errout;
244 } else
245 tp = NULL;
246 break;
247 }
248 }
249
250 if (tp == NULL) {
251 /* Proto-tcf does not exist, create new one */
252
Patrick McHardyadd93b62008-01-22 22:11:33 -0800253 if (tca[TCA_KIND] == NULL || !protocol)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700254 goto errout;
255
256 err = -ENOENT;
Eric Dumazetcc7ec452011-01-19 19:26:56 +0000257 if (n->nlmsg_type != RTM_NEWTFILTER ||
258 !(n->nlmsg_flags & NLM_F_CREATE))
Linus Torvalds1da177e2005-04-16 15:20:36 -0700259 goto errout;
260
261
262 /* Create new proto tcf */
263
264 err = -ENOBUFS;
Stephen Hemmingeraa767bf2008-01-21 02:26:41 -0800265 tp = kzalloc(sizeof(*tp), GFP_KERNEL);
266 if (tp == NULL)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700267 goto errout;
Patrick McHardyf2df8242008-05-20 14:34:46 -0700268 err = -ENOENT;
Patrick McHardyadd93b62008-01-22 22:11:33 -0800269 tp_ops = tcf_proto_lookup_ops(tca[TCA_KIND]);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700270 if (tp_ops == NULL) {
Johannes Berg95a5afc2008-10-16 15:24:51 -0700271#ifdef CONFIG_MODULES
Patrick McHardyadd93b62008-01-22 22:11:33 -0800272 struct nlattr *kind = tca[TCA_KIND];
Linus Torvalds1da177e2005-04-16 15:20:36 -0700273 char name[IFNAMSIZ];
274
275 if (kind != NULL &&
Patrick McHardyadd93b62008-01-22 22:11:33 -0800276 nla_strlcpy(name, kind, IFNAMSIZ) < IFNAMSIZ) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700277 rtnl_unlock();
278 request_module("cls_%s", name);
279 rtnl_lock();
280 tp_ops = tcf_proto_lookup_ops(kind);
281 /* We dropped the RTNL semaphore in order to
282 * perform the module load. So, even if we
283 * succeeded in loading the module we have to
284 * replay the request. We indicate this using
285 * -EAGAIN.
286 */
287 if (tp_ops != NULL) {
288 module_put(tp_ops->owner);
289 err = -EAGAIN;
290 }
291 }
292#endif
293 kfree(tp);
294 goto errout;
295 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700296 tp->ops = tp_ops;
297 tp->protocol = protocol;
John Fastabend25d8c0d2014-09-12 20:05:27 -0700298 tp->prio = nprio ? :
299 TC_H_MAJ(tcf_auto_prio(rtnl_dereference(*back)));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700300 tp->q = q;
301 tp->classify = tp_ops->classify;
302 tp->classid = parent;
Stephen Hemmingeraa767bf2008-01-21 02:26:41 -0800303
304 err = tp_ops->init(tp);
305 if (err != 0) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700306 module_put(tp_ops->owner);
307 kfree(tp);
308 goto errout;
309 }
310
Minoru Usui12186be2009-06-02 02:17:34 -0700311 tp_created = 1;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700312
Patrick McHardyadd93b62008-01-22 22:11:33 -0800313 } else if (tca[TCA_KIND] && nla_strcmp(tca[TCA_KIND], tp->ops->kind))
Linus Torvalds1da177e2005-04-16 15:20:36 -0700314 goto errout;
315
316 fh = tp->ops->get(tp, t->tcm_handle);
317
318 if (fh == 0) {
319 if (n->nlmsg_type == RTM_DELTFILTER && t->tcm_handle == 0) {
John Fastabend25d8c0d2014-09-12 20:05:27 -0700320 struct tcf_proto *next = rtnl_dereference(tp->next);
321
322 RCU_INIT_POINTER(*back, next);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700323
Eric Dumazetfa59b272016-10-09 20:25:55 -0700324 tfilter_notify(net, skb, n, tp, fh,
325 RTM_DELTFILTER, false);
Cong Wang1e052be2015-03-06 11:47:59 -0800326 tcf_destroy(tp, true);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700327 err = 0;
328 goto errout;
329 }
330
331 err = -ENOENT;
Stephen Hemmingeraa767bf2008-01-21 02:26:41 -0800332 if (n->nlmsg_type != RTM_NEWTFILTER ||
333 !(n->nlmsg_flags & NLM_F_CREATE))
Linus Torvalds1da177e2005-04-16 15:20:36 -0700334 goto errout;
335 } else {
336 switch (n->nlmsg_type) {
YOSHIFUJI Hideaki10297b92007-02-09 23:25:16 +0900337 case RTM_NEWTFILTER:
Linus Torvalds1da177e2005-04-16 15:20:36 -0700338 err = -EEXIST;
Minoru Usui12186be2009-06-02 02:17:34 -0700339 if (n->nlmsg_flags & NLM_F_EXCL) {
340 if (tp_created)
Cong Wang1e052be2015-03-06 11:47:59 -0800341 tcf_destroy(tp, true);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700342 goto errout;
Minoru Usui12186be2009-06-02 02:17:34 -0700343 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700344 break;
345 case RTM_DELTFILTER:
346 err = tp->ops->delete(tp, fh);
Cong Wang1e052be2015-03-06 11:47:59 -0800347 if (err == 0) {
WANG Congd7443182015-05-05 15:22:02 -0700348 struct tcf_proto *next = rtnl_dereference(tp->next);
Cong Wang1e052be2015-03-06 11:47:59 -0800349
Jamal Hadi Salim9ee78372016-10-24 20:18:27 -0400350 tfilter_notify(net, skb, n, tp,
351 t->tcm_handle,
Eric Dumazetfa59b272016-10-09 20:25:55 -0700352 RTM_DELTFILTER, false);
WANG Congd7443182015-05-05 15:22:02 -0700353 if (tcf_destroy(tp, false))
Cong Wang1e052be2015-03-06 11:47:59 -0800354 RCU_INIT_POINTER(*back, next);
Cong Wang1e052be2015-03-06 11:47:59 -0800355 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700356 goto errout;
357 case RTM_GETTFILTER:
Jamal Hadi Salim5a7a5552016-09-18 08:45:33 -0400358 err = tfilter_notify(net, skb, n, tp, fh,
Eric Dumazetfa59b272016-10-09 20:25:55 -0700359 RTM_NEWTFILTER, true);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700360 goto errout;
361 default:
362 err = -EINVAL;
363 goto errout;
364 }
365 }
366
Cong Wang2f7ef2f2014-04-25 13:54:06 -0700367 err = tp->ops->change(net, skb, tp, cl, t->tcm_handle, tca, &fh,
368 n->nlmsg_flags & NLM_F_CREATE ? TCA_ACT_NOREPLACE : TCA_ACT_REPLACE);
Minoru Usui12186be2009-06-02 02:17:34 -0700369 if (err == 0) {
370 if (tp_created) {
John Fastabend25d8c0d2014-09-12 20:05:27 -0700371 RCU_INIT_POINTER(tp->next, rtnl_dereference(*back));
372 rcu_assign_pointer(*back, tp);
Minoru Usui12186be2009-06-02 02:17:34 -0700373 }
Eric Dumazetfa59b272016-10-09 20:25:55 -0700374 tfilter_notify(net, skb, n, tp, fh, RTM_NEWTFILTER, false);
Minoru Usui12186be2009-06-02 02:17:34 -0700375 } else {
376 if (tp_created)
Cong Wang1e052be2015-03-06 11:47:59 -0800377 tcf_destroy(tp, true);
Minoru Usui12186be2009-06-02 02:17:34 -0700378 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700379
380errout:
381 if (cl)
382 cops->put(q, cl);
383 if (err == -EAGAIN)
384 /* Replay the request. */
385 goto replay;
386 return err;
387}
388
Jamal Hadi Salim0b0f43f2016-06-05 10:41:32 -0400389static int tcf_fill_node(struct net *net, struct sk_buff *skb,
390 struct tcf_proto *tp, unsigned long fh, u32 portid,
391 u32 seq, u16 flags, int event)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700392{
393 struct tcmsg *tcm;
394 struct nlmsghdr *nlh;
Arnaldo Carvalho de Melo27a884d2007-04-19 20:29:13 -0700395 unsigned char *b = skb_tail_pointer(skb);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700396
Eric W. Biederman15e47302012-09-07 20:12:54 +0000397 nlh = nlmsg_put(skb, portid, seq, event, sizeof(*tcm), flags);
David S. Miller942b8162012-06-26 21:48:50 -0700398 if (!nlh)
399 goto out_nlmsg_trim;
400 tcm = nlmsg_data(nlh);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700401 tcm->tcm_family = AF_UNSPEC;
Patrick McHardy9ef1d4c2005-06-28 12:55:30 -0700402 tcm->tcm__pad1 = 0;
Jiri Pirkoad61df92009-10-08 01:21:46 -0700403 tcm->tcm__pad2 = 0;
David S. Miller5ce2d482008-07-08 17:06:30 -0700404 tcm->tcm_ifindex = qdisc_dev(tp->q)->ifindex;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700405 tcm->tcm_parent = tp->classid;
406 tcm->tcm_info = TC_H_MAKE(tp->prio, tp->protocol);
David S. Miller1b34ec42012-03-29 05:11:39 -0400407 if (nla_put_string(skb, TCA_KIND, tp->ops->kind))
408 goto nla_put_failure;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700409 tcm->tcm_handle = fh;
410 if (RTM_DELTFILTER != event) {
411 tcm->tcm_handle = 0;
WANG Cong832d1d52014-01-09 16:14:01 -0800412 if (tp->ops->dump && tp->ops->dump(net, tp, fh, skb, tcm) < 0)
Patrick McHardyadd93b62008-01-22 22:11:33 -0800413 goto nla_put_failure;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700414 }
Arnaldo Carvalho de Melo27a884d2007-04-19 20:29:13 -0700415 nlh->nlmsg_len = skb_tail_pointer(skb) - b;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700416 return skb->len;
417
David S. Miller942b8162012-06-26 21:48:50 -0700418out_nlmsg_trim:
Patrick McHardyadd93b62008-01-22 22:11:33 -0800419nla_put_failure:
Arnaldo Carvalho de Melodc5fc572007-03-25 23:06:12 -0700420 nlmsg_trim(skb, b);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700421 return -1;
422}
423
Tom Goff7316ae82010-03-19 15:40:13 +0000424static int tfilter_notify(struct net *net, struct sk_buff *oskb,
425 struct nlmsghdr *n, struct tcf_proto *tp,
Eric Dumazetfa59b272016-10-09 20:25:55 -0700426 unsigned long fh, int event, bool unicast)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700427{
428 struct sk_buff *skb;
Eric W. Biederman15e47302012-09-07 20:12:54 +0000429 u32 portid = oskb ? NETLINK_CB(oskb).portid : 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700430
431 skb = alloc_skb(NLMSG_GOODSIZE, GFP_KERNEL);
432 if (!skb)
433 return -ENOBUFS;
434
Roman Mashak30a391a2016-11-16 17:16:10 -0500435 if (tcf_fill_node(net, skb, tp, fh, portid, n->nlmsg_seq,
436 n->nlmsg_flags, event) <= 0) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700437 kfree_skb(skb);
438 return -EINVAL;
439 }
440
Eric Dumazetfa59b272016-10-09 20:25:55 -0700441 if (unicast)
442 return netlink_unicast(net->rtnl, skb, portid, MSG_DONTWAIT);
443
Eric W. Biederman15e47302012-09-07 20:12:54 +0000444 return rtnetlink_send(skb, net, portid, RTNLGRP_TC,
Stephen Hemmingeraa767bf2008-01-21 02:26:41 -0800445 n->nlmsg_flags & NLM_F_ECHO);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700446}
447
Stephen Hemmingeraa767bf2008-01-21 02:26:41 -0800448struct tcf_dump_args {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700449 struct tcf_walker w;
450 struct sk_buff *skb;
451 struct netlink_callback *cb;
452};
453
Stephen Hemmingeraa767bf2008-01-21 02:26:41 -0800454static int tcf_node_dump(struct tcf_proto *tp, unsigned long n,
455 struct tcf_walker *arg)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700456{
Stephen Hemmingeraa767bf2008-01-21 02:26:41 -0800457 struct tcf_dump_args *a = (void *)arg;
WANG Cong832d1d52014-01-09 16:14:01 -0800458 struct net *net = sock_net(a->skb->sk);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700459
WANG Cong832d1d52014-01-09 16:14:01 -0800460 return tcf_fill_node(net, a->skb, tp, n, NETLINK_CB(a->cb->skb).portid,
Jamal Hadi Salim5a7a5552016-09-18 08:45:33 -0400461 a->cb->nlh->nlmsg_seq, NLM_F_MULTI,
462 RTM_NEWTFILTER);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700463}
464
Eric Dumazetbd27a872009-11-05 20:57:26 -0800465/* called with RTNL */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700466static int tc_dump_tfilter(struct sk_buff *skb, struct netlink_callback *cb)
467{
YOSHIFUJI Hideaki3b1e0a62008-03-26 02:26:21 +0900468 struct net *net = sock_net(skb->sk);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700469 int t;
470 int s_t;
471 struct net_device *dev;
472 struct Qdisc *q;
John Fastabend25d8c0d2014-09-12 20:05:27 -0700473 struct tcf_proto *tp, __rcu **chain;
David S. Miller942b8162012-06-26 21:48:50 -0700474 struct tcmsg *tcm = nlmsg_data(cb->nlh);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700475 unsigned long cl = 0;
Eric Dumazet20fea082007-11-14 01:44:41 -0800476 const struct Qdisc_class_ops *cops;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700477 struct tcf_dump_args arg;
478
Hong zhi guo573ce262013-03-27 06:47:04 +0000479 if (nlmsg_len(cb->nlh) < sizeof(*tcm))
Linus Torvalds1da177e2005-04-16 15:20:36 -0700480 return skb->len;
Eric Dumazetcc7ec452011-01-19 19:26:56 +0000481 dev = __dev_get_by_index(net, tcm->tcm_ifindex);
482 if (!dev)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700483 return skb->len;
484
Linus Torvalds1da177e2005-04-16 15:20:36 -0700485 if (!tcm->tcm_parent)
Patrick McHardyaf356af2009-09-04 06:41:18 +0000486 q = dev->qdisc;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700487 else
488 q = qdisc_lookup(dev, TC_H_MAJ(tcm->tcm_parent));
489 if (!q)
490 goto out;
Eric Dumazetcc7ec452011-01-19 19:26:56 +0000491 cops = q->ops->cl_ops;
492 if (!cops)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700493 goto errout;
Patrick McHardy71ebe5e2009-09-04 06:41:15 +0000494 if (cops->tcf_chain == NULL)
495 goto errout;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700496 if (TC_H_MIN(tcm->tcm_parent)) {
497 cl = cops->get(q, tcm->tcm_parent);
498 if (cl == 0)
499 goto errout;
500 }
501 chain = cops->tcf_chain(q, cl);
502 if (chain == NULL)
503 goto errout;
504
505 s_t = cb->args[0];
506
John Fastabend25d8c0d2014-09-12 20:05:27 -0700507 for (tp = rtnl_dereference(*chain), t = 0;
508 tp; tp = rtnl_dereference(tp->next), t++) {
Eric Dumazetcc7ec452011-01-19 19:26:56 +0000509 if (t < s_t)
510 continue;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700511 if (TC_H_MAJ(tcm->tcm_info) &&
512 TC_H_MAJ(tcm->tcm_info) != tp->prio)
513 continue;
514 if (TC_H_MIN(tcm->tcm_info) &&
515 TC_H_MIN(tcm->tcm_info) != tp->protocol)
516 continue;
517 if (t > s_t)
Jamal Hadi Salim0b0f43f2016-06-05 10:41:32 -0400518 memset(&cb->args[1], 0,
519 sizeof(cb->args)-sizeof(cb->args[0]));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700520 if (cb->args[1] == 0) {
Jamal Hadi Salim0b0f43f2016-06-05 10:41:32 -0400521 if (tcf_fill_node(net, skb, tp, 0,
522 NETLINK_CB(cb->skb).portid,
Stephen Hemmingeraa767bf2008-01-21 02:26:41 -0800523 cb->nlh->nlmsg_seq, NLM_F_MULTI,
524 RTM_NEWTFILTER) <= 0)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700525 break;
Stephen Hemmingeraa767bf2008-01-21 02:26:41 -0800526
Linus Torvalds1da177e2005-04-16 15:20:36 -0700527 cb->args[1] = 1;
528 }
529 if (tp->ops->walk == NULL)
530 continue;
531 arg.w.fn = tcf_node_dump;
532 arg.skb = skb;
533 arg.cb = cb;
534 arg.w.stop = 0;
Eric Dumazetcc7ec452011-01-19 19:26:56 +0000535 arg.w.skip = cb->args[1] - 1;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700536 arg.w.count = 0;
537 tp->ops->walk(tp, &arg.w);
Eric Dumazetcc7ec452011-01-19 19:26:56 +0000538 cb->args[1] = arg.w.count + 1;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700539 if (arg.w.stop)
540 break;
541 }
542
543 cb->args[0] = t;
544
545errout:
546 if (cl)
547 cops->put(q, cl);
548out:
Linus Torvalds1da177e2005-04-16 15:20:36 -0700549 return skb->len;
550}
551
WANG Cong18d02642014-09-25 10:26:37 -0700552void tcf_exts_destroy(struct tcf_exts *exts)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700553{
554#ifdef CONFIG_NET_CLS_ACT
WANG Cong22dc13c2016-08-13 22:35:00 -0700555 LIST_HEAD(actions);
556
557 tcf_exts_to_list(exts, &actions);
558 tcf_action_destroy(&actions, TCA_ACT_UNBIND);
559 kfree(exts->actions);
560 exts->nr_actions = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700561#endif
562}
Stephen Hemmingeraa767bf2008-01-21 02:26:41 -0800563EXPORT_SYMBOL(tcf_exts_destroy);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700564
Benjamin LaHaisec1b52732013-01-14 05:15:39 +0000565int tcf_exts_validate(struct net *net, struct tcf_proto *tp, struct nlattr **tb,
Jamal Hadi Salim5a7a5552016-09-18 08:45:33 -0400566 struct nlattr *rate_tlv, struct tcf_exts *exts, bool ovr)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700567{
Linus Torvalds1da177e2005-04-16 15:20:36 -0700568#ifdef CONFIG_NET_CLS_ACT
569 {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700570 struct tc_action *act;
571
WANG Cong5da57f42013-12-15 20:15:07 -0800572 if (exts->police && tb[exts->police]) {
573 act = tcf_action_init_1(net, tb[exts->police], rate_tlv,
Jamal Hadi Salim5a7a5552016-09-18 08:45:33 -0400574 "police", ovr, TCA_ACT_BIND);
Patrick McHardyab27cfb2008-01-23 20:33:13 -0800575 if (IS_ERR(act))
576 return PTR_ERR(act);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700577
WANG Cong33be6272013-12-15 20:15:05 -0800578 act->type = exts->type = TCA_OLD_COMPAT;
WANG Cong22dc13c2016-08-13 22:35:00 -0700579 exts->actions[0] = act;
580 exts->nr_actions = 1;
WANG Cong5da57f42013-12-15 20:15:07 -0800581 } else if (exts->action && tb[exts->action]) {
WANG Cong22dc13c2016-08-13 22:35:00 -0700582 LIST_HEAD(actions);
583 int err, i = 0;
584
WANG Cong5da57f42013-12-15 20:15:07 -0800585 err = tcf_action_init(net, tb[exts->action], rate_tlv,
Jamal Hadi Salim5a7a5552016-09-18 08:45:33 -0400586 NULL, ovr, TCA_ACT_BIND,
587 &actions);
WANG Cong33be6272013-12-15 20:15:05 -0800588 if (err)
589 return err;
WANG Cong22dc13c2016-08-13 22:35:00 -0700590 list_for_each_entry(act, &actions, list)
591 exts->actions[i++] = act;
592 exts->nr_actions = i;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700593 }
594 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700595#else
WANG Cong5da57f42013-12-15 20:15:07 -0800596 if ((exts->action && tb[exts->action]) ||
597 (exts->police && tb[exts->police]))
Linus Torvalds1da177e2005-04-16 15:20:36 -0700598 return -EOPNOTSUPP;
599#endif
600
601 return 0;
602}
Stephen Hemmingeraa767bf2008-01-21 02:26:41 -0800603EXPORT_SYMBOL(tcf_exts_validate);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700604
Stephen Hemmingeraa767bf2008-01-21 02:26:41 -0800605void tcf_exts_change(struct tcf_proto *tp, struct tcf_exts *dst,
606 struct tcf_exts *src)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700607{
608#ifdef CONFIG_NET_CLS_ACT
WANG Cong22dc13c2016-08-13 22:35:00 -0700609 struct tcf_exts old = *dst;
610
Cong Wanga49eb422014-04-25 13:55:30 -0700611 tcf_tree_lock(tp);
WANG Cong22dc13c2016-08-13 22:35:00 -0700612 dst->nr_actions = src->nr_actions;
613 dst->actions = src->actions;
WANG Cong5301e3e2014-10-06 17:21:54 -0700614 dst->type = src->type;
Cong Wanga49eb422014-04-25 13:55:30 -0700615 tcf_tree_unlock(tp);
WANG Cong22dc13c2016-08-13 22:35:00 -0700616
617 tcf_exts_destroy(&old);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700618#endif
619}
Stephen Hemmingeraa767bf2008-01-21 02:26:41 -0800620EXPORT_SYMBOL(tcf_exts_change);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700621
WANG Cong22dc13c2016-08-13 22:35:00 -0700622#ifdef CONFIG_NET_CLS_ACT
623static struct tc_action *tcf_exts_first_act(struct tcf_exts *exts)
624{
625 if (exts->nr_actions == 0)
626 return NULL;
627 else
628 return exts->actions[0];
629}
630#endif
WANG Cong33be6272013-12-15 20:15:05 -0800631
WANG Cong5da57f42013-12-15 20:15:07 -0800632int tcf_exts_dump(struct sk_buff *skb, struct tcf_exts *exts)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700633{
634#ifdef CONFIG_NET_CLS_ACT
Cong Wang9cc63db2014-07-16 14:25:30 -0700635 struct nlattr *nest;
636
WANG Cong22dc13c2016-08-13 22:35:00 -0700637 if (exts->action && exts->nr_actions) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700638 /*
639 * again for backward compatible mode - we want
640 * to work with both old and new modes of entering
641 * tc data even if iproute2 was newer - jhs
642 */
WANG Cong33be6272013-12-15 20:15:05 -0800643 if (exts->type != TCA_OLD_COMPAT) {
WANG Cong22dc13c2016-08-13 22:35:00 -0700644 LIST_HEAD(actions);
645
WANG Cong5da57f42013-12-15 20:15:07 -0800646 nest = nla_nest_start(skb, exts->action);
Patrick McHardy4b3550ef2008-01-23 20:34:11 -0800647 if (nest == NULL)
648 goto nla_put_failure;
WANG Cong22dc13c2016-08-13 22:35:00 -0700649
650 tcf_exts_to_list(exts, &actions);
651 if (tcf_action_dump(skb, &actions, 0, 0) < 0)
Patrick McHardyadd93b62008-01-22 22:11:33 -0800652 goto nla_put_failure;
Patrick McHardy4b3550ef2008-01-23 20:34:11 -0800653 nla_nest_end(skb, nest);
WANG Cong5da57f42013-12-15 20:15:07 -0800654 } else if (exts->police) {
WANG Cong33be6272013-12-15 20:15:05 -0800655 struct tc_action *act = tcf_exts_first_act(exts);
WANG Cong5da57f42013-12-15 20:15:07 -0800656 nest = nla_nest_start(skb, exts->police);
Jamal Hadi Salim63acd682013-12-23 08:02:12 -0500657 if (nest == NULL || !act)
Patrick McHardy4b3550ef2008-01-23 20:34:11 -0800658 goto nla_put_failure;
WANG Cong33be6272013-12-15 20:15:05 -0800659 if (tcf_action_dump_old(skb, act, 0, 0) < 0)
Patrick McHardyadd93b62008-01-22 22:11:33 -0800660 goto nla_put_failure;
Patrick McHardy4b3550ef2008-01-23 20:34:11 -0800661 nla_nest_end(skb, nest);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700662 }
663 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700664 return 0;
Cong Wang9cc63db2014-07-16 14:25:30 -0700665
666nla_put_failure:
667 nla_nest_cancel(skb, nest);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700668 return -1;
Cong Wang9cc63db2014-07-16 14:25:30 -0700669#else
670 return 0;
671#endif
Linus Torvalds1da177e2005-04-16 15:20:36 -0700672}
Stephen Hemmingeraa767bf2008-01-21 02:26:41 -0800673EXPORT_SYMBOL(tcf_exts_dump);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700674
Stephen Hemmingeraa767bf2008-01-21 02:26:41 -0800675
WANG Cong5da57f42013-12-15 20:15:07 -0800676int tcf_exts_dump_stats(struct sk_buff *skb, struct tcf_exts *exts)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700677{
678#ifdef CONFIG_NET_CLS_ACT
WANG Cong33be6272013-12-15 20:15:05 -0800679 struct tc_action *a = tcf_exts_first_act(exts);
Ignacy Gawędzkib057df22015-02-03 19:05:18 +0100680 if (a != NULL && tcf_action_copy_stats(skb, a, 1) < 0)
WANG Cong33be6272013-12-15 20:15:05 -0800681 return -1;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700682#endif
683 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700684}
Stephen Hemmingeraa767bf2008-01-21 02:26:41 -0800685EXPORT_SYMBOL(tcf_exts_dump_stats);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700686
687static int __init tc_filter_init(void)
688{
Greg Rosec7ac8672011-06-10 01:27:09 +0000689 rtnl_register(PF_UNSPEC, RTM_NEWTFILTER, tc_ctl_tfilter, NULL, NULL);
690 rtnl_register(PF_UNSPEC, RTM_DELTFILTER, tc_ctl_tfilter, NULL, NULL);
Thomas Graf82623c02007-03-22 11:56:22 -0700691 rtnl_register(PF_UNSPEC, RTM_GETTFILTER, tc_ctl_tfilter,
Greg Rosec7ac8672011-06-10 01:27:09 +0000692 tc_dump_tfilter, NULL);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700693
Linus Torvalds1da177e2005-04-16 15:20:36 -0700694 return 0;
695}
696
697subsys_initcall(tc_filter_init);