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