/*
- * Copyright (c) 2009, 2010, 2011, 2012, 2013, 2014 Nicira, Inc.
+ * Copyright (c) 2009, 2010, 2011, 2012, 2013, 2014, 2015 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 "simap.h"
#include "stream.h"
#include "timeval.h"
-#include "vconn.h"
-#include "vlog.h"
+#include "openvswitch/vconn.h"
+#include "openvswitch/vlog.h"
#include "bundles.h"
struct ofconn {
/* Configuration that persists from one connection to the next. */
- struct list node; /* In struct connmgr's "all_conns" list. */
+ struct ovs_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. */
*
* When 'updates' is nonempty, 'sent_abbrev_update' is true if 'updates'
* contains an update event of type NXFME_ABBREV and false otherwise.. */
- struct list updates OVS_GUARDED_BY(ofproto_mutex);
+ struct ovs_list updates OVS_GUARDED_BY(ofproto_mutex);
bool sent_abbrev_update OVS_GUARDED_BY(ofproto_mutex);
/* Active bundles. Contains "struct ofp_bundle"s. */
static void ofconn_send(const struct ofconn *, struct ofpbuf *,
struct rconn_packet_counter *);
-static void do_send_packet_ins(struct ofconn *, struct list *txq);
+static void do_send_packet_ins(struct ofconn *, struct ovs_list *txq);
/* A listener for incoming OpenFlow "service" connections. */
struct ofservice {
char *local_port_name;
/* OpenFlow connections. */
- struct hmap controllers; /* Controller "struct ofconn"s. */
- struct list all_conns; /* Contains "struct ofconn"s. */
+ struct hmap controllers; /* All OFCONN_PRIMARY controllers. */
+ struct ovs_list all_conns; /* All controllers. */
uint64_t master_election_id; /* monotonically increasing sequence number
* for master election */
bool master_election_id_defined;
packets += rconn_count_txqlen(ofconn->rconn);
for (i = 0; i < N_SCHEDULERS; i++) {
- packets += pinsched_count_txqlen(ofconn->schedulers[i]);
+ struct pinsched_stats stats;
+
+ pinsched_get_stats(ofconn->schedulers[i], &stats);
+ packets += stats.n_queued;;
}
packets += pktbuf_count_packets(ofconn->pktbuf);
}
time_t last_connection = rconn_get_last_connection(rconn);
time_t last_disconnect = rconn_get_last_disconnect(rconn);
int last_error = rconn_get_last_error(rconn);
+ int i;
shash_add(info, target, cinfo);
cinfo->is_connected = rconn_is_connected(rconn);
cinfo->role = ofconn->role;
- cinfo->pairs.n = 0;
-
+ smap_init(&cinfo->pairs);
if (last_error) {
- cinfo->pairs.keys[cinfo->pairs.n] = "last_error";
- cinfo->pairs.values[cinfo->pairs.n++]
- = xstrdup(ovs_retval_to_string(last_error));
+ smap_add(&cinfo->pairs, "last_error",
+ ovs_retval_to_string(last_error));
}
- cinfo->pairs.keys[cinfo->pairs.n] = "state";
- cinfo->pairs.values[cinfo->pairs.n++]
- = xstrdup(rconn_get_state(rconn));
+ smap_add(&cinfo->pairs, "state", rconn_get_state(rconn));
if (last_connection != TIME_MIN) {
- cinfo->pairs.keys[cinfo->pairs.n] = "sec_since_connect";
- cinfo->pairs.values[cinfo->pairs.n++]
- = xasprintf("%ld", (long int) (now - last_connection));
+ smap_add_format(&cinfo->pairs, "sec_since_connect",
+ "%ld", (long int) (now - last_connection));
}
if (last_disconnect != TIME_MIN) {
- cinfo->pairs.keys[cinfo->pairs.n] = "sec_since_disconnect";
- cinfo->pairs.values[cinfo->pairs.n++]
- = xasprintf("%ld", (long int) (now - last_disconnect));
+ smap_add_format(&cinfo->pairs, "sec_since_disconnect",
+ "%ld", (long int) (now - last_disconnect));
+ }
+
+ for (i = 0; i < N_SCHEDULERS; i++) {
+ if (ofconn->schedulers[i]) {
+ const char *name = i ? "miss" : "action";
+ struct pinsched_stats stats;
+
+ pinsched_get_stats(ofconn->schedulers[i], &stats);
+ smap_add_nocopy(&cinfo->pairs,
+ xasprintf("packet-in-%s-backlog", name),
+ xasprintf("%u", stats.n_queued));
+ smap_add_nocopy(&cinfo->pairs,
+ xasprintf("packet-in-%s-bypassed", name),
+ xasprintf("%llu", stats.n_normal));
+ smap_add_nocopy(&cinfo->pairs,
+ xasprintf("packet-in-%s-queued", name),
+ xasprintf("%llu", stats.n_limited));
+ smap_add_nocopy(&cinfo->pairs,
+ xasprintf("packet-in-%s-dropped", name),
+ xasprintf("%llu", stats.n_queue_dropped));
+ }
}
}
}
SHASH_FOR_EACH (node, info) {
struct ofproto_controller_info *cinfo = node->data;
- while (cinfo->pairs.n) {
- free(CONST_CAST(char *, cinfo->pairs.values[--cinfo->pairs.n]));
- }
+ smap_destroy(&cinfo->pairs);
free(cinfo);
}
shash_destroy(info);
/* Add all the remotes. */
HMAP_FOR_EACH (ofconn, hmap_node, &mgr->controllers) {
const char *target = rconn_get_target(ofconn->rconn);
- struct sockaddr_storage ss;
+ union {
+ struct sockaddr_storage ss;
+ struct sockaddr_in in;
+ } sa;
if (ofconn->band == OFPROTO_IN_BAND
- && stream_parse_target_with_default_port(target, OFP_OLD_PORT, &ss)
- && ss.ss_family == AF_INET) {
- addrs[n_addrs++] = *(struct sockaddr_in *) &ss;
+ && stream_parse_target_with_default_port(target, OFP_PORT, &sa.ss)
+ && sa.ss.ss_family == AF_INET) {
+ addrs[n_addrs++] = sa.in;
}
}
for (i = 0; i < mgr->n_extra_remotes; i++) {
ofconn_get_master_election_id(ofconn, &status.generation_id);
buf = ofputil_encode_role_status(&status, ofconn_get_protocol(ofconn));
-
- ofconn_send(ofconn, buf, NULL);
+ if (buf) {
+ ofconn_send(ofconn, buf, NULL);
+ }
}
/* Changes 'ofconn''s role to 'role'. If 'role' is OFPCR12_ROLE_MASTER then
if (role != ofconn->role && role == OFPCR12_ROLE_MASTER) {
struct ofconn *other;
- HMAP_FOR_EACH (other, hmap_node, &ofconn->connmgr->controllers) {
+ LIST_FOR_EACH (other, node, &ofconn->connmgr->all_conns) {
if (other->role == OFPCR12_ROLE_MASTER) {
other->role = OFPCR12_ROLE_SLAVE;
ofconn_send_role_status(other, OFPCR12_ROLE_SLAVE, OFPCRR_MASTER_REQUEST);
ofconn_set_protocol(struct ofconn *ofconn, enum ofputil_protocol protocol)
{
ofconn->protocol = protocol;
+ if (!(protocol & OFPUTIL_P_OF14_UP)) {
+ uint32_t *master = ofconn->master_async_config;
+ uint32_t *slave = ofconn->slave_async_config;
+
+ /* OFPR_ACTION_SET is not supported before OF1.4 */
+ master[OAM_PACKET_IN] &= ~(1u << OFPR_ACTION_SET);
+ slave [OAM_PACKET_IN] &= ~(1u << OFPR_ACTION_SET);
+
+ /* OFPR_GROUP is not supported before OF1.4 */
+ master[OAM_PACKET_IN] &= ~(1u << OFPR_GROUP);
+ slave [OAM_PACKET_IN] &= ~(1u << OFPR_GROUP);
+ }
}
/* Returns the currently configured packet in format for 'ofconn', one of
uint32_t *master_masks, uint32_t *slave_masks)
{
size_t size = sizeof ofconn->master_async_config;
+
+ /* Make sure we know the protocol version and the async_config
+ * masks are properly updated by calling ofconn_get_protocol() */
+ if (OFPUTIL_P_NONE == ofconn_get_protocol(ofconn)){
+ OVS_NOT_REACHED();
+ }
+
memcpy(master_masks, ofconn->master_async_config, size);
memcpy(slave_masks, ofconn->slave_async_config, size);
}
/* Sends each of the messages in list 'replies' on 'ofconn' in order,
* accounting them as replies. */
void
-ofconn_send_replies(const struct ofconn *ofconn, struct list *replies)
+ofconn_send_replies(const struct ofconn *ofconn, struct ovs_list *replies)
{
- struct ofpbuf *reply, *next;
+ struct ofpbuf *reply;
- LIST_FOR_EACH_SAFE (reply, next, list_node, replies) {
- list_remove(&reply->list_node);
+ LIST_FOR_EACH_POP (reply, list_node, replies) {
ofconn_send_reply(ofconn, reply);
}
}
/* Same as pktbuf_retrieve(), using the pktbuf owned by 'ofconn'. */
enum ofperr
ofconn_pktbuf_retrieve(struct ofconn *ofconn, uint32_t id,
- struct ofpbuf **bufferp, ofp_port_t *in_port)
+ struct dp_packet **bufferp, ofp_port_t *in_port)
{
return pktbuf_retrieve(ofconn->pktbuf, id, bufferp, in_port);
}
}
ofconn->last_op = now;
}
+\f
+/* OpenFlow 1.4 bundles. */
+
+static inline uint32_t
+bundle_hash(uint32_t id)
+{
+ return hash_int(id, 0);
+}
-struct hmap *
-ofconn_get_bundles(struct ofconn *ofconn)
+struct ofp_bundle *
+ofconn_get_bundle(struct ofconn *ofconn, uint32_t id)
{
- return &ofconn->bundles;
+ struct ofp_bundle *bundle;
+
+ HMAP_FOR_EACH_IN_BUCKET(bundle, node, bundle_hash(id), &ofconn->bundles) {
+ if (bundle->id == id) {
+ return bundle;
+ }
+ }
+
+ return NULL;
}
+enum ofperr
+ofconn_insert_bundle(struct ofconn *ofconn, struct ofp_bundle *bundle)
+{
+ /* XXX: Check the limit of open bundles */
+
+ hmap_insert(&ofconn->bundles, &bundle->node, bundle_hash(bundle->id));
+
+ return 0;
+}
+
+enum ofperr
+ofconn_remove_bundle(struct ofconn *ofconn, struct ofp_bundle *bundle)
+{
+ hmap_remove(&ofconn->bundles, &bundle->node);
+
+ return 0;
+}
+
+static void
+bundle_remove_all(struct ofconn *ofconn)
+{
+ struct ofp_bundle *b, *next;
+
+ HMAP_FOR_EACH_SAFE (b, next, node, &ofconn->bundles) {
+ ofp_bundle_remove__(ofconn, b);
+ }
+}
\f
/* Private ofconn functions. */
/* "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_PACKET_IN] = ((1u << OFPR_NO_MATCH)
+ | (1u << OFPR_ACTION)
+ | (1u << OFPR_ACTION_SET)
+ | (1u << OFPR_GROUP));
master[OAM_PORT_STATUS] = ((1u << OFPPR_ADD)
| (1u << OFPPR_DELETE)
| (1u << OFPPR_MODIFY));
hmap_remove(&ofconn->connmgr->controllers, &ofconn->hmap_node);
}
- ofp_bundle_remove_all(ofconn);
+ bundle_remove_all(ofconn);
+ hmap_destroy(&ofconn->bundles);
hmap_destroy(&ofconn->monitors);
list_remove(&ofconn->node);
size_t i;
for (i = 0; i < N_SCHEDULERS; i++) {
- struct list txq;
+ struct ovs_list txq;
pinsched_run(ofconn->schedulers[i], &txq);
do_send_packet_ins(ofconn, &txq);
enum ofputil_protocol protocol = ofconn_get_protocol(ofconn);
if (protocol != OFPUTIL_P_NONE
- && ofputil_protocol_to_ofp_version(protocol) >= OFP13_VERSION) {
- enum ofproto_table_config config;
-
- config = ofproto_table_get_config(ofconn->connmgr->ofproto,
- pin->up.table_id);
- if (config == OFPROTO_TABLE_MISS_DEFAULT) {
- return false;
- }
+ && ofputil_protocol_to_ofp_version(protocol) >= OFP13_VERSION
+ && (ofproto_table_get_miss_config(ofconn->connmgr->ofproto,
+ pin->up.table_id)
+ == OFPUTIL_TABLE_MISS_DEFAULT)) {
+ return false;
}
}
return true;
/* The default "table-miss" behaviour for OpenFlow1.3+ is to drop the
* packet rather than to send the packet to the controller.
*
- * This function returns false to indicate that a packet_in message
+ * This function returns true to indicate that a packet_in message
* for a "table-miss" should be sent to at least one controller.
* That is there is at least one controller with controller_id 0
* which connected using an OpenFlow version earlier than OpenFlow1.3.
* This logic assumes that "table-miss" packet_in messages
* are always sent to controller_id 0. */
bool
-connmgr_wants_packet_in_on_miss(struct connmgr *mgr)
+connmgr_wants_packet_in_on_miss(struct connmgr *mgr) OVS_EXCLUDED(ofproto_mutex)
{
struct ofconn *ofconn;
+ ovs_mutex_lock(&ofproto_mutex);
LIST_FOR_EACH (ofconn, node, &mgr->all_conns) {
enum ofputil_protocol protocol = ofconn_get_protocol(ofconn);
if (ofconn->controller_id == 0 &&
(protocol == OFPUTIL_P_NONE ||
ofputil_protocol_to_ofp_version(protocol) < OFP13_VERSION)) {
+ ovs_mutex_unlock(&ofproto_mutex);
return true;
}
}
+ ovs_mutex_unlock(&ofproto_mutex);
+
return false;
}
static enum ofp_packet_in_reason
wire_reason(struct ofconn *ofconn, const struct ofproto_packet_in *pin)
{
- if (pin->miss_type == OFPROTO_PACKET_IN_MISS_FLOW
- && pin->up.reason == OFPR_ACTION) {
- enum ofputil_protocol protocol = ofconn_get_protocol(ofconn);
+ enum ofputil_protocol protocol = ofconn_get_protocol(ofconn);
- if (protocol != OFPUTIL_P_NONE
- && ofputil_protocol_to_ofp_version(protocol) >= OFP13_VERSION) {
- return OFPR_NO_MATCH;
+ if (pin->miss_type == OFPROTO_PACKET_IN_MISS_FLOW
+ && pin->up.reason == OFPR_ACTION
+ && protocol != OFPUTIL_P_NONE
+ && ofputil_protocol_to_ofp_version(protocol) >= OFP13_VERSION) {
+ return OFPR_NO_MATCH;
+ }
+
+ switch (pin->up.reason) {
+ case OFPR_ACTION_SET:
+ case OFPR_GROUP:
+ case OFPR_PACKET_OUT:
+ if (!(protocol & OFPUTIL_P_OF14_UP)) {
+ /* Only supported in OF1.4+ */
+ return OFPR_ACTION;
}
+ /* Fall through. */
+ case OFPR_NO_MATCH:
+ case OFPR_ACTION:
+ case OFPR_INVALID_TTL:
+ case OFPR_N_REASONS:
+ default:
+ return pin->up.reason;
}
- return pin->up.reason;
}
/* Given 'pin', sends an OFPT_PACKET_IN message to each OpenFlow controller as
enum ofp_packet_in_reason reason = wire_reason(ofconn, pin);
if (ofconn_wants_packet_in_on_miss(ofconn, pin)
- && ofconn_receives_async_msg(ofconn, OAM_PACKET_IN, pin->up.reason)
+ && ofconn_receives_async_msg(ofconn, OAM_PACKET_IN, reason)
&& ofconn->controller_id == pin->controller_id) {
schedule_packet_in(ofconn, *pin, reason);
}
}
static void
-do_send_packet_ins(struct ofconn *ofconn, struct list *txq)
+do_send_packet_ins(struct ofconn *ofconn, struct ovs_list *txq)
{
- struct ofpbuf *pin, *next_pin;
-
- LIST_FOR_EACH_SAFE (pin, next_pin, list_node, txq) {
- list_remove(&pin->list_node);
+ struct ofpbuf *pin;
+ LIST_FOR_EACH_POP (pin, list_node, txq) {
if (rconn_send_with_limit(ofconn->rconn, pin,
ofconn->packet_in_counter, 100) == EAGAIN) {
static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(5, 5);
{
struct connmgr *mgr = ofconn->connmgr;
uint16_t controller_max_len;
- struct list txq;
+ struct ovs_list txq;
pin.up.total_len = pin.up.packet_len;
ofpact_pad(&ofpacts);
match_init_catchall(&match);
- ofproto_add_flow(mgr->ofproto, &match, 0, ofpbuf_data(&ofpacts),
- ofpbuf_size(&ofpacts));
+ ofproto_add_flow(mgr->ofproto, &match, 0, ofpacts.data,
+ ofpacts.size);
ofpbuf_uninit(&ofpacts);
}
}
+
+/* Returns the number of hidden rules created by the in-band and fail-open
+ * implementations in table 0. (Subtracting this count from the number of
+ * rules in the table 0 classifier, as returned by classifier_count(), yields
+ * the number of flows that OVS should report via OpenFlow for table 0.) */
+int
+connmgr_count_hidden_rules(const struct connmgr *mgr)
+{
+ int n_hidden = 0;
+ if (mgr->in_band) {
+ n_hidden += in_band_count_rules(mgr->in_band);
+ }
+ if (mgr->fail_open) {
+ n_hidden += fail_open_count_rules(mgr->fail_open);
+ }
+ return n_hidden;
+}
\f
/* Creates a new ofservice for 'target' in 'mgr'. Returns 0 if successful,
* otherwise a positive errno value.
m = ofmonitor_lookup(ofconn, request->id);
if (m) {
- return OFPERR_NXBRC_FM_DUPLICATE_ID;
+ return OFPERR_OFPMOFC_MONITOR_EXISTS;
}
m = xmalloc(sizeof *m);
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)
+ const struct ofconn *abbrev_ofconn, ovs_be32 abbrev_xid,
+ const struct rule_actions *old_actions)
OVS_REQUIRES(ofproto_mutex)
{
enum nx_flow_monitor_flags update;
HMAP_FOR_EACH (m, ofconn_node, &ofconn->monitors) {
if (m->flags & update
&& (m->table_id == 0xff || m->table_id == rule->table_id)
+ && (ofproto_rule_has_out_port(rule, m->out_port)
+ || (old_actions
+ && ofpacts_output_to_port(old_actions->ofpacts,
+ old_actions->ofpacts_len,
+ m->out_port)))
&& cls_rule_is_loose_match(&rule->cr, &m->match)) {
flags |= m->flags;
}
ofconn->sent_abbrev_update = false;
}
- if (ofconn != abbrev_ofconn || ofconn->monitor_paused) {
+ if (flags & NXFMF_OWN || ofconn != abbrev_ofconn
+ || ofconn->monitor_paused) {
struct ofputil_flow_update fu;
struct match match;
struct ofconn *ofconn;
LIST_FOR_EACH (ofconn, node, &mgr->all_conns) {
- struct ofpbuf *msg, *next;
+ struct ofpbuf *msg;
- LIST_FOR_EACH_SAFE (msg, next, list_node, &ofconn->updates) {
+ LIST_FOR_EACH_POP (msg, list_node, &ofconn->updates) {
unsigned int n_bytes;
- list_remove(&msg->list_node);
ofconn_send(ofconn, msg, ofconn->monitor_counter);
n_bytes = rconn_packet_counter_n_bytes(ofconn->monitor_counter);
if (!ofconn->monitor_paused && n_bytes > 128 * 1024) {
struct rule_collection rules;
struct ofpbuf *resumed;
struct ofmonitor *m;
- struct list msgs;
+ struct ovs_list msgs;
rule_collection_init(&rules);
HMAP_FOR_EACH (m, ofconn_node, &ofconn->monitors) {