in-band: Generalize the in-band code to arbitrary (IP,port) pairs.
[openvswitch] / ofproto / ofproto.c
index 0c11289cb02f96b850be9f4caf4c6dcb6532faf3..5f2f6f4c0c2ae60f0efede1d6f0f6492fea1048c 100644 (file)
@@ -200,9 +200,11 @@ struct ofconn {
     struct rconn_packet_counter *reply_counter;
 
     /* type == OFCONN_CONTROLLER only. */
+    enum nx_role role;           /* Role. */
     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. */
+    enum ofproto_band band;      /* In-band or out-of-band? */
 };
 
 /* We use OFPR_NO_MATCH and OFPR_ACTION as indexes into struct ofconn's
@@ -246,11 +248,13 @@ struct ofproto {
 
     /* Configuration. */
     struct switch_status *switch_status;
-    struct in_band *in_band;
     struct fail_open *fail_open;
     struct netflow *netflow;
     struct ofproto_sflow *sflow;
 
+    /* In-band control. */
+    struct in_band *in_band;
+    long long int next_in_band_update;
     /* Flow table. */
     struct classifier cls;
     bool need_revalidate;
@@ -465,6 +469,9 @@ update_controller(struct ofconn *ofconn, const struct ofproto_controller *c)
     int probe_interval;
     int i;
 
+    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;
@@ -513,17 +520,63 @@ find_controller_by_target(struct ofproto *ofproto, const char *target)
     return NULL;
 }
 
+static void
+update_in_band_remotes(struct ofproto *ofproto)
+{
+    const struct ofconn *ofconn;
+    struct sockaddr_in *addrs;
+    bool discovery;
+    size_t n_addrs;
+
+
+    addrs = xmalloc(hmap_count(&ofproto->controllers) * sizeof *addrs);
+    n_addrs = 0;
+
+    /* Add all the remotes. */
+    discovery = false;
+    HMAP_FOR_EACH (ofconn, struct ofconn, hmap_node, &ofproto->controllers) {
+        struct sockaddr_in *sin = &addrs[n_addrs];
+
+        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;
+        }
+    }
+
+    /* 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);
+        }
+        in_band_set_remotes(ofproto->in_band, addrs, n_addrs);
+        ofproto->next_in_band_update = time_msec() + 1000;
+    } else {
+        in_band_destroy(ofproto->in_band);
+        ofproto->in_band = NULL;
+    }
+
+    /* Clean up. */
+    free(addrs);
+}
+
 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;
 
     shash_init(&new_controllers);
@@ -536,8 +589,6 @@ ofproto_set_controllers(struct ofproto *p,
         }
     }
 
-    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,
@@ -549,14 +600,9 @@ ofproto_set_controllers(struct ofproto *p,
             ofconn_destroy(ofconn);
         } else {
             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;
             }
@@ -564,18 +610,7 @@ ofproto_set_controllers(struct ofproto *p,
     }
     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);
-        }
-    } else {
-        in_band_destroy(p->in_band);
-        p->in_band = NULL;
-    }
-    free(in_band_rconns);
+    update_in_band_remotes(p);
 
     if (!hmap_is_empty(&p->controllers)
         && fail_mode == OFPROTO_FAIL_STANDALONE) {
@@ -938,6 +973,9 @@ ofproto_run1(struct ofproto *p)
     }
 
     if (p->in_band) {
+        if (time_msec() >= p->next_in_band_update) {
+            update_in_band_remotes(p);
+        }
         in_band_run(p->in_band);
     }
 
