Add function get_null_fd(), to reduce code redundancy.
[openvswitch] / vswitchd / mgmt.c
index f5dcd184027ccb37174d27d68c79f7aaa84b87f5..45c3580243d39c721de193d02f2323677b3b7253 100644 (file)
@@ -1,28 +1,16 @@
 /* Copyright (c) 2009 Nicira Networks
- * 
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
  *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
+ * 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:
  *
- * You should have received a copy of the GNU General Public License
- * along with this program.  If not, see <http://www.gnu.org/licenses/>.
- *
- * In addition, as a special exception, Nicira Networks gives permission
- * to link the code of its release of vswitchd with the OpenSSL project's
- * "OpenSSL" library (or with modified versions of it that use the same
- * license as the "OpenSSL" library), and distribute the linked
- * executables.  You must obey the GNU General Public License in all
- * respects for all of the code used other than "OpenSSL".  If you modify
- * this file, you may extend this exception to your version of the file,
- * but you are not obligated to do so.  If you do not wish to do so,
- * delete this exception statement from your version.
+ *     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>
@@ -31,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"
@@ -47,6 +38,7 @@
 #include "svec.h"
 #include "vconn.h"
 #include "vconn-ssl.h"
+#include "xenserver.h"
 #include "xtoxll.h"
 
 #define THIS_MODULE VLM_mgmt
 
 static struct svec mgmt_cfg;
 static uint8_t cfg_cookie[CFG_COOKIE_LEN];
+static bool need_reconfigure = false;
 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;
 uint64_t mgmt_id;
 
 
@@ -69,6 +63,7 @@ struct rconn_packet_counter *txqlen; /* # pkts queued for tx on mgmt_rconn. */
 static uint64_t pick_fallback_mgmt_id(void);
 static void send_config_update(uint32_t xid, bool use_xid);
 static void send_resources_update(uint32_t xid, bool use_xid);
+static int recv_ofmp(uint32_t xid, struct ofmp_header *ofmph, size_t len);
 
 void
 mgmt_init(void)
@@ -85,6 +80,8 @@ mgmt_init(void)
         /* Randomly generate a mgmt id */
         mgmt_id = pick_fallback_mgmt_id();
     }
+
+    ofpbuf_init(&ext_data_buffer, 0);
 }
 
 #ifdef HAVE_OPENSSL
@@ -107,6 +104,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. */
@@ -118,7 +116,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"));
     }
@@ -137,6 +141,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;
@@ -219,40 +224,6 @@ mgmt_reconfigure(void)
     }
 }
 
-static int
-send_openflow_buffer(struct ofpbuf *buffer)
-{               
-    int retval;
-
-    if (!mgmt_rconn) {
-        VLOG_ERR("attempt to send openflow packet with no rconn\n");
-        return EINVAL;
-    }
-
-    update_openflow_length(buffer);
-    retval = rconn_send_with_limit(mgmt_rconn, buffer, txqlen, TXQ_LIMIT);
-    if (retval) {
-        VLOG_WARN_RL(&rl, "send to %s failed: %s",
-                     rconn_get_name(mgmt_rconn), strerror(retval));
-    }   
-    return retval;
-}   
-    
-static void
-send_features_reply(uint32_t xid)
-{
-    struct ofpbuf *buffer;
-    struct ofp_switch_features *ofr;
-
-    ofr = make_openflow_xid(sizeof *ofr, OFPT_FEATURES_REPLY, xid, &buffer);
-    ofr->datapath_id  = 0;
-    ofr->n_tables     = 0;
-    ofr->n_buffers    = 0;
-    ofr->capabilities = 0;
-    ofr->actions      = 0;
-    send_openflow_buffer(buffer);
-}
-
 static void *
 make_ofmp_xid(size_t ofmp_len, uint16_t type, uint32_t xid,
         struct ofpbuf **bufferp)
@@ -280,6 +251,93 @@ make_ofmp(size_t ofmp_len, uint16_t type, struct ofpbuf **bufferp)
     return oh;
 }
 
