libnl 1.1.4
qdisc.c
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
175 return msg;
176errout:
177 nlmsg_free(msg);
178
179 return NULL;
180}
181
182/**
183 * Build a netlink message to add a new qdisc
184 * @arg qdisc qdisc to add
185 * @arg flags additional netlink message flags
186 *
187 * Builds a new netlink message requesting an addition of a qdisc.
188 * The netlink message header isn't fully equipped with all relevant
189 * fields and must be sent out via nl_send_auto_complete() or
190 * supplemented as needed.
191 *
192 * Common message flags used:
193 * - NLM_F_REPLACE - replace a potential existing qdisc
194 *
195 * @return New netlink message
196 */
197struct nl_msg *rtnl_qdisc_build_add_request(struct rtnl_qdisc *qdisc,
198 int flags)
199{
200 struct nl_msg *msg;
201
202 msg = qdisc_build(qdisc, RTM_NEWQDISC, NLM_F_CREATE | flags);
203 if (!msg)
204 nl_errno(ENOMEM);
205
206 return msg;
207}
208
209/**
210 * Add a new qdisc
211 * @arg handle netlink handle
212 * @arg qdisc qdisc to delete
213 * @arg flags additional netlink message flags
214 *
215 * Builds a netlink message by calling rtnl_qdisc_build_add_request(),
216 * sends the request to the kernel and waits for the ACK to be
217 * received and thus blocks until the request has been processed.
218 *
219 * Common message flags used:
220 * - NLM_F_REPLACE - replace a potential existing qdisc
221 *
222 * @return 0 on success or a negative error code
223 */
224int rtnl_qdisc_add(struct nl_handle *handle, struct rtnl_qdisc *qdisc,
225 int flags)
226{
227 struct nl_msg *msg;
228 int err;
229
230 msg = rtnl_qdisc_build_add_request(qdisc, flags);
231 if (!msg)
232 return nl_errno(ENOMEM);
233
234 err = nl_send_auto_complete(handle, msg);
235 nlmsg_free(msg);
236 if (err < 0)
237 return err;
238
239 return nl_wait_for_ack(handle);
240}
241
242/** @} */
243
244/**
245 * @name QDisc Modification
246 * @{
247 */
248
249/**
250 * Build a netlink message to change attributes of a existing qdisc
251 * @arg qdisc qdisc to change
252 * @arg new new qdisc attributes
253 *
254 * Builds a new netlink message requesting an change of qdisc
255 * attributes. The netlink message header isn't fully equipped
256 * with all relevant fields and must be sent out via
257 * nl_send_auto_complete() or supplemented as needed.
258 *
259 * @return New netlink message
260 */
261struct nl_msg *rtnl_qdisc_build_change_request(struct rtnl_qdisc *qdisc,
262 struct rtnl_qdisc *new)
263{
264 return qdisc_build(qdisc, RTM_NEWQDISC, NLM_F_REPLACE);
265}
266
267/**
268 * Change attributes of a qdisc
269 * @arg handle netlink handle
270 * @arg qdisc qdisc to change
271 * @arg new new qdisc attributes
272 *
273 * Builds a netlink message by calling rtnl_qdisc_build_change_request(),
274 * sends the request to the kernel and waits for the ACK to be
275 * received and thus blocks until the request has been processed.
276 *
277 * @return 0 on success or a negative error code
278 */
279int rtnl_qdisc_change(struct nl_handle *handle, struct rtnl_qdisc *qdisc,
280 struct rtnl_qdisc *new)
281{
282 struct nl_msg *msg;
283 int err;
284
285 msg = rtnl_qdisc_build_change_request(qdisc, new);
286 if (!msg)
287 return nl_errno(ENOMEM);
288
289 err = nl_send_auto_complete(handle, msg);
290 nlmsg_free(msg);
291 if (err < 0)
292 return err;
293
294 return nl_wait_for_ack(handle);
295}
296
297/** @} */
298
299/**
300 * @name QDisc Deletion
301 * @{
302 */
303
304/**
305 * Build a netlink request message to delete a qdisc
306 * @arg qdisc qdisc to delete
307 *
308 * Builds a new netlink message requesting a deletion of a qdisc.
309 * The netlink message header isn't fully equipped with all relevant
310 * fields and must thus be sent out via nl_send_auto_complete()
311 * or supplemented as needed.
312 *
313 * @return New netlink message
314 */
315struct nl_msg *rtnl_qdisc_build_delete_request(struct rtnl_qdisc *qdisc)
316{
317 struct nl_msg *msg;
318 struct tcmsg tchdr;
319 int required = TCA_ATTR_IFINDEX | TCA_ATTR_PARENT;
320
321 if ((qdisc->ce_mask & required) != required)
322 BUG();
323
324 msg = nlmsg_alloc_simple(RTM_DELQDISC, 0);
325 if (!msg)
326 return NULL;
327
328 tchdr.tcm_family = AF_UNSPEC,
329 tchdr.tcm_handle = qdisc->q_handle,
330 tchdr.tcm_parent = qdisc->q_parent,
331 tchdr.tcm_ifindex = qdisc->q_ifindex,
332 nlmsg_append(msg, &tchdr, sizeof(tchdr), NLMSG_ALIGNTO);
333
334 return msg;
335}
336
337/**
338 * Delete a qdisc
339 * @arg handle netlink handle
340 * @arg qdisc qdisc to delete
341 *
342 * Builds a netlink message by calling rtnl_qdisc_build_delete_request(),
343 * sends the request to the kernel and waits for the ACK to be
344 * received and thus blocks until the request has been processed.
345 *
346 * @return 0 on success or a negative error code
347 */
348int rtnl_qdisc_delete(struct nl_handle *handle, struct rtnl_qdisc *qdisc)
349{
350 struct nl_msg *msg;
351 int err;
352
354 if (!msg)
355 return nl_errno(ENOMEM);
356
357 err = nl_send_auto_complete(handle, msg);
358 nlmsg_free(msg);
359 if (err < 0)
360 return err;
361
362 return nl_wait_for_ack(handle);
363}
364
365/** @} */
366
367/**
368 * @name Qdisc Cache Management
369 * @{
370 */
371
372/**
373 * Build a qdisc cache including all qdiscs currently configured in
374 * the kernel
375 * @arg handle netlink handle
376 *
377 * Allocates a new cache, initializes it properly and updates it to
378 * include all qdiscs currently configured in the kernel.
379 *
380 * @note The caller is responsible for destroying and freeing the
381 * cache after using it.
382 * @return The cache or NULL if an error has occured.
383 */
384struct nl_cache * rtnl_qdisc_alloc_cache(struct nl_handle *handle)
385{
386 struct nl_cache * cache;
387
389 if (cache == NULL)
390 return NULL;
391
392 if (handle && nl_cache_refill(handle, cache) < 0) {
393 nl_cache_free(cache);
394 return NULL;
395 }
396
397 return cache;
398}
399
400/**
401 * Look up qdisc by its parent in the provided cache
402 * @arg cache qdisc cache
403 * @arg ifindex interface the qdisc is attached to
404 * @arg parent parent handle
405 * @return pointer to qdisc inside the cache or NULL if no match was found.
406 */
407struct rtnl_qdisc * rtnl_qdisc_get_by_parent(struct nl_cache *cache,
408 int ifindex, uint32_t parent)
409{
410 struct rtnl_qdisc *q;
411
412 if (cache->c_ops != &rtnl_qdisc_ops)
413 return NULL;
414
415 nl_list_for_each_entry(q, &cache->c_items, ce_list) {
416 if (q->q_parent == parent && q->q_ifindex == ifindex) {
417 nl_object_get((struct nl_object *) q);
418 return q;
419 }
420 }
421
422 return NULL;
423}
424
425/**
426 * Look up qdisc by its handle in the provided cache
427 * @arg cache qdisc cache
428 * @arg ifindex interface the qdisc is attached to
429 * @arg handle qdisc handle
430 * @return pointer to qdisc inside the cache or NULL if no match was found.
431 */
432struct rtnl_qdisc * rtnl_qdisc_get(struct nl_cache *cache,
433 int ifindex, uint32_t handle)
434{
435 struct rtnl_qdisc *q;
436
437 if (cache->c_ops != &rtnl_qdisc_ops)
438 return NULL;
439
440 nl_list_for_each_entry(q, &cache->c_items, ce_list) {
441 if (q->q_handle == handle && q->q_ifindex == ifindex) {
442 nl_object_get((struct nl_object *) q);
443 return q;
444 }
445 }
446
447 return NULL;
448}
449
450/** @} */
451
452static struct nl_cache_ops rtnl_qdisc_ops = {
453 .co_name = "route/qdisc",
454 .co_hdrsize = sizeof(struct tcmsg),
455 .co_msgtypes = {
456 { RTM_NEWQDISC, NL_ACT_NEW, "new" },
457 { RTM_DELQDISC, NL_ACT_DEL, "del" },
458 { RTM_GETQDISC, NL_ACT_GET, "get" },
459 END_OF_MSGTYPES_LIST,
460 },
461 .co_protocol = NETLINK_ROUTE,
462 .co_request_update = qdisc_request_update,
463 .co_msg_parser = qdisc_msg_parser,
464 .co_obj_ops = &qdisc_obj_ops,
465};
466
467static void __init qdisc_init(void)
468{
470}
471
472static void __exit qdisc_exit(void)
473{
475}
476
477/** @} */
int nla_put_nested(struct nl_msg *n, int attrtype, struct nl_msg *nested)
Add a nested netlink attribute to a netlink message.
Definition: attr.c:535
int nl_cache_mngt_unregister(struct nl_cache_ops *ops)
Unregister a set of cache operations.
Definition: cache_mngt.c:261
int nl_cache_mngt_register(struct nl_cache_ops *ops)
Register a set of cache operations.
Definition: cache_mngt.c:226
void nl_cache_free(struct nl_cache *cache)
Free a cache.
Definition: cache.c:277
struct nl_cache * nl_cache_alloc(struct nl_cache_ops *ops)
Allocate an empty cache.
Definition: cache.c:170
int nl_cache_refill(struct nl_handle *handle, struct nl_cache *cache)
(Re)fill a cache with the contents in the kernel.
Definition: cache.c:680
struct nl_msg * nlmsg_alloc_simple(int nlmsgtype, int flags)
Allocate a new netlink message.
Definition: msg.c:448
void nlmsg_free(struct nl_msg *n)
Free a netlink message.
Definition: msg.c:656
#define NLM_F_CREATE
Create config object if it doesn't already exist.
#define NLM_F_REPLACE
Replace existing matching config object with this request.
#define NLM_F_DUMP
Dump all entries.
int nlmsg_append(struct nl_msg *n, void *data, size_t len, int pad)
Append data to tail of a netlink message.
Definition: msg.c:549
int nl_send_auto_complete(struct nl_handle *handle, struct nl_msg *msg)
Send netlink message and check & extend header values as needed.
Definition: nl.c:373
int nl_send_simple(struct nl_handle *handle, int type, int flags, void *buf, size_t size)
Send simple netlink message using nl_send_auto_complete()
Definition: nl.c:410
int nl_wait_for_ack(struct nl_handle *handle)
Wait for ACK.
Definition: nl.c:801
void nl_object_get(struct nl_object *obj)
Acquire a reference on a object.
Definition: object.c:167
struct nl_msg * rtnl_qdisc_build_add_request(struct rtnl_qdisc *qdisc, int flags)
Build a netlink message to add a new qdisc.
Definition: qdisc.c:197
struct nl_msg * rtnl_qdisc_build_change_request(struct rtnl_qdisc *qdisc, struct rtnl_qdisc *new)
Build a netlink message to change attributes of a existing qdisc.
Definition: qdisc.c:261
int rtnl_qdisc_add(struct nl_handle *handle, struct rtnl_qdisc *qdisc, int flags)
Add a new qdisc.
Definition: qdisc.c:224
struct nl_cache * rtnl_qdisc_alloc_cache(struct nl_handle *handle)
Build a qdisc cache including all qdiscs currently configured in the kernel.
Definition: qdisc.c:384
int rtnl_qdisc_change(struct nl_handle *handle, struct rtnl_qdisc *qdisc, struct rtnl_qdisc *new)
Change attributes of a qdisc.
Definition: qdisc.c:279
int rtnl_qdisc_delete(struct nl_handle *handle, struct rtnl_qdisc *qdisc)
Delete a qdisc.
Definition: qdisc.c:348
struct rtnl_qdisc * rtnl_qdisc_get_by_parent(struct nl_cache *cache, int ifindex, uint32_t parent)
Look up qdisc by its parent in the provided cache.
Definition: qdisc.c:407
struct rtnl_qdisc * rtnl_qdisc_get(struct nl_cache *cache, int ifindex, uint32_t handle)
Look up qdisc by its handle in the provided cache.
Definition: qdisc.c:432
struct nl_msg * rtnl_qdisc_build_delete_request(struct rtnl_qdisc *qdisc)
Build a netlink request message to delete a qdisc.
Definition: qdisc.c:315
Cache Operations.
Definition: cache-api.h:164
Netlink message header.
uint16_t nlmsg_type
Message type (content type)
Qdisc Operations.
Definition: qdisc-modules.h:26
struct nl_msg *(* qo_get_opts)(struct rtnl_qdisc *)
Must return the contents supposed to be in TCA_OPTIONS.
Definition: qdisc-modules.h:41
int(* qo_msg_parser)(struct rtnl_qdisc *)
TCA_OPTIONS message parser.
Definition: qdisc-modules.h:46
Netlink socket address.
Definition: netlink-kernel.h:9