+
+ /* Force all active connections to reconnect, since there is no way to
+ * notify a controller that the datapath ID has changed. */
+ ofproto_reconnect_controllers(p);
+ }
+}
+
+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_PRIMARY);
+ ofconn->pktbuf = pktbuf_create();
+ ofconn->miss_send_len = OFP_DEFAULT_MISS_SEND_LEN;
+ if (discovery) {
+ ofconn->discovery = discovery;
+ } else {
+ char *name = ofconn_make_name(ofproto, c->target);
+ rconn_connect(ofconn->rconn, c->target, name);
+ free(name);
+ }
+ 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 probe_interval;
+
+ ofconn->band = (is_in_band_controller(c)
+ ? OFPROTO_IN_BAND : OFPROTO_OUT_OF_BAND);
+
+ rconn_set_max_backoff(ofconn->rconn, c->max_backoff);
+
+ probe_interval = c->probe_interval ? MAX(c->probe_interval, 5) : 0;
+ rconn_set_probe_interval(ofconn->rconn, probe_interval);
+
+ if (ofconn->discovery) {
+ discovery_set_update_resolv_conf(ofconn->discovery,
+ c->update_resolv_conf);
+ discovery_set_accept_controller_re(ofconn->discovery, c->accept_re);
+ }
+
+ ofconn_set_rate_limit(ofconn, c->rate_limit, c->burst_limit);
+}
+
+static const char *
+ofconn_get_target(const struct ofconn *ofconn)
+{
+ return ofconn->discovery ? "discover" : rconn_get_target(ofconn->rconn);
+}
+
+static struct ofconn *
+find_controller_by_target(struct ofproto *ofproto, const char *target)
+{
+ struct ofconn *ofconn;
+
+ HMAP_FOR_EACH_WITH_HASH (ofconn, hmap_node,
+ hash_string(target, 0), &ofproto->controllers) {
+ if (!strcmp(ofconn_get_target(ofconn), target)) {
+ return ofconn;
+ }
+ }
+ return NULL;
+}
+
+static void
+update_in_band_remotes(struct ofproto *ofproto)
+{
+ const struct ofconn *ofconn;
+ struct sockaddr_in *addrs;
+ size_t max_addrs, n_addrs;
+ bool discovery;
+ size_t i;
+
+ /* Allocate enough memory for as many remotes as we could possibly have. */
+ max_addrs = ofproto->n_extra_remotes + hmap_count(&ofproto->controllers);
+ addrs = xmalloc(max_addrs * sizeof *addrs);
+ n_addrs = 0;
+
+ /* Add all the remotes. */
+ discovery = false;
+ HMAP_FOR_EACH (ofconn, hmap_node, &ofproto->controllers) {
+ struct sockaddr_in *sin = &addrs[n_addrs];
+
+ if (ofconn->band == OFPROTO_OUT_OF_BAND) {
+ continue;
+ }
+
+ sin->sin_addr.s_addr = rconn_get_remote_ip(ofconn->rconn);
+ if (sin->sin_addr.s_addr) {
+ sin->sin_port = rconn_get_remote_port(ofconn->rconn);
+ n_addrs++;
+ }
+ if (ofconn->discovery) {
+ discovery = true;
+ }
+ }
+ for (i = 0; i < ofproto->n_extra_remotes; i++) {
+ addrs[n_addrs++] = ofproto->extra_in_band_remotes[i];
+ }
+
+ /* Create or update or destroy in-band.
+ *
+ * Ordinarily we only enable in-band if there's at least one remote
+ * address, but discovery needs the in-band rules for DHCP to be installed
+ * even before we know any remote addresses. */
+ if (n_addrs || discovery) {
+ if (!ofproto->in_band) {
+ in_band_create(ofproto, ofproto->dpif, ofproto->switch_status,
+ &ofproto->in_band);
+ }
+ if (ofproto->in_band) {
+ in_band_set_remotes(ofproto->in_band, addrs, n_addrs);
+ }
+ in_band_set_queue(ofproto->in_band, ofproto->in_band_queue);
+ ofproto->next_in_band_update = time_msec() + 1000;
+ } else {
+ in_band_destroy(ofproto->in_band);
+ ofproto->in_band = NULL;
+ }
+
+ /* Clean up. */
+ free(addrs);
+}
+
+static void
+update_fail_open(struct ofproto *p)
+{
+ struct ofconn *ofconn;
+
+ if (!hmap_is_empty(&p->controllers)
+ && p->fail_mode == OFPROTO_FAIL_STANDALONE) {
+ struct rconn **rconns;
+ size_t n;
+
+ if (!p->fail_open) {
+ p->fail_open = fail_open_create(p, p->switch_status);
+ }
+
+ n = 0;
+ rconns = xmalloc(hmap_count(&p->controllers) * sizeof *rconns);
+ HMAP_FOR_EACH (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;