@@ -7,7 +7,9 @@ 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 can now trace put_dhcp_opts and put_dhcp_optsv6 actions.
+ * ovn-trace:
+ - New --ovs option to also print OpenFlow flows.
+ - put_dhcp_opts and put_dhcp_optsv6 actions may now be traced.
- Fixed regression in table stats maintenance introduced in OVS
2.3.0, wherein the number of OpenFlow table hits and misses was
not accurate.
@@ -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 <stdbool.h>
-#include <openvswitch/list.h>
-#include <openvswitch/types.h>
-#include <openflow/openflow.h>
+#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;
@@ -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,
@@ -255,6 +255,65 @@
<dd>
Selects all three forms of output.
</dd>
+
+ <dt><code>--ovs</code>[<code>=</code><var>remote</var>]</dt>
+ <dd>
+ <p>
+ Makes <code>ovn-trace</code> attempt to obtain and display the OpenFlow
+ flows that correspond to each OVN logical flow. To do so,
+ <code>ovn-trace</code> connects to <var>remote</var> (by default,
+ <code>unix:@RUNDIR@/br-int.mgmt</code>) over OpenFlow and retrieves the
+ flows. If <var>remote</var> is specified, it must be an active
+ OpenFlow connection method described in <code>ovs-ofctl</code>(8).
+ </p>
+
+ <p>
+ Keep in mind a few important aspects of the difference between logical
+ flows and OpenFlow:
+ </p>
+
+ <ul>
+ <li>
+ 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 match more than one of these flows (which is not a problem
+ because all of them have the same actions). <code>ovn-trace</code>
+ currently shows all corresponding flows.
+ </li>
+
+ <li>
+ Some logical flows can map to the Open vSwitch ``conjunctive match''
+ extension (see <code>ovs-ofctl</code>(8)). The OpenFlow match for a
+ conjunctive match shows a match on <code>conj_id</code>. Currently
+ <code>ovn-trace</code> cannot display the flows with
+ <code>conjunction</code> actions that effectively produce the
+ <code>conj_id</code> match.
+ </li>
+
+ <li>
+ 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.
+ </li>
+
+ <li>
+ 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 <code>ovn-architecture</code>(7) for
+ information on these flows.
+ </li>
+
+ <li>
+ When <code>ovn-trace</code> omits uninteresting logical flows from
+ output, it does not look up the corresponding OpenFlow flows.
+ </li>
+ </ul>
+ </dd>
</dl>
<h2>Daemon Options</h2>
@@ -266,7 +325,7 @@
<h2>PKI Options</h2>
<p>
PKI configuration is required to use SSL for the connection to the
- database.
+ database (and the switch, if <code>--ovs</code> is specified).
</p>
<xi:include href="lib/ssl.xml" xmlns:xi="http://www.w3.org/2003/XInclude"/>
@@ -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"
@@ -63,6 +65,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);
@@ -143,6 +149,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[])
{
@@ -153,6 +165,7 @@ parse_options(int argc, char *argv[])
OPT_SUMMARY,
OPT_MINIMAL,
OPT_ALL,
+ OPT_OVS,
DAEMON_OPTION_ENUMS,
SSL_OPTION_ENUMS,
VLOG_OPTION_ENUMS
@@ -164,6 +177,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,
@@ -207,6 +221,10 @@ parse_options(int argc, char *argv[])
detailed = summary = minimal = true;
break;
+ case OPT_OVS:
+ ovs = optarg ? optarg : default_ovs();
+ break;
+
case 'h':
usage();
@@ -245,7 +263,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\
@@ -257,11 +275,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);
}
@@ -303,6 +324,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;
@@ -644,6 +666,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);
@@ -1394,6 +1417,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)
@@ -1417,7 +1488,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)",
@@ -1430,6 +1502,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);
}
@@ -1463,6 +1538,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\")",
@@ -1494,6 +1576,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);
}
@@ -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
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 <blp@ovn.org> --- NEWS | 4 +- 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, 315 insertions(+), 129 deletions(-)