Home | History | Annotate | Download | only in route
      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-2008 Thomas Graf <tgraf (at) suug.ch>
     10  */
     11 
     12 /**
     13  * @ingroup tc
     14  * @defgroup qdisc Queueing Disciplines
     15  *
     16  * @par Qdisc Handles
     17  * In general, qdiscs are identified by the major part of a traffic control
     18  * handle (the upper 16 bits). A few special values exist though:
     19  *  - \c TC_H_ROOT: root qdisc (directly attached to the device)
     20  *  - \c TC_H_INGRESS: ingress qdisc (directly attached to the device)
     21  *  - \c TC_H_UNSPEC: unspecified qdisc (no reference)
     22  *
     23  * @par 1) Adding a Qdisc
     24  * @code
     25  * // Allocate a new empty qdisc to be filled out
     26  * struct rtnl_qdisc *qdisc = rtnl_qdisc_alloc();
     27  *
     28  * // ... specify the kind of the Qdisc
     29  * rtnl_qdisc_set_kind(qdisc, "pfifo");
     30  *
     31  * // Specify the device the qdisc should be attached to
     32  * rtnl_qdisc_set_ifindex(qdisc, ifindex);
     33  *
     34  * // ... specify the parent qdisc
     35  * rtnl_qdisc_set_parent(qdisc, TC_H_ROOT);
     36  *
     37  * // Specifying the handle is not required but makes reidentifying easier
     38  * // and may help to avoid adding a qdisc twice.
     39  * rtnl_qdisc_set_handle(qdisc, 0x000A0000);
     40  *
     41  * // Now on to specify the qdisc specific options, see the relevant qdisc
     42  * // modules for documentation, in this example we set the upper limit of
     43  * // the packet fifo qdisc to 64
     44  * rtnl_qdisc_fifo_set_limit(qdisc, 64);
     45  *
     46  * rtnl_qdisc_add(handle, qdisc, NLM_R_REPLACE);
     47  *
     48  * // Free up the memory
     49  * rtnl_qdisc_put(qdisc);
     50  * @endcode
     51  *
     52  * @par 2) Deleting a Qdisc
     53  * @code
     54  * // Allocate a new empty qdisc to be filled out with the parameters
     55  * // specifying the qdisc to be deleted. Alternatively a fully equiped
     56  * // Qdisc object from a cache can be used.
     57  * struct rtnl_qdisc *qdisc = rtnl_qdisc_alloc();
     58  *
     59  * // The interface index of the device the qdisc is on and the parent handle
     60  * // are the least required fields to be filled out.
     61  * // Note: Specify TC_H_ROOT or TC_H_INGRESS as parent handle to delete the
     62  * //       root respectively root ingress qdisc.
     63  * rtnl_qdisc_set_ifindex(qdisc, ifindex);
     64  * rtnl_qdisc_set_parent(qdisc, parent_handle);
     65  *
     66  * // If required for identification, the handle can be specified as well.
     67  * rtnl_qdisc_set_handle(qdisc, qdisc_handle);
     68  *
     69  * // Not required but maybe helpful as sanity check, the kind of the qdisc
     70  * // can be specified to avoid mistakes.
     71  * rtnl_qdisc_set_kind(qdisc, "pfifo");
     72  *
     73  * // Finally delete the qdisc with rtnl_qdisc_delete(), alternatively
     74  * // rtnl_qdisc_build_delete_request() can be invoked to generate an
     75  * // appropritate netlink message to send out.
     76  * rtnl_qdisc_delete(handle, qdisc);
     77  *
     78  * // Free up the memory
     79  * rtnl_qdisc_put(qdisc);
     80  * @endcode
     81  *
     82  * @{
     83  */
     84 
     85 #include <netlink-local.h>
     86 #include <netlink-tc.h>
     87 #include <netlink/netlink.h>
     88 #include <netlink/utils.h>
     89 #include <netlink/route/link.h>
     90 #include <netlink/route/tc.h>
     91 #include <netlink/route/qdisc.h>
     92 #include <netlink/route/class.h>
     93 #include <netlink/route/classifier.h>
     94 #include <netlink/route/qdisc-modules.h>
     95 
     96 static struct nl_cache_ops rtnl_qdisc_ops;
     97 
     98 static int qdisc_msg_parser(struct nl_cache_ops *ops, struct sockaddr_nl *who,
     99 			    struct nlmsghdr *n, struct nl_parser_param *pp)
    100 {
    101 	int err;
    102 	struct rtnl_qdisc *qdisc;
    103 	struct rtnl_qdisc_ops *qops;
    104 
    105 	qdisc = rtnl_qdisc_alloc();
    106 	if (!qdisc) {
    107 		err = -NLE_NOMEM;
    108 		goto errout;
    109 	}
    110 
    111 	qdisc->ce_msgtype = n->nlmsg_type;
    112 
    113 	err = tca_msg_parser(n, (struct rtnl_tca *) qdisc);
    114 	if (err < 0)
    115 		goto errout_free;
    116 
    117 	qops = rtnl_qdisc_lookup_ops(qdisc);
    118 	if (qops && qops->qo_msg_parser) {
    119 		err = qops->qo_msg_parser(qdisc);
    120 		if (err < 0)
    121 			goto errout_free;
    122 	}
    123 
    124 	err = pp->pp_cb((struct nl_object *) qdisc, pp);
    125 errout_free:
    126 	rtnl_qdisc_put(qdisc);
    127 errout:
    128 	return err;
    129 }
    130 
    131 static int qdisc_request_update(struct nl_cache *c, struct nl_sock *sk)
    132 {
    133 	struct tcmsg tchdr = {
    134 		.tcm_family = AF_UNSPEC,
    135 		.tcm_ifindex = c->c_iarg1,
    136 	};
    137 
    138 	return nl_send_simple(sk, RTM_GETQDISC, NLM_F_DUMP, &tchdr,
    139 			      sizeof(tchdr));
    140 }
    141 
    142 /**
    143  * @name QDisc Addition
    144  * @{
    145  */
    146 
    147 static int qdisc_build(struct rtnl_qdisc *qdisc, int type, int flags,
    148 		       struct nl_msg **result)
    149 {
    150 	struct rtnl_qdisc_ops *qops;
    151 	int err;
    152 
    153 	err = tca_build_msg((struct rtnl_tca *) qdisc, type, flags, result);
    154 	if (err < 0)
    155 		return err;
    156 
    157 	qops = rtnl_qdisc_lookup_ops(qdisc);
    158 	if (qops && qops->qo_get_opts) {
    159 		struct nl_msg *opts;
    160 
    161 		opts = qops->qo_get_opts(qdisc);
    162 		if (opts) {
    163 			err = nla_put_nested(*result, TCA_OPTIONS, opts);
    164 			nlmsg_free(opts);
    165 			if (err < 0)
    166 				goto errout;
    167 		}
    168 	}
    169 	/* Some qdiscs don't accept properly nested messages (e.g. netem). To
    170 	 * accomodate for this, they can complete the message themselves.
    171 	 */
    172 	else if (qops && qops->qo_build_msg) {
    173 		err = qops->qo_build_msg(qdisc, *result);
    174 		if (err < 0)
    175 			goto errout;
    176 	}
    177 
    178 	return 0;
    179 errout:
    180 	nlmsg_free(*result);
    181 
    182 	return err;
    183 }
    184 
    185 /**
    186  * Build a netlink message to add a new qdisc
    187  * @arg qdisc		qdisc to add
    188  * @arg flags		additional netlink message flags
    189  * @arg result		Pointer to store resulting message.
    190  *
    191  * Builds a new netlink message requesting an addition of a qdisc.
    192  * The netlink message header isn't fully equipped with all relevant
    193  * fields and must be sent out via nl_send_auto_complete() or
    194  * supplemented as needed.
    195  *
    196  * Common message flags used:
    197  *  - NLM_F_REPLACE - replace a potential existing qdisc
    198  *
    199  * @return 0 on success or a negative error code.
    200  */
    201 int rtnl_qdisc_build_add_request(struct rtnl_qdisc *qdisc, int flags,
    202 				 struct nl_msg **result)
    203 {
    204 	return qdisc_build(qdisc, RTM_NEWQDISC, NLM_F_CREATE | flags, result);
    205 }
    206 
    207 /**
    208  * Add a new qdisc
    209  * @arg sk		Netlink socket.
    210  * @arg qdisc		qdisc to delete
    211  * @arg flags		additional netlink message flags
    212  *
    213  * Builds a netlink message by calling rtnl_qdisc_build_add_request(),
    214  * sends the request to the kernel and waits for the ACK to be
    215  * received and thus blocks until the request has been processed.
    216  *
    217  * Common message flags used:
    218  *  - NLM_F_REPLACE - replace a potential existing qdisc
    219  *
    220  * @return 0 on success or a negative error code
    221  */
    222 int rtnl_qdisc_add(struct nl_sock *sk, struct rtnl_qdisc *qdisc,
    223 		   int flags)
    224 {
    225 	struct nl_msg *msg;
    226 	int err;
    227 
    228 	if ((err = rtnl_qdisc_build_add_request(qdisc, flags, &msg)) < 0)
    229 		return err;
    230 
    231 	err = nl_send_auto_complete(sk, msg);
    232 	nlmsg_free(msg);
    233 	if (err < 0)
    234 		return err;
    235 
    236 	return wait_for_ack(sk);
    237 }
    238 
    239 /** @} */
    240 
    241 /**
    242  * @name QDisc Modification
    243  * @{
    244  */
    245 
    246 /**
    247  * Build a netlink message to change attributes of a existing qdisc
    248  * @arg qdisc		qdisc to change
    249  * @arg new		new qdisc attributes
    250  * @arg result		Pointer to store resulting message.
    251  *
    252  * Builds a new netlink message requesting an change of qdisc
    253  * attributes. The netlink message header isn't fully equipped
    254  * with all relevant fields and must be sent out via
    255  * nl_send_auto_complete() or supplemented as needed.
    256  *
    257  * @return 0 on success or a negative error code.
    258  */
    259 int rtnl_qdisc_build_change_request(struct rtnl_qdisc *qdisc,
    260 				    struct rtnl_qdisc *new,
    261 				    struct nl_msg **result)
    262 {
    263 	return qdisc_build(qdisc, RTM_NEWQDISC, NLM_F_REPLACE, result);
    264 }
    265 
    266 /**
    267  * Change attributes of a qdisc
    268  * @arg sk		Netlink socket.
    269  * @arg qdisc		qdisc to change
    270  * @arg new		new qdisc attributes
    271  *
    272  * Builds a netlink message by calling rtnl_qdisc_build_change_request(),
    273  * sends the request to the kernel and waits for the ACK to be
    274  * received and thus blocks until the request has been processed.
    275  *
    276  * @return 0 on success or a negative error code
    277  */
    278 int rtnl_qdisc_change(struct nl_sock *sk, struct rtnl_qdisc *qdisc,
    279 		      struct rtnl_qdisc *new)
    280 {
    281 	struct nl_msg *msg;
    282 	int err;
    283 
    284 	if ((err = rtnl_qdisc_build_change_request(qdisc, new, &msg)) < 0)
    285 		return err;
    286 
    287 	err = nl_send_auto_complete(sk, msg);
    288 	nlmsg_free(msg);
    289 	if (err < 0)
    290 		return err;
    291 
    292 	return wait_for_ack(sk);
    293 }
    294 
    295 /** @} */
    296 
    297 /**
    298  * @name QDisc Deletion
    299  * @{
    300  */
    301 
    302 /**
    303  * Build a netlink request message to delete a qdisc
    304  * @arg qdisc		qdisc to delete
    305  * @arg result		Pointer to store resulting message.
    306  *
    307  * Builds a new netlink message requesting a deletion of a qdisc.
    308  * The netlink message header isn't fully equipped with all relevant
    309  * fields and must thus be sent out via nl_send_auto_complete()
    310  * or supplemented as needed.
    311  *
    312  * @return 0 on success or a negative error code.
    313  */
    314 int rtnl_qdisc_build_delete_request(struct rtnl_qdisc *qdisc,
    315 				    struct nl_msg **result)
    316 {
    317 	struct nl_msg *msg;
    318 	struct tcmsg tchdr;
    319 	int required = TCA_ATTR_IFINDEX | TCA_ATTR_PARENT;
    320 
    321 	if ((qdisc->ce_mask & required) != required)
    322 		BUG();
    323 
    324 	msg = nlmsg_alloc_simple(RTM_DELQDISC, 0);
    325 	if (!msg)
    326 		return -NLE_NOMEM;
    327 
    328 	tchdr.tcm_family = AF_UNSPEC;
    329 	tchdr.tcm_handle = qdisc->q_handle;
    330 	tchdr.tcm_parent = qdisc->q_parent;
    331 	tchdr.tcm_ifindex = qdisc->q_ifindex;
    332 	if (nlmsg_append(msg, &tchdr, sizeof(tchdr), NLMSG_ALIGNTO) < 0) {
    333 		nlmsg_free(msg);
    334 		return -NLE_MSGSIZE;
    335 	}
    336 
    337 	*result = msg;
    338 	return 0;
    339 }
    340 
    341 /**
    342  * Delete a qdisc
    343  * @arg sk		Netlink socket.
    344  * @arg qdisc		qdisc to delete
    345  *
    346  * Builds a netlink message by calling rtnl_qdisc_build_delete_request(),
    347  * sends the request to the kernel and waits for the ACK to be
    348  * received and thus blocks until the request has been processed.
    349  *
    350  * @return 0 on success or a negative error code
    351  */
    352 int rtnl_qdisc_delete(struct nl_sock *sk, struct rtnl_qdisc *qdisc)
    353 {
    354 	struct nl_msg *msg;
    355 	int err;
    356 
    357 	if ((err = rtnl_qdisc_build_delete_request(qdisc, &msg)) < 0)
    358 		return err;
    359 
    360 	err = nl_send_auto_complete(sk, msg);
    361 	nlmsg_free(msg);
    362 	if (err < 0)
    363 		return err;
    364 
    365 	return wait_for_ack(sk);
    366 }
    367 
    368 /** @} */
    369 
    370 /**
    371  * @name Qdisc Cache Management
    372  * @{
    373  */
    374 
    375 /**
    376  * Build a qdisc cache including all qdiscs currently configured in
    377  * the kernel
    378  * @arg sk		Netlink socket.
    379  * @arg result		Pointer to store resulting message.
    380  *
    381  * Allocates a new cache, initializes it properly and updates it to
    382  * include all qdiscs currently configured in the kernel.
    383  *
    384  * @return 0 on success or a negative error code.
    385  */
    386 int rtnl_qdisc_alloc_cache(struct nl_sock *sk, struct nl_cache **result)
    387 {
    388 	return nl_cache_alloc_and_fill(&rtnl_qdisc_ops, sk, result);
    389 }
    390 
    391 /**
    392  * Look up qdisc by its parent in the provided cache
    393  * @arg cache		qdisc cache
    394  * @arg ifindex		interface the qdisc is attached to
    395  * @arg parent		parent handle
    396  * @return pointer to qdisc inside the cache or NULL if no match was found.
    397  */
    398 struct rtnl_qdisc * rtnl_qdisc_get_by_parent(struct nl_cache *cache,
    399 					     int ifindex, uint32_t parent)
    400 {
    401 	struct rtnl_qdisc *q;
    402 
    403 	if (cache->c_ops != &rtnl_qdisc_ops)
    404 		return NULL;
    405 
    406 	nl_list_for_each_entry(q, &cache->c_items, ce_list) {
    407 		if (q->q_parent == parent && q->q_ifindex == ifindex) {
    408 			nl_object_get((struct nl_object *) q);
    409 			return q;
    410 		}
    411 	}
    412 
    413 	return NULL;
    414 }
    415 
    416 /**
    417  * Look up qdisc by its handle in the provided cache
    418  * @arg cache		qdisc cache
    419  * @arg ifindex		interface the qdisc is attached to
    420  * @arg handle		qdisc handle
    421  * @return pointer to qdisc inside the cache or NULL if no match was found.
    422  */
    423 struct rtnl_qdisc * rtnl_qdisc_get(struct nl_cache *cache,
    424 				   int ifindex, uint32_t handle)
    425 {
    426 	struct rtnl_qdisc *q;
    427 
    428 	if (cache->c_ops != &rtnl_qdisc_ops)
    429 		return NULL;
    430 
    431 	nl_list_for_each_entry(q, &cache->c_items, ce_list) {
    432 		if (q->q_handle == handle && q->q_ifindex == ifindex) {
    433 			nl_object_get((struct nl_object *) q);
    434 			return q;
    435 		}
    436 	}
    437 
    438 	return NULL;
    439 }
    440 
    441 /** @} */
    442 
    443 static struct nl_cache_ops rtnl_qdisc_ops = {
    444 	.co_name		= "route/qdisc",
    445 	.co_hdrsize		= sizeof(struct tcmsg),
    446 	.co_msgtypes		= {
    447 					{ RTM_NEWQDISC, NL_ACT_NEW, "new" },
    448 					{ RTM_DELQDISC, NL_ACT_DEL, "del" },
    449 					{ RTM_GETQDISC, NL_ACT_GET, "get" },
    450 					END_OF_MSGTYPES_LIST,
    451 				  },
    452 	.co_protocol		= NETLINK_ROUTE,
    453 	.co_request_update	= qdisc_request_update,
    454 	.co_msg_parser		= qdisc_msg_parser,
    455 	.co_obj_ops		= &qdisc_obj_ops,
    456 };
    457 
    458 static void __init qdisc_init(void)
    459 {
    460 	nl_cache_mngt_register(&rtnl_qdisc_ops);
    461 }
    462 
    463 static void __exit qdisc_exit(void)
    464 {
    465 	nl_cache_mngt_unregister(&rtnl_qdisc_ops);
    466 }
    467 
    468 /** @} */
    469