#include <config.h>
#include "svec.h"
#include <assert.h>
+#include <ctype.h>
#include <stdlib.h>
#include <string.h>
+#include "dynamic-string.h"
#include "util.h"
+#define THIS_MODULE VLM_svec
+#include "vlog.h"
+
void
svec_init(struct svec *svec)
{
svec->names[svec->n++] = name;
}
+void
+svec_append(struct svec *svec, const struct svec *other)
+{
+ size_t i;
+ for (i = 0; i < other->n; i++) {
+ svec_add(svec, other->names[i]);
+ }
+}
+
void
svec_terminate(struct svec *svec)
{
printf("\"%s\"\n", svec->names[i]);
}
}
+
+/* Breaks 'words' into words at white space, respecting shell-like quoting
+ * conventions, and appends the words to 'svec'. */
+void
+svec_parse_words(struct svec *svec, const char *words)
+{
+ struct ds word = DS_EMPTY_INITIALIZER;
+ const char *p, *q;
+
+ for (p = words; *p != '\0'; p = q) {
+ int quote = 0;
+
+ while (isspace((unsigned char) *p)) {
+ p++;
+ }
+ if (*p == '\0') {
+ break;
+ }
+
+ ds_clear(&word);
+ for (q = p; *q != '\0'; q++) {
+ if (*q == quote) {
+ quote = 0;
+ } else if (*q == '\'' || *q == '"') {
+ quote = *q;
+ } else if (*q == '\\' && (!quote || quote == '"')) {
+ q++;
+ if (*q == '\0') {
+ VLOG_WARN("%s: ends in trailing backslash", words);
+ break;
+ }
+ ds_put_char(&word, *q);
+ } else if (isspace((unsigned char) *q) && !quote) {
+ q++;
+ break;
+ } else {
+ ds_put_char(&word, *q);
+ }
+ }
+ svec_add(svec, ds_cstr(&word));
+ if (quote) {
+ VLOG_WARN("%s: word ends inside quoted string", words);
+ }
+ }
+ ds_destroy(&word);
+}
void svec_clear(struct svec *);
void svec_add(struct svec *, const char *);
void svec_add_nocopy(struct svec *, char *);
-void svec_merge(struct svec *, const struct svec *);
+void svec_append(struct svec *, const struct svec *);
void svec_terminate(struct svec *);
void svec_sort(struct svec *);
void svec_unique(struct svec *);
bool svec_is_sorted(const struct svec *);
void svec_swap(struct svec *a, struct svec *b);
void svec_print(const struct svec *svec, const char *title);
+void svec_parse_words(struct svec *svec, const char *words);
#endif /* svec.h */
bool sent_config_request; /* Successfully sent config request? */
bool sent_features_request; /* Successfully sent features request? */
+ /* Support for remote controllers. */
+ char *controller; /* NULL if there is no remote controller;
+ * "discover" to do controller discovery;
+ * otherwise a vconn name. */
+ struct svec secchan_opts; /* Additional options for secchan. */
+
/* Secure channel. */
enum {
SC_UNSTARTED, /* Not yet started. */
struct bridge *br;
LIST_FOR_EACH (br, struct bridge, node, &all_bridges) {
+ if (br->secchan) {
+ process_wait(br->secchan);
+ }
+ if (br->controller) {
+ continue;
+ }
+
rconn_run_wait(br->rconn);
if (!bridge_is_backlogged(br)) {
rconn_recv_wait(br->rconn);
}
- if (br->secchan) {
- process_wait(br->secchan);
- }
if (br->ml) {
mac_learning_wait(br->ml);
}
br->sent_config_request = false;
br->sent_features_request = false;
+ svec_init(&br->secchan_opts);
+
br->sc_state = SC_UNSTARTED;
br->sc_retries = 0;
br->secchan = NULL;
free(status);
}
+static void
+kill_secchan(struct bridge *br)
+{
+ if (br->sc_state == SC_RUNNING) {
+ process_kill(br->secchan, SIGTERM);
+ br->sc_state = SC_DYING;
+ }
+}
+
static void
run_secchan(struct bridge *br)
{
} else if (!rconn_is_alive(br->rconn)) {
VLOG_ERR("%s: connection to secchan closed unexpectedly, "
"killing secchan", br->name);
- process_kill(br->secchan, SIGTERM);
- br->sc_state = SC_DYING;
+ kill_secchan(br);
}
break;
/* Assemble command-line arguments. */
svec_init(&argv);
svec_add(&argv, "secchan");
- svec_add(&argv, "--out-of-band");
+ if (!br->controller) {
+ svec_add(&argv, "--out-of-band");
+ svec_add(&argv, "--max-backoff=1");
+ }
svec_add(&argv, "--fail=closed");
- svec_add(&argv, "--max-backoff=1");
svec_add(&argv, "--no-stp");
if (!stat(ofp_rundir, &s)) {
svec_add_nocopy(&argv,
"%d{%b %d %H:%M:%S}",
br->dp_idx,
"%c|%p|%m"));
+ svec_append(&argv, &br->secchan_opts);
svec_add_nocopy(&argv, xasprintf("nl:%d", br->dp_idx));
- svec_add_nocopy(&argv, xasprintf("fd:%d", sockets[1]));
+ if (br->controller) {
+ if (strcmp(br->controller, "discover")) {
+ svec_add(&argv, br->controller);
+ }
+ } else {
+ svec_add_nocopy(&argv, xasprintf("fd:%d", sockets[1]));
+ }
svec_terminate(&argv);
/* Start secchan. */
for (i = 0; i < br->n_ports; i++) {
port_destroy(br->ports[i]);
}
+ free(br->controller);
+ svec_destroy(&br->secchan_opts);
ft_destroy(br->ft);
stats_request_destroy(br->flow_rq);
stats_mgr_destroy(br->stats_mgr);
{
int iteration;
+ if (br->controller) {
+ run_secchan(br);
+ return;
+ }
+
rconn_run(br->rconn);
if (rconn_is_connected(br->rconn)) {
bridge_reconfigure_one(struct bridge *br)
{
struct svec old_ports, new_ports;
+ const char *controller;
+ char *ctl;
size_t i;
/* Collect old and new ports. */
port_reconfigure(br->ports[i]);
}
+ /* Configure remote controller. */
+ controller = cfg_get_string(0, "bridge.%s.controller", br->name);
+ ctl = controller ? xstrdup(controller) : NULL;
+ if ((ctl == NULL) != (br->controller == NULL)
+ || (ctl && br->controller && strcmp(ctl, br->controller))) {
+ br->sc_retries = 0;
+ kill_secchan(br);
+ }
+ free(br->controller);
+ br->controller = ctl;
+
+ /* Allow arbitrary secchan options if a remote controller is configured. */
+ svec_clear(&br->secchan_opts);
+ if (ctl) {
+ for (i = 0; ; i++) {
+ const char *opt;
+ opt = cfg_get_string(i, "bridge.%s.secchan.options", br->name);
+ if (!opt) {
+ break;
+ }
+ svec_parse_words(&br->secchan_opts, opt);
+ }
+ }
+
/* Revive secchan if it's dead. */
if (br->sc_state == SC_DEAD) {
br->sc_retries = 0;
.fi
.RE
+.SS "OpenFlow controller connectivity"
+By default, \fBvswitchd\fR performs all configured bridging and
+switching locally. It can also be configured to connect a given
+bridge to an external OpenFlow controller, such as NOX, by setting
+\fBbridge.\fIname\fB.controller\fR to one of the following forms:
+.
+.TP
+\fBdiscover\fR
+Use controller discovery to find the local OpenFlow controller.
+Refer to \fBsecchan\fR(8) for information on how to configure a DHCP
+server to support controller discovery.
+.
+.TP
+\fBssl:\fIhost\fR[\fB:\fIport\fR]
+The specified SSL \fIport\fR (default: 6633) on the given remote
+\fIhost\fR. The \fB--private-key\fR, \fB--certificate\fR, and
+\fB--ca-cert\fR options are mandatory when this form is used.
+.
+.TP
+\fBtcp:\fIhost\fR[\fB:\fIport\fR]
+The specified TCP \fIport\fR (default: 6633) on the given remote
+\fIhost\fR.
+.
+.TP
+\fBunix:\fIfile\fR
+The Unix domain server socket named \fIfile\fR.
+.PP
+When an external controller is configured,
+\fBbridge.\fIname\fB.secchan.options\fR may be used to pass arbitrary
+additional options to \fBsecchan\fR. Each value for this option is
+broken into words at white space, which are then passed as individual
+arguments on \fBsecchan\fR's command line. Single or double quotes
+may be used to prevent word-splitting; outside of single quotes, a
+backslash quotes the following single character.
+.PP
+If an external controller is configured, and in-band control is used,
+but not controller discovery (see \fBsecchan\fR(8) for more
+information on this terminology), the local bridge port must
+configured with an IP address by some entity outside \fBvswitch\fR,
+which will not itself configure an IP address on it.
+.PP
.SH "SEE ALSO"
.BR vswitchd (8).