summaryrefslogtreecommitdiff
path: root/ofproto
diff options
context:
space:
mode:
authorBen Pfaff <blp@ovn.org>2016-12-06 14:11:15 -0800
committerBen Pfaff <blp@ovn.org>2017-01-05 16:58:41 -0800
commitd13ee22840c76e2e86274094b9d136440b28bcb0 (patch)
treea8e6ccbfbe30a720bcfa9edb388dc0a1fec83a2f /ofproto
parent07a3cd5c0a15596b3a53d1f3b5b974ceb7fa86dd (diff)
downloadopenvswitch-d13ee22840c76e2e86274094b9d136440b28bcb0.tar.gz
ofproto-dpif: Break trace functionality into a separate source file.
An upcoming commit will rewrite much of the ofproto/trace functionality. As the mechanism behind this grows and evolves, it makes sense to move it into its own file, especially since ofproto-dpif.c is too big anyway. Signed-off-by: Ben Pfaff <blp@ovn.org> Acked-by: Lance Richardson <lrichard@redhat.com> Acked-by: Justin Pettit <jpettit@ovn.org>
Diffstat (limited to 'ofproto')
-rw-r--r--ofproto/automake.mk2
-rw-r--r--ofproto/ofproto-dpif-trace.c555
-rw-r--r--ofproto/ofproto-dpif-trace.h20
-rw-r--r--ofproto/ofproto-dpif.c524
-rw-r--r--ofproto/ofproto-dpif.h2
5 files changed, 582 insertions, 521 deletions
diff --git a/ofproto/automake.mk b/ofproto/automake.mk
index 5a83a60e4..1c111945b 100644
--- a/ofproto/automake.mk
+++ b/ofproto/automake.mk
@@ -38,6 +38,8 @@ ofproto_libofproto_la_SOURCES = \
ofproto/ofproto-dpif-rid.h \
ofproto/ofproto-dpif-sflow.c \
ofproto/ofproto-dpif-sflow.h \
+ ofproto/ofproto-dpif-trace.c \
+ ofproto/ofproto-dpif-trace.h \
ofproto/ofproto-dpif-upcall.c \
ofproto/ofproto-dpif-upcall.h \
ofproto/ofproto-dpif-xlate.c \
diff --git a/ofproto/ofproto-dpif-trace.c b/ofproto/ofproto-dpif-trace.c
new file mode 100644
index 000000000..52c175875
--- /dev/null
+++ b/ofproto/ofproto-dpif-trace.c
@@ -0,0 +1,555 @@
+/*
+ * Copyright (c) 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.
+ * You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <config.h>
+
+#include "ofproto-dpif-trace.h"
+
+#include "dpif.h"
+#include "ofproto-dpif-xlate.h"
+#include "openvswitch/ofp-parse.h"
+#include "unixctl.h"
+
+struct trace_ctx {
+ struct xlate_out xout;
+ struct xlate_in xin;
+ const struct flow *key;
+ struct flow flow;
+ struct ds *result;
+ struct flow_wildcards wc;
+ struct ofpbuf odp_actions;
+};
+
+static void ofproto_trace(struct ofproto_dpif *, struct flow *,
+ const struct dp_packet *packet,
+ const struct ofpact[], size_t ofpacts_len,
+ struct ds *);
+
+static void
+trace_format_rule(struct ofproto *ofproto, struct ds *result, int level,
+ const struct rule_dpif *rule)
+{
+ const struct rule_actions *actions;
+ ovs_be64 cookie;
+
+ ds_put_char_multiple(result, '\t', level);
+ if (!rule) {
+ ds_put_cstr(result, "No match\n");
+ return;
+ }
+
+ ovs_mutex_lock(&rule->up.mutex);
+ cookie = rule->up.flow_cookie;
+ ovs_mutex_unlock(&rule->up.mutex);
+
+ ds_put_format(result, "Rule: table=%"PRIu8" cookie=%#"PRIx64" ",
+ rule ? rule->up.table_id : 0, ntohll(cookie));
+ cls_rule_format(&rule->up.cr, ofproto_get_tun_tab(ofproto), result);
+ ds_put_char(result, '\n');
+
+ actions = rule_get_actions(&rule->up);
+
+ ds_put_char_multiple(result, '\t', level);
+ ds_put_cstr(result, "OpenFlow actions=");
+ ofpacts_format(actions->ofpacts, actions->ofpacts_len, result);
+ ds_put_char(result, '\n');
+}
+
+static void
+trace_format_flow(struct ds *result, int level, const char *title,
+ struct trace_ctx *trace)
+{
+ ds_put_char_multiple(result, '\t', level);
+ ds_put_format(result, "%s: ", title);
+ /* Do not report unchanged flows for resubmits. */
+ if ((level > 0 && flow_equal(&trace->xin.flow, &trace->flow))
+ || (level == 0 && flow_equal(&trace->xin.flow, trace->key))) {
+ ds_put_cstr(result, "unchanged");
+ } else {
+ flow_format(result, &trace->xin.flow);
+ trace->flow = trace->xin.flow;
+ }
+ ds_put_char(result, '\n');
+}
+
+static void
+trace_format_regs(struct ds *result, int level, const char *title,
+ struct trace_ctx *trace)
+{
+ size_t i;
+
+ ds_put_char_multiple(result, '\t', level);
+ ds_put_format(result, "%s:", title);
+ for (i = 0; i < FLOW_N_REGS; i++) {
+ ds_put_format(result, " reg%"PRIuSIZE"=0x%"PRIx32, i, trace->flow.regs[i]);
+ }
+ ds_put_char(result, '\n');
+}
+
+static void
+trace_format_odp(struct ds *result, int level, const char *title,
+ struct trace_ctx *trace)
+{
+ struct ofpbuf *odp_actions = &trace->odp_actions;
+
+ ds_put_char_multiple(result, '\t', level);
+ ds_put_format(result, "%s: ", title);
+ format_odp_actions(result, odp_actions->data, odp_actions->size);
+ ds_put_char(result, '\n');
+}
+
+static void
+trace_format_megaflow(struct ds *result, int level, const char *title,
+ struct trace_ctx *trace)
+{
+ struct match match;
+
+ ds_put_char_multiple(result, '\t', level);
+ ds_put_format(result, "%s: ", title);
+ match_init(&match, trace->key, &trace->wc);
+ match_format(&match, result, OFP_DEFAULT_PRIORITY);
+ ds_put_char(result, '\n');
+}
+
+static void trace_report(struct xlate_in *, int indentation,
+ const char *format, ...)
+ OVS_PRINTF_FORMAT(3, 4);
+static void trace_report_valist(struct xlate_in *, int indentation,
+ const char *format, va_list args)
+ OVS_PRINTF_FORMAT(3, 0);
+
+static void
+trace_resubmit(struct xlate_in *xin, struct rule_dpif *rule, int indentation)
+{
+ struct trace_ctx *trace = CONTAINER_OF(xin, struct trace_ctx, xin);
+ struct ds *result = trace->result;
+
+ if (!indentation) {
+ if (rule == xin->ofproto->miss_rule) {
+ trace_report(xin, indentation,
+ "No match, flow generates \"packet in\"s.");
+ } else if (rule == xin->ofproto->no_packet_in_rule) {
+ trace_report(xin, indentation, "No match, packets dropped because "
+ "OFPPC_NO_PACKET_IN is set on in_port.");
+ } else if (rule == xin->ofproto->drop_frags_rule) {
+ trace_report(xin, indentation,
+ "Packets dropped because they are IP fragments and "
+ "the fragment handling mode is \"drop\".");
+ }
+ }
+
+ ds_put_char(result, '\n');
+ if (indentation) {
+ trace_format_flow(result, indentation, "Resubmitted flow", trace);
+ trace_format_regs(result, indentation, "Resubmitted regs", trace);
+ trace_format_odp(result, indentation, "Resubmitted odp", trace);
+ trace_format_megaflow(result, indentation, "Resubmitted megaflow",
+ trace);
+ }
+ trace_format_rule(&xin->ofproto->up, result, indentation, rule);
+}
+
+static void
+trace_report_valist(struct xlate_in *xin, int indentation,
+ const char *format, va_list args)
+{
+ struct trace_ctx *trace = CONTAINER_OF(xin, struct trace_ctx, xin);
+ struct ds *result = trace->result;
+
+ ds_put_char_multiple(result, '\t', indentation);
+ ds_put_format_valist(result, format, args);
+ ds_put_char(result, '\n');
+}
+
+static void
+trace_report(struct xlate_in *xin, int indentation, const char *format, ...)
+{
+ va_list args;
+
+ va_start(args, format);
+ trace_report_valist(xin, indentation, format, args);
+ va_end(args);
+}
+
+/* Parses the 'argc' elements of 'argv', ignoring argv[0]. The following
+ * forms are supported:
+ *
+ * - [dpname] odp_flow [-generate | packet]
+ * - bridge br_flow [-generate | packet]
+ *
+ * On success, initializes '*ofprotop' and 'flow' and returns NULL. On failure
+ * returns a nonnull malloced error message. */
+static char * OVS_WARN_UNUSED_RESULT
+parse_flow_and_packet(int argc, const char *argv[],
+ struct ofproto_dpif **ofprotop, struct flow *flow,
+ struct dp_packet **packetp)
+{
+ const struct dpif_backer *backer = NULL;
+ const char *error = NULL;
+ char *m_err = NULL;
+ struct simap port_names = SIMAP_INITIALIZER(&port_names);
+ struct dp_packet *packet;
+ struct ofpbuf odp_key;
+ struct ofpbuf odp_mask;
+
+ ofpbuf_init(&odp_key, 0);
+ ofpbuf_init(&odp_mask, 0);
+
+ /* Handle "-generate" or a hex string as the last argument. */
+ if (!strcmp(argv[argc - 1], "-generate")) {
+ packet = dp_packet_new(0);
+ argc--;
+ } else {
+ error = eth_from_hex(argv[argc - 1], &packet);
+ if (!error) {
+ argc--;
+ } else if (argc == 4) {
+ /* The 3-argument form must end in "-generate' or a hex string. */
+ goto exit;
+ }
+ error = NULL;
+ }
+
+ /* odp_flow can have its in_port specified as a name instead of port no.
+ * We do not yet know whether a given flow is a odp_flow or a br_flow.
+ * But, to know whether a flow is odp_flow through odp_flow_from_string(),
+ * we need to create a simap of name to port no. */
+ if (argc == 3) {
+ const char *dp_type;
+ if (!strncmp(argv[1], "ovs-", 4)) {
+ dp_type = argv[1] + 4;
+ } else {
+ dp_type = argv[1];
+ }
+ backer = shash_find_data(&all_dpif_backers, dp_type);
+ } else if (argc == 2) {
+ struct shash_node *node;
+ if (shash_count(&all_dpif_backers) == 1) {
+ node = shash_first(&all_dpif_backers);
+ backer = node->data;
+ }
+ } else {
+ error = "Syntax error";
+ goto exit;
+ }
+ if (backer && backer->dpif) {
+ struct dpif_port dpif_port;
+ struct dpif_port_dump port_dump;
+ DPIF_PORT_FOR_EACH (&dpif_port, &port_dump, backer->dpif) {
+ simap_put(&port_names, dpif_port.name,
+ odp_to_u32(dpif_port.port_no));
+ }
+ }
+
+ /* Parse the flow and determine whether a datapath or
+ * bridge is specified. If function odp_flow_key_from_string()
+ * returns 0, the flow is a odp_flow. If function
+ * parse_ofp_exact_flow() returns NULL, the flow is a br_flow. */
+ if (!odp_flow_from_string(argv[argc - 1], &port_names,
+ &odp_key, &odp_mask)) {
+ if (!backer) {
+ error = "Cannot find the datapath";
+ goto exit;
+ }
+
+ if (odp_flow_key_to_flow(odp_key.data, odp_key.size, flow) == ODP_FIT_ERROR) {
+ error = "Failed to parse datapath flow key";
+ goto exit;
+ }
+
+ *ofprotop = xlate_lookup_ofproto(backer, flow,
+ &flow->in_port.ofp_port);
+ if (*ofprotop == NULL) {
+ error = "Invalid datapath flow";
+ goto exit;
+ }
+
+ flow->tunnel.metadata.tab = ofproto_get_tun_tab(&(*ofprotop)->up);
+
+ /* Convert Geneve options to OpenFlow format now. This isn't actually
+ * required in order to get the right results since the ofproto xlate
+ * actions will handle this for us. However, converting now ensures
+ * that our formatting code will always be able to consistently print
+ * in OpenFlow format, which is what we use here. */
+ if (flow->tunnel.flags & FLOW_TNL_F_UDPIF) {
+ struct flow_tnl tnl;
+ int err;
+
+ memcpy(&tnl, &flow->tunnel, sizeof tnl);
+ err = tun_metadata_from_geneve_udpif(flow->tunnel.metadata.tab,
+ &tnl, &tnl, &flow->tunnel);
+ if (err) {
+ error = "Failed to parse Geneve options";
+ goto exit;
+ }
+ }
+ } else {
+ char *err;
+
+ if (argc != 3) {
+ error = "Must specify bridge name";
+ goto exit;
+ }
+
+ *ofprotop = ofproto_dpif_lookup(argv[1]);
+ if (!*ofprotop) {
+ error = "Unknown bridge name";
+ goto exit;
+ }
+
+ err = parse_ofp_exact_flow(flow, NULL,
+ ofproto_get_tun_tab(&(*ofprotop)->up),
+ argv[argc - 1], NULL);
+ if (err) {
+ m_err = xasprintf("Bad openflow flow syntax: %s", err);
+ free(err);
+ goto exit;
+ }
+ }
+
+ /* Generate a packet, if requested. */
+ if (packet) {
+ if (!dp_packet_size(packet)) {
+ flow_compose(packet, flow);
+ } else {
+ /* Use the metadata from the flow and the packet argument
+ * to reconstruct the flow. */
+ pkt_metadata_from_flow(&packet->md, flow);
+ flow_extract(packet, flow);
+ }
+ }
+
+exit:
+ if (error && !m_err) {
+ m_err = xstrdup(error);
+ }
+ if (m_err) {
+ dp_packet_delete(packet);
+ packet = NULL;
+ }
+ *packetp = packet;
+ ofpbuf_uninit(&odp_key);
+ ofpbuf_uninit(&odp_mask);
+ simap_destroy(&port_names);
+ return m_err;
+}
+
+static void
+ofproto_unixctl_trace(struct unixctl_conn *conn, int argc, const char *argv[],
+ void *aux OVS_UNUSED)
+{
+ struct ofproto_dpif *ofproto;
+ struct dp_packet *packet;
+ char *error;
+ struct flow flow;
+
+ error = parse_flow_and_packet(argc, argv, &ofproto, &flow, &packet);
+ if (!error) {
+ struct ds result;
+
+ ds_init(&result);
+ ofproto_trace(ofproto, &flow, packet, NULL, 0, &result);
+ unixctl_command_reply(conn, ds_cstr(&result));
+ ds_destroy(&result);
+ dp_packet_delete(packet);
+ } else {
+ unixctl_command_reply_error(conn, error);
+ free(error);
+ }
+}
+
+static void
+ofproto_unixctl_trace_actions(struct unixctl_conn *conn, int argc,
+ const char *argv[], void *aux OVS_UNUSED)
+{
+ enum ofputil_protocol usable_protocols;
+ struct ofproto_dpif *ofproto;
+ bool enforce_consistency;
+ struct ofpbuf ofpacts;
+ struct dp_packet *packet;
+ struct ds result;
+ struct flow flow;
+ uint16_t in_port;
+
+ /* Three kinds of error return values! */
+ enum ofperr retval;
+ char *error;
+
+ packet = NULL;
+ ds_init(&result);
+ ofpbuf_init(&ofpacts, 0);
+
+ /* Parse actions. */
+ error = ofpacts_parse_actions(argv[--argc], &ofpacts, &usable_protocols);
+ if (error) {
+ unixctl_command_reply_error(conn, error);
+ free(error);
+ goto exit;
+ }
+
+ /* OpenFlow 1.1 and later suggest that the switch enforces certain forms of
+ * consistency between the flow and the actions. With -consistent, we
+ * enforce consistency even for a flow supported in OpenFlow 1.0. */
+ if (!strcmp(argv[1], "-consistent")) {
+ enforce_consistency = true;
+ argv++;
+ argc--;
+ } else {
+ enforce_consistency = false;
+ }
+
+ error = parse_flow_and_packet(argc, argv, &ofproto, &flow, &packet);
+ if (error) {
+ unixctl_command_reply_error(conn, error);
+ free(error);
+ goto exit;
+ }
+
+ /* Do the same checks as handle_packet_out() in ofproto.c.
+ *
+ * We pass a 'table_id' of 0 to ofpacts_check(), which isn't
+ * strictly correct because these actions aren't in any table, but it's OK
+ * because it 'table_id' is used only to check goto_table instructions, but
+ * packet-outs take a list of actions and therefore it can't include
+ * instructions.
+ *
+ * We skip the "meter" check here because meter is an instruction, not an
+ * action, and thus cannot appear in ofpacts. */
+ in_port = ofp_to_u16(flow.in_port.ofp_port);
+ if (in_port >= ofproto->up.max_ports && in_port < ofp_to_u16(OFPP_MAX)) {
+ unixctl_command_reply_error(conn, "invalid in_port");
+ goto exit;
+ }
+ if (enforce_consistency) {
+ retval = ofpacts_check_consistency(ofpacts.data, ofpacts.size, &flow,
+ u16_to_ofp(ofproto->up.max_ports),
+ 0, ofproto->up.n_tables,
+ usable_protocols);
+ } else {
+ retval = ofpacts_check(ofpacts.data, ofpacts.size, &flow,
+ u16_to_ofp(ofproto->up.max_ports), 0,
+ ofproto->up.n_tables, &usable_protocols);
+ }
+ if (!retval) {
+ retval = ofproto_check_ofpacts(&ofproto->up, ofpacts.data,
+ ofpacts.size);
+ }
+
+ if (retval) {
+ ds_clear(&result);
+ ds_put_format(&result, "Bad actions: %s", ofperr_to_string(retval));
+ unixctl_command_reply_error(conn, ds_cstr(&result));
+ goto exit;
+ }
+
+ ofproto_trace(ofproto, &flow, packet,
+ ofpacts.data, ofpacts.size, &result);
+ unixctl_command_reply(conn, ds_cstr(&result));
+
+exit:
+ ds_destroy(&result);
+ dp_packet_delete(packet);
+ ofpbuf_uninit(&ofpacts);
+}
+
+/* Implements a "trace" through 'ofproto''s flow table, appending a textual
+ * description of the results to 'ds'.
+ *
+ * The trace follows a packet with the specified 'flow' through the flow
+ * table. 'packet' may be nonnull to trace an actual packet, with consequent
+ * side effects (if it is nonnull then its flow must be 'flow').
+ *
+ * If 'ofpacts' is nonnull then its 'ofpacts_len' bytes specify the actions to
+ * trace, otherwise the actions are determined by a flow table lookup. */
+static void
+ofproto_trace(struct ofproto_dpif *ofproto, struct flow *flow,
+ const struct dp_packet *packet,
+ const struct ofpact ofpacts[], size_t ofpacts_len,
+ struct ds *ds)
+{
+ struct trace_ctx trace;
+ enum xlate_error error;
+
+ ds_put_format(ds, "Bridge: %s\n", ofproto->up.name);
+ ds_put_cstr(ds, "Flow: ");
+ flow_format(ds, flow);
+ ds_put_char(ds, '\n');
+
+ ofpbuf_init(&trace.odp_actions, 0);
+
+ trace.result = ds;
+ trace.key = flow; /* Original flow key, used for megaflow. */
+ trace.flow = *flow; /* May be modified by actions. */
+ xlate_in_init(&trace.xin, ofproto,
+ ofproto_dpif_get_tables_version(ofproto), flow,
+ flow->in_port.ofp_port, NULL, ntohs(flow->tcp_flags),
+ packet, &trace.wc, &trace.odp_actions);
+ trace.xin.ofpacts = ofpacts;
+ trace.xin.ofpacts_len = ofpacts_len;
+ trace.xin.resubmit_hook = trace_resubmit;
+ trace.xin.report_hook = trace_report_valist;
+
+ error = xlate_actions(&trace.xin, &trace.xout);
+ ds_put_char(ds, '\n');
+ trace.xin.flow.actset_output = 0;
+ trace_format_flow(ds, 0, "Final flow", &trace);
+ trace_format_megaflow(ds, 0, "Megaflow", &trace);
+
+ ds_put_cstr(ds, "Datapath actions: ");
+ format_odp_actions(ds, trace.odp_actions.data, trace.odp_actions.size);
+
+ if (error != XLATE_OK) {
+ ds_put_format(ds, "\nTranslation failed (%s), packet is dropped.\n",
+ xlate_strerror(error));
+ } else if (trace.xout.slow) {
+ enum slow_path_reason slow;
+
+ ds_put_cstr(ds, "\nThis flow is handled by the userspace "
+ "slow path because it:");
+
+ slow = trace.xout.slow;
+ while (slow) {
+ enum slow_path_reason bit = rightmost_1bit(slow);
+
+ ds_put_format(ds, "\n\t- %s.",
+ slow_path_reason_to_explanation(bit));
+
+ slow &= ~bit;
+ }
+ }
+
+ xlate_out_uninit(&trace.xout);
+ ofpbuf_uninit(&trace.odp_actions);
+}
+
+void
+ofproto_dpif_trace_init(void)
+{
+ static bool registered;
+ if (registered) {
+ return;
+ }
+ registered = true;
+
+ unixctl_command_register(
+ "ofproto/trace",
+ "{[dp_name] odp_flow | bridge br_flow} [-generate|packet]",
+ 1, 3, ofproto_unixctl_trace, NULL);
+ unixctl_command_register(
+ "ofproto/trace-packet-out",
+ "[-consistent] {[dp_name] odp_flow | bridge br_flow} [-generate|packet] actions",
+ 2, 6, ofproto_unixctl_trace_actions, NULL);
+}
diff --git a/ofproto/ofproto-dpif-trace.h b/ofproto/ofproto-dpif-trace.h
new file mode 100644
index 000000000..06d63f894
--- /dev/null
+++ b/ofproto/ofproto-dpif-trace.h
@@ -0,0 +1,20 @@
+/* Copyright (c) 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.
+ * You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License. */
+
+#ifndef OFPROTO_DPIF_TRACE_H
+#define OFPROTO_DPIF_TRACE_H 1
+
+void ofproto_dpif_trace_init(void);
+
+#endif /* ofproto-dpif-trace.h */
diff --git a/ofproto/ofproto-dpif.c b/ofproto/ofproto-dpif.c
index fabe4ca79..3b036a116 100644
--- a/ofproto/ofproto-dpif.c
+++ b/ofproto/ofproto-dpif.c
@@ -47,6 +47,7 @@
#include "ofproto-dpif-monitor.h"
#include "ofproto-dpif-rid.h"
#include "ofproto-dpif-sflow.h"
+#include "ofproto-dpif-trace.h"
#include "ofproto-dpif-upcall.h"
#include "ofproto-dpif-xlate.h"
#include "ofproto-dpif-xlate-cache.h"
@@ -197,11 +198,6 @@ ofproto_dpif_cast(const struct ofproto *ofproto)
return CONTAINER_OF(ofproto, struct ofproto_dpif, up);
}
-static void ofproto_trace(struct ofproto_dpif *, struct flow *,
- const struct dp_packet *packet,
- const struct ofpact[], size_t ofpacts_len,
- struct ds *);
-
/* Global variables. */
static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 5);
@@ -255,6 +251,7 @@ init(const struct shash *iface_hints)
}
ofproto_unixctl_init();
+ ofproto_dpif_trace_init();
udpif_init();
}
@@ -4550,7 +4547,7 @@ get_netflow_ids(const struct ofproto *ofproto_,
dpif_get_netflow_ids(ofproto->backer->dpif, engine_type, engine_id);
}
-static struct ofproto_dpif *
+struct ofproto_dpif *
ofproto_dpif_lookup(const char *name)
{
struct ofproto_dpif *ofproto;
@@ -4714,513 +4711,6 @@ ofproto_unixctl_mcast_snooping_show(struct unixctl_conn *conn,
ds_destroy(&ds);
}
-struct trace_ctx {
- struct xlate_out xout;
- struct xlate_in xin;
- const struct flow *key;
- struct flow flow;
- struct ds *result;
- struct flow_wildcards wc;
- struct ofpbuf odp_actions;
-};
-
-static void
-trace_format_rule(struct ofproto *ofproto, struct ds *result, int level,
- const struct rule_dpif *rule)
-{
- const struct rule_actions *actions;
- ovs_be64 cookie;
-
- ds_put_char_multiple(result, '\t', level);
- if (!rule) {
- ds_put_cstr(result, "No match\n");
- return;
- }
-
- ovs_mutex_lock(&rule->up.mutex);
- cookie = rule->up.flow_cookie;
- ovs_mutex_unlock(&rule->up.mutex);
-
- ds_put_format(result, "Rule: table=%"PRIu8" cookie=%#"PRIx64" ",
- rule ? rule->up.table_id : 0, ntohll(cookie));
- cls_rule_format(&rule->up.cr, ofproto_get_tun_tab(ofproto), result);
- ds_put_char(result, '\n');
-
- actions = rule->up.actions;
-
- ds_put_char_multiple(result, '\t', level);
- ds_put_cstr(result, "OpenFlow actions=");
- ofpacts_format(actions->ofpacts, actions->ofpacts_len, result);
- ds_put_char(result, '\n');
-}
-
-static void
-trace_format_flow(struct ds *result, int level, const char *title,
- struct trace_ctx *trace)
-{
- ds_put_char_multiple(result, '\t', level);
- ds_put_format(result, "%s: ", title);
- /* Do not report unchanged flows for resubmits. */
- if ((level > 0 && flow_equal(&trace->xin.flow, &trace->flow))
- || (level == 0 && flow_equal(&trace->xin.flow, trace->key))) {
- ds_put_cstr(result, "unchanged");
- } else {
- flow_format(result, &trace->xin.flow);
- trace->flow = trace->xin.flow;
- }
- ds_put_char(result, '\n');
-}
-
-static void
-trace_format_regs(struct ds *result, int level, const char *title,
- struct trace_ctx *trace)
-{
- size_t i;
-
- ds_put_char_multiple(result, '\t', level);
- ds_put_format(result, "%s:", title);
- for (i = 0; i < FLOW_N_REGS; i++) {
- ds_put_format(result, " reg%"PRIuSIZE"=0x%"PRIx32, i, trace->flow.regs[i]);
- }
- ds_put_char(result, '\n');
-}
-
-static void
-trace_format_odp(struct ds *result, int level, const char *title,
- struct trace_ctx *trace)
-{
- struct ofpbuf *odp_actions = &trace->odp_actions;
-
- ds_put_char_multiple(result, '\t', level);
- ds_put_format(result, "%s: ", title);
- format_odp_actions(result, odp_actions->data, odp_actions->size);
- ds_put_char(result, '\n');
-}
-
-static void
-trace_format_megaflow(struct ds *result, int level, const char *title,
- struct trace_ctx *trace)
-{
- struct match match;
-
- ds_put_char_multiple(result, '\t', level);
- ds_put_format(result, "%s: ", title);
- match_init(&match, trace->key, &trace->wc);
- match_format(&match, result, OFP_DEFAULT_PRIORITY);
- ds_put_char(result, '\n');
-}
-
-static void trace_report(struct xlate_in *, int indentation,
- const char *format, ...)
- OVS_PRINTF_FORMAT(3, 4);
-static void trace_report_valist(struct xlate_in *, int indentation,
- const char *format, va_list args)
- OVS_PRINTF_FORMAT(3, 0);
-
-static void
-trace_resubmit(struct xlate_in *xin, struct rule_dpif *rule, int indentation)
-{
- struct trace_ctx *trace = CONTAINER_OF(xin, struct trace_ctx, xin);
- struct ds *result = trace->result;
-
- if (!indentation) {
- if (rule == xin->ofproto->miss_rule) {
- trace_report(xin, indentation,
- "No match, flow generates \"packet in\"s.");
- } else if (rule == xin->ofproto->no_packet_in_rule) {
- trace_report(xin, indentation, "No match, packets dropped because "
- "OFPPC_NO_PACKET_IN is set on in_port.");
- } else if (rule == xin->ofproto->drop_frags_rule) {
- trace_report(xin, indentation,
- "Packets dropped because they are IP fragments and "
- "the fragment handling mode is \"drop\".");
- }
- }
-
- ds_put_char(result, '\n');
- if (indentation) {
- trace_format_flow(result, indentation, "Resubmitted flow", trace);
- trace_format_regs(result, indentation, "Resubmitted regs", trace);
- trace_format_odp(result, indentation, "Resubmitted odp", trace);
- trace_format_megaflow(result, indentation, "Resubmitted megaflow",
- trace);
- }
- trace_format_rule(&xin->ofproto->up, result, indentation, rule);
-}
-
-static void
-trace_report_valist(struct xlate_in *xin, int indentation,
- const char *format, va_list args)
-{
- struct trace_ctx *trace = CONTAINER_OF(xin, struct trace_ctx, xin);
- struct ds *result = trace->result;
-
- ds_put_char_multiple(result, '\t', indentation);
- ds_put_format_valist(result, format, args);
- ds_put_char(result, '\n');
-}
-
-static void
-trace_report(struct xlate_in *xin, int indentation, const char *format, ...)
-{
- va_list args;
-
- va_start(args, format);
- trace_report_valist(xin, indentation, format, args);
- va_end(args);
-}
-
-/* Parses the 'argc' elements of 'argv', ignoring argv[0]. The following
- * forms are supported:
- *
- * - [dpname] odp_flow [-generate | packet]
- * - bridge br_flow [-generate | packet]
- *
- * On success, initializes '*ofprotop' and 'flow' and returns NULL. On failure
- * returns a nonnull malloced error message. */
-static char * OVS_WARN_UNUSED_RESULT
-parse_flow_and_packet(int argc, const char *argv[],
- struct ofproto_dpif **ofprotop, struct flow *flow,
- struct dp_packet **packetp)
-{
- const struct dpif_backer *backer = NULL;
- const char *error = NULL;
- char *m_err = NULL;
- struct simap port_names = SIMAP_INITIALIZER(&port_names);
- struct dp_packet *packet;
- struct ofpbuf odp_key;
- struct ofpbuf odp_mask;
-
- ofpbuf_init(&odp_key, 0);
- ofpbuf_init(&odp_mask, 0);
-
- /* Handle "-generate" or a hex string as the last argument. */
- if (!strcmp(argv[argc - 1], "-generate")) {
- packet = dp_packet_new(0);
- argc--;
- } else {
- error = eth_from_hex(argv[argc - 1], &packet);
- if (!error) {
- argc--;
- } else if (argc == 4) {
- /* The 3-argument form must end in "-generate' or a hex string. */
- goto exit;
- }
- error = NULL;
- }
-
- /* odp_flow can have its in_port specified as a name instead of port no.
- * We do not yet know whether a given flow is a odp_flow or a br_flow.
- * But, to know whether a flow is odp_flow through odp_flow_from_string(),
- * we need to create a simap of name to port no. */
- if (argc == 3) {
- const char *dp_type;
- if (!strncmp(argv[1], "ovs-", 4)) {
- dp_type = argv[1] + 4;
- } else {
- dp_type = argv[1];
- }
- backer = shash_find_data(&all_dpif_backers, dp_type);
- } else if (argc == 2) {
- struct shash_node *node;
- if (shash_count(&all_dpif_backers) == 1) {
- node = shash_first(&all_dpif_backers);
- backer = node->data;
- }
- } else {
- error = "Syntax error";
- goto exit;
- }
- if (backer && backer->dpif) {
- struct dpif_port dpif_port;
- struct dpif_port_dump port_dump;
- DPIF_PORT_FOR_EACH (&dpif_port, &port_dump, backer->dpif) {
- simap_put(&port_names, dpif_port.name,
- odp_to_u32(dpif_port.port_no));
- }
- }
-
- /* Parse the flow and determine whether a datapath or
- * bridge is specified. If function odp_flow_key_from_string()
- * returns 0, the flow is a odp_flow. If function
- * parse_ofp_exact_flow() returns NULL, the flow is a br_flow. */
- if (!odp_flow_from_string(argv[argc - 1], &port_names,
- &odp_key, &odp_mask)) {
- if (!backer) {
- error = "Cannot find the datapath";
- goto exit;
- }
-
- if (odp_flow_key_to_flow(odp_key.data, odp_key.size, flow) == ODP_FIT_ERROR) {
- error = "Failed to parse datapath flow key";
- goto exit;
- }
-
- *ofprotop = xlate_lookup_ofproto(backer, flow,
- &flow->in_port.ofp_port);
- if (*ofprotop == NULL) {
- error = "Invalid datapath flow";
- goto exit;
- }
-
- flow->tunnel.metadata.tab = ofproto_get_tun_tab(&(*ofprotop)->up);
-
- /* Convert Geneve options to OpenFlow format now. This isn't actually
- * required in order to get the right results since the ofproto xlate
- * actions will handle this for us. However, converting now ensures
- * that our formatting code will always be able to consistently print
- * in OpenFlow format, which is what we use here. */
- if (flow->tunnel.flags & FLOW_TNL_F_UDPIF) {
- struct flow_tnl tnl;
- int err;
-
- memcpy(&tnl, &flow->tunnel, sizeof tnl);
- err = tun_metadata_from_geneve_udpif(flow->tunnel.metadata.tab,
- &tnl, &tnl, &flow->tunnel);
- if (err) {
- error = "Failed to parse Geneve options";
- goto exit;
- }
- }
- } else {
- char *err;
-
- if (argc != 3) {
- error = "Must specify bridge name";
- goto exit;
- }
-
- *ofprotop = ofproto_dpif_lookup(argv[1]);
- if (!*ofprotop) {
- error = "Unknown bridge name";
- goto exit;
- }
-
- err = parse_ofp_exact_flow(flow, NULL,
- ofproto_get_tun_tab(&(*ofprotop)->up),
- argv[argc - 1], NULL);
- if (err) {
- m_err = xasprintf("Bad openflow flow syntax: %s", err);
- free(err);
- goto exit;
- }
- }
-
- /* Generate a packet, if requested. */
- if (packet) {
- if (!dp_packet_size(packet)) {
- flow_compose(packet, flow);
- } else {
- /* Use the metadata from the flow and the packet argument
- * to reconstruct the flow. */
- pkt_metadata_from_flow(&packet->md, flow);
- flow_extract(packet, flow);
- }
- }
-
-exit:
- if (error && !m_err) {
- m_err = xstrdup(error);
- }
- if (m_err) {
- dp_packet_delete(packet);
- packet = NULL;
- }
- *packetp = packet;
- ofpbuf_uninit(&odp_key);
- ofpbuf_uninit(&odp_mask);
- simap_destroy(&port_names);
- return m_err;
-}
-
-static void
-ofproto_unixctl_trace(struct unixctl_conn *conn, int argc, const char *argv[],
- void *aux OVS_UNUSED)
-{
- struct ofproto_dpif *ofproto;
- struct dp_packet *packet;
- char *error;
- struct flow flow;
-
- error = parse_flow_and_packet(argc, argv, &ofproto, &flow, &packet);
- if (!error) {
- struct ds result;
-
- ds_init(&result);
- ofproto_trace(ofproto, &flow, packet, NULL, 0, &result);
- unixctl_command_reply(conn, ds_cstr(&result));
- ds_destroy(&result);
- dp_packet_delete(packet);
- } else {
- unixctl_command_reply_error(conn, error);
- free(error);
- }
-}
-
-static void
-ofproto_unixctl_trace_actions(struct unixctl_conn *conn, int argc,
- const char *argv[], void *aux OVS_UNUSED)
-{
- enum ofputil_protocol usable_protocols;
- struct ofproto_dpif *ofproto;
- bool enforce_consistency;
- struct ofpbuf ofpacts;
- struct dp_packet *packet;
- struct ds result;
- struct flow flow;
- uint16_t in_port;
-
- /* Three kinds of error return values! */
- enum ofperr retval;
- char *error;
-
- packet = NULL;
- ds_init(&result);
- ofpbuf_init(&ofpacts, 0);
-
- /* Parse actions. */
- error = ofpacts_parse_actions(argv[--argc], &ofpacts, &usable_protocols);
- if (error) {
- unixctl_command_reply_error(conn, error);
- free(error);
- goto exit;
- }
-
- /* OpenFlow 1.1 and later suggest that the switch enforces certain forms of
- * consistency between the flow and the actions. With -consistent, we
- * enforce consistency even for a flow supported in OpenFlow 1.0. */
- if (!strcmp(argv[1], "-consistent")) {
- enforce_consistency = true;
- argv++;
- argc--;
- } else {
- enforce_consistency = false;
- }
-
- error = parse_flow_and_packet(argc, argv, &ofproto, &flow, &packet);
- if (error) {
- unixctl_command_reply_error(conn, error);
- free(error);
- goto exit;
- }
-
- /* Do the same checks as handle_packet_out() in ofproto.c.
- *
- * We pass a 'table_id' of 0 to ofpacts_check(), which isn't
- * strictly correct because these actions aren't in any table, but it's OK
- * because it 'table_id' is used only to check goto_table instructions, but
- * packet-outs take a list of actions and therefore it can't include
- * instructions.
- *
- * We skip the "meter" check here because meter is an instruction, not an
- * action, and thus cannot appear in ofpacts. */
- in_port = ofp_to_u16(flow.in_port.ofp_port);
- if (in_port >= ofproto->up.max_ports && in_port < ofp_to_u16(OFPP_MAX)) {
- unixctl_command_reply_error(conn, "invalid in_port");
- goto exit;
- }
- if (enforce_consistency) {
- retval = ofpacts_check_consistency(ofpacts.data, ofpacts.size, &flow,
- u16_to_ofp(ofproto->up.max_ports),
- 0, ofproto->up.n_tables,
- usable_protocols);
- } else {
- retval = ofpacts_check(ofpacts.data, ofpacts.size, &flow,
- u16_to_ofp(ofproto->up.max_ports), 0,
- ofproto->up.n_tables, &usable_protocols);
- }
- if (!retval) {
- retval = ofproto_check_ofpacts(&ofproto->up, ofpacts.data,
- ofpacts.size);
- }
-
- if (retval) {
- ds_clear(&result);
- ds_put_format(&result, "Bad actions: %s", ofperr_to_string(retval));
- unixctl_command_reply_error(conn, ds_cstr(&result));
- goto exit;
- }
-
- ofproto_trace(ofproto, &flow, packet,
- ofpacts.data, ofpacts.size, &result);
- unixctl_command_reply(conn, ds_cstr(&result));
-
-exit:
- ds_destroy(&result);
- dp_packet_delete(packet);
- ofpbuf_uninit(&ofpacts);
-}
-
-/* Implements a "trace" through 'ofproto''s flow table, appending a textual
- * description of the results to 'ds'.
- *
- * The trace follows a packet with the specified 'flow' through the flow
- * table. 'packet' may be nonnull to trace an actual packet, with consequent
- * side effects (if it is nonnull then its flow must be 'flow').
- *
- * If 'ofpacts' is nonnull then its 'ofpacts_len' bytes specify the actions to
- * trace, otherwise the actions are determined by a flow table lookup. */
-static void
-ofproto_trace(struct ofproto_dpif *ofproto, struct flow *flow,
- const struct dp_packet *packet,
- const struct ofpact ofpacts[], size_t ofpacts_len,
- struct ds *ds)
-{
- struct trace_ctx trace;
- enum xlate_error error;
-
- ds_put_format(ds, "Bridge: %s\n", ofproto->up.name);
- ds_put_cstr(ds, "Flow: ");
- flow_format(ds, flow);
- ds_put_char(ds, '\n');
-
- ofpbuf_init(&trace.odp_actions, 0);
-
- trace.result = ds;
- trace.key = flow; /* Original flow key, used for megaflow. */
- trace.flow = *flow; /* May be modified by actions. */
- xlate_in_init(&trace.xin, ofproto,
- ofproto_dpif_get_tables_version(ofproto), flow,
- flow->in_port.ofp_port, NULL, ntohs(flow->tcp_flags),
- packet, &trace.wc, &trace.odp_actions);
- trace.xin.ofpacts = ofpacts;
- trace.xin.ofpacts_len = ofpacts_len;
- trace.xin.resubmit_hook = trace_resubmit;
- trace.xin.report_hook = trace_report_valist;
-
- error = xlate_actions(&trace.xin, &trace.xout);
- ds_put_char(ds, '\n');
- trace.xin.flow.actset_output = 0;
- trace_format_flow(ds, 0, "Final flow", &trace);
- trace_format_megaflow(ds, 0, "Megaflow", &trace);
-
- ds_put_cstr(ds, "Datapath actions: ");
- format_odp_actions(ds, trace.odp_actions.data, trace.odp_actions.size);
-
- if (error != XLATE_OK) {
- ds_put_format(ds, "\nTranslation failed (%s), packet is dropped.\n",
- xlate_strerror(error));
- } else if (trace.xout.slow) {
- enum slow_path_reason slow;
-
- ds_put_cstr(ds, "\nThis flow is handled by the userspace "
- "slow path because it:");
-
- slow = trace.xout.slow;
- while (slow) {
- enum slow_path_reason bit = rightmost_1bit(slow);
-
- ds_put_format(ds, "\n\t- %s.",
- slow_path_reason_to_explanation(bit));
-
- slow &= ~bit;
- }
- }
-
- xlate_out_uninit(&trace.xout);
- ofpbuf_uninit(&trace.odp_actions);
-}
-
/* Store the current ofprotos in 'ofproto_shash'. Returns a sorted list
* of the 'ofproto_shash' nodes. It is the responsibility of the caller
* to destroy 'ofproto_shash' and free the returned value. */
@@ -5478,14 +4968,6 @@ ofproto_unixctl_init(void)
}
registered = true;
- unixctl_command_register(
- "ofproto/trace",
- "{[dp_name] odp_flow | bridge br_flow} [-generate|packet]",
- 1, 3, ofproto_unixctl_trace, NULL);
- unixctl_command_register(
- "ofproto/trace-packet-out",
- "[-consistent] {[dp_name] odp_flow | bridge br_flow} [-generate|packet] actions",
- 2, 6, ofproto_unixctl_trace_actions, NULL);
unixctl_command_register("fdb/flush", "[bridge]", 0, 1,
ofproto_unixctl_fdb_flush, NULL);
unixctl_command_register("fdb/show", "bridge", 1, 1,
diff --git a/ofproto/ofproto-dpif.h b/ofproto/ofproto-dpif.h
index ea03768c5..d62c730f4 100644
--- a/ofproto/ofproto-dpif.h
+++ b/ofproto/ofproto-dpif.h
@@ -274,6 +274,8 @@ struct ofproto_dpif {
/* All existing ofproto_dpif instances, indexed by ->up.name. */
extern struct hmap all_ofproto_dpifs;
+struct ofproto_dpif *ofproto_dpif_lookup(const char *name);
+
ovs_version_t ofproto_dpif_get_tables_version(struct ofproto_dpif *);
void ofproto_dpif_credit_table_stats(struct ofproto_dpif *, uint8_t table_id,