cls_api.c 13.7 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 22 23 24
/*
 * 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>
#include <linux/skbuff.h>
#include <linux/init.h>
#include <linux/kmod.h>
25
#include <linux/netlink.h>
26
#include <linux/err.h>
27 28
#include <net/net_namespace.h>
#include <net/sock.h>
29
#include <net/netlink.h>
L
Linus Torvalds 已提交
30 31 32 33 34
#include <net/pkt_sched.h>
#include <net/pkt_cls.h>

/* The list of all installed classifier types */

35
static struct tcf_proto_ops *tcf_proto_base __read_mostly;
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 struct tcf_proto_ops *tcf_proto_lookup_ops(struct nlattr *kind)
L
Linus Torvalds 已提交
43 44 45 46 47 48
{
	struct tcf_proto_ops *t = NULL;

	if (kind) {
		read_lock(&cls_mod_lock);
		for (t = tcf_proto_base; t; t = t->next) {
49
			if (nla_strcmp(kind, t->kind) == 0) {
L
Linus Torvalds 已提交
50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78
				if (!try_module_get(t->owner))
					t = NULL;
				break;
			}
		}
		read_unlock(&cls_mod_lock);
	}
	return t;
}

/* Register(unregister) new classifier type */

int register_tcf_proto_ops(struct tcf_proto_ops *ops)
{
	struct tcf_proto_ops *t, **tp;
	int rc = -EEXIST;

	write_lock(&cls_mod_lock);
	for (tp = &tcf_proto_base; (t = *tp) != NULL; tp = &t->next)
		if (!strcmp(ops->kind, t->kind))
			goto out;

	ops->next = NULL;
	*tp = ops;
	rc = 0;
out:
	write_unlock(&cls_mod_lock);
	return rc;
}
79
EXPORT_SYMBOL(register_tcf_proto_ops);
L
Linus Torvalds 已提交
80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98

int unregister_tcf_proto_ops(struct tcf_proto_ops *ops)
{
	struct tcf_proto_ops *t, **tp;
	int rc = -ENOENT;

	write_lock(&cls_mod_lock);
	for (tp = &tcf_proto_base; (t=*tp) != NULL; tp = &t->next)
		if (t == ops)
			break;

	if (!t)
		goto out;
	*tp = t->next;
	rc = 0;
out:
	write_unlock(&cls_mod_lock);
	return rc;
}
99
EXPORT_SYMBOL(unregister_tcf_proto_ops);
L
Linus Torvalds 已提交
100 101 102 103 104 105 106

static int tfilter_notify(struct sk_buff *oskb, struct nlmsghdr *n,
			  struct tcf_proto *tp, unsigned long fh, int event);


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

107
static inline u32 tcf_auto_prio(struct tcf_proto *tp)
L
Linus Torvalds 已提交
108
{
109
	u32 first = TC_H_MAKE(0xC0000000U, 0U);
L
Linus Torvalds 已提交
110 111 112 113 114 115 116 117 118 119 120

	if (tp)
		first = tp->prio-1;

	return first;
}

/* Add/change/delete/get a filter node */

