[ovs-dev] [PATCH 4/4] ovs: Implement 802.1ag Connectivity Fault Management

Ethan Jackson ethan at nicira.com
Wed Nov 24 10:40:24 UTC 2010


This commit implements a subset of the 802.1ag specification for
Connectivity Fault Management (CFM) using Continuity Check Messages
(CCM).  When CFM is configured on an interface CCMs are broadcast
at regular intervals to detect missing or unexpected connectivity.
---
 lib/automake.mk            |    2 +
 lib/cfm.c                  |  425 ++++++++++++++++++++++++++++++++++++++++++++
 lib/cfm.h                  |   80 +++++++++
 lib/packets.h              |   15 ++
 lib/vlog-modules.def       |    1 +
 utilities/ovs-vsctl.c      |    8 +
 vswitchd/bridge.c          |  193 ++++++++++++++++++++
 vswitchd/vswitch.ovsschema |   63 +++++++
 8 files changed, 787 insertions(+), 0 deletions(-)
 create mode 100644 lib/cfm.c
 create mode 100644 lib/cfm.h

diff --git a/lib/automake.mk b/lib/automake.mk
index 4ebbb19..719ae48 100644
--- a/lib/automake.mk
+++ b/lib/automake.mk
@@ -17,6 +17,8 @@ lib_libopenvswitch_a_SOURCES = \
 	lib/byte-order.h \
 	lib/byteq.c \
 	lib/byteq.h \
+	lib/cfm.c \
+	lib/cfm.h \
 	lib/classifier.c \
 	lib/classifier.h \
 	lib/command-line.c \
