cls_api.c 18.4 KB
Newer Older
L
Linus Torvalds 已提交
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
/*
 * net/sched/cls_api.c	Packet classifier API.
 *
 *		This program is free software; you can redistribute it and/or
 *		modify it under the terms of the GNU General Public License
 *		as published by the Free Software Foundation; either version
 *		2 of the License, or (at your option) any later version.
 *
 * Authors:	Alexey Kuznetsov, <kuznet@ms2.inr.ac.ru>
 *
 * Changes:
 *
 * Eduardo J. Blanco <ejbs@netlabs.com.uy> :990222: kmod support
 *
 */

#include <linux/module.h>
#include <linux/types.h>
#include <linux/kernel.h>
#include <linux/string.h>
#include <linux/errno.h>
22
#include <linux/err.h>
L
Linus Torvalds 已提交
23 24 25
#include <linux/skbuff.h>
#include <linux/init.h>
#include <linux/kmod.h>
26
#include <linux/err.h>
27
#include <linux/slab.h>
28 29
#include <net/net_namespace.h>
#include <net/sock.h>
30
#include <net/netlink.h>
L
Linus Torvalds 已提交
31 32 33 34
#include <net/pkt_sched.h>
#include <net/pkt_cls.h>

/* The list of all installed classifier types */
35
static LIST_HEAD(tcf_proto_base);
L
Linus Torvalds 已提交
36 37 38 39 40 41

/* Protects list of registered TC modules. It is pure SMP lock. */
static DEFINE_RWLOCK(cls_mod_lock);

/* Find classifier type by string name */

42
static const struct tcf_proto_ops *tcf_proto_lookup_ops(const char *kind)
L
Linus Torvalds 已提交
43
{
44
	const struct tcf_proto_ops *t, *res = NULL;
L
Linus Torvalds 已提交
45 46 47

	if (kind) {
		read_lock(&cls_mod_lock);
48
		list_for_each_entry(t, &tcf_proto_base, head) {
49
			if (strcmp(kind, t->kind) == 0) {
50 51
				if (try_module_get(t->owner))
					res = t;
L
Linus Torvalds 已提交
52 53 54 55 56
				break;
			}
		}
		read_unlock(&cls_mod_lock);
	}
57
	return res;
L
Linus Torvalds 已提交
58 59 60 61 62 63
}

/* Register(unregister) new classifier type */

int register_tcf_proto_ops(struct tcf_proto_ops *ops)
{
64
	struct tcf_proto_ops *t;
L
Linus Torvalds 已提交
65 66 67
	int rc = -EEXIST;

	write_lock(&cls_mod_lock);
68
	list_for_each_entry(t, &tcf_proto_base, head)
L
Linus Torvalds 已提交
69 70 71
		if (!strcmp(ops->kind, t->kind))
			goto out;

72
	list_add_tail(&ops->head, &tcf_proto_base);
L
Linus Torvalds 已提交
73 74 75 76 77
	rc = 0;
out:
	write_unlock(&cls_mod_lock);
	return rc;
}
78
EXPORT_SYMBOL(register_tcf_proto_ops);
L
Linus Torvalds 已提交
79 80 81

int unregister_tcf_proto_ops(struct tcf_proto_ops *ops)
{
82
	struct tcf_proto_ops *t;
L
Linus Torvalds 已提交
83 84
	int rc = -ENOENT;

85 86 87 88 89
	/* Wait for outstanding call_rcu()s, if any, from a
	 * tcf_proto_ops's destroy() handler.
	 */
	rcu_barrier();

L
Linus Torvalds 已提交
90
	write_lock(&cls_mod_lock);
91 92 93 94
	list_for_each_entry(t, &tcf_proto_base, head) {
		if (t == ops) {
			list_del(&t->head);
			rc = 0;
L
Linus Torvalds 已提交
95
			break;
96 97
		}
	}
L
Linus Torvalds 已提交
98 99 100
	write_unlock(&cls_mod_lock);
	return rc;
}
101
EXPORT_SYMBOL(unregister_tcf_proto_ops);
L
Linus Torvalds 已提交
102

103 104
static int tfilter_notify(struct net *net, struct sk_buff *oskb,
			  struct nlmsghdr *n, struct tcf_proto *tp,
105
			  unsigned long fh, int event, bool unicast);
L
Linus Torvalds 已提交
106

