From patchwork Sat Dec 10 06:56:33 2016 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Ben Pfaff X-Patchwork-Id: 704700 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Received: from mail.linuxfoundation.org (mail.linuxfoundation.org [140.211.169.12]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by ozlabs.org (Postfix) with ESMTPS id 3tbKfw5YwGz9tld for ; Sat, 10 Dec 2016 17:57:44 +1100 (AEDT) Received: from mail.linux-foundation.org (localhost [127.0.0.1]) by mail.linuxfoundation.org (Postfix) with ESMTP id 44167A87; Sat, 10 Dec 2016 06:56:48 +0000 (UTC) X-Original-To: dev@openvswitch.org Delivered-To: ovs-dev@mail.linuxfoundation.org Received: from smtp1.linuxfoundation.org (smtp1.linux-foundation.org [172.17.192.35]) by mail.linuxfoundation.org (Postfix) with ESMTPS id 88DE1982 for ; Sat, 10 Dec 2016 06:56:46 +0000 (UTC) X-Greylist: domain auto-whitelisted by SQLgrey-1.7.6 Received: from relay2-d.mail.gandi.net (relay2-d.mail.gandi.net [217.70.183.194]) by smtp1.linuxfoundation.org (Postfix) with ESMTPS id DD366A0 for ; Sat, 10 Dec 2016 06:56:44 +0000 (UTC) Received: from mfilter27-d.gandi.net (mfilter27-d.gandi.net [217.70.178.155]) by relay2-d.mail.gandi.net (Postfix) with ESMTP id AD75BC5A5D; Sat, 10 Dec 2016 07:56:43 +0100 (CET) X-Virus-Scanned: Debian amavisd-new at mfilter27-d.gandi.net Received: from relay2-d.mail.gandi.net ([IPv6:::ffff:217.70.183.194]) by mfilter27-d.gandi.net (mfilter27-d.gandi.net [::ffff:10.0.15.180]) (amavisd-new, port 10024) with ESMTP id qg9iu0j9BP69; Sat, 10 Dec 2016 07:56:41 +0100 (CET) X-Originating-IP: 173.228.112.229 Received: from sigabrt.gateway.sonic.net (173-228-112-229.dsl.dynamic.fusionbroadband.com [173.228.112.229]) (Authenticated sender: blp@ovn.org) by relay2-d.mail.gandi.net (Postfix) with ESMTPSA id 095DBC5A55; Sat, 10 Dec 2016 07:56:39 +0100 (CET) From: Ben Pfaff To: dev@openvswitch.org Date: Fri, 9 Dec 2016 22:56:33 -0800 Message-Id: <20161210065633.32634-3-blp@ovn.org> X-Mailer: git-send-email 2.10.2 In-Reply-To: <20161210065633.32634-1-blp@ovn.org> References: <20161210065633.32634-1-blp@ovn.org> X-Spam-Status: No, score=-2.6 required=5.0 tests=BAYES_00, RCVD_IN_DNSWL_LOW autolearn=ham version=3.3.1 X-Spam-Checker-Version: SpamAssassin 3.3.1 (2010-03-16) on smtp1.linux-foundation.org Cc: Ben Pfaff Subject: [ovs-dev] [PATCH v2 2/2] ovn-trace: New --ovs option to also print OpenFlow flows. X-BeenThere: ovs-dev@openvswitch.org X-Mailman-Version: 2.1.12 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , MIME-Version: 1.0 Sender: ovs-dev-bounces@openvswitch.org Errors-To: ovs-dev-bounces@openvswitch.org Sometimes seeing the OpenFlow flows that back a given logical flow can provide additional insight. This commit adds a new --ovs option to ovn-trace that makes it connect to Open vSwitch over OpenFlow and retrieve and print the OpenFlow flows behind each logical flow encountered during a trace. Signed-off-by: Ben Pfaff --- NEWS | 1 + include/openvswitch/vconn.h | 15 +++-- lib/vconn.c | 121 ++++++++++++++++++++++++++++++++- ovn/utilities/ovn-trace.8.xml | 61 ++++++++++++++++- ovn/utilities/ovn-trace.c | 91 ++++++++++++++++++++++++- utilities/ovs-ofctl.c | 152 +++++++++--------------------------------- 6 files changed, 313 insertions(+), 128 deletions(-) diff --git a/NEWS b/NEWS index 5c5628c..51041c2 100644 --- a/NEWS +++ b/NEWS @@ -7,6 +7,7 @@ Post-v2.6.0 * DSCP marking is now supported, via the new northbound QoS table. * IPAM now supports fixed MAC addresses. * Support for source IP address based routing. + * ovn-trace has a new --ovs option to also print OpenFlow flows. - Fixed regression in table stats maintenance introduced in OVS 2.3.0, wherein the number of OpenFlow table hits and misses was not accurate. diff --git a/include/openvswitch/vconn.h b/include/openvswitch/vconn.h index d2fbd16..acdc79c 100644 --- a/include/openvswitch/vconn.h +++ b/include/openvswitch/vconn.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015 Nicira, Inc. + * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 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. @@ -18,9 +18,10 @@ #define OPENVSWITCH_VCONN_H 1 #include -#include -#include -#include +#include "openvswitch/list.h" +#include "openvswitch/types.h" +#include "openvswitch/ofp-util.h" +#include "openflow/openflow.h" #ifdef __cplusplus extern "C" { @@ -31,6 +32,8 @@ struct pvconn; struct pvconn_class; struct vconn; struct vconn_class; +struct ofputil_flow_stats; +struct ofputil_flow_stats_request; void vconn_usage(bool active, bool passive, bool bootstrap); @@ -56,6 +59,10 @@ int vconn_transact_noreply(struct vconn *, struct ofpbuf *, struct ofpbuf **); int vconn_transact_multiple_noreply(struct vconn *, struct ovs_list *requests, struct ofpbuf **replyp); +int vconn_dump_flows(struct vconn *, const struct ofputil_flow_stats_request *, + enum ofputil_protocol, + struct ofputil_flow_stats **fsesp, size_t *n_fsesp); + /* Bundle errors must be free()d by the caller. */ struct vconn_bundle_error { struct ovs_list list_node; diff --git a/lib/vconn.c b/lib/vconn.c index d5a17f6..57cf429 100644 --- a/lib/vconn.c +++ b/lib/vconn.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015 Nicira, Inc. + * Copyright (c) 2008-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. @@ -943,6 +943,125 @@ vconn_transact_multiple_noreply(struct vconn *vconn, struct ovs_list *requests, return 0; } +static int +recv_flow_stats_reply(struct vconn *vconn, ovs_be32 send_xid, + struct ofpbuf **replyp, + struct ofputil_flow_stats *fs, struct ofpbuf *ofpacts) +{ + static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(5, 5); + struct ofpbuf *reply = *replyp; + + for (;;) { + int retval; + bool more; + + /* Get a flow stats reply message, if we don't already have one. */ + if (!reply) { + enum ofptype type; + enum ofperr error; + + do { + error = vconn_recv_block(vconn, &reply); + if (error) { + return error; + } + } while (((struct ofp_header *) reply->data)->xid != send_xid); + + error = ofptype_decode(&type, reply->data); + if (error || type != OFPTYPE_FLOW_STATS_REPLY) { + VLOG_WARN_RL(&rl, "received bad reply: %s", + ofp_to_string(reply->data, reply->size, 1)); + return EPROTO; + } + } + + /* Pull an individual flow stats reply out of the message. */ + retval = ofputil_decode_flow_stats_reply(fs, reply, false, ofpacts); + switch (retval) { + case 0: + *replyp = reply; + return 0; + + case EOF: + more = ofpmp_more(reply->header); + ofpbuf_delete(reply); + reply = NULL; + if (!more) { + *replyp = NULL; + return EOF; + } + break; + + default: + VLOG_WARN_RL(&rl, "parse error in reply (%s)", + ofperr_to_string(retval)); + return EPROTO; + } + } +} + +/* Send 'request' to 'vconn', encoding it with the given 'protocol', and then + * waits for, parses, and accumulates all of the replies into '*fsesp' and + * '*n_fsesp'. The caller is responsible for freeing all of the flows. + * Returns 0 if successful, otherwise a positive errno value. Always frees + * 'request'. */ +int +vconn_dump_flows(struct vconn *vconn, + const struct ofputil_flow_stats_request *fsr, + enum ofputil_protocol protocol, + struct ofputil_flow_stats **fsesp, size_t *n_fsesp) +{ + struct ofputil_flow_stats *fses = NULL; + size_t n_fses = 0; + size_t allocated_fses = 0; + + struct ofpbuf *request = ofputil_encode_flow_stats_request(fsr, protocol); + const struct ofp_header *oh = request->data; + ovs_be32 send_xid = oh->xid; + int error = vconn_send_block(vconn, request); + if (error) { + goto exit; + } + + struct ofpbuf *reply = NULL; + struct ofpbuf ofpacts; + ofpbuf_init(&ofpacts, 0); + for (;;) { + if (n_fses >= allocated_fses) { + fses = x2nrealloc(fses, &allocated_fses, sizeof *fses); + } + + struct ofputil_flow_stats *fs = &fses[n_fses]; + error = recv_flow_stats_reply(vconn, send_xid, &reply, fs, &ofpacts); + if (error) { + if (error == EOF) { + error = 0; + } + break; + } + fs->ofpacts = xmemdup(fs->ofpacts, fs->ofpacts_len); + n_fses++; + } + ofpbuf_uninit(&ofpacts); + ofpbuf_delete(reply); + + if (error) { + for (size_t i = 0; i < n_fses; i++) { + free(CONST_CAST(struct ofpact *, fses[i].ofpacts)); + } + free(fses); + + fses = NULL; + n_fses = 0; + } + +exit: + *fsesp = fses; + *n_fsesp = n_fses; + return error; +} + + static enum ofperr vconn_bundle_reply_validate(struct ofpbuf *reply, struct ofputil_bundle_ctrl_msg *request, diff --git a/ovn/utilities/ovn-trace.8.xml b/ovn/utilities/ovn-trace.8.xml index 92dcb17..776f69a 100644 --- a/ovn/utilities/ovn-trace.8.xml +++ b/ovn/utilities/ovn-trace.8.xml @@ -255,6 +255,65 @@
Selects all three forms of output.
+ +
--ovs[=remote]
+
+

+ Makes ovn-trace attempt to obtain and display the OpenFlow + flows that correspond to each OVN logical flow. To do so, + ovn-trace connects to remote (by default, + unix:@RUNDIR@/br-int.mgmt) over OpenFlow and retrieves the + flows. If remote is specified, it must be an active + OpenFlow connection method described in ovs-ofctl(8). +

+ +

+ Keep in mind a few important aspects of the difference between logical + flows and OpenFlow: +

+ +
    +
  • + Each logical flow maps to one or more OpenFlow flows. An actual + packet ordinarily matches only one of these, although in some cases + it can more than one of these flows (which is not a problem because + all of them have the same actions). ovn-trace currently + shows all corresponding flows. +
  • + +
  • + Some logical flows can map to the Open vSwitch ``conjunctive match'' + extension (see ovs-ofctl(8)). The OpenFlow match for a + conjunctive match shows a match on conj_id. Currently + ovn-trace cannot display the flows with + conjunction actions that effective produce the + conj_id match. +
  • + +
  • + Some logical flows may not be represented in the OpenFlow tables on a + given hypervisor, if they could not be used on that hypervisor. For + example, if no VIF in a logical switch resides on a given hypervisor, + and the logical switch is not otherwise reachable on that hypervisor + (e.g. over a series of hops through logical switches and routers + starting from a VIF on the hypervisor), then the logical flow may not + be represented there. +
  • + +
  • + Some OpenFlow flows do not correspond to logical flows, such as + OpenFlow flows that map between physical and logical ports. These + flows will never show up in a trace. See ``Architectural Physical + Life Cycle of a Packet'' in ovn-architecture(7) for + information on these flows. +
  • + +
  • + When ovn-trace omits uninteresting logical flows from + output, it does not look up the corresponding OpenFlow flows. +
  • +
+

Daemon Options

@@ -266,7 +325,7 @@

PKI Options

PKI configuration is required to use SSL for the connection to the - database. + database (and the switch, if --ovs is specified).

diff --git a/ovn/utilities/ovn-trace.c b/ovn/utilities/ovn-trace.c index 04aa532..85b1139 100644 --- a/ovn/utilities/ovn-trace.c +++ b/ovn/utilities/ovn-trace.c @@ -27,6 +27,8 @@ #include "nx-match.h" #include "openvswitch/dynamic-string.h" #include "openvswitch/ofp-actions.h" +#include "openvswitch/ofp-print.h" +#include "openvswitch/vconn.h" #include "openvswitch/vlog.h" #include "ovn/actions.h" #include "ovn/expr.h" @@ -62,6 +64,10 @@ static bool summary; /* --minimal: Show a trace with only minimal information. */ static bool minimal; +/* --ovs: OVS instance to contact to get OpenFlow flows. */ +static const char *ovs; +static struct vconn *vconn; + OVS_NO_RETURN static void usage(void); static void parse_options(int argc, char *argv[]); static char *trace(const char *datapath, const char *flow); @@ -142,6 +148,12 @@ main(int argc, char *argv[]) } } +static char * +default_ovs(void) +{ + return xasprintf("unix:%s/br-int.mgmt", ovs_rundir()); +} + static void parse_options(int argc, char *argv[]) { @@ -152,6 +164,7 @@ parse_options(int argc, char *argv[]) OPT_SUMMARY, OPT_MINIMAL, OPT_ALL, + OPT_OVS, DAEMON_OPTION_ENUMS, SSL_OPTION_ENUMS, VLOG_OPTION_ENUMS @@ -163,6 +176,7 @@ parse_options(int argc, char *argv[]) {"summary", no_argument, NULL, OPT_SUMMARY}, {"minimal", no_argument, NULL, OPT_MINIMAL}, {"all", no_argument, NULL, OPT_ALL}, + {"ovs", optional_argument, NULL, OPT_OVS}, {"help", no_argument, NULL, 'h'}, {"version", no_argument, NULL, 'V'}, DAEMON_LONG_OPTIONS, @@ -206,6 +220,10 @@ parse_options(int argc, char *argv[]) detailed = summary = minimal = true; break; + case OPT_OVS: + ovs = optarg ? optarg : default_ovs(); + break; + case 'h': usage(); @@ -244,7 +262,7 @@ usage(void) usage: %s [OPTIONS] DATAPATH MICROFLOW\n\ %s [OPTIONS] --detach\n\ \n\ -Option format options:\n\ +Output format options:\n\ --detailed table-by-table \"backtrace\" (default)\n\ --summary less detailed, more parseable\n\ --minimal minimum to explain externally visible behavior\n\ @@ -256,11 +274,14 @@ Option format options:\n\ Other options:\n\ --db=DATABASE connect to DATABASE\n\ (default: %s)\n\ + --ovs[=REMOTE] obtain corresponding OpenFlow flows from REMOTE\n\ + (default: %s)\n\ --unixctl=SOCKET set control socket name\n\ -h, --help display this help message\n\ -V, --version display version information\n", - default_sb_db()); + default_sb_db(), default_ovs()); stream_usage("database", true, true, false); + vconn_usage(true, false, false); exit(EXIT_SUCCESS); } @@ -302,6 +323,7 @@ struct ovntrace_mcgroup { enum ovntrace_pipeline { P_INGRESS, P_EGRESS }; struct ovntrace_flow { + struct uuid uuid; enum ovntrace_pipeline pipeline; int table_id; char *stage_name; @@ -638,6 +660,7 @@ read_flows(void) } struct ovntrace_flow *flow = xzalloc(sizeof *flow); + flow->uuid = sblf->header_.uuid; flow->pipeline = (!strcmp(sblf->pipeline, "ingress") ? P_INGRESS : P_EGRESS); @@ -1339,6 +1362,54 @@ may_omit_stage(const struct ovntrace_flow *f, uint8_t table_id) } static void +trace_openflow(const struct ovntrace_flow *f, struct ovs_list *super) +{ + struct ofputil_flow_stats_request fsr = { + .cookie = htonll(f->uuid.parts[0]), + .cookie_mask = OVS_BE64_MAX, + .out_port = OFPP_ANY, + .out_group = OFPG_ANY, + .table_id = OFPTT_ALL, + }; + + struct ofputil_flow_stats *fses; + size_t n_fses; + int error = vconn_dump_flows(vconn, &fsr, OFPUTIL_P_OF13_OXM, + &fses, &n_fses); + if (error) { + ovntrace_node_append(super, OVNTRACE_NODE_ERROR, + "*** error obtaining flow stats (%s)", + ovs_strerror(error)); + VLOG_WARN("%s: error obtaining flow stats (%s)", + ovs, ovs_strerror(error)); + return; + } + + if (n_fses) { + struct ds s = DS_EMPTY_INITIALIZER; + for (size_t i = 0; i < n_fses; i++) { + ds_clear(&s); + ofp_print_flow_stats(&s, &fses[i]); + + /* ofp_print_flow_stats() indents its output with a space. + * Omit it. */ + const char *p = ds_cstr(&s); + p += strspn(p, " "); + ovntrace_node_append(super, OVNTRACE_NODE_ACTION, "%s", p); + } + ds_destroy(&s); + } else { + ovntrace_node_append(super, OVNTRACE_NODE_ERROR, + "*** no OpenFlow flows"); + } + + for (size_t i = 0; i < n_fses; i++) { + free(CONST_CAST(struct ofpact *, fses[i].ofpacts)); + } + free(fses); +} + +static void trace__(const struct ovntrace_datapath *dp, struct flow *uflow, uint8_t table_id, enum ovntrace_pipeline pipeline, struct ovs_list *super) @@ -1362,7 +1433,8 @@ trace__(const struct ovntrace_datapath *dp, struct flow *uflow, } else if (f->source) { ds_put_format(&s, "(%s): ", f->source); } - ds_put_format(&s, "%s, priority %d", f->match_s, f->priority); + ds_put_format(&s, "%s, priority %d, uuid %08x", + f->match_s, f->priority, f->uuid.parts[0]); } else { char *stage_name = ovntrace_stage_name(dp, table_id, pipeline); ds_put_format(&s, "%s%sno match (implicit drop)", @@ -1375,6 +1447,9 @@ trace__(const struct ovntrace_datapath *dp, struct flow *uflow, ds_destroy(&s); if (f) { + if (vconn) { + trace_openflow(f, &node->subs); + } trace_actions(f->ovnacts, f->ovnacts_len, dp, uflow, table_id, pipeline, &node->subs); } @@ -1408,6 +1483,13 @@ trace(const char *dp_s, const char *flow_s) flow_format(&output, &uflow); ds_put_char(&output, '\n'); + if (ovs) { + int retval = vconn_open_block(ovs, 1 << OFP13_VERSION, 0, &vconn); + if (retval) { + VLOG_WARN("%s: connection failed (%s)", ovs, ovs_strerror(retval)); + } + } + struct ovs_list root = OVS_LIST_INITIALIZER(&root); struct ovntrace_node *node = ovntrace_node_append( &root, OVNTRACE_NODE_PIPELINE, "ingress(dp=\"%s\", inport=\"%s\")", @@ -1439,6 +1521,9 @@ trace(const char *dp_s, const char *flow_s) ovntrace_node_prune_hard(&root); ovntrace_node_print_summary(&output, &root, 0); } + + vconn_close(vconn); + return ds_steal_cstr(&output); } diff --git a/utilities/ovs-ofctl.c b/utilities/ovs-ofctl.c index 7f5bb61..061b42a 100644 --- a/utilities/ovs-ofctl.c +++ b/utilities/ovs-ofctl.c @@ -129,10 +129,6 @@ static const struct ovs_cmdl_command *get_all_commands(void); OVS_NO_RETURN static void usage(void); static void parse_options(int argc, char *argv[]); -static bool recv_flow_stats_reply(struct vconn *, ovs_be32 send_xid, - struct ofpbuf **replyp, - struct ofputil_flow_stats *, - struct ofpbuf *ofpacts); int main(int argc, char *argv[]) { @@ -1166,14 +1162,14 @@ set_protocol_for_flow_dump(struct vconn *vconn, static struct vconn * prepare_dump_flows(int argc, char *argv[], bool aggregate, - struct ofpbuf **requestp) + struct ofputil_flow_stats_request *fsr, + enum ofputil_protocol *protocolp) { enum ofputil_protocol usable_protocols, protocol; - struct ofputil_flow_stats_request fsr; struct vconn *vconn; char *error; - error = parse_ofp_flow_stats_request_str(&fsr, aggregate, + error = parse_ofp_flow_stats_request_str(fsr, aggregate, argc > 2 ? argv[2] : "", &usable_protocols); if (error) { @@ -1181,19 +1177,19 @@ prepare_dump_flows(int argc, char *argv[], bool aggregate, } protocol = open_vconn(argv[1], &vconn); - protocol = set_protocol_for_flow_dump(vconn, protocol, usable_protocols); - *requestp = ofputil_encode_flow_stats_request(&fsr, protocol); + *protocolp = set_protocol_for_flow_dump(vconn, protocol, usable_protocols); return vconn; } static void ofctl_dump_flows__(int argc, char *argv[], bool aggregate) { - struct ofpbuf *request; + struct ofputil_flow_stats_request fsr; + enum ofputil_protocol protocol; struct vconn *vconn; - vconn = prepare_dump_flows(argc, argv, aggregate, &request); - dump_transaction(vconn, request); + vconn = prepare_dump_flows(argc, argv, aggregate, &fsr, &protocol); + dump_transaction(vconn, ofputil_encode_flow_stats_request(&fsr, protocol)); vconn_close(vconn); } @@ -1270,52 +1266,29 @@ ofctl_dump_flows(struct ovs_cmdl_context *ctx) ofctl_dump_flows__(ctx->argc, ctx->argv, false); return; } else { - struct ofputil_flow_stats *fses; - size_t n_fses, allocated_fses; - struct ofpbuf *request; - struct ofpbuf ofpacts; - struct ofpbuf *reply; + struct ofputil_flow_stats_request fsr; + enum ofputil_protocol protocol; struct vconn *vconn; - ovs_be32 send_xid; - struct ds s; - size_t i; - - vconn = prepare_dump_flows(ctx->argc, ctx->argv, false, &request); - send_xid = ((struct ofp_header *) request->data)->xid; - send_openflow_buffer(vconn, request); - - fses = NULL; - n_fses = allocated_fses = 0; - reply = NULL; - ofpbuf_init(&ofpacts, 0); - for (;;) { - struct ofputil_flow_stats *fs; - if (n_fses >= allocated_fses) { - fses = x2nrealloc(fses, &allocated_fses, sizeof *fses); - } + vconn = prepare_dump_flows(ctx->argc, ctx->argv, false, + &fsr, &protocol); - fs = &fses[n_fses]; - if (!recv_flow_stats_reply(vconn, send_xid, &reply, fs, - &ofpacts)) { - break; - } - fs->ofpacts = xmemdup(fs->ofpacts, fs->ofpacts_len); - n_fses++; - } - ofpbuf_uninit(&ofpacts); + struct ofputil_flow_stats *fses; + size_t n_fses; + run(vconn_dump_flows(vconn, &fsr, protocol, &fses, &n_fses), + "dump flows"); qsort(fses, n_fses, sizeof *fses, compare_flows); - ds_init(&s); - for (i = 0; i < n_fses; i++) { + struct ds s = DS_EMPTY_INITIALIZER; + for (size_t i = 0; i < n_fses; i++) { ds_clear(&s); ofp_print_flow_stats(&s, &fses[i]); puts(ds_cstr(&s)); } ds_destroy(&s); - for (i = 0; i < n_fses; i++) { + for (size_t i = 0; i < n_fses; i++) { free(CONST_CAST(struct ofpact *, fses[i].ofpacts)); } free(fses); @@ -3365,59 +3338,6 @@ read_flows_from_file(const char *filename, struct fte_state *state, int index) return usable_protocols; } -static bool -recv_flow_stats_reply(struct vconn *vconn, ovs_be32 send_xid, - struct ofpbuf **replyp, - struct ofputil_flow_stats *fs, struct ofpbuf *ofpacts) -{ - struct ofpbuf *reply = *replyp; - - for (;;) { - int retval; - bool more; - - /* Get a flow stats reply message, if we don't already have one. */ - if (!reply) { - enum ofptype type; - enum ofperr error; - - do { - run(vconn_recv_block(vconn, &reply), - "OpenFlow packet receive failed"); - } while (((struct ofp_header *) reply->data)->xid != send_xid); - - error = ofptype_decode(&type, reply->data); - if (error || type != OFPTYPE_FLOW_STATS_REPLY) { - ovs_fatal(0, "received bad reply: %s", - ofp_to_string(reply->data, reply->size, - verbosity + 1)); - } - } - - /* Pull an individual flow stats reply out of the message. */ - retval = ofputil_decode_flow_stats_reply(fs, reply, false, ofpacts); - switch (retval) { - case 0: - *replyp = reply; - return true; - - case EOF: - more = ofpmp_more(reply->header); - ofpbuf_delete(reply); - reply = NULL; - if (!more) { - *replyp = NULL; - return false; - } - break; - - default: - ovs_fatal(0, "parse error in reply (%s)", - ofperr_to_string(retval)); - } - } -} - /* Reads the OpenFlow flow table from 'vconn', which has currently active flow * format 'protocol', and adds them as flow table entries in 'tables' for the * version with the specified 'index'. */ @@ -3427,11 +3347,6 @@ read_flows_from_switch(struct vconn *vconn, struct fte_state *state, int index) { struct ofputil_flow_stats_request fsr; - struct ofputil_flow_stats fs; - struct ofpbuf *request; - struct ofpbuf ofpacts; - struct ofpbuf *reply; - ovs_be32 send_xid; fsr.aggregate = false; match_init_catchall(&fsr.match); @@ -3439,28 +3354,27 @@ read_flows_from_switch(struct vconn *vconn, fsr.out_group = OFPG_ANY; fsr.table_id = 0xff; fsr.cookie = fsr.cookie_mask = htonll(0); - request = ofputil_encode_flow_stats_request(&fsr, protocol); - send_xid = ((struct ofp_header *) request->data)->xid; - send_openflow_buffer(vconn, request); - reply = NULL; - ofpbuf_init(&ofpacts, 0); - while (recv_flow_stats_reply(vconn, send_xid, &reply, &fs, &ofpacts)) { + struct ofputil_flow_stats *fses; + size_t n_fses; + run(vconn_dump_flows(vconn, &fsr, protocol, &fses, &n_fses), + "dump flows"); + for (size_t i = 0; i < n_fses; i++) { + const struct ofputil_flow_stats *fs = &fses[i]; struct fte_version *version; version = xmalloc(sizeof *version); - version->cookie = fs.cookie; - version->idle_timeout = fs.idle_timeout; - version->hard_timeout = fs.hard_timeout; - version->importance = fs.importance; + version->cookie = fs->cookie; + version->idle_timeout = fs->idle_timeout; + version->hard_timeout = fs->hard_timeout; + version->importance = fs->importance; version->flags = 0; - version->ofpacts_len = fs.ofpacts_len; - version->ofpacts = xmemdup(fs.ofpacts, fs.ofpacts_len); - version->table_id = fs.table_id; + version->ofpacts_len = fs->ofpacts_len; + version->ofpacts = xmemdup(fs->ofpacts, fs->ofpacts_len); + version->table_id = fs->table_id; - fte_queue(state, &fs.match, fs.priority, version, index); + fte_queue(state, &fs->match, fs->priority, version, index); } - ofpbuf_uninit(&ofpacts); } static void