--- /dev/null
+/*
+ * 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 next_table_id; /* OpenFlow table for 'next' to resubmit. */
+ 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")) {
+ if (ctx->next_table_id) {
+ emit_resubmit(ctx, ctx->next_table_id);
+ } else {
+ action_error(ctx, "\"next\" action not allowed here.");
+ }
+ } else if (lexer_match_id(ctx->lexer, "output")) {
+ /* Table 64 does logical-to-physical translation. */
+ 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.
+ *
+ * 'next_table_id' should be the OpenFlow table to which the "next" action will
+ * resubmit, or 0 to disable "next".
+ *
+ * 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 next_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.next_table_id = next_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 next_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, next_table_id,
+ ofpacts, prereqsp);
+ lexer_destroy(&lexer);
+
+ return error;
+}
--- /dev/null
+/*
+ * 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 next_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 next_table_id,
+ struct ofpbuf *ofpacts, struct expr **prereqsp)
+ OVS_WARN_UNUSED_RESULT;;
+
+#endif /* ovn/actions.h */
-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 \
#include "json.h"
#include "lex.h"
#include "match.h"
+#include "ofp-actions.h"
#include "shash.h"
#include "simap.h"
#include "openvswitch/vlog.h"
}
}
+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,
{
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;
}
}
}
- 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,
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);
OVS_NOT_REACHED();
}
}
+\f
+/* 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;
+}
#include "meta-flow.h"
struct ds;
+struct ofpbuf;
struct shash;
struct simap;
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 *);
struct hmap *matches);
void expr_matches_destroy(struct hmap *matches);
void expr_matches_print(const struct hmap *matches, FILE *);
+\f
+/* 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 */
"logical_datapath": {"type": "uuid"},
"table_id": {"type": {"key": {"type": "integer",
"minInteger": 0,
- "maxInteger": 127}}},
+ "maxInteger": 31}}},
"priority": {"type": {"key": {"type": "integer",
"minInteger": 0,
"maxInteger": 65535}}},
<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>, e.g. <code>outport = "vif0";</code> to set the
+ logical output port. 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. To set only a subset of bits in a field,
+ <var>field</var> may be a subfield or <var>constant</var> may be
+ masked, e.g. <code>vlan.pcp[2] = 1;</code> and <code>vlan.pcp =
+ 4/4;</code> both set the most sigificant bit of the VLAN PCP. Not
+ all fields are modifiable (e.g. <code>eth.type</code> and
+ <code>ip.proto</code> are read-only), and not all modifiable fields
+ may be partially modified (e.g. <code>ip.ttl</code> must assigned as
+ a whole).
</dd>
</dl>
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.
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 `>=').
]])
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
+next; => actions=resubmit(,11), prereqs=1
+output; => actions=resubmit(,64), prereqs=1
+outport="eth0"; next; outport="LOCAL"; next; => 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.
+next; 123; => Syntax error at `123'.
+next; xyzzy; => Syntax error at `xyzzy' expecting action.
+
+# "drop;" must be on its own:
+drop; next; => Syntax error at `next' expecting end of input.
+next; drop; => Syntax error at `drop' expecting action.
+
+# Missing ";":
+next => 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
#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"
{
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]");
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);
shash_destroy(&symtab);
}
\f
+/* 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, 11,
+ &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);
+}
+\f
static unsigned int
parse_relops(const char *s)
{
}
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},
{"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;