libnl 2.0

/build/buildd/libnl2-2.0/lib/route/cls/ematch.c

00001 /*
00002  * lib/route/cls/ematch.c       Extended Matches
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) 2008-2009 Thomas Graf <tgraf@suug.ch>
00010  */
00011 
00012 /**
00013  * @ingroup cls
00014  * @defgroup ematch Extended Match
00015  *
00016  * @{
00017  */
00018 
00019 #include <netlink-local.h>
00020 #include <netlink-tc.h>
00021 #include <netlink/netlink.h>
00022 #include <netlink/route/classifier.h>
00023 #include <netlink/route/classifier-modules.h>
00024 #include <netlink/route/cls/ematch.h>
00025 
00026 /**
00027  * @name Module Registration
00028  * @{
00029  */
00030 
00031 static NL_LIST_HEAD(ematch_ops_list);
00032 
00033 /**
00034  * Register ematch module
00035  * @arg ops             Module operations.
00036  *
00037  * @return 0 on success or a negative error code.
00038  */
00039 int rtnl_ematch_register(struct rtnl_ematch_ops *ops)
00040 {
00041         if (rtnl_ematch_lookup_ops(ops->eo_kind))
00042                 return -NLE_EXIST;
00043 
00044         nl_list_add_tail(&ops->eo_list, &ematch_ops_list);
00045 
00046         return 0;
00047 }
00048 
00049 /**
00050  * Unregister ematch module
00051  * @arg ops             Module operations.
00052  *
00053  * @return 0 on success or a negative error code.
00054  */
00055 int rtnl_ematch_unregister(struct rtnl_ematch_ops *ops)
00056 {
00057         struct rtnl_ematch_ops *o;
00058 
00059         nl_list_for_each_entry(o, &ematch_ops_list, eo_list) {
00060                 if (ops->eo_kind == o->eo_kind) {
00061                         nl_list_del(&o->eo_list);
00062                         return 0;
00063                 }
00064         }
00065 
00066         return -NLE_OBJ_NOTFOUND;
00067 }
00068 
00069 /**
00070  * Lookup ematch module by kind
00071  * @arg kind            Module kind.
00072  *
00073  * @return Module operations or NULL if not found.
00074  */
00075 struct rtnl_ematch_ops *rtnl_ematch_lookup_ops(int kind)
00076 {
00077         struct rtnl_ematch_ops *ops;
00078 
00079         nl_list_for_each_entry(ops, &ematch_ops_list, eo_list)
00080                 if (ops->eo_kind == kind)
00081                         return ops;
00082 
00083         return NULL;
00084 }
00085 
00086 /**
00087  * Lookup ematch module by name
00088  * @arg name            Name of ematch module.
00089  *
00090  * @return Module operations or NULL if not fuond.
00091  */
00092 struct rtnl_ematch_ops *rtnl_ematch_lookup_ops_name(const char *name)
00093 {
00094         struct rtnl_ematch_ops *ops;
00095 
00096         nl_list_for_each_entry(ops, &ematch_ops_list, eo_list)
00097                 if (!strcasecmp(ops->eo_name, name))
00098                         return ops;
00099 
00100         return NULL;
00101 }
00102 
00103 /** @} */
00104 
00105 /**
00106  * @name Match
00107  */
00108 
00109 struct rtnl_ematch *rtnl_ematch_alloc(struct rtnl_ematch_ops *ops)
00110 {
00111         struct rtnl_ematch *e;
00112         size_t len = sizeof(*e) + (ops ? ops->eo_datalen : 0);
00113 
00114         if (!(e = calloc(1, len)))
00115                 return NULL;
00116 
00117         NL_INIT_LIST_HEAD(&e->e_list);
00118         NL_INIT_LIST_HEAD(&e->e_childs);
00119 
00120         if (ops) {
00121                 e->e_ops = ops;
00122                 e->e_kind = ops->eo_kind;
00123         }
00124 
00125         return e;
00126 }
00127 
00128 /**
00129  * Add ematch to the end of the parent's list of children.
00130  * @arg parent          Parent ematch.
00131  * @arg child           Ematch to be added as new child of parent.
00132  */
00133 void rtnl_ematch_add_child(struct rtnl_ematch *parent,
00134                            struct rtnl_ematch *child)
00135 {
00136         nl_list_add_tail(&child->e_list, &parent->e_childs);
00137 }
00138 
00139 /**
00140  * Remove ematch from the list it is linked to.
00141  * @arg ematch          Ematch to be unlinked.
00142  */
00143 void rtnl_ematch_unlink(struct rtnl_ematch *ematch)
00144 {
00145         nl_list_del(&ematch->e_list);
00146 }
00147 
00148 void rtnl_ematch_free(struct rtnl_ematch *ematch)
00149 {
00150         if (!ematch)
00151                 return;
00152 
00153         free(ematch);
00154 }
00155 
00156 void rtnl_ematch_set_flags(struct rtnl_ematch *ematch, uint16_t flags)
00157 {
00158         ematch->e_flags |= flags;
00159 }
00160 
00161 void rtnl_ematch_unset_flags(struct rtnl_ematch *ematch, uint16_t flags)
00162 {
00163         ematch->e_flags &= ~flags;
00164 }
00165 
00166 uint16_t rtnl_ematch_get_flags(struct rtnl_ematch *ematch)
00167 {
00168         return ematch->e_flags;
00169 }
00170 
00171 void *rtnl_ematch_data(struct rtnl_ematch *ematch)
00172 {
00173         return ematch->e_data;
00174 }
00175 
00176 /** @} */
00177 
00178 /**
00179  * @name Tree
00180  */
00181 
00182 struct rtnl_ematch_tree *rtnl_ematch_tree_alloc(uint16_t progid)
00183 {
00184         struct rtnl_ematch_tree *tree;
00185 
00186         if (!(tree = calloc(1, sizeof(*tree))))
00187                 return NULL;
00188 
00189         NL_INIT_LIST_HEAD(&tree->et_list);
00190         tree->et_progid = progid;
00191 
00192         return tree;
00193 }
00194 
00195 static void free_ematch_list(struct nl_list_head *head)
00196 {
00197         struct rtnl_ematch *pos, *next;
00198 
00199         nl_list_for_each_entry_safe(pos, next, head, e_list) {
00200                 if (!nl_list_empty(&pos->e_childs))
00201                         free_ematch_list(&pos->e_childs);
00202                 rtnl_ematch_free(pos);
00203         }
00204 }
00205 
00206 void rtnl_ematch_tree_free(struct rtnl_ematch_tree *tree)
00207 {
00208         if (!tree)
00209                 return;
00210 
00211         free_ematch_list(&tree->et_list);
00212         free(tree);
00213 }
00214 
00215 void rtnl_ematch_tree_add_tail(struct rtnl_ematch_tree *tree,
00216                                struct rtnl_ematch *ematch)
00217 {
00218         nl_list_add_tail(&ematch->e_list, &tree->et_list);
00219 }
00220 
00221 static inline uint32_t container_ref(struct rtnl_ematch *ematch)
00222 {
00223         return *((uint32_t *) rtnl_ematch_data(ematch));
00224 }
00225 
00226 static int link_tree(struct rtnl_ematch *index[], int nmatches, int pos,
00227                      struct nl_list_head *root)
00228 {
00229         struct rtnl_ematch *ematch;
00230         int i;
00231 
00232         for (i = pos; i < nmatches; i++) {
00233                 ematch = index[i];
00234 
00235                 nl_list_add_tail(&ematch->e_list, root);
00236 
00237                 if (ematch->e_kind == TCF_EM_CONTAINER)
00238                         link_tree(index, nmatches, container_ref(ematch),
00239                                   &ematch->e_childs);
00240 
00241                 if (!(ematch->e_flags & TCF_EM_REL_MASK))
00242                         return 0;
00243         }
00244 
00245         /* Last entry in chain can't possibly have no relation */
00246         return -NLE_INVAL;
00247 }
00248 
00249 static struct nla_policy tree_policy[TCA_EMATCH_TREE_MAX+1] = {
00250         [TCA_EMATCH_TREE_HDR]  = { .minlen=sizeof(struct tcf_ematch_tree_hdr) },
00251         [TCA_EMATCH_TREE_LIST] = { .type = NLA_NESTED },
00252 };
00253 
00254 /**
00255  * Parse ematch netlink attributes
00256  *
00257  * @return 0 on success or a negative error code.
00258  */
00259 int rtnl_ematch_parse(struct nlattr *attr, struct rtnl_ematch_tree **result)
00260 {
00261         struct nlattr *a, *tb[TCA_EMATCH_TREE_MAX+1];
00262         struct tcf_ematch_tree_hdr *thdr;
00263         struct rtnl_ematch_tree *tree;
00264         struct rtnl_ematch **index;
00265         int nmatches = 0, err, remaining;
00266 
00267         err = nla_parse_nested(tb, TCA_EMATCH_TREE_MAX, attr, tree_policy);
00268         if (err < 0)
00269                 return err;
00270 
00271         if (!tb[TCA_EMATCH_TREE_HDR])
00272                 return -NLE_MISSING_ATTR;
00273 
00274         thdr = nla_data(tb[TCA_EMATCH_TREE_HDR]);
00275 
00276         /* Ignore empty trees */
00277         if (thdr->nmatches == 0)
00278                 return 0;
00279 
00280         if (!tb[TCA_EMATCH_TREE_LIST])
00281                 return -NLE_MISSING_ATTR;
00282 
00283         if (thdr->nmatches > (nla_len(tb[TCA_EMATCH_TREE_LIST]) /
00284                               nla_total_size(sizeof(struct tcf_ematch_hdr))))
00285                 return -NLE_INVAL;
00286 
00287         if (!(index = calloc(thdr->nmatches, sizeof(struct rtnl_ematch *))))
00288                 return -NLE_NOMEM;
00289 
00290         if (!(tree = rtnl_ematch_tree_alloc(thdr->progid))) {
00291                 err = -NLE_NOMEM;
00292                 goto errout;
00293         }
00294 
00295         nla_for_each_nested(a, tb[TCA_EMATCH_TREE_LIST], remaining) {
00296                 struct rtnl_ematch_ops *ops;
00297                 struct tcf_ematch_hdr *hdr;
00298                 struct rtnl_ematch *ematch;
00299                 void *data;
00300                 size_t len;
00301 
00302                 if (nla_len(a) < sizeof(*hdr)) {
00303                         err = -NLE_INVAL;
00304                         goto errout;
00305                 }
00306 
00307                 if (nmatches >= thdr->nmatches) {
00308                         err = -NLE_RANGE;
00309                         goto errout;
00310                 }
00311 
00312                 hdr = nla_data(a);
00313                 data = nla_data(a) + NLA_ALIGN(sizeof(*hdr));
00314                 len = nla_len(a) - NLA_ALIGN(sizeof(*hdr));
00315 
00316                 ops = rtnl_ematch_lookup_ops(hdr->kind);
00317                 if (ops && ops->eo_datalen && len < ops->eo_datalen) {
00318                         err = -NLE_INVAL;
00319                         goto errout;
00320                 }
00321 
00322                 if (!(ematch = rtnl_ematch_alloc(ops))) {
00323                         err = -NLE_NOMEM;
00324                         goto errout;
00325                 }
00326 
00327                 ematch->e_id = hdr->matchid;
00328                 ematch->e_kind = hdr->kind;
00329                 ematch->e_flags = hdr->flags;
00330 
00331                 if (ops && (err = ops->eo_parse(ematch, data, len)) < 0)
00332                         goto errout;
00333 
00334                 if (hdr->kind == TCF_EM_CONTAINER &&
00335                     container_ref(ematch) >= thdr->nmatches) {
00336                         err = -NLE_INVAL;
00337                         goto errout;
00338                 }
00339 
00340                 index[nmatches++] = ematch;
00341         }
00342 
00343         if (nmatches != thdr->nmatches) {
00344                 err = -NLE_INVAL;
00345                 goto errout;
00346         }
00347 
00348         err = link_tree(index, nmatches, 0, &tree->et_list);
00349         if (err < 0)
00350                 goto errout;
00351 
00352         free(index);
00353         *result = tree;
00354 
00355         return 0;
00356 
00357 errout:
00358         rtnl_ematch_tree_free(tree);
00359         free(index);
00360         return err;
00361 }
00362 
00363 static void dump_ematch_sequence(struct nl_list_head *head,
00364                                  struct nl_dump_params *p)
00365 {
00366         struct rtnl_ematch *match;
00367 
00368         nl_list_for_each_entry(match, head, e_list) {
00369                 if (match->e_flags & TCF_EM_INVERT)
00370                         nl_dump(p, "NOT ");
00371 
00372                 if (match->e_kind == TCF_EM_CONTAINER) {
00373                         nl_dump(p, "(");
00374                         dump_ematch_sequence(&match->e_childs, p);
00375                         nl_dump(p, ")");
00376                 } else if (!match->e_ops) {
00377                         nl_dump(p, "[unknown ematch %d]", match->e_kind);
00378                 } else {
00379                         nl_dump(p, "%s(", match->e_ops->eo_name);
00380 
00381                         if (match->e_ops->eo_dump)
00382                                 match->e_ops->eo_dump(match, p);
00383 
00384                         nl_dump(p, ")");
00385                 }
00386 
00387                 switch (match->e_flags & TCF_EM_REL_MASK) {
00388                 case TCF_EM_REL_AND:
00389                         nl_dump(p, " AND ");
00390                         break;
00391                 case TCF_EM_REL_OR:
00392                         nl_dump(p, " OR ");
00393                         break;
00394                 default:
00395                         /* end of first level ematch sequence */
00396                         return;
00397                 }
00398         }
00399 }
00400 
00401 void rtnl_ematch_tree_dump(struct rtnl_ematch_tree *tree,
00402                            struct nl_dump_params *p)
00403 {
00404         dump_ematch_sequence(&tree->et_list, p);
00405         nl_dump(p, "\n");
00406 }
00407 
00408 /** @} */
00409 
00410 /** @} */