qdisc.c revision 054c80d775f2ae9b8f50260bdfcb821e99c0da2a
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@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
28static struct nl_cache_ops rtnl_qdisc_ops;
29static struct nl_object_ops qdisc_obj_ops;
30
31static 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);
44errout:
45	rtnl_qdisc_put(qdisc);
46
47	return err;
48}
49
50static 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
66struct 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
77void 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
89static 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 */
114int 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 */
162int 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 */
188int 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 */
247int 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 */
273int 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
306nla_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 */
340int 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 */
368int 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 */
387struct 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 */
419struct 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 */
450void 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 */
474void 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 */
496int 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 */
511int 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
519static 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
526static 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
534static 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
550static 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
564static void __init qdisc_init(void)
565{
566	rtnl_tc_type_register(&qdisc_ops);
567	nl_cache_mngt_register(&rtnl_qdisc_ops);
568}
569
570static 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