diff options
author | Ben Pfaff <blp@ovn.org> | 2016-12-06 14:11:15 -0800 |
---|---|---|
committer | Ben Pfaff <blp@ovn.org> | 2017-01-05 16:58:41 -0800 |
commit | d13ee22840c76e2e86274094b9d136440b28bcb0 (patch) | |
tree | a8e6ccbfbe30a720bcfa9edb388dc0a1fec83a2f /ofproto | |
parent | 07a3cd5c0a15596b3a53d1f3b5b974ceb7fa86dd (diff) | |
download | openvswitch-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.mk | 2 | ||||
-rw-r--r-- | ofproto/ofproto-dpif-trace.c | 555 | ||||
-rw-r--r-- | ofproto/ofproto-dpif-trace.h | 20 | ||||
-rw-r--r-- | ofproto/ofproto-dpif.c | 524 | ||||
-rw-r--r-- | ofproto/ofproto-dpif.h | 2 |
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, |