summaryrefslogtreecommitdiff
path: root/ovn/lib/expr.c
diff options
context:
space:
mode:
authorBen Pfaff <blp@ovn.org>2016-08-03 15:35:07 -0700
committerBen Pfaff <blp@ovn.org>2016-08-15 19:34:47 -0700
commit803a6f3d32541362b1014090100edd747f0aac9b (patch)
treec6fc17a272eb7200abc2d8caa3372c2a1732358a /ovn/lib/expr.c
parent0f8e9c126ba36041d2f446f2a661a7c603087795 (diff)
downloadopenvswitch-803a6f3d32541362b1014090100edd747f0aac9b.tar.gz
expr: New function expr_evaluate().
An upcoming commit will need to evaluate individual expressions outside the context of a classifier. test-ovn already had a function to do this but it wasn't general-purpose, so this commit makes a general-purpose version and adopts it for use in test-ovn as well. Signed-off-by: Ben Pfaff <blp@ovn.org> Acked-by: Ryan Moats <rmoats@us.ibm.com> Acked-by: Justin Pettit <jpettit@ovn.org>
Diffstat (limited to 'ovn/lib/expr.c')
-rw-r--r--ovn/lib/expr.c118
1 files changed, 117 insertions, 1 deletions
diff --git a/ovn/lib/expr.c b/ovn/lib/expr.c
index ad4b4c1b8..433b99d30 100644
--- a/ovn/lib/expr.c
+++ b/ovn/lib/expr.c
@@ -119,6 +119,22 @@ expr_relop_invert(enum expr_relop relop)
default: OVS_NOT_REACHED();
}
}
+
+/* Checks whether 'relop' is true for strcmp()-like 3-way comparison result
+ * 'cmp'. */
+static bool
+expr_relop_test(enum expr_relop relop, int cmp)
+{
+ switch (relop) {
+ case EXPR_R_EQ: return cmp == 0;
+ case EXPR_R_NE: return cmp != 0;
+ case EXPR_R_LT: return cmp < 0;
+ case EXPR_R_LE: return cmp <= 0;
+ case EXPR_R_GT: return cmp > 0;
+ case EXPR_R_GE: return cmp >= 0;
+ default: OVS_NOT_REACHED();
+ }
+}
/* Constructing and manipulating expressions. */
@@ -2451,9 +2467,17 @@ expr_match_add(struct hmap *matches, struct expr_match *match)
hmap_insert(matches, &match->hmap_node, hash);
}
+/* Applies EXPR_T_CMP-typed 'expr' to 'm'. This will only work properly if 'm'
+ * doesn't already match on 'expr->cmp.symbol', because it replaces any
+ * existing match on that symbol instead of intersecting with it.
+ *
+ * If 'expr' is a comparison on a string field, uses 'lookup_port' and 'aux' to
+ * convert the string to a port number. In such a case, if the port can't be
+ * found, returns false. In all other cases, returns true. */
static bool
constrain_match(const struct expr *expr,
- bool (*lookup_port)(const void *aux, const char *port_name,
+ bool (*lookup_port)(const void *aux,
+ const char *port_name,
unsigned int *portp),
const void *aux, struct match *m)
{
@@ -2794,6 +2818,98 @@ expr_is_normalized(const struct expr *expr)
OVS_NOT_REACHED();
}
}
+
+static bool
+expr_evaluate_andor(const struct expr *e, const struct flow *f,
+ bool short_circuit,
+ bool (*lookup_port)(const void *aux, const char *port_name,
+ unsigned int *portp),
+ const void *aux)
+{
+ const struct expr *sub;
+
+ LIST_FOR_EACH (sub, node, &e->andor) {
+ if (expr_evaluate(sub, f, lookup_port, aux) == short_circuit) {
+ return short_circuit;
+ }
+ }
+ return !short_circuit;
+}
+
+static bool
+expr_evaluate_cmp(const struct expr *e, const struct flow *f,
+ bool (*lookup_port)(const void *aux, const char *port_name,
+ unsigned int *portp),
+ const void *aux)
+{
+ const struct expr_symbol *s = e->cmp.symbol;
+ const struct mf_field *field = s->field;
+
+ int cmp;
+ if (e->cmp.symbol->width) {
+ int n_bytes = field->n_bytes;
+ const uint8_t *cst = &e->cmp.value.u8[sizeof e->cmp.value - n_bytes];
+ const uint8_t *mask = &e->cmp.mask.u8[sizeof e->cmp.mask - n_bytes];
+
+ /* Get field value and mask off undesired bits. */
+ union mf_value value;
+ mf_get_value(field, f, &value);
+ for (int i = 0; i < field->n_bytes; i++) {
+ value.b[i] &= mask[i];
+ }
+
+ /* Compare against constant. */
+ cmp = memcmp(&value, cst, n_bytes);
+ } else {
+ /* Get field value. */
+ struct mf_subfield sf = { .field = field, .ofs = 0,
+ .n_bits = field->n_bits };
+ uint64_t value = mf_get_subfield(&sf, f);
+
+ /* Get constant. */
+ unsigned int cst;
+ if (!lookup_port(aux, e->cmp.string, &cst)) {
+ return false;
+ }
+
+ /* Compare. */
+ cmp = value < cst ? -1 : value > cst;
+ }
+
+ return expr_relop_test(e->cmp.relop, cmp);
+}
+
+/* Evaluates 'e' against microflow 'uflow' and returns the result.
+ *
+ * 'lookup_port' must be a function to map from a port name to a port number
+ * and 'aux' auxiliary data to pass to it; see expr_to_matches() for more
+ * details.
+ *
+ * This isn't particularly fast. For performance-sensitive tasks, use
+ * expr_to_matches() and the classifier. */
+bool
+expr_evaluate(const struct expr *e, const struct flow *uflow,
+ bool (*lookup_port)(const void *aux, const char *port_name,
+ unsigned int *portp),
+ const void *aux)
+{
+ switch (e->type) {
+ case EXPR_T_CMP:
+ return expr_evaluate_cmp(e, uflow, lookup_port, aux);
+
+ case EXPR_T_AND:
+ return expr_evaluate_andor(e, uflow, false, lookup_port, aux);
+
+ case EXPR_T_OR:
+ return expr_evaluate_andor(e, uflow, true, lookup_port, aux);
+
+ case EXPR_T_BOOLEAN:
+ return e->boolean;
+
+ default:
+ OVS_NOT_REACHED();
+ }
+}
/* Action parsing helper. */