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-2011 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-api.h>
00091 #include <netlink/route/qdisc.h>
00092 #include <netlink/route/class.h>
00093 #include <netlink/route/classifier.h>
00094 
00095 static struct nl_cache_ops rtnl_qdisc_ops;
00096 
00097 static int qdisc_msg_parser(struct nl_cache_ops *ops, struct sockaddr_nl *who,
00098                             struct nlmsghdr *n, struct nl_parser_param *pp)
00099 {
00100         struct rtnl_qdisc *qdisc;
00101         int err;
00102 
00103         if (!(qdisc = rtnl_qdisc_alloc()))
00104                 return -NLE_NOMEM;
00105 
00106         if ((err = rtnl_tc_msg_parse(n, TC_CAST(qdisc))) < 0)
00107                 goto errout;
00108 
00109         err = pp->pp_cb(OBJ_CAST(qdisc), pp);
00110 errout:
00111         rtnl_qdisc_put(qdisc);
00112 
00113         return err;
00114 }
00115 
00116 static int qdisc_request_update(struct nl_cache *c, struct nl_sock *sk)
00117 {
00118         struct tcmsg tchdr = {
00119                 .tcm_family = AF_UNSPEC,
00120                 .tcm_ifindex = c->c_iarg1,
00121         };
00122 
00123         return nl_send_simple(sk, RTM_GETQDISC, NLM_F_DUMP, &tchdr,
00124                               sizeof(tchdr));
00125 }
00126 
00127 /**
00128  * @name QDisc Addition
00129  * @{
00130  */
00131 
00132 static int qdisc_build(struct rtnl_qdisc *qdisc, int type, int flags,
00133                        struct nl_msg **result)
00134 {
00135         return rtnl_tc_msg_build(TC_CAST(qdisc), type, flags, result);
00136 
00137 #if 0
00138         /* Some qdiscs don't accept properly nested messages (e.g. netem). To
00139          * accomodate for this, they can complete the message themselves.
00140          */             
00141         else if (qops && qops->qo_build_msg) {
00142                 err = qops->qo_build_msg(qdisc, *result);
00143                 if (err < 0)
00144                         goto errout;
00145         }
00146 #endif
00147 }
00148 
00149 /**
00150  * Build a netlink message to add a new qdisc
00151  * @arg qdisc           qdisc to add 
00152  * @arg flags           additional netlink message flags
00153  * @arg result          Pointer to store resulting message.
00154  *
00155  * Builds a new netlink message requesting an addition of a qdisc.
00156  * The netlink message header isn't fully equipped with all relevant
00157  * fields and must be sent out via nl_send_auto_complete() or
00158  * supplemented as needed. 
00159  *
00160  * Common message flags used:
00161  *  - NLM_F_REPLACE - replace a potential existing qdisc
00162  *
00163  * @return 0 on success or a negative error code.
00164  */
00165 int rtnl_qdisc_build_add_request(struct rtnl_qdisc *qdisc, int flags,
00166                                  struct nl_msg **result)
00167 {
00168         return qdisc_build(qdisc, RTM_NEWQDISC, NLM_F_CREATE | flags, result);
00169 }
00170 
00171 /**
00172  * Add a new qdisc
00173  * @arg sk              Netlink socket.
00174  * @arg qdisc           qdisc to delete
00175  * @arg flags           additional netlink message flags
00176  *
00177  * Builds a netlink message by calling rtnl_qdisc_build_add_request(),
00178  * sends the request to the kernel and waits for the ACK to be
00179  * received and thus blocks until the request has been processed.
00180  *
00181  * Common message flags used:
00182  *  - NLM_F_REPLACE - replace a potential existing qdisc
00183  *
00184  * @return 0 on success or a negative error code
00185  */
00186 int rtnl_qdisc_add(struct nl_sock *sk, struct rtnl_qdisc *qdisc,
00187                    int flags)
00188 {
00189         struct nl_msg *msg;
00190         int err;
00191 
00192         if ((err = rtnl_qdisc_build_add_request(qdisc, flags, &msg)) < 0)
00193                 return err;
00194 
00195         err = nl_send_auto_complete(sk, msg);
00196         nlmsg_free(msg);
00197         if (err < 0)
00198                 return err;
00199 
00200         return wait_for_ack(sk);
00201 }
00202 
00203 /** @} */
00204 
00205 /**
00206  * @name QDisc Modification
00207  * @{
00208  */
00209 
00210 /**
00211  * Build a netlink message to change attributes of a existing qdisc
00212  * @arg qdisc           qdisc to change
00213  * @arg new             new qdisc attributes
00214  * @arg result          Pointer to store resulting message.
00215  *
00216  * Builds a new netlink message requesting an change of qdisc
00217  * attributes. The netlink message header isn't fully equipped
00218  * with all relevant fields and must be sent out via
00219  * nl_send_auto_complete() or supplemented as needed. 
00220  *
00221  * @return 0 on success or a negative error code.
00222  */
00223 int rtnl_qdisc_build_change_request(struct rtnl_qdisc *qdisc,
00224                                     struct rtnl_qdisc *new,
00225                                     struct nl_msg **result)
00226 {
00227         return qdisc_build(qdisc, RTM_NEWQDISC, NLM_F_REPLACE, result);
00228 }
00229 
00230 /**
00231  * Change attributes of a qdisc
00232  * @arg sk              Netlink socket.
00233  * @arg qdisc           qdisc to change
00234  * @arg new             new qdisc attributes
00235  *
00236  * Builds a netlink message by calling rtnl_qdisc_build_change_request(),
00237  * sends the request to the kernel and waits for the ACK to be
00238  * received and thus blocks until the request has been processed.
00239  *
00240  * @return 0 on success or a negative error code
00241  */
00242 int rtnl_qdisc_change(struct nl_sock *sk, struct rtnl_qdisc *qdisc,
00243                       struct rtnl_qdisc *new)
00244 {
00245         struct nl_msg *msg;
00246         int err;
00247 
00248         if ((err = rtnl_qdisc_build_change_request(qdisc, new, &msg)) < 0)
00249                 return err;
00250 
00251         err = nl_send_auto_complete(sk, msg);
00252         nlmsg_free(msg);
00253         if (err < 0)
00254                 return err;
00255 
00256         return wait_for_ack(sk);
00257 }
00258 
00259 /** @} */
00260 
00261 /**
00262  * @name QDisc Deletion
00263  * @{
00264  */
00265 
00266 /**
00267  * Build a netlink request message to delete a qdisc
00268  * @arg qdisc           qdisc to delete
00269  * @arg result          Pointer to store resulting message.
00270  *
00271  * Builds a new netlink message requesting a deletion of a qdisc.
00272  * The netlink message header isn't fully equipped with all relevant
00273  * fields and must thus be sent out via nl_send_auto_complete()
00274  * or supplemented as needed.
00275  *
00276  * @return 0 on success or a negative error code.
00277  */
00278 int rtnl_qdisc_build_delete_request(struct rtnl_qdisc *qdisc,
00279                                     struct nl_msg **result)
00280 {
00281         struct nl_msg *msg;
00282         struct tcmsg tchdr;
00283         int required = TCA_ATTR_IFINDEX | TCA_ATTR_PARENT;
00284 
00285         if ((qdisc->ce_mask & required) != required)
00286                 BUG();
00287 
00288         msg = nlmsg_alloc_simple(RTM_DELQDISC, 0);
00289         if (!msg)
00290                 return -NLE_NOMEM;
00291 
00292         tchdr.tcm_family = AF_UNSPEC;
00293         tchdr.tcm_handle = qdisc->q_handle;
00294         tchdr.tcm_parent = qdisc->q_parent;
00295         tchdr.tcm_ifindex = qdisc->q_ifindex;
00296         if (nlmsg_append(msg, &tchdr, sizeof(tchdr), NLMSG_ALIGNTO) < 0) {
00297                 nlmsg_free(msg);
00298                 return -NLE_MSGSIZE;
00299         }
00300 
00301         *result = msg;
00302         return 0;
00303 }
00304 
00305 /**
00306  * Delete a qdisc
00307  * @arg sk              Netlink socket.
00308  * @arg qdisc           qdisc to delete
00309  *
00310  * Builds a netlink message by calling rtnl_qdisc_build_delete_request(),
00311  * sends the request to the kernel and waits for the ACK to be
00312  * received and thus blocks until the request has been processed.
00313  *
00314  * @return 0 on success or a negative error code
00315  */
00316 int rtnl_qdisc_delete(struct nl_sock *sk, struct rtnl_qdisc *qdisc)
00317 {
00318         struct nl_msg *msg;
00319         int err;
00320 
00321         if ((err = rtnl_qdisc_build_delete_request(qdisc, &msg)) < 0)
00322                 return err;
00323 
00324         err = nl_send_auto_complete(sk, msg);
00325         nlmsg_free(msg);
00326         if (err < 0)
00327                 return err;
00328 
00329         return wait_for_ack(sk);
00330 }
00331 
00332 /** @} */
00333 
00334 /**
00335  * @name Qdisc Cache Management
00336  * @{
00337  */
00338 
00339 /**
00340  * Build a qdisc cache including all qdiscs currently configured in
00341  * the kernel
00342  * @arg sk              Netlink socket.
00343  * @arg result          Pointer to store resulting message.
00344  *
00345  * Allocates a new cache, initializes it properly and updates it to
00346  * include all qdiscs currently configured in the kernel.
00347  *
00348  * @return 0 on success or a negative error code.
00349  */
00350 int rtnl_qdisc_alloc_cache(struct nl_sock *sk, struct nl_cache **result)
00351 {
00352         return nl_cache_alloc_and_fill(&rtnl_qdisc_ops, sk, result);
00353 }
00354 
00355 /**
00356  * Look up qdisc by its parent in the provided cache
00357  * @arg cache           qdisc cache
00358  * @arg ifindex         interface the qdisc is attached to
00359  * @arg parent          parent handle
00360  * @return pointer to qdisc inside the cache or NULL if no match was found.
00361  */
00362 struct rtnl_qdisc * rtnl_qdisc_get_by_parent(struct nl_cache *cache,
00363                                              int ifindex, uint32_t parent)
00364 {
00365         struct rtnl_qdisc *q;
00366 
00367         if (cache->c_ops != &rtnl_qdisc_ops)
00368                 return NULL;
00369 
00370         nl_list_for_each_entry(q, &cache->c_items, ce_list) {
00371                 if (q->q_parent == parent && q->q_ifindex == ifindex) {
00372                         nl_object_get((struct nl_object *) q);
00373                         return q;
00374                 }
00375         }
00376 
00377         return NULL;
00378 }
00379 
00380 /**
00381  * Look up qdisc by its handle in the provided cache
00382  * @arg cache           qdisc cache
00383  * @arg ifindex         interface the qdisc is attached to
00384  * @arg handle          qdisc handle
00385  * @return pointer to qdisc inside the cache or NULL if no match was found.
00386  */
00387 struct rtnl_qdisc * rtnl_qdisc_get(struct nl_cache *cache,
00388                                    int ifindex, uint32_t handle)
00389 {
00390         struct rtnl_qdisc *q;
00391 
00392         if (cache->c_ops != &rtnl_qdisc_ops)
00393                 return NULL;
00394 
00395         nl_list_for_each_entry(q, &cache->c_items, ce_list) {
00396                 if (q->q_handle == handle && q->q_ifindex == ifindex) {
00397                         nl_object_get((struct nl_object *) q);
00398                         return q;
00399                 }
00400         }
00401 
00402         return NULL;
00403 }
00404 
00405 /** @} */
00406 
00407 /**
00408  * @name Allocation/Freeing
00409  * @{
00410  */
00411 
00412 struct rtnl_qdisc *rtnl_qdisc_alloc(void)
00413 {
00414         struct rtnl_tc *tc;
00415 
00416         tc = TC_CAST(nl_object_alloc(&qdisc_obj_ops));
00417         if (tc)
00418                 tc->tc_type = RTNL_TC_TYPE_QDISC;
00419 
00420         return (struct rtnl_qdisc *) tc;
00421 }
00422 
00423 void rtnl_qdisc_put(struct rtnl_qdisc *qdisc)
00424 {
00425         nl_object_put((struct nl_object *) qdisc);
00426 }
00427 
00428 /** @} */
00429 
00430 /**
00431  * @name Iterators
00432  * @{
00433  */
00434 
00435 /**
00436  * Call a callback for each child class of a qdisc
00437  * @arg qdisc           the parent qdisc
00438  * @arg cache           a class cache including all classes of the interface
00439  *                      the specified qdisc is attached to
00440  * @arg cb              callback function
00441  * @arg arg             argument to be passed to callback function
00442  */
00443 void rtnl_qdisc_foreach_child(struct rtnl_qdisc *qdisc, struct nl_cache *cache,
00444                               void (*cb)(struct nl_object *, void *), void *arg)
00445 {
00446         struct rtnl_class *filter;
00447         
00448         filter = rtnl_class_alloc();
00449         if (!filter)
00450                 return;
00451 
00452         rtnl_tc_set_parent(TC_CAST(filter), qdisc->q_handle);
00453         rtnl_tc_set_ifindex(TC_CAST(filter), qdisc->q_ifindex);
00454         rtnl_tc_set_kind(TC_CAST(filter), qdisc->q_kind);
00455 
00456         nl_cache_foreach_filter(cache, OBJ_CAST(filter), cb, arg);
00457 
00458         rtnl_class_put(filter);
00459 }
00460 
00461 /**
00462  * Call a callback for each filter attached to the qdisc
00463  * @arg qdisc           the parent qdisc
00464  * @arg cache           a filter cache including at least all the filters
00465  *                      attached to the specified qdisc
00466  * @arg cb              callback function
00467  * @arg arg             argument to be passed to callback function
00468  */
00469 void rtnl_qdisc_foreach_cls(struct rtnl_qdisc *qdisc, struct nl_cache *cache,
00470                             void (*cb)(struct nl_object *, void *), void *arg)
00471 {
00472         struct rtnl_cls *filter;
00473 
00474         filter = rtnl_cls_alloc();
00475         if (!filter)
00476                 return;
00477 
00478         rtnl_tc_set_ifindex((struct rtnl_tc *) filter, qdisc->q_ifindex);
00479         rtnl_tc_set_parent((struct rtnl_tc *) filter, qdisc->q_parent);
00480 
00481         nl_cache_foreach_filter(cache, (struct nl_object *) filter, cb, arg);
00482         rtnl_cls_put(filter);
00483 }
00484 
00485 /** @} */
00486 
00487 static void qdisc_dump_details(struct rtnl_tc *tc, struct nl_dump_params *p)
00488 {
00489         struct rtnl_qdisc *qdisc = (struct rtnl_qdisc *) tc;
00490 
00491         nl_dump(p, "refcnt %u ", qdisc->q_info);
00492 }
00493 
00494 static struct rtnl_tc_type_ops qdisc_ops = {
00495         .tt_type                = RTNL_TC_TYPE_QDISC,
00496         .tt_dump_prefix         = "qdisc",
00497         .tt_dump = {
00498             [NL_DUMP_DETAILS]   = qdisc_dump_details,
00499         },
00500 };
00501 
00502 static struct nl_cache_ops rtnl_qdisc_ops = {
00503         .co_name                = "route/qdisc",
00504         .co_hdrsize             = sizeof(struct tcmsg),
00505         .co_msgtypes            = {
00506                                         { RTM_NEWQDISC, NL_ACT_NEW, "new" },
00507                                         { RTM_DELQDISC, NL_ACT_DEL, "del" },
00508                                         { RTM_GETQDISC, NL_ACT_GET, "get" },
00509                                         END_OF_MSGTYPES_LIST,
00510                                   },
00511         .co_protocol            = NETLINK_ROUTE,
00512         .co_request_update      = qdisc_request_update,
00513         .co_msg_parser          = qdisc_msg_parser,
00514         .co_obj_ops             = &qdisc_obj_ops,
00515 };
00516 
00517 struct nl_object_ops qdisc_obj_ops = {
00518         .oo_name                = "route/qdisc",
00519         .oo_size                = sizeof(struct rtnl_qdisc),
00520         .oo_free_data           = rtnl_tc_free_data,
00521         .oo_clone               = rtnl_tc_clone,
00522         .oo_dump = {
00523             [NL_DUMP_LINE]      = rtnl_tc_dump_line,
00524             [NL_DUMP_DETAILS]   = rtnl_tc_dump_details,
00525             [NL_DUMP_STATS]     = rtnl_tc_dump_stats,
00526         },
00527         .oo_compare             = rtnl_tc_compare,
00528         .oo_id_attrs            = (TCA_ATTR_IFINDEX | TCA_ATTR_HANDLE),
00529 };
00530 
00531 static void __init qdisc_init(void)
00532 {
00533         rtnl_tc_type_register(&qdisc_ops);
00534         nl_cache_mngt_register(&rtnl_qdisc_ops);
00535 }
00536 
00537 static void __exit qdisc_exit(void)
00538 {
00539         nl_cache_mngt_unregister(&rtnl_qdisc_ops);
00540         rtnl_tc_type_unregister(&qdisc_ops);
00541 }
00542 
00543 /** @} */