/*
- * Copyright (c) 2015 Nicira, Inc.
+ * Copyright (c) 2015, 2016 Nicira, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
*/
#include <config.h>
+#include "byte-order.h"
#include "expr.h"
-#include "dynamic-string.h"
-#include "json.h"
+#include "openvswitch/json.h"
#include "lex.h"
-#include "match.h"
-#include "ofp-actions.h"
-#include "shash.h"
+#include "logical-fields.h"
+#include "openvswitch/dynamic-string.h"
+#include "openvswitch/match.h"
+#include "openvswitch/ofp-actions.h"
+#include "openvswitch/vlog.h"
+#include "openvswitch/shash.h"
#include "simap.h"
#include "sset.h"
-#include "openvswitch/vlog.h"
+#include "util.h"
VLOG_DEFINE_THIS_MODULE(expr);
\f
{
struct expr *e = xmalloc(sizeof *e);
e->type = type;
- list_init(&e->andor);
+ ovs_list_init(&e->andor);
return e;
}
return a;
} else if (a->type == type) {
if (b->type == type) {
- list_splice(&a->andor, b->andor.next, &b->andor);
+ ovs_list_splice(&a->andor, b->andor.next, &b->andor);
free(b);
} else {
- list_push_back(&a->andor, &b->node);
+ ovs_list_push_back(&a->andor, &b->node);
}
return a;
} else if (b->type == type) {
- list_push_front(&b->andor, &a->node);
+ ovs_list_push_front(&b->andor, &a->node);
return b;
} else {
struct expr *e = expr_create_andor(type);
- list_push_back(&e->andor, &a->node);
- list_push_back(&e->andor, &b->node);
+ ovs_list_push_back(&e->andor, &a->node);
+ ovs_list_push_back(&e->andor, &b->node);
return e;
}
}
if (andor->type == EXPR_T_AND) {
/* Conjunction junction, what's your function? */
}
- list_splice(&before->node, new->andor.next, &new->andor);
+ ovs_list_splice(&before->node, new->andor.next, &new->andor);
free(new);
} else {
- list_insert(&before->node, &new->node);
+ ovs_list_insert(&before->node, &new->node);
}
}
expr_destroy(expr);
return expr_create_boolean(short_circuit);
} else {
- list_remove(&sub->node);
+ ovs_list_remove(&sub->node);
expr_destroy(sub);
}
}
}
- if (list_is_short(&expr->andor)) {
- if (list_is_empty(&expr->andor)) {
+ if (ovs_list_is_short(&expr->andor)) {
+ if (ovs_list_is_empty(&expr->andor)) {
free(expr);
return expr_create_boolean(!short_circuit);
} else {
- sub = expr_from_node(list_front(&expr->andor));
+ sub = expr_from_node(ovs_list_front(&expr->andor));
free(expr);
return sub;
}
\f
/* Parsing. */
-/* Type of a "union expr_constant" or "struct expr_constant_set". */
-enum expr_constant_type {
- EXPR_C_INTEGER,
- EXPR_C_STRING
-};
-
-/* A string or integer constant (one must know which from context). */
-union expr_constant {
- /* Integer constant.
- *
- * The width of a constant isn't always clear, e.g. if you write "1",
- * there's no way to tell whether you mean for that to be a 1-bit constant
- * or a 128-bit constant or somewhere in between. */
- struct {
- union mf_subvalue value;
- union mf_subvalue mask; /* Only initialized if 'masked'. */
- bool masked;
-
- enum lex_format format; /* From the constant's lex_token. */
- };
-
- /* Null-terminated string constant. */
- char *string;
-};
-
-/* A collection of "union expr_constant"s of the same type. */
-struct expr_constant_set {
- union expr_constant *values; /* Constants. */
- size_t n_values; /* Number of constants. */
- enum expr_constant_type type; /* Type of the constants. */
- bool in_curlies; /* Whether the constants were in {}. */
-};
-
-/* A reference to a symbol or a subfield of a symbol.
- *
- * For string fields, ofs and n_bits are 0. */
-struct expr_field {
- const struct expr_symbol *symbol; /* The symbol. */
- int ofs; /* Starting bit offset. */
- int n_bits; /* Number of bits. */
-};
-
/* Context maintained during expr_parse(). */
struct expr_context {
struct lexer *lexer; /* Lexer for pulling more tokens. */
const struct shash *symtab; /* Symbol table. */
+ const struct shash *macros; /* Table of macros. */
char *error; /* Error, if any, otherwise NULL. */
bool not; /* True inside odd number of NOT operators. */
};
struct expr *expr_parse__(struct expr_context *);
static void expr_not(struct expr *);
-static void expr_constant_set_destroy(struct expr_constant_set *);
static bool parse_field(struct expr_context *, struct expr_field *);
static bool
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
}
}
+static bool
+parse_macros(struct expr_context *ctx, struct expr_constant_set *cs,
+ size_t *allocated_values)
+{
+ struct expr_constant_set *addr_set
+ = shash_find_data(ctx->macros, ctx->lexer->token.s);
+ if (!addr_set) {
+ expr_syntax_error(ctx, "expecting address set name.");
+ return false;
+ }
+
+ if (!assign_constant_set_type(ctx, cs, EXPR_C_INTEGER)) {
+ return false;
+ }
+
+ size_t n_values = cs->n_values + addr_set->n_values;
+ if (n_values >= *allocated_values) {
+ cs->values = xrealloc(cs->values, n_values * sizeof *cs->values);
+ *allocated_values = n_values;
+ }
+ for (size_t i = 0; i < addr_set->n_values; i++) {
+ cs->values[cs->n_values++] = addr_set->values[i];
+ }
+
+ return true;
+}
+
static bool
parse_constant(struct expr_context *ctx, struct expr_constant_set *cs,
size_t *allocated_values)
}
lexer_get(ctx->lexer);
return true;
+ } else if (ctx->lexer->token.type == LEX_T_MACRO) {
+ if (!parse_macros(ctx, cs, allocated_values)) {
+ return false;
+ }
+ lexer_get(ctx->lexer);
+ return true;
} else {
expr_syntax_error(ctx, "expecting constant.");
return false;
return ok;
}
-static void
+void
expr_constant_set_destroy(struct expr_constant_set *cs)
{
if (cs) {
}
}
+/* Adds a macro named 'name' to 'macros', replacing any existing macro with the
+ * given name. */
+void
+expr_macros_add(struct shash *macros, const char *name,
+ const char *const *values, size_t n_values)
+{
+ /* Replace any existing entry for this name. */
+ expr_macros_remove(macros, name);
+
+ struct expr_constant_set *cs = xzalloc(sizeof *cs);
+ cs->type = EXPR_C_INTEGER;
+ cs->in_curlies = true;
+ cs->n_values = 0;
+ cs->values = xmalloc(n_values * sizeof *cs->values);
+ for (size_t i = 0; i < n_values; i++) {
+ /* Use the lexer to convert each macro into the proper
+ * integer format. */
+ struct lexer lex;
+ lexer_init(&lex, values[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",
+ values[i], lex.token.type);
+ } else {
+ union expr_constant *c = &cs->values[cs->n_values++];
+ 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);
+ }
+
+ shash_add(macros, name, cs);
+}
+
+void
+expr_macros_remove(struct shash *macros, const char *name)
+{
+ struct expr_constant_set *cs = shash_find_and_delete(macros, name);
+ if (cs) {
+ expr_constant_set_destroy(cs);
+ free(cs);
+ }
+}
+
+/* Destroy all contents of 'macros'. */
+void
+expr_macros_destroy(struct shash *macros)
+{
+ struct shash_node *node, *next;
+
+ SHASH_FOR_EACH_SAFE (node, next, macros) {
+ struct expr_constant_set *cs = node->data;
+
+ shash_delete(macros, node);
+ expr_constant_set_destroy(cs);
+ }
+}
+
static struct expr *
expr_parse_primary(struct expr_context *ctx, bool *atomic)
{
* 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 *macros, char **errorp)
{
- struct expr_context ctx;
-
- ctx.lexer = lexer;
- ctx.symtab = symtab;
- ctx.error = NULL;
- ctx.not = false;
-
+ struct expr_context ctx = { .lexer = lexer,
+ .symtab = symtab,
+ .macros = macros };
struct expr *e = expr_parse__(&ctx);
*errorp = ctx.error;
ovs_assert((ctx.error != NULL) != (e != NULL));
/* 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 *macros, 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, macros, errorp);
if (!*errorp && lexer.token.type != LEX_T_END) {
*errorp = xstrdup("Extra tokens at end of input.");
expr_destroy(expr);
lexer_init(&lexer, s);
lexer_get(&lexer);
- struct expr_context ctx;
- ctx.lexer = &lexer;
- ctx.symtab = symtab;
- ctx.error = NULL;
- ctx.not = false;
-
+ struct expr_context ctx = { .lexer = &lexer, .symtab = symtab };
bool ok = parse_field(&ctx, field);
if (!ok) {
*errorp = ctx.error;
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;
LIST_FOR_EACH (sub, node, &expr->andor) {
struct expr *new_sub = expr_clone(sub);
- list_push_back(&new->andor, &new_sub->node);
+ ovs_list_push_back(&new->andor, &new_sub->node);
}
return new;
}
case EXPR_T_AND:
case EXPR_T_OR:
LIST_FOR_EACH_SAFE (sub, next, node, &expr->andor) {
- list_remove(&sub->node);
+ ovs_list_remove(&sub->node);
expr_destroy(sub);
}
break;
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);
}
struct annotation_nesting an;
an.symbol = symbol;
- list_push_back(nesting, &an.node);
+ ovs_list_push_back(nesting, &an.node);
struct expr *prereqs = NULL;
if (symbol->prereqs) {
}
}
- list_remove(&an.node);
+ ovs_list_remove(&an.node);
return prereqs ? expr_combine(EXPR_T_AND, expr, prereqs) : expr;
error:
expr_destroy(expr);
expr_destroy(prereqs);
- list_remove(&an.node);
+ ovs_list_remove(&an.node);
return NULL;
}
struct expr *sub, *next;
LIST_FOR_EACH_SAFE (sub, next, node, &expr->andor) {
- list_remove(&sub->node);
+ ovs_list_remove(&sub->node);
struct expr *new_sub = expr_annotate__(sub, symtab,
nesting, errorp);
if (!new_sub) {
* - 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)
{
case EXPR_T_AND:
case EXPR_T_OR:
LIST_FOR_EACH_SAFE (sub, next, node, &expr->andor) {
- list_remove(&sub->node);
+ ovs_list_remove(&sub->node);
expr_insert_andor(expr, next, expr_simplify(sub));
}
return expr_fix(expr);
enum expr_type b_type = a->expr->type;
return a_type < b_type ? -1 : a_type > b_type;
} else if (a->type == EXPR_T_AND || a->type == EXPR_T_OR) {
- size_t a_len = list_size(&a->expr->andor);
- size_t b_len = list_size(&b->expr->andor);
+ size_t a_len = ovs_list_size(&a->expr->andor);
+ size_t b_len = ovs_list_size(&b->expr->andor);
return a_len < b_len ? -1 : a_len > b_len;
} else {
return 0;
static struct expr *
crush_and_string(struct expr *expr, const struct expr_symbol *symbol)
{
- ovs_assert(!list_is_short(&expr->andor));
+ ovs_assert(!ovs_list_is_short(&expr->andor));
struct expr *singleton = NULL;
* 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);
+ ovs_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);
+ ovs_list_insert(&next->node, &new->node);
singleton = new;
} else {
bool match = !strcmp(new->cmp.string, singleton->cmp.string);
case EXPR_T_AND:
OVS_NOT_REACHED();
case EXPR_T_OR:
- list_insert(&next->node, &new->node);
+ ovs_list_insert(&next->node, &new->node);
break;
case EXPR_T_BOOLEAN:
if (!new->boolean) {
return expr_create_boolean(false);
}
}
- list_remove(&singleton->node);
+ ovs_list_remove(&singleton->node);
expr_destroy(expr);
return singleton;
}
sub->type = EXPR_T_CMP;
sub->cmp.symbol = symbol;
sub->cmp.string = xstrdup(string);
- list_push_back(&expr->andor, &sub->node);
+ ovs_list_push_back(&expr->andor, &sub->node);
}
+ sset_destroy(&result);
return expr_fix(expr);
}
static struct expr *
crush_and_numeric(struct expr *expr, const struct expr_symbol *symbol)
{
- ovs_assert(!list_is_short(&expr->andor));
+ ovs_assert(!ovs_list_is_short(&expr->andor));
union mf_subvalue value, mask;
memset(&value, 0, sizeof value);
struct expr *sub, *next = NULL;
LIST_FOR_EACH_SAFE (sub, next, node, &expr->andor) {
- list_remove(&sub->node);
+ ovs_list_remove(&sub->node);
struct expr *new = crush_cmps(sub, symbol);
switch (new->type) {
case EXPR_T_CMP:
case EXPR_T_AND:
OVS_NOT_REACHED();
case EXPR_T_OR:
- list_insert(&next->node, &new->node);
+ ovs_list_insert(&next->node, &new->node);
break;
case EXPR_T_BOOLEAN:
if (!new->boolean) {
expr_destroy(expr);
return new;
}
- free(new);
+ expr_destroy(new);
break;
}
}
- if (list_is_empty(&expr->andor)) {
+ if (ovs_list_is_empty(&expr->andor)) {
if (is_all_zeros(&mask, sizeof mask)) {
expr_destroy(expr);
return expr_create_boolean(true);
expr_destroy(expr);
return cmp;
}
- } else if (list_is_short(&expr->andor)) {
+ } else if (ovs_list_is_short(&expr->andor)) {
/* Transform "a && (b || c || d)" into "ab || ac || ad" where "ab" is
* computed as "a && b", etc. */
- struct expr *disjuncts = expr_from_node(list_pop_front(&expr->andor));
+ struct expr *disjuncts = expr_from_node(ovs_list_pop_front(&expr->andor));
struct expr *or;
or = xmalloc(sizeof *or);
or->type = EXPR_T_OR;
- list_init(&or->andor);
+ ovs_list_init(&or->andor);
ovs_assert(disjuncts->type == EXPR_T_OR);
LIST_FOR_EACH_SAFE (sub, next, node, &disjuncts->andor) {
ovs_assert(sub->type == EXPR_T_CMP);
- list_remove(&sub->node);
+ ovs_list_remove(&sub->node);
if (mf_subvalue_intersect(&value, &mask,
&sub->cmp.value, &sub->cmp.mask,
&sub->cmp.value, &sub->cmp.mask)) {
- list_push_back(&or->andor, &sub->node);
+ ovs_list_push_back(&or->andor, &sub->node);
} else {
- free(sub);
+ expr_destroy(sub);
}
}
free(disjuncts);
free(expr);
- if (list_is_empty(&or->andor)) {
+ if (ovs_list_is_empty(&or->andor)) {
free(or);
return expr_create_boolean(false);
- } else if (list_is_short(&or->andor)) {
- struct expr *cmp = expr_from_node(list_pop_front(&or->andor));
+ } else if (ovs_list_is_short(&or->andor)) {
+ struct expr *cmp = expr_from_node(ovs_list_pop_front(&or->andor));
free(or);
return cmp;
} else {
} else {
/* Transform "x && (a0 || a1) && (b0 || b1) && ..." into
* "(xa0b0 || xa0b1 || xa1b0 || xa1b1) && ...". */
- struct expr *as = expr_from_node(list_pop_front(&expr->andor));
- struct expr *bs = expr_from_node(list_pop_front(&expr->andor));
+ struct expr *as = expr_from_node(ovs_list_pop_front(&expr->andor));
+ struct expr *bs = expr_from_node(ovs_list_pop_front(&expr->andor));
struct expr *new = NULL;
struct expr *or;
or = xmalloc(sizeof *or);
or->type = EXPR_T_OR;
- list_init(&or->andor);
+ ovs_list_init(&or->andor);
struct expr *a;
LIST_FOR_EACH (a, node, &as->andor) {
if (mf_subvalue_intersect(&a_value, &a_mask,
&b->cmp.value, &b->cmp.mask,
&new->cmp.value, &new->cmp.mask)) {
- list_push_back(&or->andor, &new->node);
+ ovs_list_push_back(&or->andor, &new->node);
new = NULL;
}
}
expr_destroy(bs);
free(new);
- if (list_is_empty(&or->andor)) {
+ if (ovs_list_is_empty(&or->andor)) {
expr_destroy(expr);
free(or);
return expr_create_boolean(false);
- } else if (list_is_short(&or->andor)) {
- struct expr *cmp = expr_from_node(list_pop_front(&or->andor));
+ } else if (ovs_list_is_short(&or->andor)) {
+ struct expr *cmp = expr_from_node(ovs_list_pop_front(&or->andor));
free(or);
- if (list_is_empty(&expr->andor)) {
+ if (ovs_list_is_empty(&expr->andor)) {
expr_destroy(expr);
return crush_cmps(cmp, symbol);
} else {
return crush_cmps(expr_combine(EXPR_T_AND, cmp, expr), symbol);
}
- } else if (!list_is_empty(&expr->andor)) {
+ } else if (!ovs_list_is_empty(&expr->andor)) {
struct expr *e = expr_combine(EXPR_T_AND, or, expr);
- ovs_assert(!list_is_short(&e->andor));
+ ovs_assert(!ovs_list_is_short(&e->andor));
return crush_cmps(e, symbol);
} else {
expr_destroy(expr);
* OR-expression entirely; if so, return the result. Otherwise, 'expr'
* is now a disjunction of cmps over the same symbol. */
LIST_FOR_EACH_SAFE (sub, next, node, &expr->andor) {
- list_remove(&sub->node);
+ ovs_list_remove(&sub->node);
expr_insert_andor(expr, next, crush_cmps(sub, symbol));
}
expr = expr_fix(expr);
}
/* Sort subexpressions by value and mask, to bring together duplicates. */
- size_t n = list_size(&expr->andor);
+ size_t n = ovs_list_size(&expr->andor);
struct expr **subs = xmalloc(n * sizeof *subs);
size_t i = 0;
qsort(subs, n, sizeof *subs, compare_cmps_cb);
/* Eliminate duplicates. */
- list_init(&expr->andor);
- list_push_back(&expr->andor, &subs[0]->node);
+ ovs_list_init(&expr->andor);
+ ovs_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 *a = expr_from_node(ovs_list_back(&expr->andor));
struct expr *b = subs[i];
if (compare_cmps_3way(a, b)) {
- list_push_back(&expr->andor, &b->node);
+ ovs_list_push_back(&expr->andor, &b->node);
} else {
- free(b);
+ expr_destroy(b);
}
}
free(subs);
static struct expr *
expr_sort(struct expr *expr)
{
- size_t n = list_size(&expr->andor);
+ size_t n = ovs_list_size(&expr->andor);
struct expr_sort *subs = xmalloc(n * sizeof *subs);
struct expr *sub;
size_t i;
qsort(subs, n, sizeof *subs, compare_expr_sort);
- list_init(&expr->andor);
+ ovs_list_init(&expr->andor);
for (int i = 0; i < n; ) {
if (subs[i].relop) {
int j;
combined = expr_combine(EXPR_T_AND, combined,
subs[k].expr);
}
- ovs_assert(!list_is_short(&combined->andor));
+ ovs_assert(!ovs_list_is_short(&combined->andor));
crushed = crush_cmps(combined, subs[i].relop);
}
if (crushed->type == EXPR_T_BOOLEAN) {
&b->cmp.value, &b->cmp.mask,
&b->cmp.value, &b->cmp.mask)
: !strcmp(a->cmp.string, b->cmp.string)) {
- list_remove(&a->node);
+ ovs_list_remove(&a->node);
expr_destroy(a);
} else {
expr_destroy(expr);
return expr_create_boolean(false);
}
}
- if (list_is_short(&expr->andor)) {
- struct expr *sub = expr_from_node(list_front(&expr->andor));
+ if (ovs_list_is_short(&expr->andor)) {
+ struct expr *sub = expr_from_node(ovs_list_front(&expr->andor));
free(expr);
return sub;
}
LIST_FOR_EACH (p, node, &term->andor) {
struct expr *new = expr_clone(p);
- list_push_back(&and->andor, &new->node);
+ ovs_list_push_back(&and->andor, &new->node);
}
} else {
struct expr *new = expr_clone(term);
- list_push_back(&and->andor, &new->node);
+ ovs_list_push_back(&and->andor, &new->node);
}
}
- list_push_back(&or->andor, &and->node);
+ ovs_list_push_back(&or->andor, &and->node);
}
expr_destroy(expr);
return expr_normalize_or(or);
LIST_FOR_EACH_SAFE (sub, next, node, &expr->andor) {
if (sub->type == EXPR_T_AND) {
- list_remove(&sub->node);
+ ovs_list_remove(&sub->node);
struct expr *new = expr_normalize_and(sub);
if (new->type == EXPR_T_BOOLEAN) {
ovs_assert(sub->type == EXPR_T_CMP);
}
}
- if (list_is_empty(&expr->andor)) {
+ if (ovs_list_is_empty(&expr->andor)) {
free(expr);
return expr_create_boolean(false);
}
- if (list_is_short(&expr->andor)) {
- struct expr *sub = expr_from_node(list_pop_front(&expr->andor));
+ if (ovs_list_is_short(&expr->andor)) {
+ struct expr *sub = expr_from_node(ovs_list_pop_front(&expr->andor));
free(expr);
return sub;
}
}
static bool
-constrain_match(const struct expr *expr, const struct simap *ports,
- struct match *m)
+constrain_match(const struct expr *expr,
+ bool (*lookup_port)(const void *aux, const char *port_name,
+ unsigned int *portp),
+ const void *aux, struct match *m)
{
ovs_assert(expr->type == EXPR_T_CMP);
if (expr->cmp.symbol->width) {
mf_mask_subfield(expr->cmp.symbol->field, &expr->cmp.value,
&expr->cmp.mask, m);
} else {
- const struct simap_node *node;
- node = ports ? simap_find(ports, expr->cmp.string) : NULL;
- if (!node) {
+ unsigned int port;
+ if (!lookup_port(aux, expr->cmp.string, &port)) {
return false;
}
union mf_subvalue x;
memset(&x, 0, sizeof x);
- x.integer = htonll(node->data);
+ x.integer = htonll(port);
mf_write_subfield(&sf, &x, m);
}
}
static bool
-add_disjunction(const struct expr *or, const struct simap *ports,
+add_disjunction(const struct expr *or,
+ bool (*lookup_port)(const void *aux, const char *port_name,
+ unsigned int *portp),
+ const void *aux,
struct match *m, uint8_t clause, uint8_t n_clauses,
uint32_t conj_id, struct hmap *matches)
{
LIST_FOR_EACH (sub, node, &or->andor) {
struct expr_match *match = expr_match_new(m, clause, n_clauses,
conj_id);
- if (constrain_match(sub, ports, &match->match)) {
+ if (constrain_match(sub, lookup_port, aux, &match->match)) {
expr_match_add(matches, match);
n++;
} else {
}
static void
-add_conjunction(const struct expr *and, const struct simap *ports,
- uint32_t *n_conjsp, struct hmap *matches)
+add_conjunction(const struct expr *and,
+ bool (*lookup_port)(const void *aux, const char *port_name,
+ unsigned int *portp),
+ const void *aux, uint32_t *n_conjsp, struct hmap *matches)
{
struct match match;
int n_clauses = 0;
LIST_FOR_EACH (sub, node, &and->andor) {
switch (sub->type) {
case EXPR_T_CMP:
- if (!constrain_match(sub, ports, &match)) {
+ if (!constrain_match(sub, lookup_port, aux, &match)) {
return;
}
break;
} else if (n_clauses == 1) {
LIST_FOR_EACH (sub, node, &and->andor) {
if (sub->type == EXPR_T_OR) {
- add_disjunction(sub, ports, &match, 0, 0, 0, matches);
+ add_disjunction(sub, lookup_port, aux, &match, 0, 0, 0,
+ matches);
}
}
} else {
(*n_conjsp)++;
LIST_FOR_EACH (sub, node, &and->andor) {
if (sub->type == EXPR_T_OR) {
- if (!add_disjunction(sub, ports, &match, clause++,
+ if (!add_disjunction(sub, lookup_port, aux, &match, clause++,
n_clauses, *n_conjsp, matches)) {
/* This clause can't ever match, so we might as well skip
* adding the other clauses--the overall disjunctive flow
}
static void
-add_cmp_flow(const struct expr *cmp, const struct simap *ports,
- struct hmap *matches)
+add_cmp_flow(const struct expr *cmp,
+ bool (*lookup_port)(const void *aux, const char *port_name,
+ unsigned int *portp),
+ const void *aux, struct hmap *matches)
{
struct expr_match *m = expr_match_new(NULL, 0, 0, 0);
- if (constrain_match(cmp, ports, &m->match)) {
+ if (constrain_match(cmp, lookup_port, aux, &m->match)) {
expr_match_add(matches, m);
} else {
free(m);
* caller should remap the conj_id and add the OpenFlow flow with its
* desired actions.
*
- * 'ports' must be a map from strings (presumably names of ports) to integers.
- * Any comparisons against string fields in 'expr' are translated into integers
- * through this map. A comparison against a string that is not in 'ports' acts
- * like a Boolean "false"; that is, it will always fail to match. For a simple
- * expression, this means that the overall expression always fails to match,
- * but an expression with a disjunction on the string field might still match
- * on other port names.
+ * 'lookup_port' must be a function to map from a port name to a port number.
+ * When successful, 'lookup_port' stores the port number into '*portp' and
+ * returns true; when there is no port by the given name, it returns false.
+ * 'aux' is passed to 'lookup_port' as auxiliary data. Any comparisons against
+ * string fields in 'expr' are translated into integers through this function.
+ * A comparison against a string that is not in 'ports' acts like a Boolean
+ * "false"; that is, it will always fail to match. For a simple expression,
+ * this means that the overall expression always fails to match, but an
+ * expression with a disjunction on the string field might still match on other
+ * port names.
*
* (This treatment of string fields might be too simplistic in general, but it
* seems reasonable for now when string fields are used only for ports.) */
uint32_t
-expr_to_matches(const struct expr *expr, const struct simap *ports,
- struct hmap *matches)
+expr_to_matches(const struct expr *expr,
+ bool (*lookup_port)(const void *aux, const char *port_name,
+ unsigned int *portp),
+ const void *aux, struct hmap *matches)
{
uint32_t n_conjs = 0;
hmap_init(matches);
switch (expr->type) {
case EXPR_T_CMP:
- add_cmp_flow(expr, ports, matches);
+ add_cmp_flow(expr, lookup_port, aux, matches);
break;
case EXPR_T_AND:
- add_conjunction(expr, ports, &n_conjs, matches);
+ add_conjunction(expr, lookup_port, aux, &n_conjs, matches);
break;
case EXPR_T_OR:
struct expr *sub;
LIST_FOR_EACH (sub, node, &expr->andor) {
- add_cmp_flow(sub, ports, matches);
+ add_cmp_flow(sub, lookup_port, aux, matches);
}
} else {
struct expr *sub;
LIST_FOR_EACH (sub, node, &expr->andor) {
if (sub->type == EXPR_T_AND) {
- add_conjunction(sub, ports, &n_conjs, matches);
+ add_conjunction(sub, lookup_port, aux, &n_conjs, matches);
} else {
- add_cmp_flow(sub, ports, matches);
+ add_cmp_flow(sub, lookup_port, aux, matches);
}
}
}
void
expr_matches_destroy(struct hmap *matches)
{
- struct expr_match *m, *n;
+ struct expr_match *m;
- HMAP_FOR_EACH_SAFE (m, n, hmap_node, matches) {
- hmap_remove(matches, &m->hmap_node);
+ HMAP_FOR_EACH_POP (m, hmap_node, matches) {
free(m->conjunctions);
free(m);
}
case EXPR_T_AND:
case EXPR_T_OR:
- if (list_is_short(&expr->andor)) {
+ if (ovs_list_is_short(&expr->andor)) {
return false;
}
LIST_FOR_EACH (sub, node, &expr->andor) {
\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 char * OVS_WARN_UNUSED_RESULT
+parse_assignment(struct lexer *lexer, struct expr_field *dst,
+ const struct shash *symtab, bool exchange,
+ bool (*lookup_port)(const void *aux, const char *port_name,
+ unsigned int *portp),
+ const void *aux, struct ofpbuf *ofpacts,
+ struct expr **prereqsp)
+{
+ struct expr_context ctx = { .lexer = lexer, .symtab = symtab };
+ struct expr *prereqs = NULL;
+
+ /* Parse destination and do basic checking. */
+ 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;
+ if (!lookup_port(aux, c->string, &port)) {
+ port = 0;
+ }
+ 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;
+ if (ctx.error) {
+ expr_destroy(prereqs);
+ prereqs = NULL;
+ }
+ *prereqsp = prereqs;
+ return ctx.error;
}
/* 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,
+ * "field = value" or "field = field2" into 'ofpacts'. The caller must have
+ * already parsed and skipped the left-hand side "field =" and pass in the
+ * field as 'dst'. Other parameters and return value match those for
+ * actions_parse(). */
+char * OVS_WARN_UNUSED_RESULT
+expr_parse_assignment(struct lexer *lexer, struct expr_field *dst,
+ const struct shash *symtab,
+ bool (*lookup_port)(const void *aux,
+ const char *port_name,
+ unsigned int *portp),
+ const void *aux,
struct ofpbuf *ofpacts, struct expr **prereqsp)
{
- struct expr_context ctx;
- ctx.lexer = lexer;
- ctx.symtab = symtab;
- ctx.error = NULL;
- ctx.not = false;
+ return parse_assignment(lexer, dst, symtab, false, lookup_port, aux,
+ ofpacts, prereqsp);
+}
- struct expr *prereqs = parse_assignment(&ctx, ports, ofpacts);
- if (ctx.error) {
+/* A helper for actions_parse(), to parse an OVN exchange action in the form
+ * "field1 <-> field2" into 'ofpacts'. The caller must have already parsed and
+ * skipped the left-hand side "field1 <->" and pass in 'field1'. Other
+ * parameters and return value match those for actions_parse(). */
+char * OVS_WARN_UNUSED_RESULT
+expr_parse_exchange(struct lexer *lexer, struct expr_field *field,
+ const struct shash *symtab,
+ bool (*lookup_port)(const void *aux,
+ const char *port_name,
+ unsigned int *portp),
+ const void *aux,
+ struct ofpbuf *ofpacts, struct expr **prereqsp)
+{
+ return parse_assignment(lexer, field, symtab, true, lookup_port, aux,
+ ofpacts, prereqsp);
+}
+
+/* Parses a field or subfield from 'lexer' into 'field', obtaining field names
+ * from 'symtab'. Returns NULL if successful, otherwise an error message owned
+ * by the caller. */
+char * OVS_WARN_UNUSED_RESULT
+expr_parse_field(struct lexer *lexer, const struct shash *symtab,
+ struct expr_field *field)
+{
+ struct expr_context ctx = { .lexer = lexer, .symtab = symtab };
+ if (!parse_field(&ctx, field)) {
+ memset(field, 0, sizeof *field);
+ }
+ return ctx.error;
+}
+
+/* Takes 'field', which was presumably parsed by expr_parse_field(), and
+ * converts it into mf_subfield 'sf' and a set of prerequisites in '*prereqsp'.
+ *
+ * 'n_bits' specifies the number of bits that the field must have, and 0
+ * indicates a string field; reports an error if 'field' has a different type
+ * or width. If 'rw' is true, it is an error if 'field' is read-only. Uses
+ * 'symtab 'for expanding references and 'lexer' for error reporting.
+ *
+ * Returns NULL if successful, otherwise an error message owned by the
+ * caller. */
+char * OVS_WARN_UNUSED_RESULT
+expr_expand_field(struct lexer *lexer, const struct shash *symtab,
+ const struct expr_field *orig_field, int n_bits, bool rw,
+ struct mf_subfield *sf, struct expr **prereqsp)
+{
+ struct expr_context ctx = { .lexer = lexer, .symtab = symtab };
+ struct expr *prereqs = NULL;
+
+ struct expr_field field = *orig_field;
+ if (!expand_symbol(&ctx, rw, &field, &prereqs)) {
+ goto exit;
+ }
+ ovs_assert(field.n_bits == orig_field->n_bits);
+
+ if (n_bits != field.n_bits) {
+ if (n_bits && field.n_bits) {
+ expr_error(&ctx, "Cannot use %d-bit field %s[%d..%d] "
+ "where %d-bit field is required.",
+ orig_field->n_bits, orig_field->symbol->name,
+ orig_field->ofs,
+ orig_field->ofs + orig_field->n_bits - 1, n_bits);
+ } else if (n_bits) {
+ expr_error(&ctx, "Cannot use string field %s where numeric "
+ "field is required.",
+ orig_field->symbol->name);
+ } else {
+ expr_error(&ctx, "Cannot use numeric field %s where string "
+ "field is required.",
+ orig_field->symbol->name);
+ }
+ }
+
+exit:
+ if (!ctx.error) {
+ mf_subfield_from_expr_field(&field, sf);
+ *prereqsp = prereqs;
+ } else {
+ memset(sf, 0, sizeof *sf);
expr_destroy(prereqs);
- prereqs = NULL;
+ *prereqsp = NULL;
}
- *prereqsp = prereqs;
+ return ctx.error;
+}
+
+char * OVS_WARN_UNUSED_RESULT
+expr_parse_constant_set(struct lexer *lexer, const struct shash *symtab,
+ struct expr_constant_set *cs)
+{
+ struct expr_context ctx = { .lexer = lexer, .symtab = symtab };
+ parse_constant_set(&ctx, cs);
return ctx.error;
}