107 108 109 110 111 112 113 114 115
static void tfilter_notify_chain(struct net *net, struct sk_buff *oskb,
				 struct nlmsghdr *n,
				 struct tcf_proto __rcu **chain, int event)
{
	struct tcf_proto __rcu **it_chain;
	struct tcf_proto *tp;

	for (it_chain = chain; (tp = rtnl_dereference(*it_chain)) != NULL;
	     it_chain = &tp->next)
116
		tfilter_notify(net, oskb, n, tp, 0, event, false);
117
}
L
Linus Torvalds 已提交
118 119 120

/* Select new prio value from the range, managed by kernel. */

121
static inline u32 tcf_auto_prio(struct tcf_proto *tp)
L
Linus Torvalds 已提交
122
{
123
	u32 first = TC_H_MAKE(0xC0000000U, 0U);
L
Linus Torvalds 已提交
124 125

	if (tp)
E
Eric Dumazet 已提交
126
		first = tp->prio - 1;
L
Linus Torvalds 已提交
127 128 129 130

	return first;
}

131
static struct tcf_proto *tcf_proto_create(const char *kind, u32 protocol,
132 133
					  u32 prio, u32 parent, struct Qdisc *q,
					  struct tcf_block *block)
134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168
{
	struct tcf_proto *tp;
	int err;

	tp = kzalloc(sizeof(*tp), GFP_KERNEL);
	if (!tp)
		return ERR_PTR(-ENOBUFS);

	err = -ENOENT;
	tp->ops = tcf_proto_lookup_ops(kind);
	if (!tp->ops) {
#ifdef CONFIG_MODULES
		rtnl_unlock();
		request_module("cls_%s", kind);
		rtnl_lock();
		tp->ops = tcf_proto_lookup_ops(kind);
		/* We dropped the RTNL semaphore in order to perform
		 * the module load. So, even if we succeeded in loading
		 * the module we have to replay the request. We indicate
		 * this using -EAGAIN.
		 */
		if (tp->ops) {
			module_put(tp->ops->owner);
			err = -EAGAIN;
		} else {
			err = -ENOENT;
		}
		goto errout;
#endif
	}
	tp->classify = tp->ops->classify;
	tp->protocol = protocol;
	tp->prio = prio;
	tp->classid = parent;
	tp->q = q;
169
	tp->block = block;
170 171 172 173 174 175 176 177 178 179 180 181 182

	err = tp->ops->init(tp);
	if (err) {
		module_put(tp->ops->owner);
		goto errout;
	}
	return tp;

errout:
	kfree(tp);
	return ERR_PTR(err);
}

183
static void tcf_proto_destroy(struct tcf_proto *tp)
184
{
185 186 187
	tp->ops->destroy(tp);
	module_put(tp->ops->owner);
	kfree_rcu(tp, rcu);
188 189
}

190
static void tcf_chain_destroy(struct tcf_proto __rcu **fl)
191 192 193 194 195
{
	struct tcf_proto *tp;

	while ((tp = rtnl_dereference(*fl)) != NULL) {
		RCU_INIT_POINTER(*fl, tp->next);
196
		tcf_proto_destroy(tp);
197 198
	}
}
199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216

int tcf_block_get(struct tcf_block **p_block,
		  struct tcf_proto __rcu **p_filter_chain)
{
	struct tcf_block *block = kzalloc(sizeof(*block), GFP_KERNEL);

	if (!block)
		return -ENOMEM;
	block->p_filter_chain = p_filter_chain;
	*p_block = block;
	return 0;
}
EXPORT_SYMBOL(tcf_block_get);

void tcf_block_put(struct tcf_block *block)
{
	if (!block)
		return;
217
	tcf_chain_destroy(block->p_filter_chain);
218 219 220
	kfree(block);
}
EXPORT_SYMBOL(tcf_block_put);
221

222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269
/* Main classifier routine: scans classifier chain attached
 * to this qdisc, (optionally) tests for protocol and asks
 * specific classifiers.
 */
int tcf_classify(struct sk_buff *skb, const struct tcf_proto *tp,
		 struct tcf_result *res, bool compat_mode)
{
	__be16 protocol = tc_skb_protocol(skb);
#ifdef CONFIG_NET_CLS_ACT
	const int max_reclassify_loop = 4;
	const struct tcf_proto *old_tp = tp;
	int limit = 0;

reclassify:
#endif
	for (; tp; tp = rcu_dereference_bh(tp->next)) {
		int err;

		if (tp->protocol != protocol &&
		    tp->protocol != htons(ETH_P_ALL))
			continue;

		err = tp->classify(skb, tp, res);
#ifdef CONFIG_NET_CLS_ACT
		if (unlikely(err == TC_ACT_RECLASSIFY && !compat_mode))
			goto reset;
#endif
		if (err >= 0)
			return err;
	}

