#include <stdbool.h>
#include <stdlib.h>
#include "byte-order.h"
+#include "cfm.h"
#include "classifier.h"
#include "coverage.h"
#include "dpif.h"
struct netdev *netdev;
struct ofp_phy_port opp; /* In host byte order. */
uint16_t odp_port;
+ struct cfm *cfm; /* Connectivity Fault Management, if any. */
};
static void ofport_free(struct ofport *);
+static void ofport_run(struct ofproto *, struct ofport *);
+static void ofport_wait(struct ofport *);
static void hton_ofp_phy_port(struct ofp_phy_port *);
struct action_xlate_ctx {
ofproto->sflow = NULL;
}
}
+\f
+/* Connectivity Fault Management configuration. */
+
+/* Clears the CFM configuration from 'port_no' on 'ofproto'. */
+void
+ofproto_iface_clear_cfm(struct ofproto *ofproto, uint32_t port_no)
+{
+ struct ofport *ofport = get_port(ofproto, port_no);
+ if (ofport && ofport->cfm){
+ cfm_destroy(ofport->cfm);
+ ofport->cfm = NULL;
+ }
+}
+
+/* Configures connectivity fault management on 'port_no' in 'ofproto'. Takes
+ * basic configuration from the configuration members in 'cfm', and the set of
+ * remote maintenance points from the 'n_remote_mps' elements in 'remote_mps'.
+ * Ignores the statistics members of '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)
+{
+ 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);
+ return;
+ }
+
+ if (!ofport->cfm) {
+ ofport->cfm = cfm_create();
+ }
+
+ ofport->cfm->mpid = cfm->mpid;
+ ofport->cfm->interval = cfm->interval;
+ memcpy(ofport->cfm->eth_src, cfm->eth_src, ETH_ADDR_LEN);
+ memcpy(ofport->cfm->maid, cfm->maid, CCM_MAID_LEN);
+
+ cfm_update_remote_mps(ofport->cfm, remote_mps, n_remote_mps);
+
+ if (!cfm_configure(ofport->cfm)) {
+ VLOG_WARN("%s: CFM configuration on port %"PRIu32" (%s) failed",
+ dpif_name(ofproto->dpif), port_no,
+ netdev_get_name(ofport->netdev));
+ cfm_destroy(ofport->cfm);
+ ofport->cfm = NULL;
+ }
+}
+/* Returns the connectivity fault management object associated with 'port_no'
+ * within 'ofproto', or a null pointer if 'ofproto' does not have a port
+ * '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)
+{
+ struct ofport *ofport = get_port(ofproto, port_no);
+ return ofport ? ofport->cfm : NULL;
+}
+\f
uint64_t
ofproto_get_datapath_id(const struct ofproto *ofproto)
{
{
struct ofconn *ofconn, *next_ofconn;
struct ofservice *ofservice;
+ struct ofport *ofport;
char *devname;
int error;
int i;
process_port_change(p, error, devname);
}
+ HMAP_FOR_EACH (ofport, hmap_node, &p->ports) {
+ ofport_run(p, ofport);
+ }
+
if (p->in_band) {
if (time_msec() >= p->next_in_band_update) {
update_in_band_remotes(p);
{
struct ofservice *ofservice;
struct ofconn *ofconn;
+ struct ofport *ofport;
size_t i;
dpif_recv_wait(p->dpif);
dpif_port_poll_wait(p->dpif);
netdev_monitor_poll_wait(p->netdev_monitor);
+ HMAP_FOR_EACH (ofport, hmap_node, &p->ports) {
+ ofport_wait(ofport);
+ }
LIST_FOR_EACH (ofconn, node, &p->all_conns) {
ofconn_wait(ofconn);
}
return ofport && !(ofport->opp.config & OFPPC_NO_FLOOD);
}
+/* Sends 'packet' out of port 'port_no' within 'p'. If 'vlan_tci' is zero the
+ * packet will not have any 802.1Q hader; if it is nonzero, then the packet
+ * will be sent with the VLAN TCI specified by 'vlan_tci & ~VLAN_CFI'.
+ *
+ * Returns 0 if successful, otherwise a positive errno value. */
int
-ofproto_send_packet(struct ofproto *p, const struct flow *flow,
- const union ofp_action *actions, size_t n_actions,
+ofproto_send_packet(struct ofproto *ofproto,
+ uint32_t port_no, uint16_t vlan_tci,
const struct ofpbuf *packet)
{
- struct action_xlate_ctx ctx;
- struct ofpbuf *odp_actions;
-
- action_xlate_ctx_init(&ctx, p, flow, packet);
- /* Always xlate packets originated in this function. */
- ctx.check_special = false;
- odp_actions = xlate_actions(&ctx, actions, n_actions);
-
- /* XXX Should we translate the dpif_execute() errno value into an OpenFlow
- * error code? */
- dpif_execute(p->dpif, odp_actions->data, odp_actions->size, packet);
+ struct ofpbuf odp_actions;
+ int error;
- ofpbuf_delete(odp_actions);
+ ofpbuf_init(&odp_actions, 32);
+ if (vlan_tci != 0) {
+ nl_msg_put_u32(&odp_actions, ODP_ACTION_ATTR_SET_DL_TCI,
+ ntohs(vlan_tci & ~VLAN_CFI));
+ }
+ nl_msg_put_u32(&odp_actions, ODP_ACTION_ATTR_OUTPUT, port_no);
+ error = dpif_execute(ofproto->dpif, odp_actions.data, odp_actions.size,
+ packet);
+ ofpbuf_uninit(&odp_actions);
- return 0;
+ if (error) {
+ VLOG_WARN_RL(&rl, "%s: failed to send packet on port %"PRIu32" (%s)",
+ dpif_name(ofproto->dpif), port_no, strerror(error));
+ }
+ return error;
}
/* Adds a flow to the OpenFlow flow table in 'p' that matches 'cls_rule' and
}
}
+static void
+ofport_run(struct ofproto *ofproto, struct ofport *ofport)
+{
+ if (ofport->cfm) {
+ struct ofpbuf *packet = cfm_run(ofport->cfm);
+ if (packet) {
+ ofproto_send_packet(ofproto, ofport->odp_port, 0, packet);
+ ofpbuf_delete(packet);
+ }
+ }
+}
+
+static void
+ofport_wait(struct ofport *ofport)
+{
+ if (ofport->cfm) {
+ cfm_wait(ofport->cfm);
+ }
+}
+
static void
ofport_free(struct ofport *ofport)
{
if (ofport) {
+ cfm_destroy(ofport->cfm);
netdev_close(ofport->netdev);
free(ofport);
}
ctx->check_special = true;
}
+static void
+ofproto_process_cfm(struct ofproto *ofproto, const struct flow *flow,
+ const struct ofpbuf *packet)
+{
+ struct ofport *ofport;
+
+ ofport = get_port(ofproto, flow->in_port);
+ if (ofport && ofport->cfm) {
+ cfm_process_heartbeat(ofport->cfm, packet);
+ }
+}
+
static struct ofpbuf *
xlate_actions(struct action_xlate_ctx *ctx,
const union ofp_action *in, size_t n_in)
ctx->recurse = 0;
ctx->last_pop_priority = -1;
- if (!ctx->check_special
- || !ctx->ofproto->ofhooks->special_cb
- || ctx->ofproto->ofhooks->special_cb(&ctx->flow, ctx->packet,
- ctx->ofproto->aux)) {
- do_xlate_actions(in, n_in, ctx);
- } else {
+ if (ctx->check_special && cfm_should_process_flow(&ctx->flow)) {
+ if (ctx->packet) {
+ ofproto_process_cfm(ctx->ofproto, &ctx->flow, ctx->packet);
+ }
+ ctx->may_set_up_flow = false;
+ } else if (ctx->check_special
+ && ctx->ofproto->ofhooks->special_cb
+ && !ctx->ofproto->ofhooks->special_cb(&ctx->flow, ctx->packet,
+ ctx->ofproto->aux)) {
ctx->may_set_up_flow = false;
+ } else {
+ do_xlate_actions(in, n_in, ctx);
}
remove_pop_action(ctx);
/* Set header pointers in 'flow'. */
flow_extract(upcall->packet, flow.tun_id, flow.in_port, &flow);
- if (p->ofhooks->special_cb
- && !p->ofhooks->special_cb(&flow, upcall->packet, p->aux)) {
+ if (cfm_should_process_flow(&flow)) {
+ ofproto_process_cfm(p, &flow, upcall->packet);
+ ofpbuf_delete(upcall->packet);
+ return;
+ } else if (p->ofhooks->special_cb
+ && !p->ofhooks->special_cb(&flow, upcall->packet, p->aux)) {
ofpbuf_delete(upcall->packet);
return;
}
/* Check with in-band control to see if this packet should be sent
* to the local port regardless of the flow table. */
if (in_band_msg_in_hook(p->in_band, &flow, upcall->packet)) {
- struct ofpbuf odp_actions;
-
- ofpbuf_init(&odp_actions, 32);
- nl_msg_put_u32(&odp_actions, ODP_ACTION_ATTR_OUTPUT, ODPP_LOCAL);
- dpif_execute(p->dpif, odp_actions.data, odp_actions.size,
- upcall->packet);
- ofpbuf_uninit(&odp_actions);
+ ofproto_send_packet(p, ODPP_LOCAL, 0, upcall->packet);
}
facet = facet_lookup_valid(p, &flow);
uint16_t *nf_output_iface, void *ofproto_)
{
struct ofproto *ofproto = ofproto_;
- int out_port;
+ struct mac_entry *dst_mac;
/* Drop frames for reserved multicast addresses. */
if (eth_addr_is_reserved(flow->dl_dst)) {
}
/* Learn source MAC (but don't try to learn from revalidation). */
- if (packet != NULL) {
- tag_type rev_tag = mac_learning_learn(ofproto->ml, flow->dl_src,
- 0, flow->in_port,
- GRAT_ARP_LOCK_NONE);
- if (rev_tag) {
+ if (packet != NULL
+ && mac_learning_may_learn(ofproto->ml, flow->dl_src, 0)) {
+ struct mac_entry *src_mac;
+
+ src_mac = mac_learning_insert(ofproto->ml, flow->dl_src, 0);
+ if (mac_entry_is_new(src_mac) || src_mac->port.i != flow->in_port) {
/* The log messages here could actually be useful in debugging,
* so keep the rate limit relatively high. */
static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(30, 300);
VLOG_DBG_RL(&rl, "learned that "ETH_ADDR_FMT" is on port %"PRIu16,
ETH_ADDR_ARGS(flow->dl_src), flow->in_port);
- ofproto_revalidate(ofproto, rev_tag);
+
+ ofproto_revalidate(ofproto,
+ mac_learning_changed(ofproto->ml, src_mac));
+ src_mac->port.i = flow->in_port;
}
}
/* Determine output port. */
- out_port = mac_learning_lookup_tag(ofproto->ml, flow->dl_dst, 0, tags,
- NULL);
- if (out_port < 0) {
+ dst_mac = mac_learning_lookup(ofproto->ml, flow->dl_dst, 0, tags);
+ if (!dst_mac) {
flood_packets(ofproto, flow->in_port, OFPPC_NO_FLOOD,
nf_output_iface, odp_actions);
- } else if (out_port != flow->in_port) {
- nl_msg_put_u32(odp_actions, ODP_ACTION_ATTR_OUTPUT, out_port);
- *nf_output_iface = out_port;
} else {
- /* Drop. */
+ int out_port = dst_mac->port.i;
+ if (out_port != flow->in_port) {
+ nl_msg_put_u32(odp_actions, ODP_ACTION_ATTR_OUTPUT, out_port);
+ *nf_output_iface = out_port;
+ } else {
+ /* Drop. */
+ }
}
return true;