Tons of ematch work

- Fixes a bunch of bugs related to ematches
 - Adds support for the nbyte ematch
 - Adds a bison/flex parser for ematch expressions, expressions
   may look like this:
      ip.length > 256 && pattern(ip6.src = 3ffe::/16)
   documenation on syntax follows
 - adds ematch support to the basic classifier (--ematch EXPR)
This commit is contained in:
Thomas Graf 2010-10-29 00:20:42 +02:00
parent e1eacd6b16
commit d7a561a137
18 changed files with 1076 additions and 226 deletions

View 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

View 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 *);

View 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_
@ -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
}

View 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>
@ -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_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 *);
struct nl_list_head eo_list;
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 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_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(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 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 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 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 void rtnl_ematch_tree_add(struct rtnl_ematch_tree *,
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_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(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_parse_expr(const char *, char **,
struct rtnl_ematch_tree **);
#ifdef __cplusplus
}

View 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" {

View file

@ -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

View 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 \

View 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;
}
}

View 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;

2
lib/route/cls/.gitignore vendored Normal file
View file

@ -0,0 +1,2 @@
ematch_syntax.[ch]
ematch_grammar.[ch]

View file

@ -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
/** @} */

View 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>
*/
/**
@ -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;
}
/** @} */

View 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>
*/
/**
@ -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?";
}
}
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 "?";
}
}
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 int cmp_parse(struct rtnl_ematch *m, void *data, size_t len)
{
memcpy(rtnl_ematch_data(m), data, len);
memcpy(rtnl_ematch_data(e), data, len);
return 0;
}
static void cmp_dump(struct rtnl_ematch *m, struct nl_dump_params *p)
{
struct tcf_em_cmp *cmp = rtnl_ematch_data(m);
static const char *align_txt[] = {
[TCF_EM_ALIGN_U8] = "u8",
[TCF_EM_ALIGN_U16] = "u16",
[TCF_EM_ALIGN_U32] = "u32"
};
nl_dump(p, "%s at %s+%u ",
align_txt(cmp), layer_txt(cmp), cmp->off);
static const char *layer_txt[] = {
[TCF_LAYER_LINK] = "eth",
[TCF_LAYER_NETWORK] = "ip",
[TCF_LAYER_TRANSPORT] = "tcp"
};
static const char *operand_txt[] = {
[TCF_EM_OPND_EQ] = "=",
[TCF_EM_OPND_LT] = "<",
[TCF_EM_OPND_GT] = ">",
};
static void cmp_dump(struct rtnl_ematch *e, struct nl_dump_params *p)
{
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->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);
}
/** @} */

View 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>
@ -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);
}

View file

@ -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);
}
/** @} */

View file

@ -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;
}

View file

@ -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; }
;

View 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)