diff --git a/lib/cfm.c b/lib/cfm.c
new file mode 100644
index 0000000..367bed9
--- /dev/null
+++ b/lib/cfm.c
@@ -0,0 +1,425 @@
+/*
+ * Copyright (c) 2010 Nicira Networks.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <config.h>
+#include "cfm.h"
+
+#include <assert.h>
+#include <inttypes.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "hash.h"
+#include "hmap.h"
+#include "ofpbuf.h"
+#include "packets.h"
+#include "poll-loop.h"
+#include "timeval.h"
+
+#include <vlog.h>
+VLOG_DEFINE_THIS_MODULE(cfm);
+
+const uint8_t ccm_opcode = 1; /* CFM message opcode meaning CCM*/
+const uint64_t dest_addr = 0x0180C2000030; /* Destination for level 0 CCMs */
+
+struct cfm_internal {
+    struct cfm cfm;
+    uint32_t seq;              /* The sequence number of our last CCM. */
+
+    uint8_t ccm_interval;      /* The CCM interval we will be using. */
+    long long ccm_interval_ms; /* The ccm_interval in milliseconds. */
+
+    long long ccm_sent;        /* The time we last sent a CCM. */
+    long long fault_check;     /* The time we last checked for faults. */
+};
+
+static long long
+ccm_interval_to_ms(uint8_t interval)
+{
+    switch (interval) {
+    case 0:  NOT_REACHED(); /* Explicitly unsupported by 802.1ag. */
+    case 1:  return 3;      /* 3ms  Not recommended due to timer resolution. */
+    case 2:  return 10;     /* 10ms Not recommended due to timer resolution. */
+    case 3:  return 100;    /* 100ms */
+    case 4:  return 1000;   /* 1s    */
+    case 5:  return 10000;  /* 10s   */
+    case 6:  return 60000;  /* 1m    */
+    case 7:  return 600000; /* 10m   */
+    default: NOT_REACHED(); /* Explicitly unsupported by 802.1ag. */
+    }
+
+    NOT_REACHED();
+}
+
+static uint8_t
+ms_to_ccm_interval(long long interval_ms)
+{
+    uint8_t i;
+
+    for (i = 7; i > 0; i--) {
+        if (ccm_interval_to_ms(i) <= interval_ms) {
+            return i;
+        }
+    }
+
+    return 1;
+}
+
+static inline struct cfm_internal *
+cfm_to_internal(struct cfm *cfm)
+{
+    return CONTAINER_OF(cfm, struct cfm_internal, cfm);
+}
+
+static struct remote_mp *
+lookup_remote_mp(const struct hmap *hmap, uint16_t mpid)
+{
+    struct remote_mp *rmp;
+
+    HMAP_FOR_EACH_WITH_HASH(rmp, node, hash_bytes(&mpid, sizeof mpid, 0),
+                            hmap) {
+        if (rmp->mpid == mpid) {
+            return rmp;
+        }
+    }
+
+    return NULL;
+}
+
+static void
+compose_and_send_ccm(struct cfm_internal *cfmi)
+{
+    struct ccm *ccm;
+    struct ofpbuf packet;
+    struct eth_header *eth;
+
+    ofpbuf_init(&packet, 0);
+
+    eth = ofpbuf_put_zeros(&packet, ETH_HEADER_LEN);
+
+    eth_addr_from_uint64(dest_addr, eth->eth_dst);
+    memcpy(eth->eth_src, cfmi->cfm.eth_src, sizeof eth->eth_src);
+    eth->eth_type = htons(ETH_TYPE_CFM);
+
+    ccm                  = ofpbuf_put_zeros(&packet, CCM_LEN);
+    ccm->mdlevel_version = 0;
+    ccm->opcode          = ccm_opcode;
+    ccm->tlv_offset      = 70;
+    ccm->seq             = htonl(++cfmi->seq);
+    ccm->mpid            = htons(cfmi->cfm.mpid);
+    memcpy(ccm->maid, cfmi->cfm.maid, sizeof ccm->maid);
+
+    /* Include the RDI bit if there's a fault */
+    ccm->flags = cfmi->ccm_interval | (cfmi->cfm.fault ? 0x80 : 0);
+
+    cfmi->cfm.send_heartbeat(&packet, cfmi->cfm.aux);
+    cfmi->ccm_sent = time_msec();
+
+    ofpbuf_uninit(&packet);
+}
+
+/* Given an array of mpids, updates the remote_mps hash of 'cfm' to reflect it.
+ * Any invalid elements are skipped */
+void
+cfm_update_remote_mps(struct cfm *cfm, uint16_t *mpid, size_t n_mpids)
+{
+    size_t i;
+    struct remote_mp *rmp, *rmp_next;
+    struct hmap new_rmps;
+
+
+    hmap_init(&new_rmps);
+
+    for (i = 0; i < n_mpids; i++) {
+
+        if (mpid[i] < 1 || mpid[i] > 8191) {
+            continue;
+        }
+
+        if ((rmp = lookup_remote_mp(&cfm->remote_mps, mpid[i]))) {
+            hmap_remove(&cfm->remote_mps, &rmp->node);
+        } else if((rmp = lookup_remote_mp(&cfm->x_remote_mps, mpid[i]))) {
+            hmap_remove(&cfm->x_remote_mps, &rmp->node);
+        } else {
+            rmp = xmalloc(sizeof *rmp);
+            rmp->mpid = mpid[i];
+        }
+
+        hmap_insert(&new_rmps, &rmp->node,
+                    hash_bytes(&mpid[i], sizeof mpid[i], 0));
+    }
+
+    HMAP_FOR_EACH_SAFE(rmp, rmp_next, node, &cfm->remote_mps) {
+        hmap_remove(&cfm->remote_mps, &rmp->node);
+        free(rmp);
+    }
+    hmap_swap(&new_rmps, &cfm->remote_mps);
+    hmap_destroy(&new_rmps);
+}
+
+/* Finds a 'remote_mp' with id 'mpid' in cfm.  If no such remote mp exists
+ * returns NULL. */
+const struct remote_mp *
+cfm_get_remote_mp(const struct cfm *cfm, uint16_t mpid)
+{
+    return lookup_remote_mp(&cfm->remote_mps, mpid);
+}
+
+/* Generates a 'maid' from 'md_name' and 'ma_name'.  A NULL indicates the
+ * default should be used. Returns NULL if unsuccessful, otherwise returns
+ * 'maid'. */
+uint8_t *
+cfm_generate_maid(const char *md_name, const char *ma_name,
+                  uint8_t maid[MAID_LEN])
+{
+    uint8_t *ma_p;
+    size_t md_len, ma_len;
+
+    if (!md_name) {
+        md_name = "ovs";
+    }
+
+    if (!ma_name) {
+        ma_name = "ovs";
+    }
+
+    md_len = strlen(md_name);
+    ma_len = strlen(ma_name);
+
+    if (md_len < 1 || ma_len < 1 ||
+        md_len + ma_len + 4 > MAID_LEN) {
+        return NULL;
+    }
+
+    memset(maid, 0, MAID_LEN);
+
+    maid[0] = 4; /* Maintenance domain string format */
+    maid[1] = md_len;
+    memcpy(&maid[2], md_name, md_len);
+
+    ma_p    = maid + 2 + md_len;
+    ma_p[0] = 2; /* Maintenance association string format */
+    ma_p[1] = ma_len;
+    memcpy(&ma_p[2], ma_name, ma_len);
+    return maid;
+}
+
+/* Returns true if the cfm library should process packets from 'flow'. */
+bool
+cfm_should_process_flow(const struct flow *flow)
+{
+    return ntohs(flow->dl_type) == ETH_TYPE_CFM &&
+        eth_addr_to_uint64(flow->dl_dst) == dest_addr;
+}
+
+/* Updates internal statistics relevant to 'packet'.  should be called on every
+ * packet whose flow returned true when passed to cfm_should_process_flow. */
+void
+cfm_process_heartbeat(struct cfm *cfm, const struct ofpbuf *packet)
+{
+    size_t offset;
+    uint16_t mpid;
+    uint8_t ccm_rdi;
+    struct ccm *ccm;
+    uint8_t ccm_interval;
+    struct cfm_internal *cfmi;
+    struct remote_mp *rmp;
+
+    cfmi   = cfm_to_internal(cfm);
+    offset = (uint8_t *)packet->l3 - (uint8_t *)packet->data;
+    ccm    = ofpbuf_at(packet, offset, CCM_LEN);
+
+    if (!ccm) {
+        VLOG_INFO("Received an unparseable 802.1ag ccm heartbeat.");
+        return;
+    }
+
+    if (ccm->opcode != ccm_opcode) {
+        VLOG_INFO("Received an unsupported 802.1ag message.");
+        return;
+    }
+
+    if (memcmp(ccm->maid, cfm->maid, sizeof ccm->maid)) {
+        uint32_t hash;
+        struct remote_maid *rmaid;
+
+        hash = hash_bytes(ccm->maid, sizeof ccm->maid, 0);
+
+        HMAP_FOR_EACH_WITH_HASH(rmaid, node, hash, &cfmi->cfm.x_remote_maids) {
+            if (memcmp(rmaid->maid, ccm->maid, sizeof rmaid->maid) == 0) {
+                rmaid->recv_time = time_msec();
+                return;
+            }
+        }
+
+        rmaid = xzalloc(sizeof *rmaid);
+        rmaid->recv_time = time_msec();
+        memcpy(rmaid->maid, ccm->maid, sizeof rmaid->maid);
+        hmap_insert(&cfmi->cfm.x_remote_maids, &rmaid->node, hash);
+        return;
+    }
+
+    mpid = ntohs(ccm->mpid);
+    rmp  = lookup_remote_mp(&cfm->remote_mps, mpid);
+
+    if (!rmp) {
+        rmp = lookup_remote_mp(&cfm->x_remote_mps, mpid);
+        if (!rmp) {
+            rmp       = xzalloc(sizeof *rmp);
+            rmp->mpid = mpid;
+            hmap_insert(&cfm->x_remote_mps, &rmp->node,
+                        hash_bytes(&mpid, sizeof mpid, 0));
+        }
+    }
+
+    rmp->fault     = false;
+    rmp->recv_seq  = ntohl(ccm->seq);
+    rmp->recv_time = time_msec();
+    ccm_interval   = ccm->flags & 0x7;
+    ccm_rdi        = ccm->flags & 0x80;
+
+    if (ccm_rdi || ccm_interval != cfmi->ccm_interval) {
+        rmp->fault = true;
+    }
+}
+
+void
+cfm_run(struct cfm *cfm)
+{
+    long long now;
+    struct cfm_internal *cfmi;
+
+    now  = time_msec();
+    cfmi = cfm_to_internal(cfm);
+
+    if (now >= cfmi->fault_check + cfmi->ccm_interval_ms * 4) {
+        bool fault;
+        struct remote_mp *rmp, *rmp_next;
+        struct remote_maid *rmaid, *rmaid_next;
+
+        fault = false;
+
+        HMAP_FOR_EACH(rmp, node, &cfm->remote_mps) {
+            rmp->fault = rmp->fault || cfmi->fault_check > rmp->recv_time;
+            fault      = rmp->fault || fault;
+        }
+
+        HMAP_FOR_EACH_SAFE(rmp, rmp_next, node, &cfm->x_remote_mps) {
+            if (cfmi->fault_check > rmp->recv_time) {
+                hmap_remove(&cfm->x_remote_mps, &rmp->node);
+                free(rmp);
+            }
+        }
+
+        HMAP_FOR_EACH_SAFE(rmaid, rmaid_next, node, &cfm->x_remote_maids) {
+            if (cfmi->fault_check > rmaid->recv_time) {
+                hmap_remove(&cfm->x_remote_maids, &rmaid->node);
+                free(rmaid);
+            }
+        }
+
+        if (cfm->x_remote_mps.n > 0 || cfm->x_remote_maids.n > 0) {
+            fault = true;
+        }
+
+        cfm->fault        = fault;
+        cfmi->fault_check = now;
+    }
+
+    if (now >= cfmi->ccm_sent + cfmi->ccm_interval_ms) {
+        compose_and_send_ccm(cfmi);
+    }
+}
+
+void
+cfm_wait(struct cfm *cfm)
+{
+    long long wait;
+    struct cfm_internal *cfmi;
+
+    cfmi = cfm_to_internal(cfm);
+    wait = MIN(cfmi->ccm_sent + cfmi->ccm_interval_ms,
+               cfmi->fault_check + cfmi->ccm_interval_ms * 4);
+    poll_timer_wait_until(wait);
+}
+
+struct cfm *
+cfm_create(void)
+{
+    struct cfm *cfm;
+    struct cfm_internal *cfmi;
+
+    cfmi = xzalloc(sizeof *cfmi);
+    cfm  = &cfmi->cfm;
+
+    hmap_init(&cfm->remote_mps);
+    hmap_init(&cfm->x_remote_mps);
+    hmap_init(&cfm->x_remote_maids);
+    return cfm;
+}
+
+/* Should be called whenever a client of the cfm library changes the internals
+ * of 'cfm'. */
+bool
+cfm_configure(struct cfm *cfm) {
+    struct cfm_internal *cfmi;
+
+    if (cfm->mpid < 1 || cfm->mpid > 8191
+        || !cfm->interval || !cfm->send_heartbeat) {
+        return false;
+    }
+
+    cfmi                  = cfm_to_internal(cfm);
+    cfmi->ccm_interval    = ms_to_ccm_interval(cfm->interval);
+    cfmi->ccm_interval_ms = ccm_interval_to_ms(cfmi->ccm_interval);
+
+    /* Force a resend and check in case anything changed */
+    cfmi->ccm_sent    = 0;
+    cfmi->fault_check = 0;
+    return true;
+}
+
+void
+cfm_destroy(struct cfm *cfm)
+{
+    struct remote_mp *rmp, *rmp_next;
+    struct remote_maid *rmaid, *rmaid_next;
+
+    if (!cfm) {
+        return;
+    }
+
+    HMAP_FOR_EACH_SAFE(rmp, rmp_next, node, &cfm->remote_mps) {
+        hmap_remove(&cfm->remote_mps, &rmp->node);
+        free(rmp);
+    }
+
+    HMAP_FOR_EACH_SAFE(rmp, rmp_next, node, &cfm->x_remote_mps) {
+        hmap_remove(&cfm->x_remote_mps, &rmp->node);
+        free(rmp);
+    }
+
+    HMAP_FOR_EACH_SAFE(rmaid, rmaid_next, node, &cfm->x_remote_maids) {
+        hmap_remove(&cfm->x_remote_mps, &rmp->node);
+        free(rmp);
+    }
+
+    hmap_destroy(&cfm->remote_mps);
+    hmap_destroy(&cfm->x_remote_mps);
+    hmap_destroy(&cfm->x_remote_maids);
+    free(cfm_to_internal(cfm));
+}
+
diff --git a/lib/cfm.h b/lib/cfm.h
new file mode 100644
index 0000000..07f3c17
--- /dev/null
+++ b/lib/cfm.h
@@ -0,0 +1,80 @@
+/*
+ * Copyright (c) 2010 Nicira Networks.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef CFM_H
+#define CFM_H 1
+
+#include <inttypes.h>
+#include "flow.h"
+#include "hmap.h"
+#include "packets.h"
+
+/* Remote MPs represent foreign network entities.  They are configured to have
+ * the same maid as this cfm */
+struct remote_mp {
+    uint16_t mpid;         /* The Maintenance Point ID of this remote_mp */
+
+    long long recv_time;   /* Time the most recent CCM was received */
+    uint32_t recv_seq;     /* The most recently received CCM sequence number */
+    bool fault;            /* True if this remote_mp has a connetivity fault */
+    struct hmap_node node; /* Node in the cfm remote_mps or x_remote_mps */
+};
+
+/* Remote maids keep track of incoming ccm messages which have a different maid
+ * then this MP */
+struct remote_maid {
+    uint8_t maid[48];      /* The remote maid */
+    long long recv_time;   /* The time this remote maid was received */
+    struct hmap_node node;
+};
+
+struct cfm {
+    uint16_t mpid;      /* The Maintenance Point ID of this cfm object */
+    uint8_t maid[48];   /* The maid of this cfm object */
+    long long interval; /* The requested transmission interval */
+    const uint8_t eth_src[ETH_ADDR_LEN];
+
+    struct hmap remote_mps;     /* Expected remote mps */
+    struct hmap x_remote_mps;   /* Unexpected remote mps */
+    struct hmap x_remote_maids; /* Unexpected remote maids */
+    bool fault;
+
+    void (*send_heartbeat)(struct ofpbuf *packet, void *aux);
+    void *aux;
+};
+
+void cfm_run(struct cfm *);
+void cfm_wait(struct cfm *);
+
+bool cfm_should_process_flow(const struct flow *flow);
+
+void cfm_process_heartbeat(struct cfm *cfm, const struct ofpbuf *packet);
+
+struct cfm *cfm_create(void);
+
+bool cfm_configure(struct cfm *cfm);
+
+uint8_t *cfm_generate_maid(const char *md_name, const char *ma_name,
+                           uint8_t maid[48]);
+
+const struct remote_mp *cfm_get_remote_mp(const struct cfm *cfm,
uint16_t mpid);
+
+void cfm_update_remote_mps(struct cfm *cfm, uint16_t *mpid, size_t n_mpids);
+
+void cfm_destroy(struct cfm *);
+
+#endif /* cfm.h */
+
diff --git a/lib/packets.h b/lib/packets.h
index 16322d6..c69381f 100644
--- a/lib/packets.h
+++ b/lib/packets.h
@@ -153,6 +153,7 @@ void compose_benign_packet(struct ofpbuf *, const char *tag,
 #define ETH_TYPE_IP            0x0800
 #define ETH_TYPE_ARP           0x0806
 #define ETH_TYPE_VLAN          0x8100
+#define ETH_TYPE_CFM           0x8902

 #define ETH_HEADER_LEN 14
 #define ETH_PAYLOAD_MIN 46
@@ -236,6 +237,20 @@ struct vlan_eth_header {
 } __attribute__((packed));
 BUILD_ASSERT_DECL(VLAN_ETH_HEADER_LEN == sizeof(struct vlan_eth_header));

+#define CCM_LEN 74
+#define MAID_LEN 48
+struct ccm{
+    uint8_t  mdlevel_version; /* MD Level and Version */
+    uint8_t  opcode;
+    uint8_t  flags;
+    uint8_t  tlv_offset;
+    uint32_t seq;
+    uint16_t mpid;
+    uint8_t  maid[MAID_LEN];
+    uint8_t  zero[16]; /* Defined by ITU-T Y.1731 should be zero */
+} __attribute__((packed));
+BUILD_ASSERT_DECL(CCM_LEN == sizeof(struct ccm));
+
 /* The "(void) (ip)[0]" below has no effect on the value, since it's the first
  * argument of a comma expression, but it makes sure that 'ip' is a pointer.
  * This is useful since a common mistake is to pass an integer instead of a
diff --git a/lib/vlog-modules.def b/lib/vlog-modules.def
index 7e62994..20d7bb3 100644
--- a/lib/vlog-modules.def
+++ b/lib/vlog-modules.def
@@ -18,6 +18,7 @@
 VLOG_MODULE(backtrace)
 VLOG_MODULE(brcompatd)
 VLOG_MODULE(bridge)
+VLOG_MODULE(cfm)
 VLOG_MODULE(collectors)
 VLOG_MODULE(controller)
 VLOG_MODULE(coverage)
diff --git a/utilities/ovs-vsctl.c b/utilities/ovs-vsctl.c
index acdcaf3..9d0234e 100644
--- a/utilities/ovs-vsctl.c
+++ b/utilities/ovs-vsctl.c
@@ -1973,6 +1973,14 @@ static const struct vsctl_table_class tables[] = {
      {{&ovsrec_table_port, &ovsrec_port_col_name, &ovsrec_port_col_qos},
       {NULL, NULL, NULL}}},

+    {&ovsrec_table_monitor,
+     {{&ovsrec_table_maintenance_point, NULL, NULL},
+      {NULL, NULL, NULL}}},
+
+    {&ovsrec_table_maintenance_point,
+     {{NULL, NULL, NULL},
+      {NULL, NULL, NULL}}},
+
     {&ovsrec_table_queue,
      {{NULL, NULL, NULL},
       {NULL, NULL, NULL}}},
diff --git a/vswitchd/bridge.c b/vswitchd/bridge.c
index e7bc5ab..29306e2 100644
--- a/vswitchd/bridge.c
+++ b/vswitchd/bridge.c
@@ -32,6 +32,7 @@
 #include <sys/types.h>
 #include <unistd.h>
 #include "bitmap.h"
+#include "cfm.h"
 #include "classifier.h"
 #include "coverage.h"
 #include "dirs.h"
@@ -91,6 +92,7 @@ struct iface {
     struct netdev *netdev;      /* Network device. */
     bool enabled;               /* May be chosen for flows? */
     const char *type;           /* Usually same as cfg->type. */
+    struct cfm *cfm;            /* Connectivity Fault Management */
     const struct ovsrec_interface *cfg;
 };

