144d362409d5469aed47d19e7908d19bd194493aThomas Graf/*
244d362409d5469aed47d19e7908d19bd194493aThomas Graf * lib/route/qdisc.c            Queueing Disciplines
344d362409d5469aed47d19e7908d19bd194493aThomas Graf *
444d362409d5469aed47d19e7908d19bd194493aThomas Graf *	This library is free software; you can redistribute it and/or
544d362409d5469aed47d19e7908d19bd194493aThomas Graf *	modify it under the terms of the GNU Lesser General Public
644d362409d5469aed47d19e7908d19bd194493aThomas Graf *	License as published by the Free Software Foundation version 2.1
744d362409d5469aed47d19e7908d19bd194493aThomas Graf *	of the License.
844d362409d5469aed47d19e7908d19bd194493aThomas Graf *
98a3efffa5b3fde252675239914118664d36a2c24Thomas Graf * Copyright (c) 2003-2008 Thomas Graf <tgraf@suug.ch>
1044d362409d5469aed47d19e7908d19bd194493aThomas Graf */
1144d362409d5469aed47d19e7908d19bd194493aThomas Graf
1244d362409d5469aed47d19e7908d19bd194493aThomas Graf/**
1344d362409d5469aed47d19e7908d19bd194493aThomas Graf * @ingroup tc
1444d362409d5469aed47d19e7908d19bd194493aThomas Graf * @defgroup qdisc Queueing Disciplines
1544d362409d5469aed47d19e7908d19bd194493aThomas Graf *
1644d362409d5469aed47d19e7908d19bd194493aThomas Graf * @par Qdisc Handles
1744d362409d5469aed47d19e7908d19bd194493aThomas Graf * In general, qdiscs are identified by the major part of a traffic control
1844d362409d5469aed47d19e7908d19bd194493aThomas Graf * handle (the upper 16 bits). A few special values exist though:
1944d362409d5469aed47d19e7908d19bd194493aThomas Graf *  - \c TC_H_ROOT: root qdisc (directly attached to the device)
2044d362409d5469aed47d19e7908d19bd194493aThomas Graf *  - \c TC_H_INGRESS: ingress qdisc (directly attached to the device)
2144d362409d5469aed47d19e7908d19bd194493aThomas Graf *  - \c TC_H_UNSPEC: unspecified qdisc (no reference)
2244d362409d5469aed47d19e7908d19bd194493aThomas Graf *
2344d362409d5469aed47d19e7908d19bd194493aThomas Graf * @par 1) Adding a Qdisc
2444d362409d5469aed47d19e7908d19bd194493aThomas Graf * @code
2544d362409d5469aed47d19e7908d19bd194493aThomas Graf * // Allocate a new empty qdisc to be filled out
2644d362409d5469aed47d19e7908d19bd194493aThomas Graf * struct rtnl_qdisc *qdisc = rtnl_qdisc_alloc();
2744d362409d5469aed47d19e7908d19bd194493aThomas Graf *
2844d362409d5469aed47d19e7908d19bd194493aThomas Graf * // ... specify the kind of the Qdisc
2944d362409d5469aed47d19e7908d19bd194493aThomas Graf * rtnl_qdisc_set_kind(qdisc, "pfifo");
3044d362409d5469aed47d19e7908d19bd194493aThomas Graf *
3144d362409d5469aed47d19e7908d19bd194493aThomas Graf * // Specify the device the qdisc should be attached to
3244d362409d5469aed47d19e7908d19bd194493aThomas Graf * rtnl_qdisc_set_ifindex(qdisc, ifindex);
3344d362409d5469aed47d19e7908d19bd194493aThomas Graf *
3444d362409d5469aed47d19e7908d19bd194493aThomas Graf * // ... specify the parent qdisc
3544d362409d5469aed47d19e7908d19bd194493aThomas Graf * rtnl_qdisc_set_parent(qdisc, TC_H_ROOT);
3644d362409d5469aed47d19e7908d19bd194493aThomas Graf *
3744d362409d5469aed47d19e7908d19bd194493aThomas Graf * // Specifying the handle is not required but makes reidentifying easier
3844d362409d5469aed47d19e7908d19bd194493aThomas Graf * // and may help to avoid adding a qdisc twice.
3944d362409d5469aed47d19e7908d19bd194493aThomas Graf * rtnl_qdisc_set_handle(qdisc, 0x000A0000);
4044d362409d5469aed47d19e7908d19bd194493aThomas Graf *
4144d362409d5469aed47d19e7908d19bd194493aThomas Graf * // Now on to specify the qdisc specific options, see the relevant qdisc
4244d362409d5469aed47d19e7908d19bd194493aThomas Graf * // modules for documentation, in this example we set the upper limit of
4344d362409d5469aed47d19e7908d19bd194493aThomas Graf * // the packet fifo qdisc to 64
4444d362409d5469aed47d19e7908d19bd194493aThomas Graf * rtnl_qdisc_fifo_set_limit(qdisc, 64);
4544d362409d5469aed47d19e7908d19bd194493aThomas Graf *
4644d362409d5469aed47d19e7908d19bd194493aThomas Graf * rtnl_qdisc_add(handle, qdisc, NLM_R_REPLACE);
4744d362409d5469aed47d19e7908d19bd194493aThomas Graf *
4844d362409d5469aed47d19e7908d19bd194493aThomas Graf * // Free up the memory
4944d362409d5469aed47d19e7908d19bd194493aThomas Graf * rtnl_qdisc_put(qdisc);
5044d362409d5469aed47d19e7908d19bd194493aThomas Graf * @endcode
5144d362409d5469aed47d19e7908d19bd194493aThomas Graf *
5244d362409d5469aed47d19e7908d19bd194493aThomas Graf * @par 2) Deleting a Qdisc
5344d362409d5469aed47d19e7908d19bd194493aThomas Graf * @code
5444d362409d5469aed47d19e7908d19bd194493aThomas Graf * // Allocate a new empty qdisc to be filled out with the parameters
5544d362409d5469aed47d19e7908d19bd194493aThomas Graf * // specifying the qdisc to be deleted. Alternatively a fully equiped
5644d362409d5469aed47d19e7908d19bd194493aThomas Graf * // Qdisc object from a cache can be used.
5744d362409d5469aed47d19e7908d19bd194493aThomas Graf * struct rtnl_qdisc *qdisc = rtnl_qdisc_alloc();
5844d362409d5469aed47d19e7908d19bd194493aThomas Graf *
5944d362409d5469aed47d19e7908d19bd194493aThomas Graf * // The interface index of the device the qdisc is on and the parent handle
6044d362409d5469aed47d19e7908d19bd194493aThomas Graf * // are the least required fields to be filled out.
6144d362409d5469aed47d19e7908d19bd194493aThomas Graf * // Note: Specify TC_H_ROOT or TC_H_INGRESS as parent handle to delete the
6244d362409d5469aed47d19e7908d19bd194493aThomas Graf * //       root respectively root ingress qdisc.
6344d362409d5469aed47d19e7908d19bd194493aThomas Graf * rtnl_qdisc_set_ifindex(qdisc, ifindex);
6444d362409d5469aed47d19e7908d19bd194493aThomas Graf * rtnl_qdisc_set_parent(qdisc, parent_handle);
6544d362409d5469aed47d19e7908d19bd194493aThomas Graf *
6644d362409d5469aed47d19e7908d19bd194493aThomas Graf * // If required for identification, the handle can be specified as well.
6744d362409d5469aed47d19e7908d19bd194493aThomas Graf * rtnl_qdisc_set_handle(qdisc, qdisc_handle);
6844d362409d5469aed47d19e7908d19bd194493aThomas Graf *
6944d362409d5469aed47d19e7908d19bd194493aThomas Graf * // Not required but maybe helpful as sanity check, the kind of the qdisc
7044d362409d5469aed47d19e7908d19bd194493aThomas Graf * // can be specified to avoid mistakes.
7144d362409d5469aed47d19e7908d19bd194493aThomas Graf * rtnl_qdisc_set_kind(qdisc, "pfifo");
7244d362409d5469aed47d19e7908d19bd194493aThomas Graf *
7344d362409d5469aed47d19e7908d19bd194493aThomas Graf * // Finally delete the qdisc with rtnl_qdisc_delete(), alternatively
7444d362409d5469aed47d19e7908d19bd194493aThomas Graf * // rtnl_qdisc_build_delete_request() can be invoked to generate an
7544d362409d5469aed47d19e7908d19bd194493aThomas Graf * // appropritate netlink message to send out.
7644d362409d5469aed47d19e7908d19bd194493aThomas Graf * rtnl_qdisc_delete(handle, qdisc);
7744d362409d5469aed47d19e7908d19bd194493aThomas Graf *
7844d362409d5469aed47d19e7908d19bd194493aThomas Graf * // Free up the memory
7944d362409d5469aed47d19e7908d19bd194493aThomas Graf * rtnl_qdisc_put(qdisc);
8044d362409d5469aed47d19e7908d19bd194493aThomas Graf * @endcode
8144d362409d5469aed47d19e7908d19bd194493aThomas Graf *
8244d362409d5469aed47d19e7908d19bd194493aThomas Graf * @{
8344d362409d5469aed47d19e7908d19bd194493aThomas Graf */
8444d362409d5469aed47d19e7908d19bd194493aThomas Graf
8544d362409d5469aed47d19e7908d19bd194493aThomas Graf#include <netlink-local.h>
8644d362409d5469aed47d19e7908d19bd194493aThomas Graf#include <netlink-tc.h>
8744d362409d5469aed47d19e7908d19bd194493aThomas Graf#include <netlink/netlink.h>
8844d362409d5469aed47d19e7908d19bd194493aThomas Graf#include <netlink/utils.h>
8944d362409d5469aed47d19e7908d19bd194493aThomas Graf#include <netlink/route/link.h>
9044d362409d5469aed47d19e7908d19bd194493aThomas Graf#include <netlink/route/tc.h>
9144d362409d5469aed47d19e7908d19bd194493aThomas Graf#include <netlink/route/qdisc.h>
9244d362409d5469aed47d19e7908d19bd194493aThomas Graf#include <netlink/route/class.h>
9344d362409d5469aed47d19e7908d19bd194493aThomas Graf#include <netlink/route/classifier.h>
9444d362409d5469aed47d19e7908d19bd194493aThomas Graf#include <netlink/route/qdisc-modules.h>
9544d362409d5469aed47d19e7908d19bd194493aThomas Graf
9644d362409d5469aed47d19e7908d19bd194493aThomas Grafstatic struct nl_cache_ops rtnl_qdisc_ops;
9744d362409d5469aed47d19e7908d19bd194493aThomas Graf
9844d362409d5469aed47d19e7908d19bd194493aThomas Grafstatic int qdisc_msg_parser(struct nl_cache_ops *ops, struct sockaddr_nl *who,
993040a1d6254465bed9e44e4d1bf279c2c50cd16aThomas Graf			    struct nlmsghdr *n, struct nl_parser_param *pp)
10044d362409d5469aed47d19e7908d19bd194493aThomas Graf{
1018a3efffa5b3fde252675239914118664d36a2c24Thomas Graf	int err;
10244d362409d5469aed47d19e7908d19bd194493aThomas Graf	struct rtnl_qdisc *qdisc;
10344d362409d5469aed47d19e7908d19bd194493aThomas Graf	struct rtnl_qdisc_ops *qops;
10444d362409d5469aed47d19e7908d19bd194493aThomas Graf
10544d362409d5469aed47d19e7908d19bd194493aThomas Graf	qdisc = rtnl_qdisc_alloc();
10644d362409d5469aed47d19e7908d19bd194493aThomas Graf	if (!qdisc) {
1078a3efffa5b3fde252675239914118664d36a2c24Thomas Graf		err = -NLE_NOMEM;
10844d362409d5469aed47d19e7908d19bd194493aThomas Graf		goto errout;
10944d362409d5469aed47d19e7908d19bd194493aThomas Graf	}
11044d362409d5469aed47d19e7908d19bd194493aThomas Graf
11144d362409d5469aed47d19e7908d19bd194493aThomas Graf	qdisc->ce_msgtype = n->nlmsg_type;
11244d362409d5469aed47d19e7908d19bd194493aThomas Graf
11344d362409d5469aed47d19e7908d19bd194493aThomas Graf	err = tca_msg_parser(n, (struct rtnl_tca *) qdisc);
11444d362409d5469aed47d19e7908d19bd194493aThomas Graf	if (err < 0)
11544d362409d5469aed47d19e7908d19bd194493aThomas Graf		goto errout_free;
11644d362409d5469aed47d19e7908d19bd194493aThomas Graf
11744d362409d5469aed47d19e7908d19bd194493aThomas Graf	qops = rtnl_qdisc_lookup_ops(qdisc);
11844d362409d5469aed47d19e7908d19bd194493aThomas Graf	if (qops && qops->qo_msg_parser) {
11944d362409d5469aed47d19e7908d19bd194493aThomas Graf		err = qops->qo_msg_parser(qdisc);
12044d362409d5469aed47d19e7908d19bd194493aThomas Graf		if (err < 0)
12144d362409d5469aed47d19e7908d19bd194493aThomas Graf			goto errout_free;
12244d362409d5469aed47d19e7908d19bd194493aThomas Graf	}
12344d362409d5469aed47d19e7908d19bd194493aThomas Graf
12444d362409d5469aed47d19e7908d19bd194493aThomas Graf	err = pp->pp_cb((struct nl_object *) qdisc, pp);
12544d362409d5469aed47d19e7908d19bd194493aThomas Graferrout_free:
12644d362409d5469aed47d19e7908d19bd194493aThomas Graf	rtnl_qdisc_put(qdisc);
12744d362409d5469aed47d19e7908d19bd194493aThomas Graferrout:
12844d362409d5469aed47d19e7908d19bd194493aThomas Graf	return err;
12944d362409d5469aed47d19e7908d19bd194493aThomas Graf}
13044d362409d5469aed47d19e7908d19bd194493aThomas Graf
1311155370f520cb64657e25153255cf7dc1424317fThomas Grafstatic int qdisc_request_update(struct nl_cache *c, struct nl_sock *sk)
13244d362409d5469aed47d19e7908d19bd194493aThomas Graf{
13344d362409d5469aed47d19e7908d19bd194493aThomas Graf	struct tcmsg tchdr = {
13444d362409d5469aed47d19e7908d19bd194493aThomas Graf		.tcm_family = AF_UNSPEC,
13544d362409d5469aed47d19e7908d19bd194493aThomas Graf		.tcm_ifindex = c->c_iarg1,
13644d362409d5469aed47d19e7908d19bd194493aThomas Graf	};
13744d362409d5469aed47d19e7908d19bd194493aThomas Graf
1381155370f520cb64657e25153255cf7dc1424317fThomas Graf	return nl_send_simple(sk, RTM_GETQDISC, NLM_F_DUMP, &tchdr,
13944d362409d5469aed47d19e7908d19bd194493aThomas Graf			      sizeof(tchdr));
14044d362409d5469aed47d19e7908d19bd194493aThomas Graf}
14144d362409d5469aed47d19e7908d19bd194493aThomas Graf
14244d362409d5469aed47d19e7908d19bd194493aThomas Graf/**
14344d362409d5469aed47d19e7908d19bd194493aThomas Graf * @name QDisc Addition
14444d362409d5469aed47d19e7908d19bd194493aThomas Graf * @{
14544d362409d5469aed47d19e7908d19bd194493aThomas Graf */
14644d362409d5469aed47d19e7908d19bd194493aThomas Graf
1478a3efffa5b3fde252675239914118664d36a2c24Thomas Grafstatic int qdisc_build(struct rtnl_qdisc *qdisc, int type, int flags,
1488a3efffa5b3fde252675239914118664d36a2c24Thomas Graf		       struct nl_msg **result)
14944d362409d5469aed47d19e7908d19bd194493aThomas Graf{
15044d362409d5469aed47d19e7908d19bd194493aThomas Graf	struct rtnl_qdisc_ops *qops;
15144d362409d5469aed47d19e7908d19bd194493aThomas Graf	int err;
15244d362409d5469aed47d19e7908d19bd194493aThomas Graf
1538a3efffa5b3fde252675239914118664d36a2c24Thomas Graf	err = tca_build_msg((struct rtnl_tca *) qdisc, type, flags, result);
1548a3efffa5b3fde252675239914118664d36a2c24Thomas Graf	if (err < 0)
1558a3efffa5b3fde252675239914118664d36a2c24Thomas Graf		return err;
15644d362409d5469aed47d19e7908d19bd194493aThomas Graf
15744d362409d5469aed47d19e7908d19bd194493aThomas Graf	qops = rtnl_qdisc_lookup_ops(qdisc);
15844d362409d5469aed47d19e7908d19bd194493aThomas Graf	if (qops && qops->qo_get_opts) {
15944d362409d5469aed47d19e7908d19bd194493aThomas Graf		struct nl_msg *opts;
16044d362409d5469aed47d19e7908d19bd194493aThomas Graf
16144d362409d5469aed47d19e7908d19bd194493aThomas Graf		opts = qops->qo_get_opts(qdisc);
16244d362409d5469aed47d19e7908d19bd194493aThomas Graf		if (opts) {
1638a3efffa5b3fde252675239914118664d36a2c24Thomas Graf			err = nla_put_nested(*result, TCA_OPTIONS, opts);
16444d362409d5469aed47d19e7908d19bd194493aThomas Graf			nlmsg_free(opts);
16544d362409d5469aed47d19e7908d19bd194493aThomas Graf			if (err < 0)
16644d362409d5469aed47d19e7908d19bd194493aThomas Graf				goto errout;
16744d362409d5469aed47d19e7908d19bd194493aThomas Graf		}
16844d362409d5469aed47d19e7908d19bd194493aThomas Graf	}
169241b2b83ba5672f5c86154d29eeb8ef4c7c6e9b4Tad Kollar	/* Some qdiscs don't accept properly nested messages (e.g. netem). To
170241b2b83ba5672f5c86154d29eeb8ef4c7c6e9b4Tad Kollar	 * accomodate for this, they can complete the message themselves.
171241b2b83ba5672f5c86154d29eeb8ef4c7c6e9b4Tad Kollar	 */
172241b2b83ba5672f5c86154d29eeb8ef4c7c6e9b4Tad Kollar	else if (qops && qops->qo_build_msg) {
1738a3efffa5b3fde252675239914118664d36a2c24Thomas Graf		err = qops->qo_build_msg(qdisc, *result);
1748a3efffa5b3fde252675239914118664d36a2c24Thomas Graf		if (err < 0)
175241b2b83ba5672f5c86154d29eeb8ef4c7c6e9b4Tad Kollar			goto errout;
176241b2b83ba5672f5c86154d29eeb8ef4c7c6e9b4Tad Kollar	}
17744d362409d5469aed47d19e7908d19bd194493aThomas Graf
1788a3efffa5b3fde252675239914118664d36a2c24Thomas Graf	return 0;
17944d362409d5469aed47d19e7908d19bd194493aThomas Graferrout:
1808a3efffa5b3fde252675239914118664d36a2c24Thomas Graf	nlmsg_free(*result);
18144d362409d5469aed47d19e7908d19bd194493aThomas Graf
1828a3efffa5b3fde252675239914118664d36a2c24Thomas Graf	return err;
18344d362409d5469aed47d19e7908d19bd194493aThomas Graf}
18444d362409d5469aed47d19e7908d19bd194493aThomas Graf
18544d362409d5469aed47d19e7908d19bd194493aThomas Graf/**
18644d362409d5469aed47d19e7908d19bd194493aThomas Graf * Build a netlink message to add a new qdisc
18744d362409d5469aed47d19e7908d19bd194493aThomas Graf * @arg qdisc		qdisc to add
18844d362409d5469aed47d19e7908d19bd194493aThomas Graf * @arg flags		additional netlink message flags
1898a3efffa5b3fde252675239914118664d36a2c24Thomas Graf * @arg result		Pointer to store resulting message.
19044d362409d5469aed47d19e7908d19bd194493aThomas Graf *
19144d362409d5469aed47d19e7908d19bd194493aThomas Graf * Builds a new netlink message requesting an addition of a qdisc.
19244d362409d5469aed47d19e7908d19bd194493aThomas Graf * The netlink message header isn't fully equipped with all relevant
19344d362409d5469aed47d19e7908d19bd194493aThomas Graf * fields and must be sent out via nl_send_auto_complete() or
19444d362409d5469aed47d19e7908d19bd194493aThomas Graf * supplemented as needed.
19544d362409d5469aed47d19e7908d19bd194493aThomas Graf *
19644d362409d5469aed47d19e7908d19bd194493aThomas Graf * Common message flags used:
19744d362409d5469aed47d19e7908d19bd194493aThomas Graf *  - NLM_F_REPLACE - replace a potential existing qdisc
19844d362409d5469aed47d19e7908d19bd194493aThomas Graf *
1998a3efffa5b3fde252675239914118664d36a2c24Thomas Graf * @return 0 on success or a negative error code.
20044d362409d5469aed47d19e7908d19bd194493aThomas Graf */
2018a3efffa5b3fde252675239914118664d36a2c24Thomas Grafint rtnl_qdisc_build_add_request(struct rtnl_qdisc *qdisc, int flags,
2028a3efffa5b3fde252675239914118664d36a2c24Thomas Graf				 struct nl_msg **result)
20344d362409d5469aed47d19e7908d19bd194493aThomas Graf{
2048a3efffa5b3fde252675239914118664d36a2c24Thomas Graf	return qdisc_build(qdisc, RTM_NEWQDISC, NLM_F_CREATE | flags, result);
20544d362409d5469aed47d19e7908d19bd194493aThomas Graf}
20644d362409d5469aed47d19e7908d19bd194493aThomas Graf
20744d362409d5469aed47d19e7908d19bd194493aThomas Graf/**
20844d362409d5469aed47d19e7908d19bd194493aThomas Graf * Add a new qdisc
2091155370f520cb64657e25153255cf7dc1424317fThomas Graf * @arg sk		Netlink socket.
21044d362409d5469aed47d19e7908d19bd194493aThomas Graf * @arg qdisc		qdisc to delete
21144d362409d5469aed47d19e7908d19bd194493aThomas Graf * @arg flags		additional netlink message flags
21244d362409d5469aed47d19e7908d19bd194493aThomas Graf *
21344d362409d5469aed47d19e7908d19bd194493aThomas Graf * Builds a netlink message by calling rtnl_qdisc_build_add_request(),
21444d362409d5469aed47d19e7908d19bd194493aThomas Graf * sends the request to the kernel and waits for the ACK to be
21544d362409d5469aed47d19e7908d19bd194493aThomas Graf * received and thus blocks until the request has been processed.
21644d362409d5469aed47d19e7908d19bd194493aThomas Graf *
21744d362409d5469aed47d19e7908d19bd194493aThomas Graf * Common message flags used:
21844d362409d5469aed47d19e7908d19bd194493aThomas Graf *  - NLM_F_REPLACE - replace a potential existing qdisc
21944d362409d5469aed47d19e7908d19bd194493aThomas Graf *
22044d362409d5469aed47d19e7908d19bd194493aThomas Graf * @return 0 on success or a negative error code
22144d362409d5469aed47d19e7908d19bd194493aThomas Graf */
2221155370f520cb64657e25153255cf7dc1424317fThomas Grafint rtnl_qdisc_add(struct nl_sock *sk, struct rtnl_qdisc *qdisc,
22344d362409d5469aed47d19e7908d19bd194493aThomas Graf		   int flags)
22444d362409d5469aed47d19e7908d19bd194493aThomas Graf{
22544d362409d5469aed47d19e7908d19bd194493aThomas Graf	struct nl_msg *msg;
22644d362409d5469aed47d19e7908d19bd194493aThomas Graf	int err;
22744d362409d5469aed47d19e7908d19bd194493aThomas Graf
2288a3efffa5b3fde252675239914118664d36a2c24Thomas Graf	if ((err = rtnl_qdisc_build_add_request(qdisc, flags, &msg)) < 0)
2298a3efffa5b3fde252675239914118664d36a2c24Thomas Graf		return err;
23044d362409d5469aed47d19e7908d19bd194493aThomas Graf
231ef50a38fbd8682a5c9efd559e7db68664977f080Thomas Graf	err = nl_send_auto_complete(sk, msg);
232ef50a38fbd8682a5c9efd559e7db68664977f080Thomas Graf	nlmsg_free(msg);
233ef50a38fbd8682a5c9efd559e7db68664977f080Thomas Graf	if (err < 0)
23444d362409d5469aed47d19e7908d19bd194493aThomas Graf		return err;
23544d362409d5469aed47d19e7908d19bd194493aThomas Graf
236cfcfca070355b246028df60da79813f09ed65755Thomas Graf	return wait_for_ack(sk);
23744d362409d5469aed47d19e7908d19bd194493aThomas Graf}
23844d362409d5469aed47d19e7908d19bd194493aThomas Graf
23944d362409d5469aed47d19e7908d19bd194493aThomas Graf/** @} */
24044d362409d5469aed47d19e7908d19bd194493aThomas Graf
24144d362409d5469aed47d19e7908d19bd194493aThomas Graf/**
24244d362409d5469aed47d19e7908d19bd194493aThomas Graf * @name QDisc Modification
24344d362409d5469aed47d19e7908d19bd194493aThomas Graf * @{
24444d362409d5469aed47d19e7908d19bd194493aThomas Graf */
24544d362409d5469aed47d19e7908d19bd194493aThomas Graf
24644d362409d5469aed47d19e7908d19bd194493aThomas Graf/**
24744d362409d5469aed47d19e7908d19bd194493aThomas Graf * Build a netlink message to change attributes of a existing qdisc
24844d362409d5469aed47d19e7908d19bd194493aThomas Graf * @arg qdisc		qdisc to change
24944d362409d5469aed47d19e7908d19bd194493aThomas Graf * @arg new		new qdisc attributes
2508a3efffa5b3fde252675239914118664d36a2c24Thomas Graf * @arg result		Pointer to store resulting message.
25144d362409d5469aed47d19e7908d19bd194493aThomas Graf *
25244d362409d5469aed47d19e7908d19bd194493aThomas Graf * Builds a new netlink message requesting an change of qdisc
25344d362409d5469aed47d19e7908d19bd194493aThomas Graf * attributes. The netlink message header isn't fully equipped
25444d362409d5469aed47d19e7908d19bd194493aThomas Graf * with all relevant fields and must be sent out via
25544d362409d5469aed47d19e7908d19bd194493aThomas Graf * nl_send_auto_complete() or supplemented as needed.
25644d362409d5469aed47d19e7908d19bd194493aThomas Graf *
2578a3efffa5b3fde252675239914118664d36a2c24Thomas Graf * @return 0 on success or a negative error code.
25844d362409d5469aed47d19e7908d19bd194493aThomas Graf */
2598a3efffa5b3fde252675239914118664d36a2c24Thomas Grafint rtnl_qdisc_build_change_request(struct rtnl_qdisc *qdisc,
2608a3efffa5b3fde252675239914118664d36a2c24Thomas Graf				    struct rtnl_qdisc *new,
2618a3efffa5b3fde252675239914118664d36a2c24Thomas Graf				    struct nl_msg **result)
26244d362409d5469aed47d19e7908d19bd194493aThomas Graf{
2638a3efffa5b3fde252675239914118664d36a2c24Thomas Graf	return qdisc_build(qdisc, RTM_NEWQDISC, NLM_F_REPLACE, result);
26444d362409d5469aed47d19e7908d19bd194493aThomas Graf}
26544d362409d5469aed47d19e7908d19bd194493aThomas Graf
26644d362409d5469aed47d19e7908d19bd194493aThomas Graf/**
26744d362409d5469aed47d19e7908d19bd194493aThomas Graf * Change attributes of a qdisc
2681155370f520cb64657e25153255cf7dc1424317fThomas Graf * @arg sk		Netlink socket.
26944d362409d5469aed47d19e7908d19bd194493aThomas Graf * @arg qdisc		qdisc to change
27044d362409d5469aed47d19e7908d19bd194493aThomas Graf * @arg new		new qdisc attributes
27144d362409d5469aed47d19e7908d19bd194493aThomas Graf *
27244d362409d5469aed47d19e7908d19bd194493aThomas Graf * Builds a netlink message by calling rtnl_qdisc_build_change_request(),
27344d362409d5469aed47d19e7908d19bd194493aThomas Graf * sends the request to the kernel and waits for the ACK to be
27444d362409d5469aed47d19e7908d19bd194493aThomas Graf * received and thus blocks until the request has been processed.
27544d362409d5469aed47d19e7908d19bd194493aThomas Graf *
27644d362409d5469aed47d19e7908d19bd194493aThomas Graf * @return 0 on success or a negative error code
27744d362409d5469aed47d19e7908d19bd194493aThomas Graf */
2781155370f520cb64657e25153255cf7dc1424317fThomas Grafint rtnl_qdisc_change(struct nl_sock *sk, struct rtnl_qdisc *qdisc,
27944d362409d5469aed47d19e7908d19bd194493aThomas Graf		      struct rtnl_qdisc *new)
28044d362409d5469aed47d19e7908d19bd194493aThomas Graf{
28144d362409d5469aed47d19e7908d19bd194493aThomas Graf	struct nl_msg *msg;
28244d362409d5469aed47d19e7908d19bd194493aThomas Graf	int err;
28344d362409d5469aed47d19e7908d19bd194493aThomas Graf
2848a3efffa5b3fde252675239914118664d36a2c24Thomas Graf	if ((err = rtnl_qdisc_build_change_request(qdisc, new, &msg)) < 0)
2858a3efffa5b3fde252675239914118664d36a2c24Thomas Graf		return err;
28644d362409d5469aed47d19e7908d19bd194493aThomas Graf
287ef50a38fbd8682a5c9efd559e7db68664977f080Thomas Graf	err = nl_send_auto_complete(sk, msg);
288ef50a38fbd8682a5c9efd559e7db68664977f080Thomas Graf	nlmsg_free(msg);
289ef50a38fbd8682a5c9efd559e7db68664977f080Thomas Graf	if (err < 0)
29044d362409d5469aed47d19e7908d19bd194493aThomas Graf		return err;
29144d362409d5469aed47d19e7908d19bd194493aThomas Graf
292cfcfca070355b246028df60da79813f09ed65755Thomas Graf	return wait_for_ack(sk);
29344d362409d5469aed47d19e7908d19bd194493aThomas Graf}
29444d362409d5469aed47d19e7908d19bd194493aThomas Graf
29544d362409d5469aed47d19e7908d19bd194493aThomas Graf/** @} */
29644d362409d5469aed47d19e7908d19bd194493aThomas Graf
29744d362409d5469aed47d19e7908d19bd194493aThomas Graf/**
29844d362409d5469aed47d19e7908d19bd194493aThomas Graf * @name QDisc Deletion
29944d362409d5469aed47d19e7908d19bd194493aThomas Graf * @{
30044d362409d5469aed47d19e7908d19bd194493aThomas Graf */
30144d362409d5469aed47d19e7908d19bd194493aThomas Graf
30244d362409d5469aed47d19e7908d19bd194493aThomas Graf/**
30344d362409d5469aed47d19e7908d19bd194493aThomas Graf * Build a netlink request message to delete a qdisc
30444d362409d5469aed47d19e7908d19bd194493aThomas Graf * @arg qdisc		qdisc to delete
3058a3efffa5b3fde252675239914118664d36a2c24Thomas Graf * @arg result		Pointer to store resulting message.
30644d362409d5469aed47d19e7908d19bd194493aThomas Graf *
30744d362409d5469aed47d19e7908d19bd194493aThomas Graf * Builds a new netlink message requesting a deletion of a qdisc.
30844d362409d5469aed47d19e7908d19bd194493aThomas Graf * The netlink message header isn't fully equipped with all relevant
30944d362409d5469aed47d19e7908d19bd194493aThomas Graf * fields and must thus be sent out via nl_send_auto_complete()
31044d362409d5469aed47d19e7908d19bd194493aThomas Graf * or supplemented as needed.
31144d362409d5469aed47d19e7908d19bd194493aThomas Graf *
3128a3efffa5b3fde252675239914118664d36a2c24Thomas Graf * @return 0 on success or a negative error code.
31344d362409d5469aed47d19e7908d19bd194493aThomas Graf */
3148a3efffa5b3fde252675239914118664d36a2c24Thomas Grafint rtnl_qdisc_build_delete_request(struct rtnl_qdisc *qdisc,
3158a3efffa5b3fde252675239914118664d36a2c24Thomas Graf				    struct nl_msg **result)
31644d362409d5469aed47d19e7908d19bd194493aThomas Graf{
31744d362409d5469aed47d19e7908d19bd194493aThomas Graf	struct nl_msg *msg;
31844d362409d5469aed47d19e7908d19bd194493aThomas Graf	struct tcmsg tchdr;
31944d362409d5469aed47d19e7908d19bd194493aThomas Graf	int required = TCA_ATTR_IFINDEX | TCA_ATTR_PARENT;
32044d362409d5469aed47d19e7908d19bd194493aThomas Graf
32144d362409d5469aed47d19e7908d19bd194493aThomas Graf	if ((qdisc->ce_mask & required) != required)
32244d362409d5469aed47d19e7908d19bd194493aThomas Graf		BUG();
32344d362409d5469aed47d19e7908d19bd194493aThomas Graf
32444d362409d5469aed47d19e7908d19bd194493aThomas Graf	msg = nlmsg_alloc_simple(RTM_DELQDISC, 0);
32544d362409d5469aed47d19e7908d19bd194493aThomas Graf	if (!msg)
3268a3efffa5b3fde252675239914118664d36a2c24Thomas Graf		return -NLE_NOMEM;
3278a3efffa5b3fde252675239914118664d36a2c24Thomas Graf
3288a3efffa5b3fde252675239914118664d36a2c24Thomas Graf	tchdr.tcm_family = AF_UNSPEC;
3298a3efffa5b3fde252675239914118664d36a2c24Thomas Graf	tchdr.tcm_handle = qdisc->q_handle;
3308a3efffa5b3fde252675239914118664d36a2c24Thomas Graf	tchdr.tcm_parent = qdisc->q_parent;
3318a3efffa5b3fde252675239914118664d36a2c24Thomas Graf	tchdr.tcm_ifindex = qdisc->q_ifindex;
3328a3efffa5b3fde252675239914118664d36a2c24Thomas Graf	if (nlmsg_append(msg, &tchdr, sizeof(tchdr), NLMSG_ALIGNTO) < 0) {
3338a3efffa5b3fde252675239914118664d36a2c24Thomas Graf		nlmsg_free(msg);
3348a3efffa5b3fde252675239914118664d36a2c24Thomas Graf		return -NLE_MSGSIZE;
3358a3efffa5b3fde252675239914118664d36a2c24Thomas Graf	}
33644d362409d5469aed47d19e7908d19bd194493aThomas Graf
3378a3efffa5b3fde252675239914118664d36a2c24Thomas Graf	*result = msg;
3388a3efffa5b3fde252675239914118664d36a2c24Thomas Graf	return 0;
33944d362409d5469aed47d19e7908d19bd194493aThomas Graf}
34044d362409d5469aed47d19e7908d19bd194493aThomas Graf
34144d362409d5469aed47d19e7908d19bd194493aThomas Graf/**
34244d362409d5469aed47d19e7908d19bd194493aThomas Graf * Delete a qdisc
3431155370f520cb64657e25153255cf7dc1424317fThomas Graf * @arg sk		Netlink socket.
34444d362409d5469aed47d19e7908d19bd194493aThomas Graf * @arg qdisc		qdisc to delete
34544d362409d5469aed47d19e7908d19bd194493aThomas Graf *
34644d362409d5469aed47d19e7908d19bd194493aThomas Graf * Builds a netlink message by calling rtnl_qdisc_build_delete_request(),
34744d362409d5469aed47d19e7908d19bd194493aThomas Graf * sends the request to the kernel and waits for the ACK to be
34844d362409d5469aed47d19e7908d19bd194493aThomas Graf * received and thus blocks until the request has been processed.
34944d362409d5469aed47d19e7908d19bd194493aThomas Graf *
35044d362409d5469aed47d19e7908d19bd194493aThomas Graf * @return 0 on success or a negative error code
35144d362409d5469aed47d19e7908d19bd194493aThomas Graf */
3521155370f520cb64657e25153255cf7dc1424317fThomas Grafint rtnl_qdisc_delete(struct nl_sock *sk, struct rtnl_qdisc *qdisc)
35344d362409d5469aed47d19e7908d19bd194493aThomas Graf{
35444d362409d5469aed47d19e7908d19bd194493aThomas Graf	struct nl_msg *msg;
35544d362409d5469aed47d19e7908d19bd194493aThomas Graf	int err;
35644d362409d5469aed47d19e7908d19bd194493aThomas Graf
3578a3efffa5b3fde252675239914118664d36a2c24Thomas Graf	if ((err = rtnl_qdisc_build_delete_request(qdisc, &msg)) < 0)
3588a3efffa5b3fde252675239914118664d36a2c24Thomas Graf		return err;
35944d362409d5469aed47d19e7908d19bd194493aThomas Graf
360ef50a38fbd8682a5c9efd559e7db68664977f080Thomas Graf	err = nl_send_auto_complete(sk, msg);
361ef50a38fbd8682a5c9efd559e7db68664977f080Thomas Graf	nlmsg_free(msg);
362ef50a38fbd8682a5c9efd559e7db68664977f080Thomas Graf	if (err < 0)
36344d362409d5469aed47d19e7908d19bd194493aThomas Graf		return err;
36444d362409d5469aed47d19e7908d19bd194493aThomas Graf
365cfcfca070355b246028df60da79813f09ed65755Thomas Graf	return wait_for_ack(sk);
36644d362409d5469aed47d19e7908d19bd194493aThomas Graf}
36744d362409d5469aed47d19e7908d19bd194493aThomas Graf
36844d362409d5469aed47d19e7908d19bd194493aThomas Graf/** @} */
36944d362409d5469aed47d19e7908d19bd194493aThomas Graf
37044d362409d5469aed47d19e7908d19bd194493aThomas Graf/**
37144d362409d5469aed47d19e7908d19bd194493aThomas Graf * @name Qdisc Cache Management
37244d362409d5469aed47d19e7908d19bd194493aThomas Graf * @{
37344d362409d5469aed47d19e7908d19bd194493aThomas Graf */
37444d362409d5469aed47d19e7908d19bd194493aThomas Graf
37544d362409d5469aed47d19e7908d19bd194493aThomas Graf/**
37644d362409d5469aed47d19e7908d19bd194493aThomas Graf * Build a qdisc cache including all qdiscs currently configured in
37744d362409d5469aed47d19e7908d19bd194493aThomas Graf * the kernel
3781155370f520cb64657e25153255cf7dc1424317fThomas Graf * @arg sk		Netlink socket.
3798a3efffa5b3fde252675239914118664d36a2c24Thomas Graf * @arg result		Pointer to store resulting message.
38044d362409d5469aed47d19e7908d19bd194493aThomas Graf *
38144d362409d5469aed47d19e7908d19bd194493aThomas Graf * Allocates a new cache, initializes it properly and updates it to
38244d362409d5469aed47d19e7908d19bd194493aThomas Graf * include all qdiscs currently configured in the kernel.
38344d362409d5469aed47d19e7908d19bd194493aThomas Graf *
3848a3efffa5b3fde252675239914118664d36a2c24Thomas Graf * @return 0 on success or a negative error code.
38544d362409d5469aed47d19e7908d19bd194493aThomas Graf */
3861155370f520cb64657e25153255cf7dc1424317fThomas Grafint rtnl_qdisc_alloc_cache(struct nl_sock *sk, struct nl_cache **result)
38744d362409d5469aed47d19e7908d19bd194493aThomas Graf{
3881155370f520cb64657e25153255cf7dc1424317fThomas Graf	return nl_cache_alloc_and_fill(&rtnl_qdisc_ops, sk, result);
38944d362409d5469aed47d19e7908d19bd194493aThomas Graf}
39044d362409d5469aed47d19e7908d19bd194493aThomas Graf
39144d362409d5469aed47d19e7908d19bd194493aThomas Graf/**
39244d362409d5469aed47d19e7908d19bd194493aThomas Graf * Look up qdisc by its parent in the provided cache
39344d362409d5469aed47d19e7908d19bd194493aThomas Graf * @arg cache		qdisc cache
39444d362409d5469aed47d19e7908d19bd194493aThomas Graf * @arg ifindex		interface the qdisc is attached to
39544d362409d5469aed47d19e7908d19bd194493aThomas Graf * @arg parent		parent handle
39644d362409d5469aed47d19e7908d19bd194493aThomas Graf * @return pointer to qdisc inside the cache or NULL if no match was found.
39744d362409d5469aed47d19e7908d19bd194493aThomas Graf */
39844d362409d5469aed47d19e7908d19bd194493aThomas Grafstruct rtnl_qdisc * rtnl_qdisc_get_by_parent(struct nl_cache *cache,
39944d362409d5469aed47d19e7908d19bd194493aThomas Graf					     int ifindex, uint32_t parent)
40044d362409d5469aed47d19e7908d19bd194493aThomas Graf{
40144d362409d5469aed47d19e7908d19bd194493aThomas Graf	struct rtnl_qdisc *q;
40244d362409d5469aed47d19e7908d19bd194493aThomas Graf
40344d362409d5469aed47d19e7908d19bd194493aThomas Graf	if (cache->c_ops != &rtnl_qdisc_ops)
40444d362409d5469aed47d19e7908d19bd194493aThomas Graf		return NULL;
40544d362409d5469aed47d19e7908d19bd194493aThomas Graf
40644d362409d5469aed47d19e7908d19bd194493aThomas Graf	nl_list_for_each_entry(q, &cache->c_items, ce_list) {
40744d362409d5469aed47d19e7908d19bd194493aThomas Graf		if (q->q_parent == parent && q->q_ifindex == ifindex) {
40844d362409d5469aed47d19e7908d19bd194493aThomas Graf			nl_object_get((struct nl_object *) q);
40944d362409d5469aed47d19e7908d19bd194493aThomas Graf			return q;
41044d362409d5469aed47d19e7908d19bd194493aThomas Graf		}
41144d362409d5469aed47d19e7908d19bd194493aThomas Graf	}
41244d362409d5469aed47d19e7908d19bd194493aThomas Graf
41344d362409d5469aed47d19e7908d19bd194493aThomas Graf	return NULL;
41444d362409d5469aed47d19e7908d19bd194493aThomas Graf}
41544d362409d5469aed47d19e7908d19bd194493aThomas Graf
41644d362409d5469aed47d19e7908d19bd194493aThomas Graf/**
41744d362409d5469aed47d19e7908d19bd194493aThomas Graf * Look up qdisc by its handle in the provided cache
41844d362409d5469aed47d19e7908d19bd194493aThomas Graf * @arg cache		qdisc cache
41944d362409d5469aed47d19e7908d19bd194493aThomas Graf * @arg ifindex		interface the qdisc is attached to
42044d362409d5469aed47d19e7908d19bd194493aThomas Graf * @arg handle		qdisc handle
42144d362409d5469aed47d19e7908d19bd194493aThomas Graf * @return pointer to qdisc inside the cache or NULL if no match was found.
42244d362409d5469aed47d19e7908d19bd194493aThomas Graf */
42344d362409d5469aed47d19e7908d19bd194493aThomas Grafstruct rtnl_qdisc * rtnl_qdisc_get(struct nl_cache *cache,
42444d362409d5469aed47d19e7908d19bd194493aThomas Graf				   int ifindex, uint32_t handle)
42544d362409d5469aed47d19e7908d19bd194493aThomas Graf{
42644d362409d5469aed47d19e7908d19bd194493aThomas Graf	struct rtnl_qdisc *q;
42744d362409d5469aed47d19e7908d19bd194493aThomas Graf
42844d362409d5469aed47d19e7908d19bd194493aThomas Graf	if (cache->c_ops != &rtnl_qdisc_ops)
42944d362409d5469aed47d19e7908d19bd194493aThomas Graf		return NULL;
43044d362409d5469aed47d19e7908d19bd194493aThomas Graf
43144d362409d5469aed47d19e7908d19bd194493aThomas Graf	nl_list_for_each_entry(q, &cache->c_items, ce_list) {
43244d362409d5469aed47d19e7908d19bd194493aThomas Graf		if (q->q_handle == handle && q->q_ifindex == ifindex) {
43344d362409d5469aed47d19e7908d19bd194493aThomas Graf			nl_object_get((struct nl_object *) q);
43444d362409d5469aed47d19e7908d19bd194493aThomas Graf			return q;
43544d362409d5469aed47d19e7908d19bd194493aThomas Graf		}
43644d362409d5469aed47d19e7908d19bd194493aThomas Graf	}
43744d362409d5469aed47d19e7908d19bd194493aThomas Graf
43844d362409d5469aed47d19e7908d19bd194493aThomas Graf	return NULL;
43944d362409d5469aed47d19e7908d19bd194493aThomas Graf}
44044d362409d5469aed47d19e7908d19bd194493aThomas Graf
44144d362409d5469aed47d19e7908d19bd194493aThomas Graf/** @} */
44244d362409d5469aed47d19e7908d19bd194493aThomas Graf
44344d362409d5469aed47d19e7908d19bd194493aThomas Grafstatic struct nl_cache_ops rtnl_qdisc_ops = {
44444d362409d5469aed47d19e7908d19bd194493aThomas Graf	.co_name		= "route/qdisc",
44544d362409d5469aed47d19e7908d19bd194493aThomas Graf	.co_hdrsize		= sizeof(struct tcmsg),
44644d362409d5469aed47d19e7908d19bd194493aThomas Graf	.co_msgtypes		= {
44744d362409d5469aed47d19e7908d19bd194493aThomas Graf					{ RTM_NEWQDISC, NL_ACT_NEW, "new" },
44844d362409d5469aed47d19e7908d19bd194493aThomas Graf					{ RTM_DELQDISC, NL_ACT_DEL, "del" },
44944d362409d5469aed47d19e7908d19bd194493aThomas Graf					{ RTM_GETQDISC, NL_ACT_GET, "get" },
45044d362409d5469aed47d19e7908d19bd194493aThomas Graf					END_OF_MSGTYPES_LIST,
45144d362409d5469aed47d19e7908d19bd194493aThomas Graf				  },
45244d362409d5469aed47d19e7908d19bd194493aThomas Graf	.co_protocol		= NETLINK_ROUTE,
45344d362409d5469aed47d19e7908d19bd194493aThomas Graf	.co_request_update	= qdisc_request_update,
45444d362409d5469aed47d19e7908d19bd194493aThomas Graf	.co_msg_parser		= qdisc_msg_parser,
45544d362409d5469aed47d19e7908d19bd194493aThomas Graf	.co_obj_ops		= &qdisc_obj_ops,
45644d362409d5469aed47d19e7908d19bd194493aThomas Graf};
45744d362409d5469aed47d19e7908d19bd194493aThomas Graf
45844d362409d5469aed47d19e7908d19bd194493aThomas Grafstatic void __init qdisc_init(void)
45944d362409d5469aed47d19e7908d19bd194493aThomas Graf{
46044d362409d5469aed47d19e7908d19bd194493aThomas Graf	nl_cache_mngt_register(&rtnl_qdisc_ops);
46144d362409d5469aed47d19e7908d19bd194493aThomas Graf}
46244d362409d5469aed47d19e7908d19bd194493aThomas Graf
46344d362409d5469aed47d19e7908d19bd194493aThomas Grafstatic void __exit qdisc_exit(void)
46444d362409d5469aed47d19e7908d19bd194493aThomas Graf{
46544d362409d5469aed47d19e7908d19bd194493aThomas Graf	nl_cache_mngt_unregister(&rtnl_qdisc_ops);
46644d362409d5469aed47d19e7908d19bd194493aThomas Graf}
46744d362409d5469aed47d19e7908d19bd194493aThomas Graf
46844d362409d5469aed47d19e7908d19bd194493aThomas Graf/** @} */
469