1 /* 2 * lib/route/qdisc.c Queueing Disciplines 3 * 4 * This library is free software; you can redistribute it and/or 5 * modify it under the terms of the GNU Lesser General Public 6 * License as published by the Free Software Foundation version 2.1 7 * of the License. 8 * 9 * Copyright (c) 2003-2011 Thomas Graf <tgraf (at) suug.ch> 10 */ 11 12 /** 13 * @ingroup tc 14 * @defgroup qdisc Queueing Disciplines 15 * @{ 16 */ 17 18 #include <netlink-private/netlink.h> 19 #include <netlink-private/tc.h> 20 #include <netlink/netlink.h> 21 #include <netlink/utils.h> 22 #include <netlink/route/link.h> 23 #include <netlink-private/route/tc-api.h> 24 #include <netlink/route/qdisc.h> 25 #include <netlink/route/class.h> 26 #include <netlink/route/classifier.h> 27 28 static struct nl_cache_ops rtnl_qdisc_ops; 29 static struct nl_object_ops qdisc_obj_ops; 30 31 static int qdisc_msg_parser(struct nl_cache_ops *ops, struct sockaddr_nl *who, 32 struct nlmsghdr *n, struct nl_parser_param *pp) 33 { 34 struct rtnl_qdisc *qdisc; 35 int err; 36 37 if (!(qdisc = rtnl_qdisc_alloc())) 38 return -NLE_NOMEM; 39 40 if ((err = rtnl_tc_msg_parse(n, TC_CAST(qdisc))) < 0) 41 goto errout; 42 43 err = pp->pp_cb(OBJ_CAST(qdisc), pp); 44 errout: 45 rtnl_qdisc_put(qdisc); 46 47 return err; 48 } 49 50 static int qdisc_request_update(struct nl_cache *c, struct nl_sock *sk) 51 { 52 struct tcmsg tchdr = { 53 .tcm_family = AF_UNSPEC, 54 .tcm_ifindex = c->c_iarg1, 55 }; 56 57 return nl_send_simple(sk, RTM_GETQDISC, NLM_F_DUMP, &tchdr, 58 sizeof(tchdr)); 59 } 60 61 /** 62 * @name Allocation/Freeing 63 * @{ 64 */ 65 66 struct rtnl_qdisc *rtnl_qdisc_alloc(void) 67 { 68 struct rtnl_tc *tc; 69 70 tc = TC_CAST(nl_object_alloc(&qdisc_obj_ops)); 71 if (tc) 72 tc->tc_type = RTNL_TC_TYPE_QDISC; 73 74 return (struct rtnl_qdisc *) tc; 75 } 76 77 void rtnl_qdisc_put(struct rtnl_qdisc *qdisc) 78 { 79 nl_object_put((struct nl_object *) qdisc); 80 } 81 82 /** @} */ 83 84 /** 85 * @name Addition / Modification / Deletion 86 * @{ 87 */ 88 89 static int build_qdisc_msg(struct rtnl_qdisc *qdisc, int type, int flags, 90 struct nl_msg **result) 91 { 92 if (!(qdisc->ce_mask & TCA_ATTR_IFINDEX)) { 93 APPBUG("ifindex must be specified"); 94 return -NLE_MISSING_ATTR; 95 } 96 97 return rtnl_tc_msg_build(TC_CAST(qdisc), type, flags, result); 98 } 99 100 /** 101 * Build a netlink message requesting the addition of a qdisc 102 * @arg qdisc Qdisc to add 103 * @arg flags Additional netlink message flags 104 * @arg result Pointer to store resulting netlink message 105 * 106 * The behaviour of this function is identical to rtnl_qdisc_add() with 107 * the exception that it will not send the message but return it int the 108 * provided return pointer instead. 109 * 110 * @see rtnl_qdisc_add() 111 * 112 * @return 0 on success or a negative error code. 113 */ 114 int rtnl_qdisc_build_add_request(struct rtnl_qdisc *qdisc, int flags, 115 struct nl_msg **result) 116 { 117 if (!(qdisc->ce_mask & (TCA_ATTR_HANDLE | TCA_ATTR_PARENT))) { 118 APPBUG("handle or parent must be specified"); 119 return -NLE_MISSING_ATTR; 120 } 121 122 return build_qdisc_msg(qdisc, RTM_NEWQDISC, flags, result); 123 } 124 125 /** 126 * Add qdisc 127 * @arg sk Netlink socket 128 * @arg qdisc Qdisc to add 129 * @arg flags Additional netlink message flags 130 * 131 * Builds a \c RTM_NEWQDISC netlink message requesting the addition 132 * of a new qdisc and sends the message to the kernel. The configuration 133 * of the qdisc is derived from the attributes of the specified qdisc. 134 * 135 * The following flags may be specified: 136 * - \c NLM_F_CREATE: Create qdisc if it does not exist, otherwise 137 * -NLE_OBJ_NOTFOUND is returned. 138 * - \c NLM_F_REPLACE: If another qdisc is already attached to the 139 * parent, replace it even if the handles mismatch. 140 * - \c NLM_F_EXCL: Return -NLE_EXISTS if a qdisc with matching 141 * handle exists already. 142 * 143 * Existing qdiscs with matching handles will be updated, unless the 144 * flag \c NLM_F_EXCL is specified. If their handles do not match, the 145 * error -NLE_EXISTS is returned unless the flag \c NLM_F_REPLACE is 146 * specified in which case the existing qdisc is replaced with the new 147 * one. If no matching qdisc exists, it will be created if the flag 148 * \c NLM_F_CREATE is set, otherwise the error -NLE_OBJ_NOTFOUND is 149 * returned. 150 * 151 * After sending, the function will wait for the ACK or an eventual 152 * error message to be received and will therefore block until the 153 * operation has been completed. 154 * 155 * @note Disabling auto-ack (nl_socket_disable_auto_ack()) will cause 156 * this function to return immediately after sending. In this case, 157 * it is the responsibility of the caller to handle any error 158 * messages returned. 159 * 160 * @return 0 on success or a negative error code. 161 */ 162 int rtnl_qdisc_add(struct nl_sock *sk, struct rtnl_qdisc *qdisc, int flags) 163 { 164 struct nl_msg *msg; 165 int err; 166 167 if ((err = rtnl_qdisc_build_add_request(qdisc, flags, &msg)) < 0) 168 return err; 169 170 return nl_send_sync(sk, msg); 171 } 172 173 /** 174 * Build netlink message requesting the update of a qdisc 175 * @arg qdisc Qdisc to update 176 * @arg new Qdisc with updated attributes 177 * @arg flags Additional netlink message flags 178 * @arg result Pointer to store resulting netlink message 179 * 180 * The behaviour of this function is identical to rtnl_qdisc_update() with 181 * the exception that it will not send the message but return it in the 182 * provided return pointer instead. 183 * 184 * @see rtnl_qdisc_update() 185 * 186 * @return 0 on success or a negative error code. 187 */ 188 int rtnl_qdisc_build_update_request(struct rtnl_qdisc *qdisc, 189 struct rtnl_qdisc *new, int flags, 190 struct nl_msg **result) 191 { 192 if (flags & (NLM_F_CREATE | NLM_F_EXCL)) { 193 APPBUG("NLM_F_CREATE and NLM_F_EXCL may not be used here, " 194 "use rtnl_qdisc_add()"); 195 return -NLE_INVAL; 196 } 197 198 if (!(qdisc->ce_mask & TCA_ATTR_IFINDEX)) { 199 APPBUG("ifindex must be specified"); 200 return -NLE_MISSING_ATTR; 201 } 202 203 if (!(qdisc->ce_mask & (TCA_ATTR_HANDLE | TCA_ATTR_PARENT))) { 204 APPBUG("handle or parent must be specified"); 205 return -NLE_MISSING_ATTR; 206 } 207 208 rtnl_tc_set_ifindex(TC_CAST(new), qdisc->q_ifindex); 209 210 if (qdisc->ce_mask & TCA_ATTR_HANDLE) 211 rtnl_tc_set_handle(TC_CAST(new), qdisc->q_handle); 212 213 if (qdisc->ce_mask & TCA_ATTR_PARENT) 214 rtnl_tc_set_parent(TC_CAST(new), qdisc->q_parent); 215 216 return build_qdisc_msg(new, RTM_NEWQDISC, flags, result); 217 } 218 219 /** 220 * Update qdisc 221 * @arg sk Netlink socket 222 * @arg qdisc Qdisc to update 223 * @arg new Qdisc with updated attributes 224 * @arg flags Additional netlink message flags 225 * 226 * Builds a \c RTM_NEWQDISC netlink message requesting the update 227 * of an existing qdisc and sends the message to the kernel. 228 * 229 * This function is a varation of rtnl_qdisc_add() to update qdiscs 230 * if the qdisc to be updated is available as qdisc object. The 231 * behaviour is identical to the one of rtnl_qdisc_add except that 232 * before constructing the message, it copies the \c ifindex, 233 * \c handle, and \c parent from the original \p qdisc to the \p new 234 * qdisc. 235 * 236 * After sending, the function will wait for the ACK or an eventual 237 * error message to be received and will therefore block until the 238 * operation has been completed. 239 * 240 * @note Disabling auto-ack (nl_socket_disable_auto_ack()) will cause 241 * this function to return immediately after sending. In this case, 242 * it is the responsibility of the caller to handle any error 243 * messages returned. 244 * 245 * @return 0 on success or a negative error code. 246 */ 247 int rtnl_qdisc_update(struct nl_sock *sk, struct rtnl_qdisc *qdisc, 248 struct rtnl_qdisc *new, int flags) 249 { 250 struct nl_msg *msg; 251 int err; 252 253 err = rtnl_qdisc_build_update_request(qdisc, new, flags, &msg); 254 if (err < 0) 255 return err; 256 257 return nl_send_sync(sk, msg); 258 } 259 260 /** 261 * Build netlink message requesting the deletion of a qdisc 262 * @arg qdisc Qdisc to delete 263 * @arg result Pointer to store resulting netlink message 264 * 265 * The behaviour of this function is identical to rtnl_qdisc_delete() with 266 * the exception that it will not send the message but return it in the 267 * provided return pointer instead. 268 * 269 * @see rtnl_qdisc_delete() 270 * 271 * @return 0 on success or a negative error code. 272 */ 273 int rtnl_qdisc_build_delete_request(struct rtnl_qdisc *qdisc, 274 struct nl_msg **result) 275 { 276 struct nl_msg *msg; 277 struct tcmsg tchdr; 278 uint32_t required = TCA_ATTR_IFINDEX | TCA_ATTR_PARENT; 279 280 if ((qdisc->ce_mask & required) != required) { 281 APPBUG("ifindex and parent must be specified"); 282 return -NLE_MISSING_ATTR; 283 } 284 285 if (!(msg = nlmsg_alloc_simple(RTM_DELQDISC, 0))) 286 return -NLE_NOMEM; 287 288 memset(&tchdr, 0, sizeof(tchdr)); 289 290 tchdr.tcm_family = AF_UNSPEC; 291 tchdr.tcm_ifindex = qdisc->q_ifindex; 292 tchdr.tcm_parent = qdisc->q_parent; 293 294 if (qdisc->ce_mask & TCA_ATTR_HANDLE) 295 tchdr.tcm_handle = qdisc->q_handle; 296 297 if (nlmsg_append(msg, &tchdr, sizeof(tchdr), NLMSG_ALIGNTO) < 0) 298 goto nla_put_failure; 299 300 if (qdisc->ce_mask & TCA_ATTR_KIND) 301 NLA_PUT_STRING(msg, TCA_KIND, qdisc->q_kind); 302 303 *result = msg; 304 return 0; 305 306 nla_put_failure: 307 nlmsg_free(msg); 308 return -NLE_MSGSIZE; 309 } 310 311 /** 312 * Delete qdisc 313 * @arg sk Netlink socket 314 * @arg qdisc Qdisc to add 315 * 316 * Builds a \c RTM_NEWQDISC netlink message requesting the deletion 317 * of a qdisc and sends the message to the kernel. 318 * 319 * The message is constructed out of the following attributes: 320 * - \c ifindex and \c parent 321 * - \c handle (optional, must match if provided) 322 * - \c kind (optional, must match if provided) 323 * 324 * All other qdisc attributes including all qdisc type specific 325 * attributes are ignored. 326 * 327 * After sending, the function will wait for the ACK or an eventual 328 * error message to be received and will therefore block until the 329 * operation has been completed. 330 * 331 * @note It is not possible to delete default qdiscs. 332 * 333 * @note Disabling auto-ack (nl_socket_disable_auto_ack()) will cause 334 * this function to return immediately after sending. In this case, 335 * it is the responsibility of the caller to handle any error 336 * messages returned. 337 * 338 * @return 0 on success or a negative error code. 339 */ 340 int rtnl_qdisc_delete(struct nl_sock *sk, struct rtnl_qdisc *qdisc) 341 { 342 struct nl_msg *msg; 343 int err; 344 345 if ((err = rtnl_qdisc_build_delete_request(qdisc, &msg)) < 0) 346 return err; 347 348 return nl_send_sync(sk, msg); 349 } 350 351 /** @} */ 352 353 /** 354 * @name Cache Related Functions 355 * @{ 356 */ 357 358 /** 359 * Allocate a cache and fill it with all configured qdiscs 360 * @arg sk Netlink socket 361 * @arg result Pointer to store the created cache 362 * 363 * Allocates a new qdisc cache and fills it with a list of all configured 364 * qdiscs on all network devices. Release the cache with nl_cache_free(). 365 * 366 * @return 0 on success or a negative error code. 367 */ 368 int rtnl_qdisc_alloc_cache(struct nl_sock *sk, struct nl_cache **result) 369 { 370 return nl_cache_alloc_and_fill(&rtnl_qdisc_ops, sk, result); 371 } 372 373 /** 374 * Search qdisc by interface index and parent 375 * @arg cache Qdisc cache 376 * @arg ifindex Interface index 377 * @arg parent Handle of parent qdisc 378 * 379 * Searches a qdisc cache previously allocated with rtnl_qdisc_alloc_cache() 380 * and searches for a qdisc matching the interface index and parent qdisc. 381 * 382 * The reference counter is incremented before returning the qdisc, therefore 383 * the reference must be given back with rtnl_qdisc_put() after usage. 384 * 385 * @return pointer to qdisc inside the cache or NULL if no match was found. 386 */ 387 struct rtnl_qdisc *rtnl_qdisc_get_by_parent(struct nl_cache *cache, 388 int ifindex, uint32_t parent) 389 { 390 struct rtnl_qdisc *q; 391 392 if (cache->c_ops != &rtnl_qdisc_ops) 393 return NULL; 394 395 nl_list_for_each_entry(q, &cache->c_items, ce_list) { 396 if (q->q_parent == parent && q->q_ifindex == ifindex) { 397 nl_object_get((struct nl_object *) q); 398 return q; 399 } 400 } 401 402 return NULL; 403 } 404 405 /** 406 * Search qdisc by interface index and handle 407 * @arg cache Qdisc cache 408 * @arg ifindex Interface index 409 * @arg handle Handle 410 * 411 * Searches a qdisc cache previously allocated with rtnl_qdisc_alloc_cache() 412 * and searches for a qdisc matching the interface index and handle. 413 * 414 * The reference counter is incremented before returning the qdisc, therefore 415 * the reference must be given back with rtnl_qdisc_put() after usage. 416 * 417 * @return Qdisc or NULL if no match was found. 418 */ 419 struct rtnl_qdisc *rtnl_qdisc_get(struct nl_cache *cache, int ifindex, 420 uint32_t handle) 421 { 422 struct rtnl_qdisc *q; 423 424 if (cache->c_ops != &rtnl_qdisc_ops) 425 return NULL; 426 427 nl_list_for_each_entry(q, &cache->c_items, ce_list) { 428 if (q->q_handle == handle && q->q_ifindex == ifindex) { 429 nl_object_get((struct nl_object *) q); 430 return q; 431 } 432 } 433 434 return NULL; 435 } 436 437 /** @} */ 438 439 /** 440 * @name Deprecated Functions 441 * @{ 442 */ 443 444 /** 445 * Call a callback for each child class of a qdisc (deprecated) 446 * 447 * @deprecated Use of this function is deprecated, it does not allow 448 * to handle the out of memory situation that can occur. 449 */ 450 void rtnl_qdisc_foreach_child(struct rtnl_qdisc *qdisc, struct nl_cache *cache, 451 void (*cb)(struct nl_object *, void *), void *arg) 452 { 453 struct rtnl_class *filter; 454 455 filter = rtnl_class_alloc(); 456 if (!filter) 457 return; 458 459 rtnl_tc_set_parent(TC_CAST(filter), qdisc->q_handle); 460 rtnl_tc_set_ifindex(TC_CAST(filter), qdisc->q_ifindex); 461 rtnl_tc_set_kind(TC_CAST(filter), qdisc->q_kind); 462 463 nl_cache_foreach_filter(cache, OBJ_CAST(filter), cb, arg); 464 465 rtnl_class_put(filter); 466 } 467 468 /** 469 * Call a callback for each filter attached to the qdisc (deprecated) 470 * 471 * @deprecated Use of this function is deprecated, it does not allow 472 * to handle the out of memory situation that can occur. 473 */ 474 void rtnl_qdisc_foreach_cls(struct rtnl_qdisc *qdisc, struct nl_cache *cache, 475 void (*cb)(struct nl_object *, void *), void *arg) 476 { 477 struct rtnl_cls *filter; 478 479 if (!(filter = rtnl_cls_alloc())) 480 return; 481 482 rtnl_tc_set_ifindex(TC_CAST(filter), qdisc->q_ifindex); 483 rtnl_tc_set_parent(TC_CAST(filter), qdisc->q_parent); 484 485 nl_cache_foreach_filter(cache, OBJ_CAST(filter), cb, arg); 486 rtnl_cls_put(filter); 487 } 488 489 /** 490 * Build a netlink message requesting the update of a qdisc 491 * 492 * @deprecated Use of this function is deprecated in favour of 493 * rtnl_qdisc_build_update_request() due to the missing 494 * possibility of specifying additional flags. 495 */ 496 int rtnl_qdisc_build_change_request(struct rtnl_qdisc *qdisc, 497 struct rtnl_qdisc *new, 498 struct nl_msg **result) 499 { 500 return rtnl_qdisc_build_update_request(qdisc, new, NLM_F_REPLACE, 501 result); 502 } 503 504 /** 505 * Change attributes of a qdisc 506 * 507 * @deprecated Use of this function is deprecated in favour of 508 * rtnl_qdisc_update() due to the missing possibility of 509 * specifying additional flags. 510 */ 511 int rtnl_qdisc_change(struct nl_sock *sk, struct rtnl_qdisc *qdisc, 512 struct rtnl_qdisc *new) 513 { 514 return rtnl_qdisc_update(sk, qdisc, new, NLM_F_REPLACE); 515 } 516 517 /** @} */ 518 519 static void qdisc_dump_details(struct rtnl_tc *tc, struct nl_dump_params *p) 520 { 521 struct rtnl_qdisc *qdisc = (struct rtnl_qdisc *) tc; 522 523 nl_dump(p, "refcnt %u ", qdisc->q_info); 524 } 525 526 static struct rtnl_tc_type_ops qdisc_ops = { 527 .tt_type = RTNL_TC_TYPE_QDISC, 528 .tt_dump_prefix = "qdisc", 529 .tt_dump = { 530 [NL_DUMP_DETAILS] = qdisc_dump_details, 531 }, 532 }; 533 534 static struct nl_cache_ops rtnl_qdisc_ops = { 535 .co_name = "route/qdisc", 536 .co_hdrsize = sizeof(struct tcmsg), 537 .co_msgtypes = { 538 { RTM_NEWQDISC, NL_ACT_NEW, "new" }, 539 { RTM_DELQDISC, NL_ACT_DEL, "del" }, 540 { RTM_GETQDISC, NL_ACT_GET, "get" }, 541 END_OF_MSGTYPES_LIST, 542 }, 543 .co_protocol = NETLINK_ROUTE, 544 .co_groups = tc_groups, 545 .co_request_update = qdisc_request_update, 546 .co_msg_parser = qdisc_msg_parser, 547 .co_obj_ops = &qdisc_obj_ops, 548 }; 549 550 static struct nl_object_ops qdisc_obj_ops = { 551 .oo_name = "route/qdisc", 552 .oo_size = sizeof(struct rtnl_qdisc), 553 .oo_free_data = rtnl_tc_free_data, 554 .oo_clone = rtnl_tc_clone, 555 .oo_dump = { 556 [NL_DUMP_LINE] = rtnl_tc_dump_line, 557 [NL_DUMP_DETAILS] = rtnl_tc_dump_details, 558 [NL_DUMP_STATS] = rtnl_tc_dump_stats, 559 }, 560 .oo_compare = rtnl_tc_compare, 561 .oo_id_attrs = (TCA_ATTR_IFINDEX | TCA_ATTR_HANDLE), 562 }; 563 564 static void __init qdisc_init(void) 565 { 566 rtnl_tc_type_register(&qdisc_ops); 567 nl_cache_mngt_register(&rtnl_qdisc_ops); 568 } 569 570 static void __exit qdisc_exit(void) 571 { 572 nl_cache_mngt_unregister(&rtnl_qdisc_ops); 573 rtnl_tc_type_unregister(&qdisc_ops); 574 } 575 576 /** @} */ 577