#include "pinsched.h"
#include "pktbuf.h"
#include "poll-loop.h"
+#include "private.h"
#include "rconn.h"
#include "shash.h"
#include "sset.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. */
};
const struct flow *, bool clone);
struct ofproto {
+ char *name; /* Datapath name. */
+ struct hmap_node hmap_node; /* In global 'all_ofprotos' hmap. */
+
/* Settings. */
uint64_t datapath_id; /* Datapath ID. */
uint64_t fallback_dpid; /* Datapath ID if no better choice found. */
};
/* Map from dpif name to struct ofproto, for use by unixctl commands. */
-static struct shash all_ofprotos = SHASH_INITIALIZER(&all_ofprotos);
+static struct hmap all_ofprotos = HMAP_INITIALIZER(&all_ofprotos);
static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 5);
ofproto_unixctl_init();
/* Connect to datapath and start listening for messages. */
- error = dpif_open(datapath, datapath_type, &dpif);
+ error = dpif_create_and_open(datapath, datapath_type, &dpif);
if (error) {
VLOG_ERR("failed to open datapath %s: %s", datapath, strerror(error));
return error;
/* Initialize settings. */
p = xzalloc(sizeof *p);
+ p->name = xstrdup(dpif_name(dpif));
+ hmap_insert(&all_ofprotos, &p->hmap_node, hash_string(p->name, 0));
p->fallback_dpid = pick_fallback_dpid();
p->datapath_id = p->fallback_dpid;
p->mfr_desc = xstrdup(DEFAULT_MFR_DESC);
p->datapath_id = pick_datapath_id(p);
VLOG_INFO("using datapath ID %016"PRIx64, p->datapath_id);
- shash_add_once(&all_ofprotos, dpif_name(p->dpif), p);
-
/* Initialize OpenFlow connections. */
p->connmgr = connmgr_create(p, datapath, local_name);
/* Clears the CFM configuration from 'port_no' on 'ofproto'. */
void
-ofproto_iface_clear_cfm(struct ofproto *ofproto, uint32_t port_no)
+ofproto_port_clear_cfm(struct ofproto *ofproto, uint32_t port_no)
{
struct ofport *ofport = get_port(ofproto, port_no);
if (ofport && ofport->cfm){
*
* This function has no effect if 'ofproto' does not have a port 'port_no'. */
void
-ofproto_iface_set_cfm(struct ofproto *ofproto, uint32_t port_no,
- const struct cfm *cfm,
- const uint16_t *remote_mps, size_t n_remote_mps)
+ofproto_port_set_cfm(struct ofproto *ofproto, uint32_t port_no,
+ const struct cfm *cfm,
+ const uint16_t *remote_mps, size_t n_remote_mps)
{
struct ofport *ofport;
ofport = get_port(ofproto, port_no);
if (!ofport) {
VLOG_WARN("%s: cannot configure CFM on nonexistent port %"PRIu32,
- dpif_name(ofproto->dpif), port_no);
+ ofproto->name, port_no);
return;
}
if (!cfm_configure(ofport->cfm)) {
VLOG_WARN("%s: CFM configuration on port %"PRIu32" (%s) failed",
- dpif_name(ofproto->dpif), port_no,
+ ofproto->name, port_no,
netdev_get_name(ofport->netdev));
cfm_destroy(ofport->cfm);
ofport->cfm = NULL;
* 'port_no' or if that port does not have CFM configured. The caller must not
* modify or destroy the returned object. */
const struct cfm *
-ofproto_iface_get_cfm(struct ofproto *ofproto, uint32_t port_no)
+ofproto_port_get_cfm(struct ofproto *ofproto, uint32_t port_no)
{
struct ofport *ofport = get_port(ofproto, port_no);
return ofport ? ofport->cfm : NULL;
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)
{
connmgr_get_snoops(ofproto->connmgr, snoops);
}
-void
-ofproto_destroy(struct ofproto *p)
+static void
+ofproto_destroy__(struct ofproto *p, bool delete)
{
struct ofport *ofport, *next_ofport;
return;
}
- shash_find_and_delete(&all_ofprotos, dpif_name(p->dpif));
+ hmap_remove(&all_ofprotos, &p->hmap_node);
ofproto_flush_flows__(p);
connmgr_destroy(p->connmgr);
classifier_destroy(&p->cls);
hmap_destroy(&p->facets);
+ if (delete) {
+ int error = dpif_delete(p->dpif);
+ if (error && error != ENOENT) {
+ VLOG_ERR("bridge %s: failed to destroy (%s)",
+ p->name, strerror(error));
+ }
+ }
dpif_close(p->dpif);
+
netdev_monitor_destroy(p->netdev_monitor);
HMAP_FOR_EACH_SAFE (ofport, next_ofport, hmap_node, &p->ports) {
hmap_remove(&p->ports, &ofport->hmap_node);
hmap_destroy(&p->ports);
+ free(p->name);
free(p);
}
+void
+ofproto_destroy(struct ofproto *p)
+{
+ ofproto_destroy__(p, false);
+}
+
+void
+ofproto_destroy_and_delete(struct ofproto *p)
+{
+ ofproto_destroy__(p, true);
+}
+
int
ofproto_run(struct ofproto *p)
{
* spin from here on out. */
static struct vlog_rate_limit rl2 = VLOG_RATE_LIMIT_INIT(1, 5);
VLOG_ERR_RL(&rl2, "%s: datapath was destroyed externally",
- dpif_name(p->dpif));
+ p->name);
return ENODEV;
}
break;
shash_destroy(info);
}
-/* Deletes port number 'odp_port' from the datapath for 'ofproto'.
+/* Makes a deep copy of 'old' into 'port'. */
+void
+ofproto_port_clone(struct ofproto_port *port, const struct ofproto_port *old)
+{
+ port->name = xstrdup(old->name);
+ port->type = xstrdup(old->type);
+ port->ofp_port = old->ofp_port;
+}
+
+/* Frees memory allocated to members of 'ofproto_port'.
+ *
+ * Do not call this function on an ofproto_port obtained from
+ * ofproto_port_dump_next(): that function retains ownership of the data in the
+ * ofproto_port. */
+void
+ofproto_port_destroy(struct ofproto_port *ofproto_port)
+{
+ free(ofproto_port->name);
+ free(ofproto_port->type);
+}
+
+/* Converts a dpif_port into an ofproto_port.
+ *
+ * This only makes a shallow copy, so make sure that the dpif_port doesn't get
+ * freed while the ofproto_port is still in use. You can choose to free the
+ * ofproto_port instead of the dpif_port. */
+static void
+ofproto_port_from_dpif_port(struct ofproto_port *ofproto_port,
+ struct dpif_port *dpif_port)
+{
+ ofproto_port->name = dpif_port->name;
+ ofproto_port->type = dpif_port->type;
+ ofproto_port->ofp_port = odp_port_to_ofp_port(dpif_port->port_no);
+}
+
+/* Initializes 'dump' to begin dumping the ports in an ofproto.
+ *
+ * This function provides no status indication. An error status for the entire
+ * dump operation is provided when it is completed by calling
+ * ofproto_port_dump_done().
+ */
+void
+ofproto_port_dump_start(struct ofproto_port_dump *dump,
+ const struct ofproto *ofproto)
+{
+ struct dpif_port_dump *dpif_dump;
+
+ dump->state = dpif_dump = xmalloc(sizeof *dpif_dump);
+ dpif_port_dump_start(dpif_dump, ofproto->dpif);
+}
+
+/* Attempts to retrieve another port from 'dump', which must have been created
+ * with ofproto_port_dump_start(). On success, stores a new ofproto_port into
+ * 'port' and returns true. On failure, returns false.
+ *
+ * Failure might indicate an actual error or merely that the last port has been
+ * dumped. An error status for the entire dump operation is provided when it
+ * is completed by calling ofproto_port_dump_done().
+ *
+ * The ofproto owns the data stored in 'port'. It will remain valid until at
+ * least the next time 'dump' is passed to ofproto_port_dump_next() or
+ * ofproto_port_dump_done(). */
+bool
+ofproto_port_dump_next(struct ofproto_port_dump *dump,
+ struct ofproto_port *port)
+{
+ struct dpif_port_dump *dpif_dump = dump->state;
+ struct dpif_port dpif_port;
+ bool ok;
+
+ ok = dpif_port_dump_next(dpif_dump, &dpif_port);
+ if (ok) {
+ ofproto_port_from_dpif_port(port, &dpif_port);
+ }
+ return ok;
+}
+
+/* Completes port table dump operation 'dump', which must have been created
+ * with ofproto_port_dump_start(). Returns 0 if the dump operation was
+ * error-free, otherwise a positive errno value describing the problem. */
+int
+ofproto_port_dump_done(struct ofproto_port_dump *dump)
+{
+ struct dpif_port_dump *dpif_dump = dump->state;
+ int error = dpif_port_dump_done(dpif_dump);
+ free(dpif_dump);
+ return error;
+}
+
+/* Attempts to add 'netdev' as a port on 'ofproto'. If successful, returns 0
+ * and sets '*ofp_portp' to the new port's port OpenFlow number (if 'ofp_portp'
+ * is non-null). On failure, returns a positive errno value and sets
+ * '*ofp_portp' to OFPP_NONE (if 'ofp_portp' is non-null). */
+int
+ofproto_port_add(struct ofproto *ofproto, struct netdev *netdev,
+ uint16_t *ofp_portp)
+{
+ uint16_t odp_port;
+ int error;
+
+ error = dpif_port_add(ofproto->dpif, netdev, &odp_port);
+ if (ofp_portp) {
+ *ofp_portp = error ? OFPP_NONE : odp_port_to_ofp_port(odp_port);
+ }
+ return error;
+}
+
+/* Looks up a port named 'devname' in 'ofproto'. On success, returns 0 and
+ * initializes '*port' appropriately; on failure, returns a positive errno
+ * value.
+ *
+ * The caller owns the data in 'port' and must free it with
+ * ofproto_port_destroy() when it is no longer needed. */
+int
+ofproto_port_query_by_name(const struct ofproto *ofproto, const char *devname,
+ struct ofproto_port *port)
+{
+ struct dpif_port dpif_port;
+ int error;
+
+ error = dpif_port_query_by_name(ofproto->dpif, devname, &dpif_port);
+ if (!error) {
+ ofproto_port_from_dpif_port(port, &dpif_port);
+ }
+ return error;
+}
+
+/* Deletes port number 'ofp_port' from the datapath for 'ofproto'.
*
* This is almost the same as calling dpif_port_del() directly on the
* datapath, but it also makes 'ofproto' close its open netdev for the port
*
* Returns 0 if successful, otherwise a positive errno. */
int
-ofproto_port_del(struct ofproto *ofproto, uint16_t odp_port)
+ofproto_port_del(struct ofproto *ofproto, uint16_t ofp_port)
{
+ uint32_t odp_port = ofp_port_to_odp_port(ofp_port);
struct ofport *ofport = get_port(ofproto, odp_port);
const char *name = ofport ? netdev_get_name(ofport->netdev) : "<unknown>";
int error;
error = dpif_port_del(ofproto->dpif, odp_port);
if (error) {
VLOG_ERR("%s: failed to remove port %"PRIu16" (%s) interface (%s)",
- dpif_name(ofproto->dpif), odp_port, name, strerror(error));
+ ofproto->name, odp_port, name, strerror(error));
} else if (ofport) {
/* '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
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)
if (error) {
VLOG_WARN_RL(&rl, "%s: failed to send packet on port %"PRIu32" (%s)",
- dpif_name(ofproto->dpif), port_no, strerror(error));
+ ofproto->name, port_no, strerror(error));
}
return error;
}
}
/* Opens and returns a netdev for 'dpif_port', or a null pointer if the netdev
- * cannot be opened. On success, also fills in 'opp', in *HOST* byte order. */
+ * 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 netdev *netdev;
}
netdev_get_flags(netdev, &flags);
+ netdev_get_features(netdev, &curr, &advertised, &supported, &peer);
- opp->port_no = odp_port_to_ofp_port(dpif_port->port_no);
+ 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 : OFPPC_PORT_DOWN;
- opp->state = netdev_get_carrier(netdev) ? 0 : OFPPS_LINK_DOWN;
- netdev_get_features(netdev, &opp->curr, &opp->advertised,
- &opp->supported, &opp->peer);
+ 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);
+
return netdev;
}
BUILD_ASSERT_DECL(sizeof *a == 48); /* Detect ofp_phy_port changes. */
return (!memcmp(a->hw_addr, b->hw_addr, sizeof a->hw_addr)
&& a->state == b->state
- && !((a->config ^ b->config) & OFPPC_PORT_DOWN)
+ && !((a->config ^ b->config) & htonl(OFPPC_PORT_DOWN))
&& a->curr == b->curr
&& a->advertised == b->advertised
&& a->supported == b->supported
ofport = xmalloc(sizeof *ofport);
ofport->netdev = netdev;
ofport->opp = *opp;
- ofport->odp_port = ofp_port_to_odp_port(opp->port_no);
+ ofport->odp_port = ofp_port_to_odp_port(ntohs(opp->port_no));
ofport->cfm = NULL;
/* Add port to 'p'. */
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 & ~OFPPC_PORT_DOWN)
- | (opp->config & OFPPC_PORT_DOWN));
+ 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;
(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 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));
}
}
+/* Obtains the NetFlow engine type and engine ID for 'ofproto' into
+ * '*engine_type' and '*engine_id', respectively. */
+void
+ofproto_get_netflow_ids(const struct ofproto *ofproto,
+ uint8_t *engine_type, uint8_t *engine_id)
+{
+ dpif_get_netflow_ids(ofproto->dpif, engine_type, engine_id);
+}
+
static void
query_aggregate_stats(struct ofproto *ofproto, struct cls_rule *target,
ovs_be16 out_port, uint8_t table_id,
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);
ds_put_format(&s, " %d:%d", i * BUCKET_WIDTH, buckets[i]);
}
}
- VLOG_INFO("%s: %s (msec:count)",
- dpif_name(ofproto->dpif), ds_cstr(&s));
+ VLOG_INFO("%s: %s (msec:count)", ofproto->name, ds_cstr(&s));
ds_destroy(&s);
}
return eth_addr_to_uint64(ea);
}
\f
+static struct ofproto *
+ofproto_lookup(const char *name)
+{
+ struct ofproto *ofproto;
+
+ HMAP_FOR_EACH_WITH_HASH (ofproto, hmap_node, hash_string(name, 0),
+ &all_ofprotos) {
+ if (!strcmp(ofproto->name, name)) {
+ return ofproto;
+ }
+ }
+ return NULL;
+}
+
static void
ofproto_unixctl_list(struct unixctl_conn *conn, const char *arg OVS_UNUSED,
void *aux OVS_UNUSED)
{
- const struct shash_node *node;
+ struct ofproto *ofproto;
struct ds results;
ds_init(&results);
- SHASH_FOR_EACH (node, &all_ofprotos) {
- ds_put_format(&results, "%s\n", node->name);
+ HMAP_FOR_EACH (ofproto, hmap_node, &all_ofprotos) {
+ ds_put_format(&results, "%s\n", ofproto->name);
}
unixctl_command_reply(conn, 200, ds_cstr(&results));
ds_destroy(&results);
goto exit;
}
- ofproto = shash_find_data(&all_ofprotos, dpname);
+ ofproto = ofproto_lookup(dpname);
if (!ofproto) {
unixctl_command_reply(conn, 501, "Unknown ofproto (use ofproto/list "
"for help)");
/* 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;