+static int
+send_openflow_buffer(struct ofpbuf *buffer)
+{               
+    int retval;
+
+    if (!mgmt_rconn) {
+        VLOG_ERR("attempt to send openflow packet with no rconn\n");
+        return EINVAL;
+    }
+
+    /* 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);
+        if (retval) {
+            VLOG_WARN_RL(&rl, "send to %s failed: %s",
+                         rconn_get_name(mgmt_rconn), strerror(retval));
+        }   
+        return retval;
+    } else {
+        struct ofmp_header *header = (struct ofmp_header *)buffer->data;
+        uint32_t xid = header->header.header.xid;
+        size_t remain = buffer->size;
+        uint8_t *ptr = buffer->data;
+        
+        /* Mark the OpenFlow header with a zero length to indicate some
+         * funkiness. 
+         */
+        header->header.header.length = 0;
+
+        while (remain > 0) {
+            struct ofpbuf *new_buffer;
+            struct ofmp_extended_data *oed;
+            size_t new_len = MIN(65535 - sizeof *oed, remain);
+
+            oed = make_ofmp_xid(sizeof *oed, OFMPT_EXTENDED_DATA, xid, 
+                    &new_buffer);
+            oed->type = header->type;
+
+            if (remain > 65535) {
+                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.
+             */
+            ofpbuf_put(new_buffer, ptr, new_len);
+
+            update_openflow_length(new_buffer);
+            retval = rconn_send_with_limit(mgmt_rconn, new_buffer, txqlen, 
+                    TXQ_LIMIT);
+            if (retval) {
+                VLOG_WARN_RL(&rl, "send to %s failed: %s",
+                             rconn_get_name(mgmt_rconn), strerror(retval));
+                ofpbuf_delete(buffer);
+                return retval;
+            }   
+
+            remain -= new_len;
+            ptr += new_len;
+        }
+
+        ofpbuf_delete(buffer);
+        return 0;
+    }
+}   
+    
+static void
+send_features_reply(uint32_t xid)
+{
+    struct ofpbuf *buffer;
+    struct ofp_switch_features *ofr;
+
+    ofr = make_openflow_xid(sizeof *ofr, OFPT_FEATURES_REPLY, xid, &buffer);
+    ofr->datapath_id  = 0;
+    ofr->n_tables     = 0;
+    ofr->n_buffers    = 0;
+    ofr->capabilities = 0;
+    ofr->actions      = 0;
+    send_openflow_buffer(buffer);
+}
+
 static void 
 send_capability_reply(uint32_t xid)
 {
@@ -305,6 +363,8 @@ send_resources_update(uint32_t xid, bool use_xid)
     struct ofmp_resources_update *ofmpru;
     struct ofmp_tlv *tlv;
     struct svec br_list;
+    struct svec port_list;
+    const char *host_uuid;
     int i;
 
     if (use_xid) {
@@ -314,11 +374,28 @@ send_resources_update(uint32_t xid, bool use_xid)
         ofmpru = make_ofmp(sizeof *ofmpru, OFMPT_RESOURCES_UPDATE, &buffer);
     }
 
+    /* On XenServer systems, each host has its own UUID, which we provide
+     * to the controller. 
+     */ 
+    host_uuid = xenserver_get_host_uuid();
+    if (host_uuid) {
+        struct ofmptsr_mgmt_uuid *mgmt_uuid_tlv;
+
+        mgmt_uuid_tlv = ofpbuf_put_zeros(buffer, sizeof(*mgmt_uuid_tlv));
+        mgmt_uuid_tlv->type = htons(OFMPTSR_MGMT_UUID);
+        mgmt_uuid_tlv->len = htons(sizeof(*mgmt_uuid_tlv));
+        mgmt_uuid_tlv->mgmt_id = htonll(mgmt_id);
+        memcpy(mgmt_uuid_tlv->uuid, host_uuid, OFMP_UUID_LEN);
+    }
+
     svec_init(&br_list);
     cfg_get_subsections(&br_list, "bridge");
     for (i=0; i < br_list.n; i++) {
         struct ofmptsr_dp *dp_tlv;
-        uint64_t dp_id = bridge_get_datapathid(br_list.names[i]);
+        uint64_t dp_id;
+        int n_uuid;
+
+        dp_id = bridge_get_datapathid(br_list.names[i]);
         if (!dp_id) {
             VLOG_WARN_RL(&rl, "bridge %s doesn't seem to exist", 
                     br_list.names[i]);
@@ -330,7 +407,81 @@ send_resources_update(uint32_t xid, bool use_xid)
 
         dp_tlv->dp_id = htonll(dp_id);
         memcpy(dp_tlv->name, br_list.names[i], strlen(br_list.names[i])+1);
+
+        /* On XenServer systems, each network has one or more UUIDs
+         * associated with it, which we provide to the controller. 
+         */
+        n_uuid = cfg_count("bridge.%s.xs-network-uuids", br_list.names[i]);
+        if (n_uuid) {
+            struct ofmptsr_dp_uuid *dp_uuid_tlv;
+            size_t tlv_len = sizeof(*dp_uuid_tlv) + n_uuid * OFMP_UUID_LEN;
+            int j;
+
+            dp_uuid_tlv = ofpbuf_put_zeros(buffer, sizeof(*dp_uuid_tlv));
+            dp_uuid_tlv->type = htons(OFMPTSR_DP_UUID);
+            dp_uuid_tlv->len = htons(tlv_len);
+            dp_uuid_tlv->dp_id = htonll(dp_id);
+
+            for (j=0; j<n_uuid; j++) {
+                const char *dp_uuid = cfg_get_string(j, 
+                        "bridge.%s.xs-network-uuids", br_list.names[i]);
+
+                /* The UUID list could change underneath us, so just
+                 * fill with zeros in that case.  Another update will be
+                 * initiated shortly, which should contain corrected data.
+                 */
+                if (dp_uuid) {
+                    ofpbuf_put(buffer, dp_uuid, OFMP_UUID_LEN);
+                } else {
+                    ofpbuf_put_zeros(buffer, OFMP_UUID_LEN);
+                }
+            }
+        }
+    }
+    svec_destroy(&br_list);
+
+    /* On XenServer systems, extended information about virtual interfaces 
+     * (VIFs) is available, which is needed by the controller. 
+     */ 
+    svec_init(&port_list);
+    bridge_get_ifaces(&port_list);
+    for (i=0; i < port_list.n; i++) {
+        const char *vif_uuid, *vm_uuid, *net_uuid;
+        uint64_t vif_mac;
+        struct ofmptsr_vif *vif_tlv;
+
+        vif_uuid = cfg_get_string(0, "port.%s.vif-uuid", port_list.names[i]);
+        if (!vif_uuid) {
+            continue;
+        }
+
+        vif_tlv = ofpbuf_put_zeros(buffer, sizeof(*vif_tlv));
+        vif_tlv->type = htons(OFMPTSR_VIF);
+        vif_tlv->len = htons(sizeof(*vif_tlv));
+
+        memcpy(vif_tlv->name, port_list.names[i], strlen(port_list.names[i])+1);
+        memcpy(vif_tlv->vif_uuid, vif_uuid, sizeof(vif_tlv->vif_uuid));
+
+        vm_uuid = cfg_get_string(0, "port.%s.vm-uuid", port_list.names[i]);
+        if (vm_uuid) {
+            memcpy(vif_tlv->vm_uuid, vm_uuid, sizeof(vif_tlv->vm_uuid));
+        } else {
+            /* In case the vif disappeared underneath us. */
+            memset(vif_tlv->vm_uuid, '\0', sizeof(vif_tlv->vm_uuid));
+        }
+
+        net_uuid = cfg_get_string(0, "port.%s.net-uuid", port_list.names[i]);
+        if (net_uuid) {
+            memcpy(vif_tlv->net_uuid, net_uuid, sizeof(vif_tlv->net_uuid));
+        } else {
+            /* In case the vif disappeared underneath us. */
+            memset(vif_tlv->net_uuid, '\0', sizeof(vif_tlv->net_uuid));
+        }
+
+        vif_mac = cfg_get_mac(0, "port.%s.vif-mac", port_list.names[i]);
+        vif_tlv->vif_mac = htonll(vif_mac);
     }
+    svec_destroy(&port_list);
 
     /* Put end marker. */
     tlv = ofpbuf_put_zeros(buffer, sizeof(*tlv));
@@ -426,11 +577,12 @@ recv_set_config(uint32_t xid UNUSED, const void *msg UNUSED)
 }
 
 static int
-recv_ofmp_capability_request(uint32_t xid, const struct ofmp_header *ofmph)
+recv_ofmp_capability_request(uint32_t xid, const struct ofmp_header *ofmph,
+        size_t len)
 {
     struct ofmp_capability_request *ofmpcr;
 
-    if (htons(ofmph->header.header.length) != sizeof(*ofmpcr)) {
+    if (len != sizeof(*ofmpcr)) {
         /* xxx Send error */
         return -EINVAL;
     }
@@ -447,18 +599,20 @@ recv_ofmp_capability_request(uint32_t xid, const struct ofmp_header *ofmph)
 }
 
 static int
-recv_ofmp_resources_request(uint32_t xid, const void *msg UNUSED)
+recv_ofmp_resources_request(uint32_t xid, const void *msg UNUSED, 
+        size_t len UNUSED)
 {
     send_resources_update(xid, true);
     return 0;
 }
 
 static int
-recv_ofmp_config_request(uint32_t xid, const struct ofmp_header *ofmph)
+recv_ofmp_config_request(uint32_t xid, const struct ofmp_header *ofmph, 
+        size_t len)
 {
     struct ofmp_config_request *ofmpcr;
 
-    if (htons(ofmph->header.header.length) != sizeof(*ofmpcr)) {
+    if (len != sizeof(*ofmpcr)) {
         /* xxx Send error */
         return -EINVAL;
     }
@@ -475,12 +629,13 @@ recv_ofmp_config_request(uint32_t xid, const struct ofmp_header *ofmph)
 }
 
 static int
-recv_ofmp_config_update(uint32_t xid, const struct ofmp_header *ofmph)
+recv_ofmp_config_update(uint32_t xid, const struct ofmp_header *ofmph,
+        size_t len)
 {
     struct ofmp_config_update *ofmpcu;
     int data_len;
 
-    data_len = htons(ofmph->header.header.length) - sizeof(*ofmpcu);
+    data_len = len - sizeof(*ofmpcu);
     if (data_len <= sizeof(*ofmpcu)) {
         /* xxx Send error. */
         return -EINVAL;
@@ -512,26 +667,65 @@ recv_ofmp_config_update(uint32_t xid, const struct ofmp_header *ofmph)
      * connection settings may have changed. */
     send_config_update_ack(xid, true);
 
-    reconfigure();
+    need_reconfigure = true;
 
+    return 0;
+}
+
+static int
+recv_ofmp_extended_data(uint32_t xid, const struct ofmp_header *ofmph,
+        size_t len)
+{
+    size_t data_len;
+    struct ofmp_extended_data *ofmped;
+    uint8_t *ptr;
+
+    data_len = len - sizeof(*ofmped);
+    if (data_len <= sizeof(*ofmped)) {
+        /* xxx Send error. */
+        return -EINVAL;
+    }
+
+    ofmped = (struct ofmp_extended_data *)ofmph;
+
+    ptr = ofpbuf_put(&ext_data_buffer, ofmped->data, data_len);
+
+    if (!ofmped->flags & OFMPEDF_MORE_DATA) {
+        recv_ofmp(xid, ext_data_buffer.data, ext_data_buffer.size);
+        ofpbuf_clear(&ext_data_buffer);
+    }
 
     return 0;
 }
 
+/* Handles receiving a management message.  Generally, this function
+ * will be called 'len' set to zero, and the length will be derived by
+ * the OpenFlow header.  With the extended data message, management
+ * messages are not constrained by OpenFlow's 64K message length limit.  
+ * The extended data handler calls this function with the 'len' set to
+ * the total message length and the OpenFlow header's length field is 
+ * ignored.
+ */
 static
-int recv_ofmp(uint32_t xid, struct ofmp_header *ofmph)
+int recv_ofmp(uint32_t xid, struct ofmp_header *ofmph, size_t len)
 {
+    if (!len) {
+        len = ntohs(ofmph->header.header.length);
+    }
+
     /* xxx Should sanity-check for min/max length */
     switch (ntohs(ofmph->type)) 
     {
         case OFMPT_CAPABILITY_REQUEST:
-            return recv_ofmp_capability_request(xid, ofmph);
+            return recv_ofmp_capability_request(xid, ofmph, len);
         case OFMPT_RESOURCES_REQUEST:
-            return recv_ofmp_resources_request(xid, ofmph);
+            return recv_ofmp_resources_request(xid, ofmph, len);
         case OFMPT_CONFIG_REQUEST:
-            return recv_ofmp_config_request(xid, ofmph);
+            return recv_ofmp_config_request(xid, ofmph, len);
         case OFMPT_CONFIG_UPDATE:
-            return recv_ofmp_config_update(xid, ofmph);
+            return recv_ofmp_config_update(xid, ofmph, len);
+        case OFMPT_EXTENDED_DATA:
+            return recv_ofmp_extended_data(xid, ofmph, len);
         default:
             VLOG_WARN_RL(&rl, "unknown mgmt message: %d", 
                     ntohs(ofmph->type));
@@ -547,11 +741,11 @@ recv_nx_msg(uint32_t xid, const void *oh)
     switch (ntohl(nh->subtype)) {
 
     case NXT_MGMT:
-        return recv_ofmp(xid, (struct ofmp_header *)oh);
+        return recv_ofmp(xid, (struct ofmp_header *)oh, 0);
 
     default:
         send_error_msg(xid, OFPET_BAD_REQUEST, OFPBRC_BAD_SUBTYPE, 
-                oh, htons(nh->header.length));
+                oh, ntohs(nh->header.length));
         return -EINVAL;
     }
 }
@@ -624,15 +818,16 @@ handle_msg(uint32_t xid, const void *msg, size_t length)
     return handler(xid, msg);
 }
 
-void 
+bool 
 mgmt_run(void)
 {
     int i;
 
     if (!mgmt_rconn) {
-        return;
+        return false;
     }
 
+    need_reconfigure = false;
     rconn_run(mgmt_rconn);
 
     /* Do some processing, but cap it at a reasonable amount so that
@@ -654,6 +849,8 @@ mgmt_run(void)
             VLOG_WARN_RL(&rl, "received too-short OpenFlow message");
         }
     }
+
+    return need_reconfigure;
 }
 
 void