]> granicus.if.org Git - libnl/commitdiff
Tons of ematch work
authorThomas Graf <tgraf@suug.ch>
Thu, 28 Oct 2010 22:20:42 +0000 (00:20 +0200)
committerThomas Graf <tgraf@suug.ch>
Thu, 28 Oct 2010 22:20:42 +0000 (00:20 +0200)
 - 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)

18 files changed:
include/netlink-types.h
include/netlink/cli/cls.h
include/netlink/route/cls/basic.h
include/netlink/route/cls/ematch.h
include/netlink/route/cls/ematch/cmp.h
include/netlink/route/cls/ematch/nbyte.h [new file with mode: 0644]
lib/Makefile.am
lib/cli/cls/basic.c
lib/route/cls.c
lib/route/cls/.gitignore [new file with mode: 0644]
lib/route/cls/basic.c
lib/route/cls/ematch.c
lib/route/cls/ematch/cmp.c
lib/route/cls/ematch/container.c
lib/route/cls/ematch/nbyte.c [new file with mode: 0644]
lib/route/cls/ematch_grammar.l [new file with mode: 0644]
lib/route/cls/ematch_syntax.y [new file with mode: 0644]
src/lib/cls.c

index 3abeff4d5962c581fde886f78602affb19e5a46f..3dc760fea25b9f297535000a4ef471127b1b0ed7 100644 (file)
@@ -520,12 +520,14 @@ struct rtnl_ematch
        uint16_t                e_id;
        uint16_t                e_kind;
        uint16_t                e_flags;
+       uint16_t                e_index;
+       size_t                  e_datalen;
 
        struct nl_list_head     e_childs;
        struct nl_list_head     e_list;
        struct rtnl_ematch_ops *e_ops;
 
-       char                    e_data[0];
+       void *                  e_data;
 };
 
 struct rtnl_ematch_tree
@@ -834,4 +836,10 @@ struct nfnl_queue_msg {
        uint32_t                queue_msg_verdict;
 };
 
+struct ematch_quoted {
+       char *  data;
+       size_t  len;
+       int     index;
+};
+
 #endif
index 85e5ffe90f1355d7725d26b166d1cad63c8f012b..cdcb17e1a92bc0bc28452bef956df42f5b8c4b6e 100644 (file)
@@ -29,6 +29,7 @@ extern struct nl_cache *      nl_cli_cls_alloc_cache(struct nl_sock *,
                                                       int, uint32_t);
 extern void                    nl_cli_cls_parse_kind(struct rtnl_cls *, char *);
 extern void                    nl_cli_cls_parse_proto(struct rtnl_cls *, char *);
+extern struct rtnl_ematch_tree *nl_cli_cls_parse_ematch(struct rtnl_cls *, char *);
 
 extern struct nl_cli_cls_module *nl_cli_cls_lookup(struct rtnl_cls_ops *);
 extern void                    nl_cli_cls_register(struct nl_cli_cls_module *);
index 7003124e78af4e5f52a38d1cdeff5b51a9745a6a..8b58c1efbce1e222eeb4ec83b398a17b00e98605 100644 (file)
@@ -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>
  */
 
 #ifndef NETLINK_BASIC_H_
 extern "C" {
 #endif
 
-extern struct rtnl_cls_ops *rtnl_basic_get_ops(void);
-extern int     rtnl_basic_set_classid(struct rtnl_cls *, uint32_t);
-extern uint32_t        rtnl_basic_get_classid(struct rtnl_cls *);
-extern int     rtnl_basic_set_ematch(struct rtnl_cls *,
-                                     struct rtnl_ematch_tree *);
-extern struct rtnl_ematch_tree *
-               rtnl_basic_get_ematch(struct rtnl_cls *);
+extern void                    rtnl_basic_set_target(struct rtnl_cls *, uint32_t);
+extern uint32_t                        rtnl_basic_get_target(struct rtnl_cls *);
+extern void                    rtnl_basic_set_ematch(struct rtnl_cls *,
+                                                     struct rtnl_ematch_tree *);
+extern struct rtnl_ematch_tree *rtnl_basic_get_ematch(struct rtnl_cls *);
 
 #ifdef __cplusplus
 }
index c4292bfab06916002c6de2e8b1ec339396c6858a..6cf59609354fb02caf7b895f649953ec88fe9398 100644 (file)
@@ -6,13 +6,14 @@
  *     License as published by the Free Software Foundation version 2.1
  *     of the License.
  *
- * Copyright (c) 2008 Thomas Graf <tgraf@suug.ch>
+ * Copyright (c) 2008-2010 Thomas Graf <tgraf@suug.ch>
  */
 
 #ifndef NETLINK_CLS_EMATCH_H_
 #define NETLINK_CLS_EMATCH_H_
 
 #include <netlink/netlink.h>
