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-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