/*
- * Copyright (c) 2009, 2010, 2011 Nicira Networks.
+ * Copyright (c) 2009, 2010, 2011, 2012 Nicira, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
#include "fail-open.h"
#include "in-band.h"
#include "odp-util.h"
+#include "ofp-actions.h"
+#include "ofp-msgs.h"
#include "ofp-util.h"
#include "ofpbuf.h"
+#include "ofproto-provider.h"
#include "pinsched.h"
#include "poll-loop.h"
#include "pktbuf.h"
-#include "private.h"
#include "rconn.h"
#include "shash.h"
+#include "simap.h"
+#include "stream.h"
#include "timeval.h"
#include "vconn.h"
#include "vlog.h"
/* An OpenFlow connection. */
struct ofconn {
- struct connmgr *connmgr; /* Connection's manager. */
+/* Configuration that persists from one connection to the next. */
+
struct list node; /* In struct connmgr's "all_conns" list. */
+ struct hmap_node hmap_node; /* In struct connmgr's "controllers" map. */
+
+ struct connmgr *connmgr; /* Connection's manager. */
struct rconn *rconn; /* OpenFlow connection. */
enum ofconn_type type; /* Type. */
- enum nx_flow_format flow_format; /* Currently selected flow format. */
- bool flow_mod_table_id; /* NXT_FLOW_MOD_TABLE_ID enabled? */
+ enum ofproto_band band; /* In-band or out-of-band? */
+ bool enable_async_msgs; /* Initially enable async messages? */
+
+/* State that should be cleared from one connection to the next. */
+
+ /* OpenFlow state. */
+ enum nx_role role; /* Role. */
+ enum ofputil_protocol protocol; /* Current protocol variant. */
+ enum nx_packet_in_format packet_in_format; /* OFPT_PACKET_IN format. */
/* Asynchronous flow table operation support. */
struct list opgroups; /* Contains pending "ofopgroups", if any. */
struct pinsched *schedulers[N_SCHEDULERS];
struct pktbuf *pktbuf; /* OpenFlow packet buffers. */
int miss_send_len; /* Bytes to send of buffered packets. */
+ uint16_t controller_id; /* Connection controller ID. */
/* Number of OpenFlow messages queued on 'rconn' as replies to OpenFlow
* requests, and the maximum number before we stop reading OpenFlow
#define OFCONN_REPLY_MAX 100
struct rconn_packet_counter *reply_counter;
- /* type == OFCONN_PRIMARY only. */
- enum nx_role role; /* Role. */
- struct hmap_node hmap_node; /* In struct connmgr's "controllers" map. */
- enum ofproto_band band; /* In-band or out-of-band? */
+ /* Asynchronous message configuration in each possible roles.
+ *
+ * A 1-bit enables sending an asynchronous message for one possible reason
+ * that the message might be generated, a 0-bit disables it. */
+ uint32_t master_async_config[OAM_N_TYPES]; /* master, other */
+ uint32_t slave_async_config[OAM_N_TYPES]; /* slave */
+
+ /* Flow monitors. */
+ struct hmap monitors; /* Contains "struct ofmonitor"s. */
+ struct list updates; /* List of "struct ofpbuf"s. */
+ bool sent_abbrev_update; /* Does 'updates' contain NXFME_ABBREV? */
+ struct rconn_packet_counter *monitor_counter;
+ uint64_t monitor_paused;
};
static struct ofconn *ofconn_create(struct connmgr *, struct rconn *,
- enum ofconn_type);
+ enum ofconn_type, bool enable_async_msgs);
static void ofconn_destroy(struct ofconn *);
+static void ofconn_flush(struct ofconn *);
static void ofconn_reconfigure(struct ofconn *,
const struct ofproto_controller *);
static void ofconn_set_rate_limit(struct ofconn *, int rate, int burst);
-static bool ofconn_receives_async_msgs(const struct ofconn *);
-
static void ofconn_send(const struct ofconn *, struct ofpbuf *,
struct rconn_packet_counter *);
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. */
+ bool enable_async_msgs; /* Initially enable async messages? */
+ uint8_t dscp; /* DSCP Value for controller connection */
};
static void ofservice_reconfigure(struct ofservice *,
const struct ofproto_controller *);
-static int ofservice_create(struct connmgr *, const char *target);
+static int ofservice_create(struct connmgr *mgr, const char *target,
+ uint32_t allowed_versions, uint8_t dscp);
static void ofservice_destroy(struct connmgr *, struct ofservice *);
static struct ofservice *ofservice_lookup(struct connmgr *,
const char *target);
/* 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;
int in_band_queue;
static void update_in_band_remotes(struct connmgr *);
static void add_snooper(struct connmgr *, struct vconn *);
+static void ofmonitor_run(struct connmgr *);
+static void ofmonitor_wait(struct connmgr *);
/* Creates and returns a new connection manager owned by 'ofproto'. 'name' is
* a name for the ofproto suitable for using in log messages.
mgr->fail_mode = OFPROTO_FAIL_SECURE;
mgr->in_band = NULL;
- mgr->next_in_band_update = LLONG_MAX;
mgr->extra_in_band_remotes = NULL;
mgr->n_extra_remotes = 0;
mgr->in_band_queue = -1;
size_t i;
if (handle_openflow && mgr->in_band) {
- if (time_msec() >= mgr->next_in_band_update) {
- update_in_band_remotes(mgr);
+ if (!in_band_run(mgr->in_band)) {
+ in_band_destroy(mgr->in_band);
+ mgr->in_band = NULL;
}
- in_band_run(mgr->in_band);
}
LIST_FOR_EACH_SAFE (ofconn, next_ofconn, node, &mgr->all_conns) {
ofconn_run(ofconn, handle_openflow);
}
+ ofmonitor_run(mgr);
/* Fail-open maintenance. Do this after processing the ofconns since
* fail-open checks the status of the controller rconn. */
struct vconn *vconn;
int retval;
- retval = pvconn_accept(ofservice->pvconn, OFP_VERSION, &vconn);
+ retval = pvconn_accept(ofservice->pvconn, &vconn);
if (!retval) {
struct rconn *rconn;
char *name;
- rconn = rconn_create(ofservice->probe_interval, 0);
+ /* Passing default value for creation of the rconn */
+ rconn = rconn_create(ofservice->probe_interval, 0, ofservice->dscp,
+ vconn_get_allowed_versions(vconn));
name = ofconn_make_name(mgr, vconn_get_name(vconn));
rconn_connect_unreliably(rconn, vconn, name);
free(name);
- ofconn = ofconn_create(mgr, rconn, OFCONN_SERVICE);
+ ofconn = ofconn_create(mgr, rconn, OFCONN_SERVICE,
+ ofservice->enable_async_msgs);
ofconn_set_rate_limit(ofconn, ofservice->rate_limit,
ofservice->burst_limit);
} else if (retval != EAGAIN) {
struct vconn *vconn;
int retval;
- retval = pvconn_accept(mgr->snoops[i], OFP_VERSION, &vconn);
+ retval = pvconn_accept(mgr->snoops[i], &vconn);
if (!retval) {
add_snooper(mgr, vconn);
} else if (retval != EAGAIN) {
LIST_FOR_EACH (ofconn, node, &mgr->all_conns) {
ofconn_wait(ofconn, handling_openflow);
}
+ ofmonitor_wait(mgr);
if (handling_openflow && mgr->in_band) {
- poll_timer_wait_until(mgr->next_in_band_update);
in_band_wait(mgr->in_band);
}
if (handling_openflow && mgr->fail_open) {
}
}
+/* Adds some memory usage statistics for 'mgr' into 'usage', for use with
+ * memory_report(). */
+void
+connmgr_get_memory_usage(const struct connmgr *mgr, struct simap *usage)
+{
+ const struct ofconn *ofconn;
+ unsigned int packets = 0;
+ unsigned int ofconns = 0;
+
+ LIST_FOR_EACH (ofconn, node, &mgr->all_conns) {
+ int i;
+
+ ofconns++;
+
+ packets += rconn_count_txqlen(ofconn->rconn);
+ for (i = 0; i < N_SCHEDULERS; i++) {
+ packets += pinsched_count_txqlen(ofconn->schedulers[i]);
+ }
+ packets += pktbuf_count_packets(ofconn->pktbuf);
+ }
+ simap_increase(usage, "ofconns", ofconns);
+ simap_increase(usage, "packets", packets);
+}
+
/* Returns the ofproto that owns 'ofconn''s connmgr. */
struct ofproto *
ofconn_get_ofproto(const struct ofconn *ofconn)
\f
/* OpenFlow configuration. */
-static void add_controller(struct connmgr *, const char *target);
+static void add_controller(struct connmgr *, const char *target, uint8_t dscp,
+ uint32_t allowed_versions);
static struct ofconn *find_controller_by_target(struct connmgr *,
const char *target);
static void update_fail_open(struct connmgr *);
SHASH_FOR_EACH (node, info) {
struct ofproto_controller_info *cinfo = node->data;
while (cinfo->pairs.n) {
- free((char *) cinfo->pairs.values[--cinfo->pairs.n]);
+ free(CONST_CAST(char *, cinfo->pairs.values[--cinfo->pairs.n]));
}
free(cinfo);
}
void
connmgr_set_controllers(struct connmgr *mgr,
const struct ofproto_controller *controllers,
- size_t n_controllers)
+ size_t n_controllers, uint32_t allowed_versions)
{
bool had_controllers = connmgr_has_controllers(mgr);
struct shash new_controllers;
if (!vconn_verify_name(c->target)) {
if (!find_controller_by_target(mgr, c->target)) {
- add_controller(mgr, c->target);
+ VLOG_INFO("%s: added primary controller \"%s\"",
+ mgr->name, c->target);
+ add_controller(mgr, c->target, c->dscp, allowed_versions);
}
} else if (!pvconn_verify_name(c->target)) {
if (!ofservice_lookup(mgr, c->target)) {
- ofservice_create(mgr, c->target);
+ VLOG_INFO("%s: added service controller \"%s\"",
+ mgr->name, c->target);
+ ofservice_create(mgr, c->target, allowed_versions, c->dscp);
}
} else {
VLOG_WARN_RL(&rl, "%s: unsupported controller \"%s\"",
/* Delete controllers that are no longer configured.
* Update configuration of all now-existing controllers. */
HMAP_FOR_EACH_SAFE (ofconn, next_ofconn, hmap_node, &mgr->controllers) {
+ const char *target = ofconn_get_target(ofconn);
struct ofproto_controller *c;
- c = shash_find_data(&new_controllers, ofconn_get_target(ofconn));
+ c = shash_find_data(&new_controllers, target);
if (!c) {
+ VLOG_INFO("%s: removed primary controller \"%s\"",
+ mgr->name, target);
ofconn_destroy(ofconn);
} else {
ofconn_reconfigure(ofconn, c);
/* Delete services that are no longer configured.
* Update configuration of all now-existing services. */
HMAP_FOR_EACH_SAFE (ofservice, next_ofservice, node, &mgr->services) {
+ const char *target = pvconn_get_name(ofservice->pvconn);
struct ofproto_controller *c;
- c = shash_find_data(&new_controllers,
- pvconn_get_name(ofservice->pvconn));
+ c = shash_find_data(&new_controllers, target);
if (!c) {
+ VLOG_INFO("%s: removed service controller \"%s\"",
+ mgr->name, target);
ofservice_destroy(mgr, ofservice);
} else {
ofservice_reconfigure(ofservice, c);
/* Creates a new controller for 'target' in 'mgr'. update_controller() needs
* to be called later to finish the new ofconn's configuration. */
static void
-add_controller(struct connmgr *mgr, const char *target)
+add_controller(struct connmgr *mgr, const char *target, uint8_t dscp,
+ uint32_t allowed_versions)
{
char *name = ofconn_make_name(mgr, target);
struct ofconn *ofconn;
- ofconn = ofconn_create(mgr, rconn_create(5, 8), OFCONN_PRIMARY);
+ ofconn = ofconn_create(mgr, rconn_create(5, 8, dscp, allowed_versions),
+ OFCONN_PRIMARY, true);
ofconn->pktbuf = pktbuf_create();
- ofconn->miss_send_len = OFP_DEFAULT_MISS_SEND_LEN;
rconn_connect(ofconn->rconn, target, name);
hmap_insert(&mgr->controllers, &ofconn->hmap_node, hash_string(target, 0));
/* Add all the remotes. */
HMAP_FOR_EACH (ofconn, hmap_node, &mgr->controllers) {
struct sockaddr_in *sin = &addrs[n_addrs];
+ const char *target = rconn_get_target(ofconn->rconn);
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);
+ if (stream_parse_target_with_default_ports(target,
+ OFP_TCP_PORT,
+ OFP_SSL_PORT,
+ sin)) {
n_addrs++;
}
}
if (!mgr->in_band) {
in_band_create(mgr->ofproto, mgr->local_port_name, &mgr->in_band);
}
- if (mgr->in_band) {
- in_band_set_remotes(mgr->in_band, addrs, n_addrs);
- }
in_band_set_queue(mgr->in_band, mgr->in_band_queue);
- mgr->next_in_band_update = time_msec() + 1000;
} else {
- in_band_destroy(mgr->in_band);
- mgr->in_band = NULL;
+ /* in_band_run() needs a chance to delete any existing in-band flows.
+ * We will destroy mgr->in_band after it's done with that. */
+ }
+ if (mgr->in_band) {
+ in_band_set_remotes(mgr->in_band, addrs, n_addrs);
}
/* Clean up. */
SSET_FOR_EACH (name, sset) {
struct pvconn *pvconn;
int error;
-
- error = pvconn_open(name, &pvconn);
+ error = pvconn_open(name, 0, &pvconn, 0);
if (!error) {
pvconns[n_pvconns++] = pvconn;
} else {
ofconn->role = role;
}
-/* Returns the currently configured flow format for 'ofconn', one of NXFF_*.
+void
+ofconn_set_invalid_ttl_to_controller(struct ofconn *ofconn, bool enable)
+{
+ uint32_t bit = 1u << OFPR_INVALID_TTL;
+ if (enable) {
+ ofconn->master_async_config[OAM_PACKET_IN] |= bit;
+ } else {
+ ofconn->master_async_config[OAM_PACKET_IN] &= ~bit;
+ }
+}
+
+bool
+ofconn_get_invalid_ttl_to_controller(struct ofconn *ofconn)
+{
+ uint32_t bit = 1u << OFPR_INVALID_TTL;
+ return (ofconn->master_async_config[OAM_PACKET_IN] & bit) != 0;
+}
+
+/* Returns the currently configured protocol for 'ofconn', one of OFPUTIL_P_*.
*
- * The default, if no other format has been set, is NXFF_OPENFLOW10. */
-enum nx_flow_format
-ofconn_get_flow_format(struct ofconn *ofconn)
+ * The default, if no other format has been set, is OFPUTIL_P_OF10_STD. */
+enum ofputil_protocol
+ofconn_get_protocol(struct ofconn *ofconn)
{
- return ofconn->flow_format;
+ return ofconn->protocol;
}
-/* Sets the flow format for 'ofconn' to 'flow_format' (one of NXFF_*). */
+/* Sets the protocol for 'ofconn' to 'protocol' (one of OFPUTIL_P_*).
+ *
+ * (This doesn't actually send anything to accomplish this. Presumably the
+ * caller already did that.) */
void
-ofconn_set_flow_format(struct ofconn *ofconn, enum nx_flow_format flow_format)
+ofconn_set_protocol(struct ofconn *ofconn, enum ofputil_protocol protocol)
{
- ofconn->flow_format = flow_format;
+ ofconn->protocol = protocol;
}
-/* Returns true if the NXT_FLOW_MOD_TABLE_ID extension is enabled, false
- * otherwise.
+/* Returns the currently configured packet in format for 'ofconn', one of
+ * NXPIF_*.
*
- * By default the extension is not enabled. */
-bool
-ofconn_get_flow_mod_table_id(const struct ofconn *ofconn)
+ * The default, if no other format has been set, is NXPIF_OPENFLOW10. */
+enum nx_packet_in_format
+ofconn_get_packet_in_format(struct ofconn *ofconn)
+{
+ return ofconn->packet_in_format;
+}
+
+/* Sets the packet in format for 'ofconn' to 'packet_in_format' (one of
+ * NXPIF_*). */
+void
+ofconn_set_packet_in_format(struct ofconn *ofconn,
+ enum nx_packet_in_format packet_in_format)
{
- return ofconn->flow_mod_table_id;
+ ofconn->packet_in_format = packet_in_format;
}
-/* Enables or disables (according to 'enable') the NXT_FLOW_MOD_TABLE_ID
- * extension on 'ofconn'. */
+/* Sets the controller connection ID for 'ofconn' to 'controller_id'.
+ *
+ * The connection controller ID is used for OFPP_CONTROLLER and
+ * NXAST_CONTROLLER actions. See "struct nx_action_controller" for details. */
void
-ofconn_set_flow_mod_table_id(struct ofconn *ofconn, bool enable)
+ofconn_set_controller_id(struct ofconn *ofconn, uint16_t controller_id)
{
- ofconn->flow_mod_table_id = enable;
+ ofconn->controller_id = controller_id;
}
/* Returns the default miss send length for 'ofconn'. */
ofconn->miss_send_len = miss_send_len;
}
+void
+ofconn_set_async_config(struct ofconn *ofconn,
+ const uint32_t master_masks[OAM_N_TYPES],
+ const uint32_t slave_masks[OAM_N_TYPES])
+{
+ size_t size = sizeof ofconn->master_async_config;
+ memcpy(ofconn->master_async_config, master_masks, size);
+ memcpy(ofconn->slave_async_config, slave_masks, size);
+}
+
/* Sends 'msg' on 'ofconn', accounting it as a reply. (If there is a
* sufficient number of OpenFlow replies in-flight on a single ofconn, then the
* connmgr will stop accepting new OpenFlow requests on that ofconn until the
}
}
-/* Sends 'error', which should be an OpenFlow error created with
- * e.g. ofp_mkerr(), on 'ofconn', as a reply to 'request'. Only at most the
+/* Sends 'error' on 'ofconn', as a reply to 'request'. Only at most the
* first 64 bytes of 'request' are used. */
void
ofconn_send_error(const struct ofconn *ofconn,
- const struct ofp_header *request, int error)
+ const struct ofp_header *request, enum ofperr error)
{
- struct ofpbuf *msg = ofputil_encode_error_msg(error, request);
- if (msg) {
- ofconn_send_reply(ofconn, msg);
+ struct ofpbuf *reply;
+
+ reply = ofperr_encode_reply(error, request);
+ if (reply) {
+ static struct vlog_rate_limit err_rl = VLOG_RATE_LIMIT_INIT(10, 10);
+
+ if (!VLOG_DROP_INFO(&err_rl)) {
+ const char *type_name;
+ size_t request_len;
+ enum ofpraw raw;
+
+ request_len = ntohs(request->length);
+ type_name = (!ofpraw_decode_partial(&raw, request,
+ MIN(64, request_len))
+ ? ofpraw_get_name(raw)
+ : "invalid");
+
+ VLOG_INFO("%s: sending %s error reply to %s message",
+ rconn_get_name(ofconn->rconn), ofperr_to_string(error),
+ type_name);
+ }
+ ofconn_send_reply(ofconn, reply);
}
}
/* Same as pktbuf_retrieve(), using the pktbuf owned by 'ofconn'. */
-int
+enum ofperr
ofconn_pktbuf_retrieve(struct ofconn *ofconn, uint32_t id,
struct ofpbuf **bufferp, uint16_t *in_port)
{
return !list_is_empty(&ofconn->opgroups);
}
-/* Returns the number of pending opgroups on 'ofconn'. */
-size_t
-ofconn_n_pending_opgroups(const struct ofconn *ofconn)
-{
- return list_size(&ofconn->opgroups);
-}
-
/* Adds 'ofconn_node' to 'ofconn''s list of pending opgroups.
*
* If 'ofconn' is destroyed or its connection drops, then 'ofconn' will remove
}
static struct ofconn *
-ofconn_create(struct connmgr *mgr, struct rconn *rconn, enum ofconn_type type)
+ofconn_create(struct connmgr *mgr, struct rconn *rconn, enum ofconn_type type,
+ bool enable_async_msgs)
{
- struct ofconn *ofconn = xzalloc(sizeof *ofconn);
+ struct ofconn *ofconn;
+
+ ofconn = xzalloc(sizeof *ofconn);
ofconn->connmgr = mgr;
list_push_back(&mgr->all_conns, &ofconn->node);
ofconn->rconn = rconn;
ofconn->type = type;
- ofconn->flow_format = NXFF_OPENFLOW10;
- ofconn->flow_mod_table_id = false;
+ ofconn->enable_async_msgs = enable_async_msgs;
+
list_init(&ofconn->opgroups);
- ofconn->role = NX_ROLE_OTHER;
- ofconn->packet_in_counter = rconn_packet_counter_create ();
- ofconn->pktbuf = NULL;
- ofconn->miss_send_len = 0;
- ofconn->reply_counter = rconn_packet_counter_create ();
+
+ hmap_init(&ofconn->monitors);
+ list_init(&ofconn->updates);
+
+ ofconn_flush(ofconn);
+
return ofconn;
}
-/* Disassociates 'ofconn' from all of the ofopgroups that it initiated that
- * have not yet completed. (Those ofopgroups will still run to completion in
- * the usual way, but any errors that they run into will not be reported on any
- * OpenFlow channel.)
- *
- * Also discards any blocked operation on 'ofconn'. */
+/* Clears all of the state in 'ofconn' that should not persist from one
+ * connection to the next. */
static void
ofconn_flush(struct ofconn *ofconn)
{
+ struct ofmonitor *monitor, *next_monitor;
+ int i;
+
+ ofconn->role = NX_ROLE_OTHER;
+ ofconn->protocol = OFPUTIL_P_OF10_STD;
+ ofconn->packet_in_format = NXPIF_OPENFLOW10;
+
+ /* Disassociate 'ofconn' from all of the ofopgroups that it initiated that
+ * have not yet completed. (Those ofopgroups will still run to completion
+ * in the usual way, but any errors that they run into will not be reported
+ * on any OpenFlow channel.)
+ *
+ * Also discard any blocked operation on 'ofconn'. */
while (!list_is_empty(&ofconn->opgroups)) {
list_init(list_pop_front(&ofconn->opgroups));
}
ofpbuf_delete(ofconn->blocked);
ofconn->blocked = NULL;
+
+ rconn_packet_counter_destroy(ofconn->packet_in_counter);
+ ofconn->packet_in_counter = rconn_packet_counter_create();
+ for (i = 0; i < N_SCHEDULERS; i++) {
+ if (ofconn->schedulers[i]) {
+ int rate, burst;
+
+ pinsched_get_limits(ofconn->schedulers[i], &rate, &burst);
+ pinsched_destroy(ofconn->schedulers[i]);
+ ofconn->schedulers[i] = pinsched_create(rate, burst);
+ }
+ }
+ if (ofconn->pktbuf) {
+ pktbuf_destroy(ofconn->pktbuf);
+ ofconn->pktbuf = pktbuf_create();
+ }
+ ofconn->miss_send_len = (ofconn->type == OFCONN_PRIMARY
+ ? OFP_DEFAULT_MISS_SEND_LEN
+ : 0);
+ ofconn->controller_id = 0;
+
+ rconn_packet_counter_destroy(ofconn->reply_counter);
+ ofconn->reply_counter = rconn_packet_counter_create();
+
+ if (ofconn->enable_async_msgs) {
+ uint32_t *master = ofconn->master_async_config;
+ uint32_t *slave = ofconn->slave_async_config;
+
+ /* "master" and "other" roles get all asynchronous messages by default,
+ * except that the controller needs to enable nonstandard "packet-in"
+ * reasons itself. */
+ master[OAM_PACKET_IN] = (1u << OFPR_NO_MATCH) | (1u << OFPR_ACTION);
+ master[OAM_PORT_STATUS] = ((1u << OFPPR_ADD)
+ | (1u << OFPPR_DELETE)
+ | (1u << OFPPR_MODIFY));
+ master[OAM_FLOW_REMOVED] = ((1u << OFPRR_IDLE_TIMEOUT)
+ | (1u << OFPRR_HARD_TIMEOUT)
+ | (1u << OFPRR_DELETE));
+
+ /* "slave" role gets port status updates by default. */
+ slave[OAM_PACKET_IN] = 0;
+ slave[OAM_PORT_STATUS] = ((1u << OFPPR_ADD)
+ | (1u << OFPPR_DELETE)
+ | (1u << OFPPR_MODIFY));
+ slave[OAM_FLOW_REMOVED] = 0;
+ } else {
+ memset(ofconn->master_async_config, 0,
+ sizeof ofconn->master_async_config);
+ memset(ofconn->slave_async_config, 0,
+ sizeof ofconn->slave_async_config);
+ }
+
+ HMAP_FOR_EACH_SAFE (monitor, next_monitor, ofconn_node,
+ &ofconn->monitors) {
+ ofmonitor_destroy(monitor);
+ }
+ rconn_packet_counter_destroy(ofconn->monitor_counter);
+ ofconn->monitor_counter = rconn_packet_counter_create();
+ ofpbuf_list_delete(&ofconn->updates); /* ...but it should be empty. */
}
static void
rconn_packet_counter_destroy(ofconn->packet_in_counter);
rconn_packet_counter_destroy(ofconn->reply_counter);
pktbuf_destroy(ofconn->pktbuf);
+ rconn_packet_counter_destroy(ofconn->monitor_counter);
free(ofconn);
}
int probe_interval;
ofconn->band = c->band;
+ ofconn->enable_async_msgs = c->enable_async_msgs;
rconn_set_max_backoff(ofconn->rconn, c->max_backoff);
rconn_set_probe_interval(ofconn->rconn, probe_interval);
ofconn_set_rate_limit(ofconn, c->rate_limit, c->burst_limit);
+
+ /* If dscp value changed reconnect. */
+ if (c->dscp != rconn_get_dscp(ofconn->rconn)) {
+ rconn_set_dscp(ofconn->rconn, c->dscp);
+ rconn_reconnect(ofconn->rconn);
+ }
}
/* Returns true if it makes sense for 'ofconn' to receive and process OpenFlow
static bool
ofconn_may_recv(const struct ofconn *ofconn)
{
- int count = rconn_packet_counter_read (ofconn->reply_counter);
+ int count = ofconn->reply_counter->n_packets;
return (!ofconn->blocked || ofconn->retry) && count < OFCONN_REPLY_MAX;
}
}
}
-/* Returns true if 'ofconn' should receive asynchronous messages. */
+/* Returns true if 'ofconn' should receive asynchronous messages of the given
+ * OAM_* 'type' and 'reason', which should be a OFPR_* value for OAM_PACKET_IN,
+ * a OFPPR_* value for OAM_PORT_STATUS, or an OFPRR_* value for
+ * OAM_FLOW_REMOVED. Returns false if the message should not be sent on
+ * 'ofconn'. */
static bool
-ofconn_receives_async_msgs(const struct ofconn *ofconn)
+ofconn_receives_async_msg(const struct ofconn *ofconn,
+ enum ofconn_async_msg_type type,
+ unsigned int reason)
{
+ const uint32_t *async_config;
+
+ assert(reason < 32);
+ assert((unsigned int) type < OAM_N_TYPES);
+
if (!rconn_is_connected(ofconn->rconn)) {
return false;
- } else if (ofconn->type == OFCONN_PRIMARY) {
- /* Primary controllers always get asynchronous messages unless they
- * have configured themselves as "slaves". */
- return ofconn->role != NX_ROLE_SLAVE;
- } else {
+ }
+
+ /* Keep the following code in sync with the documentation in the
+ * "Asynchronous Messages" section in DESIGN. */
+
+ if (ofconn->type == OFCONN_SERVICE && !ofconn->miss_send_len) {
/* Service connections don't get asynchronous messages unless they have
* explicitly asked for them by setting a nonzero miss send length. */
- return ofconn->miss_send_len > 0;
+ return false;
+ }
+
+ async_config = (ofconn->role == NX_ROLE_SLAVE
+ ? ofconn->slave_async_config
+ : ofconn->master_async_config);
+ if (!(async_config[type] & (1u << reason))) {
+ return false;
}
+
+ return true;
}
/* Returns a human-readable name for an OpenFlow connection between 'mgr' and
ofconn_send(const struct ofconn *ofconn, struct ofpbuf *msg,
struct rconn_packet_counter *counter)
{
- update_openflow_length(msg);
- if (rconn_send(ofconn->rconn, msg, counter)) {
- ofpbuf_delete(msg);
- }
+ ofpmsg_update_length(msg);
+ rconn_send(ofconn->rconn, msg, counter);
}
\f
/* Sending asynchronous messages. */
-static void schedule_packet_in(struct ofconn *, struct ofputil_packet_in,
- const struct flow *, struct ofpbuf *rw_packet);
+static void schedule_packet_in(struct ofconn *, struct ofputil_packet_in);
/* Sends an OFPT_PORT_STATUS message with 'opp' and 'reason' to appropriate
* controllers managed by 'mgr'. */
void
-connmgr_send_port_status(struct connmgr *mgr, const struct ofp_phy_port *opp,
- uint8_t reason)
+connmgr_send_port_status(struct connmgr *mgr,
+ const struct ofputil_phy_port *pp, uint8_t reason)
{
/* XXX Should limit the number of queued port status change messages. */
+ struct ofputil_port_status ps;
struct ofconn *ofconn;
+ ps.reason = reason;
+ ps.desc = *pp;
LIST_FOR_EACH (ofconn, node, &mgr->all_conns) {
- struct ofp_port_status *ops;
- struct ofpbuf *b;
+ if (ofconn_receives_async_msg(ofconn, OAM_PORT_STATUS, reason)) {
+ struct ofpbuf *msg;
- /* Primary controllers, even slaves, should always get port status
- updates. Otherwise obey ofconn_receives_async_msgs(). */
- if (ofconn->type != OFCONN_PRIMARY
- && !ofconn_receives_async_msgs(ofconn)) {
- continue;
+ msg = ofputil_encode_port_status(&ps, ofconn->protocol);
+ ofconn_send(ofconn, msg, NULL);
}
-
- ops = make_openflow_xid(sizeof *ops, OFPT_PORT_STATUS, 0, &b);
- ops->reason = reason;
- ops->desc = *opp;
- ofconn_send(ofconn, b, NULL);
}
}
struct ofconn *ofconn;
LIST_FOR_EACH (ofconn, node, &mgr->all_conns) {
- struct ofpbuf *msg;
-
- if (!ofconn_receives_async_msgs(ofconn)) {
- continue;
+ if (ofconn_receives_async_msg(ofconn, OAM_FLOW_REMOVED, fr->reason)) {
+ struct ofpbuf *msg;
+
+ /* Account flow expirations as replies to OpenFlow requests. That
+ * works because preventing OpenFlow requests from being processed
+ * also prevents new flows from being added (and expiring). (It
+ * also prevents processing OpenFlow requests that would not add
+ * new flows, so it is imperfect.) */
+ msg = ofputil_encode_flow_removed(fr, ofconn->protocol);
+ ofconn_send_reply(ofconn, msg);
}
-
- /* Account flow expirations as replies to OpenFlow requests. That
- * works because preventing OpenFlow requests from being processed also
- * prevents new flows from being added (and expiring). (It also
- * prevents processing OpenFlow requests that would not add new flows,
- * so it is imperfect.) */
- msg = ofputil_encode_flow_removed(fr, ofconn->flow_format);
- ofconn_send_reply(ofconn, msg);
}
}
/* Given 'pin', sends an OFPT_PACKET_IN message to each OpenFlow controller as
* necessary according to their individual configurations.
*
- * 'rw_packet' may be NULL. Otherwise, 'rw_packet' must contain the same data
- * as pin->packet. (rw_packet == pin->packet is also valid.) Ownership of
- * 'rw_packet' is transferred to this function. */
+ * The caller doesn't need to fill in pin->buffer_id or pin->total_len. */
void
connmgr_send_packet_in(struct connmgr *mgr,
- const struct ofputil_packet_in *pin,
- const struct flow *flow, struct ofpbuf *rw_packet)
+ const struct ofputil_packet_in *pin)
{
- struct ofconn *ofconn, *prev;
+ struct ofconn *ofconn;
- prev = NULL;
LIST_FOR_EACH (ofconn, node, &mgr->all_conns) {
- if (ofconn_receives_async_msgs(ofconn)) {
- if (prev) {
- schedule_packet_in(prev, *pin, flow, NULL);
- }
- prev = ofconn;
+ if (ofconn_receives_async_msg(ofconn, OAM_PACKET_IN, pin->reason)
+ && ofconn->controller_id == pin->controller_id) {
+ schedule_packet_in(ofconn, *pin);
}
}
- if (prev) {
- schedule_packet_in(prev, *pin, flow, rw_packet);
- } else {
- ofpbuf_delete(rw_packet);
- }
}
/* pinsched callback for sending 'ofp_packet_in' on 'ofconn'. */
ofconn->packet_in_counter, 100);
}
-/* Takes 'pin', whose packet has the flow specified by 'flow', composes an
- * OpenFlow packet-in message from it, and passes it to 'ofconn''s packet
- * scheduler for sending.
- *
- * 'rw_packet' may be NULL. Otherwise, 'rw_packet' must contain the same data
- * as pin->packet. (rw_packet == pin->packet is also valid.) Ownership of
- * 'rw_packet' is transferred to this function. */
+/* Takes 'pin', composes an OpenFlow packet-in message from it, and passes it
+ * to 'ofconn''s packet scheduler for sending. */
static void
-schedule_packet_in(struct ofconn *ofconn, struct ofputil_packet_in pin,
- const struct flow *flow, struct ofpbuf *rw_packet)
+schedule_packet_in(struct ofconn *ofconn, struct ofputil_packet_in pin)
{
struct connmgr *mgr = ofconn->connmgr;
+ pin.total_len = pin.packet_len;
+
/* Get OpenFlow buffer_id. */
if (pin.reason == OFPR_ACTION) {
pin.buffer_id = UINT32_MAX;
} else if (!ofconn->pktbuf) {
pin.buffer_id = UINT32_MAX;
} else {
- pin.buffer_id = pktbuf_save(ofconn->pktbuf, pin.packet, flow->in_port);
+ pin.buffer_id = pktbuf_save(ofconn->pktbuf, pin.packet, pin.packet_len,
+ pin.fmd.in_port);
}
/* Figure out how much of the packet to send. */
if (pin.reason == OFPR_NO_MATCH) {
- pin.send_len = pin.packet->size;
+ pin.send_len = pin.packet_len;
} else {
/* Caller should have initialized 'send_len' to 'max_len' specified in
- * struct ofp_action_output. */
+ * output action. */
}
if (pin.buffer_id != UINT32_MAX) {
pin.send_len = MIN(pin.send_len, ofconn->miss_send_len);
* immediately call into do_send_packet_in() or it might buffer it for a
* while (until a later call to pinsched_run()). */
pinsched_send(ofconn->schedulers[pin.reason == OFPR_NO_MATCH ? 0 : 1],
- flow->in_port, ofputil_encode_packet_in(&pin, rw_packet),
+ pin.fmd.in_port,
+ ofputil_encode_packet_in(&pin, ofconn->protocol,
+ ofconn->packet_in_format),
do_send_packet_in, ofconn);
}
\f
}
return false;
}
-
-/* Sends 'packet' to each controller connected to 'mgr'. Takes ownership of
- * 'packet'. */
-void
-connmgr_broadcast(struct connmgr *mgr, struct ofpbuf *packet)
-{
- struct ofconn *ofconn, *prev;
-
- prev = NULL;
- LIST_FOR_EACH (ofconn, node, &mgr->all_conns) {
- if (prev) {
- ofconn_send_reply(ofconn, ofpbuf_clone(packet));
- }
- if (rconn_is_connected(ofconn->rconn)) {
- prev = ofconn;
- }
- }
- if (prev) {
- ofconn_send_reply(prev, packet);
- } else {
- ofpbuf_delete(packet);
- }
-}
\f
/* In-band configuration. */
* traffic until a controller has been defined and it tells us to do so. */
if (!connmgr_has_controllers(mgr)
&& mgr->fail_mode == OFPROTO_FAIL_STANDALONE) {
- union ofp_action action;
- struct cls_rule rule;
+ struct ofpbuf ofpacts;
+ struct match match;
+
+ ofpbuf_init(&ofpacts, OFPACT_OUTPUT_SIZE);
+ ofpact_put_OUTPUT(&ofpacts)->port = OFPP_NORMAL;
+ ofpact_pad(&ofpacts);
+
+ match_init_catchall(&match);
+ ofproto_add_flow(mgr->ofproto, &match, 0, ofpacts.data, ofpacts.size);
- memset(&action, 0, sizeof action);
- action.type = htons(OFPAT_OUTPUT);
- action.output.len = htons(sizeof action);
- action.output.port = htons(OFPP_NORMAL);
- cls_rule_init_catchall(&rule, 0);
- ofproto_add_flow(mgr->ofproto, &rule, &action, 1);
+ ofpbuf_uninit(&ofpacts);
}
}
\f
* ofservice_reconfigure() must be called to fully configure the new
* ofservice. */
static int
-ofservice_create(struct connmgr *mgr, const char *target)
+ofservice_create(struct connmgr *mgr, const char *target,
+ uint32_t allowed_versions, uint8_t dscp)
{
struct ofservice *ofservice;
struct pvconn *pvconn;
int error;
- error = pvconn_open(target, &pvconn);
+ error = pvconn_open(target, allowed_versions, &pvconn, dscp);
if (error) {
return error;
}
ofservice->probe_interval = c->probe_interval;
ofservice->rate_limit = c->rate_limit;
ofservice->burst_limit = c->burst_limit;
+ ofservice->enable_async_msgs = c->enable_async_msgs;
+ ofservice->dscp = c->dscp;
}
/* Finds and returns the ofservice within 'mgr' that has the given
}
return NULL;
}
+\f
+/* Flow monitors (NXST_FLOW_MONITOR). */
+
+/* A counter incremented when something significant happens to an OpenFlow
+ * rule.
+ *
+ * - When a rule is added, its 'add_seqno' and 'modify_seqno' are set to
+ * the current value (which is then incremented).
+ *
+ * - When a rule is modified, its 'modify_seqno' is set to the current
+ * value (which is then incremented).
+ *
+ * Thus, by comparing an old value of monitor_seqno against a rule's
+ * 'add_seqno', one can tell whether the rule was added before or after the old
+ * value was read, and similarly for 'modify_seqno'.
+ *
+ * 32 bits should normally be sufficient (and would be nice, to save space in
+ * each rule) but then we'd have to have some special cases for wraparound.
+ *
+ * We initialize monitor_seqno to 1 to allow 0 to be used as an invalid
+ * value. */
+static uint64_t monitor_seqno = 1;
+
+COVERAGE_DEFINE(ofmonitor_pause);
+COVERAGE_DEFINE(ofmonitor_resume);
+
+enum ofperr
+ofmonitor_create(const struct ofputil_flow_monitor_request *request,
+ struct ofconn *ofconn, struct ofmonitor **monitorp)
+{
+ struct ofmonitor *m;
+
+ *monitorp = NULL;
+
+ m = ofmonitor_lookup(ofconn, request->id);
+ if (m) {
+ return OFPERR_NXBRC_FM_DUPLICATE_ID;
+ }
+
+ m = xmalloc(sizeof *m);
+ m->ofconn = ofconn;
+ hmap_insert(&ofconn->monitors, &m->ofconn_node, hash_int(request->id, 0));
+ m->id = request->id;
+ m->flags = request->flags;
+ m->out_port = request->out_port;
+ m->table_id = request->table_id;
+ minimatch_init(&m->match, &request->match);
+
+ *monitorp = m;
+ return 0;
+}
+
+struct ofmonitor *
+ofmonitor_lookup(struct ofconn *ofconn, uint32_t id)
+{
+ struct ofmonitor *m;
+
+ HMAP_FOR_EACH_IN_BUCKET (m, ofconn_node, hash_int(id, 0),
+ &ofconn->monitors) {
+ if (m->id == id) {
+ return m;
+ }
+ }
+ return NULL;
+}
+
+void
+ofmonitor_destroy(struct ofmonitor *m)
+{
+ if (m) {
+ hmap_remove(&m->ofconn->monitors, &m->ofconn_node);
+ free(m);
+ }
+}
+
+void
+ofmonitor_report(struct connmgr *mgr, struct rule *rule,
+ enum nx_flow_update_event event,
+ enum ofp_flow_removed_reason reason,
+ const struct ofconn *abbrev_ofconn, ovs_be32 abbrev_xid)
+{
+ enum nx_flow_monitor_flags update;
+ struct ofconn *ofconn;
+
+ switch (event) {
+ case NXFME_ADDED:
+ update = NXFMF_ADD;
+ rule->add_seqno = rule->modify_seqno = monitor_seqno++;
+ break;
+
+ case NXFME_DELETED:
+ update = NXFMF_DELETE;
+ break;
+
+ case NXFME_MODIFIED:
+ update = NXFMF_MODIFY;
+ rule->modify_seqno = monitor_seqno++;
+ break;
+
+ default:
+ case NXFME_ABBREV:
+ NOT_REACHED();
+ }
+
+ LIST_FOR_EACH (ofconn, node, &mgr->all_conns) {
+ enum nx_flow_monitor_flags flags = 0;
+ struct ofmonitor *m;
+
+ if (ofconn->monitor_paused) {
+ /* Only send NXFME_DELETED notifications for flows that were added
+ * before we paused. */
+ if (event != NXFME_DELETED
+ || rule->add_seqno > ofconn->monitor_paused) {
+ continue;
+ }
+ }
+
+ HMAP_FOR_EACH (m, ofconn_node, &ofconn->monitors) {
+ if (m->flags & update
+ && (m->table_id == 0xff || m->table_id == rule->table_id)
+ && ofoperation_has_out_port(rule->pending, m->out_port)
+ && cls_rule_is_loose_match(&rule->cr, &m->match)) {
+ flags |= m->flags;
+ }
+ }
+
+ if (flags) {
+ if (list_is_empty(&ofconn->updates)) {
+ ofputil_start_flow_update(&ofconn->updates);
+ ofconn->sent_abbrev_update = false;
+ }
+
+ if (ofconn != abbrev_ofconn || ofconn->monitor_paused) {
+ struct ofputil_flow_update fu;
+ struct match match;
+
+ fu.event = event;
+ fu.reason = event == NXFME_DELETED ? reason : 0;
+ fu.idle_timeout = rule->idle_timeout;
+ fu.hard_timeout = rule->hard_timeout;
+ fu.table_id = rule->table_id;
+ fu.cookie = rule->flow_cookie;
+ minimatch_expand(&rule->cr.match, &match);
+ fu.match = &match;
+ fu.priority = rule->cr.priority;
+ if (flags & NXFMF_ACTIONS) {
+ fu.ofpacts = rule->ofpacts;
+ fu.ofpacts_len = rule->ofpacts_len;
+ } else {
+ fu.ofpacts = NULL;
+ fu.ofpacts_len = 0;
+ }
+ ofputil_append_flow_update(&fu, &ofconn->updates);
+ } else if (!ofconn->sent_abbrev_update) {
+ struct ofputil_flow_update fu;
+
+ fu.event = NXFME_ABBREV;
+ fu.xid = abbrev_xid;
+ ofputil_append_flow_update(&fu, &ofconn->updates);
+
+ ofconn->sent_abbrev_update = true;
+ }
+ }
+ }
+}
+
+void
+ofmonitor_flush(struct connmgr *mgr)
+{
+ struct ofconn *ofconn;
+
+ LIST_FOR_EACH (ofconn, node, &mgr->all_conns) {
+ struct ofpbuf *msg, *next;
+
+ LIST_FOR_EACH_SAFE (msg, next, list_node, &ofconn->updates) {
+ list_remove(&msg->list_node);
+ ofconn_send(ofconn, msg, ofconn->monitor_counter);
+ if (!ofconn->monitor_paused
+ && ofconn->monitor_counter->n_bytes > 128 * 1024) {
+ struct ofpbuf *pause;
+
+ COVERAGE_INC(ofmonitor_pause);
+ ofconn->monitor_paused = monitor_seqno++;
+ pause = ofpraw_alloc_xid(OFPRAW_NXT_FLOW_MONITOR_PAUSED,
+ OFP10_VERSION, htonl(0), 0);
+ ofconn_send(ofconn, pause, ofconn->monitor_counter);
+ }
+ }
+ }
+}
+
+static void
+ofmonitor_resume(struct ofconn *ofconn)
+{
+ struct ofpbuf *resumed;
+ struct ofmonitor *m;
+ struct list rules;
+ struct list msgs;
+
+ list_init(&rules);
+ HMAP_FOR_EACH (m, ofconn_node, &ofconn->monitors) {
+ ofmonitor_collect_resume_rules(m, ofconn->monitor_paused, &rules);
+ }
+
+ list_init(&msgs);
+ ofmonitor_compose_refresh_updates(&rules, &msgs);
+
+ resumed = ofpraw_alloc_xid(OFPRAW_NXT_FLOW_MONITOR_RESUMED, OFP10_VERSION,
+ htonl(0), 0);
+ list_push_back(&msgs, &resumed->list_node);
+ ofconn_send_replies(ofconn, &msgs);
+
+ ofconn->monitor_paused = 0;
+}
+
+static void
+ofmonitor_run(struct connmgr *mgr)
+{
+ struct ofconn *ofconn;
+
+ LIST_FOR_EACH (ofconn, node, &mgr->all_conns) {
+ if (ofconn->monitor_paused && !ofconn->monitor_counter->n_packets) {
+ COVERAGE_INC(ofmonitor_resume);
+ ofmonitor_resume(ofconn);
+ }
+ }
+}
+
+static void
+ofmonitor_wait(struct connmgr *mgr)
+{
+ struct ofconn *ofconn;
+
+ LIST_FOR_EACH (ofconn, node, &mgr->all_conns) {
+ if (ofconn->monitor_paused && !ofconn->monitor_counter->n_packets) {
+ poll_immediate_wake();
+ }
+ }
+}