From: Justin Pettit Date: Tue, 14 Jul 2009 20:03:57 +0000 (-0700) Subject: vswitchd: Add unixctl command to dump all flows, including hidden ones X-Git-Url: https://pintos-os.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=4f2cad2c3b1079ede2d3eed0dd25423552105255;p=openvswitch vswitchd: Add unixctl command to dump all flows, including hidden ones Previously, the only way to query the flow table was to run "ovs-ofctl dump-flows". This returned most flows, but not those marked hidden by secchan. Hidden flows are setup by mechanisms such as in-band control, since they must not be modified by users of the controller. However, when debugging problems on the switch, it is often useful to see what the flow table is actually doing. The new "bridge/dump-flows" command added to ovs-appctl shows all flows being used by the OpenFlow stack. --- diff --git a/lib/ofp-print.c b/lib/ofp-print.c index 1e0f0aa0..63b59ddb 100644 --- a/lib/ofp-print.c +++ b/lib/ofp-print.c @@ -37,8 +37,6 @@ #include "util.h" static void ofp_print_port_name(struct ds *string, uint16_t port); -static void ofp_print_match(struct ds *, const struct ofp_match *, - int verbosity); /* Returns a string that represents the contents of the Ethernet frame in the * 'len' bytes starting at 'data' to 'stream' as output by tcpdump. @@ -380,7 +378,7 @@ ofp_print_action(struct ds *string, const struct ofp_action_header *ah, return len; } -static void +void ofp_print_actions(struct ds *string, const struct ofp_action_header *action, size_t actions_len) { @@ -627,7 +625,7 @@ print_ip_netmask(struct ds *string, const char *leader, uint32_t ip, ds_put_char(string, ','); } -static void +void ofp_print_match(struct ds *f, const struct ofp_match *om, int verbosity) { char *s = ofp_match_to_string(om, verbosity); diff --git a/lib/ofp-print.h b/lib/ofp-print.h index 2c9548b3..a362a65a 100644 --- a/lib/ofp-print.h +++ b/lib/ofp-print.h @@ -24,6 +24,8 @@ struct ofp_flow_mod; struct ofp_match; +struct ds; +struct ofp_action_header; #ifdef __cplusplus extern "C" { @@ -32,11 +34,15 @@ extern "C" { void ofp_print(FILE *, const void *, size_t, int verbosity); void ofp_print_packet(FILE *stream, const void *data, size_t len, size_t total_len); +void ofp_print_actions(struct ds *, const struct ofp_action_header *, size_t); +void ofp_print_match(struct ds *, const struct ofp_match *, int verbosity); + char *ofp_to_string(const void *, size_t, int verbosity); char *ofp_match_to_string(const struct ofp_match *, int verbosity); char *ofp_packet_to_string(const void *data, size_t len, size_t total_len); char *ofp_message_type_to_string(uint8_t type); + #ifdef __cplusplus } #endif diff --git a/secchan/ofproto.c b/secchan/ofproto.c index 19baab9b..9f3a8496 100644 --- a/secchan/ofproto.c +++ b/secchan/ofproto.c @@ -26,6 +26,7 @@ #include "coverage.h" #include "discovery.h" #include "dpif.h" +#include "dynamic-string.h" #include "executer.h" #include "fail-open.h" #include "in-band.h" @@ -51,6 +52,7 @@ #include "svec.h" #include "tag.h" #include "timeval.h" +#include "unixctl.h" #include "vconn.h" #include "vconn-ssl.h" #include "xtoxll.h" @@ -2471,6 +2473,59 @@ handle_flow_stats_request(struct ofproto *p, struct ofconn *ofconn, return 0; } +struct flow_stats_ds_cbdata { + struct ofproto *ofproto; + struct ds *results; +}; + +static void +flow_stats_ds_cb(struct cls_rule *rule_, void *cbdata_) +{ + struct rule *rule = rule_from_cls_rule(rule_); + struct flow_stats_ds_cbdata *cbdata = cbdata_; + struct ds *results = cbdata->results; + struct ofp_match match; + uint64_t packet_count, byte_count; + size_t act_len = sizeof *rule->actions * rule->n_actions; + + /* Don't report on subrules. */ + if (rule->super != NULL) { + return; + } + + query_stats(cbdata->ofproto, rule, &packet_count, &byte_count); + flow_to_match(&rule->cr.flow, rule->cr.wc.wildcards, &match); + + ds_put_format(results, "duration=%llds, ", + (time_msec() - rule->created) / 1000); + ds_put_format(results, "priority=%u", rule->cr.priority); + ds_put_format(results, "n_packets=%"PRIu64", ", packet_count); + ds_put_format(results, "n_bytes=%"PRIu64", ", byte_count); + ofp_print_match(results, &match, true); + ofp_print_actions(results, &rule->actions->header, act_len); + ds_put_cstr(results, "\n"); +} + +/* Adds a pretty-printed description of all flows to 'results', including + * those marked hidden by secchan (e.g., by in-band control). */ +void +ofproto_get_all_flows(struct ofproto *p, struct ds *results) +{ + struct ofp_match match; + struct cls_rule target; + struct flow_stats_ds_cbdata cbdata; + + memset(&match, 0, sizeof match); + match.wildcards = htonl(OFPFW_ALL); + + cbdata.ofproto = p; + cbdata.results = results; + + cls_rule_from_match(&target, &match, 0); + classifier_for_each_match(&p->cls, &target, CLS_INC_ALL, + flow_stats_ds_cb, &cbdata); +} + struct aggregate_stats_cbdata { struct ofproto *ofproto; uint16_t out_port; diff --git a/secchan/ofproto.h b/secchan/ofproto.h index f4c1b409..398cac4f 100644 --- a/secchan/ofproto.h +++ b/secchan/ofproto.h @@ -80,6 +80,7 @@ bool ofproto_get_discovery(const struct ofproto *); const char *ofproto_get_controller(const struct ofproto *); void ofproto_get_listeners(const struct ofproto *, struct svec *); void ofproto_get_snoops(const struct ofproto *, struct svec *); +void ofproto_get_all_flows(struct ofproto *p, struct ds *); /* Functions for use by ofproto implementation modules, not by clients. */ int ofproto_send_packet(struct ofproto *, const flow_t *, diff --git a/vswitchd/bridge.c b/vswitchd/bridge.c index b1073205..057172e4 100644 --- a/vswitchd/bridge.c +++ b/vswitchd/bridge.c @@ -192,6 +192,7 @@ enum { DP_MAX = 256 }; static struct bridge *bridge_create(const char *name); static void bridge_destroy(struct bridge *); static struct bridge *bridge_lookup(const char *name); +static void bridge_unixctl_dump_flows(struct unixctl_conn *, const char *); static int bridge_run_one(struct bridge *); static void bridge_reconfigure_one(struct bridge *); static void bridge_reconfigure_controller(struct bridge *); @@ -303,6 +304,8 @@ bridge_init(void) } } + unixctl_command_register("bridge/dump-flows", bridge_unixctl_dump_flows); + bridge_reconfigure(); } @@ -958,6 +961,27 @@ bridge_get_datapathid(const char *name) return br ? ofproto_get_datapath_id(br->ofproto) : 0; } +/* Handle requests for a listing of all flows known by the OpenFlow + * stack, including those normally hidden. */ +static void +bridge_unixctl_dump_flows(struct unixctl_conn *conn, const char *args) +{ + struct bridge *br; + struct ds results; + + br = bridge_lookup(args); + if (!br) { + unixctl_command_reply(conn, 501, "Unknown bridge"); + return; + } + + ds_init(&results); + ofproto_get_all_flows(br->ofproto, &results); + + unixctl_command_reply(conn, 200, ds_cstr(&results)); + ds_destroy(&results); +} + static int bridge_run_one(struct bridge *br) {