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