The cfm_fault column of the database is the logical OR of a number of reasons that CFM can be in a faulted state. A controller may want to have more specific information in which case it can look at the cfm_fault_status column which this patch adds.
Signed-off-by: Ethan Jackson <[email protected]> --- lib/cfm.c | 92 +++++++++++++++++++++++++++++++++++--------- lib/cfm.h | 11 +++++- ofproto/ofproto-provider.h | 7 ++- ofproto/ofproto.c | 7 ++- vswitchd/bridge.c | 16 ++++++++ vswitchd/vswitch.ovsschema | 6 ++- vswitchd/vswitch.xml | 29 ++++++++++++++ 7 files changed, 140 insertions(+), 28 deletions(-) diff --git a/lib/cfm.c b/lib/cfm.c index 537eeaa..9d905a5 100644 --- a/lib/cfm.c +++ b/lib/cfm.c @@ -85,8 +85,9 @@ struct cfm { uint64_t mpid; bool extended; /* Extended mode. */ - bool fault; /* Indicates connectivity fault. */ - bool unexpected_recv; /* Received an unexpected CCM. */ + int fault; /* Connectivity fault status. */ + bool maid_recv; /* Received a CCM from an unexpected MAID. */ + bool overflow_recv; /* Received CCM which overflows CFM_MAX_RMPS. */ bool opup; /* Operational State. */ bool remote_opup; /* Remote Operational State. */ @@ -137,6 +138,40 @@ cfm_ccm_addr(const struct cfm *cfm) } static void +ds_put_cfm_fault(struct ds *ds, int fault) +{ + size_t length = ds->length; + + if (fault & CFM_FAULT_RECV) { + ds_put_cstr(ds, "recv "); + } + + if (fault & CFM_FAULT_RDI) { + ds_put_cstr(ds, "rdi "); + } + + if (fault & CFM_FAULT_MAID) { + ds_put_cstr(ds, "maid "); + } + + if (fault & CFM_FAULT_LOOPBACK) { + ds_put_cstr(ds, "loopback "); + } + + if (fault & CFM_FAULT_OVERFLOW) { + ds_put_cstr(ds, "overflow "); + } + + if (fault & CFM_FAULT_OVERRIDE) { + ds_put_cstr(ds, "override "); + } + + if (ds->length > length) { + ds_truncate(ds, ds->length - 1); + } +} + +static void cfm_generate_maid(struct cfm *cfm) { const char *ovs_md_name = "ovs"; @@ -291,8 +326,17 @@ cfm_run(struct cfm *cfm) struct remote_mp *rmp, *rmp_next; bool old_cfm_fault = cfm->fault; - cfm->fault = cfm->unexpected_recv; - cfm->unexpected_recv = false; + cfm->fault = 0; + + if (cfm->maid_recv) { + cfm->maid_recv = false; + cfm->fault |= CFM_FAULT_MAID; + } + + if (cfm->overflow_recv) { + cfm->overflow_recv = false; + cfm->fault |= CFM_FAULT_OVERFLOW; + } cfm->rmps_array_len = 0; free(cfm->rmps_array); @@ -313,13 +357,13 @@ cfm_run(struct cfm *cfm) if (rmp->mpid == cfm->mpid) { VLOG_WARN_RL(&rl,"%s: received CCM with local MPID" " %"PRIu64, cfm->name, rmp->mpid); - cfm->fault = true; + cfm->fault |= CFM_FAULT_LOOPBACK; } if (rmp->rdi) { VLOG_DBG("%s: RDI bit flagged from RMP %"PRIu64, cfm->name, rmp->mpid); - cfm->fault = true; + cfm->fault |= CFM_FAULT_RDI; } if (!rmp->opup) { @@ -331,12 +375,16 @@ cfm_run(struct cfm *cfm) } if (hmap_is_empty(&cfm->remote_mps)) { - cfm->fault = true; + cfm->fault |= CFM_FAULT_RECV; } if (old_cfm_fault != cfm->fault) { - VLOG_INFO_RL(&rl, "%s: CFM fault status changed to %s", - cfm->name, cfm->fault ? "true" : "false"); + struct ds ds = DS_EMPTY_INITIALIZER; + + ds_put_cfm_fault(&ds, cfm->fault); + VLOG_INFO_RL(&rl, "%s: CFM fault status changed: %s", cfm->name, + ds_cstr_ro(&ds)); + ds_destroy(&ds); } timer_set_duration(&cfm->fault_timer, interval); @@ -481,7 +529,7 @@ cfm_process_heartbeat(struct cfm *cfm, const struct ofpbuf *p) * bonds. Furthermore, faults can be maliciously triggered by crafting * invalid CCMs. */ if (memcmp(ccm->maid, cfm->maid, sizeof ccm->maid)) { - cfm->unexpected_recv = true; + cfm->maid_recv = true; VLOG_WARN_RL(&rl, "%s: Received unexpected remote MAID from MAC " ETH_ADDR_FMT, cfm->name, ETH_ADDR_ARGS(eth->eth_src)); } else { @@ -522,7 +570,7 @@ cfm_process_heartbeat(struct cfm *cfm, const struct ofpbuf *p) rmp = xzalloc(sizeof *rmp); hmap_insert(&cfm->remote_mps, &rmp->node, hash_mpid(ccm_mpid)); } else { - cfm->unexpected_recv = true; + cfm->overflow_recv = true; VLOG_WARN_RL(&rl, "%s: dropped CCM with MPID %"PRIu64" from MAC " ETH_ADDR_FMT, cfm->name, ccm_mpid, @@ -551,13 +599,14 @@ cfm_process_heartbeat(struct cfm *cfm, const struct ofpbuf *p) } } -/* Gets the fault status of 'cfm'. Returns true when 'cfm' has detected - * connectivity problems, false otherwise. */ -bool +/* Gets the fault status of 'cfm'. Returns a bit mask of 'cfm_fault_reason's + * indicating the cause of the connectivity fault, or zero of there is no + * fault. */ +int cfm_get_fault(const struct cfm *cfm) { if (cfm->fault_override >= 0) { - return cfm->fault_override; + return cfm->fault_override ? CFM_FAULT_OVERRIDE : 0; } return cfm->fault; } @@ -602,11 +651,16 @@ cfm_print_details(struct ds *ds, const struct cfm *cfm) struct remote_mp *rmp; ds_put_format(ds, "---- %s ----\n", cfm->name); - ds_put_format(ds, "MPID %"PRIu64":%s%s%s%s\n", cfm->mpid, + ds_put_format(ds, "MPID %"PRIu64":%s%s\n", cfm->mpid, cfm->extended ? " extended" : "", - cfm_get_fault(cfm) ? " fault" : "", - cfm->fault_override >= 0 ? " fault_override" : "", - cfm->unexpected_recv ? " unexpected_recv" : ""); + cfm->fault_override >= 0 ? " fault_override" : ""); + + + if (cfm_get_fault(cfm)) { + ds_put_cstr(ds, "\tfault: "); + ds_put_cfm_fault(ds, cfm_get_fault(cfm)); + ds_put_cstr(ds, "\n"); + } ds_put_format(ds, "\topstate: %s\n", cfm->opup ? "up" : "down"); ds_put_format(ds, "\tremote_opstate: %s\n", diff --git a/lib/cfm.h b/lib/cfm.h index 5106a51..84c287a 100644 --- a/lib/cfm.h +++ b/lib/cfm.h @@ -24,6 +24,15 @@ struct flow; struct ofpbuf; +enum cfm_fault_reason { + CFM_FAULT_RECV = 1 << 0, /* Received no CCMs. */ + CFM_FAULT_RDI = 1 << 1, /* Received CCM with RDI bit set. */ + CFM_FAULT_MAID = 1 << 2, /* Received CCM from a different MAID. */ + CFM_FAULT_LOOPBACK = 1 << 3, /* Received CCM with local MPID. */ + CFM_FAULT_OVERFLOW = 1 << 4, /* Received CCMs from too many endpoints. */ + CFM_FAULT_OVERRIDE = 1 << 5 /* Administrative fault. */ +}; + struct cfm_settings { uint64_t mpid; /* The MPID of this CFM. */ int interval; /* The requested transmission interval. */ @@ -43,7 +52,7 @@ void cfm_wait(struct cfm *); bool cfm_configure(struct cfm *, const struct cfm_settings *); bool cfm_should_process_flow(const struct cfm *cfm, const struct flow *); void cfm_process_heartbeat(struct cfm *, const struct ofpbuf *packet); -bool cfm_get_fault(const struct cfm *); +int cfm_get_fault(const struct cfm *); bool cfm_get_opup(const struct cfm *); void cfm_get_remote_mpids(const struct cfm *, const uint64_t **rmps, size_t *n_rmps); diff --git a/ofproto/ofproto-provider.h b/ofproto/ofproto-provider.h index cb97188..3927a2e 100644 --- a/ofproto/ofproto-provider.h +++ b/ofproto/ofproto-provider.h @@ -961,9 +961,10 @@ struct ofproto_class { * support CFM, as does a null pointer. */ int (*set_cfm)(struct ofport *ofport, const struct cfm_settings *s); - /* Checks the fault status of CFM configured on 'ofport'. Returns 1 if CFM - * is faulted (generally indicating a connectivity problem), 0 if CFM is - * not faulted, or -1 if CFM is not enabled on 'port' + /* Checks the fault status of CFM configured on 'ofport'. Returns a + * bitmask of 'cfm_fault_reason's to indicate a CFM fault (generally + * indicating a connectivity problem). Returns zero if CFM is not faulted, + * and -1 if CFM is not enabled on 'port'. * * This function may be a null pointer if the ofproto implementation does * not support CFM. */ diff --git a/ofproto/ofproto.c b/ofproto/ofproto.c index c35d440..64d19d7 100644 --- a/ofproto/ofproto.c +++ b/ofproto/ofproto.c @@ -2435,9 +2435,10 @@ ofproto_get_netflow_ids(const struct ofproto *ofproto, ofproto->ofproto_class->get_netflow_ids(ofproto, engine_type, engine_id); } -/* Checks the fault status of CFM for 'ofp_port' within 'ofproto'. Returns 1 - * if CFM is faulted (generally indiciating a connectivity problem), 0 if CFM - * is not faulted, and -1 if CFM is not enabled on 'ofp_port'. */ +/* Checks the fault status of CFM for 'ofp_port' within 'ofproto'. Returns a + * bitmask of 'cfm_fault_reason's to indicate a CFM fault (generally + * indicating a connectivity problem). Returns zero if CFM is not faulted, + * and -1 if CFM is not enabled on 'port'. */ int ofproto_port_get_cfm_fault(const struct ofproto *ofproto, uint16_t ofp_port) { diff --git a/vswitchd/bridge.c b/vswitchd/bridge.c index c175c58..7f6807d 100644 --- a/vswitchd/bridge.c +++ b/vswitchd/bridge.c @@ -285,6 +285,7 @@ bridge_init(const char *remote) ovsdb_idl_omit_alert(idl, &ovsrec_interface_col_statistics); ovsdb_idl_omit_alert(idl, &ovsrec_interface_col_status); ovsdb_idl_omit_alert(idl, &ovsrec_interface_col_cfm_fault); + ovsdb_idl_omit_alert(idl, &ovsrec_interface_col_cfm_fault_status); ovsdb_idl_omit_alert(idl, &ovsrec_interface_col_cfm_remote_mpids); ovsdb_idl_omit_alert(idl, &ovsrec_interface_col_lacp_current); ovsdb_idl_omit(idl, &ovsrec_interface_col_external_ids); @@ -1554,10 +1555,24 @@ iface_refresh_cfm_stats(struct iface *iface) fault = ofproto_port_get_cfm_fault(iface->port->bridge->ofproto, iface->ofp_port); if (fault >= 0) { + char *keys[]= {"recv", "maid", "rdi", "loopback", "overflow"}; bool fault_bool = fault; + size_t i = 0; + + bool vals[ARRAY_SIZE(keys)]; + + vals[i++] = fault & CFM_FAULT_RECV; + vals[i++] = fault & CFM_FAULT_MAID; + vals[i++] = fault & CFM_FAULT_RDI; + vals[i++] = fault & CFM_FAULT_LOOPBACK; + vals[i++] = fault & CFM_FAULT_OVERFLOW; + ovsrec_interface_set_cfm_fault(cfg, &fault_bool, 1); + ovsrec_interface_set_cfm_fault_status(cfg, keys, vals, + ARRAY_SIZE(keys)); } else { ovsrec_interface_set_cfm_fault(cfg, NULL, 0); + ovsrec_interface_set_cfm_fault_status(cfg, NULL, NULL, 0); } error = ofproto_port_get_cfm_remote_mpids(iface->port->bridge->ofproto, @@ -3046,6 +3061,7 @@ iface_clear_db_record(const struct ovsrec_interface *if_cfg) ovsrec_interface_set_link_state(if_cfg, NULL); ovsrec_interface_set_mtu(if_cfg, NULL, 0); ovsrec_interface_set_cfm_fault(if_cfg, NULL, 0); + ovsrec_interface_set_cfm_fault_status(if_cfg, NULL, NULL, 0); ovsrec_interface_set_cfm_remote_mpids(if_cfg, NULL, 0); ovsrec_interface_set_lacp_current(if_cfg, NULL, 0); ovsrec_interface_set_statistics(if_cfg, NULL, NULL, 0); diff --git a/vswitchd/vswitch.ovsschema b/vswitchd/vswitch.ovsschema index 6f2c458..993dd5d 100644 --- a/vswitchd/vswitch.ovsschema +++ b/vswitchd/vswitch.ovsschema @@ -1,6 +1,6 @@ {"name": "Open_vSwitch", - "version": "6.5.0", - "cksum": "2847700438 16419", + "version": "7.0.0", + "cksum": "3395223101 16535", "tables": { "Open_vSwitch": { "columns": { @@ -205,6 +205,8 @@ "min": 0, "max": 1}, "ephemeral": true}, + "cfm_fault_status": { + "type": {"key": "string", "value": "boolean", "min": 0, "max": "unlimited"}}, "lacp_current": { "type": {"key": {"type": "boolean"}, "min": 0, "max": 1}, diff --git a/vswitchd/vswitch.xml b/vswitchd/vswitch.xml index 7be7891..812caa4 100644 --- a/vswitchd/vswitch.xml +++ b/vswitchd/vswitch.xml @@ -1699,6 +1699,35 @@ </p> </column> + <column name="cfm_fault_status" key="recv"> + When true, indicates a CFM fault was triggered due to a lack fo CCMs + received on the <ref table="Interface"/>. + </column> + + <column name="cfm_fault_status" key="rdi"> + When true, indicates a CFM fault was triggered due to the reception of + a CCM with the RDI bit flagged. Endpoints set the RDI bit in their + CCMs when they are not receiving CCMs themselves. This typically + indicates a unidirectinoal connectivity failure. + </column> + + <column name="cfm_fault_status" key="maid"> + When true, indicates a CFM fault was triggered due to the reception of + a CCM with a MAID other than the one Open vSwitch uses. + </column> + + <column name="cfm_fault_status" key="loopback"> + When true, indicates a CFM fault was triggered due to the reception of + a CCM advertising the same MPID configured in the + <ref column="cfm_mpid"/> of this <ref table="Interface"/> this may + indicate a loop in the network. + </column> + + <column name="cfm_fault_status" key="overflow"> + When true, indicates a CFM fault was triggered because the CFM module + received CCMs from more remote endpoints than it can keep track of. + </column> + <column name="cfm_remote_mpids"> When CFM is properly configured, Open vSwitch will occasionally receive CCM broadcasts. These broadcasts contain the MPID of the -- 1.7.8.3 _______________________________________________ dev mailing list [email protected] http://openvswitch.org/mailman/listinfo/dev
