diff options
author | Thomas Graf <tgraf@suug.ch> | 2010-10-28 22:20:42 (GMT) |
---|---|---|
committer | Thomas Graf <tgraf@suug.ch> | 2010-10-28 22:20:42 (GMT) |
commit | d7a561a1372f819efc8cede30dc550d8e3afcc28 (patch) | |
tree | da9fefa62f5ffdca82fa374ebe0b207202719410 /lib | |
parent | e1eacd6b16b014eb42bcf6683ebe2334c3a35c68 (diff) | |
download | libnl-d7a561a1372f819efc8cede30dc550d8e3afcc28.zip libnl-d7a561a1372f819efc8cede30dc550d8e3afcc28.tar.gz libnl-d7a561a1372f819efc8cede30dc550d8e3afcc28.tar.bz2 |
Tons of ematch work
- Fixes a bunch of bugs related to ematches
- Adds support for the nbyte ematch
- Adds a bison/flex parser for ematch expressions, expressions
may look like this:
ip.length > 256 && pattern(ip6.src = 3ffe::/16)
documenation on syntax follows
- adds ematch support to the basic classifier (--ematch EXPR)
Diffstat (limited to 'lib')
-rw-r--r-- | lib/Makefile.am | 15 | ||||
-rw-r--r-- | lib/cli/cls/basic.c | 12 | ||||
-rw-r--r-- | lib/route/cls.c | 12 | ||||
-rw-r--r-- | lib/route/cls/.gitignore | 2 | ||||
-rw-r--r-- | lib/route/cls/basic.c | 62 | ||||
-rw-r--r-- | lib/route/cls/ematch.c | 378 | ||||
-rw-r--r-- | lib/route/cls/ematch/cmp.c | 95 | ||||
-rw-r--r-- | lib/route/cls/ematch/container.c | 18 | ||||
-rw-r--r-- | lib/route/cls/ematch/nbyte.c | 139 | ||||
-rw-r--r-- | lib/route/cls/ematch_grammar.l | 105 | ||||
-rw-r--r-- | lib/route/cls/ematch_syntax.y | 291 |
11 files changed, 950 insertions, 179 deletions
diff --git a/lib/Makefile.am b/lib/Makefile.am index 52660ec..5a520cb 100644 --- a/lib/Makefile.am +++ b/lib/Makefile.am @@ -23,7 +23,9 @@ libnl_nf_la_SOURCES = \ CLEANFILES = \ route/pktloc_grammar.c route/pktloc_grammar.h \ - route/pktloc_syntax.c route/pktloc_syntax.h + route/pktloc_syntax.c route/pktloc_syntax.h \ + route/cls/ematch_grammar.c route/cls/ematch_grammar.h \ + route/cls/ematch_syntax.c route/cls/ematch_syntax.h # Hack to avoid using ylwrap. It does not function correctly in combination # with --header-file= @@ -33,6 +35,12 @@ route/pktloc_grammar.c: route/pktloc_grammar.l route/pktloc_syntax.c: route/pktloc_syntax.y $(YACC) -d $(YFLAGS) -o $@ $^ +route/cls/ematch_grammar.c: route/cls/ematch_grammar.l + $(LEX) --header-file=route/cls/ematch_grammar.h $(LFLAGS) -o $@ $^ + +route/cls/ematch_syntax.c: route/cls/ematch_syntax.y + $(YACC) -d $(YFLAGS) -o $@ $^ + libnl_route_la_LIBADD = libnl.la libnl_route_la_SOURCES = \ route/addr.c route/class.c route/class_api.c route/class_obj.c \ @@ -43,6 +51,11 @@ libnl_route_la_SOURCES = \ \ route/cls/fw.c route/cls/police.c route/cls/u32.c route/cls/basic.c \ \ + route/cls/ematch_syntax.c route/cls/ematch_grammar.c \ + route/cls/ematch.c \ + route/cls/ematch/container.c route/cls/ematch/cmp.c \ + route/cls/ematch/nbyte.c \ + \ route/link/api.c route/link/vlan.c \ \ route/sch/blackhole.c route/sch/cbq.c route/sch/dsmark.c \ diff --git a/lib/cli/cls/basic.c b/lib/cli/cls/basic.c index fbe2173..9ec46ef 100644 --- a/lib/cli/cls/basic.c +++ b/lib/cli/cls/basic.c @@ -21,6 +21,7 @@ static void print_usage(void) "OPTIONS\n" " -h, --help Show this help text.\n" " -t, --target=ID Target class to send matching packets to\n" +" -e, --ematch=EXPR Ematch expression\n" "\n" "EXAMPLE" " # Create a \"catch-all\" classifier, attached to \"q_root\", classyfing\n" @@ -30,6 +31,7 @@ static void print_usage(void) static int parse_argv(struct rtnl_cls *cls, int argc, char **argv) { + struct rtnl_ematch_tree *tree; uint32_t target; int err; @@ -42,10 +44,11 @@ static int parse_argv(struct rtnl_cls *cls, int argc, char **argv) static struct option long_opts[] = { { "help", 0, 0, 'h' }, { "target", 1, 0, 't' }, + { "ematch", 1, 0, 'e' }, { 0, 0, 0, 0 } }; - c = getopt_long(argc, argv, "ht:", long_opts, &optidx); + c = getopt_long(argc, argv, "ht:e:", long_opts, &optidx); if (c == -1) break; @@ -59,7 +62,12 @@ static int parse_argv(struct rtnl_cls *cls, int argc, char **argv) nl_cli_fatal(err, "Unable to parse target \"%s\":", optarg, nl_geterror(err)); - rtnl_basic_set_classid(cls, target); + rtnl_basic_set_target(cls, target); + break; + + case 'e': + tree = nl_cli_cls_parse_ematch(cls, optarg); + rtnl_basic_set_ematch(cls, tree); break; } } diff --git a/lib/route/cls.c b/lib/route/cls.c index 6d75e47..093b030 100644 --- a/lib/route/cls.c +++ b/lib/route/cls.c @@ -98,19 +98,17 @@ static int cls_build(struct rtnl_cls *cls, int type, int flags, cops = rtnl_cls_lookup_ops(cls); if (cops && cops->co_get_opts) { - struct nl_msg *opts; + struct nlattr *opts; - if (!(opts = nlmsg_alloc())) { + if (!(opts = nla_nest_start(*result, TCA_OPTIONS))) { err = -NLE_NOMEM; goto errout; } - if (!(err = cops->co_get_opts(cls, opts))) - err = nla_put_nested(*result, TCA_OPTIONS, opts); - - nlmsg_free(opts); - if (err < 0) + if ((err = cops->co_get_opts(cls, *result)) < 0) goto errout; + + nla_nest_end(*result, opts); } return 0; diff --git a/lib/route/cls/.gitignore b/lib/route/cls/.gitignore new file mode 100644 index 0000000..30f4521 --- /dev/null +++ b/lib/route/cls/.gitignore @@ -0,0 +1,2 @@ +ematch_syntax.[ch] +ematch_grammar.[ch] diff --git a/lib/route/cls/basic.c b/lib/route/cls/basic.c index b4772d2..835fd08 100644 --- a/lib/route/cls/basic.c +++ b/lib/route/cls/basic.c @@ -28,23 +28,23 @@ #include <netlink/route/classifier.h> #include <netlink/route/classifier-modules.h> #include <netlink/route/cls/basic.h> +#include <netlink/route/cls/ematch.h> struct rtnl_basic { - uint32_t b_classid; + uint32_t b_target; + struct rtnl_ematch_tree * b_ematch; int b_mask; }; /** @cond SKIP */ -#define BASIC_ATTR_CLASSID 0x001 +#define BASIC_ATTR_TARGET 0x001 #define BASIC_ATTR_EMATCH 0x002 /** @endcond */ static struct nla_policy basic_policy[TCA_BASIC_MAX+1] = { [TCA_BASIC_CLASSID] = { .type = NLA_U32 }, [TCA_BASIC_EMATCHES] = { .type = NLA_NESTED }, - [TCA_BASIC_ACT] = { .type = NLA_NESTED }, - [TCA_BASIC_POLICE] = { .type = NLA_NESTED }, }; static int basic_clone(struct rtnl_cls *_dst, struct rtnl_cls *_src) @@ -54,11 +54,9 @@ static int basic_clone(struct rtnl_cls *_dst, struct rtnl_cls *_src) static void basic_free_data(struct rtnl_cls *cls) { -#if 0 struct rtnl_basic *basic = rtnl_cls_data(cls); rtnl_ematch_tree_free(basic->b_ematch); -#endif } static int basic_msg_parser(struct rtnl_cls *cls) @@ -72,27 +70,17 @@ static int basic_msg_parser(struct rtnl_cls *cls) return err; if (tb[TCA_BASIC_CLASSID]) { - basic->b_classid = nla_get_u32(tb[TCA_BASIC_CLASSID]); - basic->b_mask |= BASIC_ATTR_CLASSID; + basic->b_target = nla_get_u32(tb[TCA_BASIC_CLASSID]); + basic->b_mask |= BASIC_ATTR_TARGET; } if (tb[TCA_BASIC_EMATCHES]) { -#if 0 - if ((err = rtnl_ematch_parse(tb[TCA_BASIC_EMATCHES], + if ((err = rtnl_ematch_parse_attr(tb[TCA_BASIC_EMATCHES], &basic->b_ematch)) < 0) return err; if (basic->b_ematch) basic->b_mask |= BASIC_ATTR_EMATCH; -#endif - } - - if (tb[TCA_BASIC_ACT]) { - /* XXX */ - } - - if (tb[TCA_BASIC_POLICE]) { - /* XXX */ } return 0; @@ -103,29 +91,24 @@ static void basic_dump_line(struct rtnl_cls *cls, struct nl_dump_params *p) struct rtnl_basic *b = rtnl_cls_data(cls); char buf[32]; -#if 0 if (b->b_mask & BASIC_ATTR_EMATCH) nl_dump(p, " ematch"); else nl_dump(p, " match-all"); -#endif - if (b->b_mask & BASIC_ATTR_CLASSID) + if (b->b_mask & BASIC_ATTR_TARGET) nl_dump(p, " target %s", - rtnl_tc_handle2str(b->b_classid, buf, sizeof(buf))); + rtnl_tc_handle2str(b->b_target, buf, sizeof(buf))); } static void basic_dump_details(struct rtnl_cls *cls, struct nl_dump_params *p) { -#if 0 struct rtnl_basic *b = rtnl_cls_data(cls); if (b->b_mask & BASIC_ATTR_EMATCH) { - nl_dump(p, "\n"); nl_dump_line(p, " ematch "); rtnl_ematch_tree_dump(b->b_ematch, p); } else -#endif nl_dump(p, "no options.\n"); } @@ -133,12 +116,12 @@ static int basic_get_opts(struct rtnl_cls *cls, struct nl_msg *msg) { struct rtnl_basic *b = rtnl_cls_data(cls); - if (!(b->b_mask & BASIC_ATTR_CLASSID)) + if (!(b->b_mask & BASIC_ATTR_TARGET)) return -NLE_MISSING_ATTR; - NLA_PUT_U32(msg, TCA_BASIC_CLASSID, b->b_classid); + NLA_PUT_U32(msg, TCA_BASIC_CLASSID, b->b_target); - return 0; + return rtnl_ematch_fill_attr(msg, TCA_BASIC_EMATCHES, b->b_ematch); nla_put_failure: return -NLE_NOMEM; @@ -149,25 +132,22 @@ nla_put_failure: * @{ */ -int rtnl_basic_set_classid(struct rtnl_cls *cls, uint32_t classid) +void rtnl_basic_set_target(struct rtnl_cls *cls, uint32_t target) { struct rtnl_basic *b = rtnl_cls_data(cls); - b->b_classid = classid; - b->b_mask |= BASIC_ATTR_CLASSID; - - return 0; + b->b_target = target; + b->b_mask |= BASIC_ATTR_TARGET; } -uint32_t rtnl_basic_get_classid(struct rtnl_cls *cls) +uint32_t rtnl_basic_get_target(struct rtnl_cls *cls) { struct rtnl_basic *b = rtnl_cls_data(cls); - return b->b_classid; + return b->b_target; } -#if 0 -int rtnl_basic_set_ematch(struct rtnl_cls *cls, struct rtnl_ematch_tree *tree) +void rtnl_basic_set_ematch(struct rtnl_cls *cls, struct rtnl_ematch_tree *tree) { struct rtnl_basic *b = rtnl_cls_data(cls); @@ -180,16 +160,12 @@ int rtnl_basic_set_ematch(struct rtnl_cls *cls, struct rtnl_ematch_tree *tree) if (tree) b->b_mask |= BASIC_ATTR_EMATCH; - - return 0; } struct rtnl_ematch_tree *rtnl_basic_get_ematch(struct rtnl_cls *cls) { - struct rtnl_basic *b = rtnl_cls_data(cls); - return b->b_ematch; + return ((struct rtnl_basic *) rtnl_cls_data(cls))->b_ematch; } -#endif /** @} */ diff --git a/lib/route/cls/ematch.c b/lib/route/cls/ematch.c index cb77b16..b0943bf 100644 --- a/lib/route/cls/ematch.c +++ b/lib/route/cls/ematch.c @@ -6,7 +6,7 @@ * License as published by the Free Software Foundation version 2.1 * of the License. * - * Copyright (c) 2008-2009 Thomas Graf <tgraf@suug.ch> + * Copyright (c) 2008-2010 Thomas Graf <tgraf@suug.ch> */ /** @@ -22,9 +22,13 @@ #include <netlink/route/classifier.h> #include <netlink/route/classifier-modules.h> #include <netlink/route/cls/ematch.h> +#include <netlink/route/cls/ematch/cmp.h> + +#include "ematch_syntax.h" +#include "ematch_grammar.h" /** - * @name Module Registration + * @name Module API * @{ */ @@ -34,6 +38,9 @@ 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) @@ -41,35 +48,19 @@ 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; } /** - * Unregister ematch module - * @arg ops Module operations. - * - * @return 0 on success or a negative error code. - */ -int rtnl_ematch_unregister(struct rtnl_ematch_ops *ops) -{ - struct rtnl_ematch_ops *o; - - nl_list_for_each_entry(o, &ematch_ops_list, eo_list) { - if (ops->eo_kind == o->eo_kind) { - nl_list_del(&o->eo_list); - return 0; - } - } - - return -NLE_OBJ_NOTFOUND; -} - -/** - * Lookup ematch module by kind + * 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) @@ -87,9 +78,11 @@ struct rtnl_ematch_ops *rtnl_ematch_lookup_ops(int kind) * 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_name(const char *name) +struct rtnl_ematch_ops *rtnl_ematch_lookup_ops_by_name(const char *name) { struct rtnl_ematch_ops *ops; @@ -106,53 +99,122 @@ struct rtnl_ematch_ops *rtnl_ematch_lookup_ops_name(const char *name) * @name Match */ -struct rtnl_ematch *rtnl_ematch_alloc(struct rtnl_ematch_ops *ops) +/** + * 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; - size_t len = sizeof(*e) + (ops ? ops->eo_datalen : 0); - if (!(e = calloc(1, len))) + 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); - if (ops) { - e->e_ops = ops; - e->e_kind = ops->eo_kind; - } - return e; } /** * Add ematch to the end of the parent's list of children. - * @arg parent Parent ematch. - * @arg child Ematch to be added as new child of parent. + * @arg parent parent ematch object + * @arg child ematch object to be added to parent + * + * The parent must be a container ematch. */ -void rtnl_ematch_add_child(struct rtnl_ematch *parent, +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 it is linked to. - * @arg ematch Ematch to be unlinked. + * 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) { - if (!ematch) - return; - + 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; @@ -179,16 +241,22 @@ void *rtnl_ematch_data(struct rtnl_ematch *ematch) * @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; } @@ -203,6 +271,12 @@ static void free_ematch_list(struct nl_list_head *head) } } +/** + * 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) @@ -210,10 +284,17 @@ void rtnl_ematch_tree_free(struct rtnl_ematch_tree *tree) free_ematch_list(&tree->et_list); free(tree); + + NL_DBG(2, "Freed ematch tree %p\n", tree); } -void rtnl_ematch_tree_add_tail(struct rtnl_ematch_tree *tree, - struct rtnl_ematch *ematch) +/** + * 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); } @@ -256,7 +337,7 @@ static struct nla_policy tree_policy[TCA_EMATCH_TREE_MAX+1] = { * * @return 0 on success or a negative error code. */ -int rtnl_ematch_parse(struct nlattr *attr, struct rtnl_ematch_tree **result) +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; @@ -264,6 +345,8 @@ int rtnl_ematch_parse(struct nlattr *attr, struct rtnl_ematch_tree **result) 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; @@ -274,12 +357,22 @@ int rtnl_ematch_parse(struct nlattr *attr, struct rtnl_ematch_tree **result) thdr = nla_data(tb[TCA_EMATCH_TREE_HDR]); /* Ignore empty trees */ - if (thdr->nmatches == 0) + 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; @@ -299,11 +392,15 @@ int rtnl_ematch_parse(struct nlattr *attr, struct rtnl_ematch_tree **result) 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; @@ -313,13 +410,20 @@ int rtnl_ematch_parse(struct nlattr *attr, struct rtnl_ematch_tree **result) data = nla_data(a) + NLA_ALIGN(sizeof(*hdr)); len = nla_len(a) - NLA_ALIGN(sizeof(*hdr)); - ops = rtnl_ematch_lookup_ops(hdr->kind); - if (ops && ops->eo_datalen && len < ops->eo_datalen) { + 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(ops))) { + if (!(ematch = rtnl_ematch_alloc())) { err = -NLE_NOMEM; goto errout; } @@ -328,15 +432,23 @@ int rtnl_ematch_parse(struct nlattr *attr, struct rtnl_ematch_tree **result) ematch->e_kind = hdr->kind; ematch->e_flags = hdr->flags; - if (ops && (err = ops->eo_parse(ematch, data, len)) < 0) - goto errout; + 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; + } - if (hdr->kind == TCF_EM_CONTAINER && - container_ref(ematch) >= thdr->nmatches) { - 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; } @@ -367,7 +479,7 @@ static void dump_ematch_sequence(struct nl_list_head *head, nl_list_for_each_entry(match, head, e_list) { if (match->e_flags & TCF_EM_INVERT) - nl_dump(p, "NOT "); + nl_dump(p, "!"); if (match->e_kind == TCF_EM_CONTAINER) { nl_dump(p, "("); @@ -376,12 +488,10 @@ static void dump_ematch_sequence(struct nl_list_head *head, } else if (!match->e_ops) { nl_dump(p, "[unknown ematch %d]", match->e_kind); } else { - nl_dump(p, "%s(", match->e_ops->eo_name); - if (match->e_ops->eo_dump) match->e_ops->eo_dump(match, p); - - nl_dump(p, ")"); + else + nl_dump(p, "[data]"); } switch (match->e_flags & TCF_EM_REL_MASK) { @@ -405,6 +515,156 @@ void rtnl_ematch_tree_dump(struct rtnl_ematch_tree *tree, 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; + } + + if (scanner) + ematch_lex_destroy(scanner); + + *result = tree; + + return 0; + +errout: + if (scanner) + ematch_lex_destroy(scanner); + + rtnl_ematch_tree_free(tree); + + return err; +} + /** @} */ diff --git a/lib/route/cls/ematch/cmp.c b/lib/route/cls/ematch/cmp.c index ec25320..2a1070a 100644 --- a/lib/route/cls/ematch/cmp.c +++ b/lib/route/cls/ematch/cmp.c @@ -6,7 +6,7 @@ * License as published by the Free Software Foundation version 2.1 * of the License. * - * Copyright (c) 2008-2009 Thomas Graf <tgraf@suug.ch> + * Copyright (c) 2008-2010 Thomas Graf <tgraf@suug.ch> */ /** @@ -22,82 +22,64 @@ #include <netlink/route/cls/ematch.h> #include <linux/tc_ematch/tc_em_cmp.h> -void rtnl_ematch_cmp_set(struct rtnl_ematch *ematch, - struct tcf_em_cmp *cfg) +void rtnl_ematch_cmp_set(struct rtnl_ematch *e, struct tcf_em_cmp *cfg) { - memcpy(rtnl_ematch_data(ematch), cfg, sizeof(*cfg)); + memcpy(rtnl_ematch_data(e), cfg, sizeof(*cfg)); } -struct tcf_em_cmp *rtnl_ematch_cmp_get(struct rtnl_ematch *ematch) +struct tcf_em_cmp *rtnl_ematch_cmp_get(struct rtnl_ematch *e) { - return rtnl_ematch_data(ematch); + return rtnl_ematch_data(e); } -static const char *align_txt(struct tcf_em_cmp *cmp) +static int cmp_parse(struct rtnl_ematch *e, void *data, size_t len) { - switch (cmp->align) { - case TCF_EM_ALIGN_U8: - return "u8"; - case TCF_EM_ALIGN_U16: - return (cmp->flags & TCF_EM_CMP_TRANS) ? "h16" : "u16"; - case TCF_EM_ALIGN_U32: - return (cmp->flags & TCF_EM_CMP_TRANS) ? "h32" : "u32"; - default: - return (cmp->flags & TCF_EM_CMP_TRANS) ? "h?" : "u?"; - } -} + memcpy(rtnl_ematch_data(e), data, len); -static const char *layer_txt(struct tcf_em_cmp *cmp) -{ - switch (cmp->layer) { - case TCF_LAYER_LINK: - return "link"; - case TCF_LAYER_NETWORK: - return "network"; - case TCF_LAYER_TRANSPORT: - return "transport"; - default: - return "?"; - } + return 0; } -static const char *relation_txt(struct tcf_em_cmp *cmp) -{ - switch (cmp->opnd) { - case TCF_EM_OPND_EQ: - return "eq"; - case TCF_EM_OPND_LT: - return "lt"; - case TCF_EM_OPND_GT: - return "gt"; - default: - return "?"; - } -} +static const char *align_txt[] = { + [TCF_EM_ALIGN_U8] = "u8", + [TCF_EM_ALIGN_U16] = "u16", + [TCF_EM_ALIGN_U32] = "u32" +}; -static int cmp_parse(struct rtnl_ematch *m, void *data, size_t len) -{ - memcpy(rtnl_ematch_data(m), data, len); +static const char *layer_txt[] = { + [TCF_LAYER_LINK] = "eth", + [TCF_LAYER_NETWORK] = "ip", + [TCF_LAYER_TRANSPORT] = "tcp" +}; - return 0; -} +static const char *operand_txt[] = { + [TCF_EM_OPND_EQ] = "=", + [TCF_EM_OPND_LT] = "<", + [TCF_EM_OPND_GT] = ">", +}; -static void cmp_dump(struct rtnl_ematch *m, struct nl_dump_params *p) +static void cmp_dump(struct rtnl_ematch *e, struct nl_dump_params *p) { - struct tcf_em_cmp *cmp = rtnl_ematch_data(m); + struct tcf_em_cmp *cmp = rtnl_ematch_data(e); + + if (cmp->flags & TCF_EM_CMP_TRANS) + nl_dump(p, "ntoh%c(", (cmp->align == TCF_EM_ALIGN_U32) ? 'l' : 's'); - nl_dump(p, "%s at %s+%u ", - align_txt(cmp), layer_txt(cmp), cmp->off); + nl_dump(p, "%s at %s+%u", + align_txt[cmp->align], layer_txt[cmp->layer], cmp->off); if (cmp->mask) - nl_dump(p, "& 0x%x ", cmp->mask); + nl_dump(p, " & 0x%x", cmp->mask); - nl_dump(p, "%s %u", relation_txt(cmp), cmp->val); + if (cmp->flags & TCF_EM_CMP_TRANS) + nl_dump(p, ")"); + + nl_dump(p, " %s %u", operand_txt[cmp->opnd], cmp->val); } static struct rtnl_ematch_ops cmp_ops = { .eo_kind = TCF_EM_CMP, .eo_name = "cmp", + .eo_minlen = sizeof(struct tcf_em_cmp), .eo_datalen = sizeof(struct tcf_em_cmp), .eo_parse = cmp_parse, .eo_dump = cmp_dump, @@ -108,9 +90,4 @@ static void __init cmp_init(void) rtnl_ematch_register(&cmp_ops); } -static void __exit cmp_exit(void) -{ - rtnl_ematch_unregister(&cmp_ops); -} - /** @} */ diff --git a/lib/route/cls/ematch/container.c b/lib/route/cls/ematch/container.c index 54d836f..ddbdce0 100644 --- a/lib/route/cls/ematch/container.c +++ b/lib/route/cls/ematch/container.c @@ -6,7 +6,7 @@ * License as published by the Free Software Foundation version 2.1 * of the License. * - * Copyright (c) 2008-2009 Thomas Graf <tgraf@suug.ch> + * Copyright (c) 2008-2010 Thomas Graf <tgraf@suug.ch> */ #include <netlink-local.h> @@ -14,26 +14,28 @@ #include <netlink/netlink.h> #include <netlink/route/cls/ematch.h> -static int container_parse(struct rtnl_ematch *m, void *data, size_t len) +static int container_parse(struct rtnl_ematch *e, void *data, size_t len) { - memcpy(m->e_data, data, sizeof(uint32_t)); + memcpy(e->e_data, data, sizeof(uint32_t)); return 0; } +static int container_fill(struct rtnl_ematch *e, struct nl_msg *msg) +{ + return nlmsg_append(msg, e->e_data, sizeof(uint32_t), 0); +} + static struct rtnl_ematch_ops container_ops = { .eo_kind = TCF_EM_CONTAINER, .eo_name = "container", + .eo_minlen = sizeof(uint32_t), .eo_datalen = sizeof(uint32_t), .eo_parse = container_parse, + .eo_fill = container_fill, }; static void __init container_init(void) { rtnl_ematch_register(&container_ops); } - -static void __exit container_exit(void) -{ - rtnl_ematch_unregister(&container_ops); -} diff --git a/lib/route/cls/ematch/nbyte.c b/lib/route/cls/ematch/nbyte.c new file mode 100644 index 0000000..25a9866 --- /dev/null +++ b/lib/route/cls/ematch/nbyte.c @@ -0,0 +1,139 @@ +/* + * lib/route/cls/ematch/nbyte.c Nbyte comparison + * + * 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) 2010 Thomas Graf <tgraf@suug.ch> + */ + +/** + * @ingroup ematch + * @defgroup em_nbyte N-Byte Comparison + * + * @{ + */ + +#include <netlink-local.h> +#include <netlink-tc.h> +#include <netlink/netlink.h> +#include <netlink/route/cls/ematch.h> +#include <netlink/route/cls/ematch/nbyte.h> + +struct nbyte_data +{ + struct tcf_em_nbyte cfg; + uint8_t * pattern; +}; + +void rtnl_ematch_nbyte_set_offset(struct rtnl_ematch *e, uint8_t layer, + uint16_t offset) +{ + struct nbyte_data *n = rtnl_ematch_data(e); + n->cfg.off = offset; + n->cfg.layer = layer; +} + +uint16_t rtnl_ematch_nbyte_get_offset(struct rtnl_ematch *e) +{ + return ((struct nbyte_data *) rtnl_ematch_data(e))->cfg.off; +} + +uint8_t rtnl_ematch_nbyte_get_layer(struct rtnl_ematch *e) +{ + return ((struct nbyte_data *) rtnl_ematch_data(e))->cfg.layer; +} + +void rtnl_ematch_nbyte_set_pattern(struct rtnl_ematch *e, + uint8_t *pattern, size_t len) +{ + struct nbyte_data *n = rtnl_ematch_data(e); + + if (n->pattern) + free(n->pattern); + + n->pattern = pattern; + n->cfg.len = len; +} + +uint8_t *rtnl_ematch_nbyte_get_pattern(struct rtnl_ematch *e) +{ + return ((struct nbyte_data *) rtnl_ematch_data(e))->pattern; +} + +size_t rtnl_ematch_nbyte_get_len(struct rtnl_ematch *e) +{ + return ((struct nbyte_data *) rtnl_ematch_data(e))->cfg.len; +} + +static const char *layer_txt(struct tcf_em_nbyte *nbyte) +{ + switch (nbyte->layer) { + case TCF_LAYER_LINK: + return "link"; + case TCF_LAYER_NETWORK: + return "net"; + case TCF_LAYER_TRANSPORT: + return "trans"; + default: + return "?"; + } +} + +static int nbyte_parse(struct rtnl_ematch *e, void *data, size_t len) +{ + struct nbyte_data *n = rtnl_ematch_data(e); + size_t hdrlen = sizeof(struct tcf_em_nbyte); + size_t plen = len - hdrlen; + + memcpy(&n->cfg, data, hdrlen); + if (plen > 0) { + if (!(n->pattern = calloc(1, plen))) + return -NLE_NOMEM; + + memcpy(n->pattern, data + hdrlen, plen); + } + + return 0; +} + +static void nbyte_dump(struct rtnl_ematch *e, struct nl_dump_params *p) +{ + struct nbyte_data *n = rtnl_ematch_data(e); + int i; + + nl_dump(p, "pattern(%u:[", n->cfg.len); + + for (i = 0; i < n->cfg.len; i++) { + nl_dump(p, "%02x", n->pattern[i]); + if (i+1 < n->cfg.len) + nl_dump(p, " "); + } + + nl_dump(p, "] at %s+%u)", layer_txt(&n->cfg), n->cfg.off); +} + +static void nbyte_free(struct rtnl_ematch *e) +{ + struct nbyte_data *n = rtnl_ematch_data(e); + free(n->pattern); +} + +static struct rtnl_ematch_ops nbyte_ops = { + .eo_kind = TCF_EM_NBYTE, + .eo_name = "nbyte", + .eo_minlen = sizeof(struct tcf_em_nbyte), + .eo_datalen = sizeof(struct nbyte_data), + .eo_parse = nbyte_parse, + .eo_dump = nbyte_dump, + .eo_free = nbyte_free, +}; + +static void __init nbyte_init(void) +{ + rtnl_ematch_register(&nbyte_ops); +} + +/** @} */ diff --git a/lib/route/cls/ematch_grammar.l b/lib/route/cls/ematch_grammar.l new file mode 100644 index 0000000..e345181 --- /dev/null +++ b/lib/route/cls/ematch_grammar.l @@ -0,0 +1,105 @@ +/* + * lib/route/cls/ematch_grammar.l ematch expression grammar + * + * 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) 2010 Thomas Graf <tgraf@suug.ch> + */ + +%{ + #include <netlink-local.h> + #include <netlink-tc.h> + #include <netlink/netlink.h> + #include <netlink/route/cls/ematch.h> + #include <netlink/route/cls/ematch/cmp.h> + #include "ematch_syntax.h" +%} + +%option 8bit +%option reentrant +%option warn +%option noyywrap +%option noinput +%option nounput +%option bison-bridge +%option prefix="ematch_" + +%x QUOTE + +%% + +[ \t\r\n]+ + +\" { + NL_DBG(4, "Beginning of quote\n"); + yylval->q.len = 32; + if (!(yylval->q.data = calloc(1, yylval->q.len))) + return ERROR; + + yylval->q.index = 0; + BEGIN(QUOTE); + } + +<QUOTE>[^\\\n\"]+ { + memcpy(yylval->q.data + yylval->q.index, yytext, + strlen(yytext)); + yylval->q.index += strlen(yytext); + } + +<QUOTE>\" { + BEGIN(0); + return QUOTED; + } + + +[[:digit:]]+ | +0[xX][[:xdigit:]]+ { + yylval->i = strtoul(yytext, NULL, 0); + return NUMBER; + } + +eq | +"=" return KW_EQ; +gt | +">" return KW_GT; +lt | +"<" return KW_LT; + +[aA][nN][dD] | +"&&" { yylval->i = TCF_EM_REL_AND; return LOGIC; } +[oO][rR] | +"||" { yylval->i = TCF_EM_REL_OR; return LOGIC; } +[nN][oO][tT] | +"!" return NOT; + +[cC][mM][pP] { yylval->i = TCF_EM_CMP; return EMATCH_CMP; } +[pP][aA][tT][tT][eE][rR][nN] { yylval->i = TCF_EM_NBYTE; return EMATCH_NBYTE; } + +"(" return KW_OPEN; +")" return KW_CLOSE; +[mM][aA][sS][kK] return KW_MASK; +[aA][tT] return KW_AT; +"+" return KW_PLUS; + +[uU]8 { yylval->i = TCF_EM_ALIGN_U8; return ALIGN; } +[uU]16 { yylval->i = TCF_EM_ALIGN_U16; return ALIGN; } +[uU]32 { yylval->i = TCF_EM_ALIGN_U32; return ALIGN; } + +[lL][iI][nN][kK] | +[eE][tT][hH] { yylval->i = TCF_LAYER_LINK; return LAYER; } +[nN][eE][tT] | +[iI][pP]6 | +[iI][pP] { yylval->i = TCF_LAYER_NETWORK; return LAYER; } +[tT][rR][aA][nN][sS][pP][oO][rR][tT] | +[tT][cC][pP] { yylval->i = TCF_LAYER_TRANSPORT; return LAYER; } + +[^ \t\r\n+()=<>&|\"]+ { + yylval->s = strdup(yytext); + if (yylval->s == NULL) + return ERROR; + NL_DBG(4, "lex STR=%s\n", yylval->s); + return STR; + } diff --git a/lib/route/cls/ematch_syntax.y b/lib/route/cls/ematch_syntax.y new file mode 100644 index 0000000..b6d04c9 --- /dev/null +++ b/lib/route/cls/ematch_syntax.y @@ -0,0 +1,291 @@ +/* + * lib/route/cls/ematch_syntax.y ematch expression syntax + * + * 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) 2010 Thomas Graf <tgraf@suug.ch> + */ + +%{ +#include <netlink-local.h> +#include <netlink-tc.h> +#include <netlink/netlink.h> +#include <netlink/utils.h> +#include <netlink/route/pktloc.h> +#include <netlink/route/cls/ematch.h> +#include <netlink/route/cls/ematch/cmp.h> +#include <netlink/route/cls/ematch/nbyte.h> +%} + +%error-verbose +%define api.pure +%name-prefix "ematch_" + +%parse-param {void *scanner} +%parse-param {char **errp} +%parse-param {struct nl_list_head *root} +%lex-param {void *scanner} + +%union { + struct tcf_em_cmp cmp; + struct ematch_quoted q; + struct rtnl_ematch * e; + struct rtnl_pktloc * loc; + uint32_t i; + char * s; +} + +%{ +extern int ematch_lex(YYSTYPE *, void *); + +static void yyerror(void *scanner, char **errp, struct nl_list_head *root, const char *msg) +{ + if (msg) + asprintf(errp, "%s", msg); +} +%} + +%token <i> ERROR LOGIC NOT OPERAND NUMBER ALIGN LAYER +%token <i> KW_OPEN "(" +%token <i> KW_CLOSE ")" +%token <i> KW_PLUS "+" +%token <i> KW_MASK "mask" +%token <i> KW_AT "at" +%token <i> EMATCH_CMP "cmp" +%token <i> EMATCH_NBYTE "pattern" +%token <i> KW_EQ "=" +%token <i> KW_GT ">" +%token <i> KW_LT "<" + +%token <s> STR + +%token <q> QUOTED + +%type <i> mask align operand +%type <e> expr match ematch +%type <cmp> cmp_expr cmp_match +%type <loc> pktloc +%type <q> pattern + +%destructor { free($$); NL_DBG(2, "string destructor\n"); } <s> +%destructor { rtnl_pktloc_put($$); NL_DBG(2, "pktloc destructor\n"); } <loc> +%destructor { free($$.data); NL_DBG(2, "quoted destructor\n"); } <q> + +%start input + +%% + +input: + /* empty */ + | expr + { + nl_list_add_tail(root, &$1->e_list); + } + ; + +expr: + match + { + $$ = $1; + } + | match LOGIC expr + { + rtnl_ematch_set_flags($1, $2); + + /* make ematch new head */ + nl_list_add_tail(&$1->e_list, &$3->e_list); + + $$ = $1; + } + ; + +match: + NOT ematch + { + rtnl_ematch_set_flags($2, TCF_EM_INVERT); + $$ = $2; + } + | ematch + { + $$ = $1; + } + ; + +ematch: + /* CMP */ + cmp_match + { + struct rtnl_ematch *e; + + if (!(e = rtnl_ematch_alloc())) { + asprintf(errp, "Unable to allocate ematch object"); + YYABORT; + } + + if (rtnl_ematch_set_kind(e, TCF_EM_CMP) < 0) + BUG(); + + rtnl_ematch_cmp_set(e, &$1); + $$ = e; + } + | EMATCH_NBYTE "(" pktloc KW_EQ pattern ")" + { + struct rtnl_ematch *e; + + if (!(e = rtnl_ematch_alloc())) { + asprintf(errp, "Unable to allocate ematch object"); + YYABORT; + } + + if (rtnl_ematch_set_kind(e, TCF_EM_NBYTE) < 0) + BUG(); + + rtnl_ematch_nbyte_set_offset(e, $3->layer, $3->offset); + rtnl_ematch_nbyte_set_pattern(e, (uint8_t *) $5.data, $5.index); + + $$ = e; + } + /* CONTAINER */ + | "(" expr ")" + { + struct rtnl_ematch *e; + + if (!(e = rtnl_ematch_alloc())) { + asprintf(errp, "Unable to allocate ematch object"); + YYABORT; + } + + if (rtnl_ematch_set_kind(e, TCF_EM_CONTAINER) < 0) + BUG(); + + /* Make e->childs the list head of a the ematch sequence */ + nl_list_add_tail(&e->e_childs, &$2->e_list); + + $$ = e; + } + ; + +/* + * CMP match + * + * match := cmp(expr) | expr + * expr := pktloc (=|>|<) NUMBER + * pktloc := alias | definition + * + */ +cmp_match: + EMATCH_CMP "(" cmp_expr ")" + { $$ = $3; } + | cmp_expr + { $$ = $1; } + ; + +cmp_expr: + pktloc operand NUMBER + { + if ($1->align == TCF_EM_ALIGN_U16 || + $1->align == TCF_EM_ALIGN_U32) + $$.flags = TCF_EM_CMP_TRANS; + + memset(&$$, 0, sizeof($$)); + + $$.mask = $1->mask; + $$.off = $1->offset; + $$.align = $1->align; + $$.layer = $1->layer; + $$.opnd = $2; + $$.val = $3; + } + ; + +/* + * pattern + */ +pattern: + QUOTED + { + $$ = $1; + } + | STR + { + struct nl_addr *addr; + + if (nl_addr_parse($1, AF_UNSPEC, &addr) == 0) { + $$.len = nl_addr_get_len(addr); + + $$.index = min_t(int, $$.len, nl_addr_get_prefixlen(addr)/8); + + if (!($$.data = calloc(1, $$.len))) { + nl_addr_put(addr); + YYABORT; + } + + memcpy($$.data, nl_addr_get_binary_addr(addr), $$.len); + nl_addr_put(addr); + } else { + asprintf(errp, "invalid pattern \"%s\"", $1); + YYABORT; + } + } + ; + +/* + * packet location + */ + +pktloc: + STR + { + struct rtnl_pktloc *loc; + + if (rtnl_pktloc_lookup($1, &loc) < 0) { + asprintf(errp, "Packet location \"%s\" not found", $1); + YYABORT; + } + + $$ = loc; + } + | align "at" LAYER "+" NUMBER mask + { + struct rtnl_pktloc *loc; + + if (!(loc = rtnl_pktloc_alloc())) { + asprintf(errp, "Unable to allocate packet location object"); + YYABORT; + } + + loc->name = strdup("<USER-DEFINED>"); + loc->align = $1; + loc->layer = $3; + loc->offset = $5; + loc->mask = $6; + + $$ = loc; + } + ; + +align: + ALIGN + { $$ = $1; } + | NUMBER + { $$ = $1; } + ; + +mask: + /* empty */ + { $$ = 0; } + | "mask" NUMBER + { $$ = $2; } + ; + +operand: + KW_EQ + { $$ = TCF_EM_OPND_EQ; } + | KW_GT + { $$ = TCF_EM_OPND_GT; } + | KW_LT + { $$ = TCF_EM_OPND_LT; } + ; |