* - "Service" 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.
+ *
+ * Currently, active (tcp, ssl, unix) connections are always "primary"
+ * connections and passive (ptcp, pssl, punix) connections are always "service"
+ * connections. There is no inherent reason for this, but it reflects the
+ * common case.
*/
enum ofconn_type {
OFCONN_PRIMARY, /* An ordinary OpenFlow controller. */
OFCONN_SERVICE /* A service connection, e.g. "ovs-ofctl". */
};
+/* A listener for incoming OpenFlow "service" connections. */
+struct ofservice {
+ struct hmap_node node; /* In struct ofproto's "services" hmap. */
+ struct pvconn *pvconn; /* OpenFlow connection listener. */
+
+ /* These are not used by ofservice directly. They are settings for
+ * accepted "struct ofconn"s from the pvconn. */
+ int probe_interval; /* Max idle time before probing, in seconds. */
+ int rate_limit; /* Max packet-in rate in packets per second. */
+ int burst_limit; /* Limit on accumulating packet credits. */
+};
+
+static struct ofservice *ofservice_lookup(struct ofproto *,
+ const char *target);
+static int ofservice_create(struct ofproto *,
+ const struct ofproto_controller *);
+static void ofservice_reconfigure(struct ofservice *,
+ const struct ofproto_controller *);
+static void ofservice_destroy(struct ofproto *, struct ofservice *);
+
/* An OpenFlow connection. */
struct ofconn {
struct ofproto *ofproto; /* The ofproto that owns this connection. */
static void ofconn_wait(struct ofconn *);
static bool ofconn_receives_async_msgs(const struct ofconn *);
static char *ofconn_make_name(const struct ofproto *, const char *target);
+static void ofconn_set_rate_limit(struct ofconn *, int rate, int burst);
static void queue_tx(struct ofpbuf *msg, const struct ofconn *ofconn,
struct rconn_packet_counter *counter);
struct hmap controllers; /* Controller "struct ofconn"s. */
struct list all_conns; /* Contains "struct ofconn"s. */
enum ofproto_fail_mode fail_mode;
- struct pvconn **listeners;
- size_t n_listeners;
+
+ /* OpenFlow listeners. */
+ struct hmap services; /* Contains "struct ofservice"s. */
struct pvconn **snoops;
size_t n_snoops;
/* Initialize OpenFlow connections. */
list_init(&p->all_conns);
hmap_init(&p->controllers);
- p->listeners = NULL;
- p->n_listeners = 0;
+ hmap_init(&p->services);
p->snoops = NULL;
p->n_snoops = 0;
static void
update_controller(struct ofconn *ofconn, const struct ofproto_controller *c)
{
- struct ofproto *ofproto = ofconn->ofproto;
int probe_interval;
- int i;
ofconn->band = (is_in_band_controller(c)
? OFPROTO_IN_BAND : OFPROTO_OUT_OF_BAND);
discovery_set_accept_controller_re(ofconn->discovery, c->accept_re);
}
- for (i = 0; i < N_SCHEDULERS; i++) {
- struct pinsched **s = &ofconn->schedulers[i];
-
- if (c->rate_limit > 0) {
- if (!*s) {
- *s = pinsched_create(c->rate_limit, c->burst_limit,
- ofproto->switch_status);
- } else {
- pinsched_set_limits(*s, c->rate_limit, c->burst_limit);
- }
- } else {
- pinsched_destroy(*s);
- *s = NULL;
- }
- }
+ ofconn_set_rate_limit(ofconn, c->rate_limit, c->burst_limit);
}
static const char *
size_t n_controllers)
{
struct shash new_controllers;
- struct ofconn *ofconn, *next;
+ struct ofconn *ofconn, *next_ofconn;
+ struct ofservice *ofservice, *next_ofservice;
bool ss_exists;
size_t i;
+ /* Create newly configured controllers and services.
+ * Create a name to ofproto_controller mapping in 'new_controllers'. */
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);
+ if (!vconn_verify_name(c->target) || !strcmp(c->target, "discover")) {
+ if (!find_controller_by_target(p, c->target)) {
+ add_controller(p, c);
+ }
+ } else if (!pvconn_verify_name(c->target)) {
+ if (!ofservice_lookup(p, c->target) && ofservice_create(p, c)) {
+ continue;
+ }
+ } else {
+ VLOG_WARN_RL(&rl, "%s: unsupported controller \"%s\"",
+ dpif_name(p->dpif), c->target);
+ continue;
}
+
+ shash_add_once(&new_controllers, c->target, &controllers[i]);
}
+ /* Delete controllers that are no longer configured.
+ * Update configuration of all now-existing controllers. */
ss_exists = false;
- HMAP_FOR_EACH_SAFE (ofconn, next, struct ofconn, hmap_node,
+ HMAP_FOR_EACH_SAFE (ofconn, next_ofconn, struct ofconn, hmap_node,
&p->controllers) {
struct ofproto_controller *c;
}
}
}
+
+ /* Delete services that are no longer configured.
+ * Update configuration of all now-existing services. */
+ HMAP_FOR_EACH_SAFE (ofservice, next_ofservice, struct ofservice, node,
+ &p->services) {
+ struct ofproto_controller *c;
+
+ c = shash_find_data(&new_controllers,
+ pvconn_get_name(ofservice->pvconn));
+ if (!c) {
+ ofservice_destroy(p, ofservice);
+ } else {
+ ofservice_reconfigure(ofservice, c);
+ }
+ }
+
shash_destroy(&new_controllers);
update_in_band_remotes(p);
-
update_fail_open(p);
if (!hmap_is_empty(&p->controllers) && !ss_exists) {
return retval;
}
-int
-ofproto_set_listeners(struct ofproto *ofproto, const struct svec *listeners)
-{
- return set_pvconns(&ofproto->listeners, &ofproto->n_listeners, listeners);
-}
-
int
ofproto_set_snoops(struct ofproto *ofproto, const struct svec *snoops)
{
}
bool
-ofproto_has_controller(const struct ofproto *ofproto)
+ofproto_has_primary_controller(const struct ofproto *ofproto)
{
return !hmap_is_empty(&ofproto->controllers);
}
return p->fail_mode;
}
-void
-ofproto_get_listeners(const struct ofproto *ofproto, struct svec *listeners)
-{
- size_t i;
-
- for (i = 0; i < ofproto->n_listeners; i++) {
- svec_add(listeners, pvconn_get_name(ofproto->listeners[i]));
- }
-}
-
void
ofproto_get_snoops(const struct ofproto *ofproto, struct svec *snoops)
{
void
ofproto_destroy(struct ofproto *p)
{
+ struct ofservice *ofservice, *next_ofservice;
struct ofconn *ofconn, *next_ofconn;
struct ofport *ofport;
unsigned int port_no;
netflow_destroy(p->netflow);
ofproto_sflow_destroy(p->sflow);
- for (i = 0; i < p->n_listeners; i++) {
- pvconn_close(p->listeners[i]);
+ HMAP_FOR_EACH_SAFE (ofservice, next_ofservice, struct ofservice, node,
+ &p->services) {
+ ofservice_destroy(p, ofservice);
}
- free(p->listeners);
+ hmap_destroy(&p->services);
for (i = 0; i < p->n_snoops; i++) {
pvconn_close(p->snoops[i]);
ofproto_run1(struct ofproto *p)
{
struct ofconn *ofconn, *next_ofconn;
+ struct ofservice *ofservice;
char *devname;
int error;
int i;
fail_open_run(p->fail_open);
}
- for (i = 0; i < p->n_listeners; i++) {
+ HMAP_FOR_EACH (ofservice, struct ofservice, node, &p->services) {
struct vconn *vconn;
int retval;
- retval = pvconn_accept(p->listeners[i], OFP_VERSION, &vconn);
+ retval = pvconn_accept(ofservice->pvconn, OFP_VERSION, &vconn);
if (!retval) {
+ struct ofconn *ofconn;
struct rconn *rconn;
char *name;
- rconn = rconn_create(60, 0);
+ rconn = rconn_create(ofservice->probe_interval, 0);
name = ofconn_make_name(p, vconn_get_name(vconn));
rconn_connect_unreliably(rconn, vconn, name);
free(name);
- ofconn_create(p, rconn, OFCONN_SERVICE);
+ ofconn = ofconn_create(p, rconn, OFCONN_SERVICE);
+ ofconn_set_rate_limit(ofconn, ofservice->rate_limit,
+ ofservice->burst_limit);
} else if (retval != EAGAIN) {
VLOG_WARN_RL(&rl, "accept failed (%s)", strerror(retval));
}
void
ofproto_wait(struct ofproto *p)
{
+ struct ofservice *ofservice;
struct ofconn *ofconn;
size_t i;
} else if (p->next_expiration != LLONG_MAX) {
poll_timer_wait_until(p->next_expiration);
}
- for (i = 0; i < p->n_listeners; i++) {
- pvconn_wait(p->listeners[i]);
+ HMAP_FOR_EACH (ofservice, struct ofservice, node, &p->services) {
+ pvconn_wait(ofservice->pvconn);
}
for (i = 0; i < p->n_snoops; i++) {
pvconn_wait(p->snoops[i]);
{
return xasprintf("%s<->%s", dpif_base_name(ofproto->dpif), target);
}
+
+static void
+ofconn_set_rate_limit(struct ofconn *ofconn, int rate, int burst)
+{
+ int i;
+
+ for (i = 0; i < N_SCHEDULERS; i++) {
+ struct pinsched **s = &ofconn->schedulers[i];
+
+ if (rate > 0) {
+ if (!*s) {
+ *s = pinsched_create(rate, burst,
+ ofconn->ofproto->switch_status);
+ } else {
+ pinsched_set_limits(*s, rate, burst);
+ }
+ } else {
+ pinsched_destroy(*s);
+ *s = NULL;
+ }
+ }
+}
+\f
+static void
+ofservice_reconfigure(struct ofservice *ofservice,
+ const struct ofproto_controller *c)
+{
+ ofservice->probe_interval = c->probe_interval;
+ ofservice->rate_limit = c->rate_limit;
+ ofservice->burst_limit = c->burst_limit;
+}
+
+/* Creates a new ofservice in 'ofproto'. Returns 0 if successful, otherwise a
+ * positive errno value. */
+static int
+ofservice_create(struct ofproto *ofproto, const struct ofproto_controller *c)
+{
+ struct ofservice *ofservice;
+ struct pvconn *pvconn;
+ int error;
+
+ error = pvconn_open(c->target, &pvconn);
+ if (error) {
+ return error;
+ }
+
+ ofservice = xzalloc(sizeof *ofservice);
+ hmap_insert(&ofproto->services, &ofservice->node,
+ hash_string(c->target, 0));
+ ofservice->pvconn = pvconn;
+
+ ofservice_reconfigure(ofservice, c);
+
+ return 0;
+}
+
+static void
+ofservice_destroy(struct ofproto *ofproto, struct ofservice *ofservice)
+{
+ hmap_remove(&ofproto->services, &ofservice->node);
+ pvconn_close(ofservice->pvconn);
+ free(ofservice);
+}
+
+/* Finds and returns the ofservice within 'ofproto' that has the given
+ * 'target', or a null pointer if none exists. */
+static struct ofservice *
+ofservice_lookup(struct ofproto *ofproto, const char *target)
+{
+ struct ofservice *ofservice;
+
+ HMAP_FOR_EACH_WITH_HASH (ofservice, struct ofservice, node,
+ hash_string(target, 0), &ofproto->services) {
+ if (!strcmp(pvconn_get_name(ofservice->pvconn), target)) {
+ return ofservice;
+ }
+ }
+ return NULL;
+}
\f
/* Caller is responsible for initializing the 'cr' member of the returned
* rule. */
const char *mfr_desc, const char *hw_desc,
const char *sw_desc, const char *serial_desc,
const char *dp_desc);
-int ofproto_set_listeners(struct ofproto *, const struct svec *listeners);
int ofproto_set_snoops(struct ofproto *, const struct svec *snoops);
int ofproto_set_netflow(struct ofproto *,
const struct netflow_options *nf_options);
/* Configuration querying. */
uint64_t ofproto_get_datapath_id(const struct ofproto *);
-bool ofproto_has_controller(const struct ofproto *);
+bool ofproto_has_primary_controller(const struct ofproto *);
enum ofproto_fail_mode ofproto_get_fail_mode(const struct ofproto *);
void ofproto_get_listeners(const struct ofproto *, struct svec *);
void ofproto_get_snoops(const struct ofproto *, struct svec *);
{
const struct ofproto *ofproto = ofproto_;
uint64_t datapath_id;
- struct svec listeners;
- size_t i;
datapath_id = ofproto_get_datapath_id(ofproto);
if (datapath_id) {
status_reply_put(sr, "datapath-id=%016"PRIx64, datapath_id);
}
-
- svec_init(&listeners);
- ofproto_get_listeners(ofproto, &listeners);
- for (i = 0; i < listeners.n; i++) {
- status_reply_put(sr, "management%zu=%s", i, listeners.names[i]);
- }
- svec_destroy(&listeners);
}
static void
const char *dp_desc; /* Datapath description. */
/* Related vconns and network devices. */
- struct svec listeners; /* Listen for management connections. */
struct svec snoops; /* Listen for controller snooping conns. */
/* Failure behavior. */
}
ofproto_set_desc(ofproto, s.mfr_desc, s.hw_desc, s.sw_desc,
s.serial_desc, s.dp_desc);
- if (!s.listeners.n) {
- svec_add_nocopy(&s.listeners, xasprintf("punix:%s/%s.mgmt",
- ovs_rundir, s.dp_name));
- } else if (s.listeners.n == 1 && !strcmp(s.listeners.names[0], "none")) {
- svec_clear(&s.listeners);
- }
- error = ofproto_set_listeners(ofproto, &s.listeners);
- if (error) {
- ovs_fatal(error, "failed to configure management connections");
- }
error = ofproto_set_snoops(ofproto, &s.snoops);
if (error) {
ovs_fatal(error,
};
char *short_options = long_options_to_short_options(long_options);
struct ofproto_controller controller_opts;
+ struct svec controllers;
+ int i;
/* Set defaults that we can figure out before parsing options. */
controller_opts.target = NULL;
s->sw_desc = NULL;
s->serial_desc = NULL;
s->dp_desc = NULL;
- svec_init(&s->listeners);
+ svec_init(&controllers);
svec_init(&s->snoops);
s->max_idle = 0;
s->enable_stp = false;
break;
case 'l':
- svec_add(&s->listeners, optarg);
+ svec_add(&controllers, optarg);
break;
case OPT_SNOOP:
/* Local vconns. */
dp_parse_name(argv[0], &s->dp_name, &s->dp_type);
- /* Controllers. */
- s->n_controllers = argc > 1 ? argc - 1 : 1;
+ /* Figure out controller names. */
+ if (!controllers.n) {
+ svec_add_nocopy(&controllers,
+ xasprintf("punix:%s/%s.mgmt", ovs_rundir, s->dp_name));
+ }
+ for (i = 1; i < argc; i++) {
+ svec_add(&controllers, argv[i]);
+ }
+ if (argc < 2) {
+ svec_add(&controllers, "discover");
+ }
+
+ /* Set up controllers. */
+ s->n_controllers = controllers.n;
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];
+ s->controllers[i].target = controllers.names[i];
}
- } else {
- s->controllers[0] = controller_opts;
- s->controllers[0].target = "discover";
}
/* Sanity check. */
LIST_FOR_EACH (br, struct bridge, node, &all_bridges) {
ofproto_wait(br->ofproto);
- if (ofproto_has_controller(br->ofproto)) {
+ if (ofproto_has_primary_controller(br->ofproto)) {
continue;
}
bridge_reconfigure_one(struct bridge *br)
{
struct shash old_ports, new_ports;
- struct svec listeners, old_listeners;
struct svec snoops, old_snoops;
struct shash_node *node;
enum ofproto_fail_mode fail_mode;
|| !strcmp(br->cfg->fail_mode, "standalone")
? OFPROTO_FAIL_STANDALONE
: OFPROTO_FAIL_SECURE;
- if ((ofproto_get_fail_mode(br->ofproto) != fail_mode)
- && !ofproto_has_controller(br->ofproto)) {
+ if (ofproto_get_fail_mode(br->ofproto) != fail_mode
+ && !ofproto_has_primary_controller(br->ofproto)) {
ofproto_flush_flows(br->ofproto);
}
ofproto_set_fail_mode(br->ofproto, fail_mode);
* versa. (XXX Should we delete all flows if we are switching from one
* controller to another?) */
- /* Configure OpenFlow management listener. */
- svec_init(&listeners);
- svec_add_nocopy(&listeners, xasprintf("punix:%s/%s.mgmt",
- ovs_rundir, br->name));
- svec_init(&old_listeners);
- ofproto_get_listeners(br->ofproto, &old_listeners);
- if (!svec_equal(&listeners, &old_listeners)) {
- ofproto_set_listeners(br->ofproto, &listeners);
- }
- svec_destroy(&listeners);
- svec_destroy(&old_listeners);
-
/* Configure OpenFlow controller connection snooping. */
svec_init(&snoops);
svec_add_nocopy(&snoops, xasprintf("punix:%s/%s.snoop",
mirror_reconfigure(br);
}
+/* Initializes 'oc' appropriately as a management service controller for
+ * 'br'.
+ *
+ * The caller must free oc->target when it is no longer needed. */
+static void
+bridge_ofproto_controller_for_mgmt(const struct bridge *br,
+ struct ofproto_controller *oc)
+{
+ oc->target = xasprintf("punix:%s/%s.mgmt", ovs_rundir, br->name);
+ oc->max_backoff = 0;
+ oc->probe_interval = 60;
+ oc->band = OFPROTO_OUT_OF_BAND;
+ oc->accept_re = NULL;
+ oc->update_resolv_conf = false;
+ oc->rate_limit = 0;
+ oc->burst_limit = 0;
+}
+
+/* Converts ovsrec_controller 'c' into an ofproto_controller in 'oc'. */
+static void
+bridge_ofproto_controller_from_ovsrec(const struct ovsrec_controller *c,
+ struct ofproto_controller *oc)
+{
+ 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->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);
+}
+
+/* Configures the IP stack for 'br''s local interface properly according to the
+ * configuration in 'c'. */
+static void
+bridge_configure_local_iface_netdev(struct bridge *br,
+ struct ovsrec_controller *c)
+{
+ struct netdev *netdev;
+ struct in_addr mask, gateway;
+
+ struct iface *local_iface;
+ struct in_addr ip;
+
+ /* Controller discovery does its own TCP/IP configuration later. */
+ if (strcmp(c->target, "discover")) {
+ return;
+ }
+
+ /* If there's no local interface or no IP address, give up. */
+ local_iface = bridge_get_local_iface(br);
+ if (!local_iface || !c->local_ip || !inet_aton(c->local_ip, &ip)) {
+ return;
+ }
+
+ /* Bring up the local interface. */
+ netdev = local_iface->netdev;
+ netdev_turn_flags_on(netdev, NETDEV_UP, true);
+
+ /* Configure the IP address and netmask. */
+ if (!c->local_netmask
+ || !inet_aton(c->local_netmask, &mask)
+ || !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));
+ }
+
+ /* Configure the default gateway. */
+ if (c->local_gateway
+ && inet_aton(c->local_gateway, &gateway)
+ && gateway.s_addr) {
+ if (!netdev_add_router(netdev, gateway)) {
+ VLOG_INFO("bridge %s: configured gateway "IP_FMT,
+ br->name, IP_ARGS(&gateway.s_addr));
+ }
+ }
+}
+
static void
bridge_reconfigure_remotes(struct bridge *br,
const struct sockaddr_in *managers,
{
struct ovsrec_controller **controllers;
size_t n_controllers;
+ bool had_primary;
+
+ struct ofproto_controller *ocs;
+ size_t n_ocs;
+ size_t i;
ofproto_set_extra_in_band_remotes(br->ofproto, managers, n_managers);
+ had_primary = ofproto_has_primary_controller(br->ofproto);
n_controllers = bridge_get_controllers(br, &controllers);
- if (ofproto_has_controller(br->ofproto) != (n_controllers != 0)) {
- ofproto_flush_flows(br->ofproto);
- }
- if (!n_controllers) {
- union ofp_action action;
- flow_t flow;
+ ocs = xmalloc((n_controllers + 1) * sizeof *ocs);
+ n_ocs = 0;
- /* Clear out controllers. */
- ofproto_set_controllers(br->ofproto, NULL, 0);
-
- /* If there are no controllers and the bridge is in standalone
- * mode, set up a flow that matches every packet and directs
- * them to OFPP_NORMAL (which goes to us). Otherwise, the
- * switch is in secure mode and we won't pass any traffic until
- * a controller has been defined and it tells us to do so. */
- if (ofproto_get_fail_mode(br->ofproto) == OFPROTO_FAIL_STANDALONE) {
- memset(&action, 0, sizeof action);
- action.type = htons(OFPAT_OUTPUT);
- action.output.len = htons(sizeof action);
- 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;
+ bridge_ofproto_controller_for_mgmt(br, &ocs[n_ocs++]);
+ for (i = 0; i < n_controllers; i++) {
+ struct ovsrec_controller *c = controllers[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 (!strncmp(c->target, "punix:", 6)
+ || !strncmp(c->target, "unix:", 5)) {
+ static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 5);
- if (strcmp(c->target, "discover")) {
- struct iface *local_iface;
- struct in_addr ip;
+ /* Prevent remote ovsdb-server users from accessing arbitrary Unix
+ * domain sockets and overwriting arbitrary local files. */
+ VLOG_ERR_RL(&rl, "%s: not adding Unix domain socket controller "
+ "\"%s\" due to possibility for remote exploit",
+ dpif_name(br->dpif), c->target);
+ continue;
+ }
- 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;
+ bridge_configure_local_iface_netdev(br, c);
+ bridge_ofproto_controller_from_ovsrec(c, &ocs[n_ocs++]);
+ }
- 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;
- }
+ ofproto_set_controllers(br->ofproto, ocs, n_ocs);
+ free(ocs[0].target); /* From bridge_ofproto_controller_for_mgmt(). */
+ free(ocs);
- 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 (had_primary != ofproto_has_primary_controller(br->ofproto)) {
+ ofproto_flush_flows(br->ofproto);
+ }
- 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));
- }
- }
- }
- }
+ /* If there are no controllers and the bridge is in standalone
+ * mode, set up a flow that matches every packet and directs
+ * them to OFPP_NORMAL (which goes to us). Otherwise, the
+ * switch is in secure mode and we won't pass any traffic until
+ * a controller has been defined and it tells us to do so. */
+ if (!n_controllers
+ && ofproto_get_fail_mode(br->ofproto) == OFPROTO_FAIL_STANDALONE) {
+ union ofp_action action;
+ flow_t flow;
- 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->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);
+ memset(&action, 0, sizeof action);
+ action.type = htons(OFPAT_OUTPUT);
+ action.output.len = htons(sizeof action);
+ action.output.port = htons(OFPP_NORMAL);
+ memset(&flow, 0, sizeof flow);
+ ofproto_add_flow(br->ofproto, &flow, OVSFW_ALL, 0, &action, 1, 0);
}
}
<table name="Controller" title="OpenFlow controller configuration.">
<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>
+ <p>
+ Open vSwitch supports two kinds of OpenFlow controllers:
+ </p>
+
+ <dl>
+ <dt>Primary controllers</dt>
+ <dd>
+ <p>
+ This is the kind of controller envisioned by the OpenFlow 1.0
+ specification. Usually, a primary controller implements a network
+ policy by taking charge of the switch's flow table.
+ </p>
+
+ <p>
+ Open vSwitch initiates and maintains persistent connections to
+ primary controllers, retrying the connection each time it fails or
+ drops. The <ref table="Bridge" column="fail_mode"/> column in the
+ <ref table="Bridge"/> table applies to primary controllers.
+ </p>
+
+ <p>
+ Open vSwitch permits a bridge to have any number of primary
+ controllers. When multiple controllers are configured, Open
+ vSwitch connects to all of them simultaneously. Because
+ OpenFlow 1.0 does not specify how multiple controllers
+ coordinate in interacting with a single switch, more than
+ one primary controller should be specified only if the
+ controllers are themselves designed to coordinate with each
+ other. (The Nicira-defined <code>NXT_ROLE</code> OpenFlow
+ vendor extension may be useful for this.)
+ </p>
+ </dd>
+ <dt>Service controllers</dt>
+ <dd>
+ <p>
+ These kinds of OpenFlow controller connections are intended for
+ occasional support and maintenance use, e.g. with
+ <code>ovs-ofctl</code>. Usually a service controller connects only
+ briefly to inspect or modify some of a switch's state.
+ </p>
+
+ <p>
+ Open vSwitch listens for incoming connections from service
+ controllers. The service controllers initiate and, if necessary,
+ maintain the connections from their end. The <ref table="Bridge"
+ column="fail_mode"/> column in the <ref table="Bridge"/> table does
+ not apply to service controllers.
+ </p>
+
+ <p>
+ Open vSwitch supports configuring any number of service controllers.
+ </p>
+ </dd>
+ </dl>
+
+ <p>
+ The <ref column="target"/> determines the type of controller.
+ </p>
<group title="Core Features">
<column name="target">
- <p>Connection method for controller.
- The following connection methods are currently
- supported:</p>
+ <p>Connection method for controller.</p>
+ <p>
+ The following connection methods are currently supported for primary
+ controllers:
+ </p>
<dl>
<dt><code>ssl:<var>ip</var></code>[<code>:<var>port</var></code>]</dt>
<dd>
<p>The specified SSL <var>port</var> (default: 6633) on the host at
- the given <var>ip</var>, which must be expressed as an IP address
- (not a DNS name). The <ref table="Open_vSwitch" column="ssl"/>
- column in the <ref table="Open_vSwitch"/> must point to a valid
- SSL configuration when this form is used.</p>
+ the given <var>ip</var>, which must be expressed as an IP address
+ (not a DNS name). The <ref table="Open_vSwitch" column="ssl"/>
+ column in the <ref table="Open_vSwitch"/> table must point to a
+ valid SSL configuration when this form is used.</p>
<p>SSL support is an optional feature that is not always built as
part of Open vSwitch.</p>
</dd>
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>
+ The following connection methods are currently supported for service
+ controllers:
+ </p>
+ <dl>
+ <dt><code>pssl:</code>[<var>port</var>][<code>:<var>ip</var></code>]</dt>
+ <dd>
+ <p>
+ Listens for SSL connections on the specified TCP <var>port</var>
+ (default: 6633). If <var>ip</var>, which must be expressed as an
+ IP address (not a DNS name), is specified, then connections are
+ restricted to the specified local IP address.
+ </p>
+ <p>
+ The <ref table="Open_vSwitch" column="ssl"/> column in the <ref
+ table="Open_vSwitch"/> table must point to a valid SSL
+ configuration when this form is used.
+ </p>
+ <p>SSL support is an optional feature that is not always built as
+ part of Open vSwitch.</p>
+ </dd>
+ <dt><code>ptcp:</code>[<var>port</var>][<code>:<var>ip</var></code>]</dt>
+ <dd>
+ Listens for connections on the specified TCP <var>port</var>
+ (default: 6633). If <var>ip</var>, which must be expressed as an
+ IP address (not a DNS name), is specified, then connections are
+ restricted to the specified local IP address.
+ </dd>
</dl>
<p>When multiple controllers are configured for a single bridge, the
<ref column="target"/> values must be unique. Duplicate