@@ -258,6 +260,8 @@ static struct iface *iface_from_dp_ifidx(const
struct bridge *,
 static void iface_set_mac(struct iface *);
 static void iface_set_ofport(const struct ovsrec_interface *, int64_t ofport);
 static void iface_update_qos(struct iface *, const struct ovsrec_qos *);
+static void iface_update_cfm(struct iface *);
+static void iface_send_heartbeat(struct ofpbuf *packet, void *aux);

 static void shash_from_ovs_idl_map(char **keys, char **values, size_t n,
                                    struct shash *);
@@ -919,6 +923,13 @@ bridge_reconfigure(const struct
ovsrec_open_vswitch *ovs_cfg)
         iterate_and_prune_ifaces(br, set_iface_properties, NULL);
     }

+    LIST_FOR_EACH (br, node, &all_bridges) {
+        struct iface *iface;
+        HMAP_FOR_EACH (iface, dp_ifidx_node, &br->ifaces) {
+            iface_update_cfm(iface);
+        }
+    }
+
     free(managers);
 }

@@ -1138,6 +1149,71 @@ dpid_from_hash(const void *data, size_t n)
 }

 static void
+iface_refresh_cfm_stats(struct iface *iface)
+{
+    size_t i, size;
+    struct cfm *cfm;
+    int64_t *x_remote_mps;
+    struct remote_mp *rmp;
+    char **x_remote_maids;
+    struct remote_maid *rmaid;
+    const struct ovsrec_monitor *cfg;
+
+    cfg = iface->cfg->monitor;
+    cfm = iface->cfm;
+
+    if (!cfm || !cfg) {
+        return;
+    }
+
+    for (i = 0; i < cfg->n_remote_mps; i++) {
+        const struct ovsrec_maintenance_point *mp;
+        const struct remote_mp *rmp;
+        int64_t rseq[1], rtime[1];
+
+        mp       = cfg->value_remote_mps[i];
+        rmp      = cfm_get_remote_mp(cfm, mp->mpid);
+        rseq[0]  = rmp->recv_seq;
+        rtime[0] = rmp->recv_time;
+
+        ovsrec_maintenance_point_set_fault(mp, &rmp->fault, 1);
+        ovsrec_maintenance_point_set_received_sequence(mp, rseq, 1);
+        ovsrec_maintenance_point_set_received_time(mp, rtime, 1);
+    }
+
+    size         = cfm->x_remote_mps.n;
+    x_remote_mps = xzalloc(size  * sizeof *x_remote_mps);
+    i            = 0;
+    HMAP_FOR_EACH(rmp, node, &cfm->x_remote_mps) {
+        x_remote_mps[i] = rmp->mpid;
+        i++;
+    }
+    ovsrec_monitor_set_unexpected_remote_mps(cfg, x_remote_mps, size);
+    free(x_remote_mps);
+
+    size           = cfm->x_remote_maids.n;
+    x_remote_maids = xzalloc(size  * sizeof *x_remote_maids);
+    i              = 0;
+    HMAP_FOR_EACH(rmaid, node, &cfm->x_remote_maids) {
+        uint64_t *maid64;
+
+        maid64 = (uint64_t *) rmaid->maid;
+        x_remote_maids[i] = xasprintf("%llx %llx %llx %llx %llx %llx",
+                                      maid64[0], maid64[1], maid64[2],
+                                      maid64[3], maid64[4], maid64[5]);
+        i++;
+    }
+    ovsrec_monitor_set_unexpected_remote_maids(cfg, x_remote_maids, size);
+
+    for (i = 0; i < size; i++) {
+        free(x_remote_maids[i]);
+    }
+    free(x_remote_maids);
+
+    ovsrec_monitor_set_fault(cfg, &cfm->fault, 1);
+}
+
+static void
 iface_refresh_stats(struct iface *iface)
 {
     struct iface_stat {
@@ -1269,6 +1345,7 @@ bridge_run(void)
                     for (j = 0; j < port->n_ifaces; j++) {
                         struct iface *iface = port->ifaces[j];
                         iface_refresh_stats(iface);
+                        iface_refresh_cfm_stats(iface);
                     }
                 }
             }
