Merge citrix into master.
[sliver-openvswitch.git] / ofproto / ofproto.c
index 55eb2c2..c26cdc2 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"
@@ -322,7 +324,7 @@ ofproto_create(const char *datapath, const struct ofhooks *ofhooks, void *aux,
 
     /* Initialize OpenFlow connections. */
     list_init(&p->all_conns);
-    p->controller = ofconn_create(p, rconn_create(15, 15));
+    p->controller = ofconn_create(p, rconn_create(5, 8));
     p->controller->pktbuf = pktbuf_create();
     p->controller->miss_send_len = OFP_DEFAULT_MISS_SEND_LEN;
     p->listeners = NULL;
@@ -1215,53 +1217,71 @@ static void
 update_port(struct ofproto *p, const char *devname)
 {
     struct odp_port odp_port;
-    struct ofport *ofport;
+    struct ofport *old_ofport;
+    struct ofport *new_ofport;
     int error;
 
     COVERAGE_INC(ofproto_update_port);
-    ofport = shash_find_data(&p->port_by_name, devname);
+
+    /* Query the datapath for port information. */
     error = dpif_port_query_by_name(p->dpif, devname, &odp_port);
-    if (!error) {
-        if (!ofport) {
-            /* New port. */
-            if (!ofport_conflicts(p, &odp_port)) {
-                ofport = make_ofport(&odp_port);
-                if (ofport) {
-                    ofport_install(p, ofport);
-                    send_port_status(p, ofport, OFPPR_ADD);
-                }
-            }
-        } else {
-            /* Modified port. */
-            struct ofport *new_ofport = make_ofport(&odp_port);
-            if (!new_ofport) {
-                return;
-            }
 
-            new_ofport->opp.config &= OFPPC_PORT_DOWN;
-            new_ofport->opp.config |= ofport->opp.config & ~OFPPC_PORT_DOWN;
-            if (ofport_equal(ofport, new_ofport)) {
-                /* False alarm--no change. */
-                ofport_free(new_ofport);
-            } else {
-                ofport_remove(p, ofport);
-                ofport_install(p, new_ofport);
-                ofport_free(ofport);
-                send_port_status(p, new_ofport, OFPPR_MODIFY);
-            }
-        }
-    } else if (error == ENOENT || error == ENODEV) {
-        /* Deleted port. */
-        if (ofport) {
-            send_port_status(p, ofport, OFPPR_DELETE);
-            ofport_remove(p, ofport);
-            ofport_free(ofport);
+    /* Find the old ofport. */
+    old_ofport = shash_find_data(&p->port_by_name, devname);
+    if (!error) {
+        if (!old_ofport) {
+            /* There's no port named 'devname' but there might be a port with
+             * the same port number.  This could happen if a port is deleted
+             * and then a new one added in its place very quickly, or if a port
+             * is renamed.  In the former case we want to send an OFPPR_DELETE
+             * and an OFPPR_ADD, and in the latter case we want to send a
+             * single OFPPR_MODIFY.  We can distinguish the cases by comparing
+             * the old port's ifindex against the new port, or perhaps less
+             * reliably but more portably by comparing the old port's MAC
+             * against the new port's MAC.  However, this code isn't that smart
+             * and always sends an OFPPR_MODIFY (XXX). */
+            old_ofport = port_array_get(&p->ports, odp_port.port);
         }
-    } else {
+    } else if (error != ENOENT && error != ENODEV) {
         VLOG_WARN_RL(&rl, "dpif_port_query_by_name returned unexpected error "
                      "%s", strerror(error));
         return;
     }
+
+    /* Create a new ofport. */
+    new_ofport = !error ? make_ofport(&odp_port) : NULL;
+
+    /* Eliminate a few pathological cases. */
+    if (!old_ofport && !new_ofport) {
+        return;
+    } else if (old_ofport && new_ofport) {
+        /* Most of the 'config' bits are OpenFlow soft state, but
+         * OFPPC_PORT_DOWN is maintained the kernel.  So transfer the OpenFlow
+         * bits from old_ofport.  (make_ofport() only sets OFPPC_PORT_DOWN and
+         * leaves the other bits 0.)  */
+        new_ofport->opp.config |= old_ofport->opp.config & ~OFPPC_PORT_DOWN;
+
+        if (ofport_equal(old_ofport, new_ofport)) {
+            /* False alarm--no change. */
+            ofport_free(new_ofport);
+            return;
+        }
+    }
+
+    /* Now deal with the normal cases. */
+    if (old_ofport) {
+        ofport_remove(p, old_ofport);
+    }
+    if (new_ofport) {
+        ofport_install(p, new_ofport);
+    }
+    send_port_status(p, new_ofport ? new_ofport : old_ofport,
+                     (!old_ofport ? OFPPR_ADD
+                      : !new_ofport ? OFPPR_DELETE
+                      : OFPPR_MODIFY));
+    ofport_free(old_ofport);
+
+    /* Update port groups. */
     refresh_port_groups(p);
 }
 
@@ -2475,6 +2495,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;