	return TC_ACT_UNSPEC; /* signal: continue lookup */
#ifdef CONFIG_NET_CLS_ACT
reset:
	if (unlikely(limit++ >= max_reclassify_loop)) {
		net_notice_ratelimited("%s: reclassify loop, rule prio %u, protocol %02x\n",
				       tp->q->ops->id, tp->prio & 0xffff,
				       ntohs(tp->protocol));
		return TC_ACT_SHOT;
	}

	tp = old_tp;
	protocol = tc_skb_protocol(skb);
	goto reclassify;
#endif
}
EXPORT_SYMBOL(tcf_classify);

L
Linus Torvalds 已提交
270 271
/* Add/change/delete/get a filter node */

272 273
static int tc_ctl_tfilter(struct sk_buff *skb, struct nlmsghdr *n,
			  struct netlink_ext_ack *extack)
L
Linus Torvalds 已提交
274
{
275
	struct net *net = sock_net(skb->sk);
276
	struct nlattr *tca[TCA_MAX + 1];
L
Linus Torvalds 已提交
277 278 279
	struct tcmsg *t;
	u32 protocol;
	u32 prio;
280
	bool prio_allocate;
L
Linus Torvalds 已提交
281 282 283
	u32 parent;
	struct net_device *dev;
	struct Qdisc  *q;
J
John Fastabend 已提交
284 285
	struct tcf_proto __rcu **back;
	struct tcf_proto __rcu **chain;
286
	struct tcf_block *block;
287
	struct tcf_proto *next;
L
Linus Torvalds 已提交
288
	struct tcf_proto *tp;
289
	const struct Qdisc_class_ops *cops;
L
Linus Torvalds 已提交
290 291 292
	unsigned long cl;
	unsigned long fh;
	int err;
293
	int tp_created;
L
Linus Torvalds 已提交
294

295
	if ((n->nlmsg_type != RTM_GETTFILTER) &&
296
	    !netlink_ns_capable(skb, net->user_ns, CAP_NET_ADMIN))
297
		return -EPERM;
298

L
Linus Torvalds 已提交
299
replay:
300 301
	tp_created = 0;

302
	err = nlmsg_parse(n, sizeof(*t), tca, TCA_MAX, NULL, extack);
303 304 305
	if (err < 0)
		return err;

306
	t = nlmsg_data(n);
L
Linus Torvalds 已提交
307 308
	protocol = TC_H_MIN(t->tcm_info);
	prio = TC_H_MAJ(t->tcm_info);
309
	prio_allocate = false;
L
Linus Torvalds 已提交
310 311 312 313
	parent = t->tcm_parent;
	cl = 0;

	if (prio == 0) {
314 315
		switch (n->nlmsg_type) {
		case RTM_DELTFILTER:
316
			if (protocol || t->tcm_handle || tca[TCA_KIND])
317 318 319 320 321 322 323 324
				return -ENOENT;
			break;
		case RTM_NEWTFILTER:
			/* If no priority is provided by the user,
			 * we allocate one.
			 */
			if (n->nlmsg_flags & NLM_F_CREATE) {
				prio = TC_H_MAKE(0x80000000U, 0U);
325
				prio_allocate = true;
326 327 328 329
				break;
			}
			/* fall-through */
		default:
L
Linus Torvalds 已提交
330
			return -ENOENT;
331
		}
L
Linus Torvalds 已提交
332 333 334 335 336
	}

	/* Find head of filter chain. */

	/* Find link */
337
	dev = __dev_get_by_index(net, t->tcm_ifindex);
338
	if (dev == NULL)
L
Linus Torvalds 已提交
339 340 341 342
		return -ENODEV;

	/* Find qdisc */
	if (!parent) {
343
		q = dev->qdisc;
L
Linus Torvalds 已提交
344
		parent = q->handle;
345 346 347 348 349
	} else {
		q = qdisc_lookup(dev, TC_H_MAJ(t->tcm_parent));
		if (q == NULL)
			return -EINVAL;
	}
L
Linus Torvalds 已提交
350 351

	/* Is it classful? */
E
Eric Dumazet 已提交
352 353
	cops = q->ops->cl_ops;
	if (!cops)
L
Linus Torvalds 已提交
354 355
		return -EINVAL;

356
	if (!cops->tcf_block)
357 358
		return -EOPNOTSUPP;

L
Linus Torvalds 已提交
359 360 361 362 363 364 365 366
	/* Do we search for filter, attached to class? */
	if (TC_H_MIN(parent)) {
		cl = cops->get(q, parent);
		if (cl == 0)
			return -ENOENT;
	}

	/* And the last stroke */
367 368
	block = cops->tcf_block(q, cl);
	if (!block) {
369
		err = -EINVAL;
L
Linus Torvalds 已提交
370
		goto errout;
371
	}
372 373
	chain = block->p_filter_chain;

374 375
	if (n->nlmsg_type == RTM_DELTFILTER && prio == 0) {
		tfilter_notify_chain(net, skb, n, chain, RTM_DELTFILTER);
376
		tcf_chain_destroy(chain);
377 378 379
		err = 0;
		goto errout;
	}
L
Linus Torvalds 已提交
380 381

	/* Check the chain for existence of proto-tcf with this priority */
J
John Fastabend 已提交
382 383 384
	for (back = chain;
	     (tp = rtnl_dereference(*back)) != NULL;
	     back = &tp->next) {
L
Linus Torvalds 已提交
385 386
		if (tp->prio >= prio) {
			if (tp->prio == prio) {
387
				if (prio_allocate ||
388 389
				    (tp->protocol != protocol && protocol)) {
					err = -EINVAL;
L
Linus Torvalds 已提交
390
					goto errout;
391
				}
392
			} else {
L
Linus Torvalds 已提交
393
				tp = NULL;
394
			}
L
Linus Torvalds 已提交
395 396 397 398 399 400 401
			break;
		}
	}

	if (tp == NULL) {
		/* Proto-tcf does not exist, create new one */

402 403
		if (tca[TCA_KIND] == NULL || !protocol) {
			err = -EINVAL;
L
Linus Torvalds 已提交
404
			goto errout;
405
		}
L
Linus Torvalds 已提交
406

E
Eric Dumazet 已提交
407
		if (n->nlmsg_type != RTM_NEWTFILTER ||
408 409
		    !(n->nlmsg_flags & NLM_F_CREATE)) {
			err = -ENOENT;
L
Linus Torvalds 已提交
410
			goto errout;
411
		}
L
Linus Torvalds 已提交
412

413 414
		if (prio_allocate)
			prio = TC_H_MAJ(tcf_auto_prio(rtnl_dereference(*back)));
L
Linus Torvalds 已提交
415

416
		tp = tcf_proto_create(nla_data(tca[TCA_KIND]),
417
				      protocol, prio, parent, q, block);
418 419
		if (IS_ERR(tp)) {
			err = PTR_ERR(tp);
L
Linus Torvalds 已提交
420 421
			goto errout;
		}
422
		tp_created = 1;
423 424
	} else if (tca[TCA_KIND] && nla_strcmp(tca[TCA_KIND], tp->ops->kind)) {
		err = -EINVAL;
L
Linus Torvalds 已提交
425
		goto errout;
426
	}
L
Linus Torvalds 已提交
427 428 429 430 431

	fh = tp->ops->get(tp, t->tcm_handle);

	if (fh == 0) {
		if (n->nlmsg_type == RTM_DELTFILTER && t->tcm_handle == 0) {
432
			next = rtnl_dereference(tp->next);
J
John Fastabend 已提交
433
			RCU_INIT_POINTER(*back, next);
434 435
			tfilter_notify(net, skb, n, tp, fh,
				       RTM_DELTFILTER, false);
436
			tcf_proto_destroy(tp);
L
Linus Torvalds 已提交
437 438 439 440
			err = 0;
			goto errout;
		}

441
		if (n->nlmsg_type != RTM_NEWTFILTER ||
442 443
		    !(n->nlmsg_flags & NLM_F_CREATE)) {
			err = -ENOENT;
L
Linus Torvalds 已提交
444
			goto errout;
445
		}
L
Linus Torvalds 已提交
446
	} else {
447 448
		bool last;

L
Linus Torvalds 已提交
449
		switch (n->nlmsg_type) {
450
		case RTM_NEWTFILTER:
451 452
			if (n->nlmsg_flags & NLM_F_EXCL) {
				if (tp_created)
453
					tcf_proto_destroy(tp);
454
				err = -EEXIST;
L
Linus Torvalds 已提交
455
				goto errout;
456
			}
L
Linus Torvalds 已提交
457 458
			break;
		case RTM_DELTFILTER:
459
			err = tp->ops->delete(tp, fh, &last);
460 461 462 463 464
			if (err)
				goto errout;
			next = rtnl_dereference(tp->next);
			tfilter_notify(net, skb, n, tp, t->tcm_handle,
				       RTM_DELTFILTER, false);
465
			if (last) {
466
				RCU_INIT_POINTER(*back, next);
467 468
				tcf_proto_destroy(tp);
			}
469
			goto errout;
L
Linus Torvalds 已提交
470
		case RTM_GETTFILTER:
J
Jamal Hadi Salim 已提交
471
			err = tfilter_notify(net, skb, n, tp, fh,
472
					     RTM_NEWTFILTER, true);
L
Linus Torvalds 已提交
473 474 475 476 477 478 479
			goto errout;
		default:
			err = -EINVAL;
			goto errout;
		}
	}

480 481
	err = tp->ops->change(net, skb, tp, cl, t->tcm_handle, tca, &fh,
			      n->nlmsg_flags & NLM_F_CREATE ? TCA_ACT_NOREPLACE : TCA_ACT_REPLACE);
482 483
	if (err == 0) {
		if (tp_created) {
J
John Fastabend 已提交
484 485
			RCU_INIT_POINTER(tp->next, rtnl_dereference(*back));
			rcu_assign_pointer(*back, tp);
486
		}
487
		tfilter_notify(net, skb, n, tp, fh, RTM_NEWTFILTER, false);
488 489
	} else {
		if (tp_created)
490
			tcf_proto_destroy(tp);
491
	}
L
Linus Torvalds 已提交
492 493 494 495 496 497 498 499 500 501

errout:
	if (cl)
		cops->put(q, cl);
	if (err == -EAGAIN)
		/* Replay the request. */
		goto replay;
	return err;
}