@@ -1285,6 +1362,7 @@ void
 bridge_wait(void)
 {
     struct bridge *br;
+    struct iface *iface;

     LIST_FOR_EACH (br, node, &all_bridges) {
         ofproto_wait(br->ofproto);
@@ -1294,6 +1372,12 @@ bridge_wait(void)

         mac_learning_wait(br->ml);
         bond_wait(br);
+
+        HMAP_FOR_EACH (iface, dp_ifidx_node, &br->ifaces) {
+            if (iface->cfm) {
+                cfm_wait(iface->cfm);
+            }
+        }
     }
     ovsdb_idl_wait(idl);
     poll_timer_wait_until(stats_timer);
@@ -1494,6 +1578,7 @@ static int
 bridge_run_one(struct bridge *br)
 {
     int error;
+    struct iface *iface;

     error = ofproto_run1(br->ofproto);
     if (error) {
@@ -1506,6 +1591,12 @@ bridge_run_one(struct bridge *br)
     error = ofproto_run2(br->ofproto, br->flush);
     br->flush = false;

+    HMAP_FOR_EACH (iface, dp_ifidx_node, &br->ifaces) {
+        if (iface->cfm) {
+            cfm_run(iface->cfm);
+        }
+    }
+
     return error;
 }

@@ -2634,10 +2725,20 @@ bridge_normal_ofhook_cb(const struct flow
*flow, const struct ofpbuf *packet,
                         struct odp_actions *actions, tag_type *tags,
                         uint16_t *nf_output_iface, void *br_)
 {
+    struct iface *iface;
     struct bridge *br = br_;

     COVERAGE_INC(bridge_process_flow);

+    iface = iface_from_dp_ifidx(br, flow->in_port);
+
+    if (cfm_should_process_flow(flow)) {
+        if (packet && iface->cfm) {
+            cfm_process_heartbeat(iface->cfm, packet);
+        }
+        return false;
+    }
+
     return process_flow(br, flow, packet, actions, tags, nf_output_iface);
 }

@@ -3778,6 +3879,28 @@ port_update_vlan_compat(struct port *port)
 
 /* Interface functions. */

+static void
+iface_send_heartbeat(struct ofpbuf *packet, void *aux)
+{
+    struct flow flow;
+    struct iface *iface;
+    union ofp_action action;
+
+    iface = (struct iface *)aux;
+
+    memset(&action, 0, sizeof action);
+    action.output.type = htons(OFPAT_OUTPUT);
+    action.output.len  = htons(sizeof action);
+    action.output.port = htons(odp_port_to_ofp_port(iface->dp_ifidx));
+
+    flow_extract(packet, 0, ODPP_NONE, &flow);
+
+    if (ofproto_send_packet(iface->port->bridge->ofproto, &flow, &action, 1,
+                            packet)) {
+        VLOG_WARN("Failed to send ccm message");
+    }
+}
+
 static struct iface *
 iface_create(struct port *port, const struct ovsrec_interface *if_cfg)
 {
@@ -3839,6 +3962,8 @@ iface_destroy(struct iface *iface)
             bond_send_learning_packets(port);
         }

+        cfm_destroy(iface->cfm);
+
         free(iface->name);
         free(iface);

@@ -3975,6 +4100,74 @@ iface_update_qos(struct iface *iface, const
struct ovsrec_qos *qos)
         }
     }
 }
