/*
- * Copyright (c) 2010, 2011 Nicira Networks.
+ * Copyright (c) 2010, 2011, 2012 Nicira, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
#define CCM_RDI_MASK 0x80
#define CFM_HEALTH_INTERVAL 6
struct ccm {
- uint8_t mdlevel_version; /* MD Level and Version */
- uint8_t opcode;
- uint8_t flags;
- uint8_t tlv_offset;
+ uint8_t mdlevel_version; /* MD Level and Version */
+ uint8_t opcode;
+ uint8_t flags;
+ uint8_t tlv_offset;
ovs_be32 seq;
ovs_be16 mpid;
- uint8_t maid[CCM_MAID_LEN];
+ uint8_t maid[CCM_MAID_LEN];
/* 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 opdown; /* Operationally down. */
- uint8_t zero[5];
+ uint8_t zero[5];
/* TLV space. */
uint8_t end_tlv;
uint64_t mpid;
bool extended; /* Extended mode. */
- int fault; /* Connectivity fault status. */
- int recv_fault; /* Bit mask of faults occuring on receive. */
+ enum cfm_fault_reason fault; /* Connectivity fault status. */
+ enum cfm_fault_reason recv_fault; /* Bit mask of faults occuring on
+ receive. */
bool opup; /* Operational State. */
bool remote_opup; /* Remote Operational State. */
received. */
int health_interval; /* Number of fault_intervals since health was
recomputed. */
-
+ long long int last_tx; /* Last CCM transmission time. */
};
/* Remote MPs represent foreign network entities that are configured to have
struct hmap_node node; /* Node in 'remote_mps' map. */
bool recv; /* CCM was received since last fault check. */
- bool rdi; /* Remote Defect Indicator. Indicates remote_mp isn't
- receiving CCMs that it's expecting to. */
bool opup; /* Operational State. */
uint32_t seq; /* Most recently received sequence number. */
uint8_t num_health_ccm; /* Number of received ccm frames every
}
static void
-ds_put_cfm_fault(struct ds *ds, int fault)
+ds_put_cfm_fault(struct ds *ds, int old_fault, int new_fault)
{
- size_t length = ds->length;
int i;
for (i = 0; i < CFM_FAULT_N_REASONS; i++) {
int reason = 1 << i;
- if (fault & reason) {
- ds_put_format(ds, "%s ", cfm_fault_reason_to_str(reason));
+ if ((old_fault | new_fault) & reason) {
+ ds_put_format(ds, " %s%s",
+ (!(old_fault & reason) ? "+"
+ : !(new_fault & reason) ? "-"
+ : ""),
+ cfm_fault_reason_to_str(reason));
}
}
-
- if (ds->length > length) {
- ds_truncate(ds, ds->length - 1);
- }
}
static void
cfm->remote_opup = true;
cfm->fault_override = -1;
cfm->health = -1;
+ cfm->last_tx = 0;
return cfm;
}
} else {
rmp->recv = false;
- if (rmp->mpid == cfm->mpid) {
- VLOG_WARN_RL(&rl,"%s: received CCM with local MPID"
- " %"PRIu64, cfm->name, rmp->mpid);
- cfm->fault |= CFM_FAULT_LOOPBACK;
- }
-
- if (rmp->rdi) {
- VLOG_DBG("%s: RDI bit flagged from RMP %"PRIu64, cfm->name,
- rmp->mpid);
- cfm->fault |= CFM_FAULT_RDI;
- }
-
if (!rmp->opup) {
cfm->remote_opup = rmp->opup;
}
cfm->fault |= CFM_FAULT_RECV;
}
- if (old_cfm_fault != cfm->fault) {
+ if (old_cfm_fault != cfm->fault && !VLOG_DROP_INFO(&rl)) {
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_put_cfm_fault(&ds, old_cfm_fault, cfm->fault);
+ VLOG_INFO("%s: CFM fault status changed:%s", cfm->name,
+ ds_cstr_ro(&ds));
ds_destroy(&ds);
}
timer_set_duration(&cfm->fault_timer, interval);
+ VLOG_DBG("%s: new fault interval", cfm->name);
}
}
if (cfm->ccm_interval == 0) {
assert(cfm->extended);
ccm->interval_ms_x = htons(cfm->ccm_interval_ms);
+ } else {
+ ccm->interval_ms_x = htons(0);
}
if (hmap_is_empty(&cfm->remote_mps)) {
ccm->flags |= CCM_RDI_MASK;
}
+
+ if (cfm->last_tx) {
+ long long int delay = time_msec() - cfm->last_tx;
+ if (delay > (cfm->ccm_interval_ms * 3 / 2)) {
+ VLOG_WARN("%s: long delay of %lldms (expected %dms) sending CCM"
+ " seq %"PRIu32, cfm->name, delay, cfm->ccm_interval_ms,
+ cfm->seq);
+ }
+ }
+ cfm->last_tx = time_msec();
}
void
* expensive changes to the network topology. It seems prudent to trigger
* them judiciously, especially when CFM is used to check slave status of
* bonds. Furthermore, faults can be maliciously triggered by crafting
- * invalid CCMs. */
+ * unexpected CCMs. */
if (memcmp(ccm->maid, cfm->maid, sizeof ccm->maid)) {
cfm->recv_fault |= CFM_FAULT_MAID;
VLOG_WARN_RL(&rl, "%s: Received unexpected remote MAID from MAC "
uint64_t ccm_mpid;
uint32_t ccm_seq;
bool ccm_opdown;
- bool fault = false;
+ enum cfm_fault_reason cfm_fault = 0;
if (cfm->extended) {
ccm_mpid = ntohll(ccm->mpid64);
ccm_seq = ntohl(ccm->seq);
if (ccm_interval != cfm->ccm_interval) {
- VLOG_WARN_RL(&rl, "%s: received a CCM with an invalid interval"
+ cfm_fault |= CFM_FAULT_INTERVAL;
+ VLOG_WARN_RL(&rl, "%s: received a CCM with an unexpected interval"
" (%"PRIu8") from RMP %"PRIu64, cfm->name,
ccm_interval, ccm_mpid);
- fault = true;
}
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"
+ cfm_fault |= CFM_FAULT_INTERVAL;
+ VLOG_WARN_RL(&rl, "%s: received a CCM with an unexpected extended"
" interval (%"PRIu16"ms) from RMP %"PRIu64, cfm->name,
ccm_interval_ms_x, ccm_mpid);
- fault = true;
}
rmp = lookup_remote_mp(cfm, ccm_mpid);
rmp = xzalloc(sizeof *rmp);
hmap_insert(&cfm->remote_mps, &rmp->node, hash_mpid(ccm_mpid));
} else {
- cfm->recv_fault |= CFM_FAULT_OVERFLOW;
+ cfm_fault |= CFM_FAULT_OVERFLOW;
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));
- fault = true;
}
}
+ if (ccm_rdi) {
+ cfm_fault |= CFM_FAULT_RDI;
+ VLOG_DBG("%s: RDI bit flagged from RMP %"PRIu64, cfm->name,
+ ccm_mpid);
+ }
+
VLOG_DBG("%s: received CCM (seq %"PRIu32") (mpid %"PRIu64")"
" (interval %"PRIu8") (RDI %s)", cfm->name, ccm_seq,
ccm_mpid, ccm_interval, ccm_rdi ? "true" : "false");
- if (ccm_rdi) {
- fault = true;
- }
if (rmp) {
+ if (rmp->mpid == cfm->mpid) {
+ cfm_fault |= CFM_FAULT_LOOPBACK;
+ VLOG_WARN_RL(&rl,"%s: received CCM with local MPID"
+ " %"PRIu64, cfm->name, rmp->mpid);
+ }
+
if (rmp->seq && ccm_seq != (rmp->seq + 1)) {
VLOG_WARN_RL(&rl, "%s: (mpid %"PRIu64") detected sequence"
" numbers which indicate possible connectivity"
" problems (previous %"PRIu32") (current %"PRIu32
")", cfm->name, ccm_mpid, rmp->seq, ccm_seq);
- fault = true;
}
rmp->mpid = ccm_mpid;
- rmp->recv = true;
- if (!fault) {
+ if (!cfm_fault) {
rmp->num_health_ccm++;
}
+ rmp->recv = true;
+ cfm->recv_fault |= cfm_fault;
rmp->seq = ccm_seq;
- rmp->rdi = ccm_rdi;
rmp->opup = !ccm_opdown;
}
}
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" : "");
-
- if (cfm_get_fault(cfm)) {
- ds_put_cstr(ds, "\tfault: ");
- ds_put_cfm_fault(ds, cfm_get_fault(cfm));
+ fault = cfm_get_fault(cfm);
+ if (fault) {
+ ds_put_cstr(ds, "\tfault:");
+ ds_put_cfm_fault(ds, fault, fault);
ds_put_cstr(ds, "\n");
}
timer_msecs_until_expired(&cfm->fault_timer));
HMAP_FOR_EACH (rmp, node, &cfm->remote_mps) {
- ds_put_format(ds, "Remote MPID %"PRIu64":%s\n",
- rmp->mpid,
- rmp->rdi ? " rdi" : "");
+ 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");