502 503 504
static int tcf_fill_node(struct net *net, struct sk_buff *skb,
			 struct tcf_proto *tp, unsigned long fh, u32 portid,
			 u32 seq, u16 flags, int event)
L
Linus Torvalds 已提交
505 506 507
{
	struct tcmsg *tcm;
	struct nlmsghdr  *nlh;
508
	unsigned char *b = skb_tail_pointer(skb);
L
Linus Torvalds 已提交
509

510
	nlh = nlmsg_put(skb, portid, seq, event, sizeof(*tcm), flags);
511 512 513
	if (!nlh)
		goto out_nlmsg_trim;
	tcm = nlmsg_data(nlh);
L
Linus Torvalds 已提交
514
	tcm->tcm_family = AF_UNSPEC;
515
	tcm->tcm__pad1 = 0;
J
Jiri Pirko 已提交
516
	tcm->tcm__pad2 = 0;
517
	tcm->tcm_ifindex = qdisc_dev(tp->q)->ifindex;
L
Linus Torvalds 已提交
518 519
	tcm->tcm_parent = tp->classid;
	tcm->tcm_info = TC_H_MAKE(tp->prio, tp->protocol);
520 521
	if (nla_put_string(skb, TCA_KIND, tp->ops->kind))
		goto nla_put_failure;
L
Linus Torvalds 已提交
522 523 524
	tcm->tcm_handle = fh;
	if (RTM_DELTFILTER != event) {
		tcm->tcm_handle = 0;
525
		if (tp->ops->dump && tp->ops->dump(net, tp, fh, skb, tcm) < 0)
526
			goto nla_put_failure;
L
Linus Torvalds 已提交
527
	}
528
	nlh->nlmsg_len = skb_tail_pointer(skb) - b;
L
Linus Torvalds 已提交
529 530
	return skb->len;

531
out_nlmsg_trim:
532
nla_put_failure:
533
	nlmsg_trim(skb, b);
L
Linus Torvalds 已提交
534 535 536
	return -1;
}

