mirror of
https://github.com/openvswitch/ovs
synced 2025-08-22 01:51:26 +00:00
ovn-trace: New --ovs option to also print OpenFlow flows.
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> Acked-by: Justin Pettit <jpettit@ovn.org>
This commit is contained in:
parent
c80eac1f85
commit
d444a914fd
4
NEWS
4
NEWS
@ -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.
|
||||
* Support for managing SSL and remote connection configuration in
|
||||
northbound and southbound databases.
|
||||
- Fixed regression in table stats maintenance introduced in OVS
|
||||
|
@ -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.
|
||||
@ -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" {
|
||||
@ -56,6 +57,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;
|
||||
|
120
lib/vconn.c
120
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,124 @@ 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Sends 'fsr' 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. */
|
||||
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,58 @@
|
||||
<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>
|
||||
To make the best use of the output, it is important to understand the
|
||||
relationship between logical flows and OpenFlow flows.
|
||||
<code>ovn-architecture</code>(7), under <em>Architectural Physical Life
|
||||
Cycle of a Packet</em>, describes this relationship. Keep in mind the
|
||||
following points:
|
||||
</p>
|
||||
|
||||
<ul>
|
||||
<li>
|
||||
<code>ovn-trace</code> currently shows all the OpenFlow flows to
|
||||
which a logical flow corresponds, even though an actual packet
|
||||
ordinarily matches only one of these.
|
||||
</li>
|
||||
|
||||
<li>
|
||||
Some logical flows can map to the Open vSwitch ``conjunctive match''
|
||||
extension (see <code>ovs-ofctl</code>(8)). 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.
|
||||
</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.
|
||||
</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 +318,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);
|
||||
@ -1393,6 +1416,54 @@ may_omit_stage(const struct ovntrace_flow *f, uint8_t table_id)
|
||||
&& ovnact_get_NEXT(f->ovnacts)->ltable == table_id + 1);
|
||||
}
|
||||
|
||||
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,
|
||||
@ -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);
|
||||
}
|
||||
@ -1465,6 +1540,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\")",
|
||||
@ -1496,6 +1578,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);
|
||||
vconn = prepare_dump_flows(ctx->argc, ctx->argv, false,
|
||||
&fsr, &protocol);
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
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
|
||||
|
Loading…
x
Reference in New Issue
Block a user