vswitchd: Add unixctl command to dump all flows, including hidden ones
authorJustin Pettit <jpettit@nicira.com>
Tue, 14 Jul 2009 20:03:57 +0000 (13:03 -0700)
committerJustin Pettit <jpettit@nicira.com>
Thu, 30 Jul 2009 00:04:18 +0000 (17:04 -0700)
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.

lib/ofp-print.c
lib/ofp-print.h
secchan/ofproto.c
secchan/ofproto.h
vswitchd/bridge.c

index 1e0f0aa09ddb35d140f506b9a799d4864b4e6361..63b59ddb0770d97d694a15d3e75de501c738909a 100644 (file)
@@ -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);
index 2c9548b326bdf3d0e744f2bcffc577b567f3780f..a362a65a1a4e59fae5fe0a3cfe028e54df1b7b96 100644 (file)
@@ -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
index 19baab9ba42918ce259d2d3548509ac6fe20c42e..9f3a8496365cfab3037b84b62ce1442ecf71d19f 100644 (file)
@@ -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;
index f4c1b4098681cb65976a7ea70ed8a1966b49b6fd..398cac4fd4401216c2da96806c1e79d50dd1522c 100644 (file)
@@ -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 *,
index b1073205df7af9088c74ddd0ca3be36791fe85b8..057172e446d95edd1810621b142a9be47934391e 100644 (file)
@@ -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)
 {