[ovs-dev] [PATCH 2/3] expr: Add address set support.
Russell Bryant
russell at ovn.org
Tue Apr 5 21:24:18 UTC 2016
Update the OVN expression parser to support address sets. Previously,
you could have a set of IP or MAC addresses in this form:
{addr1, addr2, ..., addrN}
This patch adds support for a bit of indirection where we can define a
set of addresses and refer to them by name.
address_set(name)
A future patch will expose the ability to define address sets for use.
Signed-off-by: Russell Bryant <russell at ovn.org>
---
ovn/controller/lflow.c | 2 +-
ovn/lib/actions.c | 2 +-
ovn/lib/expr.c | 139 ++++++++++++++++++++++++++++++++++++++++++++++---
ovn/lib/expr.h | 15 ++++++
tests/ovn.at | 36 +++++++++++++
tests/test-ovn.c | 31 +++++++++--
6 files changed, 214 insertions(+), 11 deletions(-)
diff --git a/ovn/controller/lflow.c b/ovn/controller/lflow.c
index 7a3466f..287ffd3 100644
--- a/ovn/controller/lflow.c
+++ b/ovn/controller/lflow.c
@@ -303,7 +303,7 @@ add_logical_flows(struct controller_ctx *ctx, const struct lport_index *lports,
struct hmap matches;
struct expr *expr;
- expr = expr_parse_string(lflow->match, &symtab, &error);
+ expr = expr_parse_string(lflow->match, &symtab, NULL, &error);
if (!error) {
if (prereqs) {
expr = expr_combine(EXPR_T_AND, expr, prereqs);
diff --git a/ovn/lib/actions.c b/ovn/lib/actions.c
index 44957c7..968f7a8 100644
--- a/ovn/lib/actions.c
+++ b/ovn/lib/actions.c
@@ -179,7 +179,7 @@ add_prerequisite(struct action_context *ctx, const char *prerequisite)
struct expr *expr;
char *error;
- expr = expr_parse_string(prerequisite, ctx->ap->symtab, &error);
+ expr = expr_parse_string(prerequisite, ctx->ap->symtab, NULL, &error);
ovs_assert(!error);
ctx->prereqs = expr_combine(EXPR_T_AND, ctx->prereqs, expr);
}
diff --git a/ovn/lib/expr.c b/ovn/lib/expr.c
index aae5df6..4ba0487 100644
--- a/ovn/lib/expr.c
+++ b/ovn/lib/expr.c
@@ -450,6 +450,7 @@ struct expr_field {
struct expr_context {
struct lexer *lexer; /* Lexer for pulling more tokens. */
const struct shash *symtab; /* Symbol table. */
+ const struct shash *address_sets; /* Table of address sets. */
char *error; /* Error, if any, otherwise NULL. */
bool not; /* True inside odd number of NOT operators. */
};
@@ -807,6 +808,61 @@ parse_constant(struct expr_context *ctx, struct expr_constant_set *cs,
}
}
+static bool
+parse_address_set(struct expr_context *ctx, struct expr_constant_set *cs)
+{
+ if (!ctx->address_sets) {
+ expr_syntax_error(ctx, "No address sets defined.");
+ return false;
+ }
+
+ if (!lexer_match(ctx->lexer, LEX_T_LPAREN)) {
+ expr_syntax_error(ctx, "Expecting '(' after 'address_set'");
+ return false;
+ }
+
+ if (ctx->lexer->token.type != LEX_T_ID) {
+ expr_syntax_error(ctx, "Expecting name after 'address_set('");
+ return false;
+ }
+
+ bool ok = true;
+ char *name = xstrdup(ctx->lexer->token.s);
+ lexer_get(ctx->lexer);
+
+ if (!lexer_match(ctx->lexer, LEX_T_RPAREN)) {
+ expr_syntax_error(ctx, "Expecting ')' after 'address_set(<name>'");
+ ok = false;
+ goto cleanup;
+ }
+
+ struct expr_constant_set *addr_set
+ = shash_find_data(ctx->address_sets, name);
+ if (!name) {
+ expr_syntax_error(ctx, "Unknown address set: '%s'", name);
+ ok = false;
+ goto cleanup;
+ }
+
+ cs->type = EXPR_C_INTEGER;
+ cs->in_curlies = true;
+ cs->n_values = addr_set->n_values;
+ cs->values = xmalloc(cs->n_values * sizeof *cs->values);
+ size_t i;
+ for (i = 0; i < cs->n_values; i++) {
+ union expr_constant *c1 = &cs->values[i];
+ union expr_constant *c2 = &addr_set->values[i];
+ c1->value = c2->value;
+ c1->format = c2->format;
+ c1->masked = c2->masked;
+ c1->mask = c2->mask;
+ }
+
+cleanup:
+ free(name);
+ return ok;
+}
+
/* Parses a single or {}-enclosed set of integer or string constants into 'cs',
* which the caller need not have initialized. Returns true on success, in
* which case the caller owns 'cs', false on failure, in which case 'cs' is
@@ -818,7 +874,9 @@ parse_constant_set(struct expr_context *ctx, struct expr_constant_set *cs)
bool ok;
memset(cs, 0, sizeof *cs);
- if (lexer_match(ctx->lexer, LEX_T_LCURLY)) {
+ if (lexer_match_id(ctx->lexer, "address_set")) {
+ ok = parse_address_set(ctx, cs);
+ } else if (lexer_match(ctx->lexer, LEX_T_LCURLY)) {
ok = true;
cs->in_curlies = true;
do {
@@ -850,6 +908,72 @@ expr_constant_set_destroy(struct expr_constant_set *cs)
}
}
+void
+expr_address_sets_add(struct shash *address_sets, const char *name,
+ const char * const *addresses, size_t n_addresses)
+{
+ /* Replace any existing entry for this name. */
+ expr_address_sets_remove(address_sets, name);
+
+ struct expr_constant_set *cset = xzalloc(sizeof *cset);
+ cset->type = EXPR_C_INTEGER;
+ cset->in_curlies = true;
+ cset->n_values = n_addresses;
+ cset->values = xmalloc(cset->n_values * sizeof *cset->values);
+ size_t i, errors = 0;
+ for (i = 0; i < n_addresses; i++) {
+ /* Use the lexer to convert each address into the proper
+ * integer format. */
+ struct lexer lex;
+ lexer_init(&lex, addresses[i]);
+ lexer_get(&lex);
+ if (lex.token.type != LEX_T_INTEGER
+ && lex.token.type != LEX_T_MASKED_INTEGER) {
+ VLOG_WARN("Invalid address set entry: '%s', token type: %d",
+ addresses[i], lex.token.type);
+ errors += 1;
+ } else {
+ union expr_constant *c = &cset->values[i - errors];
+ c->value = lex.token.value;
+ c->format = lex.token.format;
+ c->masked = lex.token.type == LEX_T_MASKED_INTEGER;
+ if (c->masked) {
+ c->mask = lex.token.mask;
+ }
+ }
+ lexer_destroy(&lex);
+ }
+ cset->n_values -= errors;
+
+ shash_add(address_sets, name, cset);
+}
+
+void
+expr_address_sets_remove(struct shash *address_sets, const char *name)
+{
+ struct expr_constant_set *cset
+ = shash_find_and_delete(address_sets, name);
+
+ if (cset) {
+ expr_constant_set_destroy(cset);
+ free(cset);
+ }
+}
+
+/* Destroy all contents of address_sets. */
+void
+expr_address_sets_destroy(struct shash *address_sets)
+{
+ struct shash_node *node, *next;
+
+ SHASH_FOR_EACH_SAFE (node, next, address_sets) {
+ struct expr_constant_set *cset = node->data;
+
+ shash_delete(address_sets, node);
+ expr_constant_set_destroy(cset);
+ }
+}
+
static struct expr *
expr_parse_primary(struct expr_context *ctx, bool *atomic)
{
@@ -1022,12 +1146,14 @@ expr_parse__(struct expr_context *ctx)
* The caller must eventually free the returned expression (with
* expr_destroy()) or error (with free()). */
struct expr *
-expr_parse(struct lexer *lexer, const struct shash *symtab, char **errorp)
+expr_parse(struct lexer *lexer, const struct shash *symtab,
+ const struct shash *address_sets, char **errorp)
{
struct expr_context ctx;
ctx.lexer = lexer;
ctx.symtab = symtab;
+ ctx.address_sets = address_sets;
ctx.error = NULL;
ctx.not = false;
@@ -1039,14 +1165,15 @@ expr_parse(struct lexer *lexer, const struct shash *symtab, char **errorp)
/* Like expr_parse(), but the expression is taken from 's'. */
struct expr *
-expr_parse_string(const char *s, const struct shash *symtab, char **errorp)
+expr_parse_string(const char *s, const struct shash *symtab,
+ const struct shash *address_sets, char **errorp)
{
struct lexer lexer;
struct expr *expr;
lexer_init(&lexer, s);
lexer_get(&lexer);
- expr = expr_parse(&lexer, symtab, errorp);
+ expr = expr_parse(&lexer, symtab, address_sets, errorp);
if (!*errorp && lexer.token.type != LEX_T_END) {
*errorp = xstrdup("Extra tokens at end of input.");
expr_destroy(expr);
@@ -1202,7 +1329,7 @@ expr_get_level(const struct expr *expr)
static enum expr_level
expr_parse_level(const char *s, const struct shash *symtab, char **errorp)
{
- struct expr *expr = expr_parse_string(s, symtab, errorp);
+ struct expr *expr = expr_parse_string(s, symtab, NULL, errorp);
enum expr_level level = expr ? expr_get_level(expr) : EXPR_L_NOMINAL;
expr_destroy(expr);
return level;
@@ -1345,7 +1472,7 @@ parse_and_annotate(const char *s, const struct shash *symtab,
char *error;
struct expr *expr;
- expr = expr_parse_string(s, symtab, &error);
+ expr = expr_parse_string(s, symtab, NULL, &error);
if (expr) {
expr = expr_annotate__(expr, symtab, nesting, &error);
}
diff --git a/ovn/lib/expr.h b/ovn/lib/expr.h
index fa7ccbe..9951001 100644
--- a/ovn/lib/expr.h
+++ b/ovn/lib/expr.h
@@ -343,8 +343,10 @@ expr_from_node(const struct ovs_list *node)
void expr_format(const struct expr *, struct ds *);
void expr_print(const struct expr *);
struct expr *expr_parse(struct lexer *, const struct shash *symtab,
+ const struct shash *address_sets,
char **errorp);
struct expr *expr_parse_string(const char *, const struct shash *symtab,
+ const struct shash *address_sets,
char **errorp);
struct expr *expr_clone(struct expr *);
@@ -391,4 +393,17 @@ char *expr_parse_field(struct lexer *, int n_bits, bool rw,
const struct shash *symtab, struct mf_subfield *,
struct expr **prereqsp);
+
+/* Address sets.
+ *
+ * Instead of referring to a set of addresses as:
+ * {addr1, addr2, ..., addrN}
+ * You can register a set of addresses and refer to them as:
+ * address_set(<name>)
+ */
+void expr_address_sets_add(struct shash *address_sets, const char *name,
+ const char * const *addresses, size_t n_addresses);
+void expr_address_sets_remove(struct shash *address_sets, const char *name);
+void expr_address_sets_destroy(struct shash *address_sets);
+
#endif /* ovn/expr.h */
diff --git a/tests/ovn.at b/tests/ovn.at
index 22121e1..d10bc46 100644
--- a/tests/ovn.at
+++ b/tests/ovn.at
@@ -434,6 +434,42 @@ AT_CHECK([expr_to_flow 'inport == "eth0" && inport == "eth1"'], [0], [dnl
])
AT_CLEANUP
+AT_SETUP([ovn -- converting expressions to flows -- address sets])
+expr_to_flow () {
+ echo "$1" | ovstest test-ovn expr-to-flows | sort
+}
+AT_CHECK([expr_to_flow 'ip4.src == {10.0.0.1, 10.0.0.2, 10.0.0.3}'], [0], [dnl
+ip,nw_src=10.0.0.1
+ip,nw_src=10.0.0.2
+ip,nw_src=10.0.0.3
+])
+AT_CHECK([expr_to_flow 'ip4.src == address_set(set1)'], [0], [dnl
+ip,nw_src=10.0.0.1
+ip,nw_src=10.0.0.2
+ip,nw_src=10.0.0.3
+])
+AT_CHECK([expr_to_flow 'ip6.src == {::1, ::2, ::3}'], [0], [dnl
+ipv6,ipv6_src=::1
+ipv6,ipv6_src=::2
+ipv6,ipv6_src=::3
+])
+AT_CHECK([expr_to_flow 'ip6.src == address_set(set2)'], [0], [dnl
+ipv6,ipv6_src=::1
+ipv6,ipv6_src=::2
+ipv6,ipv6_src=::3
+])
+AT_CHECK([expr_to_flow 'eth.src == {00:00:00:00:00:01, 00:00:00:00:00:02, 00:00:00:00:00:03}'], [0], [dnl
+dl_src=00:00:00:00:00:01
+dl_src=00:00:00:00:00:02
+dl_src=00:00:00:00:00:03
+])
+AT_CHECK([expr_to_flow 'eth.src == address_set(set3)'], [0], [dnl
+dl_src=00:00:00:00:00:01
+dl_src=00:00:00:00:00:02
+dl_src=00:00:00:00:00:03
+])
+AT_CLEANUP
+
AT_SETUP([ovn -- action parsing])
dnl Text before => is input, text after => is expected output.
AT_DATA([test-cases.txt], [[
diff --git a/tests/test-ovn.c b/tests/test-ovn.c
index 9290692..7758cb5 100644
--- a/tests/test-ovn.c
+++ b/tests/test-ovn.c
@@ -238,6 +238,26 @@ create_symtab(struct shash *symtab)
expr_symtab_add_string(symtab, "big_string", MFF_XREG0, NULL);
}
+static void
+create_address_sets(struct shash *address_sets)
+{
+ shash_init(address_sets);
+
+ const char * const addrs1[] = {
+ "10.0.0.1", "10.0.0.2", "10.0.0.3",
+ };
+ const char * const addrs2[] = {
+ "::1", "::2", "::3",
+ };
+ const char * const addrs3[] = {
+ "00:00:00:00:00:01", "00:00:00:00:00:02", "00:00:00:00:00:03",
+ };
+
+ expr_address_sets_add(address_sets, "set1", addrs1, 3);
+ expr_address_sets_add(address_sets, "set2", addrs2, 3);
+ expr_address_sets_add(address_sets, "set3", addrs3, 3);
+}
+
static bool
lookup_port_cb(const void *ports_, const char *port_name, unsigned int *portp)
{
@@ -254,10 +274,12 @@ static void
test_parse_expr__(int steps)
{
struct shash symtab;
+ struct shash address_sets;
struct simap ports;
struct ds input;
create_symtab(&symtab);
+ create_address_sets(&address_sets);
simap_init(&ports);
simap_put(&ports, "eth0", 5);
@@ -269,7 +291,8 @@ test_parse_expr__(int steps)
struct expr *expr;
char *error;
- expr = expr_parse_string(ds_cstr(&input), &symtab, &error);
+ expr = expr_parse_string(ds_cstr(&input), &symtab, &address_sets,
+ &error);
if (!error && steps > 0) {
expr = expr_annotate(expr, &symtab, &error);
}
@@ -306,6 +329,8 @@ test_parse_expr__(int steps)
simap_destroy(&ports);
expr_symtab_destroy(&symtab);
shash_destroy(&symtab);
+ expr_address_sets_destroy(&address_sets);
+ shash_destroy(&address_sets);
}
static void
@@ -450,7 +475,7 @@ test_evaluate_expr(struct ovs_cmdl_context *ctx)
struct expr *expr;
char *error;
- expr = expr_parse_string(ds_cstr(&input), &symtab, &error);
+ expr = expr_parse_string(ds_cstr(&input), &symtab, NULL, &error);
if (!error) {
expr = expr_annotate(expr, &symtab, &error);
}
@@ -924,7 +949,7 @@ test_tree_shape_exhaustively(struct expr *expr, struct shash *symtab,
expr_format(expr, &s);
char *error;
- modified = expr_parse_string(ds_cstr(&s), symtab, &error);
+ modified = expr_parse_string(ds_cstr(&s), symtab, NULL, &error);
if (error) {
fprintf(stderr, "%s fails to parse (%s)\n",
ds_cstr(&s), error);
--
2.5.5
More information about the dev
mailing list