Signed-off-by: Ben Pfaff <b...@nicira.com> --- ovn/lib/actions.c | 242 ++++++++++++++++++++++++++++++++++++++++++++++++++++ ovn/lib/actions.h | 38 +++++++++ ovn/lib/automake.mk | 2 + ovn/lib/expr.c | 179 ++++++++++++++++++++++++++++++++++---- ovn/lib/expr.h | 15 +++- ovn/ovn-sb.xml | 6 +- tests/ovn.at | 75 ++++++++++++---- tests/test-ovn.c | 74 ++++++++++++++-- 8 files changed, 588 insertions(+), 43 deletions(-) create mode 100644 ovn/lib/actions.c create mode 100644 ovn/lib/actions.h
diff --git a/ovn/lib/actions.c b/ovn/lib/actions.c new file mode 100644 index 0000000..02b7a20 --- /dev/null +++ b/ovn/lib/actions.c @@ -0,0 +1,242 @@ +/* + * Copyright (c) 2015 Nicira, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <config.h> +#include "actions.h" +#include <stdarg.h> +#include <stdbool.h> +#include "compiler.h" +#include "dynamic-string.h" +#include "expr.h" +#include "lex.h" +#include "ofp-actions.h" +#include "ofpbuf.h" + +/* Context maintained during actions_parse(). */ +struct action_context { + /* Input. */ + struct lexer *lexer; /* Lexer for pulling more tokens. */ + const struct shash *symtab; /* Symbol table. */ + uint8_t table_id; /* Current logical table. */ + const struct simap *ports; /* Map from port name to number. */ + + /* State. */ + char *error; /* Error, if any, otherwise NULL. */ + + /* Output. */ + struct ofpbuf *ofpacts; /* Actions. */ + struct expr *prereqs; /* Prerequisites to apply to match. */ +}; + +static bool +action_error_handle_common(struct action_context *ctx) +{ + if (ctx->error) { + /* Already have an error, suppress this one since the cascade seems + * unlikely to be useful. */ + return true; + } else if (ctx->lexer->token.type == LEX_T_ERROR) { + /* The lexer signaled an error. Nothing at the action level + * accepts an error token, so we'll inevitably end up here with some + * meaningless parse error. Report the lexical error instead. */ + ctx->error = xstrdup(ctx->lexer->token.s); + return true; + } else { + return false; + } +} + +static void OVS_PRINTF_FORMAT(2, 3) +action_error(struct action_context *ctx, const char *message, ...) +{ + if (action_error_handle_common(ctx)) { + return; + } + + va_list args; + va_start(args, message); + ctx->error = xvasprintf(message, args); + va_end(args); +} + +static void OVS_PRINTF_FORMAT(2, 3) +action_syntax_error(struct action_context *ctx, const char *message, ...) +{ + if (action_error_handle_common(ctx)) { + return; + } + + struct ds s; + + ds_init(&s); + ds_put_cstr(&s, "Syntax error"); + if (ctx->lexer->token.type == LEX_T_END) { + ds_put_cstr(&s, " at end of input"); + } else if (ctx->lexer->start) { + ds_put_format(&s, " at `%.*s'", + (int) (ctx->lexer->input - ctx->lexer->start), + ctx->lexer->start); + } + + if (message) { + ds_put_char(&s, ' '); + + va_list args; + va_start(args, message); + ds_put_format_valist(&s, message, args); + va_end(args); + } + ds_put_char(&s, '.'); + + ctx->error = ds_steal_cstr(&s); +} + +static void +parse_set_action(struct action_context *ctx) +{ + struct expr *prereqs; + char *error; + + error = expr_parse_assignment(ctx->lexer, ctx->symtab, ctx->ports, + ctx->ofpacts, &prereqs); + if (error) { + action_error(ctx, "%s", error); + free(error); + return; + } + + ctx->prereqs = expr_combine(EXPR_T_AND, ctx->prereqs, prereqs); +} + +static void +emit_resubmit(struct action_context *ctx, uint8_t table_id) +{ + struct ofpact_resubmit *resubmit = ofpact_put_RESUBMIT(ctx->ofpacts); + resubmit->in_port = OFPP_IN_PORT; + resubmit->table_id = table_id; +} + +static void +parse_actions(struct action_context *ctx) +{ + /* "drop;" by itself is a valid (empty) set of actions, but it can't be + * combined with other actions because that doesn't make sense. */ + if (ctx->lexer->token.type == LEX_T_ID + && !strcmp(ctx->lexer->token.s, "drop") + && lexer_lookahead(ctx->lexer) == LEX_T_SEMICOLON) { + lexer_get(ctx->lexer); /* Skip "drop". */ + lexer_get(ctx->lexer); /* Skip ";". */ + if (ctx->lexer->token.type != LEX_T_END) { + action_syntax_error(ctx, "expecting end of input"); + } + return; + } + + while (ctx->lexer->token.type != LEX_T_END) { + if (ctx->lexer->token.type != LEX_T_ID) { + action_syntax_error(ctx, NULL); + break; + } + + enum lex_type lookahead = lexer_lookahead(ctx->lexer); + if (lookahead == LEX_T_EQUALS || lookahead == LEX_T_LSQUARE) { + parse_set_action(ctx); + } else if (lexer_match_id(ctx->lexer, "next")) { + emit_resubmit(ctx, ctx->table_id + 1); + } else if (lexer_match_id(ctx->lexer, "output")) { + emit_resubmit(ctx, 64); + } else { + action_syntax_error(ctx, "expecting action"); + } + if (!lexer_match(ctx->lexer, LEX_T_SEMICOLON)) { + action_syntax_error(ctx, "expecting ';'"); + } + if (ctx->error) { + return; + } + } +} + +/* Parses OVN actions, in the format described for the "actions" column in the + * Pipeline table in ovn-sb(5), and appends the parsed versions of the actions + * to 'ofpacts' as "struct ofpact"s. + * + * 'symtab' provides a table of "struct expr_symbol"s to support (as one would + * provide to expr_parse()). + * + * 'ports' must be a map from strings (presumably names of ports) to integers + * (as one would provide to expr_to_matches()). Strings used in the actions + * that are not in 'ports' are translated to zero. + * + * 'table_id' should be the OpenFlow table in which the actions will be used, + * to allow OVN "next" actions to go to the correct OpenFlow table (that is, + * table_id+1). + * + * Some actions add extra requirements (prerequisites) to the flow's match. If + * so, this function sets '*prereqsp' to the actions' prerequisites; otherwise, + * it sets '*prereqsp' to NULL. The caller owns '*prereqsp' and must + * eventually free it. + * + * Returns NULL on success, otherwise a malloc()'d error message that the + * caller must free. On failure, 'ofpacts' has the same contents and + * '*prereqsp' is set to NULL, but some tokens may have been consumed from + * 'lexer'. + */ +char * OVS_WARN_UNUSED_RESULT +actions_parse(struct lexer *lexer, const struct shash *symtab, + const struct simap *ports, uint8_t table_id, + struct ofpbuf *ofpacts, struct expr **prereqsp) +{ + size_t ofpacts_start = ofpacts->size; + + struct action_context ctx; + ctx.lexer = lexer; + ctx.symtab = symtab; + ctx.ports = ports; + ctx.table_id = table_id; + ctx.error = NULL; + ctx.ofpacts = ofpacts; + ctx.prereqs = NULL; + + parse_actions(&ctx); + + if (!ctx.error) { + *prereqsp = ctx.prereqs; + return NULL; + } else { + ofpacts->size = ofpacts_start; + expr_destroy(ctx.prereqs); + *prereqsp = NULL; + return ctx.error; + } +} + +/* Like actions_parse(), but the actions are taken from 's'. */ +char * OVS_WARN_UNUSED_RESULT +actions_parse_string(const char *s, const struct shash *symtab, + const struct simap *ports, uint8_t table_id, + struct ofpbuf *ofpacts, struct expr **prereqsp) +{ + struct lexer lexer; + char *error; + + lexer_init(&lexer, s); + lexer_get(&lexer); + error = actions_parse(&lexer, symtab, ports, table_id, ofpacts, prereqsp); + lexer_destroy(&lexer); + + return error; +} diff --git a/ovn/lib/actions.h b/ovn/lib/actions.h new file mode 100644 index 0000000..baef953 --- /dev/null +++ b/ovn/lib/actions.h @@ -0,0 +1,38 @@ +/* + * Copyright (c) 2015 Nicira, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef OVN_ACTIONS_H +#define OVN_ACTIONS_H 1 + +#include <stdint.h> +#include "compiler.h" + +struct expr; +struct lexer; +struct ofpbuf; +struct shash; +struct simap; + +char *actions_parse(struct lexer *, const struct shash *symtab, + const struct simap *ports, uint8_t table_id, + struct ofpbuf *ofpacts, struct expr **prereqsp) + OVS_WARN_UNUSED_RESULT;; +char *actions_parse_string(const char *s, const struct shash *symtab, + const struct simap *ports, uint8_t table_id, + struct ofpbuf *ofpacts, struct expr **prereqsp) + OVS_WARN_UNUSED_RESULT;; + +#endif /* ovn/actions.h */ diff --git a/ovn/lib/automake.mk b/ovn/lib/automake.mk index 454f2ef..956c83f 100644 --- a/ovn/lib/automake.mk +++ b/ovn/lib/automake.mk @@ -4,6 +4,8 @@ ovn_lib_libovn_la_LDFLAGS = \ -Wl,--version-script=$(top_builddir)/ovn/lib/libovn.sym \ $(AM_LDFLAGS) ovn_lib_libovn_la_SOURCES = \ + ovn/lib/actions.c \ + ovn/lib/actions.h \ ovn/lib/expr.c \ ovn/lib/expr.h \ ovn/lib/lex.c \ diff --git a/ovn/lib/expr.c b/ovn/lib/expr.c index a1275a3..c81c453 100644 --- a/ovn/lib/expr.c +++ b/ovn/lib/expr.c @@ -20,6 +20,7 @@ #include "json.h" #include "lex.h" #include "match.h" +#include "ofp-actions.h" #include "shash.h" #include "simap.h" #include "openvswitch/vlog.h" @@ -562,6 +563,33 @@ expr_constant_width(const union expr_constant *c) } } +static bool +type_check(struct expr_context *ctx, const struct expr_field *f, + struct expr_constant_set *cs) +{ + if (cs->type != (f->symbol->width ? EXPR_C_INTEGER : EXPR_C_STRING)) { + expr_error(ctx, "%s field %s is not compatible with %s constant.", + f->symbol->width ? "Integer" : "String", + f->symbol->name, + cs->type == EXPR_C_INTEGER ? "integer" : "string"); + return false; + } + + if (f->symbol->width) { + for (size_t i = 0; i < cs->n_values; i++) { + int w = expr_constant_width(&cs->values[i]); + if (w > f->symbol->width) { + expr_error(ctx, "%d-bit constant is not compatible with " + "%d-bit field %s.", + w, f->symbol->width, f->symbol->name); + return false; + } + } + } + + return true; +} + static struct expr * make_cmp(struct expr_context *ctx, const struct expr_field *f, enum expr_relop r, @@ -569,11 +597,7 @@ make_cmp(struct expr_context *ctx, { struct expr *e = NULL; - if (cs->type != (f->symbol->width ? EXPR_C_INTEGER : EXPR_C_STRING)) { - expr_error(ctx, "Can't compare %s field %s to %s constant.", - f->symbol->width ? "integer" : "string", - f->symbol->name, - cs->type == EXPR_C_INTEGER ? "integer" : "string"); + if (!type_check(ctx, f, cs)) { goto exit; } @@ -624,18 +648,6 @@ make_cmp(struct expr_context *ctx, } } - if (f->symbol->width) { - for (size_t i = 0; i < cs->n_values; i++) { - int w = expr_constant_width(&cs->values[i]); - if (w > f->symbol->width) { - expr_error(ctx, "Cannot compare %d-bit constant against " - "%d-bit field %s.", - w, f->symbol->width, f->symbol->name); - goto exit; - } - } - } - e = make_cmp__(f, r, &cs->values[0]); for (size_t i = 1; i < cs->n_values; i++) { e = expr_combine(r == EXPR_R_EQ ? EXPR_T_OR : EXPR_T_AND, @@ -1334,7 +1346,9 @@ parse_and_annotate(const char *s, const struct shash *symtab, if (expr) { expr = expr_annotate__(expr, symtab, nesting, &error); } - if (!expr) { + if (expr) { + *errorp = NULL; + } else { *errorp = xasprintf("Error parsing expression `%s' encountered as " "prerequisite or predicate of initial expression: " "%s", s, error); @@ -2465,3 +2479,132 @@ expr_is_normalized(const struct expr *expr) OVS_NOT_REACHED(); } } + +/* Action parsing helper. */ + +static struct expr * +parse_assignment(struct expr_context *ctx, const struct simap *ports, + struct ofpbuf *ofpacts) +{ + struct expr *prereqs = NULL; + + struct expr_field f; + if (!parse_field(ctx, &f)) { + goto exit; + } + if (!lexer_match(ctx->lexer, LEX_T_EQUALS)) { + expr_syntax_error(ctx, "expecting `='."); + goto exit; + } + + if (f.symbol->expansion && f.symbol->level != EXPR_L_ORDINAL) { + expr_error(ctx, "Can't assign to predicate symbol %s.", + f.symbol->name); + goto exit; + } + + struct expr_constant_set cs; + if (!parse_constant_set(ctx, &cs)) { + goto exit; + } + + if (!type_check(ctx, &f, &cs)) { + goto exit_destroy_cs; + } + if (cs.in_curlies) { + expr_error(ctx, "Assignments require a single value."); + goto exit_destroy_cs; + } + + const struct expr_symbol *orig_symbol = f.symbol; + union expr_constant *c = cs.values; + for (;;) { + /* Accumulate prerequisites. */ + if (f.symbol->prereqs) { + struct ovs_list nesting = OVS_LIST_INITIALIZER(&nesting); + char *error; + struct expr *e; + e = parse_and_annotate(f.symbol->prereqs, ctx->symtab, &nesting, + &error); + if (error) { + expr_error(ctx, "%s", error); + free(error); + goto exit_destroy_cs; + } + prereqs = expr_combine(EXPR_T_AND, prereqs, e); + } + + /* If there's no expansion, we're done. */ + if (!f.symbol->expansion) { + break; + } + + /* Expand. */ + struct expr_field expansion; + char *error; + if (!parse_field_from_string(f.symbol->expansion, ctx->symtab, + &expansion, &error)) { + expr_error(ctx, "%s", error); + free(error); + goto exit_destroy_cs; + } + f.symbol = expansion.symbol; + f.ofs += expansion.ofs; + } + + if (!f.symbol->field->writable) { + expr_error(ctx, "Field %s is not modifiable.", orig_symbol->name); + goto exit_destroy_cs; + } + + struct ofpact_set_field *sf = ofpact_put_SET_FIELD(ofpacts); + sf->field = f.symbol->field; + if (f.symbol->width) { + mf_subvalue_shift(&c->value, f.ofs); + if (!c->masked) { + memset(&c->mask, 0, sizeof c->mask); + bitwise_one(&c->mask, sizeof c->mask, f.ofs, f.n_bits); + } else { + mf_subvalue_shift(&c->mask, f.ofs); + } + + memcpy(&sf->value, &c->value.u8[sizeof c->value - sf->field->n_bytes], + sf->field->n_bytes); + memcpy(&sf->mask, &c->mask.u8[sizeof c->mask - sf->field->n_bytes], + sf->field->n_bytes); + } else { + uint32_t port = simap_get(ports, c->string); + bitwise_put(port, &sf->value, + sf->field->n_bytes, 0, sf->field->n_bits); + bitwise_put(UINT64_MAX, &sf->mask, + sf->field->n_bytes, 0, sf->field->n_bits); + } + +exit_destroy_cs: + expr_constant_set_destroy(&cs); +exit: + return prereqs; +} + +/* A helper for actions_parse(), to parse an OVN assignment action in the form + * "field = value" into 'ofpacts'. The parameters and return value match those + * for actions_parse(). */ +char * +expr_parse_assignment(struct lexer *lexer, const struct shash *symtab, + const struct simap *ports, + struct ofpbuf *ofpacts, struct expr **prereqsp) +{ + struct expr_context ctx; + ctx.lexer = lexer; + ctx.symtab = symtab; + ctx.error = NULL; + ctx.not = false; + + struct expr *prereqs = parse_assignment(&ctx, ports, ofpacts); + if (ctx.error) { + expr_destroy(prereqs); + prereqs = NULL; + } + *prereqsp = prereqs; + return ctx.error; +} diff --git a/ovn/lib/expr.h b/ovn/lib/expr.h index 3540708..501152e 100644 --- a/ovn/lib/expr.h +++ b/ovn/lib/expr.h @@ -60,6 +60,7 @@ #include "meta-flow.h" struct ds; +struct ofpbuf; struct shash; struct simap; @@ -341,14 +342,16 @@ 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 *, char **errorp); -struct expr *expr_parse_string(const char *, const struct shash *, +struct expr *expr_parse(struct lexer *, const struct shash *symtab, + char **errorp); +struct expr *expr_parse_string(const char *, const struct shash *symtab, char **errorp); struct expr *expr_clone(struct expr *); void expr_destroy(struct expr *); -struct expr *expr_annotate(struct expr *, const struct shash *, char **errorp); +struct expr *expr_annotate(struct expr *, const struct shash *symtab, + char **errorp); struct expr *expr_simplify(struct expr *); struct expr *expr_normalize(struct expr *); @@ -371,5 +374,11 @@ uint32_t expr_to_matches(const struct expr *, const struct simap *ports, struct hmap *matches); void expr_matches_destroy(struct hmap *matches); void expr_matches_print(const struct hmap *matches, FILE *); + +/* Action parsing helper. */ + +char *expr_parse_assignment(struct lexer *lexer, const struct shash *symtab, + const struct simap *ports, struct ofpbuf *ofpacts, + struct expr **prereqsp); #endif /* ovn/expr.h */ diff --git a/ovn/ovn-sb.xml b/ovn/ovn-sb.xml index 31ee871..a466396 100644 --- a/ovn/ovn-sb.xml +++ b/ovn/ovn-sb.xml @@ -586,7 +586,11 @@ <dt><code><var>field</var> = <var>constant</var>;</code></dt> <dd> Sets data or metadata field <var>field</var> to constant value - <var>constant</var>. + <var>constant</var>. Assigning to a field with prerequisites + implicitly adds those prerequisites to <ref column="match"/>; thus, + for example, a flow that sets <code>tcp.dst</code> applies only to + TCP flows, regardless of whether its <ref column="match"/> mentions + any TCP field. </dd> </dl> diff --git a/tests/ovn.at b/tests/ovn.at index 24479ec..205d70d 100644 --- a/tests/ovn.at +++ b/tests/ovn.at @@ -200,8 +200,8 @@ ip6.src == ::1 => ip6.src == 0x1 inport == "eth0" !(inport != "eth0") => inport == "eth0" -ip4.src == "eth0" => Can't compare integer field ip4.src to string constant. -inport == 1 => Can't compare string field inport to integer constant. +ip4.src == "eth0" => Integer field ip4.src is not compatible with string constant. +inport == 1 => String field inport is not compatible with integer constant. ip4.src > {1, 2, 3} => Only == and != operators may be used with value sets. eth.type > 0x800 => Only == and != operators may be used with nominal field eth.type. @@ -253,7 +253,7 @@ eth.dst == {} => Syntax error at `}' expecting constant. eth.src > 00:00:00:00:11:11/00:00:00:00:ff:ff => Only == and != operators may be used with masked constants. Consider using subfields instead (e.g. eth.src[0..15] > 0x1111 in place of eth.src > 00:00:00:00:11:11/00:00:00:00:ff:ff). -ip4.src == ::1 => Cannot compare 128-bit constant against 32-bit field ip4.src. +ip4.src == ::1 => 128-bit constant is not compatible with 32-bit field ip4.src. 1 == eth.type == 2 => Range expressions must have the form `x < field < y' or `x > field > y', with each `<' optionally replaced by `<=' or `>' by `>='). ]]) @@ -351,31 +351,74 @@ AT_SETUP([ovn -- converting expressions to flows -- string fields]) expr_to_flow () { echo "$1" | ovstest test-ovn expr-to-flows | sort } -AT_CHECK([expr_to_flow 'inport == "eth0"'], [0], [in_port=5 +AT_CHECK([expr_to_flow 'inport == "eth0"'], [0], [reg6=0x5 ]) -AT_CHECK([expr_to_flow 'inport == "eth1"'], [0], [in_port=6 +AT_CHECK([expr_to_flow 'inport == "eth1"'], [0], [reg6=0x6 ]) AT_CHECK([expr_to_flow 'inport == "eth2"'], [0], [(no flows) ]) AT_CHECK([expr_to_flow 'inport == "eth0" && ip'], [0], [dnl -ip,in_port=5 -ipv6,in_port=5 +ip,reg6=0x5 +ipv6,reg6=0x5 ]) AT_CHECK([expr_to_flow 'inport == "eth1" && ip'], [0], [dnl -ip,in_port=6 -ipv6,in_port=6 +ip,reg6=0x6 +ipv6,reg6=0x6 ]) AT_CHECK([expr_to_flow 'inport == "eth2" && ip'], [0], [(no flows) ]) AT_CHECK([expr_to_flow 'inport == {"eth0", "eth1", "eth2", "LOCAL"}'], [0], -[in_port=5 -in_port=6 -in_port=LOCAL +[reg6=0x5 +reg6=0x6 +reg6=0xfffe ]) AT_CHECK([expr_to_flow 'inport == {"eth0", "eth1", "eth2"} && ip'], [0], [dnl -ip,in_port=5 -ip,in_port=6 -ipv6,in_port=5 -ipv6,in_port=6 +ip,reg6=0x5 +ip,reg6=0x6 +ipv6,reg6=0x5 +ipv6,reg6=0x6 ]) AT_CLEANUP + +AT_SETUP([ovn -- action parsing]) +dnl Text before => is input, text after => is expected output. +AT_DATA([test-cases.txt], [[ +# Positive tests. +drop; => actions=drop, prereqs=1 +resubmit; => actions=resubmit(,11), prereqs=1 +output; => actions=resubmit(,64), prereqs=1 +outport="eth0"; resubmit; outport="LOCAL"; resubmit; => actions=set_field:0x5->reg7,resubmit(,11),set_field:0xfffe->reg7,resubmit(,11), prereqs=1 +tcp.dst=80; => actions=set_field:80->tcp_dst, prereqs=ip.proto == 0x6 && (eth.type == 0x800 || eth.type == 0x86dd) +eth.dst[40] = 1; => actions=set_field:01:00:00:00:00:00/01:00:00:00:00:00->eth_dst, prereqs=1 +vlan.pcp = 2; => actions=set_field:0x4000/0xe000->vlan_tci, prereqs=vlan.tci[12] +vlan.tci[13..15] = 2; => actions=set_field:0x4000/0xe000->vlan_tci, prereqs=1 + +## Negative tests. + +; => Syntax error at `;'. +xyzzy; => Syntax error at `xyzzy' expecting action. +resubmit; 123; => Syntax error at `123'. +resubmit; xyzzy; => Syntax error at `xyzzy' expecting action. + +# "drop;" must be on its own: +drop; resubmit; => Syntax error at `resubmit' expecting end of input. +resubmit; drop; => Syntax error at `drop' expecting action. + +# Missing ";": +resubmit => Syntax error at end of input expecting ';'. + +inport[1] = 1; => Cannot select subfield of string field inport. +ip.proto[1] = 1; => Cannot select subfield of nominal field ip.proto. +eth.dst[40] == 1; => Syntax error at `==' expecting `='. +ip = 1; => Can't assign to predicate symbol ip. +ip.proto = 6; => Field ip.proto is not modifiable. +inport = {"a", "b"}; => Assignments require a single value. +inport = {}; => Syntax error at `}' expecting constant. +bad_prereq = 123; => Error parsing expression `xyzzy' encountered as prerequisite or predicate of initial expression: Syntax error at `xyzzy' expecting field name. +self_recurse = 123; => Error parsing expression `self_recurse != 0' encountered as prerequisite or predicate of initial expression: Error parsing expression `self_recurse != 0' encountered as prerequisite or predicate of initial expression: Recursive expansion of symbol `self_recurse'. +vlan.present = 0; => Can't assign to predicate symbol vlan.present. +]]) +sed 's/ =>.*//' test-cases.txt > input.txt +sed 's/.* => //' test-cases.txt > expout +AT_CHECK([ovstest test-ovn parse-actions < input.txt], [0], [expout]) +AT_CLEANUP diff --git a/tests/test-ovn.c b/tests/test-ovn.c index b6fdb0c..9e56cee 100644 --- a/tests/test-ovn.c +++ b/tests/test-ovn.c @@ -22,6 +22,9 @@ #include "dynamic-string.h" #include "fatal-signal.h" #include "match.h" +#include "ofp-actions.h" +#include "ofpbuf.h" +#include "ovn/lib/actions.h" #include "ovn/lib/expr.h" #include "ovn/lib/lex.h" #include "ovs-thread.h" @@ -132,13 +135,15 @@ create_symtab(struct shash *symtab) { shash_init(symtab); - expr_symtab_add_string(symtab, "inport", MFF_IN_PORT, NULL); - expr_symtab_add_string(symtab, "outport", MFF_ACTSET_OUTPUT, NULL); + /* Reserve a pair of registers for the logical inport and outport. A full + * 32-bit register each is bigger than we need, but the expression code + * doesn't yet support string fields that occupy less than a full OXM. */ + expr_symtab_add_string(symtab, "inport", MFF_REG6, NULL); + expr_symtab_add_string(symtab, "outport", MFF_REG7, NULL); expr_symtab_add_field(symtab, "xreg0", MFF_XREG0, NULL, false); expr_symtab_add_field(symtab, "xreg1", MFF_XREG1, NULL, false); expr_symtab_add_field(symtab, "xreg2", MFF_XREG2, NULL, false); - expr_symtab_add_field(symtab, "xreg3", MFF_XREG3, NULL, false); expr_symtab_add_subfield(symtab, "reg0", NULL, "xreg0[32..63]"); expr_symtab_add_subfield(symtab, "reg1", NULL, "xreg0[0..31]"); @@ -146,8 +151,6 @@ create_symtab(struct shash *symtab) expr_symtab_add_subfield(symtab, "reg3", NULL, "xreg1[0..31]"); expr_symtab_add_subfield(symtab, "reg4", NULL, "xreg2[32..63]"); expr_symtab_add_subfield(symtab, "reg5", NULL, "xreg2[0..31]"); - expr_symtab_add_subfield(symtab, "reg6", NULL, "xreg3[32..63]"); - expr_symtab_add_subfield(symtab, "reg7", NULL, "xreg3[0..31]"); expr_symtab_add_field(symtab, "eth.src", MFF_ETH_SRC, NULL, false); expr_symtab_add_field(symtab, "eth.dst", MFF_ETH_DST, NULL, false); @@ -1110,6 +1113,60 @@ test_exhaustive(struct ovs_cmdl_context *ctx OVS_UNUSED) shash_destroy(&symtab); } +/* Actions. */ + +static void +test_parse_actions(struct ovs_cmdl_context *ctx OVS_UNUSED) +{ + struct shash symtab; + struct simap ports; + struct ds input; + + create_symtab(&symtab); + + simap_init(&ports); + simap_put(&ports, "eth0", 5); + simap_put(&ports, "eth1", 6); + simap_put(&ports, "LOCAL", ofp_to_u16(OFPP_LOCAL)); + + ds_init(&input); + while (!ds_get_test_line(&input, stdin)) { + struct ofpbuf ofpacts; + struct expr *prereqs; + char *error; + + ofpbuf_init(&ofpacts, 0); + error = actions_parse_string(ds_cstr(&input), &symtab, &ports, 10, + &ofpacts, &prereqs); + if (!error) { + struct ds output; + + ds_init(&output); + ds_put_cstr(&output, "actions="); + ofpacts_format(ofpacts.data, ofpacts.size, &output); + ds_put_cstr(&output, ", prereqs="); + if (prereqs) { + expr_format(prereqs, &output); + } else { + ds_put_char(&output, '1'); + } + puts(ds_cstr(&output)); + ds_destroy(&output); + } else { + puts(error); + free(error); + } + + expr_destroy(prereqs); + ofpbuf_uninit(&ofpacts); + } + ds_destroy(&input); + + simap_destroy(&ports); + expr_symtab_destroy(&symtab); + shash_destroy(&symtab); +} + static unsigned int parse_relops(const char *s) { @@ -1266,7 +1323,10 @@ test_ovn_main(int argc, char *argv[]) } static const struct ovs_cmdl_command commands[] = { + /* Lexer. */ {"lex", NULL, 0, 0, test_lex}, + + /* Expressions. */ {"parse-expr", NULL, 0, 0, test_parse_expr}, {"annotate-expr", NULL, 0, 0, test_annotate_expr}, {"simplify-expr", NULL, 0, 0, test_simplify_expr}, @@ -1276,6 +1336,10 @@ test_ovn_main(int argc, char *argv[]) {"composition", NULL, 1, 1, test_composition}, {"tree-shape", NULL, 1, 1, test_tree_shape}, {"exhaustive", NULL, 1, 1, test_exhaustive}, + + /* Actions. */ + {"parse-actions", NULL, 0, 0, test_parse_actions}, + {NULL, NULL, 0, 0, NULL}, }; struct ovs_cmdl_context ctx; -- 2.1.3 _______________________________________________ dev mailing list dev@openvswitch.org http://openvswitch.org/mailman/listinfo/dev