537 538
static int tfilter_notify(struct net *net, struct sk_buff *oskb,
			  struct nlmsghdr *n, struct tcf_proto *tp,
539
			  unsigned long fh, int event, bool unicast)
L
Linus Torvalds 已提交
540 541
{
	struct sk_buff *skb;
542
	u32 portid = oskb ? NETLINK_CB(oskb).portid : 0;
L
Linus Torvalds 已提交
543 544 545 546 547

	skb = alloc_skb(NLMSG_GOODSIZE, GFP_KERNEL);
	if (!skb)
		return -ENOBUFS;

548 549
	if (tcf_fill_node(net, skb, tp, fh, portid, n->nlmsg_seq,
			  n->nlmsg_flags, event) <= 0) {
L
Linus Torvalds 已提交
550 551 552 553
		kfree_skb(skb);
		return -EINVAL;
	}

554 555 556
	if (unicast)
		return netlink_unicast(net->rtnl, skb, portid, MSG_DONTWAIT);

557
	return rtnetlink_send(skb, net, portid, RTNLGRP_TC,
558
			      n->nlmsg_flags & NLM_F_ECHO);
L
Linus Torvalds 已提交
559 560
}

561
struct tcf_dump_args {
L
Linus Torvalds 已提交
562 563 564 565 566
	struct tcf_walker w;
	struct sk_buff *skb;
	struct netlink_callback *cb;
};

