Add fallback definition of SIG_ATOMIC_MAX
[openvswitch] / lib / cfm.c
index b4f64c71957feb9f633259275fec355a32a71893..c3d96d8a0b45557049c0c20ce8921bc80894b6e4 100644 (file)
--- a/lib/cfm.c
+++ b/lib/cfm.c
@@ -22,6 +22,7 @@
 #include <stdlib.h>
 #include <string.h>
 
+#include "byte-order.h"
 #include "dynamic-string.h"
 #include "flow.h"
 #include "hash.h"
@@ -61,7 +62,11 @@ struct ccm {
     ovs_be32 seq;
     ovs_be16 mpid;
     uint8_t  maid[CCM_MAID_LEN];
-    uint8_t  zero[16]; /* Defined by ITU-T Y.1731 should be zero */
+
+    /* Defined by ITU-T Y.1731 should be zero */
+    ovs_be16 interval_ms_x;      /* Transmission interval in ms. */
+    ovs_be64 mpid64;             /* MPID in extended mode. */
+    uint8_t  zero[6];
 } __attribute__((packed));
 BUILD_ASSERT_DECL(CCM_LEN == sizeof(struct ccm));
 
@@ -69,7 +74,7 @@ struct cfm {
     char *name;                 /* Name of this CFM object. */
     struct hmap_node hmap_node; /* Node in all_cfms list. */
 
-    uint16_t mpid;
+    uint64_t mpid;
     bool extended;         /* Extended mode. */
     bool fault;            /* Indicates connectivity fault. */
     bool unexpected_recv;  /* Received an unexpected CCM. */
@@ -83,12 +88,17 @@ struct cfm {
     struct timer fault_timer; /* Check for faults when expired. */
 
     struct hmap remote_mps;   /* Remote MPs. */
+
+    /* Result of cfm_get_remote_mpids(). Updated only during fault check to
+     * avoid flapping. */
+    uint64_t *rmps_array;     /* Cache of remote_mps. */
+    size_t rmps_array_len;    /* Number of rmps in 'rmps_array'. */
 };
 
 /* Remote MPs represent foreign network entities that are configured to have
  * the same MAID as this CFM instance. */
 struct remote_mp {
-    uint16_t mpid;         /* The Maintenance Point ID of this 'remote_mp'. */
+    uint64_t mpid;         /* The Maintenance Point ID of this 'remote_mp'. */
     struct hmap_node node; /* Node in 'remote_mps' map. */
 
     bool recv;           /* CCM was received since last fault check. */
@@ -180,20 +190,21 @@ ms_to_ccm_interval(int interval_ms)
 }
 
 static uint32_t
-hash_mpid(uint8_t mpid)
+hash_mpid(uint64_t mpid)
 {
-    return hash_int(mpid, 0);
+    return hash_bytes(&mpid, sizeof mpid, 0);
 }
 
 static bool
-cfm_is_valid_mpid(uint32_t mpid)
+cfm_is_valid_mpid(bool extended, uint64_t mpid)
 {
-    /* 802.1ag specification requires MPIDs to be within the range [1, 8191] */
-    return mpid >= 1 && mpid <= 8191;
+    /* 802.1ag specification requires MPIDs to be within the range [1, 8191].
+     * In extended mode we relax this requirement. */
+    return mpid >= 1 && (extended || mpid <= 8191);
 }
 
 static struct remote_mp *
-lookup_remote_mp(const struct cfm *cfm, uint16_t mpid)
+lookup_remote_mp(const struct cfm *cfm, uint64_t mpid)
 {
     struct remote_mp *rmp;
 
@@ -243,6 +254,7 @@ cfm_destroy(struct cfm *cfm)
 
     hmap_destroy(&cfm->remote_mps);
     hmap_remove(&all_cfms, &cfm->hmap_node);
+    free(cfm->rmps_array);
     free(cfm->name);
     free(cfm);
 }
@@ -258,10 +270,15 @@ cfm_run(struct cfm *cfm)
         cfm->fault = cfm->unexpected_recv;
         cfm->unexpected_recv = false;
 
+        cfm->rmps_array_len = 0;
+        free(cfm->rmps_array);
+        cfm->rmps_array = xmalloc(hmap_count(&cfm->remote_mps) *
+                                  sizeof *cfm->rmps_array);
+
         HMAP_FOR_EACH_SAFE (rmp, rmp_next, node, &cfm->remote_mps) {
 
             if (!rmp->recv) {
-                VLOG_DBG("%s: No CCM from RMP %"PRIu16" in the last %lldms",
+                VLOG_DBG("%s: no CCM from RMP %"PRIu64" in the last %lldms",
                          cfm->name, rmp->mpid, interval);
                 hmap_remove(&cfm->remote_mps, &rmp->node);
                 free(rmp);
@@ -269,16 +286,18 @@ cfm_run(struct cfm *cfm)
                 rmp->recv = false;
 
                 if (rmp->mpid == cfm->mpid) {
-                    VLOG_WARN_RL(&rl, "%s: Received CCM with local MPID (%d)",
-                                 cfm->name, rmp->mpid);
+                    VLOG_WARN_RL(&rl,"%s: received CCM with local MPID"
+                                 " %"PRIu64, cfm->name, rmp->mpid);
                     cfm->fault = true;
                 }
 
                 if (rmp->rdi) {
-                    VLOG_DBG("%s: RDI bit flagged from RMP %"PRIu16, cfm->name,
+                    VLOG_DBG("%s: RDI bit flagged from RMP %"PRIu64, cfm->name,
                              rmp->mpid);
                     cfm->fault = true;
                 }
+
+                cfm->rmps_array[cfm->rmps_array_len++] = rmp->mpid;
             }
         }
 
@@ -313,11 +332,23 @@ cfm_compose_ccm(struct cfm *cfm, struct ofpbuf *packet,
     ccm->opcode = CCM_OPCODE;
     ccm->tlv_offset = 70;
     ccm->seq = htonl(++cfm->seq);
-    ccm->mpid = htons(cfm->mpid);
     ccm->flags = cfm->ccm_interval;
     memcpy(ccm->maid, cfm->maid, sizeof ccm->maid);
     memset(ccm->zero, 0, sizeof ccm->zero);
 
+    if (cfm->extended) {
+        ccm->mpid = htons(hash_mpid(cfm->mpid));
+        ccm->mpid64 = htonll(cfm->mpid);
+    } else {
+        ccm->mpid = htons(cfm->mpid);
+        ccm->mpid64 = htonll(0);
+    }
+
+    if (cfm->ccm_interval == 0) {
+        assert(cfm->extended);
+        ccm->interval_ms_x = htons(cfm->ccm_interval_ms);
+    }
+
     if (hmap_is_empty(&cfm->remote_mps)) {
         ccm->flags |= CCM_RDI_MASK;
     }
@@ -326,7 +357,6 @@ cfm_compose_ccm(struct cfm *cfm, struct ofpbuf *packet,
 void
 cfm_wait(struct cfm *cfm)
 {
-
     timer_wait(&cfm->tx_timer);
     timer_wait(&cfm->fault_timer);
 }
@@ -336,18 +366,25 @@ bool
 cfm_configure(struct cfm *cfm, const struct cfm_settings *s)
 {
     uint8_t interval;
+    int interval_ms;
 
-    if (!cfm_is_valid_mpid(s->mpid) || s->interval <= 0) {
+    if (!cfm_is_valid_mpid(s->extended, s->mpid) || s->interval <= 0) {
         return false;
     }
 
     cfm->mpid = s->mpid;
     cfm->extended = s->extended;
     interval = ms_to_ccm_interval(s->interval);
+    interval_ms = ccm_interval_to_ms(interval);
 
-    if (interval != cfm->ccm_interval) {
+    if (cfm->extended && interval_ms != s->interval) {
+        interval = 0;
+        interval_ms = MIN(s->interval, UINT16_MAX);
+    }
+
+    if (interval != cfm->ccm_interval || interval_ms != cfm->ccm_interval_ms) {
         cfm->ccm_interval = interval;
-        cfm->ccm_interval_ms = ccm_interval_to_ms(interval);
+        cfm->ccm_interval_ms = interval_ms;
 
         timer_set_expired(&cfm->tx_timer);
         timer_set_duration(&cfm->fault_timer, cfm_fault_interval(cfm));
@@ -371,10 +408,6 @@ void
 cfm_process_heartbeat(struct cfm *cfm, const struct ofpbuf *p)
 {
     struct ccm *ccm;
-    bool ccm_rdi;
-    uint16_t ccm_mpid;
-    uint8_t ccm_interval;
-    struct remote_mp *rmp;
     struct eth_header *eth;
 
     eth = p->l2;
@@ -406,16 +439,28 @@ cfm_process_heartbeat(struct cfm *cfm, const struct ofpbuf *p)
         VLOG_WARN_RL(&rl, "%s: Received unexpected remote MAID from MAC "
                      ETH_ADDR_FMT, cfm->name, ETH_ADDR_ARGS(eth->eth_src));
     } else {
-        ccm_mpid = ntohs(ccm->mpid);
-        ccm_interval = ccm->flags & 0x7;
-        ccm_rdi = ccm->flags & CCM_RDI_MASK;
+        uint8_t ccm_interval = ccm->flags & 0x7;
+        bool ccm_rdi = ccm->flags & CCM_RDI_MASK;
+        uint16_t ccm_interval_ms_x = ntohs(ccm->interval_ms_x);
+
+        struct remote_mp *rmp;
+        uint64_t ccm_mpid;
+
+        ccm_mpid = cfm->extended ? ntohll(ccm->mpid64) : ntohs(ccm->mpid);
 
         if (ccm_interval != cfm->ccm_interval) {
             VLOG_WARN_RL(&rl, "%s: received a CCM with an invalid interval"
-                         " (%"PRIu8") from RMP %"PRIu16, cfm->name,
+                         " (%"PRIu8") from RMP %"PRIu64, cfm->name,
                          ccm_interval, ccm_mpid);
         }
 
+        if (cfm->extended && ccm_interval == 0
+            && ccm_interval_ms_x != cfm->ccm_interval_ms) {
+            VLOG_WARN_RL(&rl, "%s: received a CCM with an invalid extended"
+                         " interval (%"PRIu16"ms) from RMP %"PRIu64, cfm->name,
+                         ccm_interval_ms_x, ccm_mpid);
+        }
+
         rmp = lookup_remote_mp(cfm, ccm_mpid);
         if (rmp) {
             rmp->recv = true;
@@ -428,12 +473,12 @@ cfm_process_heartbeat(struct cfm *cfm, const struct ofpbuf *p)
             hmap_insert(&cfm->remote_mps, &rmp->node, hash_mpid(rmp->mpid));
         } else {
             cfm->unexpected_recv = true;
-            VLOG_WARN_RL(&rl, "%s: dropped CCM with MPID %d from MAC "
+            VLOG_WARN_RL(&rl, "%s: dropped CCM with MPID %"PRIu64" from MAC "
                          ETH_ADDR_FMT, cfm->name, ccm_mpid,
                          ETH_ADDR_ARGS(eth->eth_src));
         }
 
-        VLOG_DBG("%s: Received CCM (seq %"PRIu32") (mpid %"PRIu16")"
+        VLOG_DBG("%s: received CCM (seq %"PRIu32") (mpid %"PRIu64")"
                  " (interval %"PRIu8") (RDI %s)", cfm->name, ntohl(ccm->seq),
                  ccm_mpid, ccm_interval, ccm_rdi ? "true" : "false");
     }
@@ -447,6 +492,17 @@ cfm_get_fault(const struct cfm *cfm)
     return cfm->fault;
 }
 
+/* Populates 'rmps' with an array of remote maintenance points reachable by
+ * 'cfm'. The number of remote maintenance points is written to 'n_rmps'.
+ * 'cfm' retains ownership of the array written to 'rmps' */
+void
+cfm_get_remote_mpids(const struct cfm *cfm, const uint64_t **rmps,
+                     size_t *n_rmps)
+{
+    *rmps = cfm->rmps_array;
+    *n_rmps = cfm->rmps_array_len;
+}
+
 static struct cfm *
 cfm_find(const char *name)
 {
@@ -474,7 +530,7 @@ cfm_unixctl_show(struct unixctl_conn *conn,
         return;
     }
 
-    ds_put_format(&ds, "MPID %"PRIu16":%s%s\n", cfm->mpid,
+    ds_put_format(&ds, "MPID %"PRIu64":%s%s\n", cfm->mpid,
                   cfm->fault ? " fault" : "",
                   cfm->unexpected_recv ? " unexpected_recv" : "");
 
@@ -486,7 +542,8 @@ cfm_unixctl_show(struct unixctl_conn *conn,
 
     ds_put_cstr(&ds, "\n");
     HMAP_FOR_EACH (rmp, node, &cfm->remote_mps) {
-        ds_put_format(&ds, "Remote MPID %"PRIu16":%s\n", rmp->mpid,
+        ds_put_format(&ds, "Remote MPID %"PRIu64":%s\n",
+                      rmp->mpid,
                       rmp->rdi ? " rdi" : "");
         ds_put_format(&ds, "\trecv since check: %s",
                       rmp->recv ? "true" : "false");