libnl 2.0

/build/buildd/libnl2-2.0/lib/route/qdisc.c

00001 /*
00002  * lib/route/qdisc.c            Queueing Disciplines
00003  *
00004  *      This library is free software; you can redistribute it and/or
00005  *      modify it under the terms of the GNU Lesser General Public
00006  *      License as published by the Free Software Foundation version 2.1
00007  *      of the License.
00008  *
00009  * Copyright (c) 2003-2008 Thomas Graf <tgraf@suug.ch>
00010  */
00011 
00012 /**
00013  * @ingroup tc
00014  * @defgroup qdisc Queueing Disciplines
00015  *
00016  * @par Qdisc Handles
00017  * In general, qdiscs are identified by the major part of a traffic control
00018  * handle (the upper 16 bits). A few special values exist though:
00019  *  - \c TC_H_ROOT: root qdisc (directly attached to the device)
00020  *  - \c TC_H_INGRESS: ingress qdisc (directly attached to the device)
00021  *  - \c TC_H_UNSPEC: unspecified qdisc (no reference)
00022  *
00023  * @par 1) Adding a Qdisc
00024  * @code
00025  * // Allocate a new empty qdisc to be filled out
00026  * struct rtnl_qdisc *qdisc = rtnl_qdisc_alloc();
00027  *
00028  * // ... specify the kind of the Qdisc
00029  * rtnl_qdisc_set_kind(qdisc, "pfifo");
00030  *
00031  * // Specify the device the qdisc should be attached to
00032  * rtnl_qdisc_set_ifindex(qdisc, ifindex);
00033  *
00034  * // ... specify the parent qdisc
00035  * rtnl_qdisc_set_parent(qdisc, TC_H_ROOT);
00036  *
00037  * // Specifying the handle is not required but makes reidentifying easier
00038  * // and may help to avoid adding a qdisc twice.
00039  * rtnl_qdisc_set_handle(qdisc, 0x000A0000);
00040  *
00041  * // Now on to specify the qdisc specific options, see the relevant qdisc
00042  * // modules for documentation, in this example we set the upper limit of
00043  * // the packet fifo qdisc to 64
00044  * rtnl_qdisc_fifo_set_limit(qdisc, 64);
00045  *
00046  * rtnl_qdisc_add(handle, qdisc, NLM_R_REPLACE);
00047  *
00048  * // Free up the memory
00049  * rtnl_qdisc_put(qdisc);
00050  * @endcode
00051  *
00052  * @par 2) Deleting a Qdisc
00053  * @code
00054  * // Allocate a new empty qdisc to be filled out with the parameters
00055  * // specifying the qdisc to be deleted. Alternatively a fully equiped
00056  * // Qdisc object from a cache can be used.
00057  * struct rtnl_qdisc *qdisc = rtnl_qdisc_alloc();
00058  *
00059  * // The interface index of the device the qdisc is on and the parent handle
00060  * // are the least required fields to be filled out.
00061  * // Note: Specify TC_H_ROOT or TC_H_INGRESS as parent handle to delete the
00062  * //       root respectively root ingress qdisc.
00063  * rtnl_qdisc_set_ifindex(qdisc, ifindex);
00064  * rtnl_qdisc_set_parent(qdisc, parent_handle);
00065  *
00066  * // If required for identification, the handle can be specified as well.
00067  * rtnl_qdisc_set_handle(qdisc, qdisc_handle);
00068  *
00069  * // Not required but maybe helpful as sanity check, the kind of the qdisc
00070  * // can be specified to avoid mistakes.
00071  * rtnl_qdisc_set_kind(qdisc, "pfifo");
00072  *
00073  * // Finally delete the qdisc with rtnl_qdisc_delete(), alternatively
00074  * // rtnl_qdisc_build_delete_request() can be invoked to generate an
00075  * // appropritate netlink message to send out.
00076  * rtnl_qdisc_delete(handle, qdisc);
00077  *
00078  * // Free up the memory
00079  * rtnl_qdisc_put(qdisc);
00080  * @endcode
00081  *
00082  * @{
00083  */
00084 
00085 #include <netlink-local.h>
00086 #include <netlink-tc.h>
00087 #include <netlink/netlink.h>
00088 #include <netlink/utils.h>
00089 #include <netlink/route/link.h>
00090 #include <netlink/route/tc.h>
00091 #include <netlink/route/qdisc.h>
00092 #include <netlink/route/class.h>
00093 #include <netlink/route/classifier.h>
00094 #include <netlink/route/qdisc-modules.h>
00095 
00096 static struct nl_cache_ops rtnl_qdisc_ops;
00097 
00098 static int qdisc_msg_parser(struct nl_cache_ops *ops, struct sockaddr_nl *who,
00099                             struct nlmsghdr *n, struct nl_parser_param *pp)
00100 {
00101         int err;
00102         struct rtnl_qdisc *qdisc;
00103         struct rtnl_qdisc_ops *qops;
00104 
00105         qdisc = rtnl_qdisc_alloc();
00106         if (!qdisc) {
00107                 err = -NLE_NOMEM;
00108                 goto errout;
00109         }
00110 
00111         qdisc->ce_msgtype = n->nlmsg_type;
00112 
00113         err = tca_msg_parser(n, (struct rtnl_tca *) qdisc);
00114         if (err < 0)
00115                 goto errout_free;
00116 
00117         qops = rtnl_qdisc_lookup_ops(qdisc);
00118         if (qops && qops->qo_msg_parser) {
00119                 err = qops->qo_msg_parser(qdisc);
00120                 if (err < 0)
00121                         goto errout_free;
00122         }
00123 
00124         err = pp->pp_cb((struct nl_object *) qdisc, pp);
00125 errout_free:
00126         rtnl_qdisc_put(qdisc);
00127 errout:
00128         return err;
00129 }
00130 
00131 static int qdisc_request_update(struct nl_cache *c, struct nl_sock *sk)
00132 {
00133         struct tcmsg tchdr = {
00134                 .tcm_family = AF_UNSPEC,
00135                 .tcm_ifindex = c->c_iarg1,
00136         };
00137 
00138         return nl_send_simple(sk, RTM_GETQDISC, NLM_F_DUMP, &tchdr,
00139                               sizeof(tchdr));
00140 }
00141 
00142 /**
00143  * @name QDisc Addition
00144  * @{
00145  */
00146 
00147 static int qdisc_build(struct rtnl_qdisc *qdisc, int type, int flags,
00148                        struct nl_msg **result)
00149 {
00150         struct rtnl_qdisc_ops *qops;
00151         int err;
00152 
00153         err = tca_build_msg((struct rtnl_tca *) qdisc, type, flags, result);
00154         if (err < 0)
00155                 return err;
00156 
00157         qops = rtnl_qdisc_lookup_ops(qdisc);
00158         if (qops && qops->qo_get_opts) {
00159                 struct nl_msg *opts;
00160                 
00161                 opts = qops->qo_get_opts(qdisc);
00162                 if (opts) {
00163                         err = nla_put_nested(*result, TCA_OPTIONS, opts);
00164                         nlmsg_free(opts);
00165                         if (err < 0)
00166                                 goto errout;
00167                 }
00168         }
00169         /* Some qdiscs don't accept properly nested messages (e.g. netem). To
00170          * accomodate for this, they can complete the message themselves.
00171          */             
00172         else if (qops && qops->qo_build_msg) {
00173                 err = qops->qo_build_msg(qdisc, *result);
00174                 if (err < 0)
00175                         goto errout;
00176         }
00177 
00178         return 0;
00179 errout:
00180         nlmsg_free(*result);
00181 
00182         return err;
00183 }
00184 
00185 /**
00186  * Build a netlink message to add a new qdisc
00187  * @arg qdisc           qdisc to add 
00188  * @arg flags           additional netlink message flags
00189  * @arg result          Pointer to store resulting message.
00190  *
00191  * Builds a new netlink message requesting an addition of a qdisc.
00192  * The netlink message header isn't fully equipped with all relevant
00193  * fields and must be sent out via nl_send_auto_complete() or
00194  * supplemented as needed. 
00195  *
00196  * Common message flags used:
00197  *  - NLM_F_REPLACE - replace a potential existing qdisc
00198  *
00199  * @return 0 on success or a negative error code.
00200  */
00201 int rtnl_qdisc_build_add_request(struct rtnl_qdisc *qdisc, int flags,
00202                                  struct nl_msg **result)
00203 {
00204         return qdisc_build(qdisc, RTM_NEWQDISC, NLM_F_CREATE | flags, result);
00205 }
00206 
00207 /**
00208  * Add a new qdisc
00209  * @arg sk              Netlink socket.
00210  * @arg qdisc           qdisc to delete
00211  * @arg flags           additional netlink message flags
00212  *
00213  * Builds a netlink message by calling rtnl_qdisc_build_add_request(),
00214  * sends the request to the kernel and waits for the ACK to be
00215  * received and thus blocks until the request has been processed.
00216  *
00217  * Common message flags used:
00218  *  - NLM_F_REPLACE - replace a potential existing qdisc
00219  *
00220  * @return 0 on success or a negative error code
00221  */
00222 int rtnl_qdisc_add(struct nl_sock *sk, struct rtnl_qdisc *qdisc,
00223                    int flags)
00224 {
00225         struct nl_msg *msg;
00226         int err;
00227 
00228         if ((err = rtnl_qdisc_build_add_request(qdisc, flags, &msg)) < 0)
00229                 return err;
00230 
00231         err = nl_send_auto_complete(sk, msg);
00232         nlmsg_free(msg);
00233         if (err < 0)
00234                 return err;
00235 
00236         return wait_for_ack(sk);
00237 }
00238 
00239 /** @} */
00240 
00241 /**
00242  * @name QDisc Modification
00243  * @{
00244  */
00245 
00246 /**
00247  * Build a netlink message to change attributes of a existing qdisc
00248  * @arg qdisc           qdisc to change
00249  * @arg new             new qdisc attributes
00250  * @arg result          Pointer to store resulting message.
00251  *
00252  * Builds a new netlink message requesting an change of qdisc
00253  * attributes. The netlink message header isn't fully equipped
00254  * with all relevant fields and must be sent out via
00255  * nl_send_auto_complete() or supplemented as needed. 
00256  *
00257  * @return 0 on success or a negative error code.
00258  */
00259 int rtnl_qdisc_build_change_request(struct rtnl_qdisc *qdisc,
00260                                     struct rtnl_qdisc *new,
00261                                     struct nl_msg **result)
00262 {
00263         return qdisc_build(qdisc, RTM_NEWQDISC, NLM_F_REPLACE, result);
00264 }
00265 
00266 /**
00267  * Change attributes of a qdisc
00268  * @arg sk              Netlink socket.
00269  * @arg qdisc           qdisc to change
00270  * @arg new             new qdisc attributes
00271  *
00272  * Builds a netlink message by calling rtnl_qdisc_build_change_request(),
00273  * sends the request to the kernel and waits for the ACK to be
00274  * received and thus blocks until the request has been processed.
00275  *
00276  * @return 0 on success or a negative error code
00277  */
00278 int rtnl_qdisc_change(struct nl_sock *sk, struct rtnl_qdisc *qdisc,
00279                       struct rtnl_qdisc *new)
00280 {
00281         struct nl_msg *msg;
00282         int err;
00283 
00284         if ((err = rtnl_qdisc_build_change_request(qdisc, new, &msg)) < 0)
00285                 return err;
00286 
00287         err = nl_send_auto_complete(sk, msg);
00288         nlmsg_free(msg);
00289         if (err < 0)
00290                 return err;
00291 
00292         return wait_for_ack(sk);
00293 }
00294 
00295 /** @} */
00296 
00297 /**
00298  * @name QDisc Deletion
00299  * @{
00300  */
00301 
00302 /**
00303  * Build a netlink request message to delete a qdisc
00304  * @arg qdisc           qdisc to delete
00305  * @arg result          Pointer to store resulting message.
00306  *
00307  * Builds a new netlink message requesting a deletion of a qdisc.
00308  * The netlink message header isn't fully equipped with all relevant
00309  * fields and must thus be sent out via nl_send_auto_complete()
00310  * or supplemented as needed.
00311  *
00312  * @return 0 on success or a negative error code.
00313  */
00314 int rtnl_qdisc_build_delete_request(struct rtnl_qdisc *qdisc,
00315                                     struct nl_msg **result)
00316 {
00317         struct nl_msg *msg;
00318         struct tcmsg tchdr;
00319         int required = TCA_ATTR_IFINDEX | TCA_ATTR_PARENT;
00320 
00321         if ((qdisc->ce_mask & required) != required)
00322                 BUG();
00323 
00324         msg = nlmsg_alloc_simple(RTM_DELQDISC, 0);
00325         if (!msg)
00326                 return -NLE_NOMEM;
00327 
00328         tchdr.tcm_family = AF_UNSPEC;
00329         tchdr.tcm_handle = qdisc->q_handle;
00330         tchdr.tcm_parent = qdisc->q_parent;
00331         tchdr.tcm_ifindex = qdisc->q_ifindex;
00332         if (nlmsg_append(msg, &tchdr, sizeof(tchdr), NLMSG_ALIGNTO) < 0) {
00333                 nlmsg_free(msg);
00334                 return -NLE_MSGSIZE;
00335         }
00336 
00337         *result = msg;
00338         return 0;
00339 }
00340 
00341 /**
00342  * Delete a qdisc
00343  * @arg sk              Netlink socket.
00344  * @arg qdisc           qdisc to delete
00345  *
00346  * Builds a netlink message by calling rtnl_qdisc_build_delete_request(),
00347  * sends the request to the kernel and waits for the ACK to be
00348  * received and thus blocks until the request has been processed.
00349  *
00350  * @return 0 on success or a negative error code
00351  */
00352 int rtnl_qdisc_delete(struct nl_sock *sk, struct rtnl_qdisc *qdisc)
00353 {
00354         struct nl_msg *msg;
00355         int err;
00356 
00357         if ((err = rtnl_qdisc_build_delete_request(qdisc, &msg)) < 0)
00358                 return err;
00359 
00360         err = nl_send_auto_complete(sk, msg);
00361         nlmsg_free(msg);
00362         if (err < 0)
00363                 return err;
00364 
00365         return wait_for_ack(sk);
00366 }
00367 
00368 /** @} */
00369 
00370 /**
00371  * @name Qdisc Cache Management
00372  * @{
00373  */
00374 
00375 /**
00376  * Build a qdisc cache including all qdiscs currently configured in
00377  * the kernel
00378  * @arg sk              Netlink socket.
00379  * @arg result          Pointer to store resulting message.
00380  *
00381  * Allocates a new cache, initializes it properly and updates it to
00382  * include all qdiscs currently configured in the kernel.
00383  *
00384  * @return 0 on success or a negative error code.
00385  */
00386 int rtnl_qdisc_alloc_cache(struct nl_sock *sk, struct nl_cache **result)
00387 {
00388         return nl_cache_alloc_and_fill(&rtnl_qdisc_ops, sk, result);
00389 }
00390 
00391 /**
00392  * Look up qdisc by its parent in the provided cache
00393  * @arg cache           qdisc cache
00394  * @arg ifindex         interface the qdisc is attached to
00395  * @arg parent          parent handle
00396  * @return pointer to qdisc inside the cache or NULL if no match was found.
00397  */
00398 struct rtnl_qdisc * rtnl_qdisc_get_by_parent(struct nl_cache *cache,
00399                                              int ifindex, uint32_t parent)
00400 {
00401         struct rtnl_qdisc *q;
00402 
00403         if (cache->c_ops != &rtnl_qdisc_ops)
00404                 return NULL;
00405 
00406         nl_list_for_each_entry(q, &cache->c_items, ce_list) {
00407                 if (q->q_parent == parent && q->q_ifindex == ifindex) {
00408                         nl_object_get((struct nl_object *) q);
00409                         return q;
00410                 }
00411         }
00412 
00413         return NULL;
00414 }
00415 
00416 /**
00417  * Look up qdisc by its handle in the provided cache
00418  * @arg cache           qdisc cache
00419  * @arg ifindex         interface the qdisc is attached to
00420  * @arg handle          qdisc handle
00421  * @return pointer to qdisc inside the cache or NULL if no match was found.
00422  */
00423 struct rtnl_qdisc * rtnl_qdisc_get(struct nl_cache *cache,
00424                                    int ifindex, uint32_t handle)
00425 {
00426         struct rtnl_qdisc *q;
00427 
00428         if (cache->c_ops != &rtnl_qdisc_ops)
00429                 return NULL;
00430 
00431         nl_list_for_each_entry(q, &cache->c_items, ce_list) {
00432                 if (q->q_handle == handle && q->q_ifindex == ifindex) {
00433                         nl_object_get((struct nl_object *) q);
00434                         return q;
00435                 }
00436         }
00437 
00438         return NULL;
00439 }
00440 
00441 /** @} */
00442 
00443 static struct nl_cache_ops rtnl_qdisc_ops = {
00444         .co_name                = "route/qdisc",
00445         .co_hdrsize             = sizeof(struct tcmsg),
00446         .co_msgtypes            = {
00447                                         { RTM_NEWQDISC, NL_ACT_NEW, "new" },
00448                                         { RTM_DELQDISC, NL_ACT_DEL, "del" },
00449                                         { RTM_GETQDISC, NL_ACT_GET, "get" },
00450                                         END_OF_MSGTYPES_LIST,
00451                                   },
00452         .co_protocol            = NETLINK_ROUTE,
00453         .co_request_update      = qdisc_request_update,
00454         .co_msg_parser          = qdisc_msg_parser,
00455         .co_obj_ops             = &qdisc_obj_ops,
00456 };
00457 
00458 static void __init qdisc_init(void)
00459 {
00460         nl_cache_mngt_register(&rtnl_qdisc_ops);
00461 }
00462 
00463 static void __exit qdisc_exit(void)
00464 {
00465         nl_cache_mngt_unregister(&rtnl_qdisc_ops);
00466 }
00467 
00468 /** @} */