[ovs-dev] [PATCH v2 20/21] expr: New function expr_parse_microflow().

Ben Pfaff blp at ovn.org
Mon Aug 8 16:14:31 UTC 2016


This allows "ovstest test-ovn evaluate-expr" work with arbitrary
microflows rather than just a few restricted variables, but the main point
is to enable the upcoming "ovn-trace" utility to accept arbitrary
microflows in a format that seems reasonable for OVN.

Signed-off-by: Ben Pfaff <blp at ovn.org>
---
 include/ovn/expr.h |   8 ++++
 ovn/lib/expr.c     | 121 +++++++++++++++++++++++++++++++++++++++++++++++++++++
 tests/test-ovn.c   |  34 ++++++++-------
 3 files changed, 147 insertions(+), 16 deletions(-)

diff --git a/include/ovn/expr.h b/include/ovn/expr.h
index fa603bb..371ba20 100644
--- a/include/ovn/expr.h
+++ b/include/ovn/expr.h
@@ -382,6 +382,14 @@ bool expr_honors_invariants(const struct expr *);
 bool expr_is_simplified(const struct expr *);
 bool expr_is_normalized(const struct expr *);
 
+char *expr_parse_microflow(const char *, const struct shash *symtab,
+                           const struct shash *macros,
+                           bool (*lookup_port)(const void *aux,
+                                               const char *port_name,
+                                               unsigned int *portp),
+                           const void *aux, struct flow *uflow)
+    OVS_WARN_UNUSED_RESULT;
+
 bool expr_evaluate(const struct expr *, const struct flow *uflow,
                    bool (*lookup_port)(const void *aux, const char *port_name,
                                        unsigned int *portp),
diff --git a/ovn/lib/expr.c b/ovn/lib/expr.c
index f90c7d1..bd613fb 100644
--- a/ovn/lib/expr.c
+++ b/ovn/lib/expr.c
@@ -2952,3 +2952,124 @@ expr_resolve_field(const struct expr_field *f)
     int n_bits = symbol->width ? f->n_bits : symbol->field->n_bits;
     return (struct mf_subfield) { symbol->field, ofs, n_bits };
 }
+
+static struct expr *
+expr_parse_microflow__(struct lexer *lexer,
+                       const struct shash *symtab,
+                       bool (*lookup_port)(const void *aux,
+                                           const char *port_name,
+                                           unsigned int *portp),
+                       const void *aux,
+                       struct expr *e, struct flow *uflow)
+{
+    char *error;
+    e = expr_annotate(e, symtab, &error);
+    if (error) {
+        lexer_error(lexer, "%s", error);
+        free(error);
+        return NULL;
+    }
+
+    struct ds annotated = DS_EMPTY_INITIALIZER;
+    expr_format(e, &annotated);
+
+    e = expr_simplify(e);
+    e = expr_normalize(e);
+
+    struct match m = MATCH_CATCHALL_INITIALIZER;
+
+    switch (e->type) {
+    case EXPR_T_BOOLEAN:
+        if (!e->boolean) {
+            lexer_error(lexer, "Constraints are contradictory.");
+        }
+        break;
+
+    case EXPR_T_OR:
+        lexer_error(lexer, "Constraints are ambiguous: %s.",
+                    ds_cstr(&annotated));
+        break;
+
+    case EXPR_T_CMP:
+        constrain_match(e, lookup_port, aux, &m);
+        break;
+
+    case EXPR_T_AND: {
+        struct expr *sub;
+        LIST_FOR_EACH (sub, node, &e->andor) {
+            if (sub->type == EXPR_T_CMP) {
+                constrain_match(sub, lookup_port, aux, &m);
+            } else {
+                ovs_assert(sub->type == EXPR_T_OR);
+                lexer_error(lexer, "Constraints are ambiguous: %s.",
+                            ds_cstr(&annotated));
+                break;
+            }
+        }
+    }
+        break;
+
+    default:
+        OVS_NOT_REACHED();
+    }
+    ds_destroy(&annotated);
+
+    *uflow = m.flow;
+    return e;
+}
+
+/* Parses 's' as a microflow, using symbols from 'symtab', macros from
+ * 'macros', and looking up port numbers using 'lookup_port' and 'aux'.  On
+ * success, stores the result in 'uflow' and returns NULL, otherwise zeros
+ * 'uflow' and returns an error message that the caller must free().
+ *
+ * A "microflow" is a description of a single stream of packets, such as half a
+ * TCP connection.  's' uses the syntax of an OVN logical expression to express
+ * constraints that describe the microflow.  For example, "ip4 && tcp.src ==
+ * 80" would set uflow->dl_type to ETH_TYPE_IP, uflow->nw_proto to IPPROTO_TCP,
+ * and uflow->tp_src to 80.
+ *
+ * Microflow expressions can be erroneous in two ways.  First, they can be
+ * ambiguous.  For example, "tcp.src == 80" is ambiguous because it does not
+ * state IPv4 or IPv6 as the Ethernet type.  "ip4 && tcp.src > 1024" is also
+ * ambiguous because it does not constrain bits of tcp.src to particular
+ * values.  Second, they can be contradictory, e.g. "ip4 && ip6".  This
+ * function will report both types of errors.
+ *
+ * This function isn't that smart, so it can yield errors for some "clever"
+ * formulations of particular microflows that area accepted other ways.  For
+ * example, all of the following expressions are equivalent:
+ *     ip4 && tcp.src[1..15] == 0x28
+ *     ip4 && tcp.src > 79 && tcp.src < 82
+ *     ip4 && 80 <= tcp.src <= 81
+ *     ip4 && tcp.src == {80, 81}
+ * but as of this writing this function only accepts the first two, rejecting
+ * the last two as ambiguous.  Just don't be too clever. */
+char * OVS_WARN_UNUSED_RESULT
+expr_parse_microflow(const char *s, const struct shash *symtab,
+                     const struct shash *macros,
+                     bool (*lookup_port)(const void *aux,
+                                         const char *port_name,
+                                         unsigned int *portp),
+                     const void *aux, struct flow *uflow)
+{
+    struct lexer lexer;
+    lexer_init(&lexer, s);
+    lexer_get(&lexer);
+
+    struct expr *e = expr_parse(&lexer, symtab, macros);
+    lexer_force_end(&lexer);
+
+    if (e) {
+        e = expr_parse_microflow__(&lexer, symtab, lookup_port, aux, e, uflow);
+    }
+
+    char *error = lexer_steal_error(&lexer);
+    lexer_destroy(&lexer);
+    expr_destroy(e);
+
+    if (error) {
+        memset(uflow, 0, sizeof *uflow);
+    }
+    return error;
+}
diff --git a/tests/test-ovn.c b/tests/test-ovn.c
index 712c54a..b8ecea7 100644
--- a/tests/test-ovn.c
+++ b/tests/test-ovn.c
@@ -338,21 +338,17 @@ lookup_atoi_cb(const void *aux OVS_UNUSED, const char *port_name,
 static void
 test_evaluate_expr(struct ovs_cmdl_context *ctx)
 {
-    int a = atoi(ctx->argv[1]);
-    int b = atoi(ctx->argv[2]);
-    int c = atoi(ctx->argv[3]);
-    struct flow uflow = { .regs = { a, b, c } };
-
     struct shash symtab;
     struct ds input;
 
-    shash_init(&symtab);
-    expr_symtab_add_field(&symtab, "reg0", MFF_REG0, NULL, false);
-    expr_symtab_add_field(&symtab, "reg1", MFF_REG1, NULL, false);
-    expr_symtab_add_field(&symtab, "reg2", MFF_REG1, NULL, false);
-    expr_symtab_add_subfield(&symtab, "a", NULL, "reg0[0..2]");
-    expr_symtab_add_subfield(&symtab, "b", NULL, "reg1[0..2]");
-    expr_symtab_add_subfield(&symtab, "c", NULL, "reg2[0..2]");
+    ovn_init_symtab(&symtab);
+
+    struct flow uflow;
+    char *error = expr_parse_microflow(ctx->argv[1], &symtab, NULL,
+                                       lookup_atoi_cb, NULL, &uflow);
+    if (error) {
+        ovs_fatal(0, "%s", error);
+    }
 
     ds_init(&input);
     while (!ds_get_test_line(&input, stdin)) {
@@ -1286,10 +1282,16 @@ expr-to-flows\n\
   differing degrees of analysis.  Available fields are based on packet\n\
   headers.\n\
 \n\
-evaluate-expr A B C\n\
-  Parses OVN expressions from stdin, evaluate them with assigned values,\n\
-  and print the results on stdout.  Available fields are 'a', 'b', and 'c'\n\
-  of 3 bits each.  A, B, and C should be in the range 0 to 7.\n\
+evaluate-expr MICROFLOW\n\
+  Parses OVN expressions from stdin and evaluates them against the flow\n\
+  specified in MICROFLOW, which must be an expression that constrains\n\
+  the packet, e.g. \"ip4 && tcp.src == 80\" for a TCP packet with source\n\
+  port 80, and prints the results on stdout, either 1 for true or 0 for\n\
+  false.  Use quoted integers, e.g. \"123\", for string fields.\n\
+\n\
+  Example: for MICROFLOW of \"ip4 && tcp.src == 80\", \"eth.type == 0x800\"\n\
+  evaluates to true, \"udp\" evaluates to false, and \"udp || tcp\"\n\
+  evaluates to true.\n\
 \n\
 composition N\n\
   Prints all the compositions of N on stdout.\n\
-- 
2.1.3




More information about the dev mailing list