567 568
static int tcf_node_dump(struct tcf_proto *tp, unsigned long n,
			 struct tcf_walker *arg)
L
Linus Torvalds 已提交
569
{
570
	struct tcf_dump_args *a = (void *)arg;
571
	struct net *net = sock_net(a->skb->sk);
L
Linus Torvalds 已提交
572

573
	return tcf_fill_node(net, a->skb, tp, n, NETLINK_CB(a->cb->skb).portid,
J
Jamal Hadi Salim 已提交
574 575
			     a->cb->nlh->nlmsg_seq, NLM_F_MULTI,
			     RTM_NEWTFILTER);
L
Linus Torvalds 已提交
576 577
}

E
Eric Dumazet 已提交
578
/* called with RTNL */
L
Linus Torvalds 已提交
579 580
static int tc_dump_tfilter(struct sk_buff *skb, struct netlink_callback *cb)
{
581
	struct net *net = sock_net(skb->sk);
L
Linus Torvalds 已提交
582 583 584 585
	int t;
	int s_t;
	struct net_device *dev;
	struct Qdisc *q;
586
	struct tcf_block *block;
J
John Fastabend 已提交
587
	struct tcf_proto *tp, __rcu **chain;
588
	struct tcmsg *tcm = nlmsg_data(cb->nlh);
L
Linus Torvalds 已提交
589
	unsigned long cl = 0;
590
	const struct Qdisc_class_ops *cops;
L
Linus Torvalds 已提交
591 592
	struct tcf_dump_args arg;

593
	if (nlmsg_len(cb->nlh) < sizeof(*tcm))
L
Linus Torvalds 已提交
594
		return skb->len;
E
Eric Dumazet 已提交
595 596
	dev = __dev_get_by_index(net, tcm->tcm_ifindex);
	if (!dev)
L
Linus Torvalds 已提交
597 598 599
		return skb->len;

	if (!tcm->tcm_parent)
600
		q = dev->qdisc;
L
Linus Torvalds 已提交
601 602 603 604
	else
		q = qdisc_lookup(dev, TC_H_MAJ(tcm->tcm_parent));
	if (!q)
		goto out;
E
Eric Dumazet 已提交
605 606
	cops = q->ops->cl_ops;
	if (!cops)
L
Linus Torvalds 已提交
607
		goto errout;
608
	if (!cops->tcf_block)
609
		goto errout;
L
Linus Torvalds 已提交
610 611 612 613 614
	if (TC_H_MIN(tcm->tcm_parent)) {
		cl = cops->get(q, tcm->tcm_parent);
		if (cl == 0)
			goto errout;
	}
615 616
	block = cops->tcf_block(q, cl);
	if (!block)
L
Linus Torvalds 已提交
617
		goto errout;
618
	chain = block->p_filter_chain;
L
Linus Torvalds 已提交
619 620 621

	s_t = cb->args[0];

J
John Fastabend 已提交
622 623
	for (tp = rtnl_dereference(*chain), t = 0;
	     tp; tp = rtnl_dereference(tp->next), t++) {
E
Eric Dumazet 已提交
624 625
		if (t < s_t)
			continue;
L
Linus Torvalds 已提交
626 627 628 629 630 631 632
		if (TC_H_MAJ(tcm->tcm_info) &&
		    TC_H_MAJ(tcm->tcm_info) != tp->prio)
			continue;
		if (TC_H_MIN(tcm->tcm_info) &&
		    TC_H_MIN(tcm->tcm_info) != tp->protocol)
			continue;
		if (t > s_t)
633 634
			memset(&cb->args[1], 0,
			       sizeof(cb->args)-sizeof(cb->args[0]));
L
Linus Torvalds 已提交
635
		if (cb->args[1] == 0) {
636 637
			if (tcf_fill_node(net, skb, tp, 0,
					  NETLINK_CB(cb->skb).portid,
638 639
					  cb->nlh->nlmsg_seq, NLM_F_MULTI,
					  RTM_NEWTFILTER) <= 0)
L
Linus Torvalds 已提交
640
				break;
641

L
Linus Torvalds 已提交
642 643 644 645 646 647 648 649
			cb->args[1] = 1;
		}
		if (tp->ops->walk == NULL)
			continue;
		arg.w.fn = tcf_node_dump;
		arg.skb = skb;
		arg.cb = cb;
		arg.w.stop = 0;
E
Eric Dumazet 已提交
650
		arg.w.skip = cb->args[1] - 1;
L
Linus Torvalds 已提交
651 652
		arg.w.count = 0;
		tp->ops->walk(tp, &arg.w);
E
Eric Dumazet 已提交
653
		cb->args[1] = arg.w.count + 1;
L
Linus Torvalds 已提交
654 655 656 657 658 659 660 661 662 663 664 665 666
		if (arg.w.stop)
			break;
	}

	cb->args[0] = t;

errout:
	if (cl)
		cops->put(q, cl);
out:
	return skb->len;
}

