123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700 |
- /*
- * lib/route/cls/ematch.c Extended Matches
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation version 2.1
- * of the License.
- *
- * Copyright (c) 2008-2013 Thomas Graf <tgraf@suug.ch>
- */
- /**
- * @ingroup cls
- * @defgroup ematch Extended Match
- *
- * @{
- */
- #include <netlink-private/netlink.h>
- #include <netlink-private/tc.h>
- #include <netlink/netlink.h>
- #include <netlink/route/classifier.h>
- #include <netlink/route/cls/ematch.h>
- #include <netlink/route/cls/ematch/cmp.h>
- #include "ematch_syntax.h"
- #include "ematch_grammar.h"
- /**
- * @name Module API
- * @{
- */
- static NL_LIST_HEAD(ematch_ops_list);
- /**
- * Register ematch module
- * @arg ops Module operations.
- *
- * This function must be called by each ematch module at initialization
- * time. It registers the calling module as available module.
- *
- * @return 0 on success or a negative error code.
- */
- int rtnl_ematch_register(struct rtnl_ematch_ops *ops)
- {
- if (rtnl_ematch_lookup_ops(ops->eo_kind))
- return -NLE_EXIST;
- NL_DBG(1, "ematch module \"%s\" registered\n", ops->eo_name);
- nl_list_add_tail(&ops->eo_list, &ematch_ops_list);
- return 0;
- }
- /**
- * Lookup ematch module by identification number.
- * @arg kind Module kind.
- *
- * Searches the list of registered ematch modules for match and returns it.
- *
- * @return Module operations or NULL if not found.
- */
- struct rtnl_ematch_ops *rtnl_ematch_lookup_ops(int kind)
- {
- struct rtnl_ematch_ops *ops;
- nl_list_for_each_entry(ops, &ematch_ops_list, eo_list)
- if (ops->eo_kind == kind)
- return ops;
- return NULL;
- }
- /**
- * Lookup ematch module by name
- * @arg name Name of ematch module.
- *
- * Searches the list of registered ematch modules for a match and returns it.
- *
- * @return Module operations or NULL if not fuond.
- */
- struct rtnl_ematch_ops *rtnl_ematch_lookup_ops_by_name(const char *name)
- {
- struct rtnl_ematch_ops *ops;
- nl_list_for_each_entry(ops, &ematch_ops_list, eo_list)
- if (!strcasecmp(ops->eo_name, name))
- return ops;
- return NULL;
- }
- /** @} */
- /**
- * @name Match
- */
- /**
- * Allocate ematch object.
- *
- * Allocates and initializes an ematch object.
- *
- * @return New ematch object or NULL.
- */
- struct rtnl_ematch *rtnl_ematch_alloc(void)
- {
- struct rtnl_ematch *e;
- if (!(e = calloc(1, sizeof(*e))))
- return NULL;
- NL_DBG(2, "allocated ematch %p\n", e);
- NL_INIT_LIST_HEAD(&e->e_list);
- NL_INIT_LIST_HEAD(&e->e_childs);
- return e;
- }
- /**
- * Add ematch to the end of the parent's list of children.
- * @arg parent parent ematch object
- * @arg child ematch object to be added to parent
- *
- * The parent must be a container ematch.
- */
- int rtnl_ematch_add_child(struct rtnl_ematch *parent,
- struct rtnl_ematch *child)
- {
- if (parent->e_kind != TCF_EM_CONTAINER)
- return -NLE_OPNOTSUPP;
- NL_DBG(2, "added ematch %p \"%s\" to container %p\n",
- child, child->e_ops->eo_name, parent);
- nl_list_add_tail(&child->e_list, &parent->e_childs);
- return 0;
- }
- /**
- * Remove ematch from the list of ematches it is linked to.
- * @arg ematch ematch object
- */
- void rtnl_ematch_unlink(struct rtnl_ematch *ematch)
- {
- NL_DBG(2, "unlinked ematch %p from any lists\n", ematch);
- if (!nl_list_empty(&ematch->e_childs))
- NL_DBG(1, "warning: ematch %p with childs was unlinked\n",
- ematch);
- nl_list_del(&ematch->e_list);
- nl_init_list_head(&ematch->e_list);
- }
- void rtnl_ematch_free(struct rtnl_ematch *ematch)
- {
- NL_DBG(2, "freed ematch %p\n", ematch);
- rtnl_ematch_unlink(ematch);
- free(ematch->e_data);
- free(ematch);
- }
- int rtnl_ematch_set_ops(struct rtnl_ematch *ematch, struct rtnl_ematch_ops *ops)
- {
- if (ematch->e_ops)
- return -NLE_EXIST;
- ematch->e_ops = ops;
- ematch->e_kind = ops->eo_kind;
- if (ops->eo_datalen) {
- ematch->e_data = calloc(1, ops->eo_datalen);
- if (!ematch->e_data)
- return -NLE_NOMEM;
- ematch->e_datalen = ops->eo_datalen;
- }
- return 0;
- }
- int rtnl_ematch_set_kind(struct rtnl_ematch *ematch, uint16_t kind)
- {
- struct rtnl_ematch_ops *ops;
- if (ematch->e_kind)
- return -NLE_EXIST;
- ematch->e_kind = kind;
- if ((ops = rtnl_ematch_lookup_ops(kind)))
- rtnl_ematch_set_ops(ematch, ops);
- return 0;
- }
- int rtnl_ematch_set_name(struct rtnl_ematch *ematch, const char *name)
- {
- struct rtnl_ematch_ops *ops;
- if (ematch->e_kind)
- return -NLE_EXIST;
- if (!(ops = rtnl_ematch_lookup_ops_by_name(name)))
- return -NLE_OPNOTSUPP;
- rtnl_ematch_set_ops(ematch, ops);
- return 0;
- }
- void rtnl_ematch_set_flags(struct rtnl_ematch *ematch, uint16_t flags)
- {
- ematch->e_flags |= flags;
- }
- void rtnl_ematch_unset_flags(struct rtnl_ematch *ematch, uint16_t flags)
- {
- ematch->e_flags &= ~flags;
- }
- uint16_t rtnl_ematch_get_flags(struct rtnl_ematch *ematch)
- {
- return ematch->e_flags;
- }
- void *rtnl_ematch_data(struct rtnl_ematch *ematch)
- {
- return ematch->e_data;
- }
- /** @} */
- /**
- * @name Tree
- */
- /**
- * Allocate ematch tree object
- * @arg progid program id
- */
- struct rtnl_ematch_tree *rtnl_ematch_tree_alloc(uint16_t progid)
- {
- struct rtnl_ematch_tree *tree;
- if (!(tree = calloc(1, sizeof(*tree))))
- return NULL;
-
- NL_INIT_LIST_HEAD(&tree->et_list);
- tree->et_progid = progid;
- NL_DBG(2, "allocated new ematch tree %p, progid=%u\n", tree, progid);
- return tree;
- }
- static void free_ematch_list(struct nl_list_head *head)
- {
- struct rtnl_ematch *pos, *next;
- nl_list_for_each_entry_safe(pos, next, head, e_list) {
- if (!nl_list_empty(&pos->e_childs))
- free_ematch_list(&pos->e_childs);
- rtnl_ematch_free(pos);
- }
- }
- /**
- * Free ematch tree object
- * @arg tree ematch tree object
- *
- * This function frees the ematch tree and all ematches attached to it.
- */
- void rtnl_ematch_tree_free(struct rtnl_ematch_tree *tree)
- {
- if (!tree)
- return;
- free_ematch_list(&tree->et_list);
- NL_DBG(2, "Freed ematch tree %p\n", tree);
- free(tree);
- }
- /**
- * Add ematch object to the end of the ematch tree
- * @arg tree ematch tree object
- * @arg ematch ematch object to add
- */
- void rtnl_ematch_tree_add(struct rtnl_ematch_tree *tree,
- struct rtnl_ematch *ematch)
- {
- nl_list_add_tail(&ematch->e_list, &tree->et_list);
- }
- static inline uint32_t container_ref(struct rtnl_ematch *ematch)
- {
- return *((uint32_t *) rtnl_ematch_data(ematch));
- }
- static int link_tree(struct rtnl_ematch *index[], int nmatches, int pos,
- struct nl_list_head *root)
- {
- struct rtnl_ematch *ematch;
- int i;
- for (i = pos; i < nmatches; i++) {
- ematch = index[i];
- nl_list_add_tail(&ematch->e_list, root);
- if (ematch->e_kind == TCF_EM_CONTAINER)
- link_tree(index, nmatches, container_ref(ematch),
- &ematch->e_childs);
- if (!(ematch->e_flags & TCF_EM_REL_MASK))
- return 0;
- }
- /* Last entry in chain can't possibly have no relation */
- return -NLE_INVAL;
- }
- static struct nla_policy tree_policy[TCA_EMATCH_TREE_MAX+1] = {
- [TCA_EMATCH_TREE_HDR] = { .minlen=sizeof(struct tcf_ematch_tree_hdr) },
- [TCA_EMATCH_TREE_LIST] = { .type = NLA_NESTED },
- };
- /**
- * Parse ematch netlink attributes
- *
- * @return 0 on success or a negative error code.
- */
- int rtnl_ematch_parse_attr(struct nlattr *attr, struct rtnl_ematch_tree **result)
- {
- struct nlattr *a, *tb[TCA_EMATCH_TREE_MAX+1];
- struct tcf_ematch_tree_hdr *thdr;
- struct rtnl_ematch_tree *tree;
- struct rtnl_ematch **index;
- int nmatches = 0, err, remaining;
- NL_DBG(2, "Parsing attribute %p as ematch tree\n", attr);
- err = nla_parse_nested(tb, TCA_EMATCH_TREE_MAX, attr, tree_policy);
- if (err < 0)
- return err;
- if (!tb[TCA_EMATCH_TREE_HDR])
- return -NLE_MISSING_ATTR;
- thdr = nla_data(tb[TCA_EMATCH_TREE_HDR]);
- /* Ignore empty trees */
- if (thdr->nmatches == 0) {
- NL_DBG(2, "Ignoring empty ematch configuration\n");
- return 0;
- }
- if (!tb[TCA_EMATCH_TREE_LIST])
- return -NLE_MISSING_ATTR;
- NL_DBG(2, "ematch tree found with nmatches=%u, progid=%u\n",
- thdr->nmatches, thdr->progid);
- /*
- * Do some basic sanity checking since we will allocate
- * index[thdr->nmatches]. Calculate how many ematch headers fit into
- * the provided data and make sure nmatches does not exceed it.
- */
- if (thdr->nmatches > (nla_len(tb[TCA_EMATCH_TREE_LIST]) /
- nla_total_size(sizeof(struct tcf_ematch_hdr))))
- return -NLE_INVAL;
- if (!(index = calloc(thdr->nmatches, sizeof(struct rtnl_ematch *))))
- return -NLE_NOMEM;
- if (!(tree = rtnl_ematch_tree_alloc(thdr->progid))) {
- err = -NLE_NOMEM;
- goto errout;
- }
- nla_for_each_nested(a, tb[TCA_EMATCH_TREE_LIST], remaining) {
- struct rtnl_ematch_ops *ops;
- struct tcf_ematch_hdr *hdr;
- struct rtnl_ematch *ematch;
- void *data;
- size_t len;
- NL_DBG(3, "parsing ematch attribute %d, len=%u\n",
- nmatches+1, nla_len(a));
- if (nla_len(a) < sizeof(*hdr)) {
- err = -NLE_INVAL;
- goto errout;
- }
- /* Quit as soon as we've parsed more matches than expected */
- if (nmatches >= thdr->nmatches) {
- err = -NLE_RANGE;
- goto errout;
- }
- hdr = nla_data(a);
- data = nla_data(a) + NLA_ALIGN(sizeof(*hdr));
- len = nla_len(a) - NLA_ALIGN(sizeof(*hdr));
- NL_DBG(3, "ematch attribute matchid=%u, kind=%u, flags=%u\n",
- hdr->matchid, hdr->kind, hdr->flags);
- /*
- * Container matches contain a reference to another sequence
- * of matches. Ensure that the reference is within boundries.
- */
- if (hdr->kind == TCF_EM_CONTAINER &&
- *((uint32_t *) data) >= thdr->nmatches) {
- err = -NLE_INVAL;
- goto errout;
- }
- if (!(ematch = rtnl_ematch_alloc())) {
- err = -NLE_NOMEM;
- goto errout;
- }
- ematch->e_id = hdr->matchid;
- ematch->e_kind = hdr->kind;
- ematch->e_flags = hdr->flags;
- if ((ops = rtnl_ematch_lookup_ops(hdr->kind))) {
- if (ops->eo_minlen && len < ops->eo_minlen) {
- rtnl_ematch_free(ematch);
- err = -NLE_INVAL;
- goto errout;
- }
- rtnl_ematch_set_ops(ematch, ops);
- if (ops->eo_parse &&
- (err = ops->eo_parse(ematch, data, len)) < 0) {
- rtnl_ematch_free(ematch);
- goto errout;
- }
- }
- NL_DBG(3, "index[%d] = %p\n", nmatches, ematch);
- index[nmatches++] = ematch;
- }
- if (nmatches != thdr->nmatches) {
- err = -NLE_INVAL;
- goto errout;
- }
- err = link_tree(index, nmatches, 0, &tree->et_list);
- if (err < 0)
- goto errout;
- free(index);
- *result = tree;
- return 0;
- errout:
- rtnl_ematch_tree_free(tree);
- free(index);
- return err;
- }
- static void dump_ematch_sequence(struct nl_list_head *head,
- struct nl_dump_params *p)
- {
- struct rtnl_ematch *match;
- nl_list_for_each_entry(match, head, e_list) {
- if (match->e_flags & TCF_EM_INVERT)
- nl_dump(p, "!");
- if (match->e_kind == TCF_EM_CONTAINER) {
- nl_dump(p, "(");
- dump_ematch_sequence(&match->e_childs, p);
- nl_dump(p, ")");
- } else if (!match->e_ops) {
- nl_dump(p, "[unknown ematch %d]", match->e_kind);
- } else {
- if (match->e_ops->eo_dump)
- match->e_ops->eo_dump(match, p);
- else
- nl_dump(p, "[data]");
- }
- switch (match->e_flags & TCF_EM_REL_MASK) {
- case TCF_EM_REL_AND:
- nl_dump(p, " AND ");
- break;
- case TCF_EM_REL_OR:
- nl_dump(p, " OR ");
- break;
- default:
- /* end of first level ematch sequence */
- return;
- }
- }
- }
- void rtnl_ematch_tree_dump(struct rtnl_ematch_tree *tree,
- struct nl_dump_params *p)
- {
- if (!tree)
- BUG();
- dump_ematch_sequence(&tree->et_list, p);
- nl_dump(p, "\n");
- }
- static int update_container_index(struct nl_list_head *list, int *index)
- {
- struct rtnl_ematch *e;
- nl_list_for_each_entry(e, list, e_list)
- e->e_index = (*index)++;
- nl_list_for_each_entry(e, list, e_list) {
- if (e->e_kind == TCF_EM_CONTAINER) {
- int err;
- if (nl_list_empty(&e->e_childs))
- return -NLE_OBJ_NOTFOUND;
- *((uint32_t *) e->e_data) = *index;
- err = update_container_index(&e->e_childs, index);
- if (err < 0)
- return err;
- }
- }
- return 0;
- }
- static int fill_ematch_sequence(struct nl_msg *msg, struct nl_list_head *list)
- {
- struct rtnl_ematch *e;
- nl_list_for_each_entry(e, list, e_list) {
- struct tcf_ematch_hdr match = {
- .matchid = e->e_id,
- .kind = e->e_kind,
- .flags = e->e_flags,
- };
- struct nlattr *attr;
- int err = 0;
- if (!(attr = nla_nest_start(msg, e->e_index + 1)))
- return -NLE_NOMEM;
- if (nlmsg_append(msg, &match, sizeof(match), 0) < 0)
- return -NLE_NOMEM;
- if (e->e_ops->eo_fill)
- err = e->e_ops->eo_fill(e, msg);
- else if (e->e_flags & TCF_EM_SIMPLE)
- err = nlmsg_append(msg, e->e_data, 4, 0);
- else if (e->e_datalen > 0)
- err = nlmsg_append(msg, e->e_data, e->e_datalen, 0);
- NL_DBG(3, "msg %p: added ematch [%d] id=%d kind=%d flags=%d\n",
- msg, e->e_index, match.matchid, match.kind, match.flags);
- if (err < 0)
- return -NLE_NOMEM;
- nla_nest_end(msg, attr);
- }
- nl_list_for_each_entry(e, list, e_list) {
- if (e->e_kind == TCF_EM_CONTAINER &&
- fill_ematch_sequence(msg, &e->e_childs) < 0)
- return -NLE_NOMEM;
- }
- return 0;
- }
- int rtnl_ematch_fill_attr(struct nl_msg *msg, int attrid,
- struct rtnl_ematch_tree *tree)
- {
- struct tcf_ematch_tree_hdr thdr = {
- .progid = tree->et_progid,
- };
- struct nlattr *list, *topattr;
- int err, index = 0;
- /* Assign index number to each ematch to allow for references
- * to be made while constructing the sequence of matches. */
- err = update_container_index(&tree->et_list, &index);
- if (err < 0)
- return err;
- if (!(topattr = nla_nest_start(msg, attrid)))
- goto nla_put_failure;
- thdr.nmatches = index;
- NLA_PUT(msg, TCA_EMATCH_TREE_HDR, sizeof(thdr), &thdr);
- if (!(list = nla_nest_start(msg, TCA_EMATCH_TREE_LIST)))
- goto nla_put_failure;
- if (fill_ematch_sequence(msg, &tree->et_list) < 0)
- goto nla_put_failure;
- nla_nest_end(msg, list);
- nla_nest_end(msg, topattr);
- return 0;
- nla_put_failure:
- return -NLE_NOMEM;
- }
- /** @} */
- extern int ematch_parse(void *, char **, struct nl_list_head *);
- int rtnl_ematch_parse_expr(const char *expr, char **errp,
- struct rtnl_ematch_tree **result)
- {
- struct rtnl_ematch_tree *tree;
- YY_BUFFER_STATE buf = NULL;
- yyscan_t scanner = NULL;
- int err;
- NL_DBG(2, "Parsing ematch expression \"%s\"\n", expr);
- if (!(tree = rtnl_ematch_tree_alloc(RTNL_EMATCH_PROGID)))
- return -NLE_FAILURE;
- if ((err = ematch_lex_init(&scanner)) < 0) {
- err = -NLE_FAILURE;
- goto errout;
- }
- buf = ematch__scan_string(expr, scanner);
- if ((err = ematch_parse(scanner, errp, &tree->et_list)) != 0) {
- ematch__delete_buffer(buf, scanner);
- err = -NLE_PARSE_ERR;
- goto errout;
- }
- ematch_lex_destroy(scanner);
- *result = tree;
- return 0;
- errout:
- if (scanner)
- ematch_lex_destroy(scanner);
- rtnl_ematch_tree_free(tree);
- return err;
- }
- static const char *layer_txt[] = {
- [TCF_LAYER_LINK] = "eth",
- [TCF_LAYER_NETWORK] = "ip",
- [TCF_LAYER_TRANSPORT] = "tcp",
- };
- char *rtnl_ematch_offset2txt(uint8_t layer, uint16_t offset, char *buf, size_t len)
- {
- snprintf(buf, len, "%s+%u",
- (layer <= TCF_LAYER_MAX) ? layer_txt[layer] : "?",
- offset);
- return buf;
- }
- static const char *operand_txt[] = {
- [TCF_EM_OPND_EQ] = "=",
- [TCF_EM_OPND_LT] = "<",
- [TCF_EM_OPND_GT] = ">",
- };
- char *rtnl_ematch_opnd2txt(uint8_t opnd, char *buf, size_t len)
- {
- snprintf(buf, len, "%s",
- opnd < ARRAY_SIZE(operand_txt) ? operand_txt[opnd] : "?");
- return buf;
- }
- /** @} */
|