/*
- * Copyright (c) 2009, 2010, 2011, 2012 Nicira Networks.
+ * Copyright (c) 2009, 2010, 2011, 2012 Nicira, Inc.
* Copyright (c) 2010 Jean Tourrilhes - HP-Labs.
*
* Licensed under the Apache License, Version 2.0 (the "License");
#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 ofproto *ofproto; /* Owning ofproto. */
struct list ofproto_node; /* In ofproto's "pending" list. */
struct list ops; /* List of "struct ofoperation"s. */
+ int n_running; /* Number of ops still pending. */
/* Data needed to send OpenFlow reply on failure or to send a buffered
* packet on success.
struct ofconn *ofconn; /* ofconn for reply (but see note above). */
struct ofp_header *request; /* Original request (truncated at 64 bytes). */
uint32_t buffer_id; /* Buffer id from original request. */
- int error; /* 0 if no error yet, otherwise error code. */
};
static struct ofopgroup *ofopgroup_create_unattached(struct ofproto *);
const struct ofp_header *,
uint32_t buffer_id);
static void ofopgroup_submit(struct ofopgroup *);
-static void ofopgroup_destroy(struct ofopgroup *);
+static void ofopgroup_complete(struct ofopgroup *);
/* A single flow table operation. */
struct ofoperation {
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. */
+
+ /* OFOPERATION_ADD. */
+ struct rule *victim; /* Rule being replaced, if any.. */
+
+ /* OFOPERATION_MODIFY: The old actions, if the actions are changing. */
+ struct ofpact *ofpacts;
+ size_t ofpacts_len;
+
ovs_be64 flow_cookie; /* Rule's old flow cookie. */
+ enum ofperr error; /* 0 if no error. */
};
-static void ofoperation_create(struct ofopgroup *, struct rule *,
- enum ofoperation_type);
+static struct ofoperation *ofoperation_create(struct ofopgroup *,
+ struct rule *,
+ enum ofoperation_type);
static void ofoperation_destroy(struct ofoperation *);
/* oftable. */
static uint64_t pick_datapath_id(const struct ofproto *);
static uint64_t pick_fallback_dpid(void);
static void ofproto_destroy__(struct ofproto *);
-static void set_internal_devs_mtu(struct ofproto *);
+static void update_mtu(struct ofproto *, struct ofport *);
/* unixctl. */
static void ofproto_unixctl_init(void);
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;
error = ofproto->ofproto_class->construct(ofproto);
if (error) {
assert(ofproto->n_tables);
ofproto->datapath_id = pick_datapath_id(ofproto);
- VLOG_INFO("using datapath ID %016"PRIx64, ofproto->datapath_id);
init_ports(ofproto);
*ofprotop = ofproto;
}
}
+uint64_t
+ofproto_get_datapath_id(const struct ofproto *ofproto)
+{
+ return ofproto->datapath_id;
+}
+
void
ofproto_set_datapath_id(struct ofproto *p, uint64_t datapath_id)
{
uint64_t old_dpid = p->datapath_id;
p->datapath_id = datapath_id ? datapath_id : pick_datapath_id(p);
if (p->datapath_id != old_dpid) {
- VLOG_INFO("datapath ID changed to %016"PRIx64, p->datapath_id);
-
/* Force all active connections to reconnect, since there is no way to
* notify a controller that the datapath ID has changed. */
ofproto_reconnect_controllers(p);
if (mfr_desc) {
if (strlen(mfr_desc) >= sizeof ods->mfr_desc) {
- VLOG_WARN("truncating mfr_desc, must be less than %zu characters",
- sizeof ods->mfr_desc);
+ VLOG_WARN("%s: truncating mfr_desc, must be less than %zu bytes",
+ p->name, sizeof ods->mfr_desc);
}
free(p->mfr_desc);
p->mfr_desc = xstrdup(mfr_desc);
}
if (hw_desc) {
if (strlen(hw_desc) >= sizeof ods->hw_desc) {
- VLOG_WARN("truncating hw_desc, must be less than %zu characters",
- sizeof ods->hw_desc);
+ VLOG_WARN("%s: truncating hw_desc, must be less than %zu bytes",
+ p->name, sizeof ods->hw_desc);
}
free(p->hw_desc);
p->hw_desc = xstrdup(hw_desc);
}
if (sw_desc) {
if (strlen(sw_desc) >= sizeof ods->sw_desc) {
- VLOG_WARN("truncating sw_desc, must be less than %zu characters",
- sizeof ods->sw_desc);
+ VLOG_WARN("%s: truncating sw_desc, must be less than %zu bytes",
+ p->name, sizeof ods->sw_desc);
}
free(p->sw_desc);
p->sw_desc = xstrdup(sw_desc);
}
if (serial_desc) {
if (strlen(serial_desc) >= sizeof ods->serial_num) {
- VLOG_WARN("truncating serial_desc, must be less than %zu "
- "characters",
- sizeof ods->serial_num);
+ VLOG_WARN("%s: truncating serial_desc, must be less than %zu "
+ "bytes", p->name, sizeof ods->serial_num);
}
free(p->serial_desc);
p->serial_desc = xstrdup(serial_desc);
}
if (dp_desc) {
if (strlen(dp_desc) >= sizeof ods->dp_desc) {
- VLOG_WARN("truncating dp_desc, must be less than %zu characters",
- sizeof ods->dp_desc);
+ VLOG_WARN("%s: truncating dp_desc, must be less than %zu bytes",
+ p->name, sizeof ods->dp_desc);
}
free(p->dp_desc);
p->dp_desc = xstrdup(dp_desc);
{
struct ofport *ofport = ofproto_get_port(ofproto, ofp_port);
if (!ofport) {
- VLOG_WARN("%s: cannot get STP status on nonexistent port %"PRIu16,
- ofproto->name, ofp_port);
+ VLOG_WARN_RL(&rl, "%s: cannot get STP status on nonexistent "
+ "port %"PRIu16, ofproto->name, ofp_port);
return ENODEV;
}
int
ofproto_run(struct ofproto *p)
{
+ struct sset changed_netdevs;
+ const char *changed_netdev;
struct ofport *ofport;
- char *devname;
int error;
error = p->ofproto_class->run(p);
}
if (p->ofproto_class->port_poll) {
+ char *devname;
+
while ((error = p->ofproto_class->port_poll(p, &devname)) != EAGAIN) {
process_port_change(p, error, devname);
}
}
+ /* Update OpenFlow port status for any port whose netdev has changed.
+ *
+ * Refreshing a given 'ofport' can cause an arbitrary ofport to be
+ * destroyed, so it's not safe to update ports directly from the
+ * HMAP_FOR_EACH loop, or even to use HMAP_FOR_EACH_SAFE. Instead, we
+ * need this two-phase approach. */
+ sset_init(&changed_netdevs);
HMAP_FOR_EACH (ofport, hmap_node, &p->ports) {
unsigned int change_seq = netdev_change_seq(ofport->netdev);
if (ofport->change_seq != change_seq) {
ofport->change_seq = change_seq;
- update_port(p, netdev_get_name(ofport->netdev));
+ sset_add(&changed_netdevs, netdev_get_name(ofport->netdev));
}
}
+ SSET_FOR_EACH (changed_netdev, &changed_netdevs) {
+ update_port(p, changed_netdev);
+ }
+ sset_destroy(&changed_netdevs);
switch (p->state) {
case S_OPENFLOW:
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: %s", p->name, 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);
}
}
sset_destroy(&devnames);
}
-/* Opens and returns a netdev for 'ofproto_port', or a null pointer if the
- * netdev cannot be opened. On success, also fills in 'opp'. */
+/* Opens and returns a netdev for 'ofproto_port' in 'ofproto', or a null
+ * pointer if the netdev cannot be opened. On success, also fills in
+ * 'opp'. */
static struct netdev *
-ofport_open(const struct ofproto_port *ofproto_port, struct ofp_phy_port *opp)
+ofport_open(const struct ofproto *ofproto,
+ const struct ofproto_port *ofproto_port,
+ struct ofputil_phy_port *pp)
{
- uint32_t curr, advertised, supported, peer;
enum netdev_flags flags;
struct netdev *netdev;
int error;
error = netdev_open(ofproto_port->name, ofproto_port->type, &netdev);
if (error) {
- VLOG_WARN_RL(&rl, "ignoring port %s (%"PRIu16") because netdev %s "
+ VLOG_WARN_RL(&rl, "%s: ignoring port %s (%"PRIu16") because netdev %s "
"cannot be opened (%s)",
+ ofproto->name,
ofproto_port->name, ofproto_port->ofp_port,
ofproto_port->name, strerror(error));
return NULL;
}
+ pp->port_no = ofproto_port->ofp_port;
+ netdev_get_etheraddr(netdev, pp->hw_addr);
+ ovs_strlcpy(pp->name, ofproto_port->name, sizeof pp->name);
netdev_get_flags(netdev, &flags);
- netdev_get_features(netdev, &curr, &advertised, &supported, &peer);
-
- opp->port_no = htons(ofproto_port->ofp_port);
- netdev_get_etheraddr(netdev, opp->hw_addr);
- ovs_strzcpy(opp->name, ofproto_port->name, sizeof opp->name);
- opp->config = flags & NETDEV_UP ? 0 : htonl(OFPPC_PORT_DOWN);
- opp->state = netdev_get_carrier(netdev) ? 0 : htonl(OFPPS_LINK_DOWN);
- opp->curr = htonl(curr);
- opp->advertised = htonl(advertised);
- opp->supported = htonl(supported);
- opp->peer = htonl(peer);
+ pp->config = flags & NETDEV_UP ? 0 : OFPUTIL_PC_PORT_DOWN;
+ pp->state = netdev_get_carrier(netdev) ? 0 : OFPUTIL_PS_LINK_DOWN;
+ netdev_get_features(netdev, &pp->curr, &pp->advertised,
+ &pp->supported, &pp->peer);
+ pp->curr_speed = netdev_features_to_bps(pp->curr);
+ pp->max_speed = netdev_features_to_bps(pp->supported);
return netdev;
}
/* Returns true if most fields of 'a' and 'b' are equal. Differences in name,
- * port number, and 'config' bits other than OFPPC_PORT_DOWN are
+ * port number, and 'config' bits other than OFPUTIL_PS_LINK_DOWN are
* disregarded. */
static bool
-ofport_equal(const struct ofp_phy_port *a, const struct ofp_phy_port *b)
+ofport_equal(const struct ofputil_phy_port *a,
+ const struct ofputil_phy_port *b)
{
- BUILD_ASSERT_DECL(sizeof *a == 48); /* Detect ofp_phy_port changes. */
- return (!memcmp(a->hw_addr, b->hw_addr, sizeof a->hw_addr)
+ return (eth_addr_equals(a->hw_addr, b->hw_addr)
&& a->state == b->state
- && !((a->config ^ b->config) & htonl(OFPPC_PORT_DOWN))
+ && !((a->config ^ b->config) & OFPUTIL_PC_PORT_DOWN)
&& a->curr == b->curr
&& a->advertised == b->advertised
&& a->supported == b->supported
- && a->peer == b->peer);
+ && a->peer == b->peer
+ && a->curr_speed == b->curr_speed
+ && a->max_speed == b->max_speed);
}
/* Adds an ofport to 'p' initialized based on the given 'netdev' and 'opp'.
* one with the same name or port number). */
static void
ofport_install(struct ofproto *p,
- struct netdev *netdev, const struct ofp_phy_port *opp)
+ struct netdev *netdev, const struct ofputil_phy_port *pp)
{
const char *netdev_name = netdev_get_name(netdev);
struct ofport *ofport;
- int dev_mtu;
int error;
/* Create ofport. */
ofport->ofproto = p;
ofport->netdev = netdev;
ofport->change_seq = netdev_change_seq(netdev);
- ofport->opp = *opp;
- ofport->ofp_port = ntohs(opp->port_no);
+ ofport->pp = *pp;
+ ofport->ofp_port = pp->port_no;
/* Add port to 'p'. */
hmap_insert(&p->ports, &ofport->hmap_node, hash_int(ofport->ofp_port, 0));
shash_add(&p->port_by_name, netdev_name, ofport);
- if (!netdev_get_mtu(netdev, &dev_mtu)) {
- set_internal_devs_mtu(p);
- ofport->mtu = dev_mtu;
- } else {
- ofport->mtu = 0;
- }
+ update_mtu(p, ofport);
/* Let the ofproto_class initialize its private data. */
error = p->ofproto_class->port_construct(ofport);
if (error) {
goto error;
}
- connmgr_send_port_status(p->connmgr, opp, OFPPR_ADD);
+ connmgr_send_port_status(p->connmgr, pp, OFPPR_ADD);
return;
error:
static void
ofport_remove(struct ofport *ofport)
{
- connmgr_send_port_status(ofport->ofproto->connmgr, &ofport->opp,
+ connmgr_send_port_status(ofport->ofproto->connmgr, &ofport->pp,
OFPPR_DELETE);
ofport_destroy(ofport);
}
}
}
-/* Updates 'port' with new 'opp' description.
+/* Updates 'port' with new 'pp' description.
*
* Does not handle a name or port number change. The caller must implement
* such a change as a delete followed by an add. */
static void
-ofport_modified(struct ofport *port, struct ofp_phy_port *opp)
+ofport_modified(struct ofport *port, struct ofputil_phy_port *pp)
{
- memcpy(port->opp.hw_addr, opp->hw_addr, ETH_ADDR_LEN);
- port->opp.config = ((port->opp.config & ~htonl(OFPPC_PORT_DOWN))
- | (opp->config & htonl(OFPPC_PORT_DOWN)));
- port->opp.state = opp->state;
- port->opp.curr = opp->curr;
- port->opp.advertised = opp->advertised;
- port->opp.supported = opp->supported;
- port->opp.peer = opp->peer;
+ memcpy(port->pp.hw_addr, pp->hw_addr, ETH_ADDR_LEN);
+ port->pp.config = ((port->pp.config & ~OFPUTIL_PC_PORT_DOWN)
+ | (pp->config & OFPUTIL_PC_PORT_DOWN));
+ port->pp.state = pp->state;
+ port->pp.curr = pp->curr;
+ port->pp.advertised = pp->advertised;
+ port->pp.supported = pp->supported;
+ port->pp.peer = pp->peer;
+ port->pp.curr_speed = pp->curr_speed;
+ port->pp.max_speed = pp->max_speed;
- connmgr_send_port_status(port->ofproto->connmgr, &port->opp, OFPPR_MODIFY);
+ connmgr_send_port_status(port->ofproto->connmgr, &port->pp, OFPPR_MODIFY);
}
/* Update OpenFlow 'state' in 'port' and notify controller. */
void
-ofproto_port_set_state(struct ofport *port, ovs_be32 state)
+ofproto_port_set_state(struct ofport *port, enum ofputil_port_state state)
{
- if (port->opp.state != state) {
- port->opp.state = state;
- connmgr_send_port_status(port->ofproto->connmgr, &port->opp,
+ if (port->pp.state != state) {
+ port->pp.state = state;
+ connmgr_send_port_status(port->ofproto->connmgr, &port->pp,
OFPPR_MODIFY);
}
}
update_port(struct ofproto *ofproto, const char *name)
{
struct ofproto_port ofproto_port;
- struct ofp_phy_port opp;
+ struct ofputil_phy_port pp;
struct netdev *netdev;
struct ofport *port;
/* Fetch 'name''s location and properties from the datapath. */
netdev = (!ofproto_port_query_by_name(ofproto, name, &ofproto_port)
- ? ofport_open(&ofproto_port, &opp)
+ ? ofport_open(ofproto, &ofproto_port, &pp)
: NULL);
if (netdev) {
port = ofproto_get_port(ofproto, ofproto_port.ofp_port);
if (port && !strcmp(netdev_get_name(port->netdev), name)) {
struct netdev *old_netdev = port->netdev;
- int dev_mtu;
/* 'name' hasn't changed location. Any properties changed? */
- if (!ofport_equal(&port->opp, &opp)) {
- ofport_modified(port, &opp);
+ if (!ofport_equal(&port->pp, &pp)) {
+ ofport_modified(port, &pp);
}
- /* If this is a non-internal port and the MTU changed, check
- * if the datapath's MTU needs to be updated. */
- if (strcmp(netdev_get_type(netdev), "internal")
- && !netdev_get_mtu(netdev, &dev_mtu)
- && port->mtu != dev_mtu) {
- set_internal_devs_mtu(ofproto);
- port->mtu = dev_mtu;
- }
+ update_mtu(ofproto, port);
/* Install the newly opened netdev in case it has changed.
* Don't close the old netdev yet in case port_modified has to
ofport_remove(port);
}
ofport_remove_with_name(ofproto, name);
- ofport_install(ofproto, netdev, &opp);
+ ofport_install(ofproto, netdev, &pp);
}
} else {
/* Any port named 'name' is gone now. */
OFPROTO_PORT_FOR_EACH (&ofproto_port, &dump, p) {
uint16_t ofp_port = ofproto_port.ofp_port;
if (ofproto_get_port(p, ofp_port)) {
- VLOG_WARN_RL(&rl, "ignoring duplicate port %"PRIu16" in datapath",
- ofp_port);
+ VLOG_WARN_RL(&rl, "%s: ignoring duplicate port %"PRIu16" "
+ "in datapath", p->name, ofp_port);
} else if (shash_find(&p->port_by_name, ofproto_port.name)) {
- VLOG_WARN_RL(&rl, "ignoring duplicate device %s in datapath",
- ofproto_port.name);
+ VLOG_WARN_RL(&rl, "%s: ignoring duplicate device %s in datapath",
+ p->name, ofproto_port.name);
} else {
- struct ofp_phy_port opp;
+ struct ofputil_phy_port pp;
struct netdev *netdev;
- netdev = ofport_open(&ofproto_port, &opp);
+ netdev = ofport_open(p, &ofproto_port, &pp);
if (netdev) {
- ofport_install(p, netdev, &opp);
+ ofport_install(p, netdev, &pp);
}
}
}
return mtu ? mtu: ETH_PAYLOAD_MAX;
}
-/* Set the MTU of all datapath devices on 'p' to the minimum of the
- * non-datapath ports. */
+/* Update MTU of all datapath devices on 'p' to the minimum of the
+ * non-datapath ports in event of 'port' added or changed. */
static void
-set_internal_devs_mtu(struct ofproto *p)
+update_mtu(struct ofproto *p, struct ofport *port)
{
struct ofport *ofport;
- int mtu = find_min_mtu(p);
+ struct netdev *netdev = port->netdev;
+ int dev_mtu, old_min;
+
+ if (netdev_get_mtu(netdev, &dev_mtu)) {
+ port->mtu = 0;
+ return;
+ }
+ if (!strcmp(netdev_get_type(port->netdev), "internal")) {
+ if (dev_mtu > p->min_mtu) {
+ if (!netdev_set_mtu(port->netdev, p->min_mtu)) {
+ dev_mtu = p->min_mtu;
+ }
+ }
+ port->mtu = dev_mtu;
+ return;
+ }
+
+ /* For non-internal port find new min mtu. */
+ old_min = p->min_mtu;
+ port->mtu = dev_mtu;
+ p->min_mtu = find_min_mtu(p);
+ if (p->min_mtu == old_min) {
+ return;
+ }
HMAP_FOR_EACH (ofport, hmap_node, &p->ports) {
struct netdev *netdev = ofport->netdev;
if (!strcmp(netdev_get_type(netdev), "internal")) {
- netdev_set_mtu(netdev, mtu);
+ if (!netdev_set_mtu(netdev, p->min_mtu)) {
+ ofport->mtu = p->min_mtu;
+ }
}
}
}
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
handle_features_request(struct ofconn *ofconn, const struct ofp_header *oh)
{
struct ofproto *ofproto = ofconn_get_ofproto(ofconn);
- struct ofp_switch_features *osf;
- struct ofpbuf *buf;
+ struct ofputil_switch_features features;
struct ofport *port;
bool arp_match_ip;
- uint32_t actions;
+ struct ofpbuf *b;
- ofproto->ofproto_class->get_features(ofproto, &arp_match_ip, &actions);
- assert(actions & (1 << OFPAT_OUTPUT)); /* sanity check */
+ ofproto->ofproto_class->get_features(ofproto, &arp_match_ip,
+ &features.actions);
+ assert(features.actions & OFPUTIL_A_OUTPUT); /* sanity check */
- osf = make_openflow_xid(sizeof *osf, OFPT_FEATURES_REPLY, oh->xid, &buf);
- osf->datapath_id = htonll(ofproto->datapath_id);
- osf->n_buffers = htonl(pktbuf_capacity());
- osf->n_tables = ofproto->n_tables;
- osf->capabilities = htonl(OFPC_FLOW_STATS | OFPC_TABLE_STATS |
- OFPC_PORT_STATS | OFPC_QUEUE_STATS);
+ features.datapath_id = ofproto->datapath_id;
+ features.n_buffers = pktbuf_capacity();
+ features.n_tables = ofproto->n_tables;
+ features.capabilities = (OFPUTIL_C_FLOW_STATS | OFPUTIL_C_TABLE_STATS |
+ OFPUTIL_C_PORT_STATS | OFPUTIL_C_QUEUE_STATS);
if (arp_match_ip) {
- osf->capabilities |= htonl(OFPC_ARP_MATCH_IP);
+ features.capabilities |= OFPUTIL_C_ARP_MATCH_IP;
}
- osf->actions = htonl(actions);
+ b = ofputil_encode_switch_features(&features, ofconn_get_protocol(ofconn),
+ oh->xid);
HMAP_FOR_EACH (port, hmap_node, &ofproto->ports) {
- ofpbuf_put(buf, &port->opp, sizeof port->opp);
+ ofputil_put_switch_features_port(&port->pp, b);
}
- ofconn_send_reply(ofconn, buf);
+ ofconn_send_reply(ofconn, b);
return 0;
}
}
}
ofconn_set_invalid_ttl_to_controller(ofconn,
- (flags & OFPC_INVALID_TTL_TO_CONTROLLER));
+ (flags & OFPC_INVALID_TTL_TO_CONTROLLER));
ofconn_set_miss_send_len(ofconn, ntohs(osc->miss_send_len));
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;
}
static void
-update_port_config(struct ofport *port, ovs_be32 config, ovs_be32 mask)
+update_port_config(struct ofport *port,
+ enum ofputil_port_config config,
+ enum ofputil_port_config mask)
{
- ovs_be32 old_config = port->opp.config;
+ enum ofputil_port_config old_config = port->pp.config;
+ enum ofputil_port_config toggle;
- mask &= config ^ port->opp.config;
- if (mask & htonl(OFPPC_PORT_DOWN)) {
- if (config & htonl(OFPPC_PORT_DOWN)) {
+ toggle = (config ^ port->pp.config) & mask;
+ if (toggle & OFPUTIL_PC_PORT_DOWN) {
+ if (config & OFPUTIL_PC_PORT_DOWN) {
netdev_turn_flags_off(port->netdev, NETDEV_UP, true);
} else {
netdev_turn_flags_on(port->netdev, NETDEV_UP, true);
}
+ toggle &= ~OFPUTIL_PC_PORT_DOWN;
}
- port->opp.config ^= mask & (htonl(OFPPC_NO_RECV | OFPPC_NO_RECV_STP |
- OFPPC_NO_FLOOD | OFPPC_NO_FWD |
- OFPPC_NO_PACKET_IN));
- if (port->opp.config != old_config) {
+ port->pp.config ^= toggle;
+ if (port->pp.config != old_config) {
port->ofproto->ofproto_class->port_reconfigured(port, old_config);
}
}
handle_port_mod(struct ofconn *ofconn, const struct ofp_header *oh)
{
struct ofproto *p = ofconn_get_ofproto(ofconn);
- const struct ofp_port_mod *opm = (const struct ofp_port_mod *) oh;
+ struct ofputil_port_mod pm;
struct ofport *port;
- int error;
+ enum ofperr error;
error = reject_slave_controller(ofconn);
if (error) {
return error;
}
- port = ofproto_get_port(p, ntohs(opm->port_no));
+ error = ofputil_decode_port_mod(oh, &pm);
+ if (error) {
+ return error;
+ }
+
+ port = ofproto_get_port(p, pm.port_no);
if (!port) {
return OFPERR_OFPPMFC_BAD_PORT;
- } else if (memcmp(port->opp.hw_addr, opm->hw_addr, OFP_ETH_ALEN)) {
+ } else if (!eth_addr_equals(port->pp.hw_addr, pm.hw_addr)) {
return OFPERR_OFPPMFC_BAD_HW_ADDR;
} else {
- update_port_config(port, opm->config, opm->mask);
- if (opm->advertise) {
- netdev_set_advertisements(port->netdev, ntohl(opm->advertise));
+ update_port_config(port, pm.config, pm.mask);
+ if (pm.advertise) {
+ netdev_set_advertisements(port->netdev, pm.advertise);
}
}
return 0;
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));
}
ofproto_port_get_stats(port, &stats);
ops = ofputil_append_stats_reply(sizeof *ops, replies);
- ops->port_no = port->opp.port_no;
+ ops->port_no = htons(port->pp.port_no);
memset(ops->pad, 0, sizeof ops->pad);
put_32aligned_be64(&ops->rx_packets, htonll(stats.rx_packets));
put_32aligned_be64(&ops->tx_packets, htonll(stats.tx_packets));
return 0;
}
+static enum ofperr
+handle_port_desc_stats_request(struct ofconn *ofconn,
+ const struct ofp_stats_msg *osm)
+{
+ struct ofproto *p = ofconn_get_ofproto(ofconn);
+ struct ofport *port;
+ struct list replies;
+
+ ofputil_start_stats_reply(osm, &replies);
+
+ HMAP_FOR_EACH (port, hmap_node, &p->ports) {
+ ofputil_append_port_desc_stats_reply(ofconn_get_protocol(ofconn),
+ &port->pp, &replies);
+ }
+
+ ofconn_send_replies(ofconn, &replies);
+ return 0;
+}
+
static void
calc_flow_duration__(long long int start, long long int now,
uint32_t *sec, uint32_t *nsec)
}
static struct oftable *
-next_visible_table(struct ofproto *ofproto, uint8_t table_id)
+next_visible_table(const struct ofproto *ofproto, uint8_t table_id)
{
struct oftable *table;
}
static struct oftable *
-first_matching_table(struct ofproto *ofproto, uint8_t table_id)
+first_matching_table(const struct ofproto *ofproto, uint8_t table_id)
{
if (table_id == 0xff) {
return next_visible_table(ofproto, 0);
}
static struct oftable *
-next_matching_table(struct ofproto *ofproto,
- struct oftable *table, uint8_t table_id)
+next_matching_table(const struct ofproto *ofproto,
+ const struct oftable *table, uint8_t table_id)
{
return (table_id == 0xff
? next_visible_table(ofproto, (table - ofproto->tables) + 1)
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");
}
: -1);
}
+/* Checks the health of the CFM for 'ofp_port' within 'ofproto'. Returns an
+ * integer value between 0 and 100 to indicate the health of the port as a
+ * percentage which is the average of cfm health of all the remote_mpids or
+ * returns -1 if CFM is not enabled on 'ofport'. */
+int
+ofproto_port_get_cfm_health(const struct ofproto *ofproto, uint16_t ofp_port)
+{
+ struct ofport *ofport = ofproto_get_port(ofproto, ofp_port);
+ return (ofport && ofproto->ofproto_class->get_cfm_health
+ ? ofproto->ofproto_class->get_cfm_health(ofport)
+ : -1);
+}
+
static enum ofperr
handle_aggregate_stats_request(struct ofconn *ofconn,
const struct ofp_stats_msg *osm)
struct ofp_queue_stats *reply;
reply = ofputil_append_stats_reply(sizeof *reply, &cbdata->replies);
- reply->port_no = cbdata->ofport->opp.port_no;
+ reply->port_no = htons(cbdata->ofport->pp.port_no);
memset(reply->pad, 0, sizeof reply->pad);
reply->queue_id = htonl(queue_id);
put_32aligned_be64(&reply->tx_bytes, htonll(stats->tx_bytes));
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;
} else if (victim && victim->pending) {
error = OFPROTO_POSTPONE;
} else {
+ struct ofoperation *op;
struct rule *evict;
if (classifier_count(&table->cls) > table->max_flows) {
}
group = ofopgroup_create(ofproto, ofconn, request, fm->buffer_id);
- ofoperation_create(group, rule, OFOPERATION_ADD);
- rule->pending->victim = victim;
+ op = ofoperation_create(group, rule, OFOPERATION_ADD);
+ op->victim = victim;
error = ofproto->ofproto_class->rule_construct(rule);
if (error) {
+ op->group->n_running--;
ofoperation_destroy(rule->pending);
} else if (evict) {
delete_flow__(evict, group);
group = ofopgroup_create(ofproto, ofconn, request, fm->buffer_id);
error = OFPERR_OFPBRC_EPERM;
LIST_FOR_EACH (rule, ofproto_node, rules) {
+ struct ofoperation *op;
+ bool actions_changed;
+ ovs_be64 new_cookie;
+
if (rule_is_modifiable(rule)) {
/* At least one rule is modifiable, don't report EPERM error. */
error = 0;
continue;
}
- if (!ofputil_actions_equal(fm->actions, fm->n_actions,
- rule->actions, rule->n_actions)) {
- 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);
+ actions_changed = !ofpacts_equal(fm->ofpacts, fm->ofpacts_len,
+ rule->ofpacts, rule->ofpacts_len);
+ new_cookie = (fm->new_cookie != htonll(UINT64_MAX)
+ ? fm->new_cookie
+ : rule->flow_cookie);
+ if (!actions_changed && new_cookie == rule->flow_cookie) {
+ /* No change at all. */
+ continue;
+ }
+
+ op = ofoperation_create(group, rule, OFOPERATION_MODIFY);
+ rule->flow_cookie = new_cookie;
+ if (actions_changed) {
+ op->ofpacts = rule->ofpacts;
+ op->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();
+ ofoperation_complete(op, 0);
}
- rule->flow_cookie = fm->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. */
* OFPRR_HARD_TIMEOUT or OFPRR_IDLE_TIMEOUT), and then removes 'rule' from its
* ofproto.
*
+ * 'rule' must not have a pending operation (that is, 'rule->pending' must be
+ * NULL).
+ *
* ofproto implementation ->run() functions should use this function to expire
* OpenFlow flows. */
void
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_flow_mod_table_id(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
default:
if (fm->command > 0xff) {
- VLOG_WARN_RL(&rl, "flow_mod has explicit table_id but "
- "flow_mod_table_id extension is not enabled");
+ VLOG_WARN_RL(&rl, "%s: flow_mod has explicit table_id but "
+ "flow_mod_table_id extension is not enabled",
+ ofproto->name);
}
return OFPERR_OFPFMFC_BAD_COMMAND;
}
role = ntohl(nrr->role);
if (role != NX_ROLE_OTHER && role != NX_ROLE_MASTER
&& role != NX_ROLE_SLAVE) {
- return OFPERR_NXBRC_BAD_ROLE;
+ return OFPERR_OFPRRFC_BAD_ROLE;
}
if (ofconn_get_role(ofconn) != role
{
const struct nx_flow_mod_table_id *msg
= (const struct nx_flow_mod_table_id *) oh;
+ enum ofputil_protocol cur, next;
+
+ cur = ofconn_get_protocol(ofconn);
+ next = ofputil_protocol_set_tid(cur, msg->set != 0);
+ ofconn_set_protocol(ofconn, next);
- ofconn_set_flow_mod_table_id(ofconn, msg->set != 0);
return 0;
}
{
const struct nx_set_flow_format *msg
= (const struct nx_set_flow_format *) oh;
- uint32_t format;
+ enum ofputil_protocol cur, next;
+ enum ofputil_protocol next_base;
- format = ntohl(msg->format);
- if (format != NXFF_OPENFLOW10 && format != NXFF_NXM) {
+ next_base = ofputil_nx_flow_format_to_protocol(ntohl(msg->format));
+ if (!next_base) {
return OFPERR_OFPBRC_EPERM;
}
- if (format != ofconn_get_flow_format(ofconn)
- && ofconn_has_pending_opgroups(ofconn)) {
- /* Avoid sending async messages in surprising flow format. */
+ cur = ofconn_get_protocol(ofconn);
+ next = ofputil_protocol_set_base(cur, next_base);
+ if (cur != next && ofconn_has_pending_opgroups(ofconn)) {
+ /* Avoid sending async messages in surprising protocol. */
return OFPROTO_POSTPONE;
}
- ofconn_set_flow_format(ofconn, format);
+ ofconn_set_protocol(ofconn, next);
return 0;
}
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, OFPT_BARRIER_REPLY, oh->xid, &buf);
+ make_openflow_xid(sizeof *oh, OFPT10_BARRIER_REPLY, oh->xid, &buf);
ofconn_send_reply(ofconn, buf);
return 0;
}
case OFPUTIL_OFPST_QUEUE_REQUEST:
return handle_queue_stats_request(ofconn, msg->data);
+ case OFPUTIL_OFPST_PORT_DESC_REQUEST:
+ return handle_port_desc_stats_request(ofconn, msg->data);
+
case OFPUTIL_MSG_INVALID:
case OFPUTIL_OFPT_HELLO:
case OFPUTIL_OFPT_ERROR:
case OFPUTIL_OFPST_PORT_REPLY:
case OFPUTIL_OFPST_TABLE_REPLY:
case OFPUTIL_OFPST_AGGREGATE_REPLY:
+ case OFPUTIL_OFPST_PORT_DESC_REPLY:
case OFPUTIL_NXT_ROLE_REPLY:
case OFPUTIL_NXT_FLOW_REMOVED:
case OFPUTIL_NXT_PACKET_IN:
case OFPUTIL_NXST_FLOW_REPLY:
case OFPUTIL_NXST_AGGREGATE_REPLY:
default:
- if (oh->type == OFPT_STATS_REQUEST || oh->type == OFPT_STATS_REPLY) {
- return OFPERR_OFPBRC_BAD_STAT;
- } else {
- return OFPERR_OFPBRC_BAD_TYPE;
- }
+ return (oh->type == OFPT10_STATS_REQUEST ||
+ oh->type == OFPT10_STATS_REPLY
+ ? OFPERR_OFPBRC_BAD_STAT
+ : OFPERR_OFPBRC_BAD_TYPE);
}
}
static void
ofopgroup_submit(struct ofopgroup *group)
{
- if (list_is_empty(&group->ops)) {
- ofopgroup_destroy(group);
+ if (!group->n_running) {
+ ofopgroup_complete(group);
} else {
list_push_back(&group->ofproto->pending, &group->ofproto_node);
group->ofproto->n_pending++;
}
static void
-ofopgroup_destroy(struct ofopgroup *group)
+ofopgroup_complete(struct ofopgroup *group)
{
- assert(list_is_empty(&group->ops));
+ struct ofproto *ofproto = group->ofproto;
+ struct ofoperation *op, *next_op;
+ int error;
+
+ assert(!group->n_running);
+
+ error = 0;
+ LIST_FOR_EACH (op, group_node, &group->ops) {
+ if (op->error) {
+ error = op->error;
+ break;
+ }
+ }
+
+ if (!error && group->ofconn && group->buffer_id != UINT32_MAX) {
+ LIST_FOR_EACH (op, group_node, &group->ops) {
+ if (op->type != OFOPERATION_DELETE) {
+ struct ofpbuf *packet;
+ uint16_t in_port;
+
+ error = ofconn_pktbuf_retrieve(group->ofconn, group->buffer_id,
+ &packet, &in_port);
+ if (packet) {
+ assert(!error);
+ error = rule_execute(op->rule, in_port, packet);
+ }
+ break;
+ }
+ }
+ }
+
+ LIST_FOR_EACH_SAFE (op, next_op, group_node, &group->ops) {
+ struct rule *rule = op->rule;
+ rule->pending = NULL;
+
+ switch (op->type) {
+ case OFOPERATION_ADD:
+ if (!op->error) {
+ ofproto_rule_destroy__(op->victim);
+ if ((rule->cr.wc.vlan_tci_mask & htons(VLAN_VID_MASK))
+ == htons(VLAN_VID_MASK)) {
+ if (ofproto->vlan_bitmap) {
+ uint16_t vid = vlan_tci_to_vid(rule->cr.flow.vlan_tci);
+
+ if (!bitmap_is_set(ofproto->vlan_bitmap, vid)) {
+ bitmap_set1(ofproto->vlan_bitmap, vid);
+ ofproto->vlans_changed = true;
+ }
+ } else {
+ ofproto->vlans_changed = true;
+ }
+ }
+ } else {
+ oftable_substitute_rule(rule, op->victim);
+ ofproto_rule_destroy__(rule);
+ }
+ break;
+
+ case OFOPERATION_DELETE:
+ assert(!op->error);
+ ofproto_rule_destroy__(rule);
+ op->rule = NULL;
+ break;
+
+ case OFOPERATION_MODIFY:
+ if (!op->error) {
+ rule->modified = time_msec();
+ } else {
+ rule->flow_cookie = op->flow_cookie;
+ if (op->ofpacts) {
+ free(rule->ofpacts);
+ rule->ofpacts = op->ofpacts;
+ rule->ofpacts_len = op->ofpacts_len;
+ op->ofpacts = NULL;
+ op->ofpacts_len = 0;
+ }
+ }
+ break;
+
+ default:
+ NOT_REACHED();
+ }
+
+ ofoperation_destroy(op);
+ }
+
if (!list_is_empty(&group->ofproto_node)) {
- assert(group->ofproto->n_pending > 0);
- group->ofproto->n_pending--;
+ assert(ofproto->n_pending > 0);
+ ofproto->n_pending--;
list_remove(&group->ofproto_node);
}
if (!list_is_empty(&group->ofconn_node)) {
list_remove(&group->ofconn_node);
- if (group->error) {
- ofconn_send_error(group->ofconn, group->request, group->error);
+ if (error) {
+ ofconn_send_error(group->ofconn, group->request, error);
}
- connmgr_retry(group->ofproto->connmgr);
+ connmgr_retry(ofproto->connmgr);
}
free(group->request);
free(group);
}
/* Initiates a new operation on 'rule', of the specified 'type', within
- * 'group'. Prior to calling, 'rule' must not have any pending operation. */
-static void
+ * 'group'. Prior to calling, 'rule' must not have any pending operation.
+ *
+ * Returns the newly created ofoperation (which is also available as
+ * rule->pending). */
+static struct ofoperation *
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;
+ group->n_running++;
+
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));
}
+
+ return op;
}
static void
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)) {
- ofopgroup_destroy(group);
- }
}
/* Indicates that 'op' completed with status 'error', which is either 0 to
ofoperation_complete(struct ofoperation *op, enum ofperr error)
{
struct ofopgroup *group = op->group;
- struct rule *rule = op->rule;
- struct ofproto *ofproto = rule->ofproto;
-
- assert(rule->pending == op);
- assert(op->status < 0);
- if (!error
- && !group->error
- && op->type != OFOPERATION_DELETE
- && group->ofconn
- && group->buffer_id != UINT32_MAX
- && list_is_singleton(&op->group_node)) {
- struct ofpbuf *packet;
- uint16_t in_port;
+ assert(op->rule->pending == op);
+ assert(group->n_running > 0);
+ assert(!error || op->type != OFOPERATION_DELETE);
- error = ofconn_pktbuf_retrieve(group->ofconn, group->buffer_id,
- &packet, &in_port);
- if (packet) {
- assert(!error);
- error = rule_execute(rule, in_port, packet);
- }
- }
- if (!group->error) {
- group->error = error;
+ op->error = error;
+ if (!--group->n_running && !list_is_empty(&group->ofproto_node)) {
+ ofopgroup_complete(group);
}
-
- switch (op->type) {
- case OFOPERATION_ADD:
- if (!error) {
- ofproto_rule_destroy__(op->victim);
- if ((rule->cr.wc.vlan_tci_mask & htons(VLAN_VID_MASK))
- == htons(VLAN_VID_MASK)) {
- if (ofproto->vlan_bitmap) {
- uint16_t vid = vlan_tci_to_vid(rule->cr.flow.vlan_tci);
-
- if (!bitmap_is_set(ofproto->vlan_bitmap, vid)) {
- bitmap_set1(ofproto->vlan_bitmap, vid);
- ofproto->vlans_changed = true;
- }
- } else {
- ofproto->vlans_changed = true;
- }
- }
- } else {
- oftable_substitute_rule(rule, op->victim);
- ofproto_rule_destroy__(rule);
- }
- break;
-
- case OFOPERATION_DELETE:
- assert(!error);
- ofproto_rule_destroy__(rule);
- op->rule = NULL;
- break;
-
- case OFOPERATION_MODIFY:
- if (!error) {
- rule->modified = time_msec();
- } else {
- free(rule->actions);
- rule->actions = op->actions;
- rule->n_actions = op->n_actions;
- op->actions = NULL;
- }
- break;
-
- default:
- NOT_REACHED();
- }
- ofoperation_destroy(op);
}
struct rule *
if (!error) {
return eth_addr_to_uint64(ea);
}
- VLOG_WARN("could not get MAC address for %s (%s)",
- netdev_get_name(port->netdev), strerror(error));
+ VLOG_WARN("%s: could not get MAC address for %s (%s)",
+ ofproto->name, netdev_get_name(port->netdev),
+ strerror(error));
}
return ofproto->fallback_dpid;
}
{
memset(table, 0, sizeof *table);
classifier_init(&table->cls);
+ table->max_flows = UINT_MAX;
}
/* Destroys 'table', including its classifier and eviction groups.