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
/* 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;
+ struct sockaddr_in *extra_in_band_remotes;
+ size_t n_extra_remotes;
+
/* Flow table. */
struct classifier cls;
bool need_revalidate;
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;
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, 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;
+ }
+ }
+ 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);
+ }
+ 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);
}
}
- 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,
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;
}
}
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) {
}
}
+static bool
+any_extras_changed(const struct ofproto *ofproto,
+ const struct sockaddr_in *extras, size_t n)
+{
+ size_t i;
+
+ if (n != ofproto->n_extra_remotes) {
+ return true;
+ }
+
+ for (i = 0; i < n; i++) {
+ const struct sockaddr_in *old = &ofproto->extra_in_band_remotes[i];
+ const struct sockaddr_in *new = &extras[i];
+
+ if (old->sin_addr.s_addr != new->sin_addr.s_addr ||
+ old->sin_port != new->sin_port) {
+ return true;
+ }
+ }
+
+ return false;
+}
+
+/* Sets the 'n' TCP port addresses in 'extras' as ones to which 'ofproto''s
+ * in-band control should guarantee access, in the same way that in-band
+ * control guarantees access to OpenFlow controllers. */
+void
+ofproto_set_extra_in_band_remotes(struct ofproto *ofproto,
+ const struct sockaddr_in *extras, size_t n)
+{
+ if (!any_extras_changed(ofproto, extras, n)) {
+ return;
+ }
+
+ free(ofproto->extra_in_band_remotes);
+ ofproto->n_extra_remotes = n;
+ ofproto->extra_in_band_remotes = xmemdup(extras, n * sizeof *extras);
+
+ update_in_band_remotes(ofproto);
+}
+
void
ofproto_set_desc(struct ofproto *p,
const char *mfr_desc, const char *hw_desc,
in_band_destroy(p->in_band);
p->in_band = NULL;
+ free(p->extra_in_band_remotes);
ofproto_flush_flows(p);
classifier_destroy(&p->cls);
}
if (p->in_band) {
+ if (time_msec() >= p->next_in_band_update) {
+ update_in_band_remotes(p);
+ }
in_band_run(p->in_band);
}
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) {
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;
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;
}
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);
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)
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;
}
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;
static void
append_port_stat(struct ofport *port, uint16_t port_no, struct ofconn *ofconn,
- struct ofpbuf *msg)
+ struct ofpbuf **msgp)
{
struct netdev_stats stats;
struct ofp_port_stats *ops;
* netdev_get_stats() will log errors. */
netdev_get_stats(port->netdev, &stats);
- ops = append_stats_reply(sizeof *ops, ofconn, &msg);
+ ops = append_stats_reply(sizeof *ops, ofconn, msgp);
ops->port_no = htons(odp_port_to_ofp_port(port_no));
memset(ops->pad, 0, sizeof ops->pad);
ops->rx_packets = htonll(stats.rx_packets);
port = port_array_get(&p->ports,
ofp_port_to_odp_port(ntohs(psr->port_no)));
if (port) {
- append_port_stat(port, ntohs(psr->port_no), ofconn, msg);
+ append_port_stat(port, ntohs(psr->port_no), ofconn, &msg);
}
} else {
PORT_ARRAY_FOR_EACH (port, &p->ports, port_no) {
- append_port_stat(port, port_no, ofconn, msg);
+ append_port_stat(port, port_no, ofconn, &msg);
}
}
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) {
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)
{
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);
break;
case OFPT_PORT_MOD:
- error = handle_port_mod(p, oh);
+ error = handle_port_mod(p, ofconn, oh);
break;
case OFPT_FLOW_MOD:
rule_uninstall(ofproto, rule);
}
}
+
static void
send_flow_removed(struct ofproto *p, struct rule *rule,
long long int now, uint8_t reason)
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 {
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,