+/* Gets the fault status of 'cfm'. Returns a bit mask of 'cfm_fault_reason's
+ * indicating the cause of the connectivity fault, or zero if there is no
+ * fault. */
+int
+cfm_get_fault(const struct cfm *cfm)
+{
+ if (cfm->fault_override >= 0) {
+ return cfm->fault_override ? CFM_FAULT_OVERRIDE : 0;
+ }
+ return cfm->fault;
+}
+
+/* Gets the health of 'cfm'. Returns an integer between 0 and 100 indicating
+ * the health of the link as a percentage of ccm frames received in
+ * CFM_HEALTH_INTERVAL * 'fault_interval' if there is only 1 remote_mpid,
+ * returns 0 if there are no remote_mpids, and returns -1 if there are more
+ * than 1 remote_mpids. */
+int
+cfm_get_health(const struct cfm *cfm)
+{
+ return cfm->health;
+}
+
+/* Gets the operational state of 'cfm'. 'cfm' is considered operationally down
+ * if it has received a CCM with the operationally down bit set from any of its
+ * remote maintenance points. Returns true if 'cfm' is operationally up. False
+ * otherwise. */
+bool
+cfm_get_opup(const struct cfm *cfm)
+{
+ return cfm->remote_opup;
+}
+
+/* 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)
+{
+ struct cfm *cfm;
+
+ HMAP_FOR_EACH_WITH_HASH (cfm, hmap_node, hash_string(name, 0), &all_cfms) {
+ if (!strcmp(cfm->name, name)) {
+ return cfm;
+ }
+ }
+ return NULL;
+}
+
+static void
+cfm_print_details(struct ds *ds, const struct cfm *cfm)
+{
+ struct remote_mp *rmp;
+ int fault;
+
+ ds_put_format(ds, "---- %s ----\n", cfm->name);
+ ds_put_format(ds, "MPID %"PRIu64":%s%s\n", cfm->mpid,
+ cfm->extended ? " extended" : "",
+ cfm->fault_override >= 0 ? " fault_override" : "");
+
+ fault = cfm_get_fault(cfm);
+ if (fault) {
+ ds_put_cstr(ds, "\tfault:");
+ ds_put_cfm_fault(ds, fault, fault);
+ ds_put_cstr(ds, "\n");
+ }
+
+ if (cfm->health == -1) {
+ ds_put_format(ds, "\taverage health: undefined\n");
+ } else {
+ ds_put_format(ds, "\taverage health: %d\n", cfm->health);
+ }
+ ds_put_format(ds, "\topstate: %s\n", cfm->opup ? "up" : "down");
+ ds_put_format(ds, "\tremote_opstate: %s\n",
+ cfm->remote_opup ? "up" : "down");
+ ds_put_format(ds, "\tinterval: %dms\n", cfm->ccm_interval_ms);
+ ds_put_format(ds, "\tnext CCM tx: %lldms\n",
+ timer_msecs_until_expired(&cfm->tx_timer));
+ ds_put_format(ds, "\tnext fault check: %lldms\n",
+ timer_msecs_until_expired(&cfm->fault_timer));
+
+ HMAP_FOR_EACH (rmp, node, &cfm->remote_mps) {
+ ds_put_format(ds, "Remote MPID %"PRIu64"\n", rmp->mpid);
+ ds_put_format(ds, "\trecv since check: %s\n",
+ rmp->recv ? "true" : "false");
+ ds_put_format(ds, "\topstate: %s\n", rmp->opup? "up" : "down");
+ }
+}
+
+static void
+cfm_unixctl_show(struct unixctl_conn *conn, int argc, const char *argv[],
+ void *aux OVS_UNUSED)
+{
+ struct ds ds = DS_EMPTY_INITIALIZER;
+ const struct cfm *cfm;
+
+ if (argc > 1) {
+ cfm = cfm_find(argv[1]);
+ if (!cfm) {
+ unixctl_command_reply_error(conn, "no such CFM object");
+ return;
+ }
+ cfm_print_details(&ds, cfm);
+ } else {
+ HMAP_FOR_EACH (cfm, hmap_node, &all_cfms) {
+ cfm_print_details(&ds, cfm);
+ }
+ }