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