#include "meta-flow.h"
#include "netdev.h"
#include "nx-match.h"
+#include "ofp-actions.h"
#include "ofp-errors.h"
#include "ofp-print.h"
#include "ofp-util.h"
#include "poll-loop.h"
#include "random.h"
#include "shash.h"
+#include "simap.h"
#include "sset.h"
#include "timeval.h"
#include "unaligned.h"
struct hmap_node hmap_node; /* In ofproto's "deletions" hmap. */
struct rule *rule; /* Rule being operated upon. */
enum ofoperation_type type; /* Type of operation. */
- int status; /* -1 if pending, otherwise 0 or error code. */
struct rule *victim; /* OFOPERATION_ADDING: Replaced rule. */
- union ofp_action *actions; /* OFOPERATION_MODIFYING: Replaced actions. */
- int n_actions; /* OFOPERATION_MODIFYING: # of old actions. */
+ struct ofpact *ofpacts; /* OFOPERATION_MODIFYING: Replaced actions. */
+ size_t ofpacts_len; /* OFOPERATION_MODIFYING: Bytes of ofpacts. */
ovs_be64 flow_cookie; /* Rule's old flow cookie. */
};
list_init(&ofproto->pending);
ofproto->n_pending = 0;
hmap_init(&ofproto->deletions);
+ ofproto->n_add = ofproto->n_delete = ofproto->n_modify = 0;
+ ofproto->first_op = ofproto->last_op = LLONG_MIN;
+ ofproto->next_op_report = LLONG_MAX;
+ ofproto->op_backoff = LLONG_MIN;
ofproto->vlan_bitmap = NULL;
ofproto->vlans_changed = false;
ofproto->min_mtu = INT_MAX;
NOT_REACHED();
}
+ if (time_msec() >= p->next_op_report) {
+ long long int ago = (time_msec() - p->first_op) / 1000;
+ long long int interval = (p->last_op - p->first_op) / 1000;
+ struct ds s;
+
+ ds_init(&s);
+ ds_put_format(&s, "%d flow_mods ",
+ p->n_add + p->n_delete + p->n_modify);
+ if (interval == ago) {
+ ds_put_format(&s, "in the last %lld s", ago);
+ } else if (interval) {
+ ds_put_format(&s, "in the %lld s starting %lld s ago",
+ interval, ago);
+ } else {
+ ds_put_format(&s, "%lld s ago", ago);
+ }
+
+ ds_put_cstr(&s, " (");
+ if (p->n_add) {
+ ds_put_format(&s, "%d adds, ", p->n_add);
+ }
+ if (p->n_delete) {
+ ds_put_format(&s, "%d deletes, ", p->n_delete);
+ }
+ if (p->n_modify) {
+ ds_put_format(&s, "%d modifications, ", p->n_modify);
+ }
+ s.length -= 2;
+ ds_put_char(&s, ')');
+
+ VLOG_INFO("%s", ds_cstr(&s));
+ ds_destroy(&s);
+
+ p->n_add = p->n_delete = p->n_modify = 0;
+ p->next_op_report = LLONG_MAX;
+ }
+
return error;
}
return connmgr_has_controllers(p->connmgr);
}
+/* Adds some memory usage statistics for 'ofproto' into 'usage', for use with
+ * memory_report(). */
+void
+ofproto_get_memory_usage(const struct ofproto *ofproto, struct simap *usage)
+{
+ const struct oftable *table;
+ unsigned int n_rules;
+
+ simap_increase(usage, "ports", hmap_count(&ofproto->ports));
+ simap_increase(usage, "ops",
+ ofproto->n_pending + hmap_count(&ofproto->deletions));
+
+ n_rules = 0;
+ OFPROTO_FOR_EACH_TABLE (table, ofproto) {
+ n_rules += classifier_count(&table->cls);
+ }
+ simap_increase(usage, "rules", n_rules);
+
+ if (ofproto->ofproto_class->get_memory_usage) {
+ ofproto->ofproto_class->get_memory_usage(ofproto, usage);
+ }
+
+ connmgr_get_memory_usage(ofproto->connmgr, usage);
+}
+
void
ofproto_get_ofproto_controller_info(const struct ofproto *ofproto,
struct shash *info)
* (0...65535, inclusive) then the flow will be visible to OpenFlow
* controllers; otherwise, it will be hidden.
*
- * The caller retains ownership of 'cls_rule' and 'actions'.
+ * The caller retains ownership of 'cls_rule' and 'ofpacts'.
*
* This is a helper function for in-band control and fail-open. */
void
ofproto_add_flow(struct ofproto *ofproto, const struct cls_rule *cls_rule,
- const union ofp_action *actions, size_t n_actions)
+ const struct ofpact *ofpacts, size_t ofpacts_len)
{
const struct rule *rule;
rule = rule_from_cls_rule(classifier_find_rule_exactly(
&ofproto->tables[0].cls, cls_rule));
- if (!rule || !ofputil_actions_equal(rule->actions, rule->n_actions,
- actions, n_actions)) {
+ if (!rule || !ofpacts_equal(rule->ofpacts, rule->ofpacts_len,
+ ofpacts, ofpacts_len)) {
struct ofputil_flow_mod fm;
memset(&fm, 0, sizeof fm);
fm.cr = *cls_rule;
fm.buffer_id = UINT32_MAX;
- fm.actions = (union ofp_action *) actions;
- fm.n_actions = n_actions;
+ fm.ofpacts = xmemdup(ofpacts, ofpacts_len);
+ fm.ofpacts_len = ofpacts_len;
add_flow(ofproto, NULL, &fm, NULL);
+ free(fm.ofpacts);
}
}
ofproto_rule_destroy__(struct rule *rule)
{
if (rule) {
- free(rule->actions);
+ free(rule->ofpacts);
rule->ofproto->ofproto_class->rule_dealloc(rule);
}
}
}
/* Returns true if 'rule' has an OpenFlow OFPAT_OUTPUT or OFPAT_ENQUEUE action
- * that outputs to 'out_port' (output to OFPP_FLOOD and OFPP_ALL doesn't
- * count). */
+ * that outputs to 'port' (output to OFPP_FLOOD and OFPP_ALL doesn't count). */
static bool
-rule_has_out_port(const struct rule *rule, uint16_t out_port)
+rule_has_out_port(const struct rule *rule, uint16_t port)
{
- const union ofp_action *oa;
- size_t left;
-
- if (out_port == OFPP_NONE) {
- return true;
- }
- OFPUTIL_ACTION_FOR_EACH_UNSAFE (oa, left, rule->actions, rule->n_actions) {
- if (action_outputs_to_port(oa, htons(out_port))) {
- return true;
- }
- }
- return false;
+ return (port == OFPP_NONE
+ || ofpacts_output_to_port(rule->ofpacts, rule->ofpacts_len, port));
}
/* Executes the actions indicated by 'rule' on 'packet' and credits 'rule''s
struct ofproto *p = ofconn_get_ofproto(ofconn);
struct ofputil_packet_out po;
struct ofpbuf *payload;
+ uint64_t ofpacts_stub[1024 / 8];
+ struct ofpbuf ofpacts;
struct flow flow;
enum ofperr error;
error = reject_slave_controller(ofconn);
if (error) {
- return error;
+ goto exit;
}
/* Decode message. */
- error = ofputil_decode_packet_out(&po, opo);
+ ofpbuf_use_stub(&ofpacts, ofpacts_stub, sizeof ofpacts_stub);
+ error = ofputil_decode_packet_out(&po, opo, &ofpacts);
if (error) {
- return error;
+ goto exit_free_ofpacts;
}
/* Get payload. */
if (po.buffer_id != UINT32_MAX) {
error = ofconn_pktbuf_retrieve(ofconn, po.buffer_id, &payload, NULL);
if (error || !payload) {
- return error;
+ goto exit_free_ofpacts;
}
} else {
payload = xmalloc(sizeof *payload);
/* Send out packet. */
flow_extract(payload, 0, 0, po.in_port, &flow);
error = p->ofproto_class->packet_out(p, payload, &flow,
- po.actions, po.n_actions);
+ po.ofpacts, po.ofpacts_len);
ofpbuf_delete(payload);
+exit_free_ofpacts:
+ ofpbuf_uninit(&ofpacts);
+exit:
return error;
}
for (i = 0; i < p->n_tables; i++) {
ots[i].table_id = i;
sprintf(ots[i].name, "table%zu", i);
- ots[i].wildcards = htonl(OFPFW_ALL);
+ ots[i].wildcards = htonl(OFPFW10_ALL);
ots[i].max_entries = htonl(1000000); /* An arbitrary big number. */
ots[i].active_count = htonl(classifier_count(&p->tables[i].cls));
}
fs.hard_age = age_secs(now - rule->modified);
ofproto->ofproto_class->rule_get_stats(rule, &fs.packet_count,
&fs.byte_count);
- fs.actions = rule->actions;
- fs.n_actions = rule->n_actions;
+ fs.ofpacts = rule->ofpacts;
+ fs.ofpacts_len = rule->ofpacts_len;
ofputil_append_flow_stats_reply(&fs, &replies);
}
ofconn_send_replies(ofconn, &replies);
ds_put_format(results, "n_bytes=%"PRIu64", ", byte_count);
cls_rule_format(&rule->cr, results);
ds_put_char(results, ',');
- if (rule->n_actions > 0) {
- ofp_print_actions(results, rule->actions, rule->n_actions);
+ if (rule->ofpacts_len > 0) {
+ ofpacts_format(rule->ofpacts, rule->ofpacts_len, results);
} else {
ds_put_cstr(results, "drop");
}
put_queue_stats(cbdata, queue_id, stats);
}
-static void
+static enum ofperr
handle_queue_stats_for_port(struct ofport *port, uint32_t queue_id,
struct queue_stats_cbdata *cbdata)
{
if (!netdev_get_queue_stats(port->netdev, queue_id, &stats)) {
put_queue_stats(cbdata, queue_id, &stats);
+ } else {
+ return OFPERR_OFPQOFC_BAD_QUEUE;
}
}
+ return 0;
}
static enum ofperr
{
struct ofproto *ofproto = ofconn_get_ofproto(ofconn);
struct queue_stats_cbdata cbdata;
- struct ofport *port;
unsigned int port_no;
+ struct ofport *port;
uint32_t queue_id;
+ enum ofperr error;
COVERAGE_INC(ofproto_queue_req);
port_no = ntohs(qsr->port_no);
queue_id = ntohl(qsr->queue_id);
if (port_no == OFPP_ALL) {
+ error = OFPERR_OFPQOFC_BAD_QUEUE;
HMAP_FOR_EACH (port, hmap_node, &ofproto->ports) {
- handle_queue_stats_for_port(port, queue_id, &cbdata);
+ if (!handle_queue_stats_for_port(port, queue_id, &cbdata)) {
+ error = 0;
+ }
}
- } else if (port_no < OFPP_MAX) {
+ } else {
port = ofproto_get_port(ofproto, port_no);
- if (port) {
- handle_queue_stats_for_port(port, queue_id, &cbdata);
- }
+ error = (port
+ ? handle_queue_stats_for_port(port, queue_id, &cbdata)
+ : OFPERR_OFPQOFC_BAD_PORT);
+ }
+ if (!error) {
+ ofconn_send_replies(ofconn, &cbdata.replies);
} else {
ofpbuf_list_delete(&cbdata.replies);
- return OFPERR_OFPQOFC_BAD_PORT;
}
- ofconn_send_replies(ofconn, &cbdata.replies);
- return 0;
+ return error;
}
static bool
* error code on failure, or OFPROTO_POSTPONE if the operation cannot be
* initiated now but may be retried later.
*
+ * Upon successful return, takes ownership of 'fm->ofpacts'. On failure,
+ * ownership remains with the caller.
+ *
* 'ofconn' is used to retrieve the packet buffer specified in ofm->buffer_id,
* if any. */
static enum ofperr
rule->ofproto = ofproto;
rule->cr = fm->cr;
rule->pending = NULL;
- rule->flow_cookie = fm->cookie;
+ rule->flow_cookie = fm->new_cookie;
rule->created = rule->modified = rule->used = time_msec();
rule->idle_timeout = fm->idle_timeout;
rule->hard_timeout = fm->hard_timeout;
rule->table_id = table - ofproto->tables;
rule->send_flow_removed = (fm->flags & OFPFF_SEND_FLOW_REM) != 0;
- rule->actions = ofputil_actions_clone(fm->actions, fm->n_actions);
- rule->n_actions = fm->n_actions;
+ rule->ofpacts = xmemdup(fm->ofpacts, fm->ofpacts_len);
+ rule->ofpacts_len = fm->ofpacts_len;
rule->evictable = true;
rule->eviction_group = NULL;
continue;
}
- if (!ofputil_actions_equal(fm->actions, fm->n_actions,
- rule->actions, rule->n_actions)) {
+ if (!ofpacts_equal(fm->ofpacts, fm->ofpacts_len,
+ rule->ofpacts, rule->ofpacts_len)) {
ofoperation_create(group, rule, OFOPERATION_MODIFY);
- rule->pending->actions = rule->actions;
- rule->pending->n_actions = rule->n_actions;
- rule->actions = ofputil_actions_clone(fm->actions, fm->n_actions);
- rule->n_actions = fm->n_actions;
- ofproto->ofproto_class->rule_modify_actions(rule);
+ rule->pending->ofpacts = rule->ofpacts;
+ rule->pending->ofpacts_len = rule->ofpacts_len;
+ rule->ofpacts = xmemdup(fm->ofpacts, fm->ofpacts_len);
+ rule->ofpacts_len = fm->ofpacts_len;
+ rule->ofproto->ofproto_class->rule_modify_actions(rule);
} else {
rule->modified = time_msec();
}
- rule->flow_cookie = fm->cookie;
+ if (fm->new_cookie != htonll(UINT64_MAX)) {
+ rule->flow_cookie = fm->new_cookie;
+ }
}
ofopgroup_submit(group);
error = collect_rules_loose(ofproto, fm->table_id, &fm->cr,
fm->cookie, fm->cookie_mask,
OFPP_NONE, &rules);
- return (error ? error
- : list_is_empty(&rules) ? add_flow(ofproto, ofconn, fm, request)
- : modify_flows__(ofproto, ofconn, fm, request, &rules));
+ if (error) {
+ return error;
+ } else if (list_is_empty(&rules)) {
+ return fm->cookie_mask ? 0 : add_flow(ofproto, ofconn, fm, request);
+ } else {
+ return modify_flows__(ofproto, ofconn, fm, request, &rules);
+ }
}
/* Implements OFPFC_MODIFY_STRICT. Returns 0 on success or an OpenFlow error
error = collect_rules_strict(ofproto, fm->table_id, &fm->cr,
fm->cookie, fm->cookie_mask,
OFPP_NONE, &rules);
- return (error ? error
- : list_is_empty(&rules) ? add_flow(ofproto, ofconn, fm, request)
- : list_is_singleton(&rules) ? modify_flows__(ofproto, ofconn,
- fm, request, &rules)
- : 0);
+
+ if (error) {
+ return error;
+ } else if (list_is_empty(&rules)) {
+ return fm->cookie_mask ? 0 : add_flow(ofproto, ofconn, fm, request);
+ } else {
+ return list_is_singleton(&rules) ? modify_flows__(ofproto, ofconn,
+ fm, request, &rules)
+ : 0;
+ }
}
\f
/* OFPFC_DELETE implementation. */
static enum ofperr
handle_flow_mod(struct ofconn *ofconn, const struct ofp_header *oh)
{
+ struct ofproto *ofproto = ofconn_get_ofproto(ofconn);
struct ofputil_flow_mod fm;
+ uint64_t ofpacts_stub[1024 / 8];
+ struct ofpbuf ofpacts;
enum ofperr error;
+ long long int now;
error = reject_slave_controller(ofconn);
if (error) {
- return error;
+ goto exit;
}
- error = ofputil_decode_flow_mod(&fm, oh, ofconn_get_protocol(ofconn));
+ ofpbuf_use_stub(&ofpacts, ofpacts_stub, sizeof ofpacts_stub);
+ error = ofputil_decode_flow_mod(&fm, oh, ofconn_get_protocol(ofconn),
+ &ofpacts);
if (error) {
- return error;
+ goto exit_free_ofpacts;
}
- /* We do not support the emergency flow cache. It will hopefully get
- * dropped from OpenFlow in the near future. */
+ /* We do not support the OpenFlow 1.0 emergency flow cache, which is not
+ * required in OpenFlow 1.0.1 and removed from OpenFlow 1.1. */
if (fm.flags & OFPFF_EMERG) {
- /* There isn't a good fit for an error code, so just state that the
- * flow table is full. */
- return OFPERR_OFPFMFC_ALL_TABLES_FULL;
+ /* We do not support the emergency flow cache. It will hopefully get
+ * dropped from OpenFlow in the near future. There is no good error
+ * code, so just state that the flow table is full. */
+ error = OFPERR_OFPFMFC_ALL_TABLES_FULL;
+ } else {
+ error = handle_flow_mod__(ofconn_get_ofproto(ofconn), ofconn, &fm, oh);
+ }
+ if (error) {
+ goto exit_free_ofpacts;
+ }
+
+ /* Record the operation for logging a summary report. */
+ switch (fm.command) {
+ case OFPFC_ADD:
+ ofproto->n_add++;
+ break;
+
+ case OFPFC_MODIFY:
+ case OFPFC_MODIFY_STRICT:
+ ofproto->n_modify++;
+ break;
+
+ case OFPFC_DELETE:
+ case OFPFC_DELETE_STRICT:
+ ofproto->n_delete++;
+ break;
}
- return handle_flow_mod__(ofconn_get_ofproto(ofconn), ofconn, &fm, oh);
+ now = time_msec();
+ if (ofproto->next_op_report == LLONG_MAX) {
+ ofproto->first_op = now;
+ ofproto->next_op_report = MAX(now + 10 * 1000,
+ ofproto->op_backoff);
+ ofproto->op_backoff = ofproto->next_op_report + 60 * 1000;
+ }
+ ofproto->last_op = now;
+
+exit_free_ofpacts:
+ ofpbuf_uninit(&ofpacts);
+exit:
+ return error;
}
static enum ofperr
slave[OAM_FLOW_REMOVED] = ntohl(msg->flow_removed_mask[1]);
ofconn_set_async_config(ofconn, master, slave);
+ if (ofconn_get_type(ofconn) == OFCONN_SERVICE &&
+ !ofconn_get_miss_send_len(ofconn)) {
+ ofconn_set_miss_send_len(ofconn, OFP_DEFAULT_MISS_SEND_LEN);
+ }
return 0;
}
static enum ofperr
handle_barrier_request(struct ofconn *ofconn, const struct ofp_header *oh)
{
- struct ofp_header *ob;
struct ofpbuf *buf;
if (ofconn_has_pending_opgroups(ofconn)) {
return OFPROTO_POSTPONE;
}
- ob = make_openflow_xid(sizeof *ob, OFPT10_BARRIER_REPLY, oh->xid, &buf);
+ make_openflow_xid(sizeof *oh, OFPT10_BARRIER_REPLY, oh->xid, &buf);
ofconn_send_reply(ofconn, buf);
return 0;
}
ofoperation_create(struct ofopgroup *group, struct rule *rule,
enum ofoperation_type type)
{
+ struct ofproto *ofproto = group->ofproto;
struct ofoperation *op;
assert(!rule->pending);
list_push_back(&group->ops, &op->group_node);
op->rule = rule;
op->type = type;
- op->status = -1;
op->flow_cookie = rule->flow_cookie;
if (type == OFOPERATION_DELETE) {
- hmap_insert(&op->group->ofproto->deletions, &op->hmap_node,
+ hmap_insert(&ofproto->deletions, &op->hmap_node,
cls_rule_hash(&rule->cr, rule->table_id));
}
}
hmap_remove(&group->ofproto->deletions, &op->hmap_node);
}
list_remove(&op->group_node);
- free(op->actions);
+ free(op->ofpacts);
free(op);
if (list_is_empty(&group->ops) && !list_is_empty(&group->ofproto_node)) {
struct ofproto *ofproto = rule->ofproto;
assert(rule->pending == op);
- assert(op->status < 0);
if (!error
&& !group->error
} else {
oftable_substitute_rule(rule, op->victim);
ofproto_rule_destroy__(rule);
+ op->rule = NULL;
}
break;
if (!error) {
rule->modified = time_msec();
} else {
- free(rule->actions);
- rule->actions = op->actions;
- rule->n_actions = op->n_actions;
- op->actions = NULL;
+ free(rule->ofpacts);
+ rule->ofpacts = op->ofpacts;
+ rule->ofpacts_len = op->ofpacts_len;
+ op->ofpacts = NULL;
}
break;
{
memset(table, 0, sizeof *table);
classifier_init(&table->cls);
+ table->max_flows = UINT_MAX;
}
/* Destroys 'table', including its classifier and eviction groups.