+#include <netlink/msg.h>
 #include <netlink/route/classifier.h>
 #include <linux/pkt_cls.h>
 
 extern "C" {
 #endif
 
+/* FIXME: Should be moved to the kernel header at some point */
+#define RTNL_EMATCH_PROGID     2
+
 struct rtnl_ematch;
 struct rtnl_ematch_tree;
 
+/**
+ * Extended Match Operations
+ */
 struct rtnl_ematch_ops
 {
-       int                             eo_kind;
-       const char *                    eo_name;
-       size_t                          eo_datalen;
-
-       int                           (*eo_parse)(struct rtnl_ematch *,
-                                                 void *, size_t);
-       void                          (*eo_dump)(struct rtnl_ematch *,
-                                                struct nl_dump_params *);
-       struct nl_list_head             eo_list;
+       int                     eo_kind;
+       const char *            eo_name;
+       size_t                  eo_minlen;
+       size_t                  eo_datalen;
+
+       int                   (*eo_parse)(struct rtnl_ematch *, void *, size_t);
+       void                  (*eo_dump)(struct rtnl_ematch *,
+                                        struct nl_dump_params *);
+       int                   (*eo_fill)(struct rtnl_ematch *, struct nl_msg *);
+       void                  (*eo_free)(struct rtnl_ematch *);
+       struct nl_list_head     eo_list;
 };
 
-extern int     rtnl_ematch_register(struct rtnl_ematch_ops *);
-extern int     rtnl_ematch_unregister(struct rtnl_ematch_ops *);
-
-extern struct rtnl_ematch_ops *
-               rtnl_ematch_lookup_ops(int);
-extern struct rtnl_ematch_ops *
-               rtnl_ematch_lookup_ops_name(const char *);
-
-extern struct rtnl_ematch *
-               rtnl_ematch_alloc(struct rtnl_ematch_ops *);
-extern void    rtnl_ematch_add_child(struct rtnl_ematch *,
-                                     struct rtnl_ematch *);
-extern void    rtnl_ematch_unlink(struct rtnl_ematch *);
-extern void    rtnl_ematch_free(struct rtnl_ematch *);
-
-extern void *  rtnl_ematch_data(struct rtnl_ematch *);
-extern void    rtnl_ematch_set_flags(struct rtnl_ematch *, uint16_t);
-extern void    rtnl_ematch_unset_flags(struct rtnl_ematch *, uint16_t);
-extern uint16_t        rtnl_ematch_get_flags(struct rtnl_ematch *);
-
-extern struct rtnl_ematch_tree *
-               rtnl_ematch_tree_alloc(uint16_t);
-extern void    rtnl_ematch_tree_free(struct rtnl_ematch_tree *);
-
-extern int     rtnl_ematch_parse(struct nlattr *, struct rtnl_ematch_tree **);
-extern void    rtnl_ematch_tree_add_tail(struct rtnl_ematch_tree *,
-                                         struct rtnl_ematch *);
-extern void    rtnl_ematch_tree_dump(struct rtnl_ematch_tree *,
-                                     struct nl_dump_params *);
+extern int                     rtnl_ematch_register(struct rtnl_ematch_ops *);
+extern struct rtnl_ematch_ops *        rtnl_ematch_lookup_ops(int);
+extern struct rtnl_ematch_ops *        rtnl_ematch_lookup_ops_by_name(const char *);
+
+extern struct rtnl_ematch *    rtnl_ematch_alloc(void);
+extern int                     rtnl_ematch_add_child(struct rtnl_ematch *,
+                                                     struct rtnl_ematch *);
+extern void                    rtnl_ematch_unlink(struct rtnl_ematch *);
+extern void                    rtnl_ematch_free(struct rtnl_ematch *);
+
+extern void *                  rtnl_ematch_data(struct rtnl_ematch *);
+extern void                    rtnl_ematch_set_flags(struct rtnl_ematch *,
+                                                     uint16_t);
+extern void                    rtnl_ematch_unset_flags(struct rtnl_ematch *,
+                                                       uint16_t);
+extern uint16_t                        rtnl_ematch_get_flags(struct rtnl_ematch *);
+extern int                     rtnl_ematch_set_ops(struct rtnl_ematch *,
+                                                   struct rtnl_ematch_ops *);
+extern int                     rtnl_ematch_set_kind(struct rtnl_ematch *,
+                                                    uint16_t);
+extern int                     rtnl_ematch_set_name(struct rtnl_ematch *,
+                                                    const char *);
+
+extern struct rtnl_ematch_tree *rtnl_ematch_tree_alloc(uint16_t);
+extern void                    rtnl_ematch_tree_free(struct rtnl_ematch_tree *);
+extern void                    rtnl_ematch_tree_add(struct rtnl_ematch_tree *,
+                                                    struct rtnl_ematch *);
+
+extern int                     rtnl_ematch_parse_attr(struct nlattr *,
+                                                      struct rtnl_ematch_tree **);
+extern int                     rtnl_ematch_fill_attr(struct nl_msg *, int,
+                                                     struct rtnl_ematch_tree *);
+extern void                    rtnl_ematch_tree_dump(struct rtnl_ematch_tree *,
+                                                     struct nl_dump_params *);
+
+
+extern int                     rtnl_ematch_parse_expr(const char *, char **,
+                                                      struct rtnl_ematch_tree **);
 
 #ifdef __cplusplus
 }
