#include <netinet/in.h>
#include <stdbool.h>
#include <stdlib.h>
+#include "autopath.h"
#include "byte-order.h"
#include "cfm.h"
#include "classifier.h"
struct ofport {
struct hmap_node hmap_node; /* In struct ofproto's "ports" hmap. */
struct netdev *netdev;
- struct ofp_phy_port opp; /* In host byte order. */
+ struct ofp_phy_port opp;
uint16_t odp_port;
struct cfm *cfm; /* Connectivity Fault Management, if any. */
};
static uint64_t pick_datapath_id(const struct ofproto *);
static uint64_t pick_fallback_dpid(void);
+static void ofproto_flush_flows__(struct ofproto *);
static int ofproto_expire(struct ofproto *);
static void flow_push_stats(struct ofproto *, const struct rule *,
struct flow *, uint64_t packets, uint64_t bytes,
return ofproto->datapath_id;
}
-bool
-ofproto_has_primary_controller(const struct ofproto *ofproto)
-{
- return connmgr_has_controllers(ofproto->connmgr);
-}
-
enum ofproto_fail_mode
ofproto_get_fail_mode(const struct ofproto *p)
{
shash_find_and_delete(&all_ofprotos, dpif_name(p->dpif));
- ofproto_flush_flows(p);
+ ofproto_flush_flows__(p);
connmgr_destroy(p->connmgr);
classifier_destroy(&p->cls);
hmap_destroy(&p->facets);
ofproto_port_del(struct ofproto *ofproto, uint16_t odp_port)
{
struct ofport *ofport = get_port(ofproto, odp_port);
- const char *name = ofport ? ofport->opp.name : "<unknown>";
+ const char *name = ofport ? netdev_get_name(ofport->netdev) : "<unknown>";
int error;
error = dpif_port_del(ofproto->dpif, odp_port);
VLOG_ERR("%s: failed to remove port %"PRIu16" (%s) interface (%s)",
dpif_name(ofproto->dpif), odp_port, name, strerror(error));
} else if (ofport) {
- /* 'name' is ofport->opp.name and update_port() is going to destroy
- * 'ofport'. Just in case update_port() refers to 'name' after it
+ /* 'name' is the netdev's name and update_port() is going to close the
+ * netdev. Just in case update_port() refers to 'name' after it
* destroys 'ofport', make a copy of it around the update_port()
* call. */
char *devname = xstrdup(name);
ofproto_port_is_floodable(struct ofproto *ofproto, uint16_t odp_port)
{
struct ofport *ofport = get_port(ofproto, odp_port);
- return ofport && !(ofport->opp.config & OFPPC_NO_FLOOD);
+ return ofport && !(ofport->opp.config & htonl(OFPPC_NO_FLOOD));
}
/* Sends 'packet' out of port 'port_no' within 'p'. If 'vlan_tci' is zero the
* will be sent with the VLAN TCI specified by 'vlan_tci & ~VLAN_CFI'.
*
* Returns 0 if successful, otherwise a positive errno value. */
-int
+static int
ofproto_send_packet(struct ofproto *ofproto,
uint32_t port_no, uint16_t vlan_tci,
const struct ofpbuf *packet)
}
}
-void
-ofproto_flush_flows(struct ofproto *ofproto)
+static void
+ofproto_flush_flows__(struct ofproto *ofproto)
{
struct facet *facet, *next_facet;
struct rule *rule, *next_rule;
}
dpif_flow_flush(ofproto->dpif);
+}
+
+void
+ofproto_flush_flows(struct ofproto *ofproto)
+{
+ ofproto_flush_flows__(ofproto);
connmgr_flushed(ofproto->connmgr);
}
\f
sset_init(&devnames);
HMAP_FOR_EACH (ofport, hmap_node, &p->ports) {
- sset_add(&devnames, ofport->opp.name);
+ sset_add(&devnames, netdev_get_name(ofport->netdev));
}
DPIF_PORT_FOR_EACH (&dpif_port, &dump, p->dpif) {
sset_add(&devnames, dpif_port.name);
sset_destroy(&devnames);
}
-static struct ofport *
-make_ofport(const struct dpif_port *dpif_port)
+/* Opens and returns a netdev for 'dpif_port', or a null pointer if the netdev
+ * cannot be opened. On success, also fills in 'opp'. */
+static struct netdev *
+ofport_open(const struct dpif_port *dpif_port, struct ofp_phy_port *opp)
{
+ uint32_t curr, advertised, supported, peer;
struct netdev_options netdev_options;
enum netdev_flags flags;
- struct ofport *ofport;
struct netdev *netdev;
int error;
return NULL;
}
- ofport = xzalloc(sizeof *ofport);
- ofport->netdev = netdev;
- ofport->odp_port = dpif_port->port_no;
- ofport->opp.port_no = odp_port_to_ofp_port(dpif_port->port_no);
- netdev_get_etheraddr(netdev, ofport->opp.hw_addr);
- ovs_strlcpy(ofport->opp.name, dpif_port->name, sizeof ofport->opp.name);
-
netdev_get_flags(netdev, &flags);
- ofport->opp.config = flags & NETDEV_UP ? 0 : OFPPC_PORT_DOWN;
+ netdev_get_features(netdev, &curr, &advertised, &supported, &peer);
- ofport->opp.state = netdev_get_carrier(netdev) ? 0 : OFPPS_LINK_DOWN;
+ opp->port_no = htons(odp_port_to_ofp_port(dpif_port->port_no));
+ netdev_get_etheraddr(netdev, opp->hw_addr);
+ ovs_strzcpy(opp->name, dpif_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);
- netdev_get_features(netdev,
- &ofport->opp.curr, &ofport->opp.advertised,
- &ofport->opp.supported, &ofport->opp.peer);
- return ofport;
+ return netdev;
}
static bool
}
}
-static int
-ofport_equal(const struct ofport *a_, const struct ofport *b_)
+/* 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
+ * disregarded. */
+static bool
+ofport_equal(const struct ofp_phy_port *a, const struct ofp_phy_port *b)
{
- const struct ofp_phy_port *a = &a_->opp;
- const struct ofp_phy_port *b = &b_->opp;
-
BUILD_ASSERT_DECL(sizeof *a == 48); /* Detect ofp_phy_port changes. */
- return (a->port_no == b->port_no
- && !memcmp(a->hw_addr, b->hw_addr, sizeof a->hw_addr)
- && !strcmp(a->name, b->name)
+ return (!memcmp(a->hw_addr, b->hw_addr, sizeof a->hw_addr)
&& a->state == b->state
- && a->config == b->config
+ && !((a->config ^ b->config) & htonl(OFPPC_PORT_DOWN))
&& a->curr == b->curr
&& a->advertised == b->advertised
&& a->supported == b->supported
&& a->peer == b->peer);
}
+/* Adds an ofport to 'p' initialized based on the given 'netdev' and 'opp'.
+ * The caller must ensure that 'p' does not have a conflicting ofport (that is,
+ * one with the same name or port number). */
static void
-ofport_install(struct ofproto *p, struct ofport *ofport)
+ofport_install(struct ofproto *p,
+ struct netdev *netdev, const struct ofp_phy_port *opp)
{
- const char *netdev_name = ofport->opp.name;
+ const char *netdev_name = netdev_get_name(netdev);
+ struct ofport *ofport;
+ connmgr_send_port_status(p->connmgr, opp, OFPPR_ADD);
+
+ /* Create ofport. */
+ ofport = xmalloc(sizeof *ofport);
+ ofport->netdev = netdev;
+ ofport->opp = *opp;
+ ofport->odp_port = ofp_port_to_odp_port(ntohs(opp->port_no));
+ ofport->cfm = NULL;
+
+ /* Add port to 'p'. */
netdev_monitor_add(p->netdev_monitor, ofport->netdev);
hmap_insert(&p->ports, &ofport->hmap_node, hash_int(ofport->odp_port, 0));
shash_add(&p->port_by_name, netdev_name, ofport);
}
}
+/* Removes 'ofport' from 'p' and destroys it. */
static void
ofport_remove(struct ofproto *p, struct ofport *ofport)
{
+ connmgr_send_port_status(p->connmgr, &ofport->opp, OFPPR_DELETE);
+
netdev_monitor_remove(p->netdev_monitor, ofport->netdev);
hmap_remove(&p->ports, &ofport->hmap_node);
shash_delete(&p->port_by_name,
- shash_find(&p->port_by_name, ofport->opp.name));
+ shash_find(&p->port_by_name,
+ netdev_get_name(ofport->netdev)));
if (p->sflow) {
ofproto_sflow_del_port(p->sflow, ofport->odp_port);
}
+
+ ofport_free(ofport);
+}
+
+/* If 'ofproto' contains an ofport named 'name', removes it from 'ofproto' and
+ * destroys it. */
+static void
+ofport_remove_with_name(struct ofproto *ofproto, const char *name)
+{
+ struct ofport *port = shash_find_data(&ofproto->port_by_name, name);
+ if (port) {
+ ofport_remove(ofproto, port);
+ }
+}
+
+/* Updates 'port' within 'ofproto' with the new 'netdev' and 'opp'.
+ *
+ * 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 ofproto *ofproto, struct ofport *port,
+ struct netdev *netdev, struct ofp_phy_port *opp)
+{
+ 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;
+
+ netdev_monitor_remove(ofproto->netdev_monitor, port->netdev);
+ netdev_monitor_add(ofproto->netdev_monitor, netdev);
+
+ netdev_close(port->netdev);
+ port->netdev = netdev;
+
+ connmgr_send_port_status(ofproto->connmgr, &port->opp, OFPPR_MODIFY);
}
static void
}
static void
-update_port(struct ofproto *p, const char *devname)
+update_port(struct ofproto *ofproto, const char *name)
{
struct dpif_port dpif_port;
- struct ofport *old_ofport;
- struct ofport *new_ofport;
- int error;
+ struct ofp_phy_port opp;
+ struct netdev *netdev;
+ struct ofport *port;
COVERAGE_INC(ofproto_update_port);
- /* Query the datapath for port information. */
- error = dpif_port_query_by_name(p->dpif, devname, &dpif_port);
-
- /* Find the old ofport. */
- old_ofport = shash_find_data(&p->port_by_name, devname);
- if (!error) {
- if (!old_ofport) {
- /* There's no port named 'devname' but there might be a port with
- * the same port number. This could happen if a port is deleted
- * and then a new one added in its place very quickly, or if a port
- * is renamed. In the former case we want to send an OFPPR_DELETE
- * and an OFPPR_ADD, and in the latter case we want to send a
- * single OFPPR_MODIFY. We can distinguish the cases by comparing
- * the old port's ifindex against the new port, or perhaps less
- * reliably but more portably by comparing the old port's MAC
- * against the new port's MAC. However, this code isn't that smart
- * and always sends an OFPPR_MODIFY (XXX). */
- old_ofport = get_port(p, dpif_port.port_no);
- }
- } else if (error != ENOENT && error != ENODEV) {
- VLOG_WARN_RL(&rl, "dpif_port_query_by_name returned unexpected error "
- "%s", strerror(error));
- goto exit;
- }
-
- /* Create a new ofport. */
- new_ofport = !error ? make_ofport(&dpif_port) : NULL;
-
- /* Eliminate a few pathological cases. */
- if (!old_ofport && !new_ofport) {
- goto exit;
- } else if (old_ofport && new_ofport) {
- /* Most of the 'config' bits are OpenFlow soft state, but
- * OFPPC_PORT_DOWN is maintained by the kernel. So transfer the
- * OpenFlow bits from old_ofport. (make_ofport() only sets
- * OFPPC_PORT_DOWN and leaves the other bits 0.) */
- new_ofport->opp.config |= old_ofport->opp.config & ~OFPPC_PORT_DOWN;
-
- if (ofport_equal(old_ofport, new_ofport)) {
- /* False alarm--no change. */
- ofport_free(new_ofport);
- goto exit;
+ /* Fetch 'name''s location and properties from the datapath. */
+ netdev = (!dpif_port_query_by_name(ofproto->dpif, name, &dpif_port)
+ ? ofport_open(&dpif_port, &opp)
+ : NULL);
+ if (netdev) {
+ port = get_port(ofproto, dpif_port.port_no);
+ if (port && !strcmp(netdev_get_name(port->netdev), name)) {
+ /* 'name' hasn't changed location. Any properties changed? */
+ if (!ofport_equal(&port->opp, &opp)) {
+ ofport_modified(ofproto, port, netdev, &opp);
+ } else {
+ netdev_close(netdev);
+ }
+ } else {
+ /* If 'port' is nonnull then its name differs from 'name' and thus
+ * we should delete it. If we think there's a port named 'name'
+ * then its port number must be wrong now so delete it too. */
+ if (port) {
+ ofport_remove(ofproto, port);
+ }
+ ofport_remove_with_name(ofproto, name);
+ ofport_install(ofproto, netdev, &opp);
}
+ } else {
+ /* Any port named 'name' is gone now. */
+ ofport_remove_with_name(ofproto, name);
}
-
- /* Now deal with the normal cases. */
- if (old_ofport) {
- ofport_remove(p, old_ofport);
- }
- if (new_ofport) {
- ofport_install(p, new_ofport);
- }
- connmgr_send_port_status(p->connmgr,
- new_ofport ? &new_ofport->opp : &old_ofport->opp,
- (!old_ofport ? OFPPR_ADD
- : !new_ofport ? OFPPR_DELETE
- : OFPPR_MODIFY));
- ofport_free(old_ofport);
-
-exit:
dpif_port_destroy(&dpif_port);
}
DPIF_PORT_FOR_EACH (&dpif_port, &dump, p->dpif) {
if (!ofport_conflicts(p, &dpif_port)) {
- struct ofport *ofport = make_ofport(&dpif_port);
- if (ofport) {
- ofport_install(p, ofport);
+ struct ofp_phy_port opp;
+ struct netdev *netdev;
+
+ netdev = ofport_open(&dpif_port, &opp);
+ if (netdev) {
+ ofport_install(p, netdev, &opp);
}
}
}
(1u << OFPAT_ENQUEUE));
HMAP_FOR_EACH (port, hmap_node, &ofproto->ports) {
- hton_ofp_phy_port(ofpbuf_put(buf, &port->opp, sizeof port->opp));
+ ofpbuf_put(buf, &port->opp, sizeof port->opp);
}
ofconn_send_reply(ofconn, buf);
const struct ofport *ofport = get_port(ctx->ofproto, port);
if (ofport) {
- if (ofport->opp.config & OFPPC_NO_FWD) {
+ if (ofport->opp.config & htonl(OFPPC_NO_FWD)) {
/* Forwarding disabled on port. */
return;
}
}
static void
-flood_packets(struct ofproto *ofproto, uint16_t odp_in_port, uint32_t mask,
+flood_packets(struct ofproto *ofproto, uint16_t odp_in_port, ovs_be32 mask,
uint16_t *nf_output_iface, struct ofpbuf *odp_actions)
{
struct ofport *ofport;
}
break;
case OFPP_FLOOD:
- flood_packets(ctx->ofproto, ctx->flow.in_port, OFPPC_NO_FLOOD,
+ flood_packets(ctx->ofproto, ctx->flow.in_port, htonl(OFPPC_NO_FLOOD),
&ctx->nf_output_iface, ctx->odp_actions);
break;
case OFPP_ALL:
- flood_packets(ctx->ofproto, ctx->flow.in_port, 0,
+ flood_packets(ctx->ofproto, ctx->flow.in_port, htonl(0),
&ctx->nf_output_iface, ctx->odp_actions);
break;
case OFPP_CONTROLLER:
const struct nx_action_set_tunnel *nast;
const struct nx_action_set_queue *nasq;
const struct nx_action_multipath *nam;
+ const struct nx_action_autopath *naa;
enum nx_action_subtype subtype = ntohs(nah->subtype);
+ const struct ofhooks *ofhooks = ctx->ofproto->ofhooks;
struct xlate_reg_state state;
+ uint16_t autopath_port;
ovs_be64 tun_id;
assert(nah->vendor == htonl(NX_VENDOR_ID));
multipath_execute(nam, &ctx->flow);
break;
+ case NXAST_AUTOPATH:
+ naa = (const struct nx_action_autopath *) nah;
+ autopath_port = (ofhooks->autopath_cb
+ ? ofhooks->autopath_cb(&ctx->flow, ntohl(naa->id),
+ &ctx->tags, ctx->ofproto->aux)
+ : OFPP_NONE);
+ autopath_execute(naa, &ctx->flow, autopath_port);
+ break;
+
/* If you add a new action here that modifies flow data, don't forget to
* update the flow key in ctx->flow at the same time. */
const struct ofport *port;
port = get_port(ctx->ofproto, ctx->flow.in_port);
- if (port && port->opp.config & (OFPPC_NO_RECV | OFPPC_NO_RECV_STP) &&
+ if (port && port->opp.config & htonl(OFPPC_NO_RECV | OFPPC_NO_RECV_STP) &&
port->opp.config & (eth_addr_equals(ctx->flow.dl_dst, eth_addr_stp)
- ? OFPPC_NO_RECV_STP : OFPPC_NO_RECV)) {
+ ? htonl(OFPPC_NO_RECV_STP)
+ : htonl(OFPPC_NO_RECV))) {
/* Drop this flow. */
return;
}
static void
update_port_config(struct ofproto *p, struct ofport *port,
- uint32_t config, uint32_t mask)
+ ovs_be32 config, ovs_be32 mask)
{
mask &= config ^ port->opp.config;
- if (mask & OFPPC_PORT_DOWN) {
- if (config & OFPPC_PORT_DOWN) {
+ if (mask & htonl(OFPPC_PORT_DOWN)) {
+ if (config & htonl(OFPPC_PORT_DOWN)) {
netdev_turn_flags_off(port->netdev, NETDEV_UP, true);
} else {
netdev_turn_flags_on(port->netdev, NETDEV_UP, true);
}
#define REVALIDATE_BITS (OFPPC_NO_RECV | OFPPC_NO_RECV_STP | \
OFPPC_NO_FWD | OFPPC_NO_FLOOD)
- if (mask & REVALIDATE_BITS) {
+ if (mask & htonl(REVALIDATE_BITS)) {
COVERAGE_INC(ofproto_costly_flags);
- port->opp.config ^= mask & REVALIDATE_BITS;
+ port->opp.config ^= mask & htonl(REVALIDATE_BITS);
p->need_revalidate = true;
}
#undef REVALIDATE_BITS
- if (mask & OFPPC_NO_PACKET_IN) {
- port->opp.config ^= OFPPC_NO_PACKET_IN;
+ if (mask & htonl(OFPPC_NO_PACKET_IN)) {
+ port->opp.config ^= htonl(OFPPC_NO_PACKET_IN);
}
}
} else if (memcmp(port->opp.hw_addr, opm->hw_addr, OFP_ETH_ALEN)) {
return ofp_mkerr(OFPET_PORT_MOD_FAILED, OFPPMFC_BAD_HW_ADDR);
} else {
- update_port_config(p, port, ntohl(opm->config), ntohl(opm->mask));
+ update_port_config(p, port, opm->config, opm->mask);
if (opm->advertise) {
netdev_set_advertisements(port->netdev, ntohl(opm->advertise));
}
netdev_get_stats(port->netdev, &stats);
ops = append_ofp_stats_reply(sizeof *ops, ofconn, msgp);
- ops->port_no = htons(port->opp.port_no);
+ ops->port_no = port->opp.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));
struct ofp_queue_stats *reply;
reply = append_ofp_stats_reply(sizeof *reply, cbdata->ofconn, &cbdata->msg);
- reply->port_no = htons(cbdata->ofport->opp.port_no);
+ reply->port_no = cbdata->ofport->opp.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));
/* Don't send a packet-in if OFPPC_NO_PACKET_IN asserted. */
struct ofport *port = get_port(p, flow.in_port);
if (port) {
- if (port->opp.config & OFPPC_NO_PACKET_IN) {
+ if (port->opp.config & htonl(OFPPC_NO_PACKET_IN)) {
COVERAGE_INC(ofproto_no_packet_in);
/* XXX install 'drop' flow entry */
ofpbuf_delete(upcall->packet);
/* Determine output port. */
dst_mac = mac_learning_lookup(ofproto->ml, flow->dl_dst, 0, tags);
if (!dst_mac) {
- flood_packets(ofproto, flow->in_port, OFPPC_NO_FLOOD,
+ flood_packets(ofproto, flow->in_port, htonl(OFPPC_NO_FLOOD),
nf_output_iface, odp_actions);
} else {
int out_port = dst_mac->port.i;
default_normal_ofhook_cb,
NULL,
NULL,
+ NULL,
NULL
};