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