From patchwork Sun Aug 14 22:24:21 2016 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Ben Pfaff X-Patchwork-Id: 659077 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Received: from archives.nicira.com (archives.nicira.com [96.126.127.54]) by ozlabs.org (Postfix) with ESMTP id 3sCCr35KC0z9t0m for ; Mon, 15 Aug 2016 08:25:39 +1000 (AEST) Received: from archives.nicira.com (localhost [127.0.0.1]) by archives.nicira.com (Postfix) with ESMTP id 06151106EA; Sun, 14 Aug 2016 15:24:43 -0700 (PDT) X-Original-To: dev@openvswitch.org Delivered-To: dev@openvswitch.org Received: from mx1e3.cudamail.com (mx1.cudamail.com [69.90.118.67]) by archives.nicira.com (Postfix) with ESMTPS id D6E78106D4 for ; Sun, 14 Aug 2016 15:24:39 -0700 (PDT) Received: from bar5.cudamail.com (localhost [127.0.0.1]) by mx1e3.cudamail.com (Postfix) with ESMTPS id 63F5C420A88 for ; Sun, 14 Aug 2016 16:24:39 -0600 (MDT) X-ASG-Debug-ID: 1471213478-09eadd6847092c0001-byXFYA Received: from mx3-pf2.cudamail.com ([192.168.14.1]) by bar5.cudamail.com with ESMTP id 9ZuRHhQBu41FJsL7 (version=TLSv1 cipher=DHE-RSA-AES256-SHA bits=256 verify=NO) for ; Sun, 14 Aug 2016 16:24:38 -0600 (MDT) X-Barracuda-Envelope-From: blp@ovn.org X-Barracuda-RBL-Trusted-Forwarder: 192.168.14.1 Received: from unknown (HELO relay3-d.mail.gandi.net) (217.70.183.195) by mx3-pf2.cudamail.com with ESMTPS (DHE-RSA-AES256-SHA encrypted); 14 Aug 2016 22:24:38 -0000 Received-SPF: pass (mx3-pf2.cudamail.com: SPF record at ovn.org designates 217.70.183.195 as permitted sender) X-Barracuda-Apparent-Source-IP: 217.70.183.195 X-Barracuda-RBL-IP: 217.70.183.195 Received: from mfilter19-d.gandi.net (mfilter19-d.gandi.net [217.70.178.147]) by relay3-d.mail.gandi.net (Postfix) with ESMTP id 42E96A80C7; Mon, 15 Aug 2016 00:24:37 +0200 (CEST) X-Virus-Scanned: Debian amavisd-new at mfilter19-d.gandi.net Received: from relay3-d.mail.gandi.net ([IPv6:::ffff:217.70.183.195]) by mfilter19-d.gandi.net (mfilter19-d.gandi.net [::ffff:10.0.15.180]) (amavisd-new, port 10024) with ESMTP id zgM9nQx-X0yq; Mon, 15 Aug 2016 00:24:35 +0200 (CEST) X-Originating-IP: 173.228.112.241 Received: from sigabrt.gateway.sonic.net (173-228-112-241.dsl.dynamic.fusionbroadband.com [173.228.112.241]) (Authenticated sender: blp@ovn.org) by relay3-d.mail.gandi.net (Postfix) with ESMTPSA id 616A3A80C0; Mon, 15 Aug 2016 00:24:34 +0200 (CEST) X-CudaMail-Envelope-Sender: blp@ovn.org From: Ben Pfaff To: dev@openvswitch.org X-CudaMail-Whitelist-To: dev@openvswitch.org X-CudaMail-MID: CM-V2-813014052 X-CudaMail-DTE: 081416 X-CudaMail-Originating-IP: 217.70.183.195 Date: Sun, 14 Aug 2016 15:24:21 -0700 X-ASG-Orig-Subj: [##CM-V2-813014052##][PATCH v5 7/8] expr: New function expr_parse_microflow(). Message-Id: <1471213462-22329-8-git-send-email-blp@ovn.org> X-Mailer: git-send-email 2.1.3 In-Reply-To: <1471213462-22329-1-git-send-email-blp@ovn.org> References: <1471213462-22329-1-git-send-email-blp@ovn.org> X-Barracuda-Connect: UNKNOWN[192.168.14.1] X-Barracuda-Start-Time: 1471213478 X-Barracuda-Encrypted: DHE-RSA-AES256-SHA X-Barracuda-URL: https://web.cudamail.com:443/cgi-mod/mark.cgi X-ASG-Whitelist: Header =?UTF-8?B?eFwtY3VkYW1haWxcLXdoaXRlbGlzdFwtdG8=?= X-Virus-Scanned: by bsmtpd at cudamail.com X-Barracuda-BRTS-Status: 1 Cc: Ben Pfaff Subject: [ovs-dev] [PATCH v5 7/8] expr: New function expr_parse_microflow(). X-BeenThere: dev@openvswitch.org X-Mailman-Version: 2.1.16 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , MIME-Version: 1.0 Errors-To: dev-bounces@openvswitch.org Sender: "dev" 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 Acked-by: Ryan Moats Acked-by: Justin Pettit --- 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 29a6f08..a86fb93 100644 --- a/tests/test-ovn.c +++ b/tests/test-ovn.c @@ -345,21 +345,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)) { @@ -1296,10 +1292,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\