vswitchd: Add support for remote controller.
authorBen Pfaff <blp@nicira.com>
Wed, 31 Dec 2008 00:02:17 +0000 (16:02 -0800)
committerBen Pfaff <blp@nicira.com>
Wed, 31 Dec 2008 00:02:17 +0000 (16:02 -0800)
lib/svec.c
lib/svec.h
lib/vlog-modules.def
vswitchd/bridge.c
vswitchd/vswitchd.conf.5

index 59d806cf37a6e542217c026e3ef494ece280d64e..3a6f53deb6c68b3e21194394a7dc7edac2d23e39 100644 (file)
 #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)
 {
@@ -86,6 +91,15 @@ svec_add_nocopy(struct svec *svec, char *name)
     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)
 {
@@ -216,3 +230,49 @@ svec_print(const struct svec *svec, const char *title)
         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);
+}
index ac99a8376b15b4be7ee40a49feb332d8e4938741..2215c3cc539af3f008c98f16e9ce946c48e0920a 100644 (file)
@@ -50,7 +50,7 @@ void svec_destroy(struct svec *);
 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 *);
@@ -60,5 +60,6 @@ bool svec_contains(const struct svec *, const char *);
 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 */
index 2a787184654f7a05a9d25c336a4bfd4f9ab6867b..45287585a82169aa0a440afe806a1738af6b5745 100644 (file)
@@ -32,6 +32,7 @@ VLOG_MODULE(stp)
 VLOG_MODULE(stp_secchan)
 VLOG_MODULE(stats)
 VLOG_MODULE(status)
+VLOG_MODULE(svec)
 VLOG_MODULE(switch)
 VLOG_MODULE(terminal)
 VLOG_MODULE(socket_util)
index fc733536829605da276c2a37ddad29e9e8ebc76a..fa76d3e0e58ae9e726c1a454d6b56c728caac5f2 100644 (file)
@@ -119,6 +119,12 @@ struct bridge {
     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. */
@@ -341,13 +347,17 @@ bridge_wait(void)
     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);
         }
@@ -392,6 +402,8 @@ bridge_create(const char *name)
     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;
@@ -437,6 +449,15 @@ log_secchan_died(enum vlog_level level, struct bridge *br, bool expected)
     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)
 {
@@ -452,8 +473,7 @@ 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;
 
@@ -511,9 +531,11 @@ start_secchan(struct bridge *br)
     /* 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,
@@ -525,8 +547,15 @@ start_secchan(struct bridge *br)
                               "%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. */
@@ -577,6 +606,8 @@ bridge_destroy(struct bridge *br)
         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);
@@ -651,6 +682,11 @@ bridge_run_one(struct bridge *br)
 {
     int iteration;
 
+    if (br->controller) {
+        run_secchan(br);
+        return;
+    }
+
     rconn_run(br->rconn);
 
     if (rconn_is_connected(br->rconn)) {
@@ -702,6 +738,8 @@ static void
 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. */
@@ -735,6 +773,30 @@ bridge_reconfigure_one(struct bridge *br)
         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;
index f3f4fb1840037cb623f68abf8267c9b0fc2d486d..b63451a6c8f8a075ffed27a1b884ac72e3d1b3d5 100644 (file)
@@ -135,6 +135,47 @@ with physical network devices \fBeth2\fR and \fBeth3\fR:
         
 .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).