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