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-2010 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/cls/ematch.h>
00024 #include <netlink/route/cls/ematch/cmp.h>
00025 
00026 #include "ematch_syntax.h"
00027 #include "ematch_grammar.h"
00028 
00029 /**
00030  * @name Module API
00031  * @{
00032  */
00033 
00034 static NL_LIST_HEAD(ematch_ops_list);
00035 
00036 /**
00037  * Register ematch module
00038  * @arg ops             Module operations.
00039  *
00040  * This function must be called by each ematch module at initialization
00041  * time. It registers the calling module as available module.
00042  *
00043  * @return 0 on success or a negative error code.
00044  */
00045 int rtnl_ematch_register(struct rtnl_ematch_ops *ops)
00046 {
00047         if (rtnl_ematch_lookup_ops(ops->eo_kind))
00048                 return -NLE_EXIST;
00049 
00050         NL_DBG(1, "ematch module \"%s\" registered\n", ops->eo_name);
00051 
00052         nl_list_add_tail(&ops->eo_list, &ematch_ops_list);
00053 
00054         return 0;
00055 }
00056 
00057 /**
00058  * Lookup ematch module by identification number.
00059  * @arg kind            Module kind.
00060  *
00061  * Searches the list of registered ematch modules for match and returns it.
00062  *
00063  * @return Module operations or NULL if not found.
00064  */
00065 struct rtnl_ematch_ops *rtnl_ematch_lookup_ops(int kind)
00066 {
00067         struct rtnl_ematch_ops *ops;
00068 
00069         nl_list_for_each_entry(ops, &ematch_ops_list, eo_list)
00070                 if (ops->eo_kind == kind)
00071                         return ops;
00072 
00073         return NULL;
00074 }
00075 
00076 /**
00077  * Lookup ematch module by name
00078  * @arg name            Name of ematch module.
00079  *
00080  * Searches the list of registered ematch modules for a match and returns it.
00081  *
00082  * @return Module operations or NULL if not fuond.
00083  */
00084 struct rtnl_ematch_ops *rtnl_ematch_lookup_ops_by_name(const char *name)
00085 {
00086         struct rtnl_ematch_ops *ops;
00087 
00088         nl_list_for_each_entry(ops, &ematch_ops_list, eo_list)
00089                 if (!strcasecmp(ops->eo_name, name))
00090                         return ops;
00091 
00092         return NULL;
00093 }
00094 
00095 /** @} */
00096 
00097 /**
00098  * @name Match
00099  */
00100 
00101 /**
00102  * Allocate ematch object.
00103  *
00104  * Allocates and initializes an ematch object.
00105  *
00106  * @return New ematch object or NULL.
00107  */
00108 struct rtnl_ematch *rtnl_ematch_alloc(void)
00109 {
00110         struct rtnl_ematch *e;
00111 
00112         if (!(e = calloc(1, sizeof(*e))))
00113                 return NULL;
00114 
00115         NL_DBG(2, "allocated ematch %p\n", e);
00116 
00117         NL_INIT_LIST_HEAD(&e->e_list);
00118         NL_INIT_LIST_HEAD(&e->e_childs);
00119 
00120         return e;
00121 }
00122 
00123 /**
00124  * Add ematch to the end of the parent's list of children.
00125  * @arg parent          parent ematch object
00126  * @arg child           ematch object to be added to parent
00127  *
00128  * The parent must be a container ematch.
00129  */
00130 int rtnl_ematch_add_child(struct rtnl_ematch *parent,
00131                            struct rtnl_ematch *child)
00132 {
00133         if (parent->e_kind != TCF_EM_CONTAINER)
00134                 return -NLE_OPNOTSUPP;
00135 
00136         NL_DBG(2, "added ematch %p \"%s\" to container %p\n",
00137                   child, child->e_ops->eo_name, parent);
00138 
00139         nl_list_add_tail(&child->e_list, &parent->e_childs);
00140 
00141         return 0;
00142 }
00143 
00144 /**
00145  * Remove ematch from the list of ematches it is linked to.
00146  * @arg ematch          ematch object
00147  */
00148 void rtnl_ematch_unlink(struct rtnl_ematch *ematch)
00149 {
00150         NL_DBG(2, "unlinked ematch %p from any lists\n", ematch);
00151 
00152         if (!nl_list_empty(&ematch->e_childs))
00153                 NL_DBG(1, "warning: ematch %p with childs was unlinked\n",
00154                           ematch);
00155 
00156         nl_list_del(&ematch->e_list);
00157         nl_init_list_head(&ematch->e_list);
00158 }
00159 
00160 void rtnl_ematch_free(struct rtnl_ematch *ematch)
00161 {
00162         NL_DBG(2, "freed ematch %p\n", ematch);
00163         rtnl_ematch_unlink(ematch);
00164         free(ematch->e_data);
00165         free(ematch);
00166 }
00167 
00168 int rtnl_ematch_set_ops(struct rtnl_ematch *ematch, struct rtnl_ematch_ops *ops)
00169 {
00170         if (ematch->e_ops)
00171                 return -NLE_EXIST;
00172 
00173         ematch->e_ops = ops;
00174         ematch->e_kind = ops->eo_kind;
00175 
00176         if (ops->eo_datalen) {
00177                 ematch->e_data = calloc(1, ops->eo_datalen);
00178                 if (!ematch->e_data)
00179                         return -NLE_NOMEM;
00180 
00181                 ematch->e_datalen = ops->eo_datalen;
00182         }
00183 
00184         return 0;
00185 }
00186 
00187 int rtnl_ematch_set_kind(struct rtnl_ematch *ematch, uint16_t kind)
00188 {
00189         struct rtnl_ematch_ops *ops;
00190 
00191         if (ematch->e_kind)
00192                 return -NLE_EXIST;
00193 
00194         ematch->e_kind = kind;
00195 
00196         if ((ops = rtnl_ematch_lookup_ops(kind)))
00197                 rtnl_ematch_set_ops(ematch, ops);
00198 
00199         return 0;
00200 }
00201 
00202 int rtnl_ematch_set_name(struct rtnl_ematch *ematch, const char *name)
00203 {
00204         struct rtnl_ematch_ops *ops;
00205 
00206         if (ematch->e_kind)
00207                 return -NLE_EXIST;
00208 
00209         if (!(ops = rtnl_ematch_lookup_ops_by_name(name)))
00210                 return -NLE_OPNOTSUPP;
00211 
00212         rtnl_ematch_set_ops(ematch, ops);
00213 
00214         return 0;
00215 }
00216 
00217 void rtnl_ematch_set_flags(struct rtnl_ematch *ematch, uint16_t flags)
00218 {
00219         ematch->e_flags |= flags;
00220 }
00221 
00222 void rtnl_ematch_unset_flags(struct rtnl_ematch *ematch, uint16_t flags)
00223 {
00224         ematch->e_flags &= ~flags;
00225 }
00226 
00227 uint16_t rtnl_ematch_get_flags(struct rtnl_ematch *ematch)
00228 {
00229         return ematch->e_flags;
00230 }
00231 
00232 void *rtnl_ematch_data(struct rtnl_ematch *ematch)
00233 {
00234         return ematch->e_data;
00235 }
00236 
00237 /** @} */
00238 
00239 /**
00240  * @name Tree
00241  */
00242 
00243 /**
00244  * Allocate ematch tree object
00245  * @arg progid          program id
00246  */
00247 struct rtnl_ematch_tree *rtnl_ematch_tree_alloc(uint16_t progid)
00248 {
00249         struct rtnl_ematch_tree *tree;
00250 
00251         if (!(tree = calloc(1, sizeof(*tree))))
00252                 return NULL;
00253         
00254         NL_INIT_LIST_HEAD(&tree->et_list);
00255         tree->et_progid = progid;
00256 
00257         NL_DBG(2, "allocated new ematch tree %p, progid=%u\n", tree, progid);
00258 
00259         return tree;
00260 }
00261 
00262 static void free_ematch_list(struct nl_list_head *head)
00263 {
00264         struct rtnl_ematch *pos, *next;
00265 
00266         nl_list_for_each_entry_safe(pos, next, head, e_list) {
00267                 if (!nl_list_empty(&pos->e_childs))
00268                         free_ematch_list(&pos->e_childs);
00269                 rtnl_ematch_free(pos);
00270         }
00271 }
00272 
00273 /**
00274  * Free ematch tree object
00275  * @arg tree            ematch tree object
00276  *
00277  * This function frees the ematch tree and all ematches attached to it.
00278  */
00279 void rtnl_ematch_tree_free(struct rtnl_ematch_tree *tree)
00280 {
00281         if (!tree)
00282                 return;
00283 
00284         free_ematch_list(&tree->et_list);
00285         free(tree);
00286 
00287         NL_DBG(2, "Freed ematch tree %p\n", tree);
00288 }
00289 
00290 /**
00291  * Add ematch object to the end of the ematch tree
00292  * @arg tree            ematch tree object
00293  * @arg ematch          ematch object to add
00294  */
00295 void rtnl_ematch_tree_add(struct rtnl_ematch_tree *tree,
00296                           struct rtnl_ematch *ematch)
00297 {
00298         nl_list_add_tail(&ematch->e_list, &tree->et_list);
00299 }
00300 
00301 static inline uint32_t container_ref(struct rtnl_ematch *ematch)
00302 {
00303         return *((uint32_t *) rtnl_ematch_data(ematch));
00304 }
00305 
00306 static int link_tree(struct rtnl_ematch *index[], int nmatches, int pos,
00307                      struct nl_list_head *root)
00308 {
00309         struct rtnl_ematch *ematch;
00310         int i;
00311 
00312         for (i = pos; i < nmatches; i++) {
00313                 ematch = index[i];
00314 
00315                 nl_list_add_tail(&ematch->e_list, root);
00316 
00317                 if (ematch->e_kind == TCF_EM_CONTAINER)
00318                         link_tree(index, nmatches, container_ref(ematch),
00319                                   &ematch->e_childs);
00320 
00321                 if (!(ematch->e_flags & TCF_EM_REL_MASK))
00322                         return 0;
00323         }
00324 
00325         /* Last entry in chain can't possibly have no relation */
00326         return -NLE_INVAL;
00327 }
00328 
00329 static struct nla_policy tree_policy[TCA_EMATCH_TREE_MAX+1] = {
00330         [TCA_EMATCH_TREE_HDR]  = { .minlen=sizeof(struct tcf_ematch_tree_hdr) },
00331         [TCA_EMATCH_TREE_LIST] = { .type = NLA_NESTED },
00332 };
00333 
00334 /**
00335  * Parse ematch netlink attributes
00336  *
00337  * @return 0 on success or a negative error code.
00338  */
00339 int rtnl_ematch_parse_attr(struct nlattr *attr, struct rtnl_ematch_tree **result)
00340 {
00341         struct nlattr *a, *tb[TCA_EMATCH_TREE_MAX+1];
00342         struct tcf_ematch_tree_hdr *thdr;
00343         struct rtnl_ematch_tree *tree;
00344         struct rtnl_ematch **index;
00345         int nmatches = 0, err, remaining;
00346 
00347         NL_DBG(2, "Parsing attribute %p as ematch tree\n", attr);
00348 
00349         err = nla_parse_nested(tb, TCA_EMATCH_TREE_MAX, attr, tree_policy);
00350         if (err < 0)
00351                 return err;
00352 
00353         if (!tb[TCA_EMATCH_TREE_HDR])
00354                 return -NLE_MISSING_ATTR;
00355 
00356         thdr = nla_data(tb[TCA_EMATCH_TREE_HDR]);
00357 
00358         /* Ignore empty trees */
00359         if (thdr->nmatches == 0) {
00360                 NL_DBG(2, "Ignoring empty ematch configuration\n");
00361                 return 0;
00362         }
00363 
00364         if (!tb[TCA_EMATCH_TREE_LIST])
00365                 return -NLE_MISSING_ATTR;
00366 
00367         NL_DBG(2, "ematch tree found with nmatches=%u, progid=%u\n",
00368                   thdr->nmatches, thdr->progid);
00369 
00370         /*
00371          * Do some basic sanity checking since we will allocate
00372          * index[thdr->nmatches]. Calculate how many ematch headers fit into
00373          * the provided data and make sure nmatches does not exceed it.
00374          */
00375         if (thdr->nmatches > (nla_len(tb[TCA_EMATCH_TREE_LIST]) /
00376                               nla_total_size(sizeof(struct tcf_ematch_hdr))))
00377                 return -NLE_INVAL;
00378 
00379         if (!(index = calloc(thdr->nmatches, sizeof(struct rtnl_ematch *))))
00380                 return -NLE_NOMEM;
00381 
00382         if (!(tree = rtnl_ematch_tree_alloc(thdr->progid))) {
00383                 err = -NLE_NOMEM;
00384                 goto errout;
00385         }
00386 
00387         nla_for_each_nested(a, tb[TCA_EMATCH_TREE_LIST], remaining) {
00388                 struct rtnl_ematch_ops *ops;
00389                 struct tcf_ematch_hdr *hdr;
00390                 struct rtnl_ematch *ematch;
00391                 void *data;
00392                 size_t len;
00393 
00394                 NL_DBG(3, "parsing ematch attribute %d, len=%u\n",
00395                           nmatches+1, nla_len(a));
00396 
00397                 if (nla_len(a) < sizeof(*hdr)) {
00398                         err = -NLE_INVAL;
00399                         goto errout;
00400                 }
00401 
00402                 /* Quit as soon as we've parsed more matches than expected */
00403                 if (nmatches >= thdr->nmatches) {
00404                         err = -NLE_RANGE;
00405                         goto errout;
00406                 }
00407 
00408                 hdr = nla_data(a);
00409                 data = nla_data(a) + NLA_ALIGN(sizeof(*hdr));
00410                 len = nla_len(a) - NLA_ALIGN(sizeof(*hdr));
00411 
00412                 NL_DBG(3, "ematch attribute matchid=%u, kind=%u, flags=%u\n",
00413                           hdr->matchid, hdr->kind, hdr->flags);
00414 
00415                 /*
00416                  * Container matches contain a reference to another sequence
00417                  * of matches. Ensure that the reference is within boundries.
00418                  */
00419                 if (hdr->kind == TCF_EM_CONTAINER &&
00420                     *((uint32_t *) data) >= thdr->nmatches) {
00421                         err = -NLE_INVAL;
00422                         goto errout;
00423                 }
00424 
00425                 if (!(ematch = rtnl_ematch_alloc())) {
00426                         err = -NLE_NOMEM;
00427                         goto errout;
00428                 }
00429 
00430                 ematch->e_id = hdr->matchid;
00431                 ematch->e_kind = hdr->kind;
00432                 ematch->e_flags = hdr->flags;
00433 
00434                 if ((ops = rtnl_ematch_lookup_ops(hdr->kind))) {
00435                         if (ops->eo_minlen && len < ops->eo_minlen) {
00436                                 rtnl_ematch_free(ematch);
00437                                 err = -NLE_INVAL;
00438                                 goto errout;
00439                         }
00440 
00441                         rtnl_ematch_set_ops(ematch, ops);
00442 
00443                         if (ops->eo_parse &&
00444                             (err = ops->eo_parse(ematch, data, len)) < 0) {
00445                                 rtnl_ematch_free(ematch);
00446                                 goto errout;
00447                         }
00448                 }
00449 
00450                 NL_DBG(3, "index[%d] = %p\n", nmatches, ematch);
00451                 index[nmatches++] = ematch;
00452         }
00453 
00454         if (nmatches != thdr->nmatches) {
00455                 err = -NLE_INVAL;
00456                 goto errout;
00457         }
00458 
00459         err = link_tree(index, nmatches, 0, &tree->et_list);
00460         if (err < 0)
00461                 goto errout;
00462 
00463         free(index);
00464         *result = tree;
00465 
00466         return 0;
00467 
00468 errout:
00469         rtnl_ematch_tree_free(tree);
00470         free(index);
00471         return err;
00472 }
00473 
00474 static void dump_ematch_sequence(struct nl_list_head *head,
00475                                  struct nl_dump_params *p)
00476 {
00477         struct rtnl_ematch *match;
00478 
00479         nl_list_for_each_entry(match, head, e_list) {
00480                 if (match->e_flags & TCF_EM_INVERT)
00481                         nl_dump(p, "!");
00482 
00483                 if (match->e_kind == TCF_EM_CONTAINER) {
00484                         nl_dump(p, "(");
00485                         dump_ematch_sequence(&match->e_childs, p);
00486                         nl_dump(p, ")");
00487                 } else if (!match->e_ops) {
00488                         nl_dump(p, "[unknown ematch %d]", match->e_kind);
00489                 } else {
00490                         if (match->e_ops->eo_dump)
00491                                 match->e_ops->eo_dump(match, p);
00492                         else
00493                                 nl_dump(p, "[data]");
00494                 }
00495 
00496                 switch (match->e_flags & TCF_EM_REL_MASK) {
00497                 case TCF_EM_REL_AND:
00498                         nl_dump(p, " AND ");
00499                         break;
00500                 case TCF_EM_REL_OR:
00501                         nl_dump(p, " OR ");
00502                         break;
00503                 default:
00504                         /* end of first level ematch sequence */
00505                         return;
00506                 }
00507         }
00508 }
00509 
00510 void rtnl_ematch_tree_dump(struct rtnl_ematch_tree *tree,
00511                            struct nl_dump_params *p)
00512 {
00513         if (!tree)
00514                 BUG();
00515 
00516         dump_ematch_sequence(&tree->et_list, p);
00517         nl_dump(p, "\n");
00518 }
00519 
00520 static int update_container_index(struct nl_list_head *list, int *index)
00521 {
00522         struct rtnl_ematch *e;
00523 
00524         nl_list_for_each_entry(e, list, e_list)
00525                 e->e_index = (*index)++;
00526 
00527         nl_list_for_each_entry(e, list, e_list) {
00528                 if (e->e_kind == TCF_EM_CONTAINER) {
00529                         int err;
00530 
00531                         if (nl_list_empty(&e->e_childs))
00532                                 return -NLE_OBJ_NOTFOUND;
00533 
00534                         *((uint32_t *) e->e_data) = *index;
00535 
00536                         err = update_container_index(&e->e_childs, index);
00537                         if (err < 0)
00538                                 return err;
00539                 }
00540         }
00541 
00542         return 0;
00543 }
00544 
00545 static int fill_ematch_sequence(struct nl_msg *msg, struct nl_list_head *list)
00546 {
00547         struct rtnl_ematch *e;
00548 
00549         nl_list_for_each_entry(e, list, e_list) {
00550                 struct tcf_ematch_hdr match = {
00551                         .matchid = e->e_id,
00552                         .kind = e->e_kind,
00553                         .flags = e->e_flags,
00554                 };
00555                 struct nlattr *attr;
00556                 int err = 0;
00557 
00558                 if (!(attr = nla_nest_start(msg, e->e_index + 1)))
00559                         return -NLE_NOMEM;
00560 
00561                 if (nlmsg_append(msg, &match, sizeof(match), 0) < 0)
00562                         return -NLE_NOMEM;
00563 
00564                 if (e->e_ops->eo_fill)
00565                         err = e->e_ops->eo_fill(e, msg);
00566                 else if (e->e_flags & TCF_EM_SIMPLE)
00567                         err = nlmsg_append(msg, e->e_data, 4, 0);
00568                 else if (e->e_datalen > 0)
00569                         err = nlmsg_append(msg, e->e_data, e->e_datalen, 0);
00570 
00571                 NL_DBG(3, "msg %p: added ematch [%d] id=%d kind=%d flags=%d\n",
00572                           msg, e->e_index, match.matchid, match.kind, match.flags);
00573 
00574                 if (err < 0)
00575                         return -NLE_NOMEM;
00576 
00577                 nla_nest_end(msg, attr);
00578         }
00579 
00580         nl_list_for_each_entry(e, list, e_list) {
00581                 if (e->e_kind == TCF_EM_CONTAINER &&
00582                     fill_ematch_sequence(msg, &e->e_childs) < 0)
00583                         return -NLE_NOMEM;
00584         }
00585 
00586         return 0;
00587 }
00588 
00589 int rtnl_ematch_fill_attr(struct nl_msg *msg, int attrid,
00590                           struct rtnl_ematch_tree *tree)
00591 {
00592         struct tcf_ematch_tree_hdr thdr = {
00593                 .progid = tree->et_progid,
00594         };
00595         struct nlattr *list, *topattr;
00596         int err, index = 0;
00597 
00598         /* Assign index number to each ematch to allow for references
00599          * to be made while constructing the sequence of matches. */
00600         err = update_container_index(&tree->et_list, &index);
00601         if (err < 0)
00602                 return err;
00603 
00604         if (!(topattr = nla_nest_start(msg, attrid)))
00605                 goto nla_put_failure;
00606 
00607         thdr.nmatches = index;
00608         NLA_PUT(msg, TCA_EMATCH_TREE_HDR, sizeof(thdr), &thdr);
00609 
00610         if (!(list = nla_nest_start(msg, TCA_EMATCH_TREE_LIST)))
00611                 goto nla_put_failure;
00612 
00613         if (fill_ematch_sequence(msg, &tree->et_list) < 0)
00614                 goto nla_put_failure;
00615 
00616         nla_nest_end(msg, list);
00617 
00618         nla_nest_end(msg, topattr);
00619 
00620         return 0;
00621 
00622 nla_put_failure:
00623         return -NLE_NOMEM;
00624 }
00625 
00626 /** @} */
00627 
00628 extern int ematch_parse(void *, char **, struct nl_list_head *);
00629 
00630 int rtnl_ematch_parse_expr(const char *expr, char **errp,
00631                            struct rtnl_ematch_tree **result)
00632 {
00633         struct rtnl_ematch_tree *tree;
00634         YY_BUFFER_STATE buf = NULL;
00635         yyscan_t scanner = NULL;
00636         int err;
00637 
00638         NL_DBG(2, "Parsing ematch expression \"%s\"\n", expr);
00639 
00640         if (!(tree = rtnl_ematch_tree_alloc(RTNL_EMATCH_PROGID)))
00641                 return -NLE_FAILURE;
00642 
00643         if ((err = ematch_lex_init(&scanner)) < 0) {
00644                 err = -NLE_FAILURE;
00645                 goto errout;
00646         }
00647 
00648         buf = ematch__scan_string(expr, scanner);
00649 
00650         if ((err = ematch_parse(scanner, errp, &tree->et_list)) != 0) {
00651                 ematch__delete_buffer(buf, scanner);
00652                 err = -NLE_PARSE_ERR;
00653                 goto errout;
00654         }
00655 
00656         if (scanner)
00657                 ematch_lex_destroy(scanner);
00658 
00659         *result = tree;
00660 
00661         return 0;
00662 
00663 errout:
00664         if (scanner)
00665                 ematch_lex_destroy(scanner);
00666 
00667         rtnl_ematch_tree_free(tree);
00668 
00669         return err;
00670 }
00671 
00672 static const char *layer_txt[] = {
00673         [TCF_LAYER_LINK]        = "eth",
00674         [TCF_LAYER_NETWORK]     = "ip",
00675         [TCF_LAYER_TRANSPORT]   = "tcp",
00676 };
00677 
00678 char *rtnl_ematch_offset2txt(uint8_t layer, uint16_t offset, char *buf, size_t len)
00679 {
00680         snprintf(buf, len, "%s+%u",
00681                  (layer <= TCF_LAYER_MAX) ? layer_txt[layer] : "?",
00682                  offset);
00683 
00684         return buf;
00685 }
00686 
00687 static const char *operand_txt[] = {
00688         [TCF_EM_OPND_EQ] = "=",
00689         [TCF_EM_OPND_LT] = "<",
00690         [TCF_EM_OPND_GT] = ">",
00691 };
00692 
00693 char *rtnl_ematch_opnd2txt(uint8_t opnd, char *buf, size_t len)
00694 {
00695         snprintf(buf, len, "%s",
00696                 opnd <= ARRAY_SIZE(operand_txt) ? operand_txt[opnd] : "?");
00697 
00698         return buf;
00699 }
00700 
00701 /** @} */