libnl  3.2.3
/build/buildd/libnl3-3.2.3/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-2011 Thomas Graf <tgraf@suug.ch>
00010  */
00011 
00012 /**
00013  * @ingroup tc
00014  * @defgroup qdisc Queueing Disciplines
00015  * @{
00016  */
00017 
00018 #include <netlink-local.h>
00019 #include <netlink-tc.h>
00020 #include <netlink/netlink.h>
00021 #include <netlink/utils.h>
00022 #include <netlink/route/link.h>
00023 #include <netlink/route/tc-api.h>
00024 #include <netlink/route/qdisc.h>
00025 #include <netlink/route/class.h>
00026 #include <netlink/route/classifier.h>
00027 
00028 static struct nl_cache_ops rtnl_qdisc_ops;
00029 static struct nl_object_ops qdisc_obj_ops;
00030 
00031 static int qdisc_msg_parser(struct nl_cache_ops *ops, struct sockaddr_nl *who,
00032                             struct nlmsghdr *n, struct nl_parser_param *pp)
00033 {
00034         struct rtnl_qdisc *qdisc;
00035         int err;
00036 
00037         if (!(qdisc = rtnl_qdisc_alloc()))
00038                 return -NLE_NOMEM;
00039 
00040         if ((err = rtnl_tc_msg_parse(n, TC_CAST(qdisc))) < 0)
00041                 goto errout;
00042 
00043         err = pp->pp_cb(OBJ_CAST(qdisc), pp);
00044 errout:
00045         rtnl_qdisc_put(qdisc);
00046 
00047         return err;
00048 }
00049 
00050 static int qdisc_request_update(struct nl_cache *c, struct nl_sock *sk)
00051 {
00052         struct tcmsg tchdr = {
00053                 .tcm_family = AF_UNSPEC,
00054                 .tcm_ifindex = c->c_iarg1,
00055         };
00056 
00057         return nl_send_simple(sk, RTM_GETQDISC, NLM_F_DUMP, &tchdr,
00058                               sizeof(tchdr));
00059 }
00060 
00061 /**
00062  * @name Allocation/Freeing
00063  * @{
00064  */
00065 
00066 struct rtnl_qdisc *rtnl_qdisc_alloc(void)
00067 {
00068         struct rtnl_tc *tc;
00069 
00070         tc = TC_CAST(nl_object_alloc(&qdisc_obj_ops));
00071         if (tc)
00072                 tc->tc_type = RTNL_TC_TYPE_QDISC;
00073 
00074         return (struct rtnl_qdisc *) tc;
00075 }
00076 
00077 void rtnl_qdisc_put(struct rtnl_qdisc *qdisc)
00078 {
00079         nl_object_put((struct nl_object *) qdisc);
00080 }
00081 
00082 /** @} */
00083 
00084 /**
00085  * @name Addition / Modification / Deletion
00086  * @{
00087  */
00088 
00089 static int build_qdisc_msg(struct rtnl_qdisc *qdisc, int type, int flags,
00090                            struct nl_msg **result)
00091 {
00092         if (!(qdisc->ce_mask & TCA_ATTR_IFINDEX)) {
00093                 APPBUG("ifindex must be specified");
00094                 return -NLE_MISSING_ATTR;
00095         }
00096 
00097         return rtnl_tc_msg_build(TC_CAST(qdisc), type, flags, result);
00098 }
00099 
00100 /**
00101  * Build a netlink message requesting the addition of a qdisc
00102  * @arg qdisc           Qdisc to add 
00103  * @arg flags           Additional netlink message flags
00104  * @arg result          Pointer to store resulting netlink message
00105  *
00106  * The behaviour of this function is identical to rtnl_qdisc_add() with
00107  * the exception that it will not send the message but return it int the
00108  * provided return pointer instead.
00109  *
00110  * @see rtnl_qdisc_add()
00111  *
00112  * @return 0 on success or a negative error code.
00113  */
00114 int rtnl_qdisc_build_add_request(struct rtnl_qdisc *qdisc, int flags,
00115                                  struct nl_msg **result)
00116 {
00117         if (!(qdisc->ce_mask & (TCA_ATTR_HANDLE | TCA_ATTR_PARENT))) {
00118                 APPBUG("handle or parent must be specified");
00119                 return -NLE_MISSING_ATTR;
00120         }
00121 
00122         return build_qdisc_msg(qdisc, RTM_NEWQDISC, flags, result);
00123 }
00124 
00125 /**
00126  * Add qdisc
00127  * @arg sk              Netlink socket
00128  * @arg qdisc           Qdisc to add 
00129  * @arg flags           Additional netlink message flags
00130  *
00131  * Builds a \c RTM_NEWQDISC netlink message requesting the addition
00132  * of a new qdisc and sends the message to the kernel. The configuration
00133  * of the qdisc is derived from the attributes of the specified qdisc.
00134  *
00135  * The following flags may be specified:
00136  *  - \c NLM_F_CREATE:  Create qdisc if it does not exist, otherwise 
00137  *                      -NLE_OBJ_NOTFOUND is returned.
00138  *  - \c NLM_F_REPLACE: If another qdisc is already attached to the
00139  *                      parent, replace it even if the handles mismatch.
00140  *  - \c NLM_F_EXCL:    Return -NLE_EXISTS if a qdisc with matching
00141  *                      handle exists already.
00142  *
00143  * Existing qdiscs with matching handles will be updated, unless the
00144  * flag \c NLM_F_EXCL is specified. If their handles do not match, the
00145  * error -NLE_EXISTS is returned unless the flag \c NLM_F_REPLACE is
00146  * specified in which case the existing qdisc is replaced with the new
00147  * one.  If no matching qdisc exists, it will be created if the flag
00148  * \c NLM_F_CREATE is set, otherwise the error -NLE_OBJ_NOTFOUND is
00149  * returned. 
00150  *
00151  * After sending, the function will wait for the ACK or an eventual
00152  * error message to be received and will therefore block until the
00153  * operation has been completed.
00154  *
00155  * @note Disabling auto-ack (nl_socket_disable_auto_ack()) will cause
00156  *       this function to return immediately after sending. In this case,
00157  *       it is the responsibility of the caller to handle any error
00158  *       messages returned.
00159  *
00160  * @return 0 on success or a negative error code.
00161  */
00162 int rtnl_qdisc_add(struct nl_sock *sk, struct rtnl_qdisc *qdisc, int flags)
00163 {
00164         struct nl_msg *msg;
00165         int err;
00166 
00167         if ((err = rtnl_qdisc_build_add_request(qdisc, flags, &msg)) < 0)
00168                 return err;
00169 
00170         return nl_send_sync(sk, msg);
00171 }
00172 
00173 /**
00174  * Build netlink message requesting the update of a qdisc
00175  * @arg qdisc           Qdisc to update
00176  * @arg new             Qdisc with updated attributes
00177  * @arg flags           Additional netlink message flags
00178  * @arg result          Pointer to store resulting netlink message
00179  *
00180  * The behaviour of this function is identical to rtnl_qdisc_update() with
00181  * the exception that it will not send the message but return it in the
00182  * provided return pointer instead.
00183  *
00184  * @see rtnl_qdisc_update()
00185  *
00186  * @return 0 on success or a negative error code.
00187  */
00188 int rtnl_qdisc_build_update_request(struct rtnl_qdisc *qdisc,
00189                                     struct rtnl_qdisc *new, int flags,
00190                                     struct nl_msg **result)
00191 {
00192         if (flags & (NLM_F_CREATE | NLM_F_EXCL)) {
00193                 APPBUG("NLM_F_CREATE and NLM_F_EXCL may not be used here, "
00194                        "use rtnl_qdisc_add()");
00195                 return -NLE_INVAL;
00196         }
00197 
00198         if (!(qdisc->ce_mask & TCA_ATTR_IFINDEX)) {
00199                 APPBUG("ifindex must be specified");
00200                 return -NLE_MISSING_ATTR;
00201         }
00202 
00203         if (!(qdisc->ce_mask & (TCA_ATTR_HANDLE | TCA_ATTR_PARENT))) {
00204                 APPBUG("handle or parent must be specified");
00205                 return -NLE_MISSING_ATTR;
00206         }
00207 
00208         rtnl_tc_set_ifindex(TC_CAST(new), qdisc->q_ifindex);
00209 
00210         if (qdisc->ce_mask & TCA_ATTR_HANDLE)
00211                 rtnl_tc_set_handle(TC_CAST(new), qdisc->q_handle);
00212 
00213         if (qdisc->ce_mask & TCA_ATTR_PARENT)
00214                 rtnl_tc_set_parent(TC_CAST(new), qdisc->q_parent);
00215 
00216         return build_qdisc_msg(new, RTM_NEWQDISC, flags, result);
00217 }
00218 
00219 /**
00220  * Update qdisc
00221  * @arg sk              Netlink socket
00222  * @arg qdisc           Qdisc to update
00223  * @arg new             Qdisc with updated attributes
00224  * @arg flags           Additional netlink message flags
00225  *
00226  * Builds a \c RTM_NEWQDISC netlink message requesting the update
00227  * of an existing qdisc and sends the message to the kernel.
00228  *
00229  * This function is a varation of rtnl_qdisc_add() to update qdiscs
00230  * if the qdisc to be updated is available as qdisc object. The
00231  * behaviour is identical to the one of rtnl_qdisc_add except that
00232  * before constructing the message, it copies the \c ifindex,
00233  * \c handle, and \c parent from the original \p qdisc to the \p new
00234  * qdisc.
00235  *
00236  * After sending, the function will wait for the ACK or an eventual
00237  * error message to be received and will therefore block until the
00238  * operation has been completed.
00239  *
00240  * @note Disabling auto-ack (nl_socket_disable_auto_ack()) will cause
00241  *       this function to return immediately after sending. In this case,
00242  *       it is the responsibility of the caller to handle any error
00243  *       messages returned.
00244  *
00245  * @return 0 on success or a negative error code.
00246  */
00247 int rtnl_qdisc_update(struct nl_sock *sk, struct rtnl_qdisc *qdisc,
00248                       struct rtnl_qdisc *new, int flags)
00249 {
00250         struct nl_msg *msg;
00251         int err;
00252 
00253         err = rtnl_qdisc_build_update_request(qdisc, new, flags, &msg);
00254         if (err < 0)
00255                 return err;
00256 
00257         return nl_send_sync(sk, msg);
00258 }
00259 
00260 /**
00261  * Build netlink message requesting the deletion of a qdisc
00262  * @arg qdisc           Qdisc to delete
00263  * @arg result          Pointer to store resulting netlink message
00264  *
00265  * The behaviour of this function is identical to rtnl_qdisc_delete() with
00266  * the exception that it will not send the message but return it in the
00267  * provided return pointer instead.
00268  *
00269  * @see rtnl_qdisc_delete()
00270  *
00271  * @return 0 on success or a negative error code.
00272  */
00273 int rtnl_qdisc_build_delete_request(struct rtnl_qdisc *qdisc,
00274                                     struct nl_msg **result)
00275 {
00276         struct nl_msg *msg;
00277         struct tcmsg tchdr;
00278         int required = TCA_ATTR_IFINDEX | TCA_ATTR_PARENT;
00279 
00280         if ((qdisc->ce_mask & required) != required) {
00281                 APPBUG("ifindex and parent must be specified");
00282                 return -NLE_MISSING_ATTR;
00283         }
00284 
00285         if (!(msg = nlmsg_alloc_simple(RTM_DELQDISC, 0)))
00286                 return -NLE_NOMEM;
00287 
00288         memset(&tchdr, 0, sizeof(tchdr));
00289 
00290         tchdr.tcm_family = AF_UNSPEC;
00291         tchdr.tcm_ifindex = qdisc->q_ifindex;
00292         tchdr.tcm_parent = qdisc->q_parent;
00293 
00294         if (qdisc->ce_mask & TCA_ATTR_HANDLE)
00295                 tchdr.tcm_handle = qdisc->q_handle;
00296 
00297         if (nlmsg_append(msg, &tchdr, sizeof(tchdr), NLMSG_ALIGNTO) < 0)
00298                 goto nla_put_failure;
00299 
00300         if (qdisc->ce_mask & TCA_ATTR_KIND)
00301                 NLA_PUT_STRING(msg, TCA_KIND, qdisc->q_kind);
00302 
00303         *result = msg;
00304         return 0;
00305 
00306 nla_put_failure:
00307         nlmsg_free(msg);
00308         return -NLE_MSGSIZE;
00309 }
00310 
00311 /**
00312  * Delete qdisc
00313  * @arg sk              Netlink socket
00314  * @arg qdisc           Qdisc to add 
00315  *
00316  * Builds a \c RTM_NEWQDISC netlink message requesting the deletion
00317  * of a qdisc and sends the message to the kernel.
00318  *
00319  * The message is constructed out of the following attributes:
00320  * - \c ifindex and \c parent
00321  * - \c handle (optional, must match if provided)
00322  * - \c kind (optional, must match if provided)
00323  *
00324  * All other qdisc attributes including all qdisc type specific
00325  * attributes are ignored.
00326  *
00327  * After sending, the function will wait for the ACK or an eventual
00328  * error message to be received and will therefore block until the
00329  * operation has been completed.
00330  *
00331  * @note It is not possible to delete default qdiscs.
00332  *
00333  * @note Disabling auto-ack (nl_socket_disable_auto_ack()) will cause
00334  *       this function to return immediately after sending. In this case,
00335  *       it is the responsibility of the caller to handle any error
00336  *       messages returned.
00337  *
00338  * @return 0 on success or a negative error code.
00339  */
00340 int rtnl_qdisc_delete(struct nl_sock *sk, struct rtnl_qdisc *qdisc)
00341 {
00342         struct nl_msg *msg;
00343         int err;
00344 
00345         if ((err = rtnl_qdisc_build_delete_request(qdisc, &msg)) < 0)
00346                 return err;
00347 
00348         return nl_send_sync(sk, msg);
00349 }
00350 
00351 /** @} */
00352 
00353 /**
00354  * @name Cache Related Functions
00355  * @{
00356  */
00357 
00358 /**
00359  * Allocate a cache and fill it with all configured qdiscs
00360  * @arg sk              Netlink socket
00361  * @arg result          Pointer to store the created cache
00362  *
00363  * Allocates a new qdisc cache and fills it with a list of all configured
00364  * qdiscs on all network devices. Release the cache with nl_cache_free().
00365  *
00366  * @return 0 on success or a negative error code.
00367  */
00368 int rtnl_qdisc_alloc_cache(struct nl_sock *sk, struct nl_cache **result)
00369 {
00370         return nl_cache_alloc_and_fill(&rtnl_qdisc_ops, sk, result);
00371 }
00372 
00373 /**
00374  * Search qdisc by interface index and parent
00375  * @arg cache           Qdisc cache
00376  * @arg ifindex         Interface index
00377  * @arg parent          Handle of parent qdisc
00378  *
00379  * Searches a qdisc cache previously allocated with rtnl_qdisc_alloc_cache()
00380  * and searches for a qdisc matching the interface index and parent qdisc.
00381  *
00382  * The reference counter is incremented before returning the qdisc, therefore
00383  * the reference must be given back with rtnl_qdisc_put() after usage.
00384  *
00385  * @return pointer to qdisc inside the cache or NULL if no match was found.
00386  */
00387 struct rtnl_qdisc *rtnl_qdisc_get_by_parent(struct nl_cache *cache,
00388                                             int ifindex, uint32_t parent)
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_parent == parent && 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  * Search qdisc by interface index and handle
00407  * @arg cache           Qdisc cache
00408  * @arg ifindex         Interface index
00409  * @arg handle          Handle
00410  *
00411  * Searches a qdisc cache previously allocated with rtnl_qdisc_alloc_cache()
00412  * and searches for a qdisc matching the interface index and handle.
00413  *
00414  * The reference counter is incremented before returning the qdisc, therefore
00415  * the reference must be given back with rtnl_qdisc_put() after usage.
00416  *
00417  * @return Qdisc or NULL if no match was found.
00418  */
00419 struct rtnl_qdisc *rtnl_qdisc_get(struct nl_cache *cache, int ifindex,
00420                                   uint32_t handle)
00421 {
00422         struct rtnl_qdisc *q;
00423 
00424         if (cache->c_ops != &rtnl_qdisc_ops)
00425                 return NULL;
00426 
00427         nl_list_for_each_entry(q, &cache->c_items, ce_list) {
00428                 if (q->q_handle == handle && q->q_ifindex == ifindex) {
00429                         nl_object_get((struct nl_object *) q);
00430                         return q;
00431                 }
00432         }
00433 
00434         return NULL;
00435 }
00436 
00437 /** @} */
00438 
00439 /**
00440  * @name Deprecated Functions
00441  * @{
00442  */
00443 
00444 /**
00445  * Call a callback for each child class of a qdisc (deprecated)
00446  *
00447  * @deprecated Use of this function is deprecated, it does not allow
00448  *             to handle the out of memory situation that can occur.
00449  */
00450 void rtnl_qdisc_foreach_child(struct rtnl_qdisc *qdisc, struct nl_cache *cache,
00451                               void (*cb)(struct nl_object *, void *), void *arg)
00452 {
00453         struct rtnl_class *filter;
00454         
00455         filter = rtnl_class_alloc();
00456         if (!filter)
00457                 return;
00458 
00459         rtnl_tc_set_parent(TC_CAST(filter), qdisc->q_handle);
00460         rtnl_tc_set_ifindex(TC_CAST(filter), qdisc->q_ifindex);
00461         rtnl_tc_set_kind(TC_CAST(filter), qdisc->q_kind);
00462 
00463         nl_cache_foreach_filter(cache, OBJ_CAST(filter), cb, arg);
00464 
00465         rtnl_class_put(filter);
00466 }
00467 
00468 /**
00469  * Call a callback for each filter attached to the qdisc (deprecated)
00470  *
00471  * @deprecated Use of this function is deprecated, it does not allow
00472  *             to handle the out of memory situation that can occur.
00473  */
00474 void rtnl_qdisc_foreach_cls(struct rtnl_qdisc *qdisc, struct nl_cache *cache,
00475                             void (*cb)(struct nl_object *, void *), void *arg)
00476 {
00477         struct rtnl_cls *filter;
00478 
00479         if (!(filter = rtnl_cls_alloc()))
00480                 return;
00481 
00482         rtnl_tc_set_ifindex(TC_CAST(filter), qdisc->q_ifindex);
00483         rtnl_tc_set_parent(TC_CAST(filter), qdisc->q_parent);
00484 
00485         nl_cache_foreach_filter(cache, OBJ_CAST(filter), cb, arg);
00486         rtnl_cls_put(filter);
00487 }
00488 
00489 /**
00490  * Build a netlink message requesting the update of a qdisc
00491  *
00492  * @deprecated Use of this function is deprecated in favour of
00493  *             rtnl_qdisc_build_update_request() due to the missing
00494  *             possibility of specifying additional flags.
00495  */
00496 int rtnl_qdisc_build_change_request(struct rtnl_qdisc *qdisc,
00497                                     struct rtnl_qdisc *new,
00498                                     struct nl_msg **result)
00499 {
00500         return rtnl_qdisc_build_update_request(qdisc, new, NLM_F_REPLACE,
00501                                                result);
00502 }
00503 
00504 /**
00505  * Change attributes of a qdisc
00506  *
00507  * @deprecated Use of this function is deprecated in favour of
00508  *             rtnl_qdisc_update() due to the missing possibility of
00509  *             specifying additional flags.
00510  */
00511 int rtnl_qdisc_change(struct nl_sock *sk, struct rtnl_qdisc *qdisc,
00512                       struct rtnl_qdisc *new)
00513 {
00514         return rtnl_qdisc_update(sk, qdisc, new, NLM_F_REPLACE);
00515 }
00516 
00517 /** @} */
00518 
00519 static void qdisc_dump_details(struct rtnl_tc *tc, struct nl_dump_params *p)
00520 {
00521         struct rtnl_qdisc *qdisc = (struct rtnl_qdisc *) tc;
00522 
00523         nl_dump(p, "refcnt %u ", qdisc->q_info);
00524 }
00525 
00526 static struct rtnl_tc_type_ops qdisc_ops = {
00527         .tt_type                = RTNL_TC_TYPE_QDISC,
00528         .tt_dump_prefix         = "qdisc",
00529         .tt_dump = {
00530             [NL_DUMP_DETAILS]   = qdisc_dump_details,
00531         },
00532 };
00533 
00534 static struct nl_cache_ops rtnl_qdisc_ops = {
00535         .co_name                = "route/qdisc",
00536         .co_hdrsize             = sizeof(struct tcmsg),
00537         .co_msgtypes            = {
00538                                         { RTM_NEWQDISC, NL_ACT_NEW, "new" },
00539                                         { RTM_DELQDISC, NL_ACT_DEL, "del" },
00540                                         { RTM_GETQDISC, NL_ACT_GET, "get" },
00541                                         END_OF_MSGTYPES_LIST,
00542                                   },
00543         .co_protocol            = NETLINK_ROUTE,
00544         .co_request_update      = qdisc_request_update,
00545         .co_msg_parser          = qdisc_msg_parser,
00546         .co_obj_ops             = &qdisc_obj_ops,
00547 };
00548 
00549 static struct nl_object_ops qdisc_obj_ops = {
00550         .oo_name                = "route/qdisc",
00551         .oo_size                = sizeof(struct rtnl_qdisc),
00552         .oo_free_data           = rtnl_tc_free_data,
00553         .oo_clone               = rtnl_tc_clone,
00554         .oo_dump = {
00555             [NL_DUMP_LINE]      = rtnl_tc_dump_line,
00556             [NL_DUMP_DETAILS]   = rtnl_tc_dump_details,
00557             [NL_DUMP_STATS]     = rtnl_tc_dump_stats,
00558         },
00559         .oo_compare             = rtnl_tc_compare,
00560         .oo_id_attrs            = (TCA_ATTR_IFINDEX | TCA_ATTR_HANDLE),
00561 };
00562 
00563 static void __init qdisc_init(void)
00564 {
00565         rtnl_tc_type_register(&qdisc_ops);
00566         nl_cache_mngt_register(&rtnl_qdisc_ops);
00567 }
00568 
00569 static void __exit qdisc_exit(void)
00570 {
00571         nl_cache_mngt_unregister(&rtnl_qdisc_ops);
00572         rtnl_tc_type_unregister(&qdisc_ops);
00573 }
00574 
00575 /** @} */