oops
[libnl.git] / lib / route / 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
96 /** @cond SKIP */
97 static struct nl_cache_ops rtnl_qdisc_ops;
98 /** @endcond */
99
100 static struct rtnl_qdisc_ops *qdisc_ops_list;
101
102 static struct rtnl_qdisc_ops *qdisc_lookup_ops(const char *kind)
103 {
104         struct rtnl_qdisc_ops *ops;
105
106         for (ops = qdisc_ops_list; ops; ops = ops->qo_next)
107                 if (!strcmp(kind, ops->qo_kind))
108                         return ops;
109
110         return NULL;
111 }
112
113 static inline struct rtnl_qdisc_ops *qdisc_ops(struct rtnl_qdisc *qdisc)
114 {
115         if (!qdisc->q_ops)
116                 qdisc->q_ops = qdisc_lookup_ops(qdisc->q_kind);
117
118         return qdisc->q_ops;
119 }
120
121 /**
122  * @name QDisc Module API
123  * @{
124  */
125
126 /**
127  * Register a qdisc module
128  * @arg ops             qdisc module operations
129  */
130 int rtnl_qdisc_register(struct rtnl_qdisc_ops *ops)
131 {
132         struct rtnl_qdisc_ops *o, **op;
133
134         if (!ops->qo_kind[0])
135                 BUG();
136
137         for (op = &qdisc_ops_list; (o = *op) != NULL; op = &o->qo_next)
138                 if (!strcasecmp(ops->qo_kind, o->qo_kind))
139                         return nl_errno(EEXIST);
140
141         ops->qo_next = NULL;
142         *op = ops;
143
144         return 0;
145 }
146
147 /**
148  * Unregister a qdisc module
149  * @arg ops             qdisc module operations
150  */
151 int rtnl_qdisc_unregister(struct rtnl_qdisc_ops *ops)
152 {
153         struct rtnl_qdisc_ops *o, **op;
154
155         for (op = &qdisc_ops_list; (o = *op) != NULL; op = &o->qo_next)
156                 if (!strcasecmp(ops->qo_kind, o->qo_kind))
157                         break;
158
159         if (!o)
160                 return nl_errno(ENOENT);
161
162         *op = ops->qo_next;
163
164         return 0;
165 }
166
167 /** @} */
168
169 static int qdisc_msg_parser(struct sockaddr_nl *who, struct nlmsghdr *n,
170                             void *arg)
171 {
172         int err = -ENOMEM;
173         struct nl_parser_param *pp = arg;
174         struct rtnl_qdisc *qdisc;
175         struct rtnl_qdisc_ops *ops;
176
177         qdisc = rtnl_qdisc_alloc();
178         if (!qdisc) {
179                 err = nl_errno(ENOMEM);
180                 goto errout;
181         }
182
183         qdisc->ce_msgtype = n->nlmsg_type;
184
185         err = tca_msg_parser(n, (struct rtnl_tca *) qdisc);
186         if (err < 0)
187                 goto errout_free;
188
189         ops = qdisc_ops(qdisc);
190         if (ops && ops->qo_msg_parser) {
191                 err = ops->qo_msg_parser(qdisc);
192                 if (err < 0)
193                         goto errout_free;
194         }
195
196         err = pp->pp_cb((struct nl_object *) qdisc, pp);
197         if (err < 0)
198                 goto errout_free;
199
200         return P_ACCEPT;
201
202 errout_free:
203         rtnl_qdisc_put(qdisc);
204 errout:
205         return err;
206 }
207
208 static int qdisc_request_update(struct nl_cache *c, struct nl_handle *h)
209 {
210         struct tcmsg tchdr = {
211                 .tcm_family = AF_UNSPEC,
212                 .tcm_ifindex = c->c_iarg1,
213         };
214
215         return nl_send_simple(h, RTM_GETQDISC, NLM_F_DUMP, &tchdr,
216                               sizeof(tchdr));
217 }
218
219 static void qdisc_free_data(struct nl_object *obj)
220 {
221         struct rtnl_qdisc *qdisc = (struct rtnl_qdisc *) obj;
222         struct rtnl_qdisc_ops *ops;
223
224         tca_free_data((struct rtnl_tca *) qdisc);
225
226         ops = qdisc_ops(qdisc);
227         if (ops && ops->qo_free_data)
228                 ops->qo_free_data(qdisc);
229 }
230
231 static int qdisc_dump_brief(struct nl_object *obj, struct nl_dump_params *p)
232 {
233         struct rtnl_qdisc *qdisc = (struct rtnl_qdisc *) obj;
234         struct rtnl_qdisc_ops *ops;
235
236         int line = tca_dump_brief((struct rtnl_tca *) qdisc, "qdisc", p, 0);
237
238         ops = qdisc_ops(qdisc);
239         if (ops && ops->qo_dump[NL_DUMP_BRIEF])
240                 line = ops->qo_dump[NL_DUMP_BRIEF](qdisc, p, line);
241
242         dp_dump(p, "\n");
243
244         return line;
245 }
246
247 static int qdisc_dump_full(struct nl_object *arg, struct nl_dump_params *p)
248 {
249         struct rtnl_qdisc *qdisc = (struct rtnl_qdisc *) arg;
250         struct rtnl_qdisc_ops *ops;
251
252         int line = qdisc_dump_brief(arg, p);
253
254         line = tca_dump_full((struct rtnl_tca *) qdisc, p, line);
255         dp_dump(p, "refcnt %u ", qdisc->q_info);
256
257         ops = qdisc_ops(qdisc);
258         if (ops && ops->qo_dump[NL_DUMP_FULL])
259                 line = ops->qo_dump[NL_DUMP_FULL](qdisc, p, line);
260
261         dp_dump(p, "\n");
262         return line;
263 }
264
265 static int qdisc_dump_stats(struct nl_object *arg, struct nl_dump_params *p)
266 {
267         struct rtnl_qdisc *qdisc = (struct rtnl_qdisc *) arg;
268         struct rtnl_qdisc_ops *ops;
269
270         int line = qdisc_dump_full(arg, p);
271         line = tca_dump_stats((struct rtnl_tca *) qdisc, p, line );
272         dp_dump(p, "\n");
273
274         ops = qdisc_ops(qdisc);
275         if (ops && ops->qo_dump[NL_DUMP_STATS])
276                 line = ops->qo_dump[NL_DUMP_STATS](qdisc, p, line);
277
278         return line;
279 }
280
281 static int qdisc_filter(struct nl_object *obj, struct nl_object *filter)
282 {
283         return tca_filter((struct rtnl_tca *) obj, (struct rtnl_tca *) filter);
284 }
285
286 /**
287  * @name QDisc Addition
288  * @{
289  */
290
291 static struct nl_msg *qdisc_build(struct rtnl_qdisc *qdisc, int type, int flags)
292 {
293         struct rtnl_qdisc_ops *ops;
294         struct nl_msg *msg;
295         int err;
296
297         msg = tca_build_msg((struct rtnl_tca *) qdisc, type, flags);
298         if (!msg)
299                 goto errout;
300
301         ops = qdisc_ops(qdisc);
302         if (ops && ops->qo_get_opts) {
303                 struct nl_msg *opts;
304                 
305                 opts = ops->qo_get_opts(qdisc);
306                 if (opts) {
307                         err = nla_put_nested(msg, TCA_OPTIONS, opts);
308                         nlmsg_free(opts);
309                         if (err < 0)
310                                 goto errout;
311                 }
312         }
313
314         return msg;
315 errout:
316         nlmsg_free(msg);
317
318         return NULL;
319 }
320
321 /**
322  * Build a netlink message to add a new qdisc
323  * @arg qdisc           qdisc to add 
324  * @arg flags           additional netlink message flags
325  *
326  * Builds a new netlink message requesting an addition of a qdisc.
327  * The netlink message header isn't fully equipped with all relevant
328  * fields and must be sent out via nl_send_auto_complete() or
329  * supplemented as needed. 
330  *
331  * Common message flags used:
332  *  - NLM_F_REPLACE - replace a potential existing qdisc
333  *
334  * @return New netlink message
335  */
336 struct nl_msg *rtnl_qdisc_build_add_request(struct rtnl_qdisc *qdisc,
337                                             int flags)
338 {
339         struct nl_msg *msg;
340
341         msg = qdisc_build(qdisc, RTM_NEWQDISC, NLM_F_CREATE | flags);
342         if (!msg)
343                 nl_errno(ENOMEM);
344
345         return msg;
346 }
347
348 /**
349  * Add a new qdisc
350  * @arg handle          netlink handle
351  * @arg qdisc           qdisc to delete
352  * @arg flags           additional netlink message flags
353  *
354  * Builds a netlink message by calling rtnl_qdisc_build_add_request(),
355  * sends the request to the kernel and waits for the ACK to be
356  * received and thus blocks until the request has been processed.
357  *
358  * Common message flags used:
359  *  - NLM_F_REPLACE - replace a potential existing qdisc
360  *
361  * @return 0 on success or a negative error code
362  */
363 int rtnl_qdisc_add(struct nl_handle *handle, struct rtnl_qdisc *qdisc,
364                    int flags)
365 {
366         struct nl_msg *msg;
367         int err;
368
369         msg = rtnl_qdisc_build_add_request(qdisc, flags);
370         if (!msg)
371                 return nl_errno(ENOMEM);
372
373         err = nl_send_auto_complete(handle, msg);
374         if (err < 0)
375                 return err;
376
377         nlmsg_free(msg);
378         return nl_wait_for_ack(handle);
379 }
380
381 /** @} */
382
383 /**
384  * @name QDisc Modification
385  * @{
386  */
387
388 /**
389  * Build a netlink message to change attributes of a existing qdisc
390  * @arg qdisc           qdisc to change
391  * @arg new             new qdisc attributes
392  *
393  * Builds a new netlink message requesting an change of qdisc
394  * attributes. The netlink message header isn't fully equipped
395  * with all relevant fields and must be sent out via
396  * nl_send_auto_complete() or supplemented as needed. 
397  *
398  * @return New netlink message
399  */
400 struct nl_msg *rtnl_qdisc_build_change_request(struct rtnl_qdisc *qdisc,
401                                                struct rtnl_qdisc *new)
402 {
403         return qdisc_build(qdisc, RTM_NEWQDISC, NLM_F_REPLACE);
404 }
405
406 /**
407  * Change attributes of a qdisc
408  * @arg handle          netlink handle
409  * @arg qdisc           qdisc to change
410  * @arg new             new qdisc attributes
411  *
412  * Builds a netlink message by calling rtnl_qdisc_build_change_request(),
413  * sends the request to the kernel and waits for the ACK to be
414  * received and thus blocks until the request has been processed.
415  *
416  * @return 0 on success or a negative error code
417  */
418 int rtnl_qdisc_change(struct nl_handle *handle, struct rtnl_qdisc *qdisc,
419                       struct rtnl_qdisc *new)
420 {
421         struct nl_msg *msg;
422         int err;
423
424         msg = rtnl_qdisc_build_change_request(qdisc, new);
425         if (!msg)
426                 return nl_errno(ENOMEM);
427
428         err = nl_send_auto_complete(handle, msg);
429         if (err < 0)
430                 return err;
431
432         nlmsg_free(msg);
433         return nl_wait_for_ack(handle);
434 }
435
436 /** @} */
437
438 /**
439  * @name QDisc Deletion
440  * @{
441  */
442
443 /**
444  * Build a netlink request message to delete a qdisc
445  * @arg qdisc           qdisc to delete
446  *
447  * Builds a new netlink message requesting a deletion of a qdisc.
448  * The netlink message header isn't fully equipped with all relevant
449  * fields and must thus be sent out via nl_send_auto_complete()
450  * or supplemented as needed.
451  *
452  * @return New netlink message
453  */
454 struct nl_msg *rtnl_qdisc_build_delete_request(struct rtnl_qdisc *qdisc)
455 {
456         struct nl_msg *msg;
457         struct tcmsg tchdr;
458         int required = TCA_ATTR_IFINDEX | TCA_ATTR_PARENT;
459
460         if ((qdisc->q_mask & required) != required)
461                 BUG();
462
463         msg = nlmsg_build_simple(RTM_DELQDISC, 0);
464         if (!msg)
465                 return NULL;
466
467         tchdr.tcm_family = AF_UNSPEC,
468         tchdr.tcm_handle = qdisc->q_handle,
469         tchdr.tcm_parent = qdisc->q_parent,
470         tchdr.tcm_ifindex = qdisc->q_ifindex,
471         nlmsg_append(msg, &tchdr, sizeof(tchdr), 1);
472
473         return msg;
474 }
475
476 /**
477  * Delete a qdisc
478  * @arg handle          netlink handle
479  * @arg qdisc           qdisc to delete
480  *
481  * Builds a netlink message by calling rtnl_qdisc_build_delete_request(),
482  * sends the request to the kernel and waits for the ACK to be
483  * received and thus blocks until the request has been processed.
484  *
485  * @return 0 on success or a negative error code
486  */
487 int rtnl_qdisc_delete(struct nl_handle *handle, struct rtnl_qdisc *qdisc)
488 {
489         struct nl_msg *msg;
490         int err;
491
492         msg = rtnl_qdisc_build_delete_request(qdisc);
493         if (!msg)
494                 return nl_errno(ENOMEM);
495
496         err = nl_send_auto_complete(handle, msg);
497         if (err < 0)
498                 return err;
499
500         nlmsg_free(msg);
501         return nl_wait_for_ack(handle);
502 }
503
504 /** @} */
505
506 /**
507  * @name General
508  * @{
509  */
510
511 /**
512  * Allocate a new qdisc object
513  * @return New qdisc object
514  */
515 struct rtnl_qdisc *rtnl_qdisc_alloc(void)
516 {
517         return (struct rtnl_qdisc *) nl_object_alloc_from_ops(&rtnl_qdisc_ops);
518 }
519
520 /**
521  * Give back reference on rqdisc object.
522  * @arg qdisc           Qdisc object to be given back.
523  *
524  * Decrements the reference counter and frees the object if the
525  * last reference has been released.
526  */
527 void rtnl_qdisc_put(struct rtnl_qdisc *qdisc)
528 {
529         nl_object_put((struct nl_object *) qdisc);
530 }
531 /**
532  * Free qdisc object.
533  * @arg qdisc           Qdisc object to be freed.
534  *
535  * @note Always use rtnl_qdisc_put() unless you're absolutely sure
536  *       that no other user may have a reference on this object.
537  */
538 void rtnl_qdisc_free(struct rtnl_qdisc *qdisc)
539 {
540         nl_object_free((struct nl_object *) qdisc);
541 }
542
543 /** @} */
544
545 /**
546  * @name Qdisc Cache Management
547  * @{
548  */
549
550 /**
551  * Build a qdisc cache including all qdiscs currently configured in
552  * the kernel
553  * @arg handle          netlink handle
554  *
555  * Allocates a new cache, initializes it properly and updates it to
556  * include all qdiscs currently configured in the kernel.
557  *
558  * @note The caller is responsible for destroying and freeing the
559  *       cache after using it.
560  * @return The cache or NULL if an error has occured.
561  */
562 struct nl_cache * rtnl_qdisc_alloc_cache(struct nl_handle *handle)
563 {
564         struct nl_cache * cache;
565         
566         cache = nl_cache_alloc_from_ops(&rtnl_qdisc_ops);
567         if (cache == NULL)
568                 return NULL;
569
570         if (nl_cache_update(handle, cache) < 0) {
571                 nl_cache_free(cache);
572                 return NULL;
573         }
574
575         return cache;
576 }
577
578 /**
579  * Look up qdisc by its parent in the provided cache
580  * @arg cache           qdisc cache
581  * @arg ifindex         interface the qdisc is attached to
582  * @arg parent          parent handle
583  * @return pointer to qdisc inside the cache or NULL if no match was found.
584  */
585 struct rtnl_qdisc * rtnl_qdisc_get_by_parent(struct nl_cache *cache,
586                                              int ifindex, uint32_t parent)
587 {
588         struct rtnl_qdisc *q;
589
590         if (cache->c_ops != &rtnl_qdisc_ops)
591                 return NULL;
592
593         nl_list_for_each_entry(q, &cache->c_items, ce_list) {
594                 if (q->q_parent == parent && q->q_ifindex == ifindex) {
595                         nl_object_get((struct nl_object *) q);
596                         return q;
597                 }
598         }
599
600         return NULL;
601 }
602
603 /**
604  * Look up qdisc by its handle in the provided cache
605  * @arg cache           qdisc cache
606  * @arg ifindex         interface the qdisc is attached to
607  * @arg handle          qdisc handle
608  * @return pointer to qdisc inside the cache or NULL if no match was found.
609  */
610 struct rtnl_qdisc * rtnl_qdisc_get(struct nl_cache *cache,
611                                    int ifindex, uint32_t handle)
612 {
613         struct rtnl_qdisc *q;
614
615         if (cache->c_ops != &rtnl_qdisc_ops)
616                 return NULL;
617
618         nl_list_for_each_entry(q, &cache->c_items, ce_list) {
619                 if (q->q_handle == handle && q->q_ifindex == ifindex) {
620                         nl_object_get((struct nl_object *) q);
621                         return q;
622                 }
623         }
624
625         return NULL;
626 }
627
628 /** @} */
629
630 /**
631  * @name Qdisc Specific Options
632  * @{
633  */
634
635 /**
636  * Return qdisc specific options for use in TCA_OPTIONS
637  * @arg qdisc           qdisc carrying the optiosn
638  * 
639  * @return new headerless netlink message carrying the options as payload
640  */
641 struct nl_msg *rtnl_qdisc_get_opts(struct rtnl_qdisc *qdisc)
642 {
643         struct rtnl_qdisc_ops *ops;
644
645         ops = qdisc_ops(qdisc);
646         if (ops && ops->qo_get_opts)
647                 return ops->qo_get_opts(qdisc);
648
649         return NULL;
650 }
651
652 /** @} */
653
654 /**
655  * @name Iterators
656  * @{
657  */
658
659 /**
660  * Call a callback for each child class of a qdisc
661  * @arg qdisc           the parent qdisc
662  * @arg cache           a class cache including all classes of the interface
663  *                      the specified qdisc is attached to
664  * @arg cb              callback function
665  * @arg arg             argument to be passed to callback function
666  */
667 void rtnl_qdisc_foreach_child(struct rtnl_qdisc *qdisc, struct nl_cache *cache,
668                               void (*cb)(struct nl_object *, void *), void *arg)
669 {
670         struct rtnl_class *filter;
671         
672         filter = rtnl_class_alloc();
673         if (!filter)
674                 return;
675
676         rtnl_class_set_parent(filter, qdisc->q_handle);
677         rtnl_class_set_ifindex(filter, qdisc->q_ifindex);
678         rtnl_class_set_kind(filter, qdisc->q_kind);
679
680         nl_cache_foreach_filter(cache, (struct nl_object *) filter, cb, arg);
681
682         rtnl_class_put(filter);
683 }
684
685 /**
686  * Call a callback for each filter attached to the qdisc
687  * @arg qdisc           the parent qdisc
688  * @arg cache           a filter cache including at least all the filters
689  *                      attached to the specified qdisc
690  * @arg cb              callback function
691  * @arg arg             argument to be passed to callback function
692  */
693 void rtnl_qdisc_foreach_cls(struct rtnl_qdisc *qdisc, struct nl_cache *cache,
694                             void (*cb)(struct nl_object *, void *), void *arg)
695 {
696         struct rtnl_cls *filter;
697
698         filter = rtnl_cls_alloc();
699         if (!filter)
700                 return;
701
702         rtnl_cls_set_ifindex(filter, qdisc->q_ifindex);
703         rtnl_cls_set_parent(filter, qdisc->q_parent);
704
705         nl_cache_foreach_filter(cache, (struct nl_object *) filter, cb, arg);
706         rtnl_cls_put(filter);
707 }
708
709 /** @} */
710
711 /**
712  * @name Attribute Modification
713  * @{
714  */
715
716 /**
717  * Set the interface index of a qdisc to the specified value
718  * @arg qdisc           qdisc to be changed
719  * @arg ifindex         new interface index
720  */
721 void rtnl_qdisc_set_ifindex(struct rtnl_qdisc *qdisc, int ifindex)
722 {
723         tca_set_ifindex((struct rtnl_tca *) qdisc, ifindex);
724 }
725
726 /**
727  * Get the interface index of a qdisc
728  * @arg qdisc           qdisc handle
729  * @return Interface index or RTNL_LINK_NOT_FOUND if not set
730  */
731 int rtnl_qdisc_get_ifindex(struct rtnl_qdisc *qdisc)
732 {
733         return tca_get_ifindex((struct rtnl_tca *) qdisc);
734 }
735
736 /**
737  * Set the handle of a qdisc to the specified value
738  * @arg qdisc           qdisc to be changed
739  * @arg handle          new handle
740  */
741 void rtnl_qdisc_set_handle(struct rtnl_qdisc *qdisc, uint32_t handle)
742 {
743         tca_set_handle((struct rtnl_tca *) qdisc, handle);
744 }
745
746 /**
747  * Get the handle of a qdisc
748  * @arg qdisc           qdisc handle
749  * @return Handle or 0 if not set
750  */
751 uint32_t rtnl_qdisc_get_handle(struct rtnl_qdisc *qdisc)
752 {
753         return tca_get_handle((struct rtnl_tca *) qdisc);
754 }
755
756 /**
757  * Set the parent handle of a qdisc to the specified value
758  * @arg qdisc           qdisc to be changed
759  * @arg parent          new parent handle
760  */
761 void rtnl_qdisc_set_parent(struct rtnl_qdisc *qdisc, uint32_t parent)
762 {
763         tca_set_parent((struct rtnl_tca *) qdisc, parent);
764 }
765
766 /**
767  * Get the parent handle of a qdisc
768  * @arg qdisc           qdisc handle
769  * @return Parent handle or 0 if not set
770  */
771 uint32_t rtnl_qdisc_get_parent(struct rtnl_qdisc *qdisc)
772 {
773         return tca_get_parent((struct rtnl_tca *) qdisc);
774 }
775
776 /**
777  * Set the kind of a qdisc to the specified value
778  * @arg qdisc           qdisc to be changed
779  * @arg name            new kind name
780  */
781 void rtnl_qdisc_set_kind(struct rtnl_qdisc *qdisc, const char *name)
782 {
783         tca_set_kind((struct rtnl_tca *) qdisc, name);
784         qdisc->q_ops = qdisc_lookup_ops(name);
785 }
786
787 /**
788  * Get the kind of a qdisc
789  * @arg qdisc           qdisc handle
790  * @return Kind or NULL if not set
791  */
792 char *rtnl_qdisc_get_kind(struct rtnl_qdisc *qdisc)
793 {
794         return tca_get_kind((struct rtnl_tca *) qdisc);
795 }
796
797 /**
798  * Get the statistic specified by the id
799  * @arg qdisc           qdisc handle
800  * @arg id              statistic id
801  * @return The current counter of the specified statistic
802  */
803 uint64_t rtnl_qdisc_get_stat(struct rtnl_qdisc *qdisc,
804                              enum rtnl_tc_stats_id id)
805 {
806         return tca_get_stat((struct rtnl_tca *) qdisc, id);
807 }
808
809 /** @} */
810
811 static struct nl_cache_ops rtnl_qdisc_ops = {
812         .co_name                = "route/qdisc",
813         .co_size                = sizeof(struct rtnl_qdisc),
814         .co_hdrsize             = sizeof(struct tcmsg),
815         .co_msgtypes            = {
816                                         { RTM_NEWQDISC, "new" },
817                                         { RTM_DELQDISC, "delete" },
818                                         { RTM_GETQDISC, "get" },
819                                         { -1, NULL },
820                                   },
821         .co_protocol            = NETLINK_ROUTE,
822         .co_request_update      = qdisc_request_update,
823         .co_msg_parser          = qdisc_msg_parser,
824         .co_free_data           = qdisc_free_data,
825         .co_dump[NL_DUMP_BRIEF] = qdisc_dump_brief,
826         .co_dump[NL_DUMP_FULL]  = qdisc_dump_full,
827         .co_dump[NL_DUMP_STATS] = qdisc_dump_stats,
828         .co_filter              = qdisc_filter,
829 };
830
831 static void __init qdisc_init(void)
832 {
833         nl_cache_mngt_register(&rtnl_qdisc_ops);
834 }
835
836 static void __exit qdisc_exit(void)
837 {
838         nl_cache_mngt_unregister(&rtnl_qdisc_ops);
839 }
840
841 /** @} */