cfm: No longer keep track of bad remote MPs and MAIDS.
[openvswitch] / ofproto / ofproto.c
index 580f45da40289acf63d770d1c2cb1c11cc9159d3..e0715b9b3aa1c2c764fb889c408d1970d287147e 100644 (file)
@@ -25,6 +25,7 @@
 #include <stdbool.h>
 #include <stdlib.h>
 #include "byte-order.h"
+#include "cfm.h"
 #include "classifier.h"
 #include "coverage.h"
 #include "dpif.h"
@@ -100,9 +101,12 @@ struct ofport {
     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 {
@@ -947,7 +951,70 @@ ofproto_set_sflow(struct ofproto *ofproto,
         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->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)
 {
@@ -1110,6 +1177,7 @@ ofproto_run1(struct ofproto *p)
 {
     struct ofconn *ofconn, *next_ofconn;
     struct ofservice *ofservice;
+    struct ofport *ofport;
     char *devname;
     int error;
     int i;
@@ -1146,6 +1214,10 @@ ofproto_run1(struct ofproto *p)
         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);
@@ -1246,11 +1318,15 @@ ofproto_wait(struct ofproto *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);
     }
@@ -1648,10 +1724,39 @@ ofport_remove(struct ofproto *p, struct ofport *ofport)
     }
 }
 
+static void
+ofport_run(struct ofproto *ofproto, struct ofport *ofport)
+{
+    if (ofport->cfm) {
+        cfm_run(ofport->cfm);
+
+        if (cfm_should_send_ccm(ofport->cfm)) {
+            struct ofpbuf packet;
+            struct ccm *ccm;
+
+            ofpbuf_init(&packet, 0);
+            ccm = compose_packet(&packet, eth_addr_ccm, ofport->opp.hw_addr,
+                                 ETH_TYPE_CFM,  sizeof *ccm);
+            cfm_compose_ccm(ofport->cfm, ccm);
+            ofproto_send_packet(ofproto, ofport->odp_port, 0, &packet);
+            ofpbuf_uninit(&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);
     }
@@ -3081,6 +3186,18 @@ action_xlate_ctx_init(struct action_xlate_ctx *ctx,
     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)
@@ -3094,13 +3211,18 @@ xlate_actions(struct action_xlate_ctx *ctx,
     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);
@@ -4375,8 +4497,12 @@ handle_miss_upcall(struct ofproto *p, struct dpif_upcall *upcall)
     /* 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;
     }
@@ -5072,7 +5198,7 @@ ofproto_unixctl_trace(struct unixctl_conn *conn, const char *args_,
         goto exit;
     }
 
-    tun_id = htonll(strtoull(tun_id_s, NULL, 10));
+    tun_id = htonll(strtoull(tun_id_s, NULL, 0));
     in_port = ofp_port_to_odp_port(atoi(in_port_s));
 
     packet_s = ofpbuf_put_hex(&packet, packet_s, NULL);
@@ -5143,7 +5269,7 @@ default_normal_ofhook_cb(const struct flow *flow, const struct ofpbuf *packet,
                          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)) {
@@ -5151,31 +5277,37 @@ default_normal_ofhook_cb(const struct flow *flow, const struct ofpbuf *packet,
     }
 
     /* 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;