667
void tcf_exts_destroy(struct tcf_exts *exts)
L
Linus Torvalds 已提交
668 669
{
#ifdef CONFIG_NET_CLS_ACT
670 671 672 673 674 675
	LIST_HEAD(actions);

	tcf_exts_to_list(exts, &actions);
	tcf_action_destroy(&actions, TCA_ACT_UNBIND);
	kfree(exts->actions);
	exts->nr_actions = 0;
L
Linus Torvalds 已提交
676 677
#endif
}
678
EXPORT_SYMBOL(tcf_exts_destroy);
L
Linus Torvalds 已提交
679

680
int tcf_exts_validate(struct net *net, struct tcf_proto *tp, struct nlattr **tb,
J
Jamal Hadi Salim 已提交
681
		      struct nlattr *rate_tlv, struct tcf_exts *exts, bool ovr)
L
Linus Torvalds 已提交
682 683 684 685 686
{
#ifdef CONFIG_NET_CLS_ACT
	{
		struct tc_action *act;

687 688
		if (exts->police && tb[exts->police]) {
			act = tcf_action_init_1(net, tb[exts->police], rate_tlv,
J
Jamal Hadi Salim 已提交
689
						"police", ovr, TCA_ACT_BIND);
690 691
			if (IS_ERR(act))
				return PTR_ERR(act);
L
Linus Torvalds 已提交
692

693
			act->type = exts->type = TCA_OLD_COMPAT;
694 695
			exts->actions[0] = act;
			exts->nr_actions = 1;
696
		} else if (exts->action && tb[exts->action]) {
697 698 699
			LIST_HEAD(actions);
			int err, i = 0;

700
			err = tcf_action_init(net, tb[exts->action], rate_tlv,
J
Jamal Hadi Salim 已提交
701 702
					      NULL, ovr, TCA_ACT_BIND,
					      &actions);
703 704
			if (err)
				return err;
705 706 707
			list_for_each_entry(act, &actions, list)
				exts->actions[i++] = act;
			exts->nr_actions = i;
L
Linus Torvalds 已提交
708 709 710
		}
	}
#else
711 712
	if ((exts->action && tb[exts->action]) ||
	    (exts->police && tb[exts->police]))
L
Linus Torvalds 已提交
713 714 715 716 717
		return -EOPNOTSUPP;
#endif

	return 0;
}
718
EXPORT_SYMBOL(tcf_exts_validate);
L
Linus Torvalds 已提交
719

720 721
void tcf_exts_change(struct tcf_proto *tp, struct tcf_exts *dst,
		     struct tcf_exts *src)
L
Linus Torvalds 已提交
722 723
{
#ifdef CONFIG_NET_CLS_ACT
724 725
	struct tcf_exts old = *dst;

726
	tcf_tree_lock(tp);
727 728
	dst->nr_actions = src->nr_actions;
	dst->actions = src->actions;
729
	dst->type = src->type;
730
	tcf_tree_unlock(tp);
731 732

	tcf_exts_destroy(&old);
L
Linus Torvalds 已提交
733 734
#endif
}
735
EXPORT_SYMBOL(tcf_exts_change);
L
Linus Torvalds 已提交
736

