qdisc.c revision ef50a38fbd8682a5c9efd559e7db68664977f080
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 Graf	if (err < 0)
12644d362409d5469aed47d19e7908d19bd194493aThomas Graf		goto errout_free;
12744d362409d5469aed47d19e7908d19bd194493aThomas Graf
128155ad439a49df034ec58ee4218834bc5b0120515Thomas Graf	err = P_ACCEPT;
12944d362409d5469aed47d19e7908d19bd194493aThomas Graf
13044d362409d5469aed47d19e7908d19bd194493aThomas Graferrout_free:
13144d362409d5469aed47d19e7908d19bd194493aThomas Graf	rtnl_qdisc_put(qdisc);
13244d362409d5469aed47d19e7908d19bd194493aThomas Graferrout:
13344d362409d5469aed47d19e7908d19bd194493aThomas Graf	return err;
13444d362409d5469aed47d19e7908d19bd194493aThomas Graf}
13544d362409d5469aed47d19e7908d19bd194493aThomas Graf
1361155370f520cb64657e25153255cf7dc1424317fThomas Grafstatic int qdisc_request_update(struct nl_cache *c, struct nl_sock *sk)
13744d362409d5469aed47d19e7908d19bd194493aThomas Graf{
13844d362409d5469aed47d19e7908d19bd194493aThomas Graf	struct tcmsg tchdr = {
13944d362409d5469aed47d19e7908d19bd194493aThomas Graf		.tcm_family = AF_UNSPEC,
14044d362409d5469aed47d19e7908d19bd194493aThomas Graf		.tcm_ifindex = c->c_iarg1,
14144d362409d5469aed47d19e7908d19bd194493aThomas Graf	};
14244d362409d5469aed47d19e7908d19bd194493aThomas Graf
1431155370f520cb64657e25153255cf7dc1424317fThomas Graf	return nl_send_simple(sk, RTM_GETQDISC, NLM_F_DUMP, &tchdr,
14444d362409d5469aed47d19e7908d19bd194493aThomas Graf			      sizeof(tchdr));
14544d362409d5469aed47d19e7908d19bd194493aThomas Graf}
14644d362409d5469aed47d19e7908d19bd194493aThomas Graf
14744d362409d5469aed47d19e7908d19bd194493aThomas Graf/**
14844d362409d5469aed47d19e7908d19bd194493aThomas Graf * @name QDisc Addition
14944d362409d5469aed47d19e7908d19bd194493aThomas Graf * @{
15044d362409d5469aed47d19e7908d19bd194493aThomas Graf */
15144d362409d5469aed47d19e7908d19bd194493aThomas Graf
1528a3efffa5b3fde252675239914118664d36a2c24Thomas Grafstatic int qdisc_build(struct rtnl_qdisc *qdisc, int type, int flags,
1538a3efffa5b3fde252675239914118664d36a2c24Thomas Graf		       struct nl_msg **result)
15444d362409d5469aed47d19e7908d19bd194493aThomas Graf{
15544d362409d5469aed47d19e7908d19bd194493aThomas Graf	struct rtnl_qdisc_ops *qops;
15644d362409d5469aed47d19e7908d19bd194493aThomas Graf	int err;
15744d362409d5469aed47d19e7908d19bd194493aThomas Graf
1588a3efffa5b3fde252675239914118664d36a2c24Thomas Graf	err = tca_build_msg((struct rtnl_tca *) qdisc, type, flags, result);
1598a3efffa5b3fde252675239914118664d36a2c24Thomas Graf	if (err < 0)
1608a3efffa5b3fde252675239914118664d36a2c24Thomas Graf		return err;
16144d362409d5469aed47d19e7908d19bd194493aThomas Graf
16244d362409d5469aed47d19e7908d19bd194493aThomas Graf	qops = rtnl_qdisc_lookup_ops(qdisc);
16344d362409d5469aed47d19e7908d19bd194493aThomas Graf	if (qops && qops->qo_get_opts) {
16444d362409d5469aed47d19e7908d19bd194493aThomas Graf		struct nl_msg *opts;
16544d362409d5469aed47d19e7908d19bd194493aThomas Graf
16644d362409d5469aed47d19e7908d19bd194493aThomas Graf		opts = qops->qo_get_opts(qdisc);
16744d362409d5469aed47d19e7908d19bd194493aThomas Graf		if (opts) {
1688a3efffa5b3fde252675239914118664d36a2c24Thomas Graf			err = nla_put_nested(*result, TCA_OPTIONS, opts);
16944d362409d5469aed47d19e7908d19bd194493aThomas Graf			nlmsg_free(opts);
17044d362409d5469aed47d19e7908d19bd194493aThomas Graf			if (err < 0)
17144d362409d5469aed47d19e7908d19bd194493aThomas Graf				goto errout;
17244d362409d5469aed47d19e7908d19bd194493aThomas Graf		}
17344d362409d5469aed47d19e7908d19bd194493aThomas Graf	}
174241b2b83ba5672f5c86154d29eeb8ef4c7c6e9b4Tad Kollar	/* Some qdiscs don't accept properly nested messages (e.g. netem). To
175241b2b83ba5672f5c86154d29eeb8ef4c7c6e9b4Tad Kollar	 * accomodate for this, they can complete the message themselves.
176241b2b83ba5672f5c86154d29eeb8ef4c7c6e9b4Tad Kollar	 */
177241b2b83ba5672f5c86154d29eeb8ef4c7c6e9b4Tad Kollar	else if (qops && qops->qo_build_msg) {
1788a3efffa5b3fde252675239914118664d36a2c24Thomas Graf		err = qops->qo_build_msg(qdisc, *result);
1798a3efffa5b3fde252675239914118664d36a2c24Thomas Graf		if (err < 0)
180241b2b83ba5672f5c86154d29eeb8ef4c7c6e9b4Tad Kollar			goto errout;
181241b2b83ba5672f5c86154d29eeb8ef4c7c6e9b4Tad Kollar	}
18244d362409d5469aed47d19e7908d19bd194493aThomas Graf
1838a3efffa5b3fde252675239914118664d36a2c24Thomas Graf	return 0;
18444d362409d5469aed47d19e7908d19bd194493aThomas Graferrout:
1858a3efffa5b3fde252675239914118664d36a2c24Thomas Graf	nlmsg_free(*result);
18644d362409d5469aed47d19e7908d19bd194493aThomas Graf
1878a3efffa5b3fde252675239914118664d36a2c24Thomas Graf	return err;
18844d362409d5469aed47d19e7908d19bd194493aThomas Graf}
18944d362409d5469aed47d19e7908d19bd194493aThomas Graf
19044d362409d5469aed47d19e7908d19bd194493aThomas Graf/**
19144d362409d5469aed47d19e7908d19bd194493aThomas Graf * Build a netlink message to add a new qdisc
19244d362409d5469aed47d19e7908d19bd194493aThomas Graf * @arg qdisc		qdisc to add
19344d362409d5469aed47d19e7908d19bd194493aThomas Graf * @arg flags		additional netlink message flags
1948a3efffa5b3fde252675239914118664d36a2c24Thomas Graf * @arg result		Pointer to store resulting message.
19544d362409d5469aed47d19e7908d19bd194493aThomas Graf *
19644d362409d5469aed47d19e7908d19bd194493aThomas Graf * Builds a new netlink message requesting an addition of a qdisc.
19744d362409d5469aed47d19e7908d19bd194493aThomas Graf * The netlink message header isn't fully equipped with all relevant
19844d362409d5469aed47d19e7908d19bd194493aThomas Graf * fields and must be sent out via nl_send_auto_complete() or
19944d362409d5469aed47d19e7908d19bd194493aThomas Graf * supplemented as needed.
20044d362409d5469aed47d19e7908d19bd194493aThomas Graf *
20144d362409d5469aed47d19e7908d19bd194493aThomas Graf * Common message flags used:
20244d362409d5469aed47d19e7908d19bd194493aThomas Graf *  - NLM_F_REPLACE - replace a potential existing qdisc
20344d362409d5469aed47d19e7908d19bd194493aThomas Graf *
2048a3efffa5b3fde252675239914118664d36a2c24Thomas Graf * @return 0 on success or a negative error code.
20544d362409d5469aed47d19e7908d19bd194493aThomas Graf */
2068a3efffa5b3fde252675239914118664d36a2c24Thomas Grafint rtnl_qdisc_build_add_request(struct rtnl_qdisc *qdisc, int flags,
2078a3efffa5b3fde252675239914118664d36a2c24Thomas Graf				 struct nl_msg **result)
20844d362409d5469aed47d19e7908d19bd194493aThomas Graf{
2098a3efffa5b3fde252675239914118664d36a2c24Thomas Graf	return qdisc_build(qdisc, RTM_NEWQDISC, NLM_F_CREATE | flags, result);
21044d362409d5469aed47d19e7908d19bd194493aThomas Graf}
21144d362409d5469aed47d19e7908d19bd194493aThomas Graf
21244d362409d5469aed47d19e7908d19bd194493aThomas Graf/**
21344d362409d5469aed47d19e7908d19bd194493aThomas Graf * Add a new qdisc
2141155370f520cb64657e25153255cf7dc1424317fThomas Graf * @arg sk		Netlink socket.
21544d362409d5469aed47d19e7908d19bd194493aThomas Graf * @arg qdisc		qdisc to delete
21644d362409d5469aed47d19e7908d19bd194493aThomas Graf * @arg flags		additional netlink message flags
21744d362409d5469aed47d19e7908d19bd194493aThomas Graf *
21844d362409d5469aed47d19e7908d19bd194493aThomas Graf * Builds a netlink message by calling rtnl_qdisc_build_add_request(),
21944d362409d5469aed47d19e7908d19bd194493aThomas Graf * sends the request to the kernel and waits for the ACK to be
22044d362409d5469aed47d19e7908d19bd194493aThomas Graf * received and thus blocks until the request has been processed.
22144d362409d5469aed47d19e7908d19bd194493aThomas Graf *
22244d362409d5469aed47d19e7908d19bd194493aThomas Graf * Common message flags used:
22344d362409d5469aed47d19e7908d19bd194493aThomas Graf *  - NLM_F_REPLACE - replace a potential existing qdisc
22444d362409d5469aed47d19e7908d19bd194493aThomas Graf *
22544d362409d5469aed47d19e7908d19bd194493aThomas Graf * @return 0 on success or a negative error code
22644d362409d5469aed47d19e7908d19bd194493aThomas Graf */
2271155370f520cb64657e25153255cf7dc1424317fThomas Grafint rtnl_qdisc_add(struct nl_sock *sk, struct rtnl_qdisc *qdisc,
22844d362409d5469aed47d19e7908d19bd194493aThomas Graf		   int flags)
22944d362409d5469aed47d19e7908d19bd194493aThomas Graf{
23044d362409d5469aed47d19e7908d19bd194493aThomas Graf	struct nl_msg *msg;
23144d362409d5469aed47d19e7908d19bd194493aThomas Graf	int err;
23244d362409d5469aed47d19e7908d19bd194493aThomas Graf
2338a3efffa5b3fde252675239914118664d36a2c24Thomas Graf	if ((err = rtnl_qdisc_build_add_request(qdisc, flags, &msg)) < 0)
2348a3efffa5b3fde252675239914118664d36a2c24Thomas Graf		return err;
23544d362409d5469aed47d19e7908d19bd194493aThomas Graf
236ef50a38fbd8682a5c9efd559e7db68664977f080Thomas Graf	err = nl_send_auto_complete(sk, msg);
237ef50a38fbd8682a5c9efd559e7db68664977f080Thomas Graf	nlmsg_free(msg);
238ef50a38fbd8682a5c9efd559e7db68664977f080Thomas Graf	if (err < 0)
23944d362409d5469aed47d19e7908d19bd194493aThomas Graf		return err;
24044d362409d5469aed47d19e7908d19bd194493aThomas Graf
2411155370f520cb64657e25153255cf7dc1424317fThomas Graf	return nl_wait_for_ack(sk);
24244d362409d5469aed47d19e7908d19bd194493aThomas Graf}
24344d362409d5469aed47d19e7908d19bd194493aThomas Graf
24444d362409d5469aed47d19e7908d19bd194493aThomas Graf/** @} */
24544d362409d5469aed47d19e7908d19bd194493aThomas Graf
24644d362409d5469aed47d19e7908d19bd194493aThomas Graf/**
24744d362409d5469aed47d19e7908d19bd194493aThomas Graf * @name QDisc Modification
24844d362409d5469aed47d19e7908d19bd194493aThomas Graf * @{
24944d362409d5469aed47d19e7908d19bd194493aThomas Graf */
25044d362409d5469aed47d19e7908d19bd194493aThomas Graf
25144d362409d5469aed47d19e7908d19bd194493aThomas Graf/**
25244d362409d5469aed47d19e7908d19bd194493aThomas Graf * Build a netlink message to change attributes of a existing qdisc
25344d362409d5469aed47d19e7908d19bd194493aThomas Graf * @arg qdisc		qdisc to change
25444d362409d5469aed47d19e7908d19bd194493aThomas Graf * @arg new		new qdisc attributes
2558a3efffa5b3fde252675239914118664d36a2c24Thomas Graf * @arg result		Pointer to store resulting message.
25644d362409d5469aed47d19e7908d19bd194493aThomas Graf *
25744d362409d5469aed47d19e7908d19bd194493aThomas Graf * Builds a new netlink message requesting an change of qdisc
25844d362409d5469aed47d19e7908d19bd194493aThomas Graf * attributes. The netlink message header isn't fully equipped
25944d362409d5469aed47d19e7908d19bd194493aThomas Graf * with all relevant fields and must be sent out via
26044d362409d5469aed47d19e7908d19bd194493aThomas Graf * nl_send_auto_complete() or supplemented as needed.
26144d362409d5469aed47d19e7908d19bd194493aThomas Graf *
2628a3efffa5b3fde252675239914118664d36a2c24Thomas Graf * @return 0 on success or a negative error code.
26344d362409d5469aed47d19e7908d19bd194493aThomas Graf */
2648a3efffa5b3fde252675239914118664d36a2c24Thomas Grafint rtnl_qdisc_build_change_request(struct rtnl_qdisc *qdisc,
2658a3efffa5b3fde252675239914118664d36a2c24Thomas Graf				    struct rtnl_qdisc *new,
2668a3efffa5b3fde252675239914118664d36a2c24Thomas Graf				    struct nl_msg **result)
26744d362409d5469aed47d19e7908d19bd194493aThomas Graf{
2688a3efffa5b3fde252675239914118664d36a2c24Thomas Graf	return qdisc_build(qdisc, RTM_NEWQDISC, NLM_F_REPLACE, result);
26944d362409d5469aed47d19e7908d19bd194493aThomas Graf}
27044d362409d5469aed47d19e7908d19bd194493aThomas Graf
27144d362409d5469aed47d19e7908d19bd194493aThomas Graf/**
27244d362409d5469aed47d19e7908d19bd194493aThomas Graf * Change attributes of a qdisc
2731155370f520cb64657e25153255cf7dc1424317fThomas Graf * @arg sk		Netlink socket.
27444d362409d5469aed47d19e7908d19bd194493aThomas Graf * @arg qdisc		qdisc to change
27544d362409d5469aed47d19e7908d19bd194493aThomas Graf * @arg new		new qdisc attributes
27644d362409d5469aed47d19e7908d19bd194493aThomas Graf *
27744d362409d5469aed47d19e7908d19bd194493aThomas Graf * Builds a netlink message by calling rtnl_qdisc_build_change_request(),
27844d362409d5469aed47d19e7908d19bd194493aThomas Graf * sends the request to the kernel and waits for the ACK to be
27944d362409d5469aed47d19e7908d19bd194493aThomas Graf * received and thus blocks until the request has been processed.
28044d362409d5469aed47d19e7908d19bd194493aThomas Graf *
28144d362409d5469aed47d19e7908d19bd194493aThomas Graf * @return 0 on success or a negative error code
28244d362409d5469aed47d19e7908d19bd194493aThomas Graf */
2831155370f520cb64657e25153255cf7dc1424317fThomas Grafint rtnl_qdisc_change(struct nl_sock *sk, struct rtnl_qdisc *qdisc,
28444d362409d5469aed47d19e7908d19bd194493aThomas Graf		      struct rtnl_qdisc *new)
28544d362409d5469aed47d19e7908d19bd194493aThomas Graf{
28644d362409d5469aed47d19e7908d19bd194493aThomas Graf	struct nl_msg *msg;
28744d362409d5469aed47d19e7908d19bd194493aThomas Graf	int err;
28844d362409d5469aed47d19e7908d19bd194493aThomas Graf
2898a3efffa5b3fde252675239914118664d36a2c24Thomas Graf	if ((err = rtnl_qdisc_build_change_request(qdisc, new, &msg)) < 0)
2908a3efffa5b3fde252675239914118664d36a2c24Thomas Graf		return err;
29144d362409d5469aed47d19e7908d19bd194493aThomas Graf
292ef50a38fbd8682a5c9efd559e7db68664977f080Thomas Graf	err = nl_send_auto_complete(sk, msg);
293ef50a38fbd8682a5c9efd559e7db68664977f080Thomas Graf	nlmsg_free(msg);
294ef50a38fbd8682a5c9efd559e7db68664977f080Thomas Graf	if (err < 0)
29544d362409d5469aed47d19e7908d19bd194493aThomas Graf		return err;
29644d362409d5469aed47d19e7908d19bd194493aThomas Graf
2971155370f520cb64657e25153255cf7dc1424317fThomas Graf	return nl_wait_for_ack(sk);
29844d362409d5469aed47d19e7908d19bd194493aThomas Graf}
29944d362409d5469aed47d19e7908d19bd194493aThomas Graf
30044d362409d5469aed47d19e7908d19bd194493aThomas Graf/** @} */
30144d362409d5469aed47d19e7908d19bd194493aThomas Graf
30244d362409d5469aed47d19e7908d19bd194493aThomas Graf/**
30344d362409d5469aed47d19e7908d19bd194493aThomas Graf * @name QDisc Deletion
30444d362409d5469aed47d19e7908d19bd194493aThomas Graf * @{
30544d362409d5469aed47d19e7908d19bd194493aThomas Graf */
30644d362409d5469aed47d19e7908d19bd194493aThomas Graf
30744d362409d5469aed47d19e7908d19bd194493aThomas Graf/**
30844d362409d5469aed47d19e7908d19bd194493aThomas Graf * Build a netlink request message to delete a qdisc
30944d362409d5469aed47d19e7908d19bd194493aThomas Graf * @arg qdisc		qdisc to delete
3108a3efffa5b3fde252675239914118664d36a2c24Thomas Graf * @arg result		Pointer to store resulting message.
31144d362409d5469aed47d19e7908d19bd194493aThomas Graf *
31244d362409d5469aed47d19e7908d19bd194493aThomas Graf * Builds a new netlink message requesting a deletion of a qdisc.
31344d362409d5469aed47d19e7908d19bd194493aThomas Graf * The netlink message header isn't fully equipped with all relevant
31444d362409d5469aed47d19e7908d19bd194493aThomas Graf * fields and must thus be sent out via nl_send_auto_complete()
31544d362409d5469aed47d19e7908d19bd194493aThomas Graf * or supplemented as needed.
31644d362409d5469aed47d19e7908d19bd194493aThomas Graf *
3178a3efffa5b3fde252675239914118664d36a2c24Thomas Graf * @return 0 on success or a negative error code.
31844d362409d5469aed47d19e7908d19bd194493aThomas Graf */
3198a3efffa5b3fde252675239914118664d36a2c24Thomas Grafint rtnl_qdisc_build_delete_request(struct rtnl_qdisc *qdisc,
3208a3efffa5b3fde252675239914118664d36a2c24Thomas Graf				    struct nl_msg **result)
32144d362409d5469aed47d19e7908d19bd194493aThomas Graf{
32244d362409d5469aed47d19e7908d19bd194493aThomas Graf	struct nl_msg *msg;
32344d362409d5469aed47d19e7908d19bd194493aThomas Graf	struct tcmsg tchdr;
32444d362409d5469aed47d19e7908d19bd194493aThomas Graf	int required = TCA_ATTR_IFINDEX | TCA_ATTR_PARENT;
32544d362409d5469aed47d19e7908d19bd194493aThomas Graf
32644d362409d5469aed47d19e7908d19bd194493aThomas Graf	if ((qdisc->ce_mask & required) != required)
32744d362409d5469aed47d19e7908d19bd194493aThomas Graf		BUG();
32844d362409d5469aed47d19e7908d19bd194493aThomas Graf
32944d362409d5469aed47d19e7908d19bd194493aThomas Graf	msg = nlmsg_alloc_simple(RTM_DELQDISC, 0);
33044d362409d5469aed47d19e7908d19bd194493aThomas Graf	if (!msg)
3318a3efffa5b3fde252675239914118664d36a2c24Thomas Graf		return -NLE_NOMEM;
3328a3efffa5b3fde252675239914118664d36a2c24Thomas Graf
3338a3efffa5b3fde252675239914118664d36a2c24Thomas Graf	tchdr.tcm_family = AF_UNSPEC;
3348a3efffa5b3fde252675239914118664d36a2c24Thomas Graf	tchdr.tcm_handle = qdisc->q_handle;
3358a3efffa5b3fde252675239914118664d36a2c24Thomas Graf	tchdr.tcm_parent = qdisc->q_parent;
3368a3efffa5b3fde252675239914118664d36a2c24Thomas Graf	tchdr.tcm_ifindex = qdisc->q_ifindex;
3378a3efffa5b3fde252675239914118664d36a2c24Thomas Graf	if (nlmsg_append(msg, &tchdr, sizeof(tchdr), NLMSG_ALIGNTO) < 0) {
3388a3efffa5b3fde252675239914118664d36a2c24Thomas Graf		nlmsg_free(msg);
3398a3efffa5b3fde252675239914118664d36a2c24Thomas Graf		return -NLE_MSGSIZE;
3408a3efffa5b3fde252675239914118664d36a2c24Thomas Graf	}
34144d362409d5469aed47d19e7908d19bd194493aThomas Graf
3428a3efffa5b3fde252675239914118664d36a2c24Thomas Graf	*result = msg;
3438a3efffa5b3fde252675239914118664d36a2c24Thomas Graf	return 0;
34444d362409d5469aed47d19e7908d19bd194493aThomas Graf}
34544d362409d5469aed47d19e7908d19bd194493aThomas Graf
34644d362409d5469aed47d19e7908d19bd194493aThomas Graf/**
34744d362409d5469aed47d19e7908d19bd194493aThomas Graf * Delete a qdisc
3481155370f520cb64657e25153255cf7dc1424317fThomas Graf * @arg sk		Netlink socket.
34944d362409d5469aed47d19e7908d19bd194493aThomas Graf * @arg qdisc		qdisc to delete
35044d362409d5469aed47d19e7908d19bd194493aThomas Graf *
35144d362409d5469aed47d19e7908d19bd194493aThomas Graf * Builds a netlink message by calling rtnl_qdisc_build_delete_request(),
35244d362409d5469aed47d19e7908d19bd194493aThomas Graf * sends the request to the kernel and waits for the ACK to be
35344d362409d5469aed47d19e7908d19bd194493aThomas Graf * received and thus blocks until the request has been processed.
35444d362409d5469aed47d19e7908d19bd194493aThomas Graf *
35544d362409d5469aed47d19e7908d19bd194493aThomas Graf * @return 0 on success or a negative error code
35644d362409d5469aed47d19e7908d19bd194493aThomas Graf */
3571155370f520cb64657e25153255cf7dc1424317fThomas Grafint rtnl_qdisc_delete(struct nl_sock *sk, struct rtnl_qdisc *qdisc)
35844d362409d5469aed47d19e7908d19bd194493aThomas Graf{
35944d362409d5469aed47d19e7908d19bd194493aThomas Graf	struct nl_msg *msg;
36044d362409d5469aed47d19e7908d19bd194493aThomas Graf	int err;
36144d362409d5469aed47d19e7908d19bd194493aThomas Graf
3628a3efffa5b3fde252675239914118664d36a2c24Thomas Graf	if ((err = rtnl_qdisc_build_delete_request(qdisc, &msg)) < 0)
3638a3efffa5b3fde252675239914118664d36a2c24Thomas Graf		return err;
36444d362409d5469aed47d19e7908d19bd194493aThomas Graf
365ef50a38fbd8682a5c9efd559e7db68664977f080Thomas Graf	err = nl_send_auto_complete(sk, msg);
366ef50a38fbd8682a5c9efd559e7db68664977f080Thomas Graf	nlmsg_free(msg);
367ef50a38fbd8682a5c9efd559e7db68664977f080Thomas Graf	if (err < 0)
36844d362409d5469aed47d19e7908d19bd194493aThomas Graf		return err;
36944d362409d5469aed47d19e7908d19bd194493aThomas Graf
3701155370f520cb64657e25153255cf7dc1424317fThomas Graf	return nl_wait_for_ack(sk);
37144d362409d5469aed47d19e7908d19bd194493aThomas Graf}
37244d362409d5469aed47d19e7908d19bd194493aThomas Graf
37344d362409d5469aed47d19e7908d19bd194493aThomas Graf/** @} */
37444d362409d5469aed47d19e7908d19bd194493aThomas Graf
37544d362409d5469aed47d19e7908d19bd194493aThomas Graf/**
37644d362409d5469aed47d19e7908d19bd194493aThomas Graf * @name Qdisc Cache Management
37744d362409d5469aed47d19e7908d19bd194493aThomas Graf * @{
37844d362409d5469aed47d19e7908d19bd194493aThomas Graf */
37944d362409d5469aed47d19e7908d19bd194493aThomas Graf
38044d362409d5469aed47d19e7908d19bd194493aThomas Graf/**
38144d362409d5469aed47d19e7908d19bd194493aThomas Graf * Build a qdisc cache including all qdiscs currently configured in
38244d362409d5469aed47d19e7908d19bd194493aThomas Graf * the kernel
3831155370f520cb64657e25153255cf7dc1424317fThomas Graf * @arg sk		Netlink socket.
3848a3efffa5b3fde252675239914118664d36a2c24Thomas Graf * @arg result		Pointer to store resulting message.
38544d362409d5469aed47d19e7908d19bd194493aThomas Graf *
38644d362409d5469aed47d19e7908d19bd194493aThomas Graf * Allocates a new cache, initializes it properly and updates it to
38744d362409d5469aed47d19e7908d19bd194493aThomas Graf * include all qdiscs currently configured in the kernel.
38844d362409d5469aed47d19e7908d19bd194493aThomas Graf *
3898a3efffa5b3fde252675239914118664d36a2c24Thomas Graf * @return 0 on success or a negative error code.
39044d362409d5469aed47d19e7908d19bd194493aThomas Graf */
3911155370f520cb64657e25153255cf7dc1424317fThomas Grafint rtnl_qdisc_alloc_cache(struct nl_sock *sk, struct nl_cache **result)
39244d362409d5469aed47d19e7908d19bd194493aThomas Graf{
3931155370f520cb64657e25153255cf7dc1424317fThomas Graf	return nl_cache_alloc_and_fill(&rtnl_qdisc_ops, sk, result);
39444d362409d5469aed47d19e7908d19bd194493aThomas Graf}
39544d362409d5469aed47d19e7908d19bd194493aThomas Graf
39644d362409d5469aed47d19e7908d19bd194493aThomas Graf/**
39744d362409d5469aed47d19e7908d19bd194493aThomas Graf * Look up qdisc by its parent in the provided cache
39844d362409d5469aed47d19e7908d19bd194493aThomas Graf * @arg cache		qdisc cache
39944d362409d5469aed47d19e7908d19bd194493aThomas Graf * @arg ifindex		interface the qdisc is attached to
40044d362409d5469aed47d19e7908d19bd194493aThomas Graf * @arg parent		parent handle
40144d362409d5469aed47d19e7908d19bd194493aThomas Graf * @return pointer to qdisc inside the cache or NULL if no match was found.
40244d362409d5469aed47d19e7908d19bd194493aThomas Graf */
40344d362409d5469aed47d19e7908d19bd194493aThomas Grafstruct rtnl_qdisc * rtnl_qdisc_get_by_parent(struct nl_cache *cache,
40444d362409d5469aed47d19e7908d19bd194493aThomas Graf					     int ifindex, uint32_t parent)
40544d362409d5469aed47d19e7908d19bd194493aThomas Graf{
40644d362409d5469aed47d19e7908d19bd194493aThomas Graf	struct rtnl_qdisc *q;
40744d362409d5469aed47d19e7908d19bd194493aThomas Graf
40844d362409d5469aed47d19e7908d19bd194493aThomas Graf	if (cache->c_ops != &rtnl_qdisc_ops)
40944d362409d5469aed47d19e7908d19bd194493aThomas Graf		return NULL;
41044d362409d5469aed47d19e7908d19bd194493aThomas Graf
41144d362409d5469aed47d19e7908d19bd194493aThomas Graf	nl_list_for_each_entry(q, &cache->c_items, ce_list) {
41244d362409d5469aed47d19e7908d19bd194493aThomas Graf		if (q->q_parent == parent && q->q_ifindex == ifindex) {
41344d362409d5469aed47d19e7908d19bd194493aThomas Graf			nl_object_get((struct nl_object *) q);
41444d362409d5469aed47d19e7908d19bd194493aThomas Graf			return q;
41544d362409d5469aed47d19e7908d19bd194493aThomas Graf		}
41644d362409d5469aed47d19e7908d19bd194493aThomas Graf	}
41744d362409d5469aed47d19e7908d19bd194493aThomas Graf
41844d362409d5469aed47d19e7908d19bd194493aThomas Graf	return NULL;
41944d362409d5469aed47d19e7908d19bd194493aThomas Graf}
42044d362409d5469aed47d19e7908d19bd194493aThomas Graf
42144d362409d5469aed47d19e7908d19bd194493aThomas Graf/**
42244d362409d5469aed47d19e7908d19bd194493aThomas Graf * Look up qdisc by its handle in the provided cache
42344d362409d5469aed47d19e7908d19bd194493aThomas Graf * @arg cache		qdisc cache
42444d362409d5469aed47d19e7908d19bd194493aThomas Graf * @arg ifindex		interface the qdisc is attached to
42544d362409d5469aed47d19e7908d19bd194493aThomas Graf * @arg handle		qdisc handle
42644d362409d5469aed47d19e7908d19bd194493aThomas Graf * @return pointer to qdisc inside the cache or NULL if no match was found.
42744d362409d5469aed47d19e7908d19bd194493aThomas Graf */
42844d362409d5469aed47d19e7908d19bd194493aThomas Grafstruct rtnl_qdisc * rtnl_qdisc_get(struct nl_cache *cache,
42944d362409d5469aed47d19e7908d19bd194493aThomas Graf				   int ifindex, uint32_t handle)
43044d362409d5469aed47d19e7908d19bd194493aThomas Graf{
43144d362409d5469aed47d19e7908d19bd194493aThomas Graf	struct rtnl_qdisc *q;
43244d362409d5469aed47d19e7908d19bd194493aThomas Graf
43344d362409d5469aed47d19e7908d19bd194493aThomas Graf	if (cache->c_ops != &rtnl_qdisc_ops)
43444d362409d5469aed47d19e7908d19bd194493aThomas Graf		return NULL;
43544d362409d5469aed47d19e7908d19bd194493aThomas Graf
43644d362409d5469aed47d19e7908d19bd194493aThomas Graf	nl_list_for_each_entry(q, &cache->c_items, ce_list) {
43744d362409d5469aed47d19e7908d19bd194493aThomas Graf		if (q->q_handle == handle && q->q_ifindex == ifindex) {
43844d362409d5469aed47d19e7908d19bd194493aThomas Graf			nl_object_get((struct nl_object *) q);
43944d362409d5469aed47d19e7908d19bd194493aThomas Graf			return q;
44044d362409d5469aed47d19e7908d19bd194493aThomas Graf		}
44144d362409d5469aed47d19e7908d19bd194493aThomas Graf	}
44244d362409d5469aed47d19e7908d19bd194493aThomas Graf
44344d362409d5469aed47d19e7908d19bd194493aThomas Graf	return NULL;
44444d362409d5469aed47d19e7908d19bd194493aThomas Graf}
44544d362409d5469aed47d19e7908d19bd194493aThomas Graf
44644d362409d5469aed47d19e7908d19bd194493aThomas Graf/** @} */
44744d362409d5469aed47d19e7908d19bd194493aThomas Graf
44844d362409d5469aed47d19e7908d19bd194493aThomas Grafstatic struct nl_cache_ops rtnl_qdisc_ops = {
44944d362409d5469aed47d19e7908d19bd194493aThomas Graf	.co_name		= "route/qdisc",
45044d362409d5469aed47d19e7908d19bd194493aThomas Graf	.co_hdrsize		= sizeof(struct tcmsg),
45144d362409d5469aed47d19e7908d19bd194493aThomas Graf	.co_msgtypes		= {
45244d362409d5469aed47d19e7908d19bd194493aThomas Graf					{ RTM_NEWQDISC, NL_ACT_NEW, "new" },
45344d362409d5469aed47d19e7908d19bd194493aThomas Graf					{ RTM_DELQDISC, NL_ACT_DEL, "del" },
45444d362409d5469aed47d19e7908d19bd194493aThomas Graf					{ RTM_GETQDISC, NL_ACT_GET, "get" },
45544d362409d5469aed47d19e7908d19bd194493aThomas Graf					END_OF_MSGTYPES_LIST,
45644d362409d5469aed47d19e7908d19bd194493aThomas Graf				  },
45744d362409d5469aed47d19e7908d19bd194493aThomas Graf	.co_protocol		= NETLINK_ROUTE,
45844d362409d5469aed47d19e7908d19bd194493aThomas Graf	.co_request_update	= qdisc_request_update,
45944d362409d5469aed47d19e7908d19bd194493aThomas Graf	.co_msg_parser		= qdisc_msg_parser,
46044d362409d5469aed47d19e7908d19bd194493aThomas Graf	.co_obj_ops		= &qdisc_obj_ops,
46144d362409d5469aed47d19e7908d19bd194493aThomas Graf};
46244d362409d5469aed47d19e7908d19bd194493aThomas Graf
46344d362409d5469aed47d19e7908d19bd194493aThomas Grafstatic void __init qdisc_init(void)
46444d362409d5469aed47d19e7908d19bd194493aThomas Graf{
46544d362409d5469aed47d19e7908d19bd194493aThomas Graf	nl_cache_mngt_register(&rtnl_qdisc_ops);
46644d362409d5469aed47d19e7908d19bd194493aThomas Graf}
46744d362409d5469aed47d19e7908d19bd194493aThomas Graf
46844d362409d5469aed47d19e7908d19bd194493aThomas Grafstatic void __exit qdisc_exit(void)
46944d362409d5469aed47d19e7908d19bd194493aThomas Graf{
47044d362409d5469aed47d19e7908d19bd194493aThomas Graf	nl_cache_mngt_unregister(&rtnl_qdisc_ops);
47144d362409d5469aed47d19e7908d19bd194493aThomas Graf}
47244d362409d5469aed47d19e7908d19bd194493aThomas Graf
47344d362409d5469aed47d19e7908d19bd194493aThomas Graf/** @} */
474