index b4ad03a52d029d790bd8d52144f163460816eb36..308113e08ebc39e48961606fcf289bc67e107755 100644 (file)
@@ -6,7 +6,7 @@
  *     License as published by the Free Software Foundation version 2.1
  *     of the License.
  *
- * Copyright (c) 2008 Thomas Graf <tgraf@suug.ch>
+ * Copyright (c) 2008-2010 Thomas Graf <tgraf@suug.ch>
  */
 
 #ifndef NETLINK_CLS_EMATCH_CMP_H_
@@ -14,6 +14,7 @@
 
 #include <netlink/netlink.h>
 #include <netlink/route/cls/ematch.h>
+#include <linux/tc_ematch/tc_em_cmp.h>
 
 #ifdef __cplusplus
 extern "C" {
diff --git a/include/netlink/route/cls/ematch/nbyte.h b/include/netlink/route/cls/ematch/nbyte.h
new file mode 100644 (file)
index 0000000..014c719
--- /dev/null
@@ -0,0 +1,36 @@
+/*
+ * netlink/route/cls/ematch/nbyte.h    N-Byte 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>
+ */
+
+#ifndef NETLINK_CLS_EMATCH_NBYTE_H_
+#define NETLINK_CLS_EMATCH_NBYTE_H_
+
+#include <netlink/netlink.h>
+#include <netlink/route/cls/ematch.h>
+#include <linux/tc_ematch/tc_em_nbyte.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+extern void            rtnl_ematch_nbyte_set_offset(struct rtnl_ematch *,
+                                                    uint8_t, uint16_t);
+extern uint16_t                rtnl_ematch_nbyte_get_offset(struct rtnl_ematch *);
+extern uint8_t         rtnl_ematch_nbyte_get_layer(struct rtnl_ematch *);
+extern void            rtnl_ematch_nbyte_set_pattern(struct rtnl_ematch *,
+                                                     uint8_t *, size_t);
+extern uint8_t *       rtnl_ematch_nbyte_get_pattern(struct rtnl_ematch *);
+extern size_t          rtnl_ematch_nbyte_get_len(struct rtnl_ematch *);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
index 52660ec38877c7e00cba6d69b262e4629a7434e1..5a520cbd048bb3ced50272953c38cafc12833dd3 100644 (file)
@@ -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 \
index fbe21738c670d003c1722ebdac61b3f03a66d226..9ec46ef07fe8598c8b1e5886f547c9024b713c4e 100644 (file)
@@ -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;
                }
        }
index 6d75e47378cd14e0a0845d0d2920685131006017..093b030ce71af2b493eb7bf29b945293dc9eae3e 100644 (file)
@@ -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 (file)
index 0000000..30f4521
--- /dev/null
@@ -0,0 +1,2 @@
+ematch_syntax.[ch]
+ematch_grammar.[ch]
index b4772d25e588368f67b06b2e2e642044aafbaeb3..835fd08c5dd69638029e490babffbca25dd42035 100644 (file)
 #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
 
 /** @} */
 
index cb77b16d53fcff375ad851f4641ed4c68a957faa..b0943bfab081d863b792d4197723cd3f82c72430 100644 (file)
@@ -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/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;
+}
+
 /** @} */
index ec25320fd97bb437853da2fec603565407f904c0..2a1070a3d5b9445bba000400f6f44ea0be4fc2a9 100644 (file)
@@ -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/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);
-}
-
 /** @} */
index 54d836fe585f85b82f6524114445d41548c6dedf..ddbdce0aa69d37c1b665c8bbee8f49fafb36afce 100644 (file)
@@ -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>
 #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 (file)
index 0000000..25a9866
--- /dev/null
@@ -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 (file)
index 0000000..e345181
--- /dev/null
@@ -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 (file)
index 0000000..b6d04c9
--- /dev/null
@@ -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; }
+       ;
index 95997f5fbacfef34731f7cf37303fc51117f4c17..7ada3d64b9a6e982403fea1921ed01b324fb1355 100644 (file)
@@ -17,6 +17,7 @@
 
 #include <netlink/cli/utils.h>
 #include <netlink/cli/cls.h>
+#include <netlink/route/cls/ematch.h>
 
 struct rtnl_cls *nl_cli_cls_alloc(void)
 {
@@ -57,6 +58,22 @@ void nl_cli_cls_parse_proto(struct rtnl_cls *cls, char *arg)
        rtnl_cls_set_protocol(cls, proto);
 }
 
+struct rtnl_ematch_tree *nl_cli_cls_parse_ematch(struct rtnl_cls *cls, char *arg)
+{
+       struct rtnl_ematch_tree *tree;
+       char *errstr = NULL;
+       int err;
+
+       if ((err = rtnl_ematch_parse_expr(arg, &errstr, &tree)) < 0)
+               nl_cli_fatal(err, "Unable to parse ematch expression: %s",
+                                 errstr);
+       
+       if (errstr)
+               free(errstr);
+
+       return tree;
+}
+
 static NL_LIST_HEAD(cls_modules);
 
 struct nl_cli_cls_module *__nl_cli_cls_lookup(struct rtnl_cls_ops *ops)