qdisc.c revision 241b2b83ba5672f5c86154d29eeb8ef4c7c6e9b4
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-2006 Thomas Graf <tgraf@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
96static struct nl_cache_ops rtnl_qdisc_ops;
97
98static 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 = -ENOMEM;
102	struct rtnl_qdisc *qdisc;
103	struct rtnl_qdisc_ops *qops;
104
105	qdisc = rtnl_qdisc_alloc();
106	if (!qdisc) {
107		err = nl_errno(ENOMEM);
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	if (err < 0)
126		goto errout_free;
127
128	err = P_ACCEPT;
129
130errout_free:
131	rtnl_qdisc_put(qdisc);
132errout:
133	return err;
134}
135
136static int qdisc_request_update(struct nl_cache *c, struct nl_handle *h)
137{
138	struct tcmsg tchdr = {
139		.tcm_family = AF_UNSPEC,
140		.tcm_ifindex = c->c_iarg1,
141	};
142
143	return nl_send_simple(h, RTM_GETQDISC, NLM_F_DUMP, &tchdr,
144			      sizeof(tchdr));
145}
146
147/**
148 * @name QDisc Addition
149 * @{
150 */
151
152static struct nl_msg *qdisc_build(struct rtnl_qdisc *qdisc, int type, int flags)
153{
154	struct rtnl_qdisc_ops *qops;
155	struct nl_msg *msg;
156	int err;
157
158	msg = tca_build_msg((struct rtnl_tca *) qdisc, type, flags);
159	if (!msg)
160		goto errout;
161
162	qops = rtnl_qdisc_lookup_ops(qdisc);
163	if (qops && qops->qo_get_opts) {
164		struct nl_msg *opts;
165
166		opts = qops->qo_get_opts(qdisc);
167		if (opts) {
168			err = nla_put_nested(msg, TCA_OPTIONS, opts);
169			nlmsg_free(opts);
170			if (err < 0)
171				goto errout;
172		}
173	}
174	/* Some qdiscs don't accept properly nested messages (e.g. netem). To
175	 * accomodate for this, they can complete the message themselves.
176	 */
177	else if (qops && qops->qo_build_msg) {
178		err = qops->qo_build_msg(qdisc, msg);
179		if ( err < 0 )
180			goto errout;
181	}
182
183	return msg;
184errout:
185	nlmsg_free(msg);
186
187	return NULL;
188}
189
190/**
191 * Build a netlink message to add a new qdisc
192 * @arg qdisc		qdisc to add
193 * @arg flags		additional netlink message flags
194 *
195 * Builds a new netlink message requesting an addition of a qdisc.
196 * The netlink message header isn't fully equipped with all relevant
197 * fields and must be sent out via nl_send_auto_complete() or
198 * supplemented as needed.
199 *
200 * Common message flags used:
201 *  - NLM_F_REPLACE - replace a potential existing qdisc
202 *
203 * @return New netlink message
204 */
205struct nl_msg *rtnl_qdisc_build_add_request(struct rtnl_qdisc *qdisc,
206					    int flags)
207{
208	struct nl_msg *msg;
209
210	msg = qdisc_build(qdisc, RTM_NEWQDISC, NLM_F_CREATE | flags);
211	if (!msg)
212		nl_errno(ENOMEM);
213
214	return msg;
215}
216
217/**
218 * Add a new qdisc
219 * @arg handle		netlink handle
220 * @arg qdisc		qdisc to delete
221 * @arg flags		additional netlink message flags
222 *
223 * Builds a netlink message by calling rtnl_qdisc_build_add_request(),
224 * sends the request to the kernel and waits for the ACK to be
225 * received and thus blocks until the request has been processed.
226 *
227 * Common message flags used:
228 *  - NLM_F_REPLACE - replace a potential existing qdisc
229 *
230 * @return 0 on success or a negative error code
231 */
232int rtnl_qdisc_add(struct nl_handle *handle, struct rtnl_qdisc *qdisc,
233		   int flags)
234{
235	struct nl_msg *msg;
236	int err;
237
238	msg = rtnl_qdisc_build_add_request(qdisc, flags);
239	if (!msg)
240		return nl_errno(ENOMEM);
241
242	err = nl_send_auto_complete(handle, msg);
243	if (err < 0)
244		return err;
245
246	nlmsg_free(msg);
247	return nl_wait_for_ack(handle);
248}
249
250/** @} */
251
252/**
253 * @name QDisc Modification
254 * @{
255 */
256
257/**
258 * Build a netlink message to change attributes of a existing qdisc
259 * @arg qdisc		qdisc to change
260 * @arg new		new qdisc attributes
261 *
262 * Builds a new netlink message requesting an change of qdisc
263 * attributes. The netlink message header isn't fully equipped
264 * with all relevant fields and must be sent out via
265 * nl_send_auto_complete() or supplemented as needed.
266 *
267 * @return New netlink message
268 */
269struct nl_msg *rtnl_qdisc_build_change_request(struct rtnl_qdisc *qdisc,
270					       struct rtnl_qdisc *new)
271{
272	return qdisc_build(qdisc, RTM_NEWQDISC, NLM_F_REPLACE);
273}
274
275/**
276 * Change attributes of a qdisc
277 * @arg handle		netlink handle
278 * @arg qdisc		qdisc to change
279 * @arg new		new qdisc attributes
280 *
281 * Builds a netlink message by calling rtnl_qdisc_build_change_request(),
282 * sends the request to the kernel and waits for the ACK to be
283 * received and thus blocks until the request has been processed.
284 *
285 * @return 0 on success or a negative error code
286 */
287int rtnl_qdisc_change(struct nl_handle *handle, struct rtnl_qdisc *qdisc,
288		      struct rtnl_qdisc *new)
289{
290	struct nl_msg *msg;
291	int err;
292
293	msg = rtnl_qdisc_build_change_request(qdisc, new);
294	if (!msg)
295		return nl_errno(ENOMEM);
296
297	err = nl_send_auto_complete(handle, msg);
298	if (err < 0)
299		return err;
300
301	nlmsg_free(msg);
302	return nl_wait_for_ack(handle);
303}
304
305/** @} */
306
307/**
308 * @name QDisc Deletion
309 * @{
310 */
311
312/**
313 * Build a netlink request message to delete a qdisc
314 * @arg qdisc		qdisc to delete
315 *
316 * Builds a new netlink message requesting a deletion of a qdisc.
317 * The netlink message header isn't fully equipped with all relevant
318 * fields and must thus be sent out via nl_send_auto_complete()
319 * or supplemented as needed.
320 *
321 * @return New netlink message
322 */
323struct nl_msg *rtnl_qdisc_build_delete_request(struct rtnl_qdisc *qdisc)
324{
325	struct nl_msg *msg;
326	struct tcmsg tchdr;
327	int required = TCA_ATTR_IFINDEX | TCA_ATTR_PARENT;
328
329	if ((qdisc->ce_mask & required) != required)
330		BUG();
331
332	msg = nlmsg_alloc_simple(RTM_DELQDISC, 0);
333	if (!msg)
334		return NULL;
335
336	tchdr.tcm_family = AF_UNSPEC,
337	tchdr.tcm_handle = qdisc->q_handle,
338	tchdr.tcm_parent = qdisc->q_parent,
339	tchdr.tcm_ifindex = qdisc->q_ifindex,
340	nlmsg_append(msg, &tchdr, sizeof(tchdr), NLMSG_ALIGNTO);
341
342	return msg;
343}
344
345/**
346 * Delete a qdisc
347 * @arg handle		netlink handle
348 * @arg qdisc		qdisc to delete
349 *
350 * Builds a netlink message by calling rtnl_qdisc_build_delete_request(),
351 * sends the request to the kernel and waits for the ACK to be
352 * received and thus blocks until the request has been processed.
353 *
354 * @return 0 on success or a negative error code
355 */
356int rtnl_qdisc_delete(struct nl_handle *handle, struct rtnl_qdisc *qdisc)
357{
358	struct nl_msg *msg;
359	int err;
360
361	msg = rtnl_qdisc_build_delete_request(qdisc);
362	if (!msg)
363		return nl_errno(ENOMEM);
364
365	err = nl_send_auto_complete(handle, msg);
366	if (err < 0)
367		return err;
368
369	nlmsg_free(msg);
370	return nl_wait_for_ack(handle);
371}
372
373/** @} */
374
375/**
376 * @name Qdisc Cache Management
377 * @{
378 */
379
380/**
381 * Build a qdisc cache including all qdiscs currently configured in
382 * the kernel
383 * @arg handle		netlink handle
384 *
385 * Allocates a new cache, initializes it properly and updates it to
386 * include all qdiscs currently configured in the kernel.
387 *
388 * @note The caller is responsible for destroying and freeing the
389 *       cache after using it.
390 * @return The cache or NULL if an error has occured.
391 */
392struct nl_cache * rtnl_qdisc_alloc_cache(struct nl_handle *handle)
393{
394	struct nl_cache * cache;
395
396	cache = nl_cache_alloc(&rtnl_qdisc_ops);
397	if (cache == NULL)
398		return NULL;
399
400	if (handle && nl_cache_refill(handle, cache) < 0) {
401		nl_cache_free(cache);
402		return NULL;
403	}
404
405	return cache;
406}
407
408/**
409 * Look up qdisc by its parent in the provided cache
410 * @arg cache		qdisc cache
411 * @arg ifindex		interface the qdisc is attached to
412 * @arg parent		parent handle
413 * @return pointer to qdisc inside the cache or NULL if no match was found.
414 */
415struct rtnl_qdisc * rtnl_qdisc_get_by_parent(struct nl_cache *cache,
416					     int ifindex, uint32_t parent)
417{
418	struct rtnl_qdisc *q;
419
420	if (cache->c_ops != &rtnl_qdisc_ops)
421		return NULL;
422
423	nl_list_for_each_entry(q, &cache->c_items, ce_list) {
424		if (q->q_parent == parent && q->q_ifindex == ifindex) {
425			nl_object_get((struct nl_object *) q);
426			return q;
427		}
428	}
429
430	return NULL;
431}
432
433/**
434 * Look up qdisc by its handle in the provided cache
435 * @arg cache		qdisc cache
436 * @arg ifindex		interface the qdisc is attached to
437 * @arg handle		qdisc handle
438 * @return pointer to qdisc inside the cache or NULL if no match was found.
439 */
440struct rtnl_qdisc * rtnl_qdisc_get(struct nl_cache *cache,
441				   int ifindex, uint32_t handle)
442{
443	struct rtnl_qdisc *q;
444
445	if (cache->c_ops != &rtnl_qdisc_ops)
446		return NULL;
447
448	nl_list_for_each_entry(q, &cache->c_items, ce_list) {
449		if (q->q_handle == handle && q->q_ifindex == ifindex) {
450			nl_object_get((struct nl_object *) q);
451			return q;
452		}
453	}
454
455	return NULL;
456}
457
458/** @} */
459
460static struct nl_cache_ops rtnl_qdisc_ops = {
461	.co_name		= "route/qdisc",
462	.co_hdrsize		= sizeof(struct tcmsg),
463	.co_msgtypes		= {
464					{ RTM_NEWQDISC, NL_ACT_NEW, "new" },
465					{ RTM_DELQDISC, NL_ACT_DEL, "del" },
466					{ RTM_GETQDISC, NL_ACT_GET, "get" },
467					END_OF_MSGTYPES_LIST,
468				  },
469	.co_protocol		= NETLINK_ROUTE,
470	.co_request_update	= qdisc_request_update,
471	.co_msg_parser		= qdisc_msg_parser,
472	.co_obj_ops		= &qdisc_obj_ops,
473};
474
475static void __init qdisc_init(void)
476{
477	nl_cache_mngt_register(&rtnl_qdisc_ops);
478}
479
480static void __exit qdisc_exit(void)
481{
482	nl_cache_mngt_unregister(&rtnl_qdisc_ops);
483}
484
485/** @} */
486