libnl
3.2.3
|
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 /** @} */