#include "dynamic-string.h"
#include "json.h"
#include "lex.h"
+#include "logical-fields.h"
#include "match.h"
#include "ofp-actions.h"
#include "shash.h"
#include "simap.h"
+#include "sset.h"
#include "openvswitch/vlog.h"
VLOG_DEFINE_THIS_MODULE(expr);
static bool
expr_get_int(struct expr_context *ctx, int *value)
{
- if (ctx->lexer->token.type == LEX_T_INTEGER
- && ctx->lexer->token.format == LEX_F_DECIMAL
- && ntohll(ctx->lexer->token.value.integer) <= INT_MAX) {
- *value = ntohll(ctx->lexer->token.value.integer);
- lexer_get(ctx->lexer);
- return true;
- } else {
+ bool ok = lexer_get_int(ctx->lexer, value);
+ if (!ok) {
expr_syntax_error(ctx, "expecting small integer.");
- return false;
}
+ return ok;
}
static bool
* - Expands references to predicate symbols, by replacing them by the
* expressions that they expand to.
*
- * In each case, annotation occurs recursively as necessary. */
+ * In each case, annotation occurs recursively as necessary.
+ *
+ * On failure, returns NULL and sets '*errorp' to an explanatory error
+ * message, which the caller must free. */
struct expr *
expr_annotate(struct expr *expr, const struct shash *symtab, char **errorp)
{
static struct expr *crush_cmps(struct expr *, const struct expr_symbol *);
-/* Implementation of crush_cmps() for expr->type == EXPR_T_AND. */
+static bool
+disjunction_matches_string(const struct expr *or, const char *s)
+{
+ const struct expr *sub;
+
+ LIST_FOR_EACH (sub, node, &or->andor) {
+ if (!strcmp(sub->cmp.string, s)) {
+ return true;
+ }
+ }
+
+ return false;
+}
+
+/* Implementation of crush_cmps() for expr->type == EXPR_T_AND and a
+ * string-typed 'symbol'. */
+static struct expr *
+crush_and_string(struct expr *expr, const struct expr_symbol *symbol)
+{
+ ovs_assert(!list_is_short(&expr->andor));
+
+ struct expr *singleton = NULL;
+
+ /* First crush each subexpression into either a single EXPR_T_CMP or an
+ * EXPR_T_OR with EXPR_T_CMP subexpressions. */
+ struct expr *sub, *next = NULL;
+ LIST_FOR_EACH_SAFE (sub, next, node, &expr->andor) {
+ list_remove(&sub->node);
+ struct expr *new = crush_cmps(sub, symbol);
+ switch (new->type) {
+ case EXPR_T_CMP:
+ if (!singleton) {
+ list_insert(&next->node, &new->node);
+ singleton = new;
+ } else {
+ bool match = !strcmp(new->cmp.string, singleton->cmp.string);
+ expr_destroy(new);
+ if (!match) {
+ expr_destroy(expr);
+ return expr_create_boolean(false);
+ }
+ }
+ break;
+ case EXPR_T_AND:
+ OVS_NOT_REACHED();
+ case EXPR_T_OR:
+ list_insert(&next->node, &new->node);
+ break;
+ case EXPR_T_BOOLEAN:
+ if (!new->boolean) {
+ expr_destroy(expr);
+ return new;
+ }
+ free(new);
+ break;
+ }
+ }
+
+ /* If we have a singleton, then the result is either the singleton itself
+ * (if the ORs allow the singleton) or false. */
+ if (singleton) {
+ LIST_FOR_EACH (sub, node, &expr->andor) {
+ if (sub->type == EXPR_T_OR
+ && !disjunction_matches_string(sub, singleton->cmp.string)) {
+ expr_destroy(expr);
+ return expr_create_boolean(false);
+ }
+ }
+ list_remove(&singleton->node);
+ expr_destroy(expr);
+ return singleton;
+ }
+
+ /* Otherwise the result is the intersection of all of the ORs. */
+ struct sset result = SSET_INITIALIZER(&result);
+ LIST_FOR_EACH_SAFE (sub, next, node, &expr->andor) {
+ struct sset strings = SSET_INITIALIZER(&strings);
+ const struct expr *s;
+ LIST_FOR_EACH (s, node, &sub->andor) {
+ sset_add(&strings, s->cmp.string);
+ }
+ if (sset_is_empty(&result)) {
+ sset_swap(&result, &strings);
+ } else {
+ sset_intersect(&result, &strings);
+ }
+ sset_destroy(&strings);
+
+ if (sset_is_empty(&result)) {
+ expr_destroy(expr);
+ sset_destroy(&result);
+ return expr_create_boolean(false);
+ }
+ }
+
+ expr_destroy(expr);
+ expr = expr_create_andor(EXPR_T_OR);
+
+ const char *string;
+ SSET_FOR_EACH (string, &result) {
+ sub = xmalloc(sizeof *sub);
+ sub->type = EXPR_T_CMP;
+ sub->cmp.symbol = symbol;
+ sub->cmp.string = xstrdup(string);
+ list_push_back(&expr->andor, &sub->node);
+ }
+ sset_destroy(&result);
+ return expr_fix(expr);
+}
+
+/* Implementation of crush_cmps() for expr->type == EXPR_T_AND and a
+ * numeric-typed 'symbol'. */
static struct expr *
-crush_and(struct expr *expr, const struct expr_symbol *symbol)
+crush_and_numeric(struct expr *expr, const struct expr_symbol *symbol)
{
ovs_assert(!list_is_short(&expr->andor));
expr_destroy(expr);
return new;
}
- free(new);
+ expr_destroy(new);
break;
}
}
&sub->cmp.value, &sub->cmp.mask)) {
list_push_back(&or->andor, &sub->node);
} else {
- free(sub);
+ expr_destroy(sub);
}
}
free(disjuncts);
}
static int
-compare_expr(const void *a_, const void *b_)
+compare_cmps_3way(const struct expr *a, const struct expr *b)
+{
+ ovs_assert(a->cmp.symbol == b->cmp.symbol);
+ if (!a->cmp.symbol->width) {
+ return strcmp(a->cmp.string, b->cmp.string);
+ } else {
+ int d = memcmp(&a->cmp.value, &b->cmp.value, sizeof a->cmp.value);
+ if (!d) {
+ d = memcmp(&a->cmp.mask, &b->cmp.mask, sizeof a->cmp.mask);
+ }
+ return d;
+ }
+}
+
+static int
+compare_cmps_cb(const void *a_, const void *b_)
{
const struct expr *const *ap = a_;
const struct expr *const *bp = b_;
const struct expr *a = *ap;
const struct expr *b = *bp;
- int d = memcmp(&a->cmp.value, &b->cmp.value, sizeof a->cmp.value);
- if (!d) {
- d = memcmp(&a->cmp.mask, &b->cmp.mask, sizeof a->cmp.mask);
- }
- return d;
+ return compare_cmps_3way(a, b);
}
/* Implementation of crush_cmps() for expr->type == EXPR_T_OR. */
}
ovs_assert(i == n);
- qsort(subs, n, sizeof *subs, compare_expr);
+ qsort(subs, n, sizeof *subs, compare_cmps_cb);
+ /* Eliminate duplicates. */
list_init(&expr->andor);
list_push_back(&expr->andor, &subs[0]->node);
for (i = 1; i < n; i++) {
struct expr *a = expr_from_node(list_back(&expr->andor));
struct expr *b = subs[i];
- if (memcmp(&a->cmp.value, &b->cmp.value, sizeof a->cmp.value)
- || memcmp(&a->cmp.mask, &b->cmp.mask, sizeof a->cmp.mask)) {
+ if (compare_cmps_3way(a, b)) {
list_push_back(&expr->andor, &b->node);
} else {
- free(b);
+ expr_destroy(b);
}
}
free(subs);
return crush_or(expr, symbol);
case EXPR_T_AND:
- return crush_and(expr, symbol);
+ return (symbol->width
+ ? crush_and_numeric(expr, symbol)
+ : crush_and_string(expr, symbol));
case EXPR_T_CMP:
return expr;
struct expr *a, *b;
LIST_FOR_EACH_SAFE (a, b, node, &expr->andor) {
if (&b->node == &expr->andor
- || a->type != EXPR_T_CMP || b->type != EXPR_T_CMP) {
- } else if (a->cmp.symbol != b->cmp.symbol) {
+ || a->type != EXPR_T_CMP || b->type != EXPR_T_CMP
+ || a->cmp.symbol != b->cmp.symbol) {
continue;
- } else if (mf_subvalue_intersect(&a->cmp.value, &a->cmp.mask,
- &b->cmp.value, &b->cmp.mask,
- &b->cmp.value, &b->cmp.mask)) {
+ } else if (a->cmp.symbol->width
+ ? mf_subvalue_intersect(&a->cmp.value, &a->cmp.mask,
+ &b->cmp.value, &b->cmp.mask,
+ &b->cmp.value, &b->cmp.mask)
+ : !strcmp(a->cmp.string, b->cmp.string)) {
list_remove(&a->node);
expr_destroy(a);
} else {
\f
/* Action parsing helper. */
-static struct expr *
-parse_assignment(struct expr_context *ctx, const struct simap *ports,
- struct ofpbuf *ofpacts)
+/* Expands 'f' repeatedly as long as it has an expansion, that is, as long as
+ * it is a subfield or a predicate. Adds any prerequisites for 'f' to
+ * '*prereqs'.
+ *
+ * If 'rw', verifies that 'f' is a read/write field.
+ *
+ * Returns true if successful, false if an error was encountered (in which case
+ * 'ctx->error' reports the particular error). */
+static bool
+expand_symbol(struct expr_context *ctx, bool rw,
+ struct expr_field *f, struct expr **prereqsp)
{
- 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;
- }
+ const struct expr_symbol *orig_symbol = f->symbol;
- 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;
+ if (f->symbol->expansion && f->symbol->level != EXPR_L_ORDINAL) {
+ expr_error(ctx, "Predicate symbol %s used where lvalue required.",
+ f->symbol->name);
+ return false;
}
- const struct expr_symbol *orig_symbol = f.symbol;
- union expr_constant *c = cs.values;
for (;;) {
/* Accumulate prerequisites. */
- if (f.symbol->prereqs) {
+ 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,
+ e = parse_and_annotate(f->symbol->prereqs, ctx->symtab, &nesting,
&error);
if (error) {
expr_error(ctx, "%s", error);
free(error);
- goto exit_destroy_cs;
+ return false;
}
- prereqs = expr_combine(EXPR_T_AND, prereqs, e);
+ *prereqsp = expr_combine(EXPR_T_AND, *prereqsp, e);
}
/* If there's no expansion, we're done. */
- if (!f.symbol->expansion) {
+ if (!f->symbol->expansion) {
break;
}
/* Expand. */
struct expr_field expansion;
char *error;
- if (!parse_field_from_string(f.symbol->expansion, ctx->symtab,
+ if (!parse_field_from_string(f->symbol->expansion, ctx->symtab,
&expansion, &error)) {
expr_error(ctx, "%s", error);
free(error);
- goto exit_destroy_cs;
+ return false;
}
- f.symbol = expansion.symbol;
- f.ofs += expansion.ofs;
+ f->symbol = expansion.symbol;
+ f->ofs += expansion.ofs;
}
- if (!f.symbol->field->writable) {
+ if (rw && !f->symbol->field->writable) {
expr_error(ctx, "Field %s is not modifiable.", orig_symbol->name);
- goto exit_destroy_cs;
+ return false;
}
- 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);
+ return true;
+}
+
+static void
+mf_subfield_from_expr_field(const struct expr_field *f, struct mf_subfield *sf)
+{
+ sf->field = f->symbol->field;
+ sf->ofs = f->ofs;
+ sf->n_bits = f->n_bits ? f->n_bits : f->symbol->field->n_bits;
+}
+
+static void
+init_stack_action(const struct expr_field *f, struct ofpact_stack *stack)
+{
+ mf_subfield_from_expr_field(f, &stack->subfield);
+}
+
+static struct expr *
+parse_assignment(struct expr_context *ctx, const struct simap *ports,
+ struct ofpbuf *ofpacts)
+{
+ struct expr *prereqs = NULL;
+
+ /* Parse destination and do basic checking. */
+ struct expr_field dst;
+ if (!parse_field(ctx, &dst)) {
+ goto exit;
+ }
+ bool exchange = lexer_match(ctx->lexer, LEX_T_EXCHANGE);
+ if (!exchange && !lexer_match(ctx->lexer, LEX_T_EQUALS)) {
+ expr_syntax_error(ctx, "expecting `='.");
+ goto exit;
+ }
+ const struct expr_symbol *orig_dst = dst.symbol;
+ if (!expand_symbol(ctx, true, &dst, &prereqs)) {
+ goto exit;
+ }
+
+ if (exchange || ctx->lexer->token.type == LEX_T_ID) {
+ struct expr_field src;
+ if (!parse_field(ctx, &src)) {
+ goto exit;
+ }
+ const struct expr_symbol *orig_src = src.symbol;
+ if (!expand_symbol(ctx, exchange, &src, &prereqs)) {
+ goto exit;
+ }
+
+ if ((dst.symbol->width != 0) != (src.symbol->width != 0)) {
+ if (exchange) {
+ expr_error(ctx,
+ "Can't exchange %s field (%s) with %s field (%s).",
+ orig_dst->width ? "integer" : "string",
+ orig_dst->name,
+ orig_src->width ? "integer" : "string",
+ orig_src->name);
+ } else {
+ expr_error(ctx, "Can't assign %s field (%s) to %s field (%s).",
+ orig_src->width ? "integer" : "string",
+ orig_src->name,
+ orig_dst->width ? "integer" : "string",
+ orig_dst->name);
+ }
+ goto exit;
+ }
+
+ if (dst.n_bits != src.n_bits) {
+ if (exchange) {
+ expr_error(ctx,
+ "Can't exchange %d-bit field with %d-bit field.",
+ dst.n_bits, src.n_bits);
+ } else {
+ expr_error(ctx,
+ "Can't assign %d-bit value to %d-bit destination.",
+ src.n_bits, dst.n_bits);
+ }
+ goto exit;
+ } else if (!dst.n_bits
+ && dst.symbol->field->n_bits != src.symbol->field->n_bits) {
+ expr_error(ctx, "String fields %s and %s are incompatible for "
+ "%s.", orig_dst->name, orig_src->name,
+ exchange ? "exchange" : "assignment");
+ goto exit;
}
- 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);
+ if (exchange) {
+ init_stack_action(&src, ofpact_put_STACK_PUSH(ofpacts));
+ init_stack_action(&dst, ofpact_put_STACK_PUSH(ofpacts));
+ init_stack_action(&src, ofpact_put_STACK_POP(ofpacts));
+ init_stack_action(&dst, ofpact_put_STACK_POP(ofpacts));
+ } else {
+ struct ofpact_reg_move *move = ofpact_put_REG_MOVE(ofpacts);
+ mf_subfield_from_expr_field(&src, &move->src);
+ mf_subfield_from_expr_field(&dst, &move->dst);
+ }
} 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);
+ struct expr_constant_set cs;
+ if (!parse_constant_set(ctx, &cs)) {
+ goto exit;
+ }
+
+ if (!type_check(ctx, &dst, &cs)) {
+ goto exit_destroy_cs;
+ }
+ if (cs.in_curlies) {
+ expr_error(ctx, "Assignments require a single value.");
+ goto exit_destroy_cs;
+ }
+
+ union expr_constant *c = cs.values;
+ struct ofpact_set_field *sf = ofpact_put_SET_FIELD(ofpacts);
+ sf->field = dst.symbol->field;
+ if (dst.symbol->width) {
+ mf_subvalue_shift(&c->value, dst.ofs);
+ if (!c->masked) {
+ memset(&c->mask, 0, sizeof c->mask);
+ bitwise_one(&c->mask, sizeof c->mask, dst.ofs, dst.n_bits);
+ } else {
+ mf_subvalue_shift(&c->mask, dst.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_one(&sf->mask, sf->field->n_bytes, 0, sf->field->n_bits);
+
+ /* If the logical input port is being zeroed, clear the OpenFlow
+ * ingress port also, to allow a packet to be sent back to its
+ * origin. */
+ if (!port && sf->field->id == MFF_LOG_INPORT) {
+ sf = ofpact_put_SET_FIELD(ofpacts);
+ sf->field = mf_from_id(MFF_IN_PORT);
+ bitwise_one(&sf->mask,
+ sf->field->n_bytes, 0, sf->field->n_bits);
+ }
+ }
+
+ exit_destroy_cs:
+ expr_constant_set_destroy(&cs);
}
-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(). */
+ * "field = value" or "field1 = field2", or a "exchange" action in the form
+ * "field1 <-> field2", 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,