/*
- * Copyright (c) 2009, 2010, 2011 Nicira Networks.
+ * Copyright (c) 2009, 2010, 2011, 2012 Nicira Networks.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
/* 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 ofproto_band band; /* In-band or out-of-band? */
+
+/* State that should be cleared from one connection to the next. */
+
+ /* OpenFlow state. */
+ enum nx_role role; /* Role. */
enum nx_flow_format flow_format; /* Currently selected flow format. */
enum nx_packet_in_format packet_in_format; /* OFPT_PACKET_IN format. */
bool flow_mod_table_id; /* NXT_FLOW_MOD_TABLE_ID enabled? */
#define OFCONN_REPLY_MAX 100
struct rconn_packet_counter *reply_counter;
- /* type == OFCONN_PRIMARY only. */
- enum nx_role role; /* Role. */
- bool invalid_ttl_to_controller; /* Send packets with invalid TTL
- to the controller. */
- 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 */
};
static struct ofconn *ofconn_create(struct connmgr *, struct rconn *,
enum ofconn_type);
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 *);
ofconn = ofconn_create(mgr, rconn_create(5, 8), OFCONN_PRIMARY);
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));
}
void
-ofconn_set_invalid_ttl_to_controller(struct ofconn *ofconn, bool val)
+ofconn_set_invalid_ttl_to_controller(struct ofconn *ofconn, bool enable)
{
- ofconn->invalid_ttl_to_controller = val;
+ 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)
{
- return ofconn->invalid_ttl_to_controller;
+ uint32_t bit = 1u << OFPR_INVALID_TTL;
+ return (ofconn->master_async_config[OAM_PACKET_IN] & bit) != 0;
}
/* Returns the currently configured flow format for 'ofconn', one of NXFF_*.
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
static struct ofconn *
ofconn_create(struct connmgr *mgr, struct rconn *rconn, enum ofconn_type type)
{
- 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->packet_in_format = NXPIF_OPENFLOW10;
- ofconn->flow_mod_table_id = false;
+
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 ();
- ofconn->invalid_ttl_to_controller = false;
+
+ 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)
{
+ uint32_t *master = ofconn->master_async_config;
+ uint32_t *slave = ofconn->slave_async_config;
+ int i;
+
+ ofconn->role = NX_ROLE_OTHER;
+ ofconn->flow_format = NXFF_OPENFLOW10;
+ ofconn->packet_in_format = NXPIF_OPENFLOW10;
+ ofconn->flow_mod_table_id = false;
+
+ /* 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);
+
+ rconn_packet_counter_destroy(ofconn->reply_counter);
+ ofconn->reply_counter = rconn_packet_counter_create();
+
+ /* "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;
}
static void
}
}
-/* 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)
{
- 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 {
- /* 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;
- }
-}
+ const uint32_t *async_config;
+
+ assert(reason < 32);
+ assert((unsigned int) type < OAM_N_TYPES);
-static bool
-ofconn_receives_async_msgs(const struct ofconn *ofconn)
-{
if (!rconn_is_connected(ofconn->rconn)) {
return false;
- } else {
- return ofconn_receives_async_msgs__(ofconn);
}
-}
-static bool
-ofconn_interested_in_packet(const struct ofconn *ofconn,
- const struct ofputil_packet_in *pin)
-{
- if (!rconn_is_connected(ofconn->rconn)) {
+ /* 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 false;
- } else if (pin->reason == OFPR_INVALID_TTL) {
- return ofconn->invalid_ttl_to_controller;
- } else {
- return ofconn_receives_async_msgs__(ofconn);
}
+
+ 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
struct ofconn *ofconn;
LIST_FOR_EACH (ofconn, node, &mgr->all_conns) {
- struct ofp_port_status *ops;
- struct ofpbuf *b;
-
- /* 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;
+ if (ofconn_receives_async_msg(ofconn, OAM_PORT_STATUS, reason)) {
+ struct ofp_port_status *ops;
+ struct ofpbuf *b;
+
+ ops = make_openflow_xid(sizeof *ops, OFPT_PORT_STATUS, 0, &b);
+ ops->reason = reason;
+ ops->desc = *opp;
+ ofconn_send(ofconn, b, 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->flow_format);
+ 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);
}
}
struct ofconn *ofconn;
LIST_FOR_EACH (ofconn, node, &mgr->all_conns) {
- if (ofconn_interested_in_packet(ofconn, pin)) {
+ if (ofconn_receives_async_msg(ofconn, OAM_PACKET_IN, pin->reason)) {
schedule_packet_in(ofconn, *pin, flow);
}
}