Defines a management control protocol between the switch and NOX.
Currently, this is only used by vswitchd. It allows the configuration
and monitoring of a switch as a whole, as opposed to the flow table view
provided by OpenFlow. To enable, add the appropriate "mgmt" keys to
"vswitchd.conf". Better docs will be forthcoming...
/* Remote command execution reply, sent when the command's execution
* completes. The reply body is struct nx_command_reply. */
NXT_COMMAND_REPLY,
+
+ /* Management protocol. See "openflow-mgmt.h". */
+ NXT_MGMT,
};
struct nicira_header {
--- /dev/null
+/* Copyright (c) 2009 The Board of Trustees of The Leland Stanford
+ * Junior University
+ *
+ * We are making the OpenFlow specification and associated documentation
+ * (Software) available for public use and benefit with the expectation
+ * that others will use, modify and enhance the Software and contribute
+ * those enhancements back to the community. However, since we would
+ * like to make the Software available for broadest use, with as few
+ * restrictions as possible permission is hereby granted, free of
+ * charge, to any person obtaining a copy of this Software to deal in
+ * the Software under the copyrights without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ *
+ * The name and trademarks of copyright holder(s) may NOT be used in
+ * advertising or publicity pertaining to the Software or any
+ * derivatives without specific, written prior permission.
+ */
+
+#ifndef OPENFLOW_OPENFLOW_MGMT_H
+#define OPENFLOW_OPENFLOW_MGMT_H 1
+
+#include "openflow/nicira-ext.h"
+
+enum ofmp_type {
+ OFMPT_CAPABILITY_REQUEST,
+ OFMPT_CAPABILITY_REPLY,
+ OFMPT_RESOURCES_REQUEST,
+ OFMPT_RESOURCES_UPDATE,
+ OFMPT_CONFIG_REQUEST,
+ OFMPT_CONFIG_UPDATE,
+ OFMPT_CONFIG_UPDATE_ACK,
+ OFMPT_ERROR
+};
+
+/* Header on all OpenFlow management packets. */
+struct ofmp_header {
+ struct nicira_header header;
+ uint16_t type; /* One of OFMPT_* above. */
+ uint8_t pad[2];
+};
+OFP_ASSERT(sizeof(struct ofmp_header) == sizeof(struct nicira_header) + 4);
+
+
+/* Generic TLV header. */
+struct ofmp_tlv {
+ uint16_t type; /* Type of value (one of OFMPTLV_*). */
+ uint16_t len; /* Length of TLV (includes this header). */
+ uint8_t data[0]; /* Value of data as defined by type and length. */
+};
+OFP_ASSERT(sizeof(struct ofmp_tlv) == 4);
+
+/* Universal TLV terminator. Used to indicate end of TLV list. */
+struct ofmp_tlv_end {
+ uint16_t type; /* Type is 0. */
+ uint16_t len; /* Length is 4. */
+};
+OFP_ASSERT(sizeof(struct ofmp_tlv_end) == 4);
+
+
+/* Bitmask of capability description styles. */
+enum ofmp_capability_format {
+ OFMPCAF_SIMPLE = 0 << 0, /* "vswitchd.conf" style. */
+};
+
+/* Body of capbility request.
+ *
+ * OFMPT_CAPABILITY_REQUEST (controller -> switch) */
+struct ofmp_capability_request {
+ struct ofmp_header header;
+ uint32_t format; /* One of OFMPCAF_*. */
+};
+OFP_ASSERT(sizeof(struct ofmp_capability_request) == 24);
+
+/* Body of reply to capability request.
+ *
+ * OFMPT_CAPABILITY_REPLY (switch -> controller). */
+struct ofmp_capability_reply {
+ struct ofmp_header header;
+ uint32_t format; /* One of OFMPCAF_*. */
+ uint64_t mgmt_id; /* Management ID. */
+ uint8_t data[0];
+};
+OFP_ASSERT(sizeof(struct ofmp_capability_reply) == 32);
+
+
+/* Resource TLV for datapath description. */
+struct ofmptsr_dp {
+ uint16_t type; /* OFMPTSR_DP. */
+ uint16_t len; /* 28. */
+ uint64_t dp_id; /* Datapath ID. */
+ uint8_t name[OFP_MAX_PORT_NAME_LEN]; /* Null-terminated name. */
+};
+OFP_ASSERT(sizeof(struct ofmptsr_dp) == 28);
+
+/* TLV types for switch resource descriptions. */
+enum ofmp_switch_resources {
+ OFMPTSR_END = 0, /* Terminator. */
+ OFMPTSR_DP, /* Datapath. */
+};
+
+/* Body of resources request.
+ *
+ * OFMPT_RESOURCES_REQUEST (controller -> switch) */
+struct ofmp_resources_request {
+ struct ofmp_header header;
+};
+
+/* Body of capbility update. Sent in response to a resources request or
+ * sent asynchronously when resources change on the switch.
+ *
+ * OFMPT_RESOURCES_UPDATE (switch -> controller) */
+struct ofmp_resources_update {
+ struct ofmp_header header;
+ uint8_t data[0];
+};
+OFP_ASSERT(sizeof(struct ofmp_resources_update) == 20);
+
+
+/* Bitmask of capability description styles. */
+enum ofmp_config_format {
+ OFMPCOF_SIMPLE = 0 << 0, /* "vswitchd.conf" style. */
+};
+
+#define CONFIG_COOKIE_LEN 20
+
+/* Body of configuration request.
+ *
+ * OFMPT_CONFIG_REQUEST (controller -> switch) */
+struct ofmp_config_request {
+ struct ofmp_header header;
+ uint32_t format; /* One of OFMPCOF_*. */
+};
+OFP_ASSERT(sizeof(struct ofmp_config_request) == 24);
+
+/* Body of configuration update. Sent in response to a configuration
+ * request from the controller. May be sent asynchronously by either
+ * the controller or switch to modify configuration or notify of
+ * changes, respectively. If sent by the controller, the switch must
+ * respond with a OFMPT_CONFIG_UPDATE_ACK.
+ *
+ * OFMPT_CONFIG_UPDATE (switch <-> controller) */
+struct ofmp_config_update {
+ struct ofmp_header header;
+ uint32_t format; /* One of OFMPCOF_*. */
+ uint8_t cookie[CONFIG_COOKIE_LEN]; /* Cookie of config attempting to be
+ * replaced by this update. */
+ uint8_t data[0];
+};
+OFP_ASSERT(sizeof(struct ofmp_config_update) == 44);
+
+/* Bitmask of configuration update ack flags. */
+enum ofmp_config_update_ack_flags {
+ OFMPCUAF_SUCCESS = 1 << 0, /* Config succeeded. */
+};
+
+/* Body of configuration update ack. Sent in response to a configuration
+ * udpate request.
+ *
+ * OFMPT_CONFIG_UPDATE_ACK (switch -> controller) */
+struct ofmp_config_update_ack {
+ struct ofmp_header header;
+ uint32_t format; /* One of OFMPCOF_*. */
+ uint32_t flags; /* One of OFMPCUAF_*. */
+ uint8_t cookie[CONFIG_COOKIE_LEN]; /* Cookie of current configuration
+ * being used in the switch. */
+};
+OFP_ASSERT(sizeof(struct ofmp_config_update_ack) == 48);
+
+/* Values for 'type' in ofmp_error_msg. */
+enum ofmp_error_type {
+ OFMPET_BAD_CONFIG /* Problem with configuration. */
+};
+
+/* ofmp_error_msg 'code' values for OFMPET_BAD_CONFIG. 'data' contains
+ * at least the first 64 bytes of the failed request. */
+enum ofmp_bad_config_code {
+ OFMPBCC_BUSY, /* Config updating, try again. */
+ OFMPBCC_OLD_COOKIE, /* Config has changed. */
+};
+
+/* Body of error message. May be sent by either the switch or the
+ * controller to indicate some error condition.
+ *
+ * OFMPT_ERROR (switch <-> controller) */
+struct ofmp_error_msg {
+ struct ofmp_header header;
+
+ uint16_t type; /* One of OFMPET_*. */
+ uint16_t code; /* Code depending on 'type'. */
+ uint8_t data[0]; /* Variable-length data. Interpreted based
+ on the type and code. */
+};
+OFP_ASSERT(sizeof(struct ofmp_error_msg) == 24);
+
+#endif /* openflow/openflow-mgmt.h */
lib/learning-switch.h \
lib/list.c \
lib/list.h \
+ lib/lockfile.c \
+ lib/lockfile.h \
lib/mac-learning.c \
lib/mac-learning.h \
lib/netdev.c \
lib/rconn.c \
lib/rconn.h \
lib/sat-math.h \
+ lib/sha1.c \
+ lib/sha1.h \
lib/shash.c \
lib/shash.h \
lib/signals.c \
#include <sys/stat.h>
#include <unistd.h>
#include "dynamic-string.h"
+#include "lockfile.h"
+#include "ofpbuf.h"
#include "packets.h"
#include "svec.h"
#include "util.h"
* "bridge.a.controller.in-band", if it existed, and I'm really not certain
* that the fix didn't break other things. */
-/* List of configuration files. */
-static struct svec cfg_files = SVEC_EMPTY_INITIALIZER;
+/* Configuration file name. */
+static char *cfg_name;
+
+/* Put the temporary file in the same directory as cfg_name, so that
+ * they are guaranteed to be in the same file system and therefore we can
+ * rename() tmp_name over cfg_name. */
+static char *tmp_name;
+
+/* Lock information. */
+static char *lock_name;
+int lock_fd;
+
+/* Flag to indicate whether local modifications have been made. */
+bool dirty;
+
+uint8_t cfg_cookie[CFG_COOKIE_LEN];
/* Current configuration. Maintained in sorted order. */
static struct svec cfg = SVEC_EMPTY_INITIALIZER;
-static void read_directory(const char *dir_name);
static bool has_double_dot(const char *key, size_t len);
static bool is_valid_key(const char *key, size_t len,
const char *file_name, int line_number,
const char *);
static void parse_setting(const char *file_name, int line_number,
const char *section, const char *);
-static void read_file(const char *file_name);
static int compare_key(const char *a, const char *b);
static char **find_key_le(const char *key);
static char **find_key_ge(const char *key);
static const char *get_nth_value(int idx, const char *key);
static bool is_type(const char *s, enum cfg_flags);
+#define CC_ALPHA "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"
+#define CC_DIGIT "0123456789"
+#define CC_ALNUM CC_ALPHA CC_DIGIT
+#define CC_SPACE " \t\r\n\v"
+
+#define CC_FILE_NAME CC_ALNUM "._-"
+#define CC_KEY CC_ALNUM "._-@$:+"
+
/* Adds 'file_name' to the set of files or directories that are read by
* cfg_read(). Returns 0 on success, otherwise a positive errno value if
* 'file_name' cannot be opened.
* This function does not actually read the named file or directory. Use
* cfg_read() to (re)read all the configuration files. */
int
-cfg_add_file(const char *file_name)
+cfg_set_file(const char *file_name)
{
int fd;
+ if (cfg_name) {
+ free(cfg_name);
+ free(lock_name);
+ free(tmp_name);
+ cfg_name = lock_name = tmp_name = NULL;
+ }
+
/* Make sure that we can open this file or directory for reading. */
fd = open(file_name, O_RDONLY);
if (fd < 0) {
close(fd);
/* Add it to the list. */
- VLOG_INFO("using \"%s\" as a configuration file", file_name);
- svec_add(&cfg_files, file_name);
+ VLOG_INFO("using \"%s\" as the configuration file", file_name);
+ cfg_name = xstrdup(file_name);
+ lock_name = xasprintf("%s.~lock~", file_name);
+ tmp_name = xasprintf("%s.~tmp~", file_name);
+ return 0;
+}
+
+static int
+update_cookie(void)
+{
+ int i;
+ SHA1Context context;
+
+ if (SHA1Reset(&context) != shaSuccess) {
+ return -1;
+ }
+ for (i = 0; i < cfg.n; i++) {
+ if (SHA1Input(&context, (uint8_t *)cfg.names[i],
+ strlen(cfg.names[i])) != shaSuccess) {
+ return -1;
+ }
+ SHA1Input(&context, (uint8_t *)"\n", 1);
+ }
+ if (SHA1Result(&context, cfg_cookie) != shaSuccess) {
+ return -1;
+ }
+
return 0;
}
void
cfg_read(void)
{
- size_t i;
+ struct ds ds;
+ FILE *file;
+ char *section;
+ int line_number;
+
+
+ if (!cfg_name) {
+ return;
+ }
/* Clear old configuration data. */
svec_clear(&cfg);
/* Read new configuration. */
- VLOG_INFO("reading configuration...");
- for (i = 0; i < cfg_files.n; i++) {
- const char *fn = cfg_files.names[i];
- struct stat s;
-
- VLOG_DBG("reading \"%s\"", fn);
- if (stat(fn, &s) < 0) {
- VLOG_WARN("failed to stat \"%s\": %s", fn, strerror(errno));
- } else if (S_ISDIR(s.st_mode)) {
- read_directory(fn);
- } else if (S_ISREG(s.st_mode)) {
- read_file(fn);
+ VLOG_INFO("reading configuration from %s", cfg_name);
+
+ file = fopen(cfg_name, "r");
+ if (!file) {
+ VLOG_ERR("failed to open \"%s\": %s", cfg_name, strerror(errno));
+ return;
+ }
+
+ ds_init(&ds);
+ section = NULL;
+ line_number = 0;
+ while (!ds_get_line(&ds, file)) {
+ const char *s = ds_cstr(&ds);
+ size_t indent = strspn(s, CC_SPACE);
+
+ line_number++;
+ s += indent;
+ if (*s == '#' || *s == '\0') {
+ /* Ignore comments and lines that contain only white space. */
+ } else if (*s == '[') {
+ if (!indent) {
+ free(section);
+ section = parse_section(cfg_name, line_number, s);
+ } else {
+ VLOG_ERR("%s:%d: ignoring indented section header",
+ cfg_name, line_number);
+ }
+ } else if (indent && !section) {
+ VLOG_ERR("%s:%d: ignoring indented line outside any section",
+ cfg_name, line_number);
} else {
- VLOG_WARN("\"%s\" is not a regular file or a directory, ignoring",
- fn);
+ if (!indent) {
+ free(section);
+ section = NULL;
+ }
+ parse_setting(cfg_name, line_number, section, s);
}
}
+ ds_destroy(&ds);
+ free(section);
+
+ svec_sort(&cfg);
+ svec_terminate(&cfg);
+ update_cookie();
+
+ fclose(file);
if (VLOG_IS_DBG_ENABLED()) {
size_t i;
}
}
+int
+cfg_get_cookie(uint8_t *cookie)
+{
+ if (dirty) {
+ update_cookie();
+ }
+
+ memcpy(cookie, cfg_cookie, sizeof(cfg_cookie));
+ return 0;
+}
+
+void
+cfg_unlock(void)
+{
+ remove_lockfile(lock_fd, lock_name);
+ lock_fd = -1;
+}
+
+/* Config may change, so caller responsible for notifying others if it
+ * did. Returns positive errno. */
+int
+cfg_lock(uint8_t *cookie)
+{
+ int fd;
+ uint8_t curr_cookie[CFG_COOKIE_LEN];
+
+ /* Put the lock file in the same directory as cfg_name, so that
+ * they are guaranteed to be in the same file system. */
+ fd = create_lockfile(lock_name);
+ if (fd < 0) {
+ /* Couldn't lock config file */
+ return -fd;
+ }
+ lock_fd = fd;
+
+ cfg_read();
+
+ if (cookie) {
+ cfg_get_cookie(curr_cookie);
+
+ if (memcmp(curr_cookie, cookie, sizeof *curr_cookie)) {
+ /* Configuration has changed, so reject. */
+ cfg_unlock();
+ return EINVAL;
+ }
+ }
+
+ return 0;
+}
+
+/* Write 'new_cfg' into the configuration file. If 'cookie' is not null,
+ * then the write only occurs if it matches the current configuration's
+ * cookie. If successful, the old configuration is returned in 'new_cfg'.
+ * Returns 0 if successful, otherwise a negative errno value. */
+int
+cfg_write(void)
+{
+ size_t i;
+ int fd;
+
+
+ svec_sort(&cfg);
+
+ fd = open(tmp_name, O_CREAT|O_WRONLY|O_TRUNC, S_IRUSR|S_IWUSR);
+ if (fd == -1) {
+ VLOG_WARN("could not open temp config file for writing: %s",
+ strerror(errno));
+ return errno;
+ }
+
+ for (i = 0; i < cfg.n; i++) {
+ int retval;
+ const char *entry = cfg.names[i];
+ retval = write(fd, entry, strlen(entry));
+ if (retval != strlen(entry)) {
+ VLOG_WARN("problem writing to temp config file %d: %s",
+ retval, strerror(errno));
+ close(fd);
+ return errno;
+ }
+ retval = write(fd, "\n", 1);
+ if (retval != 1) {
+ VLOG_WARN("problem writing to temp config file %d: %s",
+ retval, strerror(errno));
+ close(fd);
+ return errno;
+ }
+ }
+ close(fd);
+
+ if (rename(tmp_name, cfg_name) < 0) {
+ VLOG_WARN("could not rename temp config file: %s",
+ strerror(errno));
+ return errno;
+ }
+
+ dirty = false;
+
+ return 0;
+}
+
+int
+cfg_write_data(uint8_t *data, size_t len)
+{
+ int fd;
+ int retval;
+
+ fd = open(tmp_name, O_CREAT|O_WRONLY|O_TRUNC, S_IRUSR|S_IWUSR);
+ if (fd == -1) {
+ VLOG_WARN("could not open temp config file for writing: %s",
+ strerror(errno));
+ return errno;
+ }
+
+ while (len > 0) {
+ retval = write(fd, data, len);
+ if (retval < 0) {
+ close(fd);
+ return errno;
+ }
+ len -= retval;
+ }
+
+ close(fd);
+
+ if (rename(tmp_name, cfg_name) < 0) {
+ VLOG_WARN("could not rename temp config file: %s",
+ strerror(errno));
+ return errno;
+ }
+
+ dirty = false;
+ cfg_read();
+
+ return 0;
+}
+
+void
+cfg_buf_put(struct ofpbuf *buffer)
+{
+ int i;
+
+ for (i = 0; i < cfg.n; i++) {
+ ofpbuf_put(buffer, cfg.names[i], strlen(cfg.names[i]));
+ ofpbuf_put(buffer, "\n", 1);
+ }
+}
+
/* Formats the printf()-style format string in the parameter 'format', which
* must be the function's last parameter, into string variable 'dst'. The
* function is responsible for freeing 'dst'. */
ds_destroy(§ion);
}
+void
+cfg_add_entry(const char *entry_, ...)
+{
+ char *entry;
+
+ FORMAT_KEY(entry_, entry);
+ svec_add_nocopy(&cfg, entry);
+ dirty = true;
+}
+
+void
+cfg_del_entry(const char *entry_, ...)
+{
+ char *entry;
+
+ FORMAT_KEY(entry_, entry);
+ svec_del(&cfg, entry);
+ free(entry);
+ dirty = true;
+}
+
+void
+cfg_del_section(const char *section_, ...)
+{
+ struct ds section;
+ va_list args;
+ char **p;
+
+ ds_init(§ion);
+ va_start(args, section_);
+ ds_put_format_valist(§ion, section_, args);
+ ds_put_char(§ion, '.');
+ va_end(args);
+
+ for (p = find_key_le(ds_cstr(§ion));
+ *p && !strncmp(section.string, *p, section.length);
+ p++) {
+ svec_del(&cfg, *p);
+ }
+ ds_destroy(§ion);
+ dirty = true;
+}
+
/* Returns the value numbered 'idx' of 'key'. Returns a null pointer if 'idx'
* is greater than or equal to cfg_count(key). The caller must not modify or
* free the returned string or retain its value beyond the next call to
free(key);
}
\f
-#define CC_ALPHA "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"
-#define CC_DIGIT "0123456789"
-#define CC_ALNUM CC_ALPHA CC_DIGIT
-#define CC_SPACE " \t\r\n\v"
-
-#define CC_FILE_NAME CC_ALNUM "._-"
-#define CC_KEY CC_ALNUM "._-@$:+"
-
-static void
-read_directory(const char *dir_name)
-{
- DIR *dir;
- struct dirent *de;
-
- dir = opendir(dir_name);
- if (!dir) {
- VLOG_ERR("failed to open \"%s\" as a directory: %s",
- dir_name, strerror(errno));
- return;
- }
-
- while ((de = readdir(dir)) != NULL) {
- const char *name = de->d_name;
- if (name[0] != '.' && name[strspn(name, CC_FILE_NAME)] == '\0') {
- char *file_name = xasprintf("%s/%s", dir_name, name);
- VLOG_DBG("reading %s in %s", name, dir_name);
- read_file(file_name);
- free(file_name);
- } else {
- VLOG_DBG("ignoring %s in %s", name, dir_name);
- }
- }
- closedir(dir);
-}
-
static bool
has_double_dot(const char *key, size_t len)
{
ds_destroy(&value);
}
-static void
-read_file(const char *file_name)
-{
- struct ds ds;
- FILE *file;
- char *section;
- int line_number;
-
- /* XXX should record st_dev, st_ino and make sure that we don't read the
- * same file twice, otherwise all the pairs from that file will get
- * doubled. */
-
- file = fopen(file_name, "r");
- if (!file) {
- VLOG_ERR("failed to open \"%s\": %s", file_name, strerror(errno));
- return;
- }
-
- ds_init(&ds);
- section = NULL;
- line_number = 0;
- while (!ds_get_line(&ds, file)) {
- const char *s = ds_cstr(&ds);
- size_t indent = strspn(s, CC_SPACE);
-
- line_number++;
- s += indent;
- if (*s == '#' || *s == '\0') {
- /* Ignore comments and lines that contain only white space. */
- } else if (*s == '[') {
- if (!indent) {
- free(section);
- section = parse_section(file_name, line_number, s);
- } else {
- VLOG_ERR("%s:%d: ignoring indented section header",
- file_name, line_number);
- }
- } else if (indent && !section) {
- VLOG_ERR("%s:%d: ignoring indented line outside any section",
- file_name, line_number);
- } else {
- if (!indent) {
- free(section);
- section = NULL;
- }
- parse_setting(file_name, line_number, section, s);
- }
- }
- ds_destroy(&ds);
- free(section);
-
- svec_sort(&cfg);
- svec_terminate(&cfg);
-
- fclose(file);
-}
-
static int
compare_key(const char *a, const char *b)
{
#include <stdbool.h>
#include <stdint.h>
+#include <unistd.h>
#include "compiler.h"
+#include "sha1.h"
struct svec;
+struct ofpbuf;
-int cfg_add_file(const char *file_name);
+int cfg_set_file(const char *file_name);
void cfg_read(void);
+int cfg_lock(uint8_t *cookie);
+void cfg_unlock(void);
+int cfg_write(void);
+int cfg_write_data(uint8_t *data, size_t len);
+#define CFG_COOKIE_LEN SHA1HashSize
+int cfg_get_cookie(uint8_t *cookie);
+
+void cfg_buf_put(struct ofpbuf *buffer);
void cfg_get_subsections(struct svec *, const char *, ...) PRINTF_FORMAT(2, 3);
enum cfg_flags {
};
void cfg_register(const char *key_spec, enum cfg_flags);
+void cfg_add_entry(const char *key, ...) PRINTF_FORMAT(1, 2);
+void cfg_del_entry(const char *key, ...) PRINTF_FORMAT(1, 2);
+void cfg_del_section(const char *key, ...) PRINTF_FORMAT(1, 2);
+
bool cfg_has(const char *key, ...) PRINTF_FORMAT(1, 2);
bool cfg_is_valid(enum cfg_flags, const char *key, ...) PRINTF_FORMAT(2, 3);
bool cfg_has_section(const char *key, ...) PRINTF_FORMAT(1, 2);
--- /dev/null
+/* Copyright (C) 2008, 2009 Nicira Networks, Inc. All rights reserved. */
+
+#include <config.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <pwd.h>
+#include <signal.h>
+#include <stdlib.h>
+#include <string.h>
+#include <stropts.h>
+#include <sys/ioctl.h>
+#include <sys/stat.h>
+#include <unistd.h>
+#include "lockfile.h"
+
+#define THIS_MODULE VLM_lockfile
+#include "vlog.h"
+
+static int
+fcntl_lock(int fd)
+{
+ struct flock l;
+ memset(&l, 0, sizeof l);
+ l.l_type = F_WRLCK;
+ l.l_whence = SEEK_SET;
+ l.l_start = 0;
+ l.l_len = 0;
+ return fcntl(fd, F_SETLK, &l) == -1 ? errno : 0;
+}
+
+/* Remove the lockfile with 'name'. If the 'fd' argument is not -1, it
+ * will be closed. */
+int
+remove_lockfile(int fd, const char *name)
+{
+ char buffer[BUFSIZ];
+ ssize_t n;
+ pid_t pid;
+
+ if (fd != -1) {
+ close(fd);
+ }
+
+ /* Remove existing lockfile. */
+ fd = open(name, O_RDWR);
+ if (fd < 0) {
+ if (errno == ENOENT) {
+ return 0;
+ } else {
+ VLOG_ERR("%s: open: %s", name, strerror(errno));
+ return -errno;
+ }
+ }
+
+ /* Read lockfile. */
+ n = read(fd, buffer, sizeof buffer - 1);
+ if (n < 0) {
+ int error = errno;
+ VLOG_ERR("%s: read: %s", name, strerror(error));
+ close(fd);
+ return -error;
+ }
+ buffer[n] = '\0';
+ if (n == 4 && memchr(buffer, '\0', n)) {
+ int32_t x;
+ memcpy(&x, buffer, sizeof x);
+ pid = x;
+ } else if (n >= 0) {
+ pid = strtol(buffer, NULL, 10);
+ }
+ if (pid <= 0) {
+ close(fd);
+ VLOG_WARN("%s: format not recognized, treating as locked.", name);
+ return -EACCES;
+ }
+
+ /* Is lockfile fresh? */
+ if (strstr(buffer, "fcntl")) {
+ int retval = fcntl_lock(fd);
+ if (retval) {
+ int error = errno;
+ close(fd);
+ VLOG_ERR("%s: device is locked (via fcntl): %s",
+ name, strerror(retval));
+ return -error;
+ } else {
+ VLOG_WARN("%s: removing stale lockfile (checked via fcntl)", name);
+ }
+ } else {
+ if (!(kill(pid, 0) < 0 && errno == ESRCH)) {
+ close(fd);
+ VLOG_ERR("%s: device is locked (without fcntl)", name);
+ return -EACCES;
+ } else {
+ VLOG_WARN("%s: removing stale lockfile (without fcntl)", name);
+ }
+ }
+ close(fd);
+
+ /* Remove stale lockfile. */
+ if (unlink(name)) {
+ VLOG_ERR("%s: unlink: %s", name, strerror(errno));
+ return -errno;
+ }
+ return 0;
+}
+
+/* Attempt to create the lockfile. On error, return -1 with errno set
+ * to an appropriate value. On success, return a file descriptor. */
+int
+create_lockfile(const char *name)
+{
+ const char *username;
+ char buffer[BUFSIZ];
+ struct passwd *pwd;
+ mode_t old_umask;
+ uid_t uid;
+ int fd;
+
+ /* Remove an existing lockfile if it was created by us. */
+ int retval = remove_lockfile(-1, name);
+ if (!retval) {
+ return retval;
+ }
+
+ /* Create file. */
+ old_umask = umask(022);
+ fd = open(name, O_WRONLY | O_CREAT | O_EXCL, 0666);
+ if (fd < 0) {
+ int error = errno;
+ VLOG_ERR("%s: create: %s", name, strerror(error));
+ umask(old_umask);
+ return -error;
+ }
+ umask(old_umask);
+
+ /* Lock file. */
+ if (fcntl_lock(fd)) {
+ int error = errno;
+ close(fd);
+ VLOG_ERR("%s: cannot lock: %s", name, strerror(error));
+ return -error;
+ }
+
+ /* Write to file. */
+ uid = getuid();
+ pwd = getpwuid(uid);
+ username = pwd ? pwd->pw_name : "unknown";
+ snprintf(buffer, sizeof buffer, "%10ld %s %.20s fcntl\n",
+ (long int) getpid(), program_name, username);
+ if (write(fd, buffer, strlen(buffer)) != strlen(buffer)) {
+ int error = errno;
+ VLOG_ERR("%s: write: %s", name, strerror(error));
+ close(fd);
+ unlink(name);
+ return -error;
+ }
+
+ return fd;
+}
--- /dev/null
+/* Copyright (c) 2008, 2009 The Board of Trustees of The Leland Stanford
+ * Junior University
+ *
+ * We are making the OpenFlow specification and associated documentation
+ * (Software) available for public use and benefit with the expectation
+ * that others will use, modify and enhance the Software and contribute
+ * those enhancements back to the community. However, since we would
+ * like to make the Software available for broadest use, with as few
+ * restrictions as possible permission is hereby granted, free of
+ * charge, to any person obtaining a copy of this Software to deal in
+ * the Software under the copyrights without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ *
+ * The name and trademarks of copyright holder(s) may NOT be used in
+ * advertising or publicity pertaining to the Software or any
+ * derivatives without specific, written prior permission.
+ */
+
+#ifndef LOCKFILE_H
+#define LOCKFILE_H 1
+
+int create_lockfile(const char *name);
+int remove_lockfile(int fd, const char *name);
+
+#endif /* lockfile.h */
--- /dev/null
+/*
+ * sha1.c
+ *
+ * Description:
+ * This file implements the Secure Hashing Algorithm 1 as
+ * defined in FIPS PUB 180-1 published April 17, 1995.
+ *
+ * The SHA-1, produces a 160-bit message digest for a given
+ * data stream. It should take about 2**n steps to find a
+ * message with the same digest as a given message and
+ * 2**(n/2) to find any two messages with the same digest,
+ * when n is the digest size in bits. Therefore, this
+ * algorithm can serve as a means of providing a
+ * "fingerprint" for a message.
+ *
+ * Portability Issues:
+ * SHA-1 is defined in terms of 32-bit "words". This code
+ * uses <stdint.h> (included via "sha1.h" to define 32 and 8
+ * bit unsigned integer types. If your C compiler does not
+ * support 32 bit unsigned integers, this code is not
+ * appropriate.
+ *
+ * Caveats:
+ * SHA-1 is designed to work with messages less than 2^64 bits
+ * long. Although SHA-1 allows a message digest to be generated
+ * for messages of any number of bits less than 2^64, this
+ * implementation only works with messages with a length that is
+ * a multiple of the size of an 8-bit character.
+ *
+ */
+
+#include "sha1.h"
+
+/*
+ * Define the SHA1 circular left shift macro
+ */
+#define SHA1CircularShift(bits,word) \
+ (((word) << (bits)) | ((word) >> (32-(bits))))
+
+/* Local Function Prototyptes */
+void SHA1PadMessage(SHA1Context *);
+void SHA1ProcessMessageBlock(SHA1Context *);
+
+/*
+ * SHA1Reset
+ *
+ * Description:
+ * This function will initialize the SHA1Context in preparation
+ * for computing a new SHA1 message digest.
+ *
+ * Parameters:
+ * context: [in/out]
+ * The context to reset.
+ *
+ * Returns:
+ * sha Error Code.
+ *
+ */
+int SHA1Reset(SHA1Context *context)
+{
+ if (!context)
+ {
+ return shaNull;
+ }
+
+ context->Length_Low = 0;
+ context->Length_High = 0;
+ context->Message_Block_Index = 0;
+
+ context->Intermediate_Hash[0] = 0x67452301;
+ context->Intermediate_Hash[1] = 0xEFCDAB89;
+ context->Intermediate_Hash[2] = 0x98BADCFE;
+ context->Intermediate_Hash[3] = 0x10325476;
+ context->Intermediate_Hash[4] = 0xC3D2E1F0;
+
+ context->Computed = 0;
+ context->Corrupted = 0;
+
+ return shaSuccess;
+}
+
+/*
+ * SHA1Result
+ *
+ * Description:
+ * This function will return the 160-bit message digest into the
+ * Message_Digest array provided by the caller.
+ * NOTE: The first octet of hash is stored in the 0th element,
+ * the last octet of hash in the 19th element.
+ *
+ * Parameters:
+ * context: [in/out]
+ * The context to use to calculate the SHA-1 hash.
+ * Message_Digest: [out]
+ * Where the digest is returned.
+ *
+ * Returns:
+ * sha Error Code.
+ *
+ */
+int SHA1Result( SHA1Context *context,
+ uint8_t Message_Digest[SHA1HashSize])
+{
+ int i;
+
+ if (!context || !Message_Digest)
+ {
+ return shaNull;
+ }
+
+ if (context->Corrupted)
+ {
+ return context->Corrupted;
+ }
+
+ if (!context->Computed)
+ {
+ SHA1PadMessage(context);
+ for(i=0; i<64; ++i)
+ {
+ /* message may be sensitive, clear it out */
+ context->Message_Block[i] = 0;
+ }
+ context->Length_Low = 0; /* and clear length */
+ context->Length_High = 0;
+ context->Computed = 1;
+ }
+
+ for(i = 0; i < SHA1HashSize; ++i)
+ {
+ Message_Digest[i] = context->Intermediate_Hash[i>>2]
+ >> 8 * ( 3 - ( i & 0x03 ) );
+ }
+
+ return shaSuccess;
+}
+
+/*
+ * SHA1Input
+ *
+ * Description:
+ * This function accepts an array of octets as the next portion
+ * of the message.
+ *
+ * Parameters:
+ * context: [in/out]
+ * The SHA context to update
+ * message_array: [in]
+ * An array of characters representing the next portion of
+ * the message.
+ * length: [in]
+ * The length of the message in message_array
+ *
+ * Returns:
+ * sha Error Code.
+ *
+ */
+int SHA1Input( SHA1Context *context,
+ const uint8_t *message_array,
+ unsigned length)
+{
+ if (!length)
+ {
+ return shaSuccess;
+ }
+
+ if (!context || !message_array)
+ {
+ return shaNull;
+ }
+
+ if (context->Computed)
+ {
+ context->Corrupted = shaStateError;
+ return shaStateError;
+ }
+
+ if (context->Corrupted)
+ {
+ return context->Corrupted;
+ }
+ while(length-- && !context->Corrupted)
+ {
+ context->Message_Block[context->Message_Block_Index++] =
+ (*message_array & 0xFF);
+
+ context->Length_Low += 8;
+ if (context->Length_Low == 0)
+ {
+ context->Length_High++;
+ if (context->Length_High == 0)
+ {
+ /* Message is too long */
+ context->Corrupted = 1;
+ }
+ }
+
+ if (context->Message_Block_Index == 64)
+ {
+ SHA1ProcessMessageBlock(context);
+ }
+
+ message_array++;
+ }
+
+ return shaSuccess;
+}
+
+/*
+ * SHA1ProcessMessageBlock
+ *
+ * Description:
+ * This function will process the next 512 bits of the message
+ * stored in the Message_Block array.
+ *
+ * Parameters:
+ * None.
+ *
+ * Returns:
+ * Nothing.
+ *
+ * Comments:
+ * Many of the variable names in this code, especially the
+ * single character names, were used because those were the
+ * names used in the publication.
+ *
+ *
+ */
+void SHA1ProcessMessageBlock(SHA1Context *context)
+{
+ const uint32_t K[] = { /* Constants defined in SHA-1 */
+ 0x5A827999,
+ 0x6ED9EBA1,
+ 0x8F1BBCDC,
+ 0xCA62C1D6
+ };
+ int t; /* Loop counter */
+ uint32_t temp; /* Temporary word value */
+ uint32_t W[80]; /* Word sequence */
+ uint32_t A, B, C, D, E; /* Word buffers */
+
+ /*
+ * Initialize the first 16 words in the array W
+ */
+ for(t = 0; t < 16; t++)
+ {
+ W[t] = context->Message_Block[t * 4] << 24;
+ W[t] |= context->Message_Block[t * 4 + 1] << 16;
+ W[t] |= context->Message_Block[t * 4 + 2] << 8;
+ W[t] |= context->Message_Block[t * 4 + 3];
+ }
+
+ for(t = 16; t < 80; t++)
+ {
+ W[t] = SHA1CircularShift(1,W[t-3] ^ W[t-8] ^ W[t-14] ^ W[t-16]);
+ }
+
+ A = context->Intermediate_Hash[0];
+ B = context->Intermediate_Hash[1];
+ C = context->Intermediate_Hash[2];
+ D = context->Intermediate_Hash[3];
+ E = context->Intermediate_Hash[4];
+
+ for(t = 0; t < 20; t++)
+ {
+ temp = SHA1CircularShift(5,A) +
+ ((B & C) | ((~B) & D)) + E + W[t] + K[0];
+ E = D;
+ D = C;
+ C = SHA1CircularShift(30,B);
+ B = A;
+ A = temp;
+ }
+
+ for(t = 20; t < 40; t++)
+ {
+ temp = SHA1CircularShift(5,A) + (B ^ C ^ D) + E + W[t] + K[1];
+ E = D;
+ D = C;
+ C = SHA1CircularShift(30,B);
+ B = A;
+ A = temp;
+ }
+
+ for(t = 40; t < 60; t++)
+ {
+ temp = SHA1CircularShift(5,A) +
+ ((B & C) | (B & D) | (C & D)) + E + W[t] + K[2];
+ E = D;
+ D = C;
+ C = SHA1CircularShift(30,B);
+ B = A;
+ A = temp;
+ }
+
+ for(t = 60; t < 80; t++)
+ {
+ temp = SHA1CircularShift(5,A) + (B ^ C ^ D) + E + W[t] + K[3];
+ E = D;
+ D = C;
+ C = SHA1CircularShift(30,B);
+ B = A;
+ A = temp;
+ }
+
+ context->Intermediate_Hash[0] += A;
+ context->Intermediate_Hash[1] += B;
+ context->Intermediate_Hash[2] += C;
+ context->Intermediate_Hash[3] += D;
+ context->Intermediate_Hash[4] += E;
+
+ context->Message_Block_Index = 0;
+}
+
+
+/*
+ * SHA1PadMessage
+ *
+ * Description:
+ * According to the standard, the message must be padded to an even
+ * 512 bits. The first padding bit must be a '1'. The last 64
+ * bits represent the length of the original message. All bits in
+ * between should be 0. This function will pad the message
+ * according to those rules by filling the Message_Block array
+ * accordingly. It will also call the ProcessMessageBlock function
+ * provided appropriately. When it returns, it can be assumed that
+ * the message digest has been computed.
+ *
+ * Parameters:
+ * context: [in/out]
+ * The context to pad
+ * ProcessMessageBlock: [in]
+ * The appropriate SHA*ProcessMessageBlock function
+ * Returns:
+ * Nothing.
+ *
+ */
+
+void SHA1PadMessage(SHA1Context *context)
+{
+ /*
+ * Check to see if the current message block is too small to hold
+ * the initial padding bits and length. If so, we will pad the
+ * block, process it, and then continue padding into a second
+ * block.
+ */
+ if (context->Message_Block_Index > 55)
+ {
+ context->Message_Block[context->Message_Block_Index++] = 0x80;
+ while(context->Message_Block_Index < 64)
+ {
+ context->Message_Block[context->Message_Block_Index++] = 0;
+ }
+
+ SHA1ProcessMessageBlock(context);
+
+ while(context->Message_Block_Index < 56)
+ {
+ context->Message_Block[context->Message_Block_Index++] = 0;
+ }
+ }
+ else
+ {
+ context->Message_Block[context->Message_Block_Index++] = 0x80;
+ while(context->Message_Block_Index < 56)
+ {
+ context->Message_Block[context->Message_Block_Index++] = 0;
+ }
+ }
+
+ /*
+ * Store the message length as the last 8 octets
+ */
+ context->Message_Block[56] = context->Length_High >> 24;
+ context->Message_Block[57] = context->Length_High >> 16;
+ context->Message_Block[58] = context->Length_High >> 8;
+ context->Message_Block[59] = context->Length_High;
+ context->Message_Block[60] = context->Length_Low >> 24;
+ context->Message_Block[61] = context->Length_Low >> 16;
+ context->Message_Block[62] = context->Length_Low >> 8;
+ context->Message_Block[63] = context->Length_Low;
+
+ SHA1ProcessMessageBlock(context);
+}
--- /dev/null
+/*
+ * sha1.h
+ *
+ * Description:
+ * This is the header file for code which implements the Secure
+ * Hashing Algorithm 1 as defined in FIPS PUB 180-1 published
+ * April 17, 1995.
+ *
+ * Many of the variable names in this code, especially the
+ * single character names, were used because those were the names
+ * used in the publication.
+ *
+ * Please read the file sha1.c for more information.
+ *
+ */
+#ifndef _SHA1_H_
+#define _SHA1_H_
+
+#include <stdint.h>
+/*
+ * If you do not have the ISO standard stdint.h header file, then you
+ * must typdef the following:
+ * name meaning
+ * uint32_t unsigned 32 bit integer
+ * uint8_t unsigned 8 bit integer (i.e., unsigned char)
+ * int_least16_t integer of >= 16 bits
+ *
+ */
+
+#ifndef _SHA_enum_
+#define _SHA_enum_
+enum
+{
+ shaSuccess = 0,
+ shaNull, /* Null pointer parameter */
+ shaInputTooLong, /* input data too long */
+ shaStateError /* called Input after Result */
+};
+#endif
+#define SHA1HashSize 20
+
+/*
+ * This structure will hold context information for the SHA-1
+ * hashing operation
+ */
+typedef struct SHA1Context
+{
+ uint32_t Intermediate_Hash[SHA1HashSize/4]; /* Message Digest */
+
+ uint32_t Length_Low; /* Message length in bits */
+ uint32_t Length_High; /* Message length in bits */
+
+ /* Index into message block array */
+ int_least16_t Message_Block_Index;
+ uint8_t Message_Block[64]; /* 512-bit message blocks */
+
+ int Computed; /* Is the digest computed? */
+ int Corrupted; /* Is the message digest corrupted? */
+} SHA1Context;
+
+/*
+ * Function Prototypes
+ */
+int SHA1Reset( SHA1Context *);
+int SHA1Input( SHA1Context *,
+ const uint8_t *,
+ unsigned int);
+int SHA1Result( SHA1Context *,
+ uint8_t Message_Digest[SHA1HashSize]);
+
+#endif
svec_add_nocopy(svec, xstrdup(name));
}
+void
+svec_del(struct svec *svec, const char *name)
+{
+ size_t offset;
+
+ offset = svec_find(svec, name);
+ if (offset != SIZE_MAX) {
+ free(svec->names[offset]);
+ svec->names[offset] = svec->names[svec->n-1];
+ svec->n--;
+ svec_sort(svec);
+ }
+}
+
static void
svec_expand(struct svec *svec)
{
void svec_clear(struct svec *);
void svec_add(struct svec *, const char *);
void svec_add_nocopy(struct svec *, char *);
+void svec_del(struct svec *, const char *);
void svec_append(struct svec *, const struct svec *);
void svec_terminate(struct svec *);
void svec_sort(struct svec *);
VLOG_MODULE(in_band)
VLOG_MODULE(leak_checker)
VLOG_MODULE(learning_switch)
+VLOG_MODULE(lockfile)
VLOG_MODULE(mac_learning)
+VLOG_MODULE(mgmt)
VLOG_MODULE(netdev)
VLOG_MODULE(netflow)
VLOG_MODULE(netlink)
char *command_dir; /* Directory that contains commands. */
};
-static void reconfigure(struct ofproto *, const char *br_name);
+static void reconfigure(struct ofproto *, struct ofsettings *);
static void parse_options(int argc, char *argv[], struct ofsettings *);
static void usage(void) NO_RETURN;
}
}
- reconfigure(ofproto, s.br_name);
+ reconfigure(ofproto, &s);
while (ofproto_is_alive(ofproto)) {
if (signal_poll(sighup)) {
- reconfigure(ofproto, s.br_name);
+ reconfigure(ofproto, &s);
}
error = ofproto_run(ofproto);
}
static void
-reconfigure(struct ofproto *ofproto, const char *br_name)
+reconfigure(struct ofproto *ofproto, struct ofsettings *s)
{
cfg_read();
- if (br_name) {
+ if (s->br_name) {
struct svec collectors;
svec_init(&collectors);
- cfg_get_all_keys(&collectors, "netflow.%s.host", br_name);
+ cfg_get_all_keys(&collectors, "netflow.%s.host", s->br_name);
ofproto_set_netflow(ofproto, &collectors);
svec_destroy(&collectors);
}
+
+ /* xxx Changing this should probably force reconnect to NOX! */
+ ofproto_set_mgmt_id(ofproto, cfg_get_mac(0, "vswitchd.mgmt.id"));
}
\f
/* User interface. */
break;
case 'F':
- error = cfg_add_file(optarg);
+ error = cfg_set_file(optarg);
if (error) {
- ofp_fatal(error, "failed to add configuration file or "
- "directory \"%s\"", optarg);
+ ofp_fatal(error, "failed to add configuration file \"%s\"",
+ optarg);
}
break;
#include "openflow/datapath-protocol.h"
#include "openflow/nicira-ext.h"
#include "openflow/openflow.h"
+#include "openflow/openflow-mgmt.h"
#include "packets.h"
#include "pinsched.h"
#include "pktbuf.h"
/* Settings. */
uint64_t datapath_id; /* Datapath ID. */
uint64_t fallback_dpid; /* Datapath ID if no better choice found. */
+ uint64_t mgmt_id; /* Management channel identifier. */
char *manufacturer; /* Manufacturer. */
char *hardware; /* Hardware. */
char *software; /* Software version. */
}
}
+void
+ofproto_set_mgmt_id(struct ofproto *p, uint64_t mgmt_id)
+{
+ p->mgmt_id = mgmt_id;
+}
+
void
ofproto_set_probe_interval(struct ofproto *p, int probe_interval)
{
}
}
+static void
+send_capability_reply(struct ofproto *p, struct ofconn *ofconn, uint32_t xid)
+{
+ struct ofmp_capability_reply *ocr;
+ struct ofpbuf *b;
+ char capabilities[] = "com.nicira.mgmt.manager=false\n";
+
+ ocr = make_openflow_xid(sizeof(*ocr), OFPT_VENDOR, xid, &b);
+ ocr->header.header.vendor = htonl(NX_VENDOR_ID);
+ ocr->header.header.subtype = htonl(NXT_MGMT);
+ ocr->header.type = htons(OFMPT_CAPABILITY_REPLY);
+
+ ocr->format = htonl(OFMPCOF_SIMPLE);
+ ocr->mgmt_id = htonll(p->mgmt_id);
+
+ ofpbuf_put(b, capabilities, strlen(capabilities));
+
+ queue_tx(b, ofconn);
+}
+
+static int
+handle_ofmp(struct ofproto *p, struct ofconn *ofconn,
+ struct ofmp_header *ofmph)
+{
+ size_t msg_len = ntohs(ofmph->header.header.length);
+ if (msg_len < sizeof(*ofmph)) {
+ VLOG_WARN_RL(&rl, "dropping short managment message: %d\n", msg_len);
+ return ofp_mkerr(OFPET_BAD_REQUEST, OFPBRC_BAD_LENGTH);
+ }
+
+ if (ofmph->type == htons(OFMPT_CAPABILITY_REQUEST)) {
+ struct ofmp_capability_request *ofmpcr;
+
+ if (msg_len < sizeof(struct ofmp_capability_request)) {
+ VLOG_WARN_RL(&rl, "dropping short capability request: %d\n",
+ msg_len);
+ return ofp_mkerr(OFPET_BAD_REQUEST, OFPBRC_BAD_LENGTH);
+ }
+
+ ofmpcr = (struct ofmp_capability_request *)ofmph;
+ if (ofmpcr->format != htonl(OFMPCAF_SIMPLE)) {
+ /* xxx Find a better type than bad subtype */
+ return ofp_mkerr(OFPET_BAD_REQUEST, OFPBRC_BAD_SUBTYPE);
+ }
+
+ send_capability_reply(p, ofconn, ofmph->header.header.xid);
+ return 0;
+ } else {
+ return ofp_mkerr(OFPET_BAD_REQUEST, OFPBRC_BAD_SUBTYPE);
+ }
+}
+
static int
handle_vendor(struct ofproto *p, struct ofconn *ofconn, void *msg)
{
return executer_handle_request(p->executer, ofconn->rconn, msg);
}
break;
+
+ case NXT_MGMT:
+ return handle_ofmp(p, ofconn, msg);
}
+
return ofp_mkerr(OFPET_BAD_REQUEST, OFPBRC_BAD_SUBTYPE);
}
/* Configuration. */
void ofproto_set_datapath_id(struct ofproto *, uint64_t datapath_id);
+void ofproto_set_mgmt_id(struct ofproto *, uint64_t mgmt_id);
void ofproto_set_probe_interval(struct ofproto *, int probe_interval);
void ofproto_set_max_backoff(struct ofproto *, int max_backoff);
void ofproto_set_desc(struct ofproto *,
.ds PN secchan
-.TH secchan 8 "February 2009" "OpenFlow" "OpenFlow Manual"
+.TH secchan 8 "March 2009" "OpenFlow" "OpenFlow Manual"
.SH NAME
secchan \- secure channel connecting an OpenFlow datapath to a controller
.SH OPTIONS
.SS "Configuration Options"
.TP
-\fB-F \fIfile\fR|\fIdir\fR, \fB--config=\fIfile\fR|\fIdir\fR
-The \fB-F\fR or \fB--config\fR option specifies a configuration file
-or directory. If a regular file is named, then \fBsecchan\fR reads
-that file. If a directory is named, then \fBsecchan\fR reads all the
-files in that directory whose names consist entirely of English
-letters, digits, and the special characters \fB._-\fR and do not begin
-with \fB.\fR.
-.IP
+\fB-F \fIfile\fR, \fB--config=\fIfile\fR
+The \fB-F\fR or \fB--config\fR option specifies a configuration file.
For a description of the configuration syntax, see \fBvswitchd.conf\fR(5).
Currently, only the NetFlow section applies to \fBsecchan\fR.
#include <errno.h>
#include <limits.h>
#include "flow.h"
+#include <limits.h>
#include "packets.h"
#undef NDEBUG
vswitchd/brcompat.h \
vswitchd/bridge.c \
vswitchd/bridge.h \
+ vswitchd/mgmt.c \
+ vswitchd/mgmt.h \
vswitchd/vswitchd.c
vswitchd_vswitchd_LDADD = \
secchan/libsecchan.a \
#include "timeval.h"
#include "util.h"
#include "vlog-socket.h"
+#include "vswitchd.h"
#include "vlog.h"
#define THIS_MODULE VLM_brcompat
+extern bool brc_enabled;
+
static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(5, 60);
/* Netlink socket to kernel datapath */
struct nl_sock *nl_sock;
-/* Pointer to configuration file name */
-static const char *config_name;
-
/* xxx Just hangs if datapath is rmmod/insmod. Learn to reconnect? */
/* The Generic Netlink family number used for bridge compatibility. */
[BRC_GENL_A_DP_NAME] = { .type = NL_A_STRING },
};
-/* Write 'new_cfg' into the configuration file. Returns 0 if successful,
- * otherwise a positive errno value. */
-static int
-brc_write_config(struct svec *new_cfg)
-{
- char *tmp_name;
- int fd;
- size_t i;
-
- svec_sort(new_cfg);
- svec_unique(new_cfg);
-
- /* Put the temporary file in the same directory as config_name, so that
- * they are guaranteed to be in the same file system and therefore we can
- * rename() tmp_name over config_name, and put "~" in its name so that
- * cfg.c will ignore it if it is reading all the files in that
- * directory. */
- tmp_name = xasprintf("%s.~tmp~", config_name);
-
- fd = open(tmp_name, O_CREAT|O_WRONLY|O_TRUNC, S_IRUSR|S_IWUSR);
- if (fd == -1) {
- VLOG_WARN_RL(&rl, "could not open temp config file for writing: %s",
- strerror(errno));
- free(tmp_name);
- return errno;
- }
-
- for (i = 0; i < new_cfg->n; i++) {
- int retval;
- const char *entry = new_cfg->names[i];
- retval = write(fd, entry, strlen(entry));
- if (retval != strlen(entry)) {
- VLOG_WARN_RL(&rl, "problem writing to temp config file %d: %s",
- retval, strerror(errno));
- free(tmp_name);
- return errno;
- }
- }
- close(fd);
-
- if (rename(tmp_name, config_name) < 0) {
- VLOG_WARN_RL(&rl, "could not rename temp config file: %s",
- strerror(errno));
- free(tmp_name);
- return errno;
- }
- free(tmp_name);
-
- return 0;
-}
-
/* Modify the existing configuration according to 'act'. The configuration
* file will be modified to reflect these changes. The caller is
* responsible for causing vswitchd to actually re-read its configuration. */
brc_modify_config(const char *dp_name, const char *port_name,
enum bmc_action act)
{
- struct svec new_cfg;
- struct svec old_br;
- struct svec old_keys;
- size_t i, j;
+ if (!brc_enabled) {
+ return;
+ }
- if (!config_name) {
+ if (cfg_lock(NULL)) {
+ /* Couldn't lock config file. */
+ /* xxx Handle this better */
return;
}
- svec_init(&new_cfg);
- svec_init(&old_br);
- svec_init(&old_keys);
- cfg_get_subsections(&old_br, "bridge");
- for (i = 0; i < old_br.n; i++) {
- /* If we're deleting the datapath, skip over its current config. */
- if ((act == BMC_DEL_DP) && !strcmp(old_br.names[i], dp_name)) {
- continue;
- }
+ switch (act) {
- cfg_get_all_keys(&old_keys, "bridge.%s.port", old_br.names[i]);
- for (j = 0; j < old_keys.n; j++) {
- if ((act == BMC_DEL_PORT)
- && !strcmp(old_br.names[i], dp_name)
- && !strcmp(old_keys.names[j], port_name)) {
- continue;
- }
- svec_add_nocopy(&new_cfg,
- xasprintf("bridge.%s.port = %s\n",
- old_br.names[i], old_keys.names[j]));
- }
- }
- svec_destroy(&old_br);
- svec_destroy(&old_keys);
+ case BMC_ADD_DP:
+ cfg_add_entry("bridge.%s.port=%s", dp_name, dp_name);
+ break;
- /* Always add the datapath's own device. */
- if (act == BMC_ADD_DP) {
- svec_add_nocopy(&new_cfg,
- xasprintf("bridge.%s.port = %s\n", dp_name, dp_name));
- }
-
- if (act == BMC_ADD_PORT) {
- svec_add_nocopy(&new_cfg, xasprintf("bridge.%s.port = %s\n",
- dp_name, port_name));
- }
+ case BMC_ADD_PORT:
+ cfg_add_entry("bridge.%s.port=%s", dp_name, port_name);
+ break;
- brc_write_config(&new_cfg);
- svec_destroy(&new_cfg);
+ case BMC_DEL_DP:
+ cfg_del_section("bridge.%s", dp_name);
+ break;
- cfg_read();
+ case BMC_DEL_PORT:
+ cfg_del_entry("bridge.%s.port=%s", dp_name, port_name);
+ break;
+ }
+
+ cfg_write();
+ cfg_unlock();
}
static int
brc_modify_config(dp_name, NULL, BMC_ADD_DP);
- bridge_reconfigure();
+ reconfigure();
if (!bridge_exists(dp_name)) {
return EINVAL;
brc_modify_config(dp_name, NULL, BMC_DEL_DP);
- bridge_reconfigure();
+ reconfigure();
if (bridge_exists(dp_name)) {
return EINVAL;
}
/* Force vswitchd to reconfigure itself. */
- bridge_reconfigure();
+ reconfigure();
return 0;
}
}
void
-brc_init(const char *file_name)
+brc_init(void)
{
if (brc_open(&nl_sock)) {
ofp_fatal(0, "could not open brcompat socket. Check "
"\"brcompat\" kernel module.");
}
-
- config_name = file_name;
}
BMC_DEL_PORT
};
-void brc_init(const char *);
+void brc_init(void);
void brc_wait(void);
void brc_run(void);
void brc_modify_config(const char *dp_name, const char *port_name,
uint16_t dp_ifidx;
};
+extern uint64_t mgmt_id;
+
struct iface {
struct port *port; /* Containing port. */
size_t port_ifidx; /* Index within containing port. */
bridge_reconfigure();
}
+#ifdef HAVE_OPENSSL
static bool
config_string_change(const char *key, char **valuep)
{
}
}
-#ifdef HAVE_OPENSSL
static void
bridge_configure_ssl(void)
{
return bridge_lookup(name) ? true : false;
}
+uint64_t
+bridge_get_datapathid(const char *name)
+{
+ struct bridge *br = bridge_lookup(name);
+ return br ? ofproto_get_datapath_id(br->ofproto) : 0;
+}
+
static int
if_up(const char *netdev_name)
{
}
cfg_get_all_keys(&new_ports, "bridge.%s.port", br->name);
+ ofproto_set_mgmt_id(br->ofproto, mgmt_id);
+
/* Get rid of deleted ports and add new ports. */
svec_sort(&old_ports);
assert(svec_is_unique(&old_ports));
pfx = xasprintf("bridge.%s.controller", br->name);
controller = cfg_get_string(0, "%s", pfx);
ctl = controller ? xstrdup(controller) : NULL;
+
+ /* If a controller is not specified for the bridge, try using the
+ * management channel's settings. */
+ if (!ctl) {
+ controller = cfg_get_string(0, "mgmt.controller");
+ ctl = controller ? xstrdup(controller) : NULL;
+ }
+
if (ctl) {
const char *fail_mode;
int bridge_run(void);
void bridge_wait(void);
bool bridge_exists(const char *);
+uint64_t bridge_get_datapathid(const char *name);
#endif /* bridge.h */
--- /dev/null
+/* 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.
+ *
+ * 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.
+ *
+ */
+
+#include <arpa/inet.h>
+#include <assert.h>
+#include <errno.h>
+
+#include "bridge.h"
+#include "cfg.h"
+#include "list.h"
+#include "mgmt.h"
+#include "openflow/nicira-ext.h"
+#include "openflow/openflow.h"
+#include "openflow/openflow-mgmt.h"
+#include "ofpbuf.h"
+#include "packets.h"
+#include "rconn.h"
+#include "svec.h"
+#include "vconn.h"
+#include "vswitchd.h"
+#include "xtoxll.h"
+
+#define THIS_MODULE VLM_mgmt
+#include "vlog.h"
+
+#define MAX_BACKOFF_DEFAULT 15
+#define PROBE_INTERVAL_DEFAULT 15
+
+static struct svec mgmt_cfg;
+static uint8_t cfg_cookie[CFG_COOKIE_LEN];
+static struct rconn *mgmt_rconn;
+static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(60, 60);
+static struct svec capabilities;
+uint64_t mgmt_id;
+
+
+#define TXQ_LIMIT 128 /* Max number of packets to queue for tx. */
+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);
+
+void
+mgmt_init(void)
+{
+ txqlen = rconn_packet_counter_create();
+
+ svec_init(&mgmt_cfg);
+ svec_init(&capabilities);
+ svec_add_nocopy(&capabilities,
+ xasprintf("com.nicira.mgmt.manager=true\n"));
+
+ mgmt_id = cfg_get_mac(0, "mgmt.id");
+ if (!mgmt_id) {
+ /* Randomly generate a mgmt id */
+ mgmt_id = pick_fallback_mgmt_id();
+ }
+}
+
+#ifdef HAVE_OPENSSL
+static bool
+config_string_change(const char *key, char **valuep)
+{
+ const char *value = cfg_get_string(0, "%s", key);
+ if (value && (!*valuep || strcmp(value, *valuep))) {
+ free(*valuep);
+ *valuep = xstrdup(value);
+ return true;
+ } else {
+ return false;
+ }
+}
+
+static void
+bridge_configure_ssl(void)
+{
+ /* XXX SSL should be configurable on a per-bridge basis.
+ * XXX should be possible to de-configure SSL. */
+ static char *private_key_file;
+ static char *certificate_file;
+ static char *cacert_file;
+
+ if (config_string_change("ssl.private-key", &private_key_file)) {
+ vconn_ssl_set_private_key_file(private_key_file);
+ }
+
+ if (config_string_change("ssl.certificate", &certificate_file)) {
+ vconn_ssl_set_certificate_file(certificate_file);
+ }
+
+ if (config_string_change("ssl.ca-cert", &cacert_file)) {
+ vconn_ssl_set_ca_cert_file(cacert_file,
+ cfg_get_bool(0, "ssl.bootstrap-ca-cert"));
+ }
+}
+#endif
+
+void
+mgmt_reconfigure(void)
+{
+ struct svec new_cfg;
+ uint8_t new_cookie[CFG_COOKIE_LEN];
+ bool cfg_updated = false;
+ const char *controller_name;
+ int max_backoff;
+ int probe_interval;
+ int retval;
+
+ if (!cfg_has_section("mgmt")) {
+ if (mgmt_rconn) {
+ rconn_destroy(mgmt_rconn);
+ mgmt_rconn = NULL;
+ }
+ return;
+ }
+
+ /* If this is an established connection, send a resources update. */
+ /* xxx This is wasteful if there were no resource changes!!! */
+ if (mgmt_rconn) {
+ send_resources_update(0, false);
+ }
+
+ cfg_get_cookie(new_cookie);
+ if (memcmp(cfg_cookie, new_cookie, sizeof(cfg_cookie))) {
+ memcpy(cfg_cookie, new_cookie, sizeof(cfg_cookie));
+ cfg_updated = true;
+ }
+
+ svec_init(&new_cfg);
+ cfg_get_subsections(&new_cfg, "mgmt");
+ if (svec_equal(&mgmt_cfg, &new_cfg)) {
+ /* Reconnecting to the controller causes the config file to be
+ * resent automatically. If we're not reconnecting and the
+ * config file has changed, we need to notify the controller of
+ * changes. */
+ if (cfg_updated) {
+ send_config_update(0, false);
+ }
+ return;
+ }
+
+ controller_name = cfg_get_string(0, "mgmt.controller");
+ if (!controller_name) {
+ VLOG_ERR("no controller specified for managment");
+ return;
+ }
+
+ max_backoff = cfg_get_int(0, "mgmt.max-backoff");
+ if (max_backoff < 1) {
+ max_backoff = MAX_BACKOFF_DEFAULT;
+ } else if (max_backoff > 3600) {
+ max_backoff = 3600;
+ }
+
+ probe_interval = cfg_get_int(0, "mgmt.probe-interval");
+ if (probe_interval < 5) {
+ probe_interval = MAX_BACKOFF_DEFAULT;
+ }
+
+ /* xxx If this changes, we need to restart bridges to use new id,
+ * xxx but they need the id before the connect to controller, but we
+ * xxx need their dpids. */
+ mgmt_id = cfg_get_mac(0, "mgmt.id");
+ if (!mgmt_id) {
+ /* Randomly generate a mgmt id */
+ mgmt_id = pick_fallback_mgmt_id();
+ }
+
+ svec_swap(&new_cfg, &mgmt_cfg);
+ svec_destroy(&new_cfg);
+
+#ifdef HAVE_OPENSSL
+ /* Configure SSL. */
+ bridge_configure_ssl();
+#endif
+
+ if (mgmt_rconn) {
+ rconn_destroy(mgmt_rconn);
+ mgmt_rconn = NULL;
+ }
+ mgmt_rconn = rconn_create(probe_interval, max_backoff);
+ if (controller_name) {
+ retval = rconn_connect(mgmt_rconn, controller_name);
+ if (retval == EAFNOSUPPORT) {
+ VLOG_ERR("no support for %s vconn", controller_name);
+ }
+ }
+}
+
+static int
+send_openflow_buffer(struct ofpbuf *buffer)
+{
+ int retval;
+
+ 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)
+{
+ struct ofmp_header *oh;
+
+ oh = make_openflow_xid(ofmp_len, OFPT_VENDOR, xid, bufferp);
+ oh->header.vendor = htonl(NX_VENDOR_ID);
+ oh->header.subtype = htonl(NXT_MGMT);
+ oh->type = htons(type);
+
+ return oh;
+}
+
+static void *
+make_ofmp(size_t ofmp_len, uint16_t type, struct ofpbuf **bufferp)
+{
+ struct ofmp_header *oh;
+
+ oh = make_openflow(ofmp_len, OFPT_VENDOR, bufferp);
+ oh->header.vendor = htonl(NX_VENDOR_ID);
+ oh->header.subtype = htonl(NXT_MGMT);
+ oh->type = htons(type);
+
+ return oh;
+}
+
+static void
+send_capability_reply(uint32_t xid)
+{
+ int i;
+ struct ofpbuf *buffer;
+ struct ofmp_capability_reply *ofmpcr;
+
+ ofmpcr = make_ofmp_xid(sizeof *ofmpcr, OFMPT_CAPABILITY_REPLY,
+ xid, &buffer);
+ ofmpcr->format = htonl(OFMPCOF_SIMPLE);
+ ofmpcr->mgmt_id = htonll(mgmt_id);
+ for (i=0; i<capabilities.n; i++) {
+ ofpbuf_put(buffer, capabilities.names[i],
+ strlen(capabilities.names[i]));
+ }
+ send_openflow_buffer(buffer);
+}
+
+static void
+send_resources_update(uint32_t xid, bool use_xid)
+{
+ struct ofpbuf *buffer;
+ struct ofmp_resources_update *ofmpru;
+ struct ofmp_tlv *tlv;
+ struct svec br_list;
+ int i;
+
+ if (use_xid) {
+ ofmpru = make_ofmp_xid(sizeof *ofmpru, OFMPT_RESOURCES_UPDATE,
+ xid, &buffer);
+ } else {
+ ofmpru = make_ofmp(sizeof *ofmpru, OFMPT_RESOURCES_UPDATE, &buffer);
+ }
+
+ 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]);
+ if (!dp_id) {
+ printf("xxx Bridge %s doesn't seem to exist!", br_list.names[i]);
+ continue;
+ }
+ dp_tlv = ofpbuf_put_zeros(buffer, sizeof(*dp_tlv));
+ dp_tlv->type = htons(OFMPTSR_DP);
+ dp_tlv->len = htons(sizeof(*dp_tlv));
+
+ dp_tlv->dp_id = htonll(dp_id);
+ memcpy(dp_tlv->name, br_list.names[i], strlen(br_list.names[i])+1);
+ }
+
+ /* Put end marker. */
+ tlv = ofpbuf_put_zeros(buffer, sizeof(*tlv));
+ tlv->type = htons(OFMPTSR_END);
+ tlv->len = htons(sizeof(*tlv));
+ send_openflow_buffer(buffer);
+}
+
+static void
+send_config_update(uint32_t xid, bool use_xid)
+{
+ struct ofpbuf *buffer;
+ struct ofmp_config_update *ofmpcu;
+
+ if (use_xid) {
+ ofmpcu = make_ofmp_xid(sizeof *ofmpcu, OFMPT_CONFIG_UPDATE,
+ xid, &buffer);
+ } else {
+ ofmpcu = make_ofmp(sizeof *ofmpcu, OFMPT_CONFIG_UPDATE, &buffer);
+ }
+
+ ofmpcu->format = htonl(OFMPCOF_SIMPLE);
+ memcpy(ofmpcu->cookie, cfg_cookie, sizeof(ofmpcu->cookie));
+ cfg_buf_put(buffer);
+ send_openflow_buffer(buffer);
+}
+
+static void
+send_config_update_ack(uint32_t xid, bool success)
+{
+ struct ofpbuf *buffer;
+ struct ofmp_config_update_ack *ofmpcua;
+
+ ofmpcua = make_ofmp_xid(sizeof *ofmpcua, OFMPT_CONFIG_UPDATE_ACK,
+ xid, &buffer);
+
+ ofmpcua->format = htonl(OFMPCOF_SIMPLE);
+ if (success) {
+ ofmpcua->flags = htonl(OFMPCUAF_SUCCESS);
+ }
+ cfg_get_cookie(ofmpcua->cookie);
+ 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)
+{
+ struct ofpbuf *buffer;
+ struct ofp_error_msg *oem;
+
+ oem = make_openflow_xid(sizeof(*oem)+len, OFPT_ERROR, xid, &buffer);
+ oem->type = htons(type);
+ oem->code = htons(code);
+ memcpy(oem->data, data, len);
+ send_openflow_buffer(buffer);
+}
+
+static int
+recv_echo_request(uint32_t xid UNUSED, const void *msg)
+{
+ const struct ofp_header *rq = msg;
+ send_openflow_buffer(make_echo_reply(rq));
+ return 0;
+}
+
+static int
+recv_features_request(uint32_t xid, const void *msg UNUSED)
+{
+ send_features_reply(xid);
+ return 0;
+}
+
+static int
+recv_set_config(uint32_t xid UNUSED, const void *msg UNUSED)
+{
+ /* Nothing to configure! */
+ return 0;
+}
+
+static int
+recv_ofmp_capability_request(uint32_t xid, const struct ofmp_header *ofmph)
+{
+ struct ofmp_capability_request *ofmpcr;
+
+ if (htons(ofmph->header.header.length) != sizeof(*ofmpcr)) {
+ /* xxx Send error */
+ return -EINVAL;
+ }
+
+ ofmpcr = (struct ofmp_capability_request *)ofmph;
+ if (ofmpcr->format != htonl(OFMPCAF_SIMPLE)) {
+ /* xxx Send error */
+ return -EINVAL;
+ }
+
+ send_capability_reply(xid);
+
+ return 0;
+}
+
+static int
+recv_ofmp_resources_request(uint32_t xid, const void *msg UNUSED)
+{
+ send_resources_update(xid, true);
+ return 0;
+}
+
+static int
+recv_ofmp_config_request(uint32_t xid, const struct ofmp_header *ofmph)
+{
+ struct ofmp_config_request *ofmpcr;
+
+ if (htons(ofmph->header.header.length) != sizeof(*ofmpcr)) {
+ /* xxx Send error */
+ return -EINVAL;
+ }
+
+ ofmpcr = (struct ofmp_config_request *)ofmph;
+ if (ofmpcr->format != htonl(OFMPCOF_SIMPLE)) {
+ /* xxx Send error */
+ return -EINVAL;
+ }
+
+ send_config_update(xid, true);
+
+ return 0;
+}
+
+static int
+recv_ofmp_config_update(uint32_t xid, const struct ofmp_header *ofmph)
+{
+ struct ofmp_config_update *ofmpcu;
+ int data_len;
+
+ data_len = htons(ofmph->header.header.length) - sizeof(*ofmpcu);
+ if (data_len <= sizeof(*ofmpcu)) {
+ /* xxx Send error. */
+ return -EINVAL;
+ }
+
+ ofmpcu = (struct ofmp_config_update *)ofmph;
+ if (ofmpcu->format != htonl(OFMPCOF_SIMPLE)) {
+ /* xxx Send error */
+ return -EINVAL;
+ }
+
+ /* Check if the supplied cookie matches our current understanding of
+ * it. If they don't match, tell the controller and let it sort
+ * things out. */
+ if (cfg_lock(ofmpcu->cookie)) {
+ /* xxx cfg_lock can fail for other reasons, such as being
+ * xxx locked... */
+ /* xxx This error message should probably send diff info. */
+ VLOG_WARN_RL(&rl, "config update failed due to bad cookie\n");
+ //send_config_update_ack(xid, false);
+ send_ofmp_error_msg(xid, OFMPET_BAD_CONFIG, OFMPBCC_OLD_COOKIE,
+ ofmph, htons(ofmph->header.header.length));
+ return 0;
+ }
+
+ /* xxx We should probably do more sanity checking than this. */
+
+ cfg_write_data(ofmpcu->data, data_len);
+ cfg_unlock();
+
+ /* Send the ACK before running reconfigure, since our management
+ * connection settings may have changed. */
+ send_config_update_ack(xid, true);
+
+ reconfigure();
+
+
+ return 0;
+}
+
+static
+int recv_ofmp(uint32_t xid, struct ofmp_header *ofmph)
+{
+ /* xxx Should sanity-check for min/max length */
+ switch (ntohs(ofmph->type))
+ {
+ case OFMPT_CAPABILITY_REQUEST:
+ return recv_ofmp_capability_request(xid, ofmph);
+ case OFMPT_RESOURCES_REQUEST:
+ return recv_ofmp_resources_request(xid, ofmph);
+ case OFMPT_CONFIG_REQUEST:
+ return recv_ofmp_config_request(xid, ofmph);
+ case OFMPT_CONFIG_UPDATE:
+ return recv_ofmp_config_update(xid, ofmph);
+ default:
+ VLOG_WARN_RL(&rl, "unknown mgmt message: %d",
+ ntohs(ofmph->type));
+ return -EINVAL;
+ }
+}
+
+static int
+recv_nx_msg(uint32_t xid, const void *oh)
+{
+ const struct nicira_header *nh = oh;
+
+ switch (ntohl(nh->subtype)) {
+
+ case NXT_MGMT:
+ return recv_ofmp(xid, (struct ofmp_header *)oh);
+
+ default:
+ send_error_msg(xid, OFPET_BAD_REQUEST, OFPBRC_BAD_SUBTYPE,
+ oh, htons(nh->header.length));
+ return -EINVAL;
+ }
+}
+
+static int
+recv_vendor(uint32_t xid, const void *oh)
+{
+ const struct ofp_vendor_header *ovh = oh;
+
+ switch (ntohl(ovh->vendor))
+ {
+ case NX_VENDOR_ID:
+ return recv_nx_msg(xid, oh);
+
+ default:
+ VLOG_WARN_RL(&rl, "unknown vendor: 0x%x", ntohl(ovh->vendor));
+ send_error_msg(xid, OFPET_BAD_REQUEST, OFPBRC_BAD_VENDOR,
+ oh, ntohs(ovh->header.length));
+ return -EINVAL;
+ }
+}
+
+static int
+handle_msg(uint32_t xid, const void *msg, size_t length)
+{
+ int (*handler)(uint32_t, const void *);
+ struct ofp_header *oh;
+ size_t min_size;
+
+ /* Check encapsulated length. */
+ oh = (struct ofp_header *) msg;
+ if (ntohs(oh->length) > length) {
+ return -EINVAL;
+ }
+ assert(oh->version == OFP_VERSION);
+
+ /* Figure out how to handle it. */
+ switch (oh->type) {
+ case OFPT_ECHO_REQUEST:
+ min_size = sizeof(struct ofp_header);
+ handler = recv_echo_request;
+ break;
+ case OFPT_ECHO_REPLY:
+ return 0;
+ case OFPT_FEATURES_REQUEST:
+ min_size = sizeof(struct ofp_header);
+ handler = recv_features_request;
+ break;
+ case OFPT_SET_CONFIG:
+ min_size = sizeof(struct ofp_switch_config);
+ handler = recv_set_config;
+ break;
+ case OFPT_VENDOR:
+ min_size = sizeof(struct ofp_vendor_header);
+ handler = recv_vendor;
+ break;
+ default:
+ VLOG_WARN_RL(&rl, "unknown openflow type: %d", oh->type);
+ send_error_msg(xid, OFPET_BAD_REQUEST, OFPBRC_BAD_TYPE,
+ msg, length);
+ return -EINVAL;
+ }
+
+ /* Handle it. */
+ if (length < min_size) {
+ return -EFAULT;
+ }
+ return handler(xid, msg);
+}
+
+void
+mgmt_run(void)
+{
+ int i;
+
+ if (!mgmt_rconn) {
+ return;
+ }
+
+ rconn_run(mgmt_rconn);
+
+ /* Do some processing, but cap it at a reasonable amount so that
+ * other processing doesn't starve. */
+ for (i=0; i<50; i++) {
+ struct ofpbuf *buffer;
+ struct ofp_header *oh;
+
+ buffer = rconn_recv(mgmt_rconn);
+ if (!buffer) {
+ break;
+ }
+
+ if (buffer->size >= sizeof *oh) {
+ oh = buffer->data;
+ handle_msg(oh->xid, buffer->data, buffer->size);
+ ofpbuf_delete(buffer);
+ } else {
+ VLOG_WARN_RL(&rl, "received too-short OpenFlow message");
+ }
+ }
+}
+
+void
+mgmt_wait(void)
+{
+ if (!mgmt_rconn) {
+ return;
+ }
+
+ rconn_run_wait(mgmt_rconn);
+ rconn_recv_wait(mgmt_rconn);
+}
+
+static uint64_t
+pick_fallback_mgmt_id(void)
+{
+ uint8_t ea[ETH_ADDR_LEN];
+ eth_addr_random(ea);
+ ea[0] = 0x00; /* Set Nicira OUI. */
+ ea[1] = 0x23;
+ ea[2] = 0x20;
+ return eth_addr_to_uint64(ea);
+}
--- /dev/null
+/* 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.
+ *
+ * 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.
+ */
+
+#ifndef VSWITCHD_MGMT_H
+#define VSWITCHD_MGMT_H 1
+
+void mgmt_init(void);
+void mgmt_reconfigure(void);
+void mgmt_run(void);
+void mgmt_wait(void);
+uint64_t mgmt_get_mgmt_id(void);
+
+#endif /* mgmt.h */
.ds PN vswitchd
.
-.TH vswitchd 8 "December 2008" "OpenFlow" "OpenFlow Manual"
+.TH vswitchd 8 "March 2009" "OpenFlow" "OpenFlow Manual"
.
.SH NAME
vswitchd \- OpenFlow-based virtual switch daemon
.
.SH SYNOPSIS
.B vswitchd
-\fB--config=\fIfile\fR|\fIdir\fR\&...
-[\fIoption\fR]\&...
+[\fI--brcompat\fR]\&
+\fIconfig\fR
.
.SH DESCRIPTION
A daemon that manages and controls any number of OpenFlow-based
virtual switches on the local machine.
.PP
+The mandatory \fIconfig\fR argument specifies a configuration file.
+For a description of \fBvswitchd\fR configuration syntax, see
+\fBvswitchd.conf\fR(5).
+.PP
At startup or upon receipt of a \fBSIGHUP\fR signal, \fBvswitchd\fR
-reads the configuration files or directories specified on the command
-line. It sets up OpenFlow datapaths and then operates switching
-across each bridge described in its configuration files. If a logfile
-was specified on the command line it will also be opened or reopened.
+reads the configuration file. It sets up OpenFlow datapaths and then
+operates switching across each bridge described in its configuration
+files. If a logfile was specified on the command line it will also
+be opened or reopened.
.PP
\fBvswitchd\fR virtual switches may be configured with any of the
following features:
the OpenFlow kernel module.
.PP
.SH OPTIONS
-.
-At least one of the following options is required:
-.
-.TP
-\fB-F \fIfile\fR|\fIdir\fR, \fB--config=\fIfile\fR|\fIdir\fR
-.
-The \fB-F\fR or \fB--config\fR option specifies a configuration file
-or directory. If a regular file is named, then \fBvswitchd\fR reads
-that file. If a directory is named, then \fBvswitchd\fR reads all the
-files in that directory whose names consist entirely of English
-letters, digits, and the special characters \fB._-\fR and do not begin
-with \fB.\fR.
-.IP
-For a description of \fBvswitchd\fR configuration syntax, see
-\fBvswitchd.conf\fR(5).
-.
-.PP
-The rest of \fBvswitchd\fR's options are truly optional:
.TP
-\fB-b \fIfile\fR, \fB--brcompat=\fIfile\fR
+\fB--brcompat\fR
.
-The \fB-b\fR or \fB--brcompat\fR option puts \fBvswitchd\fR into bridge
-compatibility mode. This means it listens for bridge ioctl commands
+The \fB--brcompat\fR option puts \fBvswitchd\fR into bridge compatibility
+mode. This means it listens for bridge ioctl commands
(e.g., those generated by the \fBbrctl\fR program) to add or remove
-datapaths and the interfaces that attach to them. The \fIfile\fR
-argument should be the master list for Bridge Configuration and be one
-of the files included in the \fB--config\fR option.
+datapaths and the interfaces that attach to them.
.
.IP ""
This option requires the \fBbrcompat_mod.ko\fR kernel module to be
#include "daemon.h"
#include "fault.h"
#include "leak-checker.h"
+#include "mgmt.h"
#include "poll-loop.h"
#include "process.h"
#include "signals.h"
#include "vconn-ssl.h"
#include "vconn.h"
#include "vlog-socket.h"
+#include "vswitchd.h"
#include "vlog.h"
#define THIS_MODULE VLM_vswitchd
static void parse_options(int argc, char *argv[]);
static void usage(void) NO_RETURN;
-static void reconfigure(void);
-
-static bool brc_enabled = false;
-struct svec config_files;
+bool brc_enabled = false;
+char *config_file;
int
main(int argc, char *argv[])
register_fault_handlers();
time_init();
vlog_init();
- svec_init(&config_files);
parse_options(argc, argv);
signal(SIGPIPE, SIG_IGN);
sighup = signal_register(SIGHUP);
}
cfg_read();
+ mgmt_init();
bridge_init();
+ mgmt_reconfigure();
need_reconfigure = false;
for (;;) {
vlog_reopen_log_file();
reconfigure();
}
+ mgmt_run();
if (bridge_run()) {
need_reconfigure = true;
}
}
signal_wait(sighup);
+ mgmt_wait();
bridge_wait();
poll_block();
}
return 0;
}
-static void
+void
reconfigure(void)
{
cfg_read();
bridge_reconfigure();
+ mgmt_reconfigure();
}
static void
parse_options(int argc, char *argv[])
{
enum {
+ OPT_BRCOMPAT = UCHAR_MAX + 1,
OPT_PEER_CA_CERT,
VLOG_OPTION_ENUMS,
LEAK_CHECKER_OPTION_ENUMS
};
static struct option long_options[] = {
- {"brcompat", required_argument, 0, 'b'},
- {"config", required_argument, 0, 'F'},
+ {"brcompat", no_argument, 0, OPT_BRCOMPAT},
{"help", no_argument, 0, 'h'},
{"version", no_argument, 0, 'V'},
DAEMON_LONG_OPTIONS,
{0, 0, 0, 0},
};
char *short_options = long_options_to_short_options(long_options);
- bool configured = false;
+ int error;
for (;;) {
- int error;
int c;
c = getopt_long(argc, argv, short_options, long_options, NULL);
}
switch (c) {
- case 'b':
+ case OPT_BRCOMPAT:
brc_enabled = true;
- brc_init(optarg);
- break;
-
- case 'F':
- configured = true;
- svec_add(&config_files, optarg);
- error = cfg_add_file(optarg);
- if (error) {
- ofp_fatal(error, "failed to add configuration file or "
- "directory \"%s\"", optarg);
- }
+ brc_init();
break;
case 'H':
}
free(short_options);
- if (!configured) {
- ofp_fatal(0, "at least one -F or --config option is required");
+ argc -= optind;
+ argv += optind;
+
+ if (argc != 1) {
+ ofp_fatal(0, "config file is only non-option argument; "
+ "use --help for usage");
}
- if (optind < argc) {
- ofp_fatal(0, "non-option arguments not accepted; use --help for help");
+
+ config_file = argv[0];
+ error = cfg_set_file(config_file);
+ if (error) {
+ ofp_fatal(error, "failed to add configuration file \"%s\"",
+ optarg);
}
}
usage(void)
{
printf("%s: virtual switch daemon\n"
- "usage: %s [OPTIONS]\n",
+ "usage: %s [OPTIONS] CONFIG\n"
+ "CONFIG is a configuration file in vswitchd.conf(5) format.\n",
program_name, program_name);
- printf("\nConfiguration options (must specify at least one):\n"
- " -F, --config=FILE|DIR reads configuration from FILE or DIR\n"
- "\nCompatibility options:\n"
- " -b, --brcompat=FILE run with bridge compatibility hooks\n");
+ printf("\nCompatibility options:\n"
+ " --brcompat run with bridge compatibility hooks\n");
daemon_usage();
vlog_usage();
printf("\nOther options:\n"
vswitchd.conf \- configuration file for \fBvswitchd\fR
.
.SH DESCRIPTION
-\fBvswitchd\fR(8), the virtual switch daemon, is configured using one
-or more configuration files named on the command line. This manual
-page describes the syntax of these configuration files.
+This manual page describes the syntax for the configuration file used
+by \fBvswitchd\fR(8), the virtual switch daemon.
.PP
The configuration file is based on key-value pairs, which are given
one per line in the form \fIkey\fB=\fIvalue\fR. Each \fIkey\fR
--- /dev/null
+/* 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.
+ *
+ * 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.
+ */
+
+#ifndef VSWITCHD_H
+#define VSWITCHD_H 1
+
+void reconfigure(void);
+
+#endif /* vswitchd.h */