summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--include/netlink-types.h10
-rw-r--r--include/netlink/cli/cls.h1
-rw-r--r--include/netlink/route/cls/basic.h14
-rw-r--r--include/netlink/route/cls/ematch.h96
-rw-r--r--include/netlink/route/cls/ematch/cmp.h3
-rw-r--r--include/netlink/route/cls/ematch/nbyte.h36
-rw-r--r--lib/Makefile.am15
-rw-r--r--lib/cli/cls/basic.c12
-rw-r--r--lib/route/cls.c12
-rw-r--r--lib/route/cls/.gitignore2
-rw-r--r--lib/route/cls/basic.c62
-rw-r--r--lib/route/cls/ematch.c378
-rw-r--r--lib/route/cls/ematch/cmp.c95
-rw-r--r--lib/route/cls/ematch/container.c18
-rw-r--r--lib/route/cls/ematch/nbyte.c139
-rw-r--r--lib/route/cls/ematch_grammar.l105
-rw-r--r--lib/route/cls/ematch_syntax.y291
-rw-r--r--src/lib/cls.c17
18 files changed, 1078 insertions, 228 deletions
diff --git a/include/netlink-types.h b/include/netlink-types.h
index 3abeff4..3dc760f 100644
--- a/include/netlink-types.h
+++ b/include/netlink-types.h
@@ -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
diff --git a/include/netlink/cli/cls.h b/include/netlink/cli/cls.h
index 85e5ffe..cdcb17e 100644
--- a/include/netlink/cli/cls.h
+++ b/include/netlink/cli/cls.h
@@ -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 *);
diff --git a/include/netlink/route/cls/basic.h b/include/netlink/route/cls/basic.h
index 7003124..8b58c1e 100644
--- a/include/netlink/route/cls/basic.h
+++ b/include/netlink/route/cls/basic.h
@@ -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_
@@ -18,13 +18,11 @@
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
}
diff --git a/include/netlink/route/cls/ematch.h b/include/netlink/route/cls/ematch.h
index c4292bf..6cf5960 100644
--- a/include/netlink/route/cls/ematch.h
+++ b/include/netlink/route/cls/ematch.h
@@ -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>
@@ -20,51 +21,68 @@
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
}
diff --git a/include/netlink/route/cls/ematch/cmp.h b/include/netlink/route/cls/ematch/cmp.h
index b4ad03a..308113e 100644
--- a/include/netlink/route/cls/ematch/cmp.h
+++ b/include/netlink/route/cls/ematch/cmp.h
@@ -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
index 0000000..014c719
--- /dev/null
+++ b/include/netlink/route/cls/ematch/nbyte.h
@@ -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
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; }
+ ;
diff --git a/src/lib/cls.c b/src/lib/cls.c
index 95997f5..7ada3d6 100644
--- a/src/lib/cls.c
+++ b/src/lib/cls.c
@@ -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)