737 738 739 740 741 742 743 744 745
#ifdef CONFIG_NET_CLS_ACT
static struct tc_action *tcf_exts_first_act(struct tcf_exts *exts)
{
	if (exts->nr_actions == 0)
		return NULL;
	else
		return exts->actions[0];
}
#endif
746

747
int tcf_exts_dump(struct sk_buff *skb, struct tcf_exts *exts)
L
Linus Torvalds 已提交
748 749
{
#ifdef CONFIG_NET_CLS_ACT
750 751
	struct nlattr *nest;

752
	if (exts->action && exts->nr_actions) {
L
Linus Torvalds 已提交
753 754 755 756 757
		/*
		 * again for backward compatible mode - we want
		 * to work with both old and new modes of entering
		 * tc data even if iproute2  was newer - jhs
		 */
758
		if (exts->type != TCA_OLD_COMPAT) {
759 760
			LIST_HEAD(actions);

761
			nest = nla_nest_start(skb, exts->action);
762 763
			if (nest == NULL)
				goto nla_put_failure;
764 765 766

			tcf_exts_to_list(exts, &actions);
			if (tcf_action_dump(skb, &actions, 0, 0) < 0)
767
				goto nla_put_failure;
768
			nla_nest_end(skb, nest);
769
		} else if (exts->police) {
770
			struct tc_action *act = tcf_exts_first_act(exts);
771
			nest = nla_nest_start(skb, exts->police);
772
			if (nest == NULL || !act)
773
				goto nla_put_failure;
774
			if (tcf_action_dump_old(skb, act, 0, 0) < 0)
775
				goto nla_put_failure;
776
			nla_nest_end(skb, nest);
L
Linus Torvalds 已提交
777 778 779
		}
	}
	return 0;
780 781 782

nla_put_failure:
	nla_nest_cancel(skb, nest);
L
Linus Torvalds 已提交
783
	return -1;
784 785 786
#else
	return 0;
#endif
L
Linus Torvalds 已提交
787
}
788
EXPORT_SYMBOL(tcf_exts_dump);
L
Linus Torvalds 已提交
789

790

791
int tcf_exts_dump_stats(struct sk_buff *skb, struct tcf_exts *exts)
L
Linus Torvalds 已提交
792 793
{
#ifdef CONFIG_NET_CLS_ACT
794
	struct tc_action *a = tcf_exts_first_act(exts);
795
	if (a != NULL && tcf_action_copy_stats(skb, a, 1) < 0)
796
		return -1;
L
Linus Torvalds 已提交
797 798 799
#endif
	return 0;
}
800
EXPORT_SYMBOL(tcf_exts_dump_stats);
L
Linus Torvalds 已提交
801

802 803 804 805 806 807 808 809 810 811 812 813 814 815 816 817 818 819 820 821 822 823 824 825
int tcf_exts_get_dev(struct net_device *dev, struct tcf_exts *exts,
		     struct net_device **hw_dev)
{
#ifdef CONFIG_NET_CLS_ACT
	const struct tc_action *a;
	LIST_HEAD(actions);

	if (tc_no_actions(exts))
		return -EINVAL;

	tcf_exts_to_list(exts, &actions);
	list_for_each_entry(a, &actions, list) {
		if (a->ops->get_dev) {
			a->ops->get_dev(a, dev_net(dev), hw_dev);
			break;
		}
	}
	if (*hw_dev)
		return 0;
#endif
	return -EOPNOTSUPP;
}
EXPORT_SYMBOL(tcf_exts_get_dev);

L
Linus Torvalds 已提交
826 827
static int __init tc_filter_init(void)
{
828 829
	rtnl_register(PF_UNSPEC, RTM_NEWTFILTER, tc_ctl_tfilter, NULL, NULL);
	rtnl_register(PF_UNSPEC, RTM_DELTFILTER, tc_ctl_tfilter, NULL, NULL);
830
	rtnl_register(PF_UNSPEC, RTM_GETTFILTER, tc_ctl_tfilter,
831
		      tc_dump_tfilter, NULL);
L
Linus Torvalds 已提交
832 833 834 835 836

	return 0;
}

subsys_initcall(tc_filter_init);