X-Git-Url: https://pintos-os.org/cgi-bin/gitweb.cgi?a=blobdiff_plain;f=vswitchd%2Fmgmt.c;h=45c3580243d39c721de193d02f2323677b3b7253;hb=a8b5f8b423e5d54ba8bb6e68cc5b9dc22215d302;hp=f5dcd184027ccb37174d27d68c79f7aaa84b87f5;hpb=064af42167bf4fc9aaea2702d80ce08074b889c0;p=openvswitch diff --git a/vswitchd/mgmt.c b/vswitchd/mgmt.c index f5dcd184..45c35802 100644 --- a/vswitchd/mgmt.c +++ b/vswitchd/mgmt.c @@ -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 . - * - * 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 @@ -31,6 +19,9 @@ #include #include #include +#include +#include +#include #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 @@ -57,9 +49,11 @@ 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; jtype = 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