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 <russ...@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 a17b5a7..2e0516a 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 _______________________________________________ dev mailing list dev@openvswitch.org http://openvswitch.org/mailman/listinfo/dev