+
+static void
+iface_update_cfm(struct iface *iface)
+{
+    size_t i;
+    struct cfm *cfm;
+    struct ovsrec_monitor *cfg;
+    uint16_t *remote_mps;
+    uint8_t ea[ETH_ADDR_LEN], maid[MAID_LEN];
+
+    cfg = iface->cfg->monitor;
+
+    if (!cfg) {
+        return;
+    }
+
+    if (cfg->mpid > UINT16_MAX) {
+        VLOG_WARN("interface %s: cfm_mep_id %llu is too large.",
+                  iface->name, cfg->mpid);
+        return;
+    }
+
+    if (netdev_get_etheraddr(iface->netdev, ea)) {
+        VLOG_WARN("iface %s: Failed to get ethernet address. "
+                  "Skipping cfm.", iface->name);
+        return;
+    }
+
+    if (!cfm_generate_maid(cfg->md_name, cfg->ma_name, maid)) {
+        VLOG_WARN("iface %s: Failed to generate maid.", iface->name);
+        return;
+    }
+
+    if (!iface->cfm) {
+        iface->cfm = cfm_create();
+    }
+
+    cfm                 = iface->cfm;
+    cfm->mpid           = cfg->mpid;
+    cfm->interval       = cfg->interval ? *cfg->interval : 1000;
+    cfm->send_heartbeat = iface_send_heartbeat;
+    cfm->aux            = iface;
+
+    memcpy(&cfm->eth_src, ea, sizeof cfm->eth_src);
+    memcpy(&cfm->maid, maid, sizeof cfm->maid);
+
+
+    remote_mps = xzalloc(cfg->n_remote_mps * sizeof *remote_mps);
+    for(i = 0; i < cfg->n_remote_mps; i++) {
+        uint64_t mpid;
+
+        mpid = cfg->value_remote_mps[i]->mpid;
+        if (mpid > UINT16_MAX) {
+            VLOG_WARN("iface %s: failed to add remote mpid %llu",
+                      iface->name, cfg->mpid);
+            continue;
+        }
+
+        remote_mps[i] = mpid;
+    }
+    cfm_update_remote_mps(cfm, remote_mps, cfg->n_remote_mps);
+    free(remote_mps);
+
+    if (!cfm_configure(iface->cfm)) {
+        cfm_destroy(iface->cfm);
+        iface->cfm = NULL;
+    }
+}
 
 /* Port mirroring. */