@@ -1042,6 +1080,7 @@ ofproto_wait(struct ofproto *p)
         ofconn_wait(ofconn);
     }
     if (p->in_band) {
+        poll_timer_wait(p->next_in_band_update - time_msec());
         in_band_wait(p->in_band);
     }
     if (p->fail_open) {
@@ -1312,6 +1351,10 @@ send_port_status(struct ofproto *p, const struct ofport *ofport,
         struct ofp_port_status *ops;
         struct ofpbuf *b;
 
+        if (ofconn->role == NX_ROLE_SLAVE) {
+            continue;
+        }
+
         ops = make_openflow_xid(sizeof *ops, OFPT_PORT_STATUS, 0, &b);
         ops->reason = reason;
         ops->desc = ofport->opp;
@@ -1467,6 +1510,7 @@ ofconn_create(struct ofproto *p, struct rconn *rconn, enum ofconn_type type)
     list_push_back(&p->all_conns, &ofconn->node);
     ofconn->rconn = rconn;
     ofconn->type = type;
+    ofconn->role = NX_ROLE_OTHER;
     ofconn->packet_in_counter = rconn_packet_counter_create ();
     ofconn->pktbuf = NULL;
     ofconn->miss_send_len = 0;
@@ -2074,7 +2118,7 @@ handle_set_config(struct ofproto *p, struct ofconn *ofconn,
     }
     flags = ntohs(osc->flags);
 
-    if (ofconn->type == OFCONN_CONTROLLER) {
+    if (ofconn->type == OFCONN_CONTROLLER && ofconn->role != NX_ROLE_SLAVE) {
         switch (flags & OFPC_FRAG_MASK) {
         case OFPC_FRAG_NORMAL:
             dpif_set_drop_frags(p->dpif, false);
@@ -2421,6 +2465,29 @@ xlate_actions(const union ofp_action *in, size_t n_in,
     return 0;
 }
 
+/* Checks whether 'ofconn' is a slave controller.  If so, returns an OpenFlow
+ * error message code (composed with ofp_mkerr()) for the caller to propagate
+ * upward.  Otherwise, returns 0.
+ *
+ * 'oh' is used to make log messages more informative. */
+static int
+reject_slave_controller(struct ofconn *ofconn, const struct ofp_header *oh)
+{
+    if (ofconn->type == OFCONN_CONTROLLER && ofconn->role == NX_ROLE_SLAVE) {
+        static struct vlog_rate_limit perm_rl = VLOG_RATE_LIMIT_INIT(1, 5);
+        char *type_name;
+
+        type_name = ofp_message_type_to_string(oh->type);
+        VLOG_WARN_RL(&perm_rl, "rejecting %s message from slave controller",
+                     type_name);
+        free(type_name);
+
+        return ofp_mkerr(OFPET_BAD_REQUEST, OFPBRC_EPERM);
+    } else {
+        return 0;
+    }
+}
+
 static int
 handle_packet_out(struct ofproto *p, struct ofconn *ofconn,
                   struct ofp_header *oh)
@@ -2433,6 +2500,11 @@ handle_packet_out(struct ofproto *p, struct ofconn *ofconn,
     flow_t flow;
     int error;
 
+    error = reject_slave_controller(ofconn, oh);
+    if (error) {
+        return error;
+    }
+
     error = check_ofp_packet_out(oh, &payload, &n_actions, p->max_ports);
     if (error) {
         return error;
@@ -2494,12 +2566,17 @@ update_port_config(struct ofproto *p, struct ofport *port,
 }
 
 static int
-handle_port_mod(struct ofproto *p, struct ofp_header *oh)
+handle_port_mod(struct ofproto *p, struct ofconn *ofconn,
+                struct ofp_header *oh)
 {
     const struct ofp_port_mod *opm;
     struct ofport *port;
     int error;
 
+    error = reject_slave_controller(ofconn, oh);
+    if (error) {
+        return error;
+    }
     error = check_ofp_message(oh, OFPT_PORT_MOD, sizeof *opm);
     if (error) {
         return error;
@@ -3296,6 +3373,10 @@ handle_flow_mod(struct ofproto *p, struct ofconn *ofconn,
     size_t n_actions;
     int error;
 
+    error = reject_slave_controller(ofconn, &ofm->header);
+    if (error) {
+        return error;
+    }
     error = check_ofp_message_array(&ofm->header, OFPT_FLOW_MOD, sizeof *ofm,
                                     sizeof *ofm->actions, &n_actions);
     if (error) {
@@ -3358,6 +3439,59 @@ handle_tun_id_from_cookie(struct ofproto *p, struct nxt_tun_id_cookie *msg)
     return 0;
 }
 
+static int
+handle_role_request(struct ofproto *ofproto,
+                    struct ofconn *ofconn, struct nicira_header *msg)
+{
+    struct nx_role_request *nrr;
+    struct nx_role_request *reply;
+    struct ofpbuf *buf;
+    uint32_t role;
+
+    if (ntohs(msg->header.length) != sizeof *nrr) {
+        VLOG_WARN_RL(&rl, "received role request of length %zu (expected %zu)",
+                     ntohs(msg->header.length), sizeof *nrr);
+        return ofp_mkerr(OFPET_BAD_REQUEST, OFPBRC_BAD_LEN);
+    }
+    nrr = (struct nx_role_request *) msg;
+
+    if (ofconn->type != OFCONN_CONTROLLER) {
+        VLOG_WARN_RL(&rl, "ignoring role request on non-controller "
+                     "connection");
+        return ofp_mkerr(OFPET_BAD_REQUEST, OFPBRC_EPERM);
+    }
+
+    role = ntohl(nrr->role);
+    if (role != NX_ROLE_OTHER && role != NX_ROLE_MASTER
+        && role != NX_ROLE_SLAVE) {
+        VLOG_WARN_RL(&rl, "received request for unknown role %"PRIu32, role);
+
+        /* There's no good error code for this. */
+        return ofp_mkerr(OFPET_BAD_REQUEST, -1);
+    }
+
+    if (role == NX_ROLE_MASTER) {
+        struct ofconn *other;
+
+        HMAP_FOR_EACH (other, struct ofconn, hmap_node,
+                       &ofproto->controllers) {
+            if (other->role == NX_ROLE_MASTER) {
+                other->role = NX_ROLE_SLAVE;
+            }
+        }
+    }
+    ofconn->role = role;
+
+    reply = make_openflow_xid(sizeof *reply, OFPT_VENDOR, msg->header.xid,
+                              &buf);
+    reply->nxh.vendor = htonl(NX_VENDOR_ID);
+    reply->nxh.subtype = htonl(NXT_ROLE_REPLY);
+    reply->role = htonl(role);
+    queue_tx(buf, ofconn, ofconn->reply_counter);
+
+    return 0;
+}
+
 static int
 handle_vendor(struct ofproto *p, struct ofconn *ofconn, void *msg)
 {
@@ -3388,6 +3522,9 @@ handle_vendor(struct ofproto *p, struct ofconn *ofconn, void *msg)
 
     case NXT_TUN_ID_FROM_COOKIE:
         return handle_tun_id_from_cookie(p, msg);
+
+    case NXT_ROLE_REQUEST:
+        return handle_role_request(p, ofconn, msg);
     }
 
     return ofp_mkerr(OFPET_BAD_REQUEST, OFPBRC_BAD_SUBTYPE);
@@ -3440,7 +3577,7 @@ handle_openflow(struct ofconn *ofconn, struct ofproto *p,
         break;
 
     case OFPT_PORT_MOD:
-        error = handle_port_mod(p, oh);
+        error = handle_port_mod(p, ofconn, oh);
         break;
 
     case OFPT_FLOW_MOD:
@@ -3658,6 +3795,7 @@ uninstall_idle_flow(struct ofproto *ofproto, struct rule *rule)
         rule_uninstall(ofproto, rule);
     }
 }
+
 static void
 send_flow_removed(struct ofproto *p, struct rule *rule,
                   long long int now, uint8_t reason)
@@ -3674,7 +3812,8 @@ send_flow_removed(struct ofproto *p, struct rule *rule,
 
     prev = NULL;
     LIST_FOR_EACH (ofconn, struct ofconn, node, &p->all_conns) {
-        if (rule->send_flow_removed && rconn_is_connected(ofconn->rconn)) {
+        if (rule->send_flow_removed && rconn_is_connected(ofconn->rconn)
+            && ofconn->role != NX_ROLE_SLAVE) {
             if (prev) {
                 queue_tx(ofpbuf_clone(buf), prev, prev->reply_counter);
             } else {
@@ -3852,11 +3991,13 @@ send_packet_in(struct ofproto *ofproto, struct ofpbuf *packet)
 
     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);
+        if (ofconn->role != NX_ROLE_SLAVE) {
+            if (prev) {
+                pinsched_send(prev->schedulers[msg->type], msg->port,
+                              ofpbuf_clone(packet), do_send_packet_in, prev);
+            }
+            prev = ofconn;
         }
-        prev = ofconn;
     }
     if (prev) {
         pinsched_send(prev->schedulers[msg->type], msg->port,