diff --git a/include/netlink-local.h b/include/netlink-local.h index 9acc0e4..63dd661 100644 --- a/include/netlink-local.h +++ b/include/netlink-local.h @@ -6,7 +6,7 @@ * License as published by the Free Software Foundation version 2.1 * of the License. * - * Copyright (c) 2003-2008 Thomas Graf + * Copyright (c) 2003-2011 Thomas Graf */ #ifndef NETLINK_LOCAL_H_ @@ -88,6 +88,13 @@ struct trans_list { assert(0); \ } while (0) +#define APPBUG(msg) \ + do { \ + fprintf(stderr, "APPLICATION BUG: %s:%d:%s: %s\n", \ + __FILE__, __LINE__, __PRETTY_FUNCTION__, msg); \ + assert(0); \ + } while(0) + extern int __nl_read_num_str_file(const char *path, int (*cb)(long, const char *)); diff --git a/include/netlink/route/qdisc.h b/include/netlink/route/qdisc.h index fbff666..10b85c5 100644 --- a/include/netlink/route/qdisc.h +++ b/include/netlink/route/qdisc.h @@ -38,24 +38,33 @@ extern int rtnl_qdisc_build_add_request(struct rtnl_qdisc *, int, struct nl_msg **); extern int rtnl_qdisc_add(struct nl_sock *, struct rtnl_qdisc *, int); -extern int rtnl_qdisc_build_change_request(struct rtnl_qdisc *, +extern int rtnl_qdisc_build_update_request(struct rtnl_qdisc *, struct rtnl_qdisc *, - struct nl_msg **); -extern int rtnl_qdisc_change(struct nl_sock *, struct rtnl_qdisc *, - struct rtnl_qdisc *); + int, struct nl_msg **); + +extern int rtnl_qdisc_update(struct nl_sock *, struct rtnl_qdisc *, + struct rtnl_qdisc *, int); extern int rtnl_qdisc_build_delete_request(struct rtnl_qdisc *, struct nl_msg **); extern int rtnl_qdisc_delete(struct nl_sock *, struct rtnl_qdisc *); /* Deprecated functions */ -extern void rtnl_qdisc_foreach_child(struct rtnl_qdisc *, struct nl_cache *, - void (*cb)(struct nl_object *, void *), - void *) __attribute__ ((deprecated)); +extern void rtnl_qdisc_foreach_child(struct rtnl_qdisc *, struct nl_cache *, + void (*cb)(struct nl_object *, void *), + void *) __attribute__ ((deprecated)); -extern void rtnl_qdisc_foreach_cls(struct rtnl_qdisc *, struct nl_cache *, - void (*cb)(struct nl_object *, void *), - void *) __attribute__ ((deprecated)); +extern void rtnl_qdisc_foreach_cls(struct rtnl_qdisc *, struct nl_cache *, + void (*cb)(struct nl_object *, void *), + void *) __attribute__ ((deprecated)); + +extern int rtnl_qdisc_build_change_request(struct rtnl_qdisc *, + struct rtnl_qdisc *, + struct nl_msg **) + __attribute__ ((deprecated)); + +extern int rtnl_qdisc_change(struct nl_sock *, struct rtnl_qdisc *, + struct rtnl_qdisc *) __attribute__ ((deprecated)); #ifdef __cplusplus } diff --git a/lib/route/qdisc.c b/lib/route/qdisc.c index 5257b9d..e5a8aa0 100644 --- a/lib/route/qdisc.c +++ b/lib/route/qdisc.c @@ -12,73 +12,6 @@ /** * @ingroup tc * @defgroup qdisc Queueing Disciplines - * - * @par Qdisc Handles - * In general, qdiscs are identified by the major part of a traffic control - * handle (the upper 16 bits). A few special values exist though: - * - \c TC_H_ROOT: root qdisc (directly attached to the device) - * - \c TC_H_INGRESS: ingress qdisc (directly attached to the device) - * - \c TC_H_UNSPEC: unspecified qdisc (no reference) - * - * @par 1) Adding a Qdisc - * @code - * // Allocate a new empty qdisc to be filled out - * struct rtnl_qdisc *qdisc = rtnl_qdisc_alloc(); - * - * // ... specify the kind of the Qdisc - * rtnl_qdisc_set_kind(qdisc, "pfifo"); - * - * // Specify the device the qdisc should be attached to - * rtnl_qdisc_set_ifindex(qdisc, ifindex); - * - * // ... specify the parent qdisc - * rtnl_qdisc_set_parent(qdisc, TC_H_ROOT); - * - * // Specifying the handle is not required but makes reidentifying easier - * // and may help to avoid adding a qdisc twice. - * rtnl_qdisc_set_handle(qdisc, 0x000A0000); - * - * // Now on to specify the qdisc specific options, see the relevant qdisc - * // modules for documentation, in this example we set the upper limit of - * // the packet fifo qdisc to 64 - * rtnl_qdisc_fifo_set_limit(qdisc, 64); - * - * rtnl_qdisc_add(handle, qdisc, NLM_R_REPLACE); - * - * // Free up the memory - * rtnl_qdisc_put(qdisc); - * @endcode - * - * @par 2) Deleting a Qdisc - * @code - * // Allocate a new empty qdisc to be filled out with the parameters - * // specifying the qdisc to be deleted. Alternatively a fully equiped - * // Qdisc object from a cache can be used. - * struct rtnl_qdisc *qdisc = rtnl_qdisc_alloc(); - * - * // The interface index of the device the qdisc is on and the parent handle - * // are the least required fields to be filled out. - * // Note: Specify TC_H_ROOT or TC_H_INGRESS as parent handle to delete the - * // root respectively root ingress qdisc. - * rtnl_qdisc_set_ifindex(qdisc, ifindex); - * rtnl_qdisc_set_parent(qdisc, parent_handle); - * - * // If required for identification, the handle can be specified as well. - * rtnl_qdisc_set_handle(qdisc, qdisc_handle); - * - * // Not required but maybe helpful as sanity check, the kind of the qdisc - * // can be specified to avoid mistakes. - * rtnl_qdisc_set_kind(qdisc, "pfifo"); - * - * // Finally delete the qdisc with rtnl_qdisc_delete(), alternatively - * // rtnl_qdisc_build_delete_request() can be invoked to generate an - * // appropritate netlink message to send out. - * rtnl_qdisc_delete(handle, qdisc); - * - * // Free up the memory - * rtnl_qdisc_put(qdisc); - * @endcode - * * @{ */ @@ -125,286 +58,6 @@ static int qdisc_request_update(struct nl_cache *c, struct nl_sock *sk) sizeof(tchdr)); } -/** - * @name QDisc Addition - * @{ - */ - -static int qdisc_build(struct rtnl_qdisc *qdisc, int type, int flags, - struct nl_msg **result) -{ - return rtnl_tc_msg_build(TC_CAST(qdisc), type, flags, result); - -#if 0 - /* Some qdiscs don't accept properly nested messages (e.g. netem). To - * accomodate for this, they can complete the message themselves. - */ - else if (qops && qops->qo_build_msg) { - err = qops->qo_build_msg(qdisc, *result); - if (err < 0) - goto errout; - } -#endif -} - -/** - * Build a netlink message to add a new qdisc - * @arg qdisc qdisc to add - * @arg flags additional netlink message flags - * @arg result Pointer to store resulting message. - * - * Builds a new netlink message requesting an addition of a qdisc. - * The netlink message header isn't fully equipped with all relevant - * fields and must be sent out via nl_send_auto_complete() or - * supplemented as needed. - * - * Common message flags used: - * - NLM_F_REPLACE - replace a potential existing qdisc - * - * @return 0 on success or a negative error code. - */ -int rtnl_qdisc_build_add_request(struct rtnl_qdisc *qdisc, int flags, - struct nl_msg **result) -{ - return qdisc_build(qdisc, RTM_NEWQDISC, NLM_F_CREATE | flags, result); -} - -/** - * Add a new qdisc - * @arg sk Netlink socket. - * @arg qdisc qdisc to delete - * @arg flags additional netlink message flags - * - * Builds a netlink message by calling rtnl_qdisc_build_add_request(), - * sends the request to the kernel and waits for the ACK to be - * received and thus blocks until the request has been processed. - * - * Common message flags used: - * - NLM_F_REPLACE - replace a potential existing qdisc - * - * @return 0 on success or a negative error code - */ -int rtnl_qdisc_add(struct nl_sock *sk, struct rtnl_qdisc *qdisc, - int flags) -{ - struct nl_msg *msg; - int err; - - if ((err = rtnl_qdisc_build_add_request(qdisc, flags, &msg)) < 0) - return err; - - err = nl_send_auto_complete(sk, msg); - nlmsg_free(msg); - if (err < 0) - return err; - - return wait_for_ack(sk); -} - -/** @} */ - -/** - * @name QDisc Modification - * @{ - */ - -/** - * Build a netlink message to change attributes of a existing qdisc - * @arg qdisc qdisc to change - * @arg new new qdisc attributes - * @arg result Pointer to store resulting message. - * - * Builds a new netlink message requesting an change of qdisc - * attributes. The netlink message header isn't fully equipped - * with all relevant fields and must be sent out via - * nl_send_auto_complete() or supplemented as needed. - * - * @return 0 on success or a negative error code. - */ -int rtnl_qdisc_build_change_request(struct rtnl_qdisc *qdisc, - struct rtnl_qdisc *new, - struct nl_msg **result) -{ - return qdisc_build(qdisc, RTM_NEWQDISC, NLM_F_REPLACE, result); -} - -/** - * Change attributes of a qdisc - * @arg sk Netlink socket. - * @arg qdisc qdisc to change - * @arg new new qdisc attributes - * - * Builds a netlink message by calling rtnl_qdisc_build_change_request(), - * sends the request to the kernel and waits for the ACK to be - * received and thus blocks until the request has been processed. - * - * @return 0 on success or a negative error code - */ -int rtnl_qdisc_change(struct nl_sock *sk, struct rtnl_qdisc *qdisc, - struct rtnl_qdisc *new) -{ - struct nl_msg *msg; - int err; - - if ((err = rtnl_qdisc_build_change_request(qdisc, new, &msg)) < 0) - return err; - - err = nl_send_auto_complete(sk, msg); - nlmsg_free(msg); - if (err < 0) - return err; - - return wait_for_ack(sk); -} - -/** @} */ - -/** - * @name QDisc Deletion - * @{ - */ - -/** - * Build a netlink request message to delete a qdisc - * @arg qdisc qdisc to delete - * @arg result Pointer to store resulting message. - * - * Builds a new netlink message requesting a deletion of a qdisc. - * The netlink message header isn't fully equipped with all relevant - * fields and must thus be sent out via nl_send_auto_complete() - * or supplemented as needed. - * - * @return 0 on success or a negative error code. - */ -int rtnl_qdisc_build_delete_request(struct rtnl_qdisc *qdisc, - struct nl_msg **result) -{ - struct nl_msg *msg; - struct tcmsg tchdr; - int required = TCA_ATTR_IFINDEX | TCA_ATTR_PARENT; - - if ((qdisc->ce_mask & required) != required) - BUG(); - - msg = nlmsg_alloc_simple(RTM_DELQDISC, 0); - if (!msg) - return -NLE_NOMEM; - - tchdr.tcm_family = AF_UNSPEC; - tchdr.tcm_handle = qdisc->q_handle; - tchdr.tcm_parent = qdisc->q_parent; - tchdr.tcm_ifindex = qdisc->q_ifindex; - if (nlmsg_append(msg, &tchdr, sizeof(tchdr), NLMSG_ALIGNTO) < 0) { - nlmsg_free(msg); - return -NLE_MSGSIZE; - } - - *result = msg; - return 0; -} - -/** - * Delete a qdisc - * @arg sk Netlink socket. - * @arg qdisc qdisc to delete - * - * Builds a netlink message by calling rtnl_qdisc_build_delete_request(), - * sends the request to the kernel and waits for the ACK to be - * received and thus blocks until the request has been processed. - * - * @return 0 on success or a negative error code - */ -int rtnl_qdisc_delete(struct nl_sock *sk, struct rtnl_qdisc *qdisc) -{ - struct nl_msg *msg; - int err; - - if ((err = rtnl_qdisc_build_delete_request(qdisc, &msg)) < 0) - return err; - - err = nl_send_auto_complete(sk, msg); - nlmsg_free(msg); - if (err < 0) - return err; - - return wait_for_ack(sk); -} - -/** @} */ - -/** - * @name Qdisc Cache Management - * @{ - */ - -/** - * Build a qdisc cache including all qdiscs currently configured in - * the kernel - * @arg sk Netlink socket. - * @arg result Pointer to store resulting message. - * - * Allocates a new cache, initializes it properly and updates it to - * include all qdiscs currently configured in the kernel. - * - * @return 0 on success or a negative error code. - */ -int rtnl_qdisc_alloc_cache(struct nl_sock *sk, struct nl_cache **result) -{ - return nl_cache_alloc_and_fill(&rtnl_qdisc_ops, sk, result); -} - -/** - * Look up qdisc by its parent in the provided cache - * @arg cache qdisc cache - * @arg ifindex interface the qdisc is attached to - * @arg parent parent handle - * @return pointer to qdisc inside the cache or NULL if no match was found. - */ -struct rtnl_qdisc * rtnl_qdisc_get_by_parent(struct nl_cache *cache, - int ifindex, uint32_t parent) -{ - struct rtnl_qdisc *q; - - if (cache->c_ops != &rtnl_qdisc_ops) - return NULL; - - nl_list_for_each_entry(q, &cache->c_items, ce_list) { - if (q->q_parent == parent && q->q_ifindex == ifindex) { - nl_object_get((struct nl_object *) q); - return q; - } - } - - return NULL; -} - -/** - * Look up qdisc by its handle in the provided cache - * @arg cache qdisc cache - * @arg ifindex interface the qdisc is attached to - * @arg handle qdisc handle - * @return pointer to qdisc inside the cache or NULL if no match was found. - */ -struct rtnl_qdisc * rtnl_qdisc_get(struct nl_cache *cache, - int ifindex, uint32_t handle) -{ - struct rtnl_qdisc *q; - - if (cache->c_ops != &rtnl_qdisc_ops) - return NULL; - - nl_list_for_each_entry(q, &cache->c_items, ce_list) { - if (q->q_handle == handle && q->q_ifindex == ifindex) { - nl_object_get((struct nl_object *) q); - return q; - } - } - - return NULL; -} - -/** @} */ - /** * @name Allocation/Freeing * @{ @@ -428,6 +81,361 @@ void rtnl_qdisc_put(struct rtnl_qdisc *qdisc) /** @} */ +/** + * @name Addition / Modification / Deletion + * @{ + */ + +static int build_qdisc_msg(struct rtnl_qdisc *qdisc, int type, int flags, + struct nl_msg **result) +{ + if (!(qdisc->ce_mask & TCA_ATTR_IFINDEX)) { + APPBUG("ifindex must be specified"); + return -NLE_MISSING_ATTR; + } + + return rtnl_tc_msg_build(TC_CAST(qdisc), type, flags, result); +} + +/** + * Build a netlink message requesting the addition of a qdisc + * @arg qdisc Qdisc to add + * @arg flags Additional netlink message flags + * @arg result Pointer to store resulting netlink message + * + * The behaviour of this function is identical to rtnl_qdisc_add() with + * the exception that it will not send the message but return it int the + * provided return pointer instead. + * + * @see rtnl_qdisc_add() + * + * @return 0 on success or a negative error code. + */ +int rtnl_qdisc_build_add_request(struct rtnl_qdisc *qdisc, int flags, + struct nl_msg **result) +{ + if (!(qdisc->ce_mask & (TCA_ATTR_HANDLE | TCA_ATTR_PARENT))) { + APPBUG("handle or parent must be specified"); + return -NLE_MISSING_ATTR; + } + + return build_qdisc_msg(qdisc, RTM_NEWQDISC, flags, result); +} + +/** + * Add qdisc + * @arg sk Netlink socket + * @arg qdisc Qdisc to add + * @arg flags Additional netlink message flags + * + * Builds a \c RTM_NEWQDISC netlink message requesting the addition + * of a new qdisc and sends the message to the kernel. The configuration + * of the qdisc is derived from the attributes of the specified qdisc. + * + * The following flags may be specified: + * - \c NLM_F_CREATE: Create qdisc if it does not exist, otherwise + * -NLE_OBJ_NOTFOUND is returned. + * - \c NLM_F_REPLACE: If another qdisc is already attached to the + * parent, replace it even if the handles mismatch. + * - \c NLM_F_EXCL: Return -NLE_EXISTS if a qdisc with matching + * handle exists already. + * + * Existing qdiscs with matching handles will be updated, unless the + * flag \c NLM_F_EXCL is specified. If their handles do not match, the + * error -NLE_EXISTS is returned unless the flag \c NLM_F_REPLACE is + * specified in which case the existing qdisc is replaced with the new + * one. If no matching qdisc exists, it will be created if the flag + * \c NLM_F_CREATE is set, otherwise the error -NLE_OBJ_NOTFOUND is + * returned. + * + * After sending, the function will wait for the ACK or an eventual + * error message to be received and will therefore block until the + * operation has been completed. + * + * @note Disabling auto-ack (nl_socket_disable_auto_ack()) will cause + * this function to return immediately after sending. In this case, + * it is the responsibility of the caller to handle any error + * messages returned. + * + * @return 0 on success or a negative error code. + */ +int rtnl_qdisc_add(struct nl_sock *sk, struct rtnl_qdisc *qdisc, int flags) +{ + struct nl_msg *msg; + int err; + + if ((err = rtnl_qdisc_build_add_request(qdisc, flags, &msg)) < 0) + return err; + + return nl_send_sync(sk, msg); +} + +/** + * Build netlink message requesting the update of a qdisc + * @arg qdisc Qdisc to update + * @arg new Qdisc with updated attributes + * @arg flags Additional netlink message flags + * @arg result Pointer to store resulting netlink message + * + * The behaviour of this function is identical to rtnl_qdisc_update() with + * the exception that it will not send the message but return it in the + * provided return pointer instead. + * + * @see rtnl_qdisc_update() + * + * @return 0 on success or a negative error code. + */ +int rtnl_qdisc_build_update_request(struct rtnl_qdisc *qdisc, + struct rtnl_qdisc *new, int flags, + struct nl_msg **result) +{ + if (flags & (NLM_F_CREATE | NLM_F_EXCL)) { + APPBUG("NLM_F_CREATE and NLM_F_EXCL may not be used here, " + "use rtnl_qdisc_add()"); + return -NLE_INVAL; + } + + if (!(qdisc->ce_mask & TCA_ATTR_IFINDEX)) { + APPBUG("ifindex must be specified"); + return -NLE_MISSING_ATTR; + } + + if (!(qdisc->ce_mask & (TCA_ATTR_HANDLE | TCA_ATTR_PARENT))) { + APPBUG("handle or parent must be specified"); + return -NLE_MISSING_ATTR; + } + + rtnl_tc_set_ifindex(TC_CAST(new), qdisc->q_ifindex); + + if (qdisc->ce_mask & TCA_ATTR_HANDLE) + rtnl_tc_set_handle(TC_CAST(new), qdisc->q_handle); + + if (qdisc->ce_mask & TCA_ATTR_PARENT) + rtnl_tc_set_parent(TC_CAST(new), qdisc->q_parent); + + return build_qdisc_msg(new, RTM_NEWQDISC, flags, result); +} + +/** + * Update qdisc + * @arg sk Netlink socket + * @arg qdisc Qdisc to update + * @arg new Qdisc with updated attributes + * @arg flags Additional netlink message flags + * + * Builds a \c RTM_NEWQDISC netlink message requesting the update + * of an existing qdisc and sends the message to the kernel. + * + * This function is a varation of rtnl_qdisc_add() to update qdiscs + * if the qdisc to be updated is available as qdisc object. The + * behaviour is identical to the one of rtnl_qdisc_add except that + * before constructing the message, it copies the \c ifindex, + * \c handle, and \c parent from the original \p qdisc to the \p new + * qdisc. + * + * After sending, the function will wait for the ACK or an eventual + * error message to be received and will therefore block until the + * operation has been completed. + * + * @note Disabling auto-ack (nl_socket_disable_auto_ack()) will cause + * this function to return immediately after sending. In this case, + * it is the responsibility of the caller to handle any error + * messages returned. + * + * @return 0 on success or a negative error code. + */ +int rtnl_qdisc_update(struct nl_sock *sk, struct rtnl_qdisc *qdisc, + struct rtnl_qdisc *new, int flags) +{ + struct nl_msg *msg; + int err; + + err = rtnl_qdisc_build_update_request(qdisc, new, flags, &msg); + if (err < 0) + return err; + + return nl_send_sync(sk, msg); +} + +/** + * Build netlink message requesting the deletion of a qdisc + * @arg qdisc Qdisc to delete + * @arg result Pointer to store resulting netlink message + * + * The behaviour of this function is identical to rtnl_qdisc_delete() with + * the exception that it will not send the message but return it in the + * provided return pointer instead. + * + * @see rtnl_qdisc_delete() + * + * @return 0 on success or a negative error code. + */ +int rtnl_qdisc_build_delete_request(struct rtnl_qdisc *qdisc, + struct nl_msg **result) +{ + struct nl_msg *msg; + struct tcmsg tchdr; + int required = TCA_ATTR_IFINDEX | TCA_ATTR_PARENT; + + if ((qdisc->ce_mask & required) != required) { + APPBUG("ifindex and parent must be specified"); + return -NLE_MISSING_ATTR; + } + + if (!(msg = nlmsg_alloc_simple(RTM_DELQDISC, 0))) + return -NLE_NOMEM; + + memset(&tchdr, 0, sizeof(tchdr)); + + tchdr.tcm_family = AF_UNSPEC; + tchdr.tcm_ifindex = qdisc->q_ifindex; + tchdr.tcm_parent = qdisc->q_parent; + + if (qdisc->ce_mask & TCA_ATTR_HANDLE) + tchdr.tcm_handle = qdisc->q_handle; + + if (nlmsg_append(msg, &tchdr, sizeof(tchdr), NLMSG_ALIGNTO) < 0) + goto nla_put_failure; + + if (qdisc->ce_mask & TCA_ATTR_KIND) + NLA_PUT_STRING(msg, TCA_KIND, qdisc->q_kind); + + *result = msg; + return 0; + +nla_put_failure: + nlmsg_free(msg); + return -NLE_MSGSIZE; +} + +/** + * Delete qdisc + * @arg sk Netlink socket + * @arg qdisc Qdisc to add + * + * Builds a \c RTM_NEWQDISC netlink message requesting the deletion + * of a qdisc and sends the message to the kernel. + * + * The message is constructed out of the following attributes: + * - \c ifindex and \c parent + * - \c handle (optional, must match if provided) + * - \c kind (optional, must match if provided) + * + * All other qdisc attributes including all qdisc type specific + * attributes are ignored. + * + * After sending, the function will wait for the ACK or an eventual + * error message to be received and will therefore block until the + * operation has been completed. + * + * @note It is not possible to delete default qdiscs. + * + * @note Disabling auto-ack (nl_socket_disable_auto_ack()) will cause + * this function to return immediately after sending. In this case, + * it is the responsibility of the caller to handle any error + * messages returned. + * + * @return 0 on success or a negative error code. + */ +int rtnl_qdisc_delete(struct nl_sock *sk, struct rtnl_qdisc *qdisc) +{ + struct nl_msg *msg; + int err; + + if ((err = rtnl_qdisc_build_delete_request(qdisc, &msg)) < 0) + return err; + + return nl_send_sync(sk, msg); +} + +/** @} */ + +/** + * @name Cache Related Functions + * @{ + */ + +/** + * Allocate a cache and fill it with all configured qdiscs + * @arg sk Netlink socket + * @arg result Pointer to store the created cache + * + * Allocates a new qdisc cache and fills it with a list of all configured + * qdiscs on all network devices. Release the cache with nl_cache_free(). + * + * @return 0 on success or a negative error code. + */ +int rtnl_qdisc_alloc_cache(struct nl_sock *sk, struct nl_cache **result) +{ + return nl_cache_alloc_and_fill(&rtnl_qdisc_ops, sk, result); +} + +/** + * Search qdisc by interface index and parent + * @arg cache Qdisc cache + * @arg ifindex Interface index + * @arg parent Handle of parent qdisc + * + * Searches a qdisc cache previously allocated with rtnl_qdisc_alloc_cache() + * and searches for a qdisc matching the interface index and parent qdisc. + * + * The reference counter is incremented before returning the qdisc, therefore + * the reference must be given back with rtnl_qdisc_put() after usage. + * + * @return pointer to qdisc inside the cache or NULL if no match was found. + */ +struct rtnl_qdisc *rtnl_qdisc_get_by_parent(struct nl_cache *cache, + int ifindex, uint32_t parent) +{ + struct rtnl_qdisc *q; + + if (cache->c_ops != &rtnl_qdisc_ops) + return NULL; + + nl_list_for_each_entry(q, &cache->c_items, ce_list) { + if (q->q_parent == parent && q->q_ifindex == ifindex) { + nl_object_get((struct nl_object *) q); + return q; + } + } + + return NULL; +} + +/** + * Search qdisc by interface index and handle + * @arg cache Qdisc cache + * @arg ifindex Interface index + * @arg handle Handle + * + * Searches a qdisc cache previously allocated with rtnl_qdisc_alloc_cache() + * and searches for a qdisc matching the interface index and handle. + * + * The reference counter is incremented before returning the qdisc, therefore + * the reference must be given back with rtnl_qdisc_put() after usage. + * + * @return Qdisc or NULL if no match was found. + */ +struct rtnl_qdisc *rtnl_qdisc_get(struct nl_cache *cache, int ifindex, + uint32_t handle) +{ + struct rtnl_qdisc *q; + + if (cache->c_ops != &rtnl_qdisc_ops) + return NULL; + + nl_list_for_each_entry(q, &cache->c_items, ce_list) { + if (q->q_handle == handle && q->q_ifindex == ifindex) { + nl_object_get((struct nl_object *) q); + return q; + } + } + + return NULL; +} + +/** @} */ + /** * @name Deprecated Functions * @{ @@ -478,6 +486,34 @@ void rtnl_qdisc_foreach_cls(struct rtnl_qdisc *qdisc, struct nl_cache *cache, rtnl_cls_put(filter); } +/** + * Build a netlink message requesting the update of a qdisc + * + * @deprecated Use of this function is deprecated in favour of + * rtnl_qdisc_build_update_request() due to the missing + * possibility of specifying additional flags. + */ +int rtnl_qdisc_build_change_request(struct rtnl_qdisc *qdisc, + struct rtnl_qdisc *new, + struct nl_msg **result) +{ + return rtnl_qdisc_build_update_request(qdisc, new, NLM_F_REPLACE, + result); +} + +/** + * Change attributes of a qdisc + * + * @deprecated Use of this function is deprecated in favour of + * rtnl_qdisc_update() due to the missing possibility of + * specifying additional flags. + */ +int rtnl_qdisc_change(struct nl_sock *sk, struct rtnl_qdisc *qdisc, + struct rtnl_qdisc *new) +{ + return rtnl_qdisc_update(sk, qdisc, new, NLM_F_REPLACE); +} + /** @} */ static void qdisc_dump_details(struct rtnl_tc *tc, struct nl_dump_params *p) diff --git a/lib/route/tc.c b/lib/route/tc.c index 09ae044..56aa410 100644 --- a/lib/route/tc.c +++ b/lib/route/tc.c @@ -189,11 +189,7 @@ int rtnl_tc_msg_build(struct rtnl_tc *tc, int type, int flags, .tcm_handle = tc->tc_handle, .tcm_parent = tc->tc_parent, }; - int req, err = -NLE_MSGSIZE; - - req = (TCA_ATTR_IFINDEX | TCA_ATTR_PARENT | TCA_ATTR_KIND); - if ((tc->ce_mask & req) != req) - return -NLE_MISSING_ATTR; + int err = -NLE_MSGSIZE; msg = nlmsg_alloc_simple(type, flags); if (!msg)