diff --git a/vswitchd/vswitch.ovsschema b/vswitchd/vswitch.ovsschema
index db8f6eb..4d452ab 100644
--- a/vswitchd/vswitch.ovsschema
+++ b/vswitchd/vswitch.ovsschema
@@ -141,6 +141,10 @@
        "ofport": {
          "type": {"key": "integer", "min": 0, "max": 1},
          "ephemeral": true},
+       "monitor": {
+         "type": {"key": {"type": "uuid",
+                          "refTable": "Monitor"},
+                  "min": 0, "max": 1}},
        "other_config": {
          "type": {"key": "string", "value": "string", "min": 0,
"max": "unlimited"}},
        "statistics": {
@@ -149,6 +153,65 @@
        "status": {
          "type": {"key": "string", "value": "string", "min": 0,
"max": "unlimited"},
          "ephemeral": true}}},
+   "Monitor": {
+     "columns": {
+       "mpid": {
+         "type" : { "key": {
+           "type": "integer",
+           "minInteger": 1,
+           "maxInteger": 8191}}},
+       "md_name": {
+         "type" : {
+           "key": { "type": "string", "minLength": 1, "maxLength": 43},
+           "min": 0,
+           "max": 1}},
+       "ma_name": {
+         "type" : {
+           "key": { "type": "string", "minLength": 1, "maxLength": 43},
+           "min": 0, "max": 1}},
+       "interval": {
+         "type": {
+           "key": { "type": "integer", "minInteger": 1},
+           "min": 0,
+           "max": 1}},
+       "remote_mps": {
+         "type": {
+           "key": { "type": "integer"},
+           "value": { "type": "uuid", "refTable": "maintenance_point"},
+           "min": 0,
+           "max": "unlimited"},
+         "ephemeral": true},
+       "unexpected_remote_mps": {
+         "type": {
+           "key": { "type": "integer"},
+           "min": 0,
+           "max": "unlimited"},
+         "ephemeral": true},
+       "unexpected_remote_maids": {
+         "type": {
+           "key": "string",
+           "min": 0,
+           "max": "unlimited"},
+         "ephemeral": true},
+       "fault": {
+         "type": {
+           "key": { "type": "boolean"}, "min": 0, "max": 1},
+         "ephemeral": true}}},
+   "maintenance_point": {
+     "columns": {
+       "mpid": {
+         "type" : {
+           "key": { "type": "integer", "minInteger": 1, "maxInteger": 8191}},
+         "mutable": false},
+       "received_time": {
+         "type": {
+           "key": { "type": "integer"}, "min": 0, "max": 1}},
+       "received_sequence": {
+         "type": {
+           "key": { "type": "integer"}, "min": 0, "max": 1}},
+       "fault": {
+         "type": { "key": { "type": "boolean"}, "min": 0, "max": 1},
+         "ephemeral": true}}},
    "QoS": {
      "columns": {
        "type": {
-- 
1.7.3.2




More information about the dev mailing list