class.c

00001 /*
00002  * lib/route/class.c            Queueing Classes
00003  *
00004  *      This library is free software; you can redistribute it and/or
00005  *      modify it under the terms of the GNU Lesser General Public
00006  *      License as published by the Free Software Foundation version 2.1
00007  *      of the License.
00008  *
00009  * Copyright (c) 2003-2010 Thomas Graf <tgraf@suug.ch>
00010  */
00011 
00012 /**
00013  * @ingroup tc
00014  * @defgroup class Queueing Classes
00015  * @{
00016  */
00017 
00018 #include <netlink-local.h>
00019 #include <netlink-tc.h>
00020 #include <netlink/netlink.h>
00021 #include <netlink/route/tc-api.h>
00022 #include <netlink/route/class.h>
00023 #include <netlink/route/qdisc.h>
00024 #include <netlink/route/classifier.h>
00025 #include <netlink/utils.h>
00026 
00027 static struct nl_cache_ops rtnl_class_ops;
00028 static struct nl_object_ops class_obj_ops;
00029 
00030 static void class_dump_details(struct rtnl_tc *tc, struct nl_dump_params *p)
00031 {
00032         struct rtnl_class *class = (struct rtnl_class *) tc;
00033         char buf[32];
00034 
00035         if (class->c_info)
00036                 nl_dump(p, "child-qdisc %s ",
00037                         rtnl_tc_handle2str(class->c_info, buf, sizeof(buf)));
00038 }
00039 
00040 
00041 static int class_msg_parser(struct nl_cache_ops *ops, struct sockaddr_nl *who,
00042                             struct nlmsghdr *nlh, struct nl_parser_param *pp)
00043 {
00044         struct rtnl_class *class;
00045         int err;
00046 
00047         if (!(class = rtnl_class_alloc()))
00048                 return -NLE_NOMEM;
00049 
00050         if ((err = rtnl_tc_msg_parse(nlh, TC_CAST(class))) < 0)
00051                 goto errout;
00052 
00053         err = pp->pp_cb(OBJ_CAST(class), pp);
00054 errout:
00055         rtnl_class_put(class);
00056 
00057         return err;
00058 }
00059 
00060 static int class_request_update(struct nl_cache *cache, struct nl_sock *sk)
00061 {
00062         struct tcmsg tchdr = {
00063                 .tcm_family = AF_UNSPEC,
00064                 .tcm_ifindex = cache->c_iarg1,
00065         };
00066 
00067         return nl_send_simple(sk, RTM_GETTCLASS, NLM_F_DUMP, &tchdr,
00068                               sizeof(tchdr));
00069 }
00070 
00071 /**
00072  * @name Addition/Modification
00073  * @{
00074  */
00075 
00076 static int class_build(struct rtnl_class *class, int type, int flags,
00077                        struct nl_msg **result)
00078 {
00079         return rtnl_tc_msg_build(TC_CAST(class), type, flags, result);
00080 }
00081 
00082 /**
00083  * Build a netlink message to add a new class
00084  * @arg class           class to add 
00085  * @arg flags           additional netlink message flags
00086  * @arg result          Pointer to store resulting message.
00087  *
00088  * Builds a new netlink message requesting an addition of a class.
00089  * The netlink message header isn't fully equipped with all relevant
00090  * fields and must be sent out via nl_send_auto_complete() or
00091  * supplemented as needed. 
00092  *
00093  * Common message flags
00094  *   - NLM_F_REPLACE - replace possibly existing classes
00095  *
00096  * @return 0 on success or a negative error code.
00097  */
00098 int rtnl_class_build_add_request(struct rtnl_class *class, int flags,
00099                                  struct nl_msg **result)
00100 {
00101         return class_build(class, RTM_NEWTCLASS, NLM_F_CREATE | flags, result);
00102 }
00103 
00104 /**
00105  * Add a new class
00106  * @arg sk              Netlink socket.
00107  * @arg class           class to delete
00108  * @arg flags           additional netlink message flags
00109  *
00110  * Builds a netlink message by calling rtnl_qdisc_build_add_request(),
00111  * sends the request to the kernel and waits for the next ACK to be
00112  * received and thus blocks until the request has been processed.
00113  *
00114  * Common message flags
00115  *   - NLM_F_REPLACE - replace possibly existing classes
00116  *
00117  * @return 0 on success or a negative error code
00118  */
00119 int rtnl_class_add(struct nl_sock *sk, struct rtnl_class *class, int flags)
00120 {
00121         struct nl_msg *msg;
00122         int err;
00123 
00124         if ((err = rtnl_class_build_add_request(class, flags, &msg)) < 0)
00125                 return err;
00126 
00127         err = nl_send_auto_complete(sk, msg);
00128         nlmsg_free(msg);
00129         if (err < 0)
00130                 return err;
00131 
00132         return wait_for_ack(sk);
00133 }
00134 
00135 int rtnl_class_build_delete_request(struct rtnl_class *class,
00136                                                                         struct nl_msg **result)
00137 {
00138         struct nl_msg *msg;
00139         struct tcmsg tchdr;
00140         int required = TCA_ATTR_IFINDEX | TCA_ATTR_PARENT;
00141 
00142         if ((class->ce_mask & required) != required)
00143                 BUG();
00144 
00145         msg = nlmsg_alloc_simple(RTM_DELTCLASS, 0);
00146         if (!msg)
00147                 return -NLE_NOMEM;
00148 
00149         tchdr.tcm_family = AF_UNSPEC;
00150         tchdr.tcm_handle = class->c_handle;
00151         tchdr.tcm_parent = class->c_parent;
00152         tchdr.tcm_ifindex = class->c_ifindex;
00153         if (nlmsg_append(msg, &tchdr, sizeof(tchdr), NLMSG_ALIGNTO) < 0) {
00154                 nlmsg_free(msg);
00155                 return -NLE_MSGSIZE;
00156         }
00157 
00158         *result = msg;
00159         return 0;
00160 }
00161 
00162 /**
00163  * Delete a class
00164  * @arg sk              Netlink socket.
00165  * @arg class           class to delete
00166  *
00167  * Builds a netlink message by calling rtnl_class_build_delete_request(),
00168  * sends the request to the kernel and waits for the ACK to be
00169  * received and thus blocks until the request has been processed.
00170  *
00171  * @return 0 on success or a negative error code
00172  */
00173 int rtnl_class_delete(struct nl_sock *sk, struct rtnl_class *class)
00174 {
00175         struct nl_msg *msg;
00176         int err;
00177 
00178         if ((err = rtnl_class_build_delete_request(class, &msg)) < 0)
00179                 return err;
00180 
00181         err = nl_send_auto_complete(sk, msg);
00182         nlmsg_free(msg);
00183         if (err < 0)
00184                 return err;
00185 
00186         return wait_for_ack(sk);
00187 }
00188 
00189 /** @} */
00190 
00191 /**
00192  * @name Allocation/Freeing
00193  * @{
00194  */
00195 
00196 struct rtnl_class *rtnl_class_alloc(void)
00197 {
00198         struct rtnl_tc *tc;
00199 
00200         tc = TC_CAST(nl_object_alloc(&class_obj_ops));
00201         if (tc)
00202                 tc->tc_type = RTNL_TC_TYPE_CLASS;
00203 
00204         return (struct rtnl_class *) tc;
00205 }
00206 
00207 void rtnl_class_put(struct rtnl_class *class)
00208 {
00209         nl_object_put((struct nl_object *) class);
00210 }
00211 
00212 /** @} */
00213 
00214 /**
00215  * @name Leaf Qdisc
00216  * @{
00217  */
00218 
00219 /**
00220  * Lookup the leaf qdisc of a class
00221  * @arg class           the parent class
00222  * @arg cache           a qdisc cache including at laest all qdiscs of the
00223  *                      interface the specified class is attached to
00224  * @return The qdisc from the cache or NULL if the class has no leaf qdisc
00225  */
00226 struct rtnl_qdisc *rtnl_class_leaf_qdisc(struct rtnl_class *class,
00227                                          struct nl_cache *cache)
00228 {
00229         struct rtnl_qdisc *leaf;
00230 
00231         if (!class->c_info)
00232                 return NULL;
00233 
00234         leaf = rtnl_qdisc_get_by_parent(cache, class->c_ifindex,
00235                                         class->c_handle);
00236         if (!leaf || leaf->q_handle != class->c_info)
00237                 return NULL;
00238 
00239         return leaf;
00240 }
00241 
00242 /** @} */
00243 
00244 
00245 /**
00246  * @name Iterators
00247  * @{
00248  */
00249 
00250 /**
00251  * Call a callback for each child of a class
00252  * @arg class           the parent class
00253  * @arg cache           a class cache including all classes of the interface
00254  *                      the specified class is attached to
00255  * @arg cb              callback function
00256  * @arg arg             argument to be passed to callback function
00257  */
00258 void rtnl_class_foreach_child(struct rtnl_class *class, struct nl_cache *cache,
00259                               void (*cb)(struct nl_object *, void *), void *arg)
00260 {
00261         struct rtnl_class *filter;
00262         
00263         filter = rtnl_class_alloc();
00264         if (!filter)
00265                 return;
00266 
00267         rtnl_tc_set_parent(TC_CAST(filter), class->c_handle);
00268         rtnl_tc_set_ifindex(TC_CAST(filter), class->c_ifindex);
00269         rtnl_tc_set_kind(TC_CAST(filter), class->c_kind);
00270 
00271         nl_cache_foreach_filter(cache, OBJ_CAST(filter), cb, arg);
00272         rtnl_class_put(filter);
00273 }
00274 
00275 /**
00276  * Call a callback for each classifier attached to the class
00277  * @arg class           the parent class
00278  * @arg cache           a filter cache including at least all the filters
00279  *                      attached to the specified class
00280  * @arg cb              callback function
00281  * @arg arg             argument to be passed to callback function
00282  */
00283 void rtnl_class_foreach_cls(struct rtnl_class *class, struct nl_cache *cache,
00284                             void (*cb)(struct nl_object *, void *), void *arg)
00285 {
00286         struct rtnl_cls *filter;
00287 
00288         filter = rtnl_cls_alloc();
00289         if (!filter)
00290                 return;
00291 
00292         rtnl_tc_set_ifindex((struct rtnl_tc *) filter, class->c_ifindex);
00293         rtnl_tc_set_parent((struct rtnl_tc *) filter, class->c_parent);
00294 
00295         nl_cache_foreach_filter(cache, (struct nl_object *) filter, cb, arg);
00296         rtnl_cls_put(filter);
00297 }
00298 
00299 /** @} */
00300 
00301 
00302 /**
00303  * @name Cache Management
00304  * @{
00305  */
00306 
00307 /**
00308  * Build a class cache including all classes attached to the specified interface
00309  * @arg sk              Netlink socket.
00310  * @arg ifindex         interface index of the link the classes are
00311  *                      attached to.
00312  *
00313  * Allocates a new cache, initializes it properly and updates it to
00314  * include all classes attached to the specified interface.
00315  *
00316  * @return The cache or NULL if an error has occured.
00317  */
00318 int rtnl_class_alloc_cache(struct nl_sock *sk, int ifindex,
00319                            struct nl_cache **result)
00320 {
00321         struct nl_cache * cache;
00322         int err;
00323         
00324         cache = nl_cache_alloc(&rtnl_class_ops);
00325         if (!cache)
00326                 return -NLE_NOMEM;
00327 
00328         cache->c_iarg1 = ifindex;
00329         
00330         if (sk && (err = nl_cache_refill(sk, cache)) < 0) {
00331                 nl_cache_free(cache);
00332                 return err;
00333         }
00334 
00335         *result = cache;
00336         return 0;
00337 }
00338 
00339 /**
00340  * Look up class by its handle in the provided cache
00341  * @arg cache           class cache
00342  * @arg ifindex         interface the class is attached to
00343  * @arg handle          class handle
00344  * @return pointer to class inside the cache or NULL if no match was found.
00345  */
00346 struct rtnl_class *rtnl_class_get(struct nl_cache *cache, int ifindex,
00347                                                                   uint32_t handle)
00348 {
00349         struct rtnl_class *class;
00350         
00351         if (cache->c_ops != &rtnl_class_ops)
00352                 return NULL;
00353 
00354         nl_list_for_each_entry(class, &cache->c_items, ce_list) {
00355                 if (class->c_handle == handle && class->c_ifindex == ifindex) {
00356                         nl_object_get((struct nl_object *) class);
00357                         return class;
00358                 }
00359         }
00360         return NULL;
00361 }
00362 
00363 /** @} */
00364 
00365 static struct rtnl_tc_type_ops class_ops = {
00366         .tt_type                = RTNL_TC_TYPE_CLASS,
00367         .tt_dump_prefix         = "class",
00368         .tt_dump = {
00369             [NL_DUMP_DETAILS]   = class_dump_details,
00370         },
00371 };
00372 
00373 static struct nl_object_ops class_obj_ops = {
00374         .oo_name                = "route/class",
00375         .oo_size                = sizeof(struct rtnl_class),
00376         .oo_free_data           = rtnl_tc_free_data,
00377         .oo_clone               = rtnl_tc_clone,
00378         .oo_dump = {
00379             [NL_DUMP_LINE]      = rtnl_tc_dump_line,
00380             [NL_DUMP_DETAILS]   = rtnl_tc_dump_details,
00381             [NL_DUMP_STATS]     = rtnl_tc_dump_stats,
00382         },
00383         .oo_compare             = rtnl_tc_compare,
00384         .oo_id_attrs            = (TCA_ATTR_IFINDEX | TCA_ATTR_HANDLE),
00385 };
00386 
00387 static struct nl_cache_ops rtnl_class_ops = {
00388         .co_name                = "route/class",
00389         .co_hdrsize             = sizeof(struct tcmsg),
00390         .co_msgtypes            = {
00391                                         { RTM_NEWTCLASS, NL_ACT_NEW, "new" },
00392                                         { RTM_DELTCLASS, NL_ACT_DEL, "del" },
00393                                         { RTM_GETTCLASS, NL_ACT_GET, "get" },
00394                                         END_OF_MSGTYPES_LIST,
00395                                   },
00396         .co_protocol            = NETLINK_ROUTE,
00397         .co_request_update      = &class_request_update,
00398         .co_msg_parser          = &class_msg_parser,
00399         .co_obj_ops             = &class_obj_ops,
00400 };
00401 
00402 static void __init class_init(void)
00403 {
00404         rtnl_tc_type_register(&class_ops);
00405         nl_cache_mngt_register(&rtnl_class_ops);
00406 }
00407 
00408 static void __exit class_exit(void)
00409 {
00410         nl_cache_mngt_unregister(&rtnl_class_ops);
00411         rtnl_tc_type_unregister(&class_ops);
00412 }
00413 
00414 /** @} */