mirroring: Allow learning to be disabled on a VLAN.
[sliver-openvswitch.git] / vswitchd / mgmt.c
index a65934b..8da640f 100644 (file)
@@ -19,6 +19,9 @@
 #include <assert.h>
 #include <errno.h>
 #include <stdlib.h>
+#include <sys/stat.h>
+#include <sys/socket.h>
+#include <sys/types.h>
 
 #include "bridge.h"
 #include "cfg.h"
@@ -51,6 +54,7 @@ static struct rconn *mgmt_rconn;
 static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(60, 60);
 static struct svec capabilities;
 static struct ofpbuf ext_data_buffer;
+static uint32_t ext_data_xid = UINT32_MAX;
 uint64_t mgmt_id;
 
 
@@ -101,6 +105,7 @@ mgmt_configure_ssl(void)
     static char *private_key_file;
     static char *certificate_file;
     static char *cacert_file;
+    struct stat s;
 
     /* XXX SSL should be configurable separate from the bridges.
      * XXX should be possible to de-configure SSL. */
@@ -112,7 +117,13 @@ mgmt_configure_ssl(void)
         vconn_ssl_set_certificate_file(certificate_file);
     }
 
-    if (config_string_change("ssl.ca-cert", &cacert_file)) {
+    /* We assume that even if the filename hasn't changed, if the CA cert 
+     * file has been removed, that we want to move back into
+     * boot-strapping mode.  This opens a small security hole, because
+     * the old certificate will still be trusted until vSwitch is
+     * restarted.  We may want to address this in vconn's SSL library. */
+    if (config_string_change("ssl.ca-cert", &cacert_file) 
+            || (stat(cacert_file, &s) && errno == ENOENT)) {
         vconn_ssl_set_ca_cert_file(cacert_file,
                 cfg_get_bool(0, "ssl.bootstrap-ca-cert"));
     }
@@ -131,6 +142,7 @@ mgmt_reconfigure(void)
     int retval;
 
     if (!cfg_has_section("mgmt")) {
+        svec_clear(&mgmt_cfg);
         if (mgmt_rconn) {
             rconn_destroy(mgmt_rconn);
             mgmt_rconn = NULL;
@@ -211,6 +223,10 @@ mgmt_reconfigure(void)
     if (retval == EAFNOSUPPORT) {
         VLOG_ERR("no support for %s vconn", controller_name);
     }
+
+    /* Reset the extended message buffer when we create a new
+     * management connection. */
+    ofpbuf_clear(&ext_data_buffer);
 }
 
 static void *
@@ -250,12 +266,18 @@ send_openflow_buffer(struct ofpbuf *buffer)
         return EINVAL;
     }
 
+    /* Make sure there's room to transmit the data.  We don't want to
+     * fail part way through a send. */
+    if (rconn_packet_counter_read(txqlen) >= TXQ_LIMIT) {
+        return EAGAIN;
+    }
+
     /* OpenFlow messages use a 16-bit length field, so messages over 64K
      * must be broken into multiple pieces. 
      */
     if (buffer->size <= 65535) {
         update_openflow_length(buffer);
-        retval = rconn_send_with_limit(mgmt_rconn, buffer, txqlen, TXQ_LIMIT);
+        retval = rconn_send(mgmt_rconn, buffer, txqlen);
         if (retval) {
             VLOG_WARN_RL(&rl, "send to %s failed: %s",
                          rconn_get_name(mgmt_rconn), strerror(retval));
@@ -281,12 +303,10 @@ send_openflow_buffer(struct ofpbuf *buffer)
                     &new_buffer);
             oed->type = header->type;
 
-            if (remain > 65535) {
+            if (remain > new_len) {
                 oed->flags |= OFMPEDF_MORE_DATA;
             }
 
-            printf("xxx SENDING LEN: %d\n", new_len);
-
             /* Copy the entire original message, including the OpenFlow
              * header, since management protocol structure definitions
              * include these headers.
@@ -294,8 +314,7 @@ send_openflow_buffer(struct ofpbuf *buffer)
             ofpbuf_put(new_buffer, ptr, new_len);
 
             update_openflow_length(new_buffer);
-            retval = rconn_send_with_limit(mgmt_rconn, new_buffer, txqlen, 
-                    TXQ_LIMIT);
+            retval = rconn_send(mgmt_rconn, new_buffer, txqlen);
             if (retval) {
                 VLOG_WARN_RL(&rl, "send to %s failed: %s",
                              rconn_get_name(mgmt_rconn), strerror(retval));
@@ -515,20 +534,6 @@ send_config_update_ack(uint32_t xid, bool success)
     send_openflow_buffer(buffer);
 }
 
-static void
-send_ofmp_error_msg(uint32_t xid, uint16_t type, uint16_t code, 
-            const void *data, size_t len)
-{
-    struct ofpbuf *buffer;
-    struct ofmp_error_msg *oem;
-
-    oem = make_ofmp_xid(sizeof(*oem)+len, OFMPT_ERROR, xid, &buffer);
-    oem->type = htons(type);
-    oem->code = htons(code);
-    memcpy(oem->data, data, len);
-    send_openflow_buffer(buffer);
-}
-
 static void
 send_error_msg(uint32_t xid, uint16_t type, uint16_t code, 
             const void *data, size_t len)
@@ -643,6 +648,14 @@ recv_ofmp_config_update(uint32_t xid, const struct ofmp_header *ofmph,
         /* xxx cfg_lock can fail for other reasons, such as being
          * xxx locked... */
         VLOG_WARN_RL(&rl, "config update failed due to bad cookie\n");
+
+        /* Check if our local view matches the controller, in which
+         * case, it is likely that there were local modifications
+         * without our being told to reread the config file. */
+        if (!memcmp(cfg_cookie, ofmpcu->cookie, sizeof cfg_cookie)) {
+            VLOG_WARN_RL(&rl, "config appears to have been locally modified "
+                              "without having told ovs-vswitchd to reload");
+        }
         send_config_update_ack(xid, false);
         return 0;
     }
@@ -665,23 +678,48 @@ static int
 recv_ofmp_extended_data(uint32_t xid, const struct ofmp_header *ofmph,
         size_t len)
 {
-    size_t data_len;
+    int data_len;
     struct ofmp_extended_data *ofmped;
-    uint8_t *ptr;
 
-    data_len = len - sizeof(*ofmped);
-    if (data_len <= sizeof(*ofmped)) {
+    if (len <= sizeof(*ofmped)) {
         /* xxx Send error. */
         return -EINVAL;
     }
 
+    ext_data_xid = xid;
     ofmped = (struct ofmp_extended_data *)ofmph;
 
-    ptr = ofpbuf_put(&ext_data_buffer, ofmped->data, data_len);
+    data_len = len - sizeof(*ofmped);
+    ofpbuf_put(&ext_data_buffer, ofmped->data, data_len);
+
+    if (!(ofmped->flags & OFMPEDF_MORE_DATA)) {
+        struct ofmp_header *new_oh;
+        int error;
+
+        /* An embedded message must be greater than the size of an
+         * OpenFlow message. */
+        new_oh = ofpbuf_at(&ext_data_buffer, 0, 65536);
+        if (!new_oh) {
+            VLOG_WARN_RL(&rl, "received short embedded message: %zu\n",
+                    ext_data_buffer.size);
+            return -EINVAL;
+        }
+
+        /* Make sure that this is a management message and that there's
+         * not an embedded extended data message. */
+        if ((new_oh->header.vendor != htonl(NX_VENDOR_ID))
+                || (new_oh->header.subtype != htonl(NXT_MGMT))
+                || (new_oh->type == htonl(OFMPT_EXTENDED_DATA))) {
+            VLOG_WARN_RL(&rl, "received bad embedded message\n");
+            return -EINVAL;
+        }
+        new_oh->header.header.xid = ext_data_xid;
+        new_oh->header.header.length = 0;
 
-    if (!ofmped->flags & OFMPEDF_MORE_DATA) {
-        recv_ofmp(xid, ext_data_buffer.data, ext_data_buffer.size);
+        error = recv_ofmp(xid, ext_data_buffer.data, ext_data_buffer.size);
         ofpbuf_clear(&ext_data_buffer);
+
+        return error;
     }
 
     return 0;
@@ -702,6 +740,12 @@ int recv_ofmp(uint32_t xid, struct ofmp_header *ofmph, size_t len)
         len = ntohs(ofmph->header.header.length);
     }
 
+    /* Reset the extended data buffer if this isn't a continuation of an 
+     * existing extended data message. */
+    if (ext_data_xid != xid) {
+        ofpbuf_clear(&ext_data_buffer);
+    }
+
     /* xxx Should sanity-check for min/max length */
     switch (ntohs(ofmph->type)) 
     {