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:
parent
e1eacd6b16
commit
d7a561a137
18 changed files with 1076 additions and 226 deletions
|
@ -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
|
||||
|
|
|
@ -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 *);
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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" {
|
||||
|
|
36
include/netlink/route/cls/ematch/nbyte.h
Normal file
36
include/netlink/route/cls/ematch/nbyte.h
Normal 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
|
|
@ -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 \
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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
2
lib/route/cls/.gitignore
vendored
Normal file
|
@ -0,0 +1,2 @@
|
|||
ematch_syntax.[ch]
|
||||
ematch_grammar.[ch]
|
|
@ -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
|
||||
|
||||
/** @} */
|
||||
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
/** @} */
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
||||
/** @} */
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
139
lib/route/cls/ematch/nbyte.c
Normal file
139
lib/route/cls/ematch/nbyte.c
Normal 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);
|
||||
}
|
||||
|
||||
/** @} */
|
105
lib/route/cls/ematch_grammar.l
Normal file
105
lib/route/cls/ematch_grammar.l
Normal 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;
|
||||
}
|
291
lib/route/cls/ematch_syntax.y
Normal file
291
lib/route/cls/ematch_syntax.y
Normal 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; }
|
||||
;
|
|
@ -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)
|
||||
|
|
Loading…
Add table
Reference in a new issue