static int tc_ctl_tfilter(struct sk_buff *skb, struct nlmsghdr *n, void *arg)
{
121
	struct net *net = sock_net(skb->sk);
122
	struct nlattr *tca[TCA_MAX + 1];
123
	spinlock_t *root_lock;
L
Linus Torvalds 已提交
124 125 126 127 128 129 130 131 132 133
	struct tcmsg *t;
	u32 protocol;
	u32 prio;
	u32 nprio;
	u32 parent;
	struct net_device *dev;
	struct Qdisc  *q;
	struct tcf_proto **back, **chain;
	struct tcf_proto *tp;
	struct tcf_proto_ops *tp_ops;
134
	const struct Qdisc_class_ops *cops;
L
Linus Torvalds 已提交
135 136 137
	unsigned long cl;
	unsigned long fh;
	int err;
138
	int tp_created = 0;
L
Linus Torvalds 已提交
139

140 141 142
	if (net != &init_net)
		return -EINVAL;

L
Linus Torvalds 已提交
143 144 145 146 147 148 149 150 151 152 153 154
replay:
	t = NLMSG_DATA(n);
	protocol = TC_H_MIN(t->tcm_info);
	prio = TC_H_MAJ(t->tcm_info);
	nprio = prio;
	parent = t->tcm_parent;
	cl = 0;

	if (prio == 0) {
		/* If no priority is given, user wants we allocated it. */
		if (n->nlmsg_type != RTM_NEWTFILTER || !(n->nlmsg_flags&NLM_F_CREATE))
			return -ENOENT;
155
		prio = TC_H_MAKE(0x80000000U, 0U);
L
Linus Torvalds 已提交
156 157 158 159 160
	}

	/* Find head of filter chain. */

	/* Find link */
161 162
	dev = __dev_get_by_index(&init_net, t->tcm_ifindex);
	if (dev == NULL)
L
Linus Torvalds 已提交
163 164
		return -ENODEV;

165 166 167 168
	err = nlmsg_parse(n, sizeof(*t), tca, TCA_MAX, NULL);
	if (err < 0)
		return err;

L
Linus Torvalds 已提交
169 170
	/* Find qdisc */
	if (!parent) {
171
		struct netdev_queue *dev_queue = netdev_get_tx_queue(dev, 0);
172
		q = dev_queue->qdisc_sleeping;
L
Linus Torvalds 已提交
173
		parent = q->handle;
174 175 176 177 178
	} else {
		q = qdisc_lookup(dev, TC_H_MAJ(t->tcm_parent));
		if (q == NULL)
			return -EINVAL;
	}
L
Linus Torvalds 已提交
179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208

	/* Is it classful? */
	if ((cops = q->ops->cl_ops) == NULL)
		return -EINVAL;

	/* 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 */
	chain = cops->tcf_chain(q, cl);
	err = -EINVAL;
	if (chain == NULL)
		goto errout;

	/* Check the chain for existence of proto-tcf with this priority */
	for (back = chain; (tp=*back) != NULL; back = &tp->next) {
		if (tp->prio >= prio) {
			if (tp->prio == prio) {
				if (!nprio || (tp->protocol != protocol && protocol))
					goto errout;
			} else
				tp = NULL;
			break;
		}
	}

209
	root_lock = qdisc_root_sleeping_lock(q);
210

L
Linus Torvalds 已提交
211 212 213
	if (tp == NULL) {
		/* Proto-tcf does not exist, create new one */

214
		if (tca[TCA_KIND] == NULL || !protocol)
L
Linus Torvalds 已提交
215 216 217 218 219 220 221 222 223 224
			goto errout;

		err = -ENOENT;
		if (n->nlmsg_type != RTM_NEWTFILTER || !(n->nlmsg_flags&NLM_F_CREATE))
			goto errout;


		/* Create new proto tcf */

		err = -ENOBUFS;
225 226
		tp = kzalloc(sizeof(*tp), GFP_KERNEL);
		if (tp == NULL)
L
Linus Torvalds 已提交
227
			goto errout;
228
		err = -ENOENT;
229
		tp_ops = tcf_proto_lookup_ops(tca[TCA_KIND]);
L
Linus Torvalds 已提交
230
		if (tp_ops == NULL) {
231
#ifdef CONFIG_MODULES
232
			struct nlattr *kind = tca[TCA_KIND];
L
Linus Torvalds 已提交
233 234 235
			char name[IFNAMSIZ];

			if (kind != NULL &&
236
			    nla_strlcpy(name, kind, IFNAMSIZ) < IFNAMSIZ) {
L
Linus Torvalds 已提交
237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257
				rtnl_unlock();
				request_module("cls_%s", name);
				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 != NULL) {
					module_put(tp_ops->owner);
					err = -EAGAIN;
				}
			}
#endif
			kfree(tp);
			goto errout;
		}
		tp->ops = tp_ops;
		tp->protocol = protocol;
258
		tp->prio = nprio ? : TC_H_MAJ(tcf_auto_prio(*back));
L
Linus Torvalds 已提交
259 260 261
		tp->q = q;
		tp->classify = tp_ops->classify;
		tp->classid = parent;
262 263 264

		err = tp_ops->init(tp);
		if (err != 0) {
L
Linus Torvalds 已提交
265 266 267 268 269
			module_put(tp_ops->owner);
			kfree(tp);
			goto errout;
		}

270
		tp_created = 1;
L
Linus Torvalds 已提交
271

272
	} else if (tca[TCA_KIND] && nla_strcmp(tca[TCA_KIND], tp->ops->kind))
L
Linus Torvalds 已提交
273 274 275 276 277 278
		goto errout;

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

	if (fh == 0) {
		if (n->nlmsg_type == RTM_DELTFILTER && t->tcm_handle == 0) {
279
			spin_lock_bh(root_lock);
L
Linus Torvalds 已提交
280
			*back = tp->next;
281
			spin_unlock_bh(root_lock);
L
Linus Torvalds 已提交
282 283 284 285 286 287 288 289

			tfilter_notify(skb, n, tp, fh, RTM_DELTFILTER);
			tcf_destroy(tp);
			err = 0;
			goto errout;
		}

		err = -ENOENT;
290 291
		if (n->nlmsg_type != RTM_NEWTFILTER ||
		    !(n->nlmsg_flags & NLM_F_CREATE))
L
Linus Torvalds 已提交
292 293 294
			goto errout;
	} else {
		switch (n->nlmsg_type) {
295
		case RTM_NEWTFILTER:
L
Linus Torvalds 已提交
296
			err = -EEXIST;
297 298 299
			if (n->nlmsg_flags & NLM_F_EXCL) {
				if (tp_created)
					tcf_destroy(tp);
L
Linus Torvalds 已提交
300
				goto errout;
301
			}
L
Linus Torvalds 已提交
302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317
			break;
		case RTM_DELTFILTER:
			err = tp->ops->delete(tp, fh);
			if (err == 0)
				tfilter_notify(skb, n, tp, fh, RTM_DELTFILTER);
			goto errout;
		case RTM_GETTFILTER:
			err = tfilter_notify(skb, n, tp, fh, RTM_NEWTFILTER);
			goto errout;
		default:
			err = -EINVAL;
			goto errout;
		}
	}

	err = tp->ops->change(tp, cl, t->tcm_handle, tca, &fh);
318 319 320 321 322 323 324
	if (err == 0) {
		if (tp_created) {
			spin_lock_bh(root_lock);
			tp->next = *back;
			*back = tp;
			spin_unlock_bh(root_lock);
		}
L
Linus Torvalds 已提交
325
		tfilter_notify(skb, n, tp, fh, RTM_NEWTFILTER);
326 327 328 329
	} else {
		if (tp_created)
			tcf_destroy(tp);
	}
L
Linus Torvalds 已提交
330 331 332 333 334 335 336 337 338 339

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

340 341
static int tcf_fill_node(struct sk_buff *skb, struct tcf_proto *tp,
			 unsigned long fh, u32 pid, u32 seq, u16 flags, int event)
L
Linus Torvalds 已提交
342 343 344
{
	struct tcmsg *tcm;
	struct nlmsghdr  *nlh;
345
	unsigned char *b = skb_tail_pointer(skb);
L
Linus Torvalds 已提交
346

J
Jamal Hadi Salim 已提交
347
	nlh = NLMSG_NEW(skb, pid, seq, event, sizeof(*tcm), flags);
L
Linus Torvalds 已提交
348 349
	tcm = NLMSG_DATA(nlh);
	tcm->tcm_family = AF_UNSPEC;
350 351
	tcm->tcm__pad1 = 0;
	tcm->tcm__pad1 = 0;
352
	tcm->tcm_ifindex = qdisc_dev(tp->q)->ifindex;
L
Linus Torvalds 已提交
353 354
	tcm->tcm_parent = tp->classid;
	tcm->tcm_info = TC_H_MAKE(tp->prio, tp->protocol);
355
	NLA_PUT_STRING(skb, TCA_KIND, tp->ops->kind);
L
Linus Torvalds 已提交
356 357 358 359
	tcm->tcm_handle = fh;
	if (RTM_DELTFILTER != event) {
		tcm->tcm_handle = 0;
		if (tp->ops->dump && tp->ops->dump(tp, fh, skb, tcm) < 0)
360
			goto nla_put_failure;
L
Linus Torvalds 已提交
361
	}
362
	nlh->nlmsg_len = skb_tail_pointer(skb) - b;
L
Linus Torvalds 已提交
363 364 365
	return skb->len;

nlmsg_failure:
366
nla_put_failure:
367
	nlmsg_trim(skb, b);
L
Linus Torvalds 已提交
368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385
	return -1;
}

static int tfilter_notify(struct sk_buff *oskb, struct nlmsghdr *n,
			  struct tcf_proto *tp, unsigned long fh, int event)
{
	struct sk_buff *skb;
	u32 pid = oskb ? NETLINK_CB(oskb).pid : 0;

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

	if (tcf_fill_node(skb, tp, fh, pid, n->nlmsg_seq, 0, event) <= 0) {
		kfree_skb(skb);
		return -EINVAL;
	}

386 387
	return rtnetlink_send(skb, &init_net, pid, RTNLGRP_TC,
			      n->nlmsg_flags & NLM_F_ECHO);
L
Linus Torvalds 已提交
388 389
}

390
struct tcf_dump_args {
L
Linus Torvalds 已提交
391 392 393 394 395
	struct tcf_walker w;
	struct sk_buff *skb;
	struct netlink_callback *cb;
};

396 397
static int tcf_node_dump(struct tcf_proto *tp, unsigned long n,
			 struct tcf_walker *arg)
L
Linus Torvalds 已提交
398
{
399
	struct tcf_dump_args *a = (void *)arg;
L
Linus Torvalds 已提交
400 401 402 403 404 405 406

	return tcf_fill_node(a->skb, tp, n, NETLINK_CB(a->cb->skb).pid,
			     a->cb->nlh->nlmsg_seq, NLM_F_MULTI, RTM_NEWTFILTER);
}

static int tc_dump_tfilter(struct sk_buff *skb, struct netlink_callback *cb)
{
407
	struct net *net = sock_net(skb->sk);
408
	struct netdev_queue *dev_queue;
L
Linus Torvalds 已提交
409 410 411 412 413
	int t;
	int s_t;
	struct net_device *dev;
	struct Qdisc *q;
	struct tcf_proto *tp, **chain;
414
	struct tcmsg *tcm = (struct tcmsg *)NLMSG_DATA(cb->nlh);
L
Linus Torvalds 已提交
415
	unsigned long cl = 0;
416
	const struct Qdisc_class_ops *cops;
L
Linus Torvalds 已提交
417 418
	struct tcf_dump_args arg;

419 420 421
	if (net != &init_net)
		return 0;

L
Linus Torvalds 已提交
422 423
	if (cb->nlh->nlmsg_len < NLMSG_LENGTH(sizeof(*tcm)))
		return skb->len;
424
	if ((dev = dev_get_by_index(&init_net, tcm->tcm_ifindex)) == NULL)
L
Linus Torvalds 已提交
425 426
		return skb->len;

427
	dev_queue = netdev_get_tx_queue(dev, 0);
L
Linus Torvalds 已提交
428
	if (!tcm->tcm_parent)
429
		q = dev_queue->qdisc_sleeping;
L
Linus Torvalds 已提交
430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458
	else
		q = qdisc_lookup(dev, TC_H_MAJ(tcm->tcm_parent));
	if (!q)
		goto out;
	if ((cops = q->ops->cl_ops) == NULL)
		goto errout;
	if (TC_H_MIN(tcm->tcm_parent)) {
		cl = cops->get(q, tcm->tcm_parent);
		if (cl == 0)
			goto errout;
	}
	chain = cops->tcf_chain(q, cl);
	if (chain == NULL)
		goto errout;

	s_t = cb->args[0];

	for (tp=*chain, t=0; tp; tp = tp->next, t++) {
		if (t < s_t) continue;
		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)
			memset(&cb->args[1], 0, sizeof(cb->args)-sizeof(cb->args[0]));
		if (cb->args[1] == 0) {
			if (tcf_fill_node(skb, tp, 0, NETLINK_CB(cb->skb).pid,
459 460
					  cb->nlh->nlmsg_seq, NLM_F_MULTI,
					  RTM_NEWTFILTER) <= 0)
L
Linus Torvalds 已提交
461
				break;
462

L
Linus Torvalds 已提交
463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488
			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;
		arg.w.skip = cb->args[1]-1;
		arg.w.count = 0;
		tp->ops->walk(tp, &arg.w);
		cb->args[1] = arg.w.count+1;
		if (arg.w.stop)
			break;
	}

	cb->args[0] = t;

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

489
void tcf_exts_destroy(struct tcf_proto *tp, struct tcf_exts *exts)
L
Linus Torvalds 已提交
490 491 492 493 494 495 496 497
{
#ifdef CONFIG_NET_CLS_ACT
	if (exts->action) {
		tcf_action_destroy(exts->action, TCA_ACT_UNBIND);
		exts->action = NULL;
	}
#endif
}
498
EXPORT_SYMBOL(tcf_exts_destroy);
L
Linus Torvalds 已提交
499

500 501
int tcf_exts_validate(struct tcf_proto *tp, struct nlattr **tb,
		  struct nlattr *rate_tlv, struct tcf_exts *exts,
502
		  const struct tcf_ext_map *map)
L
Linus Torvalds 已提交
503 504
{
	memset(exts, 0, sizeof(*exts));
505

L
Linus Torvalds 已提交
506 507 508 509
#ifdef CONFIG_NET_CLS_ACT
	{
		struct tc_action *act;

510
		if (map->police && tb[map->police]) {
511
			act = tcf_action_init_1(tb[map->police], rate_tlv,
512
						"police", TCA_ACT_NOREPLACE,
513 514 515
						TCA_ACT_BIND);
			if (IS_ERR(act))
				return PTR_ERR(act);
L
Linus Torvalds 已提交
516 517 518

			act->type = TCA_OLD_COMPAT;
			exts->action = act;
519
		} else if (map->action && tb[map->action]) {
520
			act = tcf_action_init(tb[map->action], rate_tlv, NULL,
521 522 523
					      TCA_ACT_NOREPLACE, TCA_ACT_BIND);
			if (IS_ERR(act))
				return PTR_ERR(act);
L
Linus Torvalds 已提交
524 525 526 527 528

			exts->action = act;
		}
	}
#else
529 530
	if ((map->action && tb[map->action]) ||
	    (map->police && tb[map->police]))
L
Linus Torvalds 已提交
531 532 533 534 535
		return -EOPNOTSUPP;
#endif

	return 0;
}
536
EXPORT_SYMBOL(tcf_exts_validate);
L
Linus Torvalds 已提交
537

538 539
void tcf_exts_change(struct tcf_proto *tp, struct tcf_exts *dst,
		     struct tcf_exts *src)
L
Linus Torvalds 已提交
540 541 542 543 544
{
#ifdef CONFIG_NET_CLS_ACT
	if (src->action) {
		struct tc_action *act;
		tcf_tree_lock(tp);
545 546
		act = dst->action;
		dst->action = src->action;
L
Linus Torvalds 已提交
547 548 549 550 551 552
		tcf_tree_unlock(tp);
		if (act)
			tcf_action_destroy(act, TCA_ACT_UNBIND);
	}
#endif
}
553
EXPORT_SYMBOL(tcf_exts_change);
L
Linus Torvalds 已提交
554

555
int tcf_exts_dump(struct sk_buff *skb, struct tcf_exts *exts,
556
		  const struct tcf_ext_map *map)
L
Linus Torvalds 已提交
557 558 559 560 561 562 563 564
{
#ifdef CONFIG_NET_CLS_ACT
	if (map->action && exts->action) {
		/*
		 * 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
		 */
565
		struct nlattr *nest;
L
Linus Torvalds 已提交
566 567

		if (exts->action->type != TCA_OLD_COMPAT) {
568 569 570
			nest = nla_nest_start(skb, map->action);
			if (nest == NULL)
				goto nla_put_failure;
L
Linus Torvalds 已提交
571
			if (tcf_action_dump(skb, exts->action, 0, 0) < 0)
572
				goto nla_put_failure;
573
			nla_nest_end(skb, nest);
L
Linus Torvalds 已提交
574
		} else if (map->police) {
575 576 577
			nest = nla_nest_start(skb, map->police);
			if (nest == NULL)
				goto nla_put_failure;
L
Linus Torvalds 已提交
578
			if (tcf_action_dump_old(skb, exts->action, 0, 0) < 0)
579
				goto nla_put_failure;
580
			nla_nest_end(skb, nest);
L
Linus Torvalds 已提交
581 582 583 584
		}
	}
#endif
	return 0;
585
nla_put_failure: __attribute__ ((unused))
L
Linus Torvalds 已提交
586 587
	return -1;
}
588
EXPORT_SYMBOL(tcf_exts_dump);
L
Linus Torvalds 已提交
589

590 591

int tcf_exts_dump_stats(struct sk_buff *skb, struct tcf_exts *exts,
592
			const struct tcf_ext_map *map)
L
Linus Torvalds 已提交
593 594 595 596
{
#ifdef CONFIG_NET_CLS_ACT
	if (exts->action)
		if (tcf_action_copy_stats(skb, exts->action, 1) < 0)
597
			goto nla_put_failure;
L
Linus Torvalds 已提交
598 599
#endif
	return 0;
600
nla_put_failure: __attribute__ ((unused))
L
Linus Torvalds 已提交
601 602
	return -1;
}
603
EXPORT_SYMBOL(tcf_exts_dump_stats);
L
Linus Torvalds 已提交
604 605 606

static int __init tc_filter_init(void)
{
607 608 609 610
	rtnl_register(PF_UNSPEC, RTM_NEWTFILTER, tc_ctl_tfilter, NULL);
	rtnl_register(PF_UNSPEC, RTM_DELTFILTER, tc_ctl_tfilter, NULL);
	rtnl_register(PF_UNSPEC, RTM_GETTFILTER, tc_ctl_tfilter,
						 tc_dump_tfilter);
L
Linus Torvalds 已提交
611 612 613 614 615

	return 0;
}

subsys_initcall(tc_filter_init);