qdisc.c revision 1155370f520cb64657e25153255cf7dc1424317f
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-2008 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;
102	struct rtnl_qdisc *qdisc;
103	struct rtnl_qdisc_ops *qops;
104
105	qdisc = rtnl_qdisc_alloc();
106	if (!qdisc) {
107		err = -NLE_NOMEM;
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_sock *sk)
137{
138	struct tcmsg tchdr = {
139		.tcm_family = AF_UNSPEC,
140		.tcm_ifindex = c->c_iarg1,
141	};
142
143	return nl_send_simple(sk, RTM_GETQDISC, NLM_F_DUMP, &tchdr,
144			      sizeof(tchdr));
145}
146
147/**
148 * @name QDisc Addition
149 * @{
150 */
151
152static int qdisc_build(struct rtnl_qdisc *qdisc, int type, int flags,
153		       struct nl_msg **result)
154{
155	struct rtnl_qdisc_ops *qops;
156	int err;
157
158	err = tca_build_msg((struct rtnl_tca *) qdisc, type, flags, result);
159	if (err < 0)
160		return err;
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(*result, 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, *result);
179		if (err < 0)
180			goto errout;
181	}
182
183	return 0;
184errout:
185	nlmsg_free(*result);
186
187	return err;
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 * @arg result		Pointer to store resulting message.
195 *
196 * Builds a new netlink message requesting an addition of a qdisc.
197 * The netlink message header isn't fully equipped with all relevant
198 * fields and must be sent out via nl_send_auto_complete() or
199 * supplemented as needed.
200 *
201 * Common message flags used:
202 *  - NLM_F_REPLACE - replace a potential existing qdisc
203 *
204 * @return 0 on success or a negative error code.
205 */
206int rtnl_qdisc_build_add_request(struct rtnl_qdisc *qdisc, int flags,
207				 struct nl_msg **result)
208{
209	return qdisc_build(qdisc, RTM_NEWQDISC, NLM_F_CREATE | flags, result);
210}
211
212/**
213 * Add a new qdisc
214 * @arg sk		Netlink socket.
215 * @arg qdisc		qdisc to delete
216 * @arg flags		additional netlink message flags
217 *
218 * Builds a netlink message by calling rtnl_qdisc_build_add_request(),
219 * sends the request to the kernel and waits for the ACK to be
220 * received and thus blocks until the request has been processed.
221 *
222 * Common message flags used:
223 *  - NLM_F_REPLACE - replace a potential existing qdisc
224 *
225 * @return 0 on success or a negative error code
226 */
227int rtnl_qdisc_add(struct nl_sock *sk, struct rtnl_qdisc *qdisc,
228		   int flags)
229{
230	struct nl_msg *msg;
231	int err;
232
233	if ((err = rtnl_qdisc_build_add_request(qdisc, flags, &msg)) < 0)
234		return err;
235
236	if ((err = nl_send_auto_complete(sk, msg)) < 0)
237		return err;
238
239	nlmsg_free(msg);
240	return nl_wait_for_ack(sk);
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 * @arg result		Pointer to store resulting message.
255 *
256 * Builds a new netlink message requesting an change of qdisc
257 * attributes. The netlink message header isn't fully equipped
258 * with all relevant fields and must be sent out via
259 * nl_send_auto_complete() or supplemented as needed.
260 *
261 * @return 0 on success or a negative error code.
262 */
263int rtnl_qdisc_build_change_request(struct rtnl_qdisc *qdisc,
264				    struct rtnl_qdisc *new,
265				    struct nl_msg **result)
266{
267	return qdisc_build(qdisc, RTM_NEWQDISC, NLM_F_REPLACE, result);
268}
269
270/**
271 * Change attributes of a qdisc
272 * @arg sk		Netlink socket.
273 * @arg qdisc		qdisc to change
274 * @arg new		new qdisc attributes
275 *
276 * Builds a netlink message by calling rtnl_qdisc_build_change_request(),
277 * sends the request to the kernel and waits for the ACK to be
278 * received and thus blocks until the request has been processed.
279 *
280 * @return 0 on success or a negative error code
281 */
282int rtnl_qdisc_change(struct nl_sock *sk, struct rtnl_qdisc *qdisc,
283		      struct rtnl_qdisc *new)
284{
285	struct nl_msg *msg;
286	int err;
287
288	if ((err = rtnl_qdisc_build_change_request(qdisc, new, &msg)) < 0)
289		return err;
290
291	if ((err = nl_send_auto_complete(sk, msg)) < 0)
292		return err;
293
294	nlmsg_free(msg);
295	return nl_wait_for_ack(sk);
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 * @arg result		Pointer to store resulting message.
309 *
310 * Builds a new netlink message requesting a deletion of a qdisc.
311 * The netlink message header isn't fully equipped with all relevant
312 * fields and must thus be sent out via nl_send_auto_complete()
313 * or supplemented as needed.
314 *
315 * @return 0 on success or a negative error code.
316 */
317int rtnl_qdisc_build_delete_request(struct rtnl_qdisc *qdisc,
318				    struct nl_msg **result)
319{
320	struct nl_msg *msg;
321	struct tcmsg tchdr;
322	int required = TCA_ATTR_IFINDEX | TCA_ATTR_PARENT;
323
324	if ((qdisc->ce_mask & required) != required)
325		BUG();
326
327	msg = nlmsg_alloc_simple(RTM_DELQDISC, 0);
328	if (!msg)
329		return -NLE_NOMEM;
330
331	tchdr.tcm_family = AF_UNSPEC;
332	tchdr.tcm_handle = qdisc->q_handle;
333	tchdr.tcm_parent = qdisc->q_parent;
334	tchdr.tcm_ifindex = qdisc->q_ifindex;
335	if (nlmsg_append(msg, &tchdr, sizeof(tchdr), NLMSG_ALIGNTO) < 0) {
336		nlmsg_free(msg);
337		return -NLE_MSGSIZE;
338	}
339
340	*result = msg;
341	return 0;
342}
343
344/**
345 * Delete a qdisc
346 * @arg sk		Netlink socket.
347 * @arg qdisc		qdisc to delete
348 *
349 * Builds a netlink message by calling rtnl_qdisc_build_delete_request(),
350 * sends the request to the kernel and waits for the ACK to be
351 * received and thus blocks until the request has been processed.
352 *
353 * @return 0 on success or a negative error code
354 */
355int rtnl_qdisc_delete(struct nl_sock *sk, struct rtnl_qdisc *qdisc)
356{
357	struct nl_msg *msg;
358	int err;
359
360	if ((err = rtnl_qdisc_build_delete_request(qdisc, &msg)) < 0)
361		return err;
362
363	if ((err = nl_send_auto_complete(sk, msg)) < 0)
364		return err;
365
366	nlmsg_free(msg);
367	return nl_wait_for_ack(sk);
368}
369
370/** @} */
371
372/**
373 * @name Qdisc Cache Management
374 * @{
375 */
376
377/**
378 * Build a qdisc cache including all qdiscs currently configured in
379 * the kernel
380 * @arg sk		Netlink socket.
381 * @arg result		Pointer to store resulting message.
382 *
383 * Allocates a new cache, initializes it properly and updates it to
384 * include all qdiscs currently configured in the kernel.
385 *
386 * @return 0 on success or a negative error code.
387 */
388int rtnl_qdisc_alloc_cache(struct nl_sock *sk, struct nl_cache **result)
389{
390	return nl_cache_alloc_and_fill(&rtnl_qdisc_ops, sk, result);
391}
392
393/**
394 * Look up qdisc by its parent in the provided cache
395 * @arg cache		qdisc cache
396 * @arg ifindex		interface the qdisc is attached to
397 * @arg parent		parent handle
398 * @return pointer to qdisc inside the cache or NULL if no match was found.
399 */
400struct rtnl_qdisc * rtnl_qdisc_get_by_parent(struct nl_cache *cache,
401					     int ifindex, uint32_t parent)
402{
403	struct rtnl_qdisc *q;
404
405	if (cache->c_ops != &rtnl_qdisc_ops)
406		return NULL;
407
408	nl_list_for_each_entry(q, &cache->c_items, ce_list) {
409		if (q->q_parent == parent && q->q_ifindex == ifindex) {
410			nl_object_get((struct nl_object *) q);
411			return q;
412		}
413	}
414
415	return NULL;
416}
417
418/**
419 * Look up qdisc by its handle in the provided cache
420 * @arg cache		qdisc cache
421 * @arg ifindex		interface the qdisc is attached to
422 * @arg handle		qdisc handle
423 * @return pointer to qdisc inside the cache or NULL if no match was found.
424 */
425struct rtnl_qdisc * rtnl_qdisc_get(struct nl_cache *cache,
426				   int ifindex, uint32_t handle)
427{
428	struct rtnl_qdisc *q;
429
430	if (cache->c_ops != &rtnl_qdisc_ops)
431		return NULL;
432
433	nl_list_for_each_entry(q, &cache->c_items, ce_list) {
434		if (q->q_handle == handle && q->q_ifindex == ifindex) {
435			nl_object_get((struct nl_object *) q);
436			return q;
437		}
438	}
439
440	return NULL;
441}
442
443/** @} */
444
445static struct nl_cache_ops rtnl_qdisc_ops = {
446	.co_name		= "route/qdisc",
447	.co_hdrsize		= sizeof(struct tcmsg),
448	.co_msgtypes		= {
449					{ RTM_NEWQDISC, NL_ACT_NEW, "new" },
450					{ RTM_DELQDISC, NL_ACT_DEL, "del" },
451					{ RTM_GETQDISC, NL_ACT_GET, "get" },
452					END_OF_MSGTYPES_LIST,
453				  },
454	.co_protocol		= NETLINK_ROUTE,
455	.co_request_update	= qdisc_request_update,
456	.co_msg_parser		= qdisc_msg_parser,
457	.co_obj_ops		= &qdisc_obj_ops,
458};
459
460static void __init qdisc_init(void)
461{
462	nl_cache_mngt_register(&rtnl_qdisc_ops);
463}
464
465static void __exit qdisc_exit(void)
466{
467	nl_cache_mngt_unregister(&rtnl_qdisc_ops);
468}
469
470/** @} */
471