oops
[libnl.git] / lib / route / class.c
1 /*
2  * lib/route/class.c            Queueing Classes
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 class Classes
15  * @{
16  */
17
18 #include <netlink-local.h>
19 #include <netlink-tc.h>
20 #include <netlink/netlink.h>
21 #include <netlink/route/tc.h>
22 #include <netlink/route/class.h>
23 #include <netlink/route/class-modules.h>
24 #include <netlink/route/qdisc.h>
25 #include <netlink/route/classifier.h>
26 #include <netlink/utils.h>
27
28 /** @cond SKIP */
29 static struct nl_cache_ops rtnl_class_ops;
30 /** @endcond */
31
32 static struct rtnl_class_ops *class_ops_list;
33
34 static struct rtnl_class_ops *class_lookup_ops(const char *kind)
35 {
36         struct rtnl_class_ops *ops;
37
38         for (ops = class_ops_list; ops; ops = ops->co_next)
39                 if (!strcmp(kind, ops->co_kind))
40                         return ops;
41
42         return NULL;
43 }
44
45 static inline struct rtnl_class_ops *class_ops(struct rtnl_class *class)
46 {
47         if (!class->c_ops)
48                 class->c_ops = class_lookup_ops(class->c_kind);
49
50         return class->c_ops;
51 }
52
53 /**
54  * @name Class Module API
55  * @{
56  */
57
58 /**
59  * Register a class module
60  * @arg ops             class module operations
61  */
62 int rtnl_class_register(struct rtnl_class_ops *ops)
63 {
64         struct rtnl_class_ops *o, **op;
65
66         if (!ops->co_kind[0])
67                 BUG();
68
69         for (op = &class_ops_list; (o = *op) != NULL; op = &o->co_next)
70                 if (!strcasecmp(ops->co_kind, o->co_kind))
71                         return nl_errno(EEXIST);
72
73         ops->co_next = NULL;
74         *op = ops;
75
76         return 0;
77 }
78
79 /**
80  * Unregister a class module
81  * @arg ops             class module operations
82  */
83 int rtnl_class_unregister(struct rtnl_class_ops *ops)
84 {
85         struct rtnl_class_ops *o, **op;
86
87         for (op = &class_ops_list; (o = *op) != NULL; op = &o->co_next)
88                 if (!strcasecmp(ops->co_kind, o->co_kind))
89                         break;
90
91         if (!o)
92                 return nl_errno(ENOENT);
93
94         *op = ops->co_next;
95
96         return 0;
97 }
98
99 /** @} */
100
101 static int class_msg_parser(struct sockaddr_nl *who, struct nlmsghdr *n,
102                             void *arg)
103 {
104         int err;
105         struct nl_parser_param *pp = arg;
106         struct rtnl_class *class;
107         struct rtnl_class_ops *ops;
108
109         class = rtnl_class_alloc();
110         if (!class) {
111                 err = nl_errno(ENOMEM);
112                 goto errout;
113         }
114         class->ce_msgtype = n->nlmsg_type;
115
116         err = tca_msg_parser(n, (struct rtnl_tca *) class);
117         if (err < 0)
118                 goto errout_free;
119
120         ops = class_ops(class);
121         if (ops && ops->co_msg_parser) {
122                 err = ops->co_msg_parser(class);
123                 if (err < 0)
124                         goto errout_free;
125         }
126
127         err = pp->pp_cb((struct nl_object *) class, pp);
128         if (err < 0)
129                 goto errout_free;
130
131         return P_ACCEPT;
132
133 errout_free:
134         rtnl_class_put(class);
135 errout:
136         return err;
137 }
138
139 static int class_request_update(struct nl_cache *cache,
140                                 struct nl_handle *handle)
141 {
142         struct tcmsg tchdr = {
143                 .tcm_family = AF_UNSPEC,
144                 .tcm_ifindex = cache->c_iarg1,
145         };
146
147         return nl_send_simple(handle, RTM_GETTCLASS, NLM_F_DUMP, &tchdr,
148                               sizeof(tchdr));
149 }
150
151 static void class_free_data(struct nl_object *obj)
152 {
153         struct rtnl_class *class = (struct rtnl_class *) obj;
154         struct rtnl_class_ops *ops;
155         
156         tca_free_data((struct rtnl_tca *) class);
157
158         ops = class_ops(class);
159         if (ops && ops->co_free_data)
160                 ops->co_free_data(class);
161 }
162
163 static int class_dump_brief(struct nl_object *obj, struct nl_dump_params *p)
164 {
165         struct rtnl_class *class = (struct rtnl_class *) obj;
166         struct rtnl_class_ops *ops;
167
168         int line = tca_dump_brief((struct rtnl_tca *) class, "class", p, 0);
169
170         ops = class_ops(class);
171         if (ops && ops->co_dump[NL_DUMP_BRIEF])
172                 line = ops->co_dump[NL_DUMP_BRIEF](class, p, line);
173         dp_dump(p, "\n");
174
175         return line;
176 }
177
178 static int class_dump_full(struct nl_object *obj, struct nl_dump_params *p)
179 {
180         struct rtnl_class *class = (struct rtnl_class *) obj;
181         struct rtnl_class_ops *ops;
182         int line;
183
184         line = class_dump_brief(obj, p);
185         line = tca_dump_full((struct rtnl_tca *) class, p, line);
186         
187         if (class->c_info) {
188                 char buf[32];
189                 dp_dump(p, "child-qdisc %s ",
190                         rtnl_tc_handle2str(class->c_info, buf, sizeof(buf)));
191         }
192
193         ops = class_ops(class);
194         if (ops && ops->co_dump[NL_DUMP_FULL])
195                 line = ops->co_dump[NL_DUMP_FULL](class, p, line);
196         else if (!class->c_info)
197                 dp_dump(p, "noop (no leaf qdisc)");
198
199         dp_dump(p, "\n");
200
201         return line;
202 }
203
204 static int class_dump_stats(struct nl_object *obj, struct nl_dump_params *p)
205 {
206         struct rtnl_class *class = (struct rtnl_class *) obj;
207         struct rtnl_class_ops *ops;
208         int line;
209
210         line = class_dump_full(obj, p);
211         line = tca_dump_stats((struct rtnl_tca *) class, p, line);
212         dp_dump(p, "\n");
213
214         ops = class_ops(class);
215         if (ops && ops->co_dump[NL_DUMP_STATS])
216                 line = ops->co_dump[NL_DUMP_STATS](class, p, line);
217
218         return line;
219 }
220
221 static int class_filter(struct nl_object *obj, struct nl_object *filter)
222 {
223         return tca_filter((struct rtnl_tca *) obj, (struct rtnl_tca *) filter);
224 }
225
226 /**
227  * @name Class Addition/Modification
228  * @{
229  */
230
231 static struct nl_msg *class_build(struct rtnl_class *class, int type, int flags)
232 {
233         struct rtnl_class_ops *ops;
234         struct nl_msg *msg;
235         int err;
236
237         msg = tca_build_msg((struct rtnl_tca *) class, type, flags);
238         if (!msg)
239                 goto errout;
240
241         ops = class_ops(class);
242         if (ops && ops->co_get_opts) {
243                 struct nl_msg *opts;
244                 
245                 opts = ops->co_get_opts(class);
246                 if (opts) {
247                         err = nla_put_nested(msg, TCA_OPTIONS, opts);
248                         nlmsg_free(opts);
249                         if (err < 0)
250                                 goto errout;
251                 }
252         }
253
254         return msg;
255 errout:
256         nlmsg_free(msg);
257         return NULL;
258 }
259
260 /**
261  * Build a netlink message to add a new class
262  * @arg class           class to add 
263  * @arg flags           additional netlink message flags
264  *
265  * Builds a new netlink message requesting an addition of a class.
266  * The netlink message header isn't fully equipped with all relevant
267  * fields and must be sent out via nl_send_auto_complete() or
268  * supplemented as needed. 
269  *
270  * Common message flags
271  *   - NLM_F_REPLACE - replace possibly existing classes
272  *
273  * @return New netlink message
274  */
275 struct nl_msg *rtnl_class_build_add_request(struct rtnl_class *class, int flags)
276 {
277         return class_build(class, RTM_NEWTCLASS, NLM_F_CREATE | flags);
278 }
279
280 /**
281  * Add a new class
282  * @arg handle          netlink handle
283  * @arg class           class to delete
284  * @arg flags           additional netlink message flags
285  *
286  * Builds a netlink message by calling rtnl_qdisc_build_add_request(),
287  * sends the request to the kernel and waits for the next ACK to be
288  * received and thus blocks until the request has been processed.
289  *
290  * Common message flags
291  *   - NLM_F_REPLACE - replace possibly existing classes
292  *
293  * @return 0 on success or a negative error code
294  */
295 int rtnl_class_add(struct nl_handle *handle, struct rtnl_class *class,
296                    int flags)
297 {
298         struct nl_msg *msg;
299         int err;
300
301         msg = rtnl_class_build_add_request(class, flags);
302         if (!msg)
303                 return nl_errno(ENOMEM);
304
305         err = nl_send_auto_complete(handle, msg);
306         if (err < 0)
307                 return err;
308
309         nlmsg_free(msg);
310         return nl_wait_for_ack(handle);
311 }
312
313 /** @} */
314
315 /**
316  * @name General
317  * @{
318  */
319
320 /**
321  * Allocate a new class object
322  * @return New class object
323  */
324 struct rtnl_class *rtnl_class_alloc(void)
325 {
326         return (struct rtnl_class *) nl_object_alloc_from_ops(&rtnl_class_ops);
327 }
328
329 /**
330  * Give back reference on rclass object.
331  * @arg class           Class object to be given back.
332  *
333  * Decrements the reference counter and frees the object if the
334  * last reference has been released.
335  */
336 void rtnl_class_put(struct rtnl_class *class)
337 {
338         nl_object_put((struct nl_object *) class);
339 }
340
341 /**
342  * Free class object.
343  * @arg class           Class object to be freed.
344  *
345  * @note Always use rtnl_class_put() unless you're absolutely sure
346  *       that no other user may have a reference on this object.
347  */
348 void rtnl_class_free(struct rtnl_class *class)
349 {
350         nl_object_free((struct nl_object *) class);
351 }
352
353 /**
354  * Build a class cache including all classes attached to the specified interface
355  * @arg handle          netlink handle
356  * @arg ifindex         interface index of the link the classes are
357  *                      attached to.
358  *
359  * Allocates a new cache, initializes it properly and updates it to
360  * include all classes attached to the specified interface.
361  *
362  * @note The caller is responsible for destroying and freeing the
363  *       cache after using it.
364  * @return The cache or NULL if an error has occured.
365  */
366 struct nl_cache * rtnl_class_alloc_cache(struct nl_handle *handle, int ifindex)
367 {
368         struct nl_cache * cache;
369         
370         cache = nl_cache_alloc_from_ops(&rtnl_class_ops);
371         if (!cache)
372                 return NULL;
373
374         cache->c_iarg1 = ifindex;
375         
376         if (nl_cache_update(handle, cache) < 0) {
377                 nl_cache_free(cache);
378                 return NULL;
379         }
380
381         return cache;
382 }
383
384 /** @} */
385
386 /**
387  * @name Leaf Qdisc Access
388  * @{
389  */
390
391 /**
392  * Determine if the class has a leaf qdisc attached
393  * @arg class           class to check
394  */
395 int rtnl_class_has_leaf_qdisc(struct rtnl_class *class)
396 {
397         return !!class->c_info;
398 }
399
400 /**
401  * Lookup the leaf qdisc of a class
402  * @arg class           the parent class
403  * @arg cache           a qdisc cache including at laest all qdiscs of the
404  *                      interface the specified class is attached to
405  * @return The qdisc from the cache or NULL if the class has no leaf qdisc
406  */
407 struct rtnl_qdisc *rtnl_class_leaf_qdisc(struct rtnl_class *class,
408                                          struct nl_cache *cache)
409 {
410         struct rtnl_qdisc *leaf;
411
412         if (!rtnl_class_has_leaf_qdisc(class))
413                 return NULL;
414
415         leaf = rtnl_qdisc_get_by_parent(cache, class->c_ifindex,
416                                         class->c_handle);
417         if (!leaf || leaf->q_handle != class->c_info)
418                 return NULL;
419
420         return leaf;
421 }
422
423 /** @} */
424
425 /**
426  * @name Iterators
427  * @{
428  */
429
430 /**
431  * Call a callback for each child class of a class
432  * @arg class           the parent class
433  * @arg cache           a class cache including all classes of the interface
434  *                      the specified class is attached to
435  * @arg cb              callback function
436  * @arg arg             argument to be passed to callback function
437  */
438 void rtnl_class_foreach_child(struct rtnl_class *class, struct nl_cache *cache,
439                               void (*cb)(struct nl_object *, void *), void *arg)
440 {
441         struct rtnl_class *filter;
442         
443         filter = rtnl_class_alloc();
444         if (!filter)
445                 return;
446
447         rtnl_class_set_parent(filter, class->c_handle);
448         rtnl_class_set_ifindex(filter, class->c_ifindex);
449         rtnl_class_set_kind(filter, class->c_kind);
450
451         nl_cache_foreach_filter(cache, (struct nl_object *) filter, cb, arg);
452         rtnl_class_put(filter);
453 }
454
455 /**
456  * Call a callback for each classifier attached to the class
457  * @arg class           the parent class
458  * @arg cache           a filter cache including at least all the filters
459  *                      attached to the specified class
460  * @arg cb              callback function
461  * @arg arg             argument to be passed to callback function
462  */
463 void rtnl_class_foreach_cls(struct rtnl_class *class, struct nl_cache *cache,
464                             void (*cb)(struct nl_object *, void *), void *arg)
465 {
466         struct rtnl_cls *filter;
467
468         filter = rtnl_cls_alloc();
469         if (!filter)
470                 return;
471
472         rtnl_cls_set_ifindex(filter, class->c_ifindex);
473         rtnl_cls_set_parent(filter, class->c_parent);
474
475         nl_cache_foreach_filter(cache, (struct nl_object *) filter, cb, arg);
476         rtnl_cls_put(filter);
477 }
478
479 /** @} */
480
481 /**
482  * @name Attribute Modifications
483  * @{
484  */
485
486 /**
487  * Set the interface index of a class to the specified value
488  * @arg class           class to be changed
489  * @arg ifindex         new interface index
490  */
491 void rtnl_class_set_ifindex(struct rtnl_class *class, int ifindex)
492 {
493         tca_set_ifindex((struct rtnl_tca *) class, ifindex);
494 }
495
496 /**
497  * Get the interface index of a class
498  * @arg class           class handle
499  * @return Interface index or RTNL_LINK_NOT_FOUND if not set
500  */
501 int rtnl_class_get_ifindex(struct rtnl_class *class)
502 {
503         return tca_get_ifindex((struct rtnl_tca *) class);
504 }
505
506
507 /**
508  * Set the handle of a class to the specified value
509  * @arg class           class to be changed
510  * @arg handle          new handle
511  */
512 void rtnl_class_set_handle(struct rtnl_class *class, uint32_t handle)
513 {
514         tca_set_handle((struct rtnl_tca *) class, handle);
515 }
516
517 /**
518  * Get the handle of a class
519  * @arg class           class handle
520  * @return Handle or 0 if not set
521  */
522 uint32_t rtnl_class_get_handle(struct rtnl_class *class)
523 {
524         return tca_get_handle((struct rtnl_tca *) class);
525 }
526
527 /**
528  * Set the parent handle of a class to the specified value
529  * @arg class           class to be changed
530  * @arg parent          new parent handle
531  */
532 void rtnl_class_set_parent(struct rtnl_class *class, uint32_t parent)
533 {
534         tca_set_parent((struct rtnl_tca *) class, parent);
535 }
536
537 /**
538  * Get the parent handle of a class
539  * @arg class           class handle
540  * @return Parent handle or 0 if not set
541  */
542 uint32_t rtnl_class_get_parent(struct rtnl_class *class)
543 {
544         return tca_get_parent((struct rtnl_tca *) class);
545 }
546
547 /**
548  * Set the kind of a class to the specified value
549  * @arg class           class to be changed
550  * @arg name            new kind name
551  */
552 void rtnl_class_set_kind(struct rtnl_class *class, const char *name)
553 {
554         tca_set_kind((struct rtnl_tca *) class, name);
555 }
556
557 /**
558  * Get the kind of a class
559  * @arg class           class handle
560  * @return Kind or NULL if not set
561  */
562 char *rtnl_class_get_kind(struct rtnl_class *class)
563 {
564         return tca_get_kind((struct rtnl_tca *) class);
565 }
566
567 /**
568  * Get the statistic specified by the id
569  * @arg class           class handle
570  * @arg id              statistic id
571  * @return The current counter of the specified statistic
572  */
573 uint64_t rtnl_class_get_stat(struct rtnl_class *class,
574                              enum rtnl_tc_stats_id id)
575 {
576         return tca_get_stat((struct rtnl_tca *) class, id);
577 }
578
579 /** @} */
580
581 static struct nl_cache_ops rtnl_class_ops = {
582         .co_name                = "route/class",
583         .co_size                = sizeof(struct rtnl_class),
584         .co_hdrsize             = sizeof(struct tcmsg),
585         .co_msgtypes            = {
586                                         { RTM_NEWTCLASS, "new" },
587                                         { RTM_DELTCLASS, "del" },
588                                         { RTM_GETTCLASS, "get" },
589                                         { -1, NULL },
590                                   },
591         .co_protocol            = NETLINK_ROUTE,
592         .co_request_update      = &class_request_update,
593         .co_msg_parser          = &class_msg_parser,
594         .co_free_data           = &class_free_data,
595         .co_dump[NL_DUMP_BRIEF] = &class_dump_brief,
596         .co_dump[NL_DUMP_FULL]  = &class_dump_full,
597         .co_dump[NL_DUMP_STATS] = &class_dump_stats,
598         .co_filter              = &class_filter,
599 };
600
601 static void __init class_init(void)
602 {
603         nl_cache_mngt_register(&rtnl_class_ops);
604 }
605
606 static void __exit class_exit(void)
607 {
608         nl_cache_mngt_unregister(&rtnl_class_ops);
609 }
610
611 /** @} */