[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