libnl 1.1
|
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-2006 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 = -ENOMEM; 00102 struct rtnl_qdisc *qdisc; 00103 struct rtnl_qdisc_ops *qops; 00104 00105 qdisc = rtnl_qdisc_alloc(); 00106 if (!qdisc) { 00107 err = nl_errno(ENOMEM); 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 if (err < 0) 00126 goto errout_free; 00127 00128 err = P_ACCEPT; 00129 00130 errout_free: 00131 rtnl_qdisc_put(qdisc); 00132 errout: 00133 return err; 00134 } 00135 00136 static int qdisc_request_update(struct nl_cache *c, struct nl_handle *h) 00137 { 00138 struct tcmsg tchdr = { 00139 .tcm_family = AF_UNSPEC, 00140 .tcm_ifindex = c->c_iarg1, 00141 }; 00142 00143 return nl_send_simple(h, RTM_GETQDISC, NLM_F_DUMP, &tchdr, 00144 sizeof(tchdr)); 00145 } 00146 00147 /** 00148 * @name QDisc Addition 00149 * @{ 00150 */ 00151 00152 static struct nl_msg *qdisc_build(struct rtnl_qdisc *qdisc, int type, int flags) 00153 { 00154 struct rtnl_qdisc_ops *qops; 00155 struct nl_msg *msg; 00156 int err; 00157 00158 msg = tca_build_msg((struct rtnl_tca *) qdisc, type, flags); 00159 if (!msg) 00160 goto errout; 00161 00162 qops = rtnl_qdisc_lookup_ops(qdisc); 00163 if (qops && qops->qo_get_opts) { 00164 struct nl_msg *opts; 00165 00166 opts = qops->qo_get_opts(qdisc); 00167 if (opts) { 00168 err = nla_put_nested(msg, TCA_OPTIONS, opts); 00169 nlmsg_free(opts); 00170 if (err < 0) 00171 goto errout; 00172 } 00173 } 00174 00175 return msg; 00176 errout: 00177 nlmsg_free(msg); 00178 00179 return NULL; 00180 } 00181 00182 /** 00183 * Build a netlink message to add a new qdisc 00184 * @arg qdisc qdisc to add 00185 * @arg flags additional netlink message flags 00186 * 00187 * Builds a new netlink message requesting an addition of a qdisc. 00188 * The netlink message header isn't fully equipped with all relevant 00189 * fields and must be sent out via nl_send_auto_complete() or 00190 * supplemented as needed. 00191 * 00192 * Common message flags used: 00193 * - NLM_F_REPLACE - replace a potential existing qdisc 00194 * 00195 * @return New netlink message 00196 */ 00197 struct nl_msg *rtnl_qdisc_build_add_request(struct rtnl_qdisc *qdisc, 00198 int flags) 00199 { 00200 struct nl_msg *msg; 00201 00202 msg = qdisc_build(qdisc, RTM_NEWQDISC, NLM_F_CREATE | flags); 00203 if (!msg) 00204 nl_errno(ENOMEM); 00205 00206 return msg; 00207 } 00208 00209 /** 00210 * Add a new qdisc 00211 * @arg handle netlink handle 00212 * @arg qdisc qdisc to delete 00213 * @arg flags additional netlink message flags 00214 * 00215 * Builds a netlink message by calling rtnl_qdisc_build_add_request(), 00216 * sends the request to the kernel and waits for the ACK to be 00217 * received and thus blocks until the request has been processed. 00218 * 00219 * Common message flags used: 00220 * - NLM_F_REPLACE - replace a potential existing qdisc 00221 * 00222 * @return 0 on success or a negative error code 00223 */ 00224 int rtnl_qdisc_add(struct nl_handle *handle, struct rtnl_qdisc *qdisc, 00225 int flags) 00226 { 00227 struct nl_msg *msg; 00228 int err; 00229 00230 msg = rtnl_qdisc_build_add_request(qdisc, flags); 00231 if (!msg) 00232 return nl_errno(ENOMEM); 00233 00234 err = nl_send_auto_complete(handle, msg); 00235 if (err < 0) 00236 return err; 00237 00238 nlmsg_free(msg); 00239 return nl_wait_for_ack(handle); 00240 } 00241 00242 /** @} */ 00243 00244 /** 00245 * @name QDisc Modification 00246 * @{ 00247 */ 00248 00249 /** 00250 * Build a netlink message to change attributes of a existing qdisc 00251 * @arg qdisc qdisc to change 00252 * @arg new new qdisc attributes 00253 * 00254 * Builds a new netlink message requesting an change of qdisc 00255 * attributes. The netlink message header isn't fully equipped 00256 * with all relevant fields and must be sent out via 00257 * nl_send_auto_complete() or supplemented as needed. 00258 * 00259 * @return New netlink message 00260 */ 00261 struct nl_msg *rtnl_qdisc_build_change_request(struct rtnl_qdisc *qdisc, 00262 struct rtnl_qdisc *new) 00263 { 00264 return qdisc_build(qdisc, RTM_NEWQDISC, NLM_F_REPLACE); 00265 } 00266 00267 /** 00268 * Change attributes of a qdisc 00269 * @arg handle netlink handle 00270 * @arg qdisc qdisc to change 00271 * @arg new new qdisc attributes 00272 * 00273 * Builds a netlink message by calling rtnl_qdisc_build_change_request(), 00274 * sends the request to the kernel and waits for the ACK to be 00275 * received and thus blocks until the request has been processed. 00276 * 00277 * @return 0 on success or a negative error code 00278 */ 00279 int rtnl_qdisc_change(struct nl_handle *handle, struct rtnl_qdisc *qdisc, 00280 struct rtnl_qdisc *new) 00281 { 00282 struct nl_msg *msg; 00283 int err; 00284 00285 msg = rtnl_qdisc_build_change_request(qdisc, new); 00286 if (!msg) 00287 return nl_errno(ENOMEM); 00288 00289 err = nl_send_auto_complete(handle, msg); 00290 if (err < 0) 00291 return err; 00292 00293 nlmsg_free(msg); 00294 return nl_wait_for_ack(handle); 00295 } 00296 00297 /** @} */ 00298 00299 /** 00300 * @name QDisc Deletion 00301 * @{ 00302 */ 00303 00304 /** 00305 * Build a netlink request message to delete a qdisc 00306 * @arg qdisc qdisc to delete 00307 * 00308 * Builds a new netlink message requesting a deletion of a qdisc. 00309 * The netlink message header isn't fully equipped with all relevant 00310 * fields and must thus be sent out via nl_send_auto_complete() 00311 * or supplemented as needed. 00312 * 00313 * @return New netlink message 00314 */ 00315 struct nl_msg *rtnl_qdisc_build_delete_request(struct rtnl_qdisc *qdisc) 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 NULL; 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 nlmsg_append(msg, &tchdr, sizeof(tchdr), NLMSG_ALIGNTO); 00333 00334 return msg; 00335 } 00336 00337 /** 00338 * Delete a qdisc 00339 * @arg handle netlink handle 00340 * @arg qdisc qdisc to delete 00341 * 00342 * Builds a netlink message by calling rtnl_qdisc_build_delete_request(), 00343 * sends the request to the kernel and waits for the ACK to be 00344 * received and thus blocks until the request has been processed. 00345 * 00346 * @return 0 on success or a negative error code 00347 */ 00348 int rtnl_qdisc_delete(struct nl_handle *handle, struct rtnl_qdisc *qdisc) 00349 { 00350 struct nl_msg *msg; 00351 int err; 00352 00353 msg = rtnl_qdisc_build_delete_request(qdisc); 00354 if (!msg) 00355 return nl_errno(ENOMEM); 00356 00357 err = nl_send_auto_complete(handle, msg); 00358 if (err < 0) 00359 return err; 00360 00361 nlmsg_free(msg); 00362 return nl_wait_for_ack(handle); 00363 } 00364 00365 /** @} */ 00366 00367 /** 00368 * @name Qdisc Cache Management 00369 * @{ 00370 */ 00371 00372 /** 00373 * Build a qdisc cache including all qdiscs currently configured in 00374 * the kernel 00375 * @arg handle netlink handle 00376 * 00377 * Allocates a new cache, initializes it properly and updates it to 00378 * include all qdiscs currently configured in the kernel. 00379 * 00380 * @note The caller is responsible for destroying and freeing the 00381 * cache after using it. 00382 * @return The cache or NULL if an error has occured. 00383 */ 00384 struct nl_cache * rtnl_qdisc_alloc_cache(struct nl_handle *handle) 00385 { 00386 struct nl_cache * cache; 00387 00388 cache = nl_cache_alloc(&rtnl_qdisc_ops); 00389 if (cache == NULL) 00390 return NULL; 00391 00392 if (handle && nl_cache_refill(handle, cache) < 0) { 00393 nl_cache_free(cache); 00394 return NULL; 00395 } 00396 00397 return cache; 00398 } 00399 00400 /** 00401 * Look up qdisc by its parent in the provided cache 00402 * @arg cache qdisc cache 00403 * @arg ifindex interface the qdisc is attached to 00404 * @arg parent parent handle 00405 * @return pointer to qdisc inside the cache or NULL if no match was found. 00406 */ 00407 struct rtnl_qdisc * rtnl_qdisc_get_by_parent(struct nl_cache *cache, 00408 int ifindex, uint32_t parent) 00409 { 00410 struct rtnl_qdisc *q; 00411 00412 if (cache->c_ops != &rtnl_qdisc_ops) 00413 return NULL; 00414 00415 nl_list_for_each_entry(q, &cache->c_items, ce_list) { 00416 if (q->q_parent == parent && q->q_ifindex == ifindex) { 00417 nl_object_get((struct nl_object *) q); 00418 return q; 00419 } 00420 } 00421 00422 return NULL; 00423 } 00424 00425 /** 00426 * Look up qdisc by its handle in the provided cache 00427 * @arg cache qdisc cache 00428 * @arg ifindex interface the qdisc is attached to 00429 * @arg handle qdisc handle 00430 * @return pointer to qdisc inside the cache or NULL if no match was found. 00431 */ 00432 struct rtnl_qdisc * rtnl_qdisc_get(struct nl_cache *cache, 00433 int ifindex, uint32_t handle) 00434 { 00435 struct rtnl_qdisc *q; 00436 00437 if (cache->c_ops != &rtnl_qdisc_ops) 00438 return NULL; 00439 00440 nl_list_for_each_entry(q, &cache->c_items, ce_list) { 00441 if (q->q_handle == handle && q->q_ifindex == ifindex) { 00442 nl_object_get((struct nl_object *) q); 00443 return q; 00444 } 00445 } 00446 00447 return NULL; 00448 } 00449 00450 /** @} */ 00451 00452 static struct nl_cache_ops rtnl_qdisc_ops = { 00453 .co_name = "route/qdisc", 00454 .co_hdrsize = sizeof(struct tcmsg), 00455 .co_msgtypes = { 00456 { RTM_NEWQDISC, NL_ACT_NEW, "new" }, 00457 { RTM_DELQDISC, NL_ACT_DEL, "del" }, 00458 { RTM_GETQDISC, NL_ACT_GET, "get" }, 00459 END_OF_MSGTYPES_LIST, 00460 }, 00461 .co_protocol = NETLINK_ROUTE, 00462 .co_request_update = qdisc_request_update, 00463 .co_msg_parser = qdisc_msg_parser, 00464 .co_obj_ops = &qdisc_obj_ops, 00465 }; 00466 00467 static void __init qdisc_init(void) 00468 { 00469 nl_cache_mngt_register(&rtnl_qdisc_ops); 00470 } 00471 00472 static void __exit qdisc_exit(void) 00473 { 00474 nl_cache_mngt_unregister(&rtnl_qdisc_ops); 00475 } 00476 00477 /** @} */