cls.c

00001 /*
00002  * lib/route/classifier.c       Classifier
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-2009 Thomas Graf <tgraf@suug.ch>
00010  */
00011 
00012 /**
00013  * @ingroup tc
00014  * @defgroup cls Classifiers
00015  *
00016  * @par Classifier Identification
00017  * - protocol
00018  * - priority
00019  * - parent
00020  * - interface
00021  * - kind
00022  * - handle
00023  * 
00024  * @{
00025  */
00026 
00027 #include <netlink-local.h>
00028 #include <netlink-tc.h>
00029 #include <netlink/netlink.h>
00030 #include <netlink/utils.h>
00031 #include <netlink/route/tc-api.h>
00032 #include <netlink/route/classifier.h>
00033 #include <netlink/route/link.h>
00034 
00035 /** @cond SKIP */
00036 #define CLS_ATTR_PRIO           (TCA_ATTR_MAX << 1)
00037 #define CLS_ATTR_PROTOCOL       (TCA_ATTR_MAX << 2)
00038 /** @endcond */
00039 
00040 static struct nl_object_ops cls_obj_ops;
00041 static struct nl_cache_ops rtnl_cls_ops;
00042 
00043 
00044 static int cls_build(struct rtnl_cls *cls, int type, int flags,
00045                      struct nl_msg **result)
00046 {
00047         int err, prio, proto;
00048         struct tcmsg *tchdr;
00049 
00050         err = rtnl_tc_msg_build(TC_CAST(cls), type, flags, result);
00051         if (err < 0)
00052                 return err;
00053 
00054         tchdr = nlmsg_data(nlmsg_hdr(*result));
00055         prio = rtnl_cls_get_prio(cls);
00056         proto = rtnl_cls_get_protocol(cls);
00057         tchdr->tcm_info = TC_H_MAKE(prio << 16, htons(proto));
00058 
00059         return 0;
00060 }
00061 
00062 /**
00063  * @name Allocation/Freeing
00064  * @{
00065  */
00066 
00067 struct rtnl_cls *rtnl_cls_alloc(void)
00068 {
00069         struct rtnl_tc *tc;
00070 
00071         tc = TC_CAST(nl_object_alloc(&cls_obj_ops));
00072         if (tc)
00073                 tc->tc_type = RTNL_TC_TYPE_CLS;
00074 
00075         return (struct rtnl_cls *) tc;
00076 }
00077 
00078 void rtnl_cls_put(struct rtnl_cls *cls)
00079 {
00080         nl_object_put((struct nl_object *) cls);
00081 }
00082 
00083 /** @} */
00084 
00085 /**
00086  * @name Attributes
00087  * @{
00088  */
00089 
00090 void rtnl_cls_set_prio(struct rtnl_cls *cls, uint16_t prio)
00091 {
00092         cls->c_prio = prio;
00093         cls->ce_mask |= CLS_ATTR_PRIO;
00094 }
00095 
00096 uint16_t rtnl_cls_get_prio(struct rtnl_cls *cls)
00097 {
00098         if (cls->ce_mask & CLS_ATTR_PRIO)
00099                 return cls->c_prio;
00100         else
00101                 return 0;
00102 }
00103 
00104 void rtnl_cls_set_protocol(struct rtnl_cls *cls, uint16_t protocol)
00105 {
00106         cls->c_protocol = protocol;
00107         cls->ce_mask |= CLS_ATTR_PROTOCOL;
00108 }
00109 
00110 uint16_t rtnl_cls_get_protocol(struct rtnl_cls *cls)
00111 {
00112         if (cls->ce_mask & CLS_ATTR_PROTOCOL)
00113                 return cls->c_protocol;
00114         else
00115                 return ETH_P_ALL;
00116 }
00117 
00118 /** @} */
00119 
00120 
00121 /**
00122  * @name Classifier Addition/Modification/Deletion
00123  * @{
00124  */
00125 
00126 /**
00127  * Build a netlink message to add a new classifier
00128  * @arg cls             classifier to add
00129  * @arg flags           additional netlink message flags
00130  * @arg result          Pointer to store resulting message.
00131  *
00132  * Builds a new netlink message requesting an addition of a classifier
00133  * The netlink message header isn't fully equipped with all relevant
00134  * fields and must be sent out via nl_send_auto_complete() or
00135  * supplemented as needed. \a classifier must contain the attributes of
00136  * the new classifier set via \c rtnl_cls_set_* functions. \a opts
00137  * may point to the clsasifier specific options.
00138  *
00139  * @return 0 on success or a negative error code.
00140  */
00141 int rtnl_cls_build_add_request(struct rtnl_cls *cls, int flags,
00142                                struct nl_msg **result)
00143 {
00144         return cls_build(cls, RTM_NEWTFILTER, NLM_F_CREATE | flags, result);
00145 }
00146 
00147 /**
00148  * Add a new classifier
00149  * @arg sk              Netlink socket.
00150  * @arg cls             classifier to add
00151  * @arg flags           additional netlink message flags
00152  *
00153  * Builds a netlink message by calling rtnl_cls_build_add_request(),
00154  * sends the request to the kernel and waits for the next ACK to be
00155  * received and thus blocks until the request has been processed.
00156  *
00157  * @return 0 on sucess or a negative error if an error occured.
00158  */
00159 int rtnl_cls_add(struct nl_sock *sk, struct rtnl_cls *cls, int flags)
00160 {
00161         struct nl_msg *msg;
00162         int err;
00163         
00164         if ((err = rtnl_cls_build_add_request(cls, flags, &msg)) < 0)
00165                 return err;
00166         
00167         err = nl_send_auto_complete(sk, msg);
00168         nlmsg_free(msg);
00169         if (err < 0)
00170                 return err;
00171 
00172         return nl_wait_for_ack(sk);
00173 }
00174 
00175 /**
00176  * Build a netlink message to change classifier attributes
00177  * @arg cls             classifier to change
00178  * @arg flags           additional netlink message flags
00179  * @arg result          Pointer to store resulting message.
00180  *
00181  * Builds a new netlink message requesting a change of a neigh
00182  * attributes. The netlink message header isn't fully equipped with
00183  * all relevant fields and must thus be sent out via nl_send_auto_complete()
00184  * or supplemented as needed.
00185  *
00186  * @return 0 on success or a negative error code.
00187  */
00188 int rtnl_cls_build_change_request(struct rtnl_cls *cls, int flags,
00189                                   struct nl_msg **result)
00190 {
00191         return cls_build(cls, RTM_NEWTFILTER, NLM_F_REPLACE | flags, result);
00192 }
00193 
00194 /**
00195  * Change a classifier
00196  * @arg sk              Netlink socket.
00197  * @arg cls             classifier to change
00198  * @arg flags           additional netlink message flags
00199  *
00200  * Builds a netlink message by calling rtnl_cls_build_change_request(),
00201  * sends the request to the kernel and waits for the next ACK to be
00202  * received and thus blocks until the request has been processed.
00203  *
00204  * @return 0 on sucess or a negative error if an error occured.
00205  */
00206 int rtnl_cls_change(struct nl_sock *sk, struct rtnl_cls *cls, int flags)
00207 {
00208         struct nl_msg *msg;
00209         int err;
00210         
00211         if ((err = rtnl_cls_build_change_request(cls, flags, &msg)) < 0)
00212                 return err;
00213         
00214         err = nl_send_auto_complete(sk, msg);
00215         nlmsg_free(msg);
00216         if (err < 0)
00217                 return err;
00218 
00219         return nl_wait_for_ack(sk);
00220 }
00221 
00222 /**
00223  * Build a netlink request message to delete a classifier
00224  * @arg cls             classifier to delete
00225  * @arg flags           additional netlink message flags
00226  * @arg result          Pointer to store resulting message.
00227  *
00228  * Builds a new netlink message requesting a deletion of a classifier.
00229  * The netlink message header isn't fully equipped with all relevant
00230  * fields and must thus be sent out via nl_send_auto_complete()
00231  * or supplemented as needed.
00232  *
00233  * @return 0 on success or a negative error code.
00234  */
00235 int rtnl_cls_build_delete_request(struct rtnl_cls *cls, int flags,
00236                                   struct nl_msg **result)
00237 {
00238         return cls_build(cls, RTM_DELTFILTER, flags, result);
00239 }
00240 
00241 
00242 /**
00243  * Delete a classifier
00244  * @arg sk              Netlink socket.
00245  * @arg cls             classifier to delete
00246  * @arg flags           additional netlink message flags
00247  *
00248  * Builds a netlink message by calling rtnl_cls_build_delete_request(),
00249  * sends the request to the kernel and waits for the next ACK to be
00250  * received and thus blocks until the request has been processed.
00251  *
00252  * @return 0 on sucess or a negative error if an error occured.
00253  */
00254 int rtnl_cls_delete(struct nl_sock *sk, struct rtnl_cls *cls, int flags)
00255 {
00256         struct nl_msg *msg;
00257         int err;
00258         
00259         if ((err = rtnl_cls_build_delete_request(cls, flags, &msg)) < 0)
00260                 return err;
00261         
00262         err = nl_send_auto_complete(sk, msg);
00263         nlmsg_free(msg);
00264         if (err < 0)
00265                 return err;
00266 
00267         return nl_wait_for_ack(sk);
00268 }
00269 
00270 /** @} */
00271 
00272 /**
00273  * @name Cache Management
00274  * @{
00275  */
00276 
00277 /**
00278  * Build a classifier cache including all classifiers attached to the
00279  * specified class/qdisc on eht specified interface.
00280  * @arg sk              Netlink socket.
00281  * @arg ifindex         interface index of the link the classes are
00282  *                      attached to.
00283  * @arg parent          parent qdisc/class
00284  * @arg result          Pointer to store resulting cache.
00285  *
00286  * Allocates a new cache, initializes it properly and updates it to
00287  * include all classes attached to the specified interface.
00288  *
00289  * @note The caller is responsible for destroying and freeing the
00290  *       cache after using it.
00291  * @return 0 on success or a negative error code.
00292  */
00293 int rtnl_cls_alloc_cache(struct nl_sock *sk, int ifindex, uint32_t parent,                       struct nl_cache **result)
00294 {
00295         struct nl_cache * cache;
00296         int err;
00297         
00298         if (!(cache = nl_cache_alloc(&rtnl_cls_ops)))
00299                 return -NLE_NOMEM;
00300 
00301         cache->c_iarg1 = ifindex;
00302         cache->c_iarg2 = parent;
00303         
00304         if (sk && (err = nl_cache_refill(sk, cache)) < 0) {
00305                 nl_cache_free(cache);
00306                 return err;
00307         }
00308 
00309         *result = cache;
00310         return 0;
00311 }
00312 
00313 /** @} */
00314 
00315 static void cls_dump_line(struct rtnl_tc *tc, struct nl_dump_params *p)
00316 {
00317         struct rtnl_cls *cls = (struct rtnl_cls *) tc;
00318         char buf[32];
00319 
00320         nl_dump(p, " prio %u protocol %s", cls->c_prio,
00321                 nl_ether_proto2str(cls->c_protocol, buf, sizeof(buf)));
00322 }
00323 
00324 static int cls_msg_parser(struct nl_cache_ops *ops, struct sockaddr_nl *who,
00325                           struct nlmsghdr *nlh, struct nl_parser_param *pp)
00326 {
00327         struct rtnl_cls *cls;
00328         int err;
00329 
00330         if (!(cls = rtnl_cls_alloc()))
00331                 return -NLE_NOMEM;
00332 
00333         if ((err = rtnl_tc_msg_parse(nlh, TC_CAST(cls))) < 0)
00334                 goto errout;
00335 
00336         cls->c_prio = TC_H_MAJ(cls->c_info) >> 16;
00337         cls->c_protocol = ntohs(TC_H_MIN(cls->c_info));
00338 
00339         err = pp->pp_cb(OBJ_CAST(cls), pp);
00340 errout:
00341         rtnl_cls_put(cls);
00342 
00343         return err;
00344 }
00345 
00346 static int cls_request_update(struct nl_cache *cache, struct nl_sock *sk)
00347 {
00348         struct tcmsg tchdr = {
00349                 .tcm_family = AF_UNSPEC,
00350                 .tcm_ifindex = cache->c_iarg1,
00351                 .tcm_parent = cache->c_iarg2,
00352         };
00353 
00354         return nl_send_simple(sk, RTM_GETTFILTER, NLM_F_DUMP, &tchdr,
00355                               sizeof(tchdr));
00356 }
00357 
00358 static struct rtnl_tc_type_ops cls_ops = {
00359         .tt_type                = RTNL_TC_TYPE_CLS,
00360         .tt_dump_prefix         = "cls",
00361         .tt_dump = {
00362                 [NL_DUMP_LINE]  = cls_dump_line,
00363         },
00364 };
00365 
00366 static struct nl_cache_ops rtnl_cls_ops = {
00367         .co_name                = "route/cls",
00368         .co_hdrsize             = sizeof(struct tcmsg),
00369         .co_msgtypes            = {
00370                                         { RTM_NEWTFILTER, NL_ACT_NEW, "new" },
00371                                         { RTM_DELTFILTER, NL_ACT_DEL, "del" },
00372                                         { RTM_GETTFILTER, NL_ACT_GET, "get" },
00373                                         END_OF_MSGTYPES_LIST,
00374                                   },
00375         .co_protocol            = NETLINK_ROUTE,
00376         .co_request_update      = cls_request_update,
00377         .co_msg_parser          = cls_msg_parser,
00378         .co_obj_ops             = &cls_obj_ops,
00379 };
00380 
00381 static struct nl_object_ops cls_obj_ops = {
00382         .oo_name                = "route/cls",
00383         .oo_size                = sizeof(struct rtnl_cls),
00384         .oo_free_data           = rtnl_tc_free_data,
00385         .oo_clone               = rtnl_tc_clone,
00386         .oo_dump = {
00387             [NL_DUMP_LINE]      = rtnl_tc_dump_line,
00388             [NL_DUMP_DETAILS]   = rtnl_tc_dump_details,
00389             [NL_DUMP_STATS]     = rtnl_tc_dump_stats,
00390         },
00391         .oo_compare             = rtnl_tc_compare,
00392         .oo_id_attrs            = (TCA_ATTR_IFINDEX | TCA_ATTR_HANDLE),
00393 };
00394 
00395 static void __init cls_init(void)
00396 {
00397         rtnl_tc_type_register(&cls_ops);
00398         nl_cache_mngt_register(&rtnl_cls_ops);
00399 }
00400 
00401 static void __exit cls_exit(void)
00402 {
00403         nl_cache_mngt_unregister(&rtnl_cls_ops);
00404         rtnl_tc_type_unregister(&cls_ops);
00405 }
00406 
00407 /** @} */