summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--ofproto/fail-open.c162
-rw-r--r--ofproto/fail-open.h12
-rw-r--r--ofproto/ofproto.c544
-rw-r--r--ofproto/ofproto.h7
-rw-r--r--ofproto/status.c9
-rw-r--r--tests/ovs-vsctl.at81
-rw-r--r--utilities/ovs-openflowd.8.in24
-rw-r--r--utilities/ovs-openflowd.c110
-rw-r--r--utilities/ovs-vsctl.8.in20
-rw-r--r--utilities/ovs-vsctl.c217
-rw-r--r--vswitchd/bridge.c204
-rw-r--r--vswitchd/ovs-brcompatd.c4
-rw-r--r--vswitchd/vswitch.ovsschema4
-rw-r--r--vswitchd/vswitch.xml146
14 files changed, 1037 insertions, 507 deletions
diff --git a/ofproto/fail-open.c b/ofproto/fail-open.c
index 6a540ef8b..a79c5b226 100644
--- a/ofproto/fail-open.c
+++ b/ofproto/fail-open.c
@@ -68,8 +68,8 @@
struct fail_open {
struct ofproto *ofproto;
- struct rconn *controller;
- int trigger_duration;
+ struct rconn **controllers;
+ size_t n_controllers;
int last_disconn_secs;
struct status_category *ss_cat;
long long int next_bogus_packet_in;
@@ -78,11 +78,58 @@ struct fail_open {
static void fail_open_recover(struct fail_open *);
-/* Returns true if 'fo' should be in fail-open mode, otherwise false. */
-static inline bool
-should_fail_open(const struct fail_open *fo)
+/* Returns the number of seconds of disconnection after which fail-open mode
+ * should activate. */
+static int
+trigger_duration(const struct fail_open *fo)
{
- return rconn_failure_duration(fo->controller) >= fo->trigger_duration;
+ if (!fo->n_controllers) {
+ /* Shouldn't ever arrive here, but if we do, never fail open. */
+ return INT_MAX;
+ } else {
+ /* Otherwise, every controller must have a chance to send an
+ * inactivity probe and reconnect before we fail open, so take the
+ * maximum probe interval and multiply by 3:
+ *
+ * - The first interval is the idle time before sending an inactivity
+ * probe.
+ *
+ * - The second interval is the time allowed for a response to the
+ * inactivity probe.
+ *
+ * - The third interval is the time allowed to reconnect after no
+ * response is received.
+ */
+ int max_probe_interval;
+ size_t i;
+
+ max_probe_interval = 0;
+ for (i = 0; i < fo->n_controllers; i++) {
+ int probe_interval = rconn_get_probe_interval(fo->controllers[i]);
+ max_probe_interval = MAX(max_probe_interval, probe_interval);
+ }
+ return max_probe_interval * 3;
+ }
+}
+
+/* Returns the number of seconds for which all controllers have been
+ * disconnected. */
+static int
+failure_duration(const struct fail_open *fo)
+{
+ int min_failure_duration;
+ size_t i;
+
+ if (!fo->n_controllers) {
+ return 0;
+ }
+
+ min_failure_duration = INT_MAX;
+ for (i = 0; i < fo->n_controllers; i++) {
+ int failure_duration = rconn_failure_duration(fo->controllers[i]);
+ min_failure_duration = MIN(min_failure_duration, failure_duration);
+ }
+ return min_failure_duration;
}
/* Returns true if 'fo' is currently in fail-open mode, otherwise false. */
@@ -92,8 +139,39 @@ fail_open_is_active(const struct fail_open *fo)
return fo->last_disconn_secs != 0;
}
+/* Returns true if at least one controller is connected (regardless of whether
+ * those controllers are believed to have authenticated and accepted this
+ * switch), false if none of them are connected. */
+static bool
+any_controller_is_connected(const struct fail_open *fo)
+{
+ size_t i;
+
+ for (i = 0; i < fo->n_controllers; i++) {
+ if (rconn_is_connected(fo->controllers[i])) {
+ return true;
+ }
+ }
+ return false;
+}
+
+/* Returns true if at least one controller is believed to have authenticated
+ * and accepted this switch, false otherwise. */
+static bool
+any_controller_is_admitted(const struct fail_open *fo)
+{
+ size_t i;
+
+ for (i = 0; i < fo->n_controllers; i++) {
+ if (rconn_is_admitted(fo->controllers[i])) {
+ return true;
+ }
+ }
+ return false;
+}
+
static void
-send_bogus_packet_in(struct fail_open *fo)
+send_bogus_packet_in(struct fail_open *fo, struct rconn *rconn)
{
uint8_t mac[ETH_ADDR_LEN];
struct ofpbuf *opi;
@@ -107,17 +185,29 @@ send_bogus_packet_in(struct fail_open *fo)
ofpbuf_uninit(&b);
/* Send. */
- rconn_send_with_limit(fo->controller, opi, fo->bogus_packet_counter, 1);
+ rconn_send_with_limit(rconn, opi, fo->bogus_packet_counter, 1);
+}
+
+static void
+send_bogus_packet_ins(struct fail_open *fo)
+{
+ size_t i;
+
+ for (i = 0; i < fo->n_controllers; i++) {
+ if (rconn_is_connected(fo->controllers[i])) {
+ send_bogus_packet_in(fo, fo->controllers[i]);
+ }
+ }
}
-/* Enter fail-open mode if we should be in it. Handle reconnecting to a
- * controller from fail-open mode. */
+/* Enter fail-open mode if we should be in it. */
void
fail_open_run(struct fail_open *fo)
{
+ int disconn_secs = failure_duration(fo);
+
/* Enter fail-open mode if 'fo' is not in it but should be. */
- if (should_fail_open(fo)) {
- int disconn_secs = rconn_failure_duration(fo->controller);
+ if (disconn_secs >= trigger_duration(fo)) {
if (!fail_open_is_active(fo)) {
VLOG_WARN("Could not connect to controller (or switch failed "
"controller's post-connection admission control "
@@ -137,10 +227,10 @@ fail_open_run(struct fail_open *fo)
/* Schedule a bogus packet-in if we're connected and in fail-open. */
if (fail_open_is_active(fo)) {
- if (rconn_is_connected(fo->controller)) {
+ if (any_controller_is_connected(fo)) {
bool expired = time_msec() >= fo->next_bogus_packet_in;
if (expired) {
- send_bogus_packet_in(fo);
+ send_bogus_packet_ins(fo);
}
if (expired || fo->next_bogus_packet_in == LLONG_MAX) {
fo->next_bogus_packet_in = time_msec() + 2000;
@@ -157,7 +247,7 @@ fail_open_run(struct fail_open *fo)
void
fail_open_maybe_recover(struct fail_open *fo)
{
- if (rconn_is_admitted(fo->controller)) {
+ if (any_controller_is_admitted(fo)) {
fail_open_recover(fo);
}
}
@@ -188,8 +278,8 @@ fail_open_wait(struct fail_open *fo)
void
fail_open_flushed(struct fail_open *fo)
{
- int disconn_secs = rconn_failure_duration(fo->controller);
- bool open = disconn_secs >= fo->trigger_duration;
+ int disconn_secs = failure_duration(fo);
+ bool open = disconn_secs >= trigger_duration(fo);
if (open) {
union ofp_action action;
flow_t flow;
@@ -210,23 +300,28 @@ static void
fail_open_status_cb(struct status_reply *sr, void *fo_)
{
struct fail_open *fo = fo_;
- int cur_duration = rconn_failure_duration(fo->controller);
+ int cur_duration = failure_duration(fo);
+ int trigger = trigger_duration(fo);
- status_reply_put(sr, "trigger-duration=%d", fo->trigger_duration);
+ status_reply_put(sr, "trigger-duration=%d", trigger);
status_reply_put(sr, "current-duration=%d", cur_duration);
status_reply_put(sr, "triggered=%s",
- cur_duration >= fo->trigger_duration ? "true" : "false");
+ cur_duration >= trigger ? "true" : "false");
}
+/* Creates and returns a new struct fail_open for 'ofproto', registering switch
+ * status with 'switch_status'.
+ *
+ * The caller should register its set of controllers with
+ * fail_open_set_controllers(). (There should be at least one controller,
+ * otherwise there isn't any point in having the struct fail_open around.) */
struct fail_open *
-fail_open_create(struct ofproto *ofproto,
- int trigger_duration, struct switch_status *switch_status,
- struct rconn *controller)
+fail_open_create(struct ofproto *ofproto, struct switch_status *switch_status)
{
struct fail_open *fo = xmalloc(sizeof *fo);
fo->ofproto = ofproto;
- fo->controller = controller;
- fo->trigger_duration = trigger_duration;
+ fo->controllers = NULL;
+ fo->n_controllers = 0;
fo->last_disconn_secs = 0;
fo->ss_cat = switch_status_register(switch_status, "fail-open",
fail_open_status_cb, fo);
@@ -235,18 +330,29 @@ fail_open_create(struct ofproto *ofproto,
return fo;
}
+/* Registers the 'n' rconns in 'rconns' as connections to the controller for
+ * 'fo'. The caller must ensure that all of the rconns remain valid until 'fo'
+ * is destroyed or a new set is registered in a subsequent call.
+ *
+ * Takes ownership of the 'rconns' array, but not of the rconns that it points
+ * to (of which the caller retains ownership). */
void
-fail_open_set_trigger_duration(struct fail_open *fo, int trigger_duration)
+fail_open_set_controllers(struct fail_open *fo,
+ struct rconn **rconns, size_t n)
{
- fo->trigger_duration = trigger_duration;
+ free(fo->controllers);
+ fo->controllers = rconns;
+ fo->n_controllers = n;
}
+/* Destroys 'fo'. */
void
fail_open_destroy(struct fail_open *fo)
{
if (fo) {
fail_open_recover(fo);
- /* We don't own fo->controller. */
+ free(fo->controllers);
+ /* We don't own the rconns behind fo->controllers. */
switch_status_unregister(fo->ss_cat);
rconn_packet_counter_destroy(fo->bogus_packet_counter);
free(fo);
diff --git a/ofproto/fail-open.h b/ofproto/fail-open.h
index 900d587e4..eab667aaa 100644
--- a/ofproto/fail-open.h
+++ b/ofproto/fail-open.h
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2008, 2009 Nicira Networks.
+ * Copyright (c) 2008, 2009, 2010 Nicira Networks.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -29,13 +29,11 @@ struct switch_status;
/* Priority of the rule added by the fail-open subsystem when a switch enters
* fail-open mode. This priority value uniquely identifies a fail-open flow
* (OpenFlow priorities max out at 65535 and nothing else in Open vSwitch
- * creates flows with this priority). */
-#define FAIL_OPEN_PRIORITY 70000
+ * creates flows with this priority). And "f0" is mnemonic for "fail open"! */
+#define FAIL_OPEN_PRIORITY 0xf0f0f0
-struct fail_open *fail_open_create(struct ofproto *, int trigger_duration,
- struct switch_status *,
- struct rconn *controller);
-void fail_open_set_trigger_duration(struct fail_open *, int trigger_duration);
+struct fail_open *fail_open_create(struct ofproto *, struct switch_status *);
+void fail_open_set_controllers(struct fail_open *, struct rconn **, size_t n);
void fail_open_destroy(struct fail_open *);
void fail_open_wait(struct fail_open *);
bool fail_open_is_active(const struct fail_open *);
diff --git a/ofproto/ofproto.c b/ofproto/ofproto.c
index ddd079bcc..0c11289cb 100644
--- a/ofproto/ofproto.c
+++ b/ofproto/ofproto.c
@@ -165,27 +165,67 @@ static void rule_post_uninstall(struct ofproto *, struct rule *);
static void send_flow_removed(struct ofproto *p, struct rule *rule,
long long int now, uint8_t reason);
-struct ofconn {
- struct list node;
- struct rconn *rconn;
- struct pktbuf *pktbuf;
- int miss_send_len;
-
- struct rconn_packet_counter *packet_in_counter;
+/* ofproto supports two kinds of OpenFlow connections:
+ *
+ * - "Controller connections": Connections to ordinary OpenFlow controllers.
+ * ofproto maintains persistent connections to these controllers and by
+ * default sends them asynchronous messages such as packet-ins.
+ *
+ * - "Transient connections", e.g. from ovs-ofctl. When these connections
+ * drop, it is the other side's responsibility to reconnect them if
+ * necessary. ofproto does not send them asynchronous messages by default.
+ */
+enum ofconn_type {
+ OFCONN_CONTROLLER, /* An OpenFlow controller. */
+ OFCONN_TRANSIENT /* A transient connection. */
+};
- /* Number of OpenFlow messages queued as replies to OpenFlow requests, and
- * the maximum number before we stop reading OpenFlow requests. */
+/* An OpenFlow connection. */
+struct ofconn {
+ struct ofproto *ofproto; /* The ofproto that owns this connection. */
+ struct list node; /* In struct ofproto's "all_conns" list. */
+ struct rconn *rconn; /* OpenFlow connection. */
+ enum ofconn_type type; /* Type. */
+
+ /* OFPT_PACKET_IN related data. */
+ struct rconn_packet_counter *packet_in_counter; /* # queued on 'rconn'. */
+ struct pinsched *schedulers[2]; /* Indexed by reason code; see below. */
+ struct pktbuf *pktbuf; /* OpenFlow packet buffers. */
+ int miss_send_len; /* Bytes to send of buffered packets. */
+
+ /* Number of OpenFlow messages queued on 'rconn' as replies to OpenFlow
+ * requests, and the maximum number before we stop reading OpenFlow
+ * requests. */
#define OFCONN_REPLY_MAX 100
struct rconn_packet_counter *reply_counter;
+
+ /* type == OFCONN_CONTROLLER only. */
+ struct hmap_node hmap_node; /* In struct ofproto's "controllers" map. */
+ struct discovery *discovery; /* Controller discovery object, if enabled. */
+ struct status_category *ss; /* Switch status category. */
};
-static struct ofconn *ofconn_create(struct ofproto *, struct rconn *);
+/* We use OFPR_NO_MATCH and OFPR_ACTION as indexes into struct ofconn's
+ * "schedulers" array. Their values are 0 and 1, and their meanings and values
+ * coincide with _ODPL_MISS_NR and _ODPL_ACTION_NR, so this is convenient. In
+ * case anything ever changes, check their values here. */
+#define N_SCHEDULERS 2
+BUILD_ASSERT_DECL(OFPR_NO_MATCH == 0);
+BUILD_ASSERT_DECL(OFPR_NO_MATCH == _ODPL_MISS_NR);
+BUILD_ASSERT_DECL(OFPR_ACTION == 1);
+BUILD_ASSERT_DECL(OFPR_ACTION == _ODPL_ACTION_NR);
+
+static struct ofconn *ofconn_create(struct ofproto *, struct rconn *,
+ enum ofconn_type);
static void ofconn_destroy(struct ofconn *);
static void ofconn_run(struct ofconn *, struct ofproto *);
static void ofconn_wait(struct ofconn *);
static void queue_tx(struct ofpbuf *msg, const struct ofconn *ofconn,
struct rconn_packet_counter *counter);
+static void send_packet_in(struct ofproto *, struct ofpbuf *odp_msg);
+static void do_send_packet_in(struct ofpbuf *odp_msg, void *ofconn);
+
struct ofproto {
/* Settings. */
uint64_t datapath_id; /* Datapath ID. */
@@ -206,11 +246,8 @@ struct ofproto {
/* Configuration. */
struct switch_status *switch_status;
- struct status_category *ss_cat;
struct in_band *in_band;
- struct discovery *discovery;
struct fail_open *fail_open;
- struct pinsched *miss_sched, *action_sched;
struct netflow *netflow;
struct ofproto_sflow *sflow;
@@ -222,8 +259,8 @@ struct ofproto {
bool tun_id_from_cookie;
/* OpenFlow connections. */
- struct list all_conns;
- struct ofconn *controller;
+ struct hmap controllers; /* Controller "struct ofconn"s. */
+ struct list all_conns; /* Contains "struct ofconn"s. */
struct pvconn **listeners;
size_t n_listeners;
struct pvconn **snoops;
@@ -243,8 +280,7 @@ static const struct ofhooks default_ofhooks;
static uint64_t pick_datapath_id(const struct ofproto *);
static uint64_t pick_fallback_dpid(void);
-static void send_packet_in_miss(struct ofpbuf *, void *ofproto);
-static void send_packet_in_action(struct ofpbuf *, void *ofproto);
+
static void update_used(struct ofproto *);
static void update_stats(struct ofproto *, struct rule *,
const struct odp_flow_stats *);
@@ -319,9 +355,7 @@ ofproto_create(const char *datapath, const char *datapath_type,
/* Initialize submodules. */
p->switch_status = switch_status_create(p);
p->in_band = NULL;
- p->discovery = NULL;
p->fail_open = NULL;
- p->miss_sched = p->action_sched = NULL;
p->netflow = NULL;
p->sflow = NULL;
@@ -333,9 +367,7 @@ ofproto_create(const char *datapath, const char *datapath_type,
/* Initialize OpenFlow connections. */
list_init(&p->all_conns);
- p->controller = ofconn_create(p, rconn_create(5, 8));
- p->controller->pktbuf = pktbuf_create();
- p->controller->miss_send_len = OFP_DEFAULT_MISS_SEND_LEN;
+ hmap_init(&p->controllers);
p->listeners = NULL;
p->n_listeners = 0;
p->snoops = NULL;
@@ -352,10 +384,6 @@ ofproto_create(const char *datapath, const char *datapath_type,
p->ml = mac_learning_create();
}
- /* Register switch status category. */
- p->ss_cat = switch_status_register(p->switch_status, "remote",
- rconn_status_cb, p->controller->rconn);
-
/* Pick final datapath ID. */
p->datapath_id = pick_datapath_id(p);
VLOG_INFO("using datapath ID %016"PRIx64, p->datapath_id);
@@ -370,103 +398,212 @@ ofproto_set_datapath_id(struct ofproto *p, uint64_t datapath_id)
uint64_t old_dpid = p->datapath_id;
p->datapath_id = datapath_id ? datapath_id : pick_datapath_id(p);
if (p->datapath_id != old_dpid) {
+ struct ofconn *ofconn;
+
VLOG_INFO("datapath ID changed to %016"PRIx64, p->datapath_id);
- rconn_reconnect(p->controller->rconn);
+
+ /* Force all active connections to reconnect, since there is no way to
+ * notify a controller that the datapath ID has changed. */
+ LIST_FOR_EACH (ofconn, struct ofconn, node, &p->all_conns) {
+ rconn_reconnect(ofconn->rconn);
+ }
}
}
-void
-ofproto_set_controller(struct ofproto *p, const struct ofproto_controller *c)
+static bool
+is_discovery_controller(const struct ofproto_controller *c)
+{
+ return !strcmp(c->target, "discover");
+}
+
+static bool
+is_in_band_controller(const struct ofproto_controller *c)
+{
+ return is_discovery_controller(c) || c->band == OFPROTO_IN_BAND;
+}
+
+/* Creates a new controller in 'ofproto'. Some of the settings are initially
+ * drawn from 'c', but update_controller() needs to be called later to finish
+ * the new ofconn's configuration. */
+static void
+add_controller(struct ofproto *ofproto, const struct ofproto_controller *c)
+{
+ struct discovery *discovery;
+ struct ofconn *ofconn;
+
+ if (is_discovery_controller(c)) {
+ int error = discovery_create(c->accept_re, c->update_resolv_conf,
+ ofproto->dpif, ofproto->switch_status,
+ &discovery);
+ if (error) {
+ return;
+ }
+ } else {
+ discovery = NULL;
+ }
+
+ ofconn = ofconn_create(ofproto, rconn_create(5, 8), OFCONN_CONTROLLER);
+ ofconn->pktbuf = pktbuf_create();
+ ofconn->miss_send_len = OFP_DEFAULT_MISS_SEND_LEN;
+ if (discovery) {
+ ofconn->discovery = discovery;
+ } else {
+ rconn_connect(ofconn->rconn, c->target);
+ }
+ hmap_insert(&ofproto->controllers, &ofconn->hmap_node,
+ hash_string(c->target, 0));
+}
+
+/* Reconfigures 'ofconn' to match 'c'. This function cannot update an ofconn's
+ * target or turn discovery on or off (these are done by creating new ofconns
+ * and deleting old ones), but it can update the rest of an ofconn's
+ * settings. */
+static void
+update_controller(struct ofconn *ofconn, const struct ofproto_controller *c)
{
- int rate_limit, burst_limit;
- bool in_band;
+ struct ofproto *ofproto = ofconn->ofproto;
+ int probe_interval;
+ int i;
- if (c) {
- int probe_interval;
- bool discovery;
+ rconn_set_max_backoff(ofconn->rconn, c->max_backoff);
- discovery = !strcmp(c->target, "discover");
- in_band = discovery || c->band == OFPROTO_IN_BAND;
+ probe_interval = c->probe_interval ? MAX(c->probe_interval, 5) : 0;
+ rconn_set_probe_interval(ofconn->rconn, probe_interval);
- rconn_set_max_backoff(p->controller->rconn, c->max_backoff);
+ if (ofconn->discovery) {
+ discovery_set_update_resolv_conf(ofconn->discovery,
+ c->update_resolv_conf);
+ discovery_set_accept_controller_re(ofconn->discovery, c->accept_re);
+ }
- probe_interval = c->probe_interval ? MAX(c->probe_interval, 5) : 0;
- rconn_set_probe_interval(p->controller->rconn, probe_interval);
+ for (i = 0; i < N_SCHEDULERS; i++) {
+ struct pinsched **s = &ofconn->schedulers[i];
- if (discovery != (p->discovery != NULL)) {
- rconn_disconnect(p->controller->rconn);
- if (discovery) {
- if (discovery_create(c->accept_re, c->update_resolv_conf,
- p->dpif, p->switch_status,
- &p->discovery)) {
- return;
- }
+ if (c->rate_limit > 0) {
+ if (!*s) {
+ *s = pinsched_create(c->rate_limit, c->burst_limit,
+ ofproto->switch_status);
} else {
- discovery_destroy(p->discovery);
- p->discovery = NULL;
+ pinsched_set_limits(*s, c->rate_limit, c->burst_limit);
}
+ } else {
+ pinsched_destroy(*s);
+ *s = NULL;
}
+ }
+}
- if (discovery) {
- discovery_set_update_resolv_conf(p->discovery,
- c->update_resolv_conf);
- discovery_set_accept_controller_re(p->discovery, c->accept_re);
- } else {
- if (strcmp(rconn_get_name(p->controller->rconn), c->target)) {
- rconn_connect(p->controller->rconn, c->target);
- }
+static const char *
+ofconn_get_target(const struct ofconn *ofconn)
+{
+ return ofconn->discovery ? "discover" : rconn_get_name(ofconn->rconn);
+}
+
+static struct ofconn *
+find_controller_by_target(struct ofproto *ofproto, const char *target)
+{
+ struct ofconn *ofconn;
+
+ HMAP_FOR_EACH_WITH_HASH (ofconn, struct ofconn, hmap_node,
+ hash_string(target, 0), &ofproto->controllers) {
+ if (!strcmp(ofconn_get_target(ofconn), target)) {
+ return ofconn;
}
- } else {
- rconn_disconnect(p->controller->rconn);
- in_band = false;
}
+ return NULL;
+}
- if (in_band != (p->in_band != NULL)) {
- if (in_band) {
- int error;
+void
+ofproto_set_controllers(struct ofproto *p,
+ const struct ofproto_controller *controllers,
+ size_t n_controllers)
+{
+ struct shash new_controllers;
+ struct rconn **in_band_rconns;
+ enum ofproto_fail_mode fail_mode;
+ struct ofconn *ofconn, *next;
+ bool ss_exists;
+ size_t n_in_band;
+ size_t i;
- error = in_band_create(p, p->dpif, p->switch_status, &p->in_band);
- if (!error) {
- in_band_set_remotes(p->in_band, &p->controller->rconn, 1);
- }
+ shash_init(&new_controllers);
+ for (i = 0; i < n_controllers; i++) {
+ const struct ofproto_controller *c = &controllers[i];
+
+ shash_add_once(&new_controllers, c->target, &controllers[i]);
+ if (!find_controller_by_target(p, c->target)) {
+ add_controller(p, c);
+ }
+ }
+
+ in_band_rconns = xmalloc(n_controllers * sizeof *in_band_rconns);
+ n_in_band = 0;
+ fail_mode = OFPROTO_FAIL_STANDALONE;
+ ss_exists = false;
+ HMAP_FOR_EACH_SAFE (ofconn, next, struct ofconn, hmap_node,
+ &p->controllers) {
+ struct ofproto_controller *c;
+
+ c = shash_find_data(&new_controllers, ofconn_get_target(ofconn));
+ if (!c) {
+ ofconn_destroy(ofconn);
} else {
- in_band_destroy(p->in_band);
- p->in_band = NULL;
+ update_controller(ofconn, c);
+
+ if (ofconn->ss) {
+ ss_exists = true;
+ }
+ if (is_in_band_controller(c)) {
+ in_band_rconns[n_in_band++] = ofconn->rconn;
+ }
+
+ if (c->fail == OFPROTO_FAIL_SECURE) {
+ fail_mode = OFPROTO_FAIL_SECURE;
+ }
+ }
+ }
+ shash_destroy(&new_controllers);
+
+ if (n_in_band) {
+ if (!p->in_band) {
+ in_band_create(p, p->dpif, p->switch_status, &p->in_band);
+ }
+ if (p->in_band) {
+ in_band_set_remotes(p->in_band, in_band_rconns, n_in_band);
}
- rconn_reconnect(p->controller->rconn);
+ } else {
+ in_band_destroy(p->in_band);
+ p->in_band = NULL;
}
+ free(in_band_rconns);
+
+ if (!hmap_is_empty(&p->controllers)
+ && fail_mode == OFPROTO_FAIL_STANDALONE) {
+ struct rconn **rconns;
+ size_t n;
- if (c && c->fail == OFPROTO_FAIL_STANDALONE) {
- struct rconn *rconn = p->controller->rconn;
- int trigger_duration = rconn_get_probe_interval(rconn) * 3;
if (!p->fail_open) {
- p->fail_open = fail_open_create(p, trigger_duration,
- p->switch_status, rconn);
- } else {
- fail_open_set_trigger_duration(p->fail_open, trigger_duration);
+ p->fail_open = fail_open_create(p, p->switch_status);
+ }
+
+ n = 0;
+ rconns = xmalloc(hmap_count(&p->controllers) * sizeof *rconns);
+ HMAP_FOR_EACH (ofconn, struct ofconn, hmap_node, &p->controllers) {
+ rconns[n++] = ofconn->rconn;
}
+
+ fail_open_set_controllers(p->fail_open, rconns, n);
+ /* p->fail_open takes ownership of 'rconns'. */
} else {
fail_open_destroy(p->fail_open);
p->fail_open = NULL;
}
- rate_limit = c ? c->rate_limit : 0;
- burst_limit = c ? c->burst_limit : 0;
- if (rate_limit > 0) {
- if (!p->miss_sched) {
- p->miss_sched = pinsched_create(rate_limit, burst_limit,
- p->switch_status);
- p->action_sched = pinsched_create(rate_limit, burst_limit,
- NULL);
- } else {
- pinsched_set_limits(p->miss_sched, rate_limit, burst_limit);
- pinsched_set_limits(p->action_sched, rate_limit, burst_limit);
- }
- } else {
- pinsched_destroy(p->miss_sched);
- p->miss_sched = NULL;
- pinsched_destroy(p->action_sched);
- p->action_sched = NULL;
+ if (!hmap_is_empty(&p->controllers) && !ss_exists) {
+ ofconn = CONTAINER_OF(hmap_first(&p->controllers),
+ struct ofconn, hmap_node);
+ ofconn->ss = switch_status_register(p->switch_status, "remote",
+ rconn_status_cb, ofconn->rconn);
}
}
@@ -629,27 +766,10 @@ ofproto_get_datapath_id(const struct ofproto *ofproto)
return ofproto->datapath_id;
}
-void
-ofproto_get_controller(const struct ofproto *p, struct ofproto_controller *c)
+bool
+ofproto_has_controller(const struct ofproto *ofproto)
{
- memset(c, 0, sizeof *c);
- if (p->discovery) {
- struct discovery *d = p->discovery;
-
- c->target = "discover";
- c->accept_re = (char *) discovery_get_accept_controller_re(d);
- c->update_resolv_conf = discovery_get_update_resolv_conf(d);
- } else if (p->controller) {
- c->target = (char *) rconn_get_name(p->controller->rconn);
- } else {
- return;
- }
-
- c->max_backoff = rconn_get_max_backoff(p->controller->rconn);
- c->probe_interval = rconn_get_probe_interval(p->controller->rconn);
- c->fail = p->fail_open ? OFPROTO_FAIL_STANDALONE : OFPROTO_FAIL_SECURE;
- c->band = p->in_band ? OFPROTO_IN_BAND : OFPROTO_OUT_OF_BAND;
- pinsched_get_limits(p->miss_sched, &c->rate_limit, &c->burst_limit);
+ return !hmap_is_empty(&ofproto->controllers);
}
void
@@ -698,6 +818,7 @@ ofproto_destroy(struct ofproto *p)
&p->all_conns) {
ofconn_destroy(ofconn);
}
+ hmap_destroy(&p->controllers);
dpif_close(p->dpif);
netdev_monitor_destroy(p->netdev_monitor);
@@ -707,14 +828,9 @@ ofproto_destroy(struct ofproto *p)
shash_destroy(&p->port_by_name);
switch_status_destroy(p->switch_status);
- discovery_destroy(p->discovery);
- pinsched_destroy(p->miss_sched);
- pinsched_destroy(p->action_sched);
netflow_destroy(p->netflow);
ofproto_sflow_destroy(p->sflow);
- switch_status_unregister(p->ss_cat);
-
for (i = 0; i < p->n_listeners; i++) {
pvconn_close(p->listeners[i]);
}
@@ -759,6 +875,27 @@ process_port_change(struct ofproto *ofproto, int error, char *devname)
}
}
+/* One of ofproto's "snoop" pvconns has accepted a new connection on 'vconn'.
+ * Connects this vconn to a controller. */
+static void
+add_snooper(struct ofproto *ofproto, struct vconn *vconn)
+{
+ struct ofconn *ofconn;
+
+ /* Arbitrarily pick the first controller in the list for monitoring. We
+ * could do something smarter or more flexible later, if it ever proves
+ * useful. */
+ LIST_FOR_EACH (ofconn, struct ofconn, node, &ofproto->all_conns) {
+ if (ofconn->type == OFCONN_CONTROLLER) {
+ rconn_add_monitor(ofconn->rconn, vconn);
+ return;
+ }
+
+ }
+ VLOG_INFO_RL(&rl, "no controller connection to monitor");
+ vconn_close(vconn);
+}
+
int
ofproto_run1(struct ofproto *p)
{
@@ -803,21 +940,6 @@ ofproto_run1(struct ofproto *p)
if (p->in_band) {
in_band_run(p->in_band);
}
- if (p->discovery) {
- char *controller_name;
- if (rconn_is_connectivity_questionable(p->controller->rconn)) {
- discovery_question_connectivity(p->discovery);
- }
- if (discovery_run(p->discovery, &controller_name)) {
- if (controller_name) {
- rconn_connect(p->controller->rconn, controller_name);
- } else {
- rconn_disconnect(p->controller->rconn);
- }
- }
- }
- pinsched_run(p->miss_sched, send_packet_in_miss, p);
- pinsched_run(p->action_sched, send_packet_in_action, p);
LIST_FOR_EACH_SAFE (ofconn, next_ofconn, struct ofconn, node,
&p->all_conns) {
@@ -836,7 +958,8 @@ ofproto_run1(struct ofproto *p)
retval = pvconn_accept(p->listeners[i], OFP_VERSION, &vconn);
if (!retval) {
- ofconn_create(p, rconn_new_from_vconn("passive", vconn));
+ ofconn_create(p, rconn_new_from_vconn("passive", vconn),
+ OFCONN_TRANSIENT);
} else if (retval != EAGAIN) {
VLOG_WARN_RL(&rl, "accept failed (%s)", strerror(retval));
}
@@ -848,7 +971,7 @@ ofproto_run1(struct ofproto *p)
retval = pvconn_accept(p->snoops[i], OFP_VERSION, &vconn);
if (!retval) {
- rconn_add_monitor(p->controller->rconn, vconn);
+ add_snooper(p, vconn);
} else if (retval != EAGAIN) {
VLOG_WARN_RL(&rl, "accept failed (%s)", strerror(retval));
}
@@ -921,14 +1044,9 @@ ofproto_wait(struct ofproto *p)
if (p->in_band) {
in_band_wait(p->in_band);
}
- if (p->discovery) {
- discovery_wait(p->discovery);
- }
if (p->fail_open) {
fail_open_wait(p->fail_open);
}
- pinsched_wait(p->miss_sched);
- pinsched_wait(p->action_sched);
if (p->sflow) {
ofproto_sflow_wait(p->sflow);
}
@@ -965,7 +1083,7 @@ ofproto_get_revalidate_set(struct ofproto *ofproto)
bool
ofproto_is_alive(const struct ofproto *p)
{
- return p->discovery || rconn_is_alive(p->controller->rconn);
+ return !hmap_is_empty(&p->controllers);
}
int
@@ -1342,14 +1460,16 @@ init_ports(struct ofproto *p)
}
static struct ofconn *
-ofconn_create(struct ofproto *p, struct rconn *rconn)
+ofconn_create(struct ofproto *p, struct rconn *rconn, enum ofconn_type type)
{
- struct ofconn *ofconn = xmalloc(sizeof *ofconn);
+ struct ofconn *ofconn = xzalloc(sizeof *ofconn);
+ ofconn->ofproto = p;
list_push_back(&p->all_conns, &ofconn->node);
ofconn->rconn = rconn;
+ ofconn->type = type;
+ ofconn->packet_in_counter = rconn_packet_counter_create ();
ofconn->pktbuf = NULL;
ofconn->miss_send_len = 0;
- ofconn->packet_in_counter = rconn_packet_counter_create ();
ofconn->reply_counter = rconn_packet_counter_create ();
return ofconn;
}
@@ -1357,7 +1477,13 @@ ofconn_create(struct ofproto *p, struct rconn *rconn)
static void
ofconn_destroy(struct ofconn *ofconn)
{
+ if (ofconn->type == OFCONN_CONTROLLER) {
+ hmap_remove(&ofconn->ofproto->controllers, &ofconn->hmap_node);
+ }
+ discovery_destroy(ofconn->discovery);
+
list_remove(&ofconn->node);
+ switch_status_unregister(ofconn->ss);
rconn_destroy(ofconn->rconn);
rconn_packet_counter_destroy(ofconn->packet_in_counter);
rconn_packet_counter_destroy(ofconn->reply_counter);
@@ -1369,6 +1495,25 @@ static void
ofconn_run(struct ofconn *ofconn, struct ofproto *p)
{
int iteration;
+ size_t i;
+
+ if (ofconn->discovery) {
+ char *controller_name;
+ if (rconn_is_connectivity_questionable(ofconn->rconn)) {
+ discovery_question_connectivity(ofconn->discovery);
+ }
+ if (discovery_run(ofconn->discovery, &controller_name)) {
+ if (controller_name) {
+ rconn_connect(ofconn->rconn, controller_name);
+ } else {
+ rconn_disconnect(ofconn->rconn);
+ }
+ }
+ }
+
+ for (i = 0; i < N_SCHEDULERS; i++) {
+ pinsched_run(ofconn->schedulers[i], do_send_packet_in, ofconn);
+ }
rconn_run(ofconn->rconn);
@@ -1388,7 +1533,7 @@ ofconn_run(struct ofconn *ofconn, struct ofproto *p)
}
}
- if (ofconn != p->controller && !rconn_is_alive(ofconn->rconn)) {
+ if (!ofconn->discovery && !rconn_is_alive(ofconn->rconn)) {
ofconn_destroy(ofconn);
}
}
@@ -1396,6 +1541,14 @@ ofconn_run(struct ofconn *ofconn, struct ofproto *p)
static void
ofconn_wait(struct ofconn *ofconn)
{
+ int i;
+
+ if (ofconn->discovery) {
+ discovery_wait(ofconn->discovery);
+ }
+ for (i = 0; i < N_SCHEDULERS; i++) {
+ pinsched_wait(ofconn->schedulers[i]);
+ }
rconn_run_wait(ofconn->rconn);
if (rconn_packet_counter_read (ofconn->reply_counter) < OFCONN_REPLY_MAX) {
rconn_recv_wait(ofconn->rconn);
@@ -1921,7 +2074,7 @@ handle_set_config(struct ofproto *p, struct ofconn *ofconn,
}
flags = ntohs(osc->flags);
- if (ofconn == p->controller) {
+ if (ofconn->type == OFCONN_CONTROLLER) {
switch (flags & OFPC_FRAG_MASK) {
case OFPC_FRAG_NORMAL:
dpif_set_drop_frags(p->dpif, false);
@@ -1936,14 +2089,6 @@ handle_set_config(struct ofproto *p, struct ofconn *ofconn,
}
}
- if ((ntohs(osc->miss_send_len) != 0) != (ofconn->miss_send_len != 0)) {
- if (ntohs(osc->miss_send_len) != 0) {
- ofconn->pktbuf = pktbuf_create();
- } else {
- pktbuf_destroy(ofconn->pktbuf);
- }
- }
-
ofconn->miss_send_len = ntohs(osc->miss_send_len);
return 0;
@@ -3333,7 +3478,6 @@ static void
handle_odp_miss_msg(struct ofproto *p, struct ofpbuf *packet)
{
struct odp_msg *msg = packet->data;
- uint16_t in_port = odp_port_to_ofp_port(msg->port);
struct rule *rule;
struct ofpbuf payload;
flow_t flow;
@@ -3369,7 +3513,7 @@ handle_odp_miss_msg(struct ofproto *p, struct ofpbuf *packet)
}
COVERAGE_INC(ofproto_packet_in);
- pinsched_send(p->miss_sched, in_port, packet, send_packet_in_miss, p);
+ send_packet_in(p, packet);
return;
}
@@ -3390,8 +3534,7 @@ handle_odp_miss_msg(struct ofproto *p, struct ofpbuf *packet)
rule_execute(p, rule, &payload, &flow);
rule_reinstall(p, rule);
- if (rule->super && rule->super->cr.priority == FAIL_OPEN_PRIORITY
- && rconn_is_connected(p->controller->rconn)) {
+ if (rule->super && rule->super->cr.priority == FAIL_OPEN_PRIORITY) {
/*
* Extra-special case for fail-open mode.
*
@@ -3402,7 +3545,7 @@ handle_odp_miss_msg(struct ofproto *p, struct ofpbuf *packet)
*
* See the top-level comment in fail-open.c for more information.
*/
- pinsched_send(p->miss_sched, in_port, packet, send_packet_in_miss, p);
+ send_packet_in(p, packet);
} else {
ofpbuf_delete(packet);
}
@@ -3416,8 +3559,7 @@ handle_odp_msg(struct ofproto *p, struct ofpbuf *packet)
switch (msg->type) {
case _ODPL_ACTION_NR:
COVERAGE_INC(ofproto_ctlr_action);
- pinsched_send(p->action_sched, odp_port_to_ofp_port(msg->port), packet,
- send_packet_in_action, p);
+ send_packet_in(p, packet);
break;
case _ODPL_SFLOW_NR:
@@ -3663,67 +3805,65 @@ update_used(struct ofproto *p)
}
static void
-do_send_packet_in(struct ofconn *ofconn, uint32_t buffer_id,
- const struct ofpbuf *packet, int send_len)
+do_send_packet_in(struct ofpbuf *packet, void *ofconn_)
{
+ struct ofconn *ofconn = ofconn_;
+ struct ofproto *ofproto = ofconn->ofproto;
struct odp_msg *msg = packet->data;
struct ofpbuf payload;
struct ofpbuf *opi;
- uint8_t reason;
+ uint32_t buffer_id;
+ int send_len;
/* Extract packet payload from 'msg'. */
payload.data = msg + 1;
payload.size = msg->length - sizeof *msg;
- /* Construct ofp_packet_in message. */
- reason = msg->type == _ODPL_ACTION_NR ? OFPR_ACTION : OFPR_NO_MATCH;
- opi = make_packet_in(buffer_id, odp_port_to_ofp_port(msg->port), reason,
- &payload, send_len);
+ /* Construct packet-in message. */
+ send_len = INT_MAX;
+ if (msg->type == _ODPL_ACTION_NR) {
+ buffer_id = UINT32_MAX;
+ } else {
+ if (ofproto->fail_open && fail_open_is_active(ofproto->fail_open)) {
+ buffer_id = pktbuf_get_null();
+ } else {
+ buffer_id = pktbuf_save(ofconn->pktbuf, &payload, msg->port);
+ }
+ if (buffer_id != UINT32_MAX) {
+ send_len = ofconn->miss_send_len;
+ }
+ }
+ opi = make_packet_in(buffer_id, odp_port_to_ofp_port(msg->port),
+ msg->type, &payload, send_len);
/* Send. */
rconn_send_with_limit(ofconn->rconn, opi, ofconn->packet_in_counter, 100);
-}
-static void
-send_packet_in_action(struct ofpbuf *packet, void *p_)
-{
- struct ofproto *p = p_;
- struct ofconn *ofconn;
- struct odp_msg *msg;
-
- msg = packet->data;
- LIST_FOR_EACH (ofconn, struct ofconn, node, &p->all_conns) {
- if (ofconn == p->controller || ofconn->miss_send_len) {
- do_send_packet_in(ofconn, UINT32_MAX, packet, msg->arg);
- }
- }
ofpbuf_delete(packet);
}
static void
-send_packet_in_miss(struct ofpbuf *packet, void *p_)
+send_packet_in(struct ofproto *ofproto, struct ofpbuf *packet)
{
- struct ofproto *p = p_;
- bool in_fail_open = p->fail_open && fail_open_is_active(p->fail_open);
- struct ofconn *ofconn;
- struct ofpbuf payload;
- struct odp_msg *msg;
+ struct odp_msg *msg = packet->data;
+ struct ofconn *ofconn, *prev;
- msg = packet->data;
- payload.data = msg + 1;
- payload.size = msg->length - sizeof *msg;
- LIST_FOR_EACH (ofconn, struct ofconn, node, &p->all_conns) {
- if (ofconn->miss_send_len) {
- struct pktbuf *pb = ofconn->pktbuf;
- uint32_t buffer_id = (in_fail_open
- ? pktbuf_get_null()
- : pktbuf_save(pb, &payload, msg->port));
- int send_len = (buffer_id != UINT32_MAX ? ofconn->miss_send_len
- : INT_MAX);
- do_send_packet_in(ofconn, buffer_id, packet, send_len);
+ assert(msg->type == _ODPL_MISS_NR || msg->type == _ODPL_ACTION_NR);
+
+ prev = NULL;
+ LIST_FOR_EACH (ofconn, struct ofconn, node, &ofproto->all_conns) {
+ if (prev) {
+ pinsched_send(prev->schedulers[msg->type], msg->port,
+ ofpbuf_clone(packet), do_send_packet_in, prev);
}
+ prev = ofconn;
+ }
+ if (prev) {
+ pinsched_send(prev->schedulers[msg->type], msg->port,
+ packet, do_send_packet_in, prev);
+ } else {
+ ofpbuf_delete(packet);
}
- ofpbuf_delete(packet);
}
static uint64_t
diff --git a/ofproto/ofproto.h b/ofproto/ofproto.h
index 29f9ee929..22ad61070 100644
--- a/ofproto/ofproto.h
+++ b/ofproto/ofproto.h
@@ -100,8 +100,8 @@ bool ofproto_is_alive(const struct ofproto *);
/* Configuration. */
void ofproto_set_datapath_id(struct ofproto *, uint64_t datapath_id);
-void ofproto_set_controller(struct ofproto *,
- const struct ofproto_controller *);
+void ofproto_set_controllers(struct ofproto *,
+ const struct ofproto_controller *, size_t n);
void ofproto_set_desc(struct ofproto *,
const char *mfr_desc, const char *hw_desc,
const char *sw_desc, const char *serial_desc,
@@ -115,8 +115,7 @@ int ofproto_set_stp(struct ofproto *, bool enable_stp);
/* Configuration querying. */
uint64_t ofproto_get_datapath_id(const struct ofproto *);
-void ofproto_get_controller(const struct ofproto *,
- struct ofproto_controller *);
+bool ofproto_has_controller(const struct ofproto *);
void ofproto_get_listeners(const struct ofproto *, struct svec *);
void ofproto_get_snoops(const struct ofproto *, struct svec *);
void ofproto_get_all_flows(struct ofproto *p, struct ds *);
diff --git a/ofproto/status.c b/ofproto/status.c
index dbedf10e0..133bde0c9 100644
--- a/ofproto/status.c
+++ b/ofproto/status.c
@@ -130,7 +130,6 @@ static void
config_status_cb(struct status_reply *sr, void *ofproto_)
{
const struct ofproto *ofproto = ofproto_;
- struct ofproto_controller controller;
uint64_t datapath_id;
struct svec listeners;
size_t i;
@@ -146,14 +145,6 @@ config_status_cb(struct status_reply *sr, void *ofproto_)
status_reply_put(sr, "management%zu=%s", i, listeners.names[i]);
}
svec_destroy(&listeners);
-
- ofproto_get_controller(ofproto, &controller);
- if (controller.probe_interval) {
- status_reply_put(sr, "probe-interval=%d", controller.probe_interval);
- }
- if (controller.max_backoff) {
- status_reply_put(sr, "max-backoff=%d", controller.max_backoff);
- }
}
static void
diff --git a/tests/ovs-vsctl.at b/tests/ovs-vsctl.at
index c63470372..e9b21065e 100644
--- a/tests/ovs-vsctl.at
+++ b/tests/ovs-vsctl.at
@@ -354,6 +354,87 @@ CHECK_IFACES([a], [a1], [a2], [a3])
OVS_VSCTL_CLEANUP
AT_CLEANUP
+AT_SETUP([controllers])
+AT_KEYWORDS([controller ovs-vsctl])
+OVS_VSCTL_SETUP
+AT_CHECK([RUN_OVS_VSCTL_TOGETHER(
+ [add-br br0],
+
+ [set-controller tcp:1.2.3.4],
+ [get-controller],
+ [get-controller default],
+ [get-controller br0],
+
+ [set-controller br0 tcp:4.5.6.7],
+ [get-controller],
+ [get-controller default],
+ [get-controller br0],
+
+ [del-controller],
+ [get-controller],
+ [get-controller default],
+ [get-controller br0],
+
+ [set-controller default tcp:8.9.10.11],
+ [get-controller],
+ [get-controller default],
+ [get-controller br0],
+
+ [del-controller default],
+ [get-controller],
+ [get-controller default],
+ [get-controller br0],
+
+ [del-controller br0],
+ [get-controller],
+ [get-controller default],
+ [get-controller br0],
+
+ [set-controller default tcp:1.2.3.4 tcp:4.5.6.7],
+ [get-controller],
+ [get-controller default],
+ [get-controller br0],
+
+ [set-controller br0 tcp:8.9.10.11 tcp:5.4.3.2],
+ [get-controller],
+ [get-controller default],
+ [get-controller br0])], [0], [
+
+tcp:1.2.3.4
+tcp:1.2.3.4
+tcp:1.2.3.4
+
+tcp:1.2.3.4
+tcp:1.2.3.4
+tcp:4.5.6.7
+
+
+
+tcp:4.5.6.7
+
+tcp:8.9.10.11
+tcp:8.9.10.11
+tcp:4.5.6.7
+
+
+
+tcp:4.5.6.7
+
+
+
+
+
+tcp:1.2.3.4\ntcp:4.5.6.7
+tcp:1.2.3.4\ntcp:4.5.6.7
+tcp:1.2.3.4\ntcp:4.5.6.7
+
+tcp:1.2.3.4\ntcp:4.5.6.7
+tcp:1.2.3.4\ntcp:4.5.6.7
+tcp:5.4.3.2\ntcp:8.9.10.11
+], [], [OVS_VSCTL_CLEANUP])
+OVS_VSCTL_CLEANUP
+AT_CLEANUP
+
dnl ----------------------------------------------------------------------
AT_BANNER([ovs-vsctl unit tests -- fake bridges])
diff --git a/utilities/ovs-openflowd.8.in b/utilities/ovs-openflowd.8.in
index 4d2f211a0..7a78bf292 100644
--- a/utilities/ovs-openflowd.8.in
+++ b/utilities/ovs-openflowd.8.in
@@ -6,12 +6,12 @@ ovs\-openflowd \- OpenFlow switch implementation
.
.SH SYNOPSIS
.B ovs\-openflowd
-[\fIoptions\fR] \fIdatapath\fR [\fIcontroller\fR]
+[\fIoptions\fR] \fIdatapath\fR [\fIcontroller\fR\&...]
.
.SH DESCRIPTION
The \fBovs\-openflowd\fR program implements an OpenFlow switch using a
-flow-based datapath. \fBovs\-openflowd\fR connects to an OpenFlow controller
-over TCP or SSL.
+flow-based datapath. \fBovs\-openflowd\fR connects to one or more
+OpenFlow controllers over TCP or SSL.
.PP
The mandatory \fIdatapath\fR argument argument specifies the local datapath
to relay. It takes one of the following forms:
@@ -19,17 +19,17 @@ to relay. It takes one of the following forms:
.so lib/dpif.man
.
.PP
-The optional \fIcontroller\fR argument specifies how to connect to
+The optional \fIcontroller\fR arguments specify how to connect to
the OpenFlow controller. It takes one of the following forms:
.
.so lib/vconn-active.man
.
.PP
-If \fIcontroller\fR is omitted, \fBovs\-openflowd\fR attempts to discover the
-location of the controller automatically (see below).
+If no \fIcontroller\fR is specified, \fBovs\-openflowd\fR attempts to
+discover the location of a controller automatically (see below).
.
-.SS "Contacting the Controller"
-The OpenFlow switch must be able to contact the OpenFlow controller
+.SS "Contacting Controllers"
+The OpenFlow switch must be able to contact the OpenFlow controllers
over the network. It can do so in one of two ways:
.
.IP out-of-band
@@ -60,8 +60,8 @@ manually or discovered automatically:
.RS
.IP "controller discovery"
To make \fBovs\-openflowd\fR discover the location of the controller
-automatically, do not specify the location of the controller on the
-\fBovs\-openflowd\fR command line.
+automatically, do not specify a controller on the \fBovs\-openflowd\fR
+command line.
.IP
In this mode, \fBovs\-openflowd\fR will broadcast a DHCP request with vendor
class identifier \fBOpenFlow\fR across the network devices added to
@@ -159,8 +159,8 @@ the local port network device, and start the DHCP client afterward.
.TP
\fB--datapath-id=\fIdpid\fR
Sets \fIdpid\fR, which must consist of exactly 16 hexadecimal digits,
-as the datapath ID that the switch will use to identify itself to the
-OpenFlow controller.
+as the datapath ID that the switch will use to identify itself to
+OpenFlow controllers.
.IP
If this option is omitted, the default datapath ID is taken from the
Ethernet address of the datapath's local port (which is typically
diff --git a/utilities/ovs-openflowd.c b/utilities/ovs-openflowd.c
index 70430c035..1460423d6 100644
--- a/utilities/ovs-openflowd.c
+++ b/utilities/ovs-openflowd.c
@@ -51,7 +51,8 @@
/* Settings that may be configured by the user. */
struct ofsettings {
/* Controller configuration. */
- struct ofproto_controller controller;
+ struct ofproto_controller *controllers;
+ size_t n_controllers;
/* Datapath. */
uint64_t datapath_id; /* Datapath ID. */
@@ -171,7 +172,7 @@ main(int argc, char *argv[])
if (error) {
ovs_fatal(error, "failed to configure STP");
}
- ofproto_set_controller(ofproto, &s.controller);
+ ofproto_set_controllers(ofproto, s.controllers, s.n_controllers);
daemonize_complete();
@@ -266,16 +267,18 @@ parse_options(int argc, char *argv[], struct ofsettings *s)
{0, 0, 0, 0},
};
char *short_options = long_options_to_short_options(long_options);
+ struct ofproto_controller controller_opts;
/* Set defaults that we can figure out before parsing options. */
- s->controller.max_backoff = 8;
- s->controller.probe_interval = 5;
- s->controller.fail = OFPROTO_FAIL_STANDALONE;
- s->controller.band = OFPROTO_IN_BAND;
- s->controller.accept_re = NULL;
- s->controller.update_resolv_conf = true;
- s->controller.rate_limit = 0;
- s->controller.burst_limit = 0;
+ controller_opts.target = NULL;
+ controller_opts.max_backoff = 8;
+ controller_opts.probe_interval = 5;
+ controller_opts.fail = OFPROTO_FAIL_STANDALONE;
+ controller_opts.band = OFPROTO_IN_BAND;
+ controller_opts.accept_re = NULL;
+ controller_opts.update_resolv_conf = true;
+ controller_opts.rate_limit = 0;
+ controller_opts.burst_limit = 0;
s->datapath_id = 0;
s->mfr_desc = NULL;
s->hw_desc = NULL;
@@ -325,26 +328,26 @@ parse_options(int argc, char *argv[], struct ofsettings *s)
break;
case OPT_ACCEPT_VCONN:
- s->controller.accept_re = optarg;
+ controller_opts.accept_re = optarg;
break;
case OPT_NO_RESOLV_CONF:
- s->controller.update_resolv_conf = false;
+ controller_opts.update_resolv_conf = false;
break;
case OPT_FAIL_MODE:
if (!strcmp(optarg, "open")) {
- s->controller.fail = OFPROTO_FAIL_STANDALONE;
+ controller_opts.fail = OFPROTO_FAIL_STANDALONE;
} else if (!strcmp(optarg, "closed")) {
- s->controller.fail = OFPROTO_FAIL_SECURE;
+ controller_opts.fail = OFPROTO_FAIL_SECURE;
} else {
ovs_fatal(0, "--fail argument must be \"open\" or \"closed\"");
}
break;
case OPT_INACTIVITY_PROBE:
- s->controller.probe_interval = atoi(optarg);
- if (s->controller.probe_interval < 5) {
+ controller_opts.probe_interval = atoi(optarg);
+ if (controller_opts.probe_interval < 5) {
ovs_fatal(0, "--inactivity-probe argument must be at least 5");
}
break;
@@ -362,28 +365,28 @@ parse_options(int argc, char *argv[], struct ofsettings *s)
break;
case OPT_MAX_BACKOFF:
- s->controller.max_backoff = atoi(optarg);
- if (s->controller.max_backoff < 1) {
+ controller_opts.max_backoff = atoi(optarg);
+ if (controller_opts.max_backoff < 1) {
ovs_fatal(0, "--max-backoff argument must be at least 1");
- } else if (s->controller.max_backoff > 3600) {
- s->controller.max_backoff = 3600;
+ } else if (controller_opts.max_backoff > 3600) {
+ controller_opts.max_backoff = 3600;
}
break;
case OPT_RATE_LIMIT:
if (optarg) {
- s->controller.rate_limit = atoi(optarg);
- if (s->controller.rate_limit < 1) {
+ controller_opts.rate_limit = atoi(optarg);
+ if (controller_opts.rate_limit < 1) {
ovs_fatal(0, "--rate-limit argument must be at least 1");
}
} else {
- s->controller.rate_limit = 1000;
+ controller_opts.rate_limit = 1000;
}
break;
case OPT_BURST_LIMIT:
- s->controller.burst_limit = atoi(optarg);
- if (s->controller.burst_limit < 1) {
+ controller_opts.burst_limit = atoi(optarg);
+ if (controller_opts.burst_limit < 1) {
ovs_fatal(0, "--burst-limit argument must be at least 1");
}
break;
@@ -397,11 +400,11 @@ parse_options(int argc, char *argv[], struct ofsettings *s)
break;
case OPT_OUT_OF_BAND:
- s->controller.band = OFPROTO_OUT_OF_BAND;
+ controller_opts.band = OFPROTO_OUT_OF_BAND;
break;
case OPT_IN_BAND:
- s->controller.band = OFPROTO_IN_BAND;
+ controller_opts.band = OFPROTO_IN_BAND;
break;
case OPT_NETFLOW:
@@ -457,25 +460,46 @@ parse_options(int argc, char *argv[], struct ofsettings *s)
"use --help for usage");
}
- /* Local and remote vconns. */
- dp_parse_name(argv[0], &s->dp_name, &s->dp_type);
-
- s->controller.target = argc > 1 ? argv[1] : "discover";
- if (!strcmp(s->controller.target, "discover")
- && s->controller.band == OFPROTO_OUT_OF_BAND) {
- ovs_fatal(0, "Cannot perform discovery with out-of-band control");
- }
-
/* Set accept_controller_regex. */
- if (!s->controller.accept_re) {
- s->controller.accept_re
+ if (!controller_opts.accept_re) {
+ controller_opts.accept_re
= stream_ssl_is_configured() ? "^ssl:.*" : "^tcp:.*";
}
/* Rate limiting. */
- if (s->controller.rate_limit && s->controller.rate_limit < 100) {
+ if (controller_opts.rate_limit && controller_opts.rate_limit < 100) {
VLOG_WARN("Rate limit set to unusually low value %d",
- s->controller.rate_limit);
+ controller_opts.rate_limit);
+ }
+
+ /* Local vconns. */
+ dp_parse_name(argv[0], &s->dp_name, &s->dp_type);
+
+ /* Controllers. */
+ s->n_controllers = argc > 1 ? argc - 1 : 1;
+ s->controllers = xmalloc(s->n_controllers * sizeof *s->controllers);
+ if (argc > 1) {
+ size_t i;
+
+ for (i = 0; i < s->n_controllers; i++) {
+ s->controllers[i] = controller_opts;
+ s->controllers[i].target = argv[i + 1];
+ }
+ } else {
+ s->controllers[0] = controller_opts;
+ s->controllers[0].target = "discover";
+ }
+
+ /* Sanity check. */
+ if (controller_opts.band == OFPROTO_OUT_OF_BAND) {
+ size_t i;
+
+ for (i = 0; i < s->n_controllers; i++) {
+ if (!strcmp(s->controllers[i].target, "discover")) {
+ ovs_fatal(0, "Cannot perform discovery with out-of-band "
+ "control");
+ }
+ }
}
}
@@ -483,10 +507,10 @@ static void
usage(void)
{
printf("%s: an OpenFlow switch implementation.\n"
- "usage: %s [OPTIONS] DATAPATH [CONTROLLER]\n"
+ "usage: %s [OPTIONS] DATAPATH [CONTROLLER...]\n"
"DATAPATH is a local datapath (e.g. \"dp0\").\n"
- "CONTROLLER is an active OpenFlow connection method; if it is\n"
- "omitted, then ovs-openflowd performs controller discovery.\n",
+ "Each CONTROLLER is an active OpenFlow connection method. If\n"
+ "none is given, ovs-openflowd performs controller discovery.\n",
program_name, program_name);
vconn_usage(true, true, true);
printf("\nOpenFlow options:\n"
diff --git a/utilities/ovs-vsctl.8.in b/utilities/ovs-vsctl.8.in
index 84193e875..cd83d0a29 100644
--- a/utilities/ovs-vsctl.8.in
+++ b/utilities/ovs-vsctl.8.in
@@ -299,13 +299,14 @@ output.
.SS "OpenFlow Controller Connectivity"
.
\fBovs\-vswitchd\fR can perform all configured bridging and switching
-locally, or it can be configured to connect a given bridge to an
-external OpenFlow controller, such as NOX.
+locally, or it can be configured to connect a given bridge to one or
+more external OpenFlow controllers, such as NOX.
.
-If a \fIbridge\fR argument is given, the settings apply only to the
-specified bridge. Otherwise, they apply to the Open vSwitch instance,
-and its configuration applies to any bridge that has not been explicitly
-configured through a \fIbridge\fR argument.
+For each of these commands, a \fIbridge\fR of \fBdefault\fR applies
+the configuration as the default for any bridge that has not been
+explicitly configured. Otherwise, \fIbridge\fR must name a bridge,
+and the settings apply only to that bridge. (Omitting \fIbridge\fR
+entirely usually has the same effect as specifying \fBdefault\fR.)
.
.IP "\fBget\-controller\fR [\fIbridge\fR]"
Prints the configured controller target.
@@ -313,9 +314,10 @@ Prints the configured controller target.
.IP "\fBdel\-controller\fR [\fIbridge\fR]"
Deletes the configured controller target.
.
-.IP "\fBset\-controller\fR [\fIbridge\fR] \fItarget\fR"
-Sets the configured controller target. The \fItarget\fR may use any of
-the following forms:
+.IP "\fBset\-controller\fR [\fIbridge\fR] \fItarget\fR\&..."
+Sets the configured controller target or targets. If more than one
+\fItarget\fR is specified, then \fIbridge\fR may not be omitted. Each
+\fItarget\fR may use any of the following forms:
.
.RS
.so lib/vconn-active.man
diff --git a/utilities/ovs-vsctl.c b/utilities/ovs-vsctl.c
index fe7838767..091e6bc7b 100644
--- a/utilities/ovs-vsctl.c
+++ b/utilities/ovs-vsctl.c
@@ -489,7 +489,8 @@ struct vsctl_context {
struct vsctl_bridge {
struct ovsrec_bridge *br_cfg;
char *name;
- struct ovsrec_controller *ctrl;
+ struct ovsrec_controller **ctrl;
+ size_t n_ctrl;
struct vsctl_bridge *parent;
int vlan;
};
@@ -508,7 +509,8 @@ struct vsctl_info {
struct shash bridges;
struct shash ports;
struct shash ifaces;
- struct ovsrec_controller *ctrl;
+ struct ovsrec_controller **ctrl;
+ size_t n_ctrl;
};
static char *
@@ -545,7 +547,13 @@ add_bridge(struct vsctl_info *b,
br->name = xstrdup(name);
br->parent = parent;
br->vlan = vlan;
- br->ctrl = parent ? parent->br_cfg->controller : br_cfg->controller;
+ if (parent) {
+ br->ctrl = parent->br_cfg->controller;
+ br->n_ctrl = parent->br_cfg->n_controller;
+ } else {
+ br->ctrl = br_cfg->controller;
+ br->n_ctrl = br_cfg->n_controller;
+ }
shash_add(&b->bridges, br->name, br);
return br;
}
@@ -610,6 +618,7 @@ get_info(const struct ovsrec_open_vswitch *ovs, struct vsctl_info *info)
shash_init(&info->ifaces);
info->ctrl = ovs->controller;
+ info->n_ctrl = ovs->n_controller;
shash_init(&bridges);
shash_init(&ports);
@@ -1440,6 +1449,29 @@ cmd_iface_to_br(struct vsctl_context *ctx)
free_info(&info);
}
+/* Print targets of the 'n_controllers' in 'controllers' on the output for
+ * 'ctx'. */
+static void
+print_controllers(struct vsctl_context *ctx,
+ struct ovsrec_controller **controllers,
+ size_t n_controllers)
+{
+ /* Print the targets in sorted order for reproducibility. */
+ struct svec targets;
+ size_t i;
+
+ svec_init(&targets);
+ for (i = 0; i < n_controllers; i++) {
+ svec_add(&targets, controllers[i]->target);
+ }
+
+ svec_sort(&targets);
+ for (i = 0; i < targets.n; i++) {
+ ds_put_format(&ctx->output, "%s\n", targets.names[i]);
+ }
+ svec_destroy(&targets);
+}
+
static void
cmd_get_controller(struct vsctl_context *ctx)
{
@@ -1447,21 +1479,14 @@ cmd_get_controller(struct vsctl_context *ctx)
get_info(ctx->ovs, &info);
- if (ctx->argc == 1) {
- /* Return the controller from the "Open_vSwitch" table */
- if (info.ctrl) {
- ds_put_format(&ctx->output, "%s\n", info.ctrl->target);
- }
+ if (ctx->argc == 1 || !strcmp(ctx->argv[1], "default")) {
+ print_controllers(ctx, info.ctrl, info.n_ctrl);
} else {
- /* Return the controller for a particular bridge. */
struct vsctl_bridge *br = find_bridge(&info, ctx->argv[1], true);
-
- /* If no controller is explicitly defined for the requested
- * bridge, fallback to the "Open_vSwitch" table's controller. */
- if (br->ctrl) {
- ds_put_format(&ctx->output, "%s\n", br->ctrl->target);
- } else if (info.ctrl) {
- ds_put_format(&ctx->output, "%s\n", info.ctrl->target);
+ if (br->n_ctrl) {
+ print_controllers(ctx, br->ctrl, br->n_ctrl);
+ } else {
+ print_controllers(ctx, info.ctrl, info.n_ctrl);
}
}
@@ -1469,60 +1494,117 @@ cmd_get_controller(struct vsctl_context *ctx)
}
static void
+delete_controllers(struct ovsrec_controller **controllers,
+ size_t n_controllers)
+{
+ size_t i;
+
+ for (i = 0; i < n_controllers; i++) {
+ ovsrec_controller_delete(controllers[i]);
+ }
+}
+
+static void
cmd_del_controller(struct vsctl_context *ctx)
{
struct vsctl_info info;
get_info(ctx->ovs, &info);
- if (ctx->argc == 1) {
- if (info.ctrl) {
- ovsrec_controller_delete(info.ctrl);
- ovsrec_open_vswitch_set_controller(ctx->ovs, NULL);
+ if (ctx->argc == 1 || !strcmp(ctx->argv[1], "default")) {
+ if (info.n_ctrl) {
+ delete_controllers(info.ctrl, info.n_ctrl);
+ ovsrec_open_vswitch_set_controller(ctx->ovs, NULL, 0);
}
} else {
struct vsctl_bridge *br = find_real_bridge(&info, ctx->argv[1], true);
-
if (br->ctrl) {
- ovsrec_controller_delete(br->ctrl);
- ovsrec_bridge_set_controller(br->br_cfg, NULL);
+ delete_controllers(br->ctrl, br->n_ctrl);
+ ovsrec_bridge_set_controller(br->br_cfg, NULL, 0);
}
}
free_info(&info);
}
+static struct ovsrec_controller **
+insert_controllers(struct ovsdb_idl_txn *txn, char *targets[], size_t n)
+{
+ struct ovsrec_controller **controllers;
+ size_t i;
+
+ controllers = xmalloc(n * sizeof *controllers);
+ for (i = 0; i < n; i++) {
+ controllers[i] = ovsrec_controller_insert(txn);
+ ovsrec_controller_set_target(controllers[i], targets[i]);
+ }
+
+ return controllers;
+}
+
+static void
+set_default_controllers(struct vsctl_context *ctx, char *targets[], size_t n)
+{
+ struct ovsrec_controller **controllers;
+
+ delete_controllers(ctx->ovs->controller, ctx->ovs->n_controller);
+
+ controllers = insert_controllers(ctx->txn, targets, n);
+ ovsrec_open_vswitch_set_controller(ctx->ovs, controllers, n);
+ free(controllers);
+}
+
static void
cmd_set_controller(struct vsctl_context *ctx)
{
struct vsctl_info info;
- struct ovsrec_controller *ctrl;
get_info(ctx->ovs, &info);
if (ctx->argc == 2) {
- /* Set the controller in the "Open_vSwitch" table. */
- if (info.ctrl) {
- ovsrec_controller_delete(info.ctrl);
- }
- ctrl = ovsrec_controller_insert(ctx->txn);
- ovsrec_controller_set_target(ctrl, ctx->argv[1]);
- ovsrec_open_vswitch_set_controller(ctx->ovs, ctrl);
+ /* Set one controller in the "Open_vSwitch" table. */
+ set_default_controllers(ctx, &ctx->argv[1], 1);
+ } else if (!strcmp(ctx->argv[1], "default")) {
+ /* Set one or more controllers in the "Open_vSwitch" table. */
+ set_default_controllers(ctx, &ctx->argv[2], ctx->argc - 2);
} else {
- /* Set the controller for a particular bridge. */
+ /* Set one or more controllers for a particular bridge. */
struct vsctl_bridge *br = find_real_bridge(&info, ctx->argv[1], true);
+ struct ovsrec_controller **controllers;
+ size_t n;
- if (br->ctrl) {
- ovsrec_controller_delete(br->ctrl);
- }
- ctrl = ovsrec_controller_insert(ctx->txn);
- ovsrec_controller_set_target(ctrl, ctx->argv[2]);
- ovsrec_bridge_set_controller(br->br_cfg, ctrl);
+ delete_controllers(br->ctrl, br->n_ctrl);
+
+ n = ctx->argc - 2;
+ controllers = insert_controllers(ctx->txn, &ctx->argv[2], n);
+ ovsrec_bridge_set_controller(br->br_cfg, controllers, n);
+ free(controllers);
}
free_info(&info);
}
+static const char *
+get_fail_mode(struct ovsrec_controller **controllers, size_t n_controllers)
+{
+ const char *fail_mode;
+ size_t i;
+
+ fail_mode = NULL;
+ for (i = 0; i < n_controllers; i++) {
+ const char *s = controllers[i]->fail_mode;
+ if (s) {
+ if (!strcmp(s, "secure")) {
+ return s;
+ } else {
+ fail_mode = s;
+ }
+ }
+ }
+
+ return fail_mode;
+}
+
static void
cmd_get_fail_mode(struct vsctl_context *ctx)
{
@@ -1531,23 +1613,18 @@ cmd_get_fail_mode(struct vsctl_context *ctx)
get_info(ctx->ovs, &info);
- if (ctx->argc == 1) {
+ if (ctx->argc == 1 || !strcmp(ctx->argv[1], "default")) {
/* Return the fail-mode from the "Open_vSwitch" table */
- if (info.ctrl && info.ctrl->fail_mode) {
- fail_mode = info.ctrl->fail_mode;
- }
+ fail_mode = get_fail_mode(info.ctrl, info.n_ctrl);
} else {
/* Return the fail-mode for a particular bridge. */
struct vsctl_bridge *br = find_bridge(&info, ctx->argv[1], true);
- /* If no controller or fail-mode is explicitly defined for the
- * requested bridge, fallback to the "Open_vSwitch" table's
- * setting. */
- if (br->ctrl && br->ctrl->fail_mode) {
- fail_mode = br->ctrl->fail_mode;
- } else if (info.ctrl && info.ctrl->fail_mode) {
- fail_mode = info.ctrl->fail_mode;
- }
+ /* If no controller is defined for the requested bridge, fallback to
+ * the "Open_vSwitch" table's controller. */
+ fail_mode = (br->n_ctrl
+ ? get_fail_mode(br->ctrl, br->n_ctrl)
+ : get_fail_mode(info.ctrl, info.n_ctrl));
}
if (fail_mode && strlen(fail_mode)) {
@@ -1558,22 +1635,29 @@ cmd_get_fail_mode(struct vsctl_context *ctx)
}
static void
+set_fail_mode(struct ovsrec_controller **controllers, size_t n_controllers,
+ const char *fail_mode)
+{
+ size_t i;
+
+ for (i = 0; i < n_controllers; i++) {
+ ovsrec_controller_set_fail_mode(controllers[i], fail_mode);
+ }
+}
+
+static void
cmd_del_fail_mode(struct vsctl_context *ctx)
{
struct vsctl_info info;
get_info(ctx->ovs, &info);
- if (ctx->argc == 1) {
- if (info.ctrl && info.ctrl->fail_mode) {
- ovsrec_controller_set_fail_mode(info.ctrl, NULL);
- }
+ if (ctx->argc == 1 || !strcmp(ctx->argv[1], "default")) {
+ set_fail_mode(info.ctrl, info.n_ctrl, NULL);
} else {
struct vsctl_bridge *br = find_real_bridge(&info, ctx->argv[1], true);
- if (br->ctrl && br->ctrl->fail_mode) {
- ovsrec_controller_set_fail_mode(br->ctrl, NULL);
- }
+ set_fail_mode(br->ctrl, br->n_ctrl, NULL);
}
free_info(&info);
@@ -1583,29 +1667,36 @@ static void
cmd_set_fail_mode(struct vsctl_context *ctx)
{
struct vsctl_info info;
+ const char *bridge;
const char *fail_mode;
get_info(ctx->ovs, &info);
- fail_mode = (ctx->argc == 2) ? ctx->argv[1] : ctx->argv[2];
+ if (ctx->argc == 2) {
+ bridge = "default";
+ fail_mode = ctx->argv[1];
+ } else {
+ bridge = ctx->argv[1];
+ fail_mode = ctx->argv[2];
+ }
if (strcmp(fail_mode, "standalone") && strcmp(fail_mode, "secure")) {
vsctl_fatal("fail-mode must be \"standalone\" or \"secure\"");
}
- if (ctx->argc == 2) {
+ if (!strcmp(bridge, "default")) {
/* Set the fail-mode in the "Open_vSwitch" table. */
if (!info.ctrl) {
vsctl_fatal("no controller declared");
}
- ovsrec_controller_set_fail_mode(info.ctrl, fail_mode);
+ set_fail_mode(info.ctrl, info.n_ctrl, fail_mode);
} else {
- struct vsctl_bridge *br = find_real_bridge(&info, ctx->argv[1], true);
+ struct vsctl_bridge *br = find_real_bridge(&info, bridge, true);
if (!br->ctrl) {
vsctl_fatal("no controller declared for %s", br->name);
}
- ovsrec_controller_set_fail_mode(br->ctrl, fail_mode);
+ set_fail_mode(br->ctrl, br->n_ctrl, fail_mode);
}
free_info(&info);
@@ -2544,7 +2635,7 @@ static const struct vsctl_command_syntax all_commands[] = {
/* Controller commands. */
{"get-controller", 0, 1, cmd_get_controller, NULL, ""},
{"del-controller", 0, 1, cmd_del_controller, NULL, ""},
- {"set-controller", 1, 2, cmd_set_controller, NULL, ""},
+ {"set-controller", 1, INT_MAX, cmd_set_controller, NULL, ""},
{"get-fail-mode", 0, 1, cmd_get_fail_mode, NULL, ""},
{"del-fail-mode", 0, 1, cmd_del_fail_mode, NULL, ""},
{"set-fail-mode", 1, 2, cmd_set_fail_mode, NULL, ""},
diff --git a/vswitchd/bridge.c b/vswitchd/bridge.c
index b9e39c9c7..cf0212205 100644
--- a/vswitchd/bridge.c
+++ b/vswitchd/bridge.c
@@ -158,11 +158,6 @@ struct bridge {
bool sent_config_request; /* Successfully sent config request? */
uint8_t default_ea[ETH_ADDR_LEN]; /* Default MAC. */
- /* Support for remote controllers. */
- char *controller; /* NULL if there is no remote controller;
- * "discover" to do controller discovery;
- * otherwise a vconn name. */
-
/* OpenFlow switch processing. */
struct ofproto *ofproto; /* OpenFlow switch. */
@@ -208,9 +203,9 @@ static void bridge_destroy(struct bridge *);
static struct bridge *bridge_lookup(const char *name);
static unixctl_cb_func bridge_unixctl_dump_flows;
static int bridge_run_one(struct bridge *);
-static const struct ovsrec_controller *bridge_get_controller(
- const struct ovsrec_open_vswitch *ovs_cfg,
- const struct bridge *br);
+static size_t bridge_get_controllers(const struct ovsrec_open_vswitch *ovs_cfg,
+ const struct bridge *br,
+ struct ovsrec_controller ***controllersp);
static void bridge_reconfigure_one(const struct ovsrec_open_vswitch *,
struct bridge *);
static void bridge_reconfigure_controller(const struct ovsrec_open_vswitch *,
@@ -745,8 +740,10 @@ bridge_reconfigure(const struct ovsrec_open_vswitch *ovs_cfg)
/* Set sFlow configuration on this bridge. */
if (br->cfg->sflow) {
const struct ovsrec_sflow *sflow_cfg = br->cfg->sflow;
- const struct ovsrec_controller *ctrl;
+ struct ovsrec_controller **controllers;
struct ofproto_sflow_options oso;
+ size_t n_controllers;
+ size_t i;
memset(&oso, 0, sizeof oso);
@@ -771,8 +768,14 @@ bridge_reconfigure(const struct ovsrec_open_vswitch *ovs_cfg)
oso.sub_id = sflow_bridge_number++;
oso.agent_device = sflow_cfg->agent;
- ctrl = bridge_get_controller(ovs_cfg, br);
- oso.control_ip = ctrl ? ctrl->local_ip : NULL;
+ oso.control_ip = NULL;
+ n_controllers = bridge_get_controllers(ovs_cfg, br, &controllers);
+ for (i = 0; i < n_controllers; i++) {
+ if (controllers[i]->local_ip) {
+ oso.control_ip = controllers[i]->local_ip;
+ break;
+ }
+ }
ofproto_set_sflow(br->ofproto, &oso);
svec_destroy(&oso.targets);
@@ -1052,7 +1055,7 @@ bridge_wait(void)
LIST_FOR_EACH (br, struct bridge, node, &all_bridges) {
ofproto_wait(br->ofproto);
- if (br->controller) {
+ if (ofproto_has_controller(br->ofproto)) {
continue;
}
@@ -1182,7 +1185,6 @@ bridge_destroy(struct bridge *br)
}
dpif_close(br->dpif);
ofproto_destroy(br->ofproto);
- free(br->controller);
mac_learning_destroy(br->ml);
port_array_destroy(&br->ifaces);
free(br->ports);
@@ -1258,21 +1260,31 @@ bridge_run_one(struct bridge *br)
return error;
}
-static const struct ovsrec_controller *
-bridge_get_controller(const struct ovsrec_open_vswitch *ovs_cfg,
- const struct bridge *br)
+static size_t
+bridge_get_controllers(const struct ovsrec_open_vswitch *ovs_cfg,
+ const struct bridge *br,
+ struct ovsrec_controller ***controllersp)
{
- const struct ovsrec_controller *controller;
+ struct ovsrec_controller **controllers;
+ size_t n_controllers;
- controller = (br->cfg->controller ? br->cfg->controller
- : ovs_cfg->controller ? ovs_cfg->controller
- : NULL);
+ if (br->cfg->n_controller) {
+ controllers = br->cfg->controller;
+ n_controllers = br->cfg->n_controller;
+ } else {
+ controllers = ovs_cfg->controller;
+ n_controllers = ovs_cfg->n_controller;
+ }
- if (controller && !strcmp(controller->target, "none")) {
- return NULL;
+ if (n_controllers == 1 && !strcmp(controllers[0]->target, "none")) {
+ controllers = NULL;
+ n_controllers = 0;
}
- return controller;
+ if (controllersp) {
+ *controllersp = controllers;
+ }
+ return n_controllers;
}
static bool
@@ -1391,7 +1403,7 @@ bridge_reconfigure_one(const struct ovsrec_open_vswitch *ovs_cfg,
* user didn't specify one.
*
* XXX perhaps we should synthesize a port ourselves in this case. */
- if (bridge_get_controller(ovs_cfg, br)) {
+ if (bridge_get_controllers(ovs_cfg, br, NULL)) {
char local_name[IF_NAMESIZE];
int error;
@@ -1506,79 +1518,21 @@ static void
bridge_reconfigure_controller(const struct ovsrec_open_vswitch *ovs_cfg,
struct bridge *br)
{
- const struct ovsrec_controller *c;
+ struct ovsrec_controller **controllers;
+ size_t n_controllers;
- c = bridge_get_controller(ovs_cfg, br);
- if ((br->controller != NULL) != (c != NULL)) {
+ n_controllers = bridge_get_controllers(ovs_cfg, br, &controllers);
+ if (ofproto_has_controller(br->ofproto) != (n_controllers != 0)) {
ofproto_flush_flows(br->ofproto);
}
- free(br->controller);
- br->controller = c ? xstrdup(c->target) : NULL;
-
- if (c) {
- struct ofproto_controller oc;
-
- if (strcmp(c->target, "discover")) {
- struct iface *local_iface;
- struct in_addr ip;
-
- local_iface = bridge_get_local_iface(br);
- if (local_iface && c->local_ip && inet_aton(c->local_ip, &ip)) {
- struct netdev *netdev = local_iface->netdev;
- struct in_addr mask, gateway;
-
- if (!c->local_netmask || !inet_aton(c->local_netmask, &mask)) {
- mask.s_addr = 0;
- }
- if (!c->local_gateway
- || !inet_aton(c->local_gateway, &gateway)) {
- gateway.s_addr = 0;
- }
-
- netdev_turn_flags_on(netdev, NETDEV_UP, true);
- if (!mask.s_addr) {
- mask.s_addr = guess_netmask(ip.s_addr);
- }
- if (!netdev_set_in4(netdev, ip, mask)) {
- VLOG_INFO("bridge %s: configured IP address "IP_FMT", "
- "netmask "IP_FMT,
- br->name, IP_ARGS(&ip.s_addr),
- IP_ARGS(&mask.s_addr));
- }
-
- if (gateway.s_addr) {
- if (!netdev_add_router(netdev, gateway)) {
- VLOG_INFO("bridge %s: configured gateway "IP_FMT,
- br->name, IP_ARGS(&gateway.s_addr));
- }
- }
- }
- }
- oc.target = c->target;
- oc.max_backoff = c->max_backoff ? *c->max_backoff / 1000 : 8;
- oc.probe_interval = (c->inactivity_probe
- ? *c->inactivity_probe / 1000 : 5);
- oc.fail = (!c->fail_mode
- || !strcmp(c->fail_mode, "standalone")
- || !strcmp(c->fail_mode, "open")
- ? OFPROTO_FAIL_STANDALONE
- : OFPROTO_FAIL_SECURE);
- oc.band = (!c->connection_mode
- || !strcmp(c->connection_mode, "in-band")
- ? OFPROTO_IN_BAND
- : OFPROTO_OUT_OF_BAND);
- oc.accept_re = c->discover_accept_regex;
- oc.update_resolv_conf = c->discover_update_resolv_conf;
- oc.rate_limit = (c->controller_rate_limit
- ? *c->controller_rate_limit : 0);
- oc.burst_limit = (c->controller_burst_limit
- ? *c->controller_burst_limit : 0);
- ofproto_set_controller(br->ofproto, &oc);
- } else {
+ if (!n_controllers) {
union ofp_action action;
flow_t flow;
+ /* Clear out controllers. */
+ ofproto_set_controllers(br->ofproto, NULL, 0);
+
/* Set up a flow that matches every packet and directs them to
* OFPP_NORMAL (which goes to us). */
memset(&action, 0, sizeof action);
@@ -1587,8 +1541,76 @@ bridge_reconfigure_controller(const struct ovsrec_open_vswitch *ovs_cfg,
action.output.port = htons(OFPP_NORMAL);
memset(&flow, 0, sizeof flow);
ofproto_add_flow(br->ofproto, &flow, OVSFW_ALL, 0, &action, 1, 0);
+ } else {
+ struct ofproto_controller *ocs;
+ size_t i;
+
+ ocs = xmalloc(n_controllers * sizeof *ocs);
+ for (i = 0; i < n_controllers; i++) {
+ struct ovsrec_controller *c = controllers[i];
+ struct ofproto_controller *oc = &ocs[i];
+
+ if (strcmp(c->target, "discover")) {
+ struct iface *local_iface;
+ struct in_addr ip;
+
+ local_iface = bridge_get_local_iface(br);
+ if (local_iface && c->local_ip
+ && inet_aton(c->local_ip, &ip)) {
+ struct netdev *netdev = local_iface->netdev;
+ struct in_addr mask, gateway;
+
+ if (!c->local_netmask
+ || !inet_aton(c->local_netmask, &mask)) {
+ mask.s_addr = 0;
+ }
+ if (!c->local_gateway
+ || !inet_aton(c->local_gateway, &gateway)) {
+ gateway.s_addr = 0;
+ }
+
+ netdev_turn_flags_on(netdev, NETDEV_UP, true);
+ if (!mask.s_addr) {
+ mask.s_addr = guess_netmask(ip.s_addr);
+ }
+ if (!netdev_set_in4(netdev, ip, mask)) {
+ VLOG_INFO("bridge %s: configured IP address "IP_FMT", "
+ "netmask "IP_FMT,
+ br->name, IP_ARGS(&ip.s_addr),
+ IP_ARGS(&mask.s_addr));
+ }
+
+ if (gateway.s_addr) {
+ if (!netdev_add_router(netdev, gateway)) {
+ VLOG_INFO("bridge %s: configured gateway "IP_FMT,
+ br->name, IP_ARGS(&gateway.s_addr));
+ }
+ }
+ }
+ }
- ofproto_set_controller(br->ofproto, NULL);
+ oc->target = c->target;
+ oc->max_backoff = c->max_backoff ? *c->max_backoff / 1000 : 8;
+ oc->probe_interval = (c->inactivity_probe
+ ? *c->inactivity_probe / 1000 : 5);
+ oc->fail = (!c->fail_mode
+ || !strcmp(c->fail_mode, "standalone")
+ || !strcmp(c->fail_mode, "open")
+ ? OFPROTO_FAIL_STANDALONE
+ : OFPROTO_FAIL_SECURE);
+ oc->band = (!c->connection_mode
+ || !strcmp(c->connection_mode, "in-band")
+ ? OFPROTO_IN_BAND
+ : OFPROTO_OUT_OF_BAND);
+ oc->accept_re = c->discover_accept_regex;
+ oc->update_resolv_conf = c->discover_update_resolv_conf;
+ oc->rate_limit = (c->controller_rate_limit
+ ? *c->controller_rate_limit : 0);
+ oc->burst_limit = (c->controller_burst_limit
+ ? *c->controller_burst_limit : 0);
+ }
+ ofproto_set_controllers(br->ofproto, ocs, n_controllers);
+ free(ocs);
}
}
diff --git a/vswitchd/ovs-brcompatd.c b/vswitchd/ovs-brcompatd.c
index b5037056e..29503015b 100644
--- a/vswitchd/ovs-brcompatd.c
+++ b/vswitchd/ovs-brcompatd.c
@@ -661,8 +661,8 @@ del_bridge(struct ovsdb_idl *idl,
if (br->sflow) {
ovsrec_sflow_delete(br->sflow);
}
- if (br->controller) {
- ovsrec_controller_delete(br->controller);
+ for (i = 0; i < br->n_controller; i++) {
+ ovsrec_controller_delete(br->controller[i]);
}
/* Remove 'br' from the vswitch's list of bridges. */
diff --git a/vswitchd/vswitch.ovsschema b/vswitchd/vswitch.ovsschema
index 8661875ff..9e3573fb5 100644
--- a/vswitchd/vswitch.ovsschema
+++ b/vswitchd/vswitch.ovsschema
@@ -9,7 +9,7 @@
"controller": {
"type": {"key": {"type": "uuid",
"refTable": "Controller"},
- "min": 0, "max": 1}},
+ "min": 0, "max": "unlimited"}},
"managers": {
"type": {"key": "string", "min": 0, "max": "unlimited"}},
"ssl": {
@@ -52,7 +52,7 @@
"controller": {
"type": {"key": {"type": "uuid",
"refTable": "Controller"},
- "min": 0, "max": 1}},
+ "min": 0, "max": "unlimited"}},
"other_config": {
"type": {"key": "string", "value": "string", "min": 0, "max": "unlimited"}},
"external_ids": {
diff --git a/vswitchd/vswitch.xml b/vswitchd/vswitch.xml
index 2aba34fdf..500a0f967 100644
--- a/vswitchd/vswitch.xml
+++ b/vswitchd/vswitch.xml
@@ -16,7 +16,7 @@
</column>
<column name="controller">
- Default <ref table="Controller"/> used by bridges. May be
+ Default OpenFlow <ref table="Controller"/> set used by bridges. May be
overridden on a per-bridge basis by the <ref table="Bridge"
column="controller"/> column in <ref table="Bridge"/>.
</column>
@@ -104,10 +104,11 @@
<group title="OpenFlow Configuration">
<column name="controller">
- OpenFlow controller. If unset, defaults to that specified by
- <ref column="controller" table="Open_vSwitch"/> in the
- <ref table="Open_vSwitch"/> table. If the default is also unset, then
- no OpenFlow controller will be used.
+ OpenFlow controller set. If unset, defaults to the set of
+ controllers specified by <ref column="controller"
+ table="Open_vSwitch"/> in the <ref table="Open_vSwitch"/>
+ table. If the default is also unset, then no OpenFlow
+ controllers will be used.
</column>
<column name="datapath_id">
@@ -587,13 +588,20 @@
</table>
<table name="Controller" title="OpenFlow controller configuration.">
- An OpenFlow controller.
+ <p>An OpenFlow controller.</p>
+
+ <p>Open vSwitch permits a bridge to have any number of OpenFlow
+ controllers. When multiple controllers are configured, Open vSwitch
+ connects to all of them simultaneously. OpenFlow 1.0 does not specify
+ how multiple controllers coordinate in interacting with a single switch,
+ so more than one controller should be specified only if the controllers
+ are themselves designed to coordinate with each other.</p>
<group title="Core Features">
<column name="target">
- Connection method for controller.
- The following connection methods are currently
- supported:
+ <p>Connection method for controller.
+ The following connection methods are currently
+ supported:</p>
<dl>
<dt><code>ssl:<var>ip</var></code>[<code>:<var>port</var></code>]</dt>
<dd>
@@ -610,15 +618,58 @@
the given <var>ip</var>, which must be expressed as an IP address
(not a DNS name).</dd>
<dt><code>discover</code></dt>
- <dd>Enables controller discovery.</dd>
+ <dd>
+ <p>Enables controller discovery.</p>
+ <p>In controller discovery mode, Open vSwitch broadcasts a DHCP
+ request with vendor class identifier <code>OpenFlow</code> across
+ all of the bridge's network devices. It will accept any valid
+ DHCP reply that has the same vendor class identifier and includes
+ a vendor-specific option with code 1 whose contents are a string
+ specifying the location of the controller in the same format as
+ <ref column="target"/>.</p>
+ <p>The DHCP reply may also, optionally, include a vendor-specific
+ option with code 2 whose contents are a string specifying the URI
+ to the base of the OpenFlow PKI
+ (e.g. <code>http://192.168.0.1/openflow/pki</code>). This URI is
+ used only for bootstrapping the OpenFlow PKI at initial switch
+ setup; <code>ovs-vswitchd</code> does not use it at all.</p>
+ </dd>
<dt><code>none</code></dt>
<dd>Disables the controller.</dd>
</dl>
+ <p>When multiple controllers are configured for a single bridge, the
+ <ref column="target"/> values must be unique. Duplicate
+ <ref column="target"/> values yield unspecified results.</p>
</column>
<column name="connection_mode">
- Either <code>in-band</code> or <code>out-of-band</code>. If not
- specified, the default is implementation-specific.
+ <p>If it is specified, this setting must be one of the following
+ strings that describes how Open vSwitch contacts this OpenFlow
+ controller over the network:</p>
+
+ <dl>
+ <dt><code>in-band</code></dt>
+ <dd>In this mode, this controller's OpenFlow traffic travels over the
+ bridge associated with the controller. With this setting, Open
+ vSwitch allows traffic to and from the controller regardless of the
+ contents of the OpenFlow flow table. (Otherwise, Open vSwitch
+ would never be able to connect to the controller, because it did
+ not have a flow to enable it.) This is the most common connection
+ mode because it is not necessary to maintain two independent
+ networks.</dd>
+ <dt><code>out-of-band</code></dt>
+ <dd>In this mode, OpenFlow traffic uses a control network separate
+ from the bridge associated with this controller, that is, the
+ bridge does not use any of its own network devices to communicate
+ with the controller. The control network must be configured
+ separately, before or after <code>ovs-vswitchd</code> is started.
+ </dd>
+ </dl>
+
+ <p>If not specified, the default is implementation-specific. If
+ <ref column="target"/> is <code>discover</code>, the connection mode
+ is always treated as <code>in-band</code> regardless of the actual
+ setting.</p>
</column>
</group>
@@ -652,7 +703,7 @@
times the inactivity probe interval
(see <ref column="inactivity_probe"/>), then Open vSwitch
will take over responsibility for setting up flows. In
- this mode, Open vSwitch causes the datapath to act like an
+ this mode, Open vSwitch causes the bridge to act like an
ordinary MAC-learning switch. Open vSwitch will continue
to retry connecting to the controller in the background
and, when the connection succeeds, it will discontinue its
@@ -663,19 +714,20 @@
connecting to the controller forever.</dd>
</dl>
</p>
- <p>If this value is unset, the default is
- implementation-specific.</p>
+ <p>If this value is unset, the default is implementation-specific.</p>
+ <p>When more than one controller is configured,
+ <ref column="fail_mode"/> is considered only when none of the
+ configured controllers can be contacted. At that point, the bridge
+ enters secure mode if any of the controllers'
+ <ref column="fail_mode"/> is set to <code>secure</code>. Otherwise,
+ it enters standalone mode if at least one <ref column="fail_mode"/>
+ is set to <code>standalone</code>. If none of the
+ <ref column="fail_mode"/> values are set, the default is
+ implementation-defined.</p>
</column>
</group>
<group title="OpenFlow Rate Limiting">
- <column name="controller_burst_limit">
- In conjunction with <ref column="controller_rate_limit"/>,
- the maximum number of unused packet credits that the bridge will
- allow to accumulate, in packets. If not specified, the default
- is implementation-specific.
- </column>
-
<column name="controller_rate_limit">
<p>The maximum rate at which packets in unknown flows will be
forwarded to the OpenFlow controller, in packets per second. This
@@ -695,11 +747,21 @@
actual rate that packets are sent to the controller is up to
twice the specified rate.</p>
</column>
+
+ <column name="controller_burst_limit">
+ In conjunction with <ref column="controller_rate_limit"/>,
+ the maximum number of unused packet credits that the bridge will
+ allow to accumulate, in packets. If not specified, the default
+ is implementation-specific.
+ </column>
</group>
- <group title="Additional Configuration for Discovery">
+ <group title="Additional Discovery Configuration">
+ <p>These values are considered only when <ref column="target"/>
+ is <code>discover</code>.</p>
+
<column name="discover_accept_regex">
- If <ref column="target"/> is <code>discover</code>, a POSIX
+ A POSIX
extended regular expression against which the discovered controller
location is validated. The regular expression is implicitly
anchored at the beginning of the controller location string, as
@@ -708,8 +770,7 @@
</column>
<column name="discover_update_resolv_conf">
- If <ref column="target"/> is <code>discover</code>,
- whether to update <code>/etc/resolv.conf</code> when the
+ Whether to update <code>/etc/resolv.conf</code> when the
controller is discovered. If not specified, the default
is implementation-specific. Open vSwitch will only modify
<code>/etc/resolv.conf</code> if the DHCP response that it receives
@@ -717,20 +778,35 @@
</column>
</group>
- <group title="Additional Configuration without Discovery">
- <column name="local_gateway">
- If <ref column="target"/> is not <code>discover</code>, the IP
- address of the gateway to configure on the local port.
- </column>
+ <group title="Additional In-Band Configuration">
+ <p>These values are considered only in in-band control mode (see
+ <ref column="connection_mode"/>) and only when <ref column="target"/>
+ is not <code>discover</code>. (For controller discovery, the network
+ configuration obtained via DHCP is used instead.)</p>
+
+ <p>When multiple controllers are configured on a single bridge, there
+ should be only one set of unique values in these columns. If different
+ values are set for these columns in different controllers, the effect
+ is unspecified.</p>
<column name="local_ip">
- If <ref column="target"/> is not <code>discover</code>, the IP
- address to configure on the local port.
+ The IP address to configure on the local port,
+ e.g. <code>192.168.0.123</code>. If this value is unset, then
+ <ref column="local_netmask"/> and <ref column="local_gateway"/> are
+ ignored.
</column>
<column name="local_netmask">
- If <ref column="target"/> is not <code>discover</code>, the IP
- netmask to configure on the local port.
+ The IP netmask to configure on the local port,
+ e.g. <code>255.255.255.0</code>. If <ref column="local_ip"/> is set
+ but this value is unset, then the default is chosen based on whether
+ the IP address is class A, B, or C.
+ </column>
+
+ <column name="local_gateway">
+ The IP address of the gateway to configure on the local port, as a
+ string, e.g. <code>192.168.0.1</code>. Leave this column unset if
+ this network has no gateway.
</column>
</group>
</table>