X-Git-Url: https://pintos-os.org/cgi-bin/gitweb.cgi?a=blobdiff_plain;f=lib%2Fcfm.c;h=134d8b78bdb767be17f86d8d07ca017aebf84366;hb=5cc2c261d33f091ea45ff3de22737cf4deb724a2;hp=8b9e5bc545d01f88577e977be69fbd75cd5fba6e;hpb=189cb9e468807f221f9db23a2fce5a721c6b137c;p=openvswitch diff --git a/lib/cfm.c b/lib/cfm.c index 8b9e5bc5..134d8b78 100644 --- a/lib/cfm.c +++ b/lib/cfm.c @@ -1,5 +1,5 @@ /* - * 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. @@ -60,20 +60,21 @@ static const uint8_t eth_addr_ccm_x[6] = { #define CCM_MAID_LEN 48 #define CCM_OPCODE 1 /* CFM message opcode meaning CCM. */ #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; @@ -86,8 +87,10 @@ struct cfm { uint64_t mpid; bool extended; /* Extended mode. */ - int fault; /* Connectivity fault status. */ - int recv_fault; /* Bit mask of faults occuring on receive. */ + bool booted; /* A full fault interval has occured. */ + 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. */ @@ -111,6 +114,12 @@ struct cfm { * avoid flapping. */ uint64_t *rmps_array; /* Cache of remote_mps. */ size_t rmps_array_len; /* Number of rmps in 'rmps_array'. */ + + int health; /* Percentage of the number of CCM frames + 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 @@ -120,10 +129,12 @@ struct remote_mp { 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 + CFM_HEALTH_INTERVAL * 'fault_interval'. */ + long long int last_rx; /* Last CCM reception time. */ + }; static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(20, 30); @@ -152,7 +163,6 @@ cfm_fault_reason_to_str(int reason) { static void ds_put_cfm_fault(struct ds *ds, int fault) { - size_t length = ds->length; int i; for (i = 0; i < CFM_FAULT_N_REASONS; i++) { @@ -163,9 +173,7 @@ ds_put_cfm_fault(struct ds *ds, int fault) } } - if (ds->length > length) { - ds_truncate(ds, ds->length - 1); - } + ds_chomp(ds, ' '); } static void @@ -290,6 +298,8 @@ cfm_create(const char *name) hmap_insert(&all_cfms, &cfm->hmap_node, hash_string(cfm->name, 0)); cfm->remote_opup = true; cfm->fault_override = -1; + cfm->health = -1; + cfm->last_tx = 0; return cfm; } @@ -332,28 +342,48 @@ cfm_run(struct cfm *cfm) sizeof *cfm->rmps_array); cfm->remote_opup = true; + if (cfm->health_interval == CFM_HEALTH_INTERVAL) { + /* Calculate the cfm health of the interface. If the number of + * remote_mpids of a cfm interface is > 1, the cfm health is + * undefined. If the number of remote_mpids is 1, the cfm health is + * the percentage of the ccm frames received in the + * (CFM_HEALTH_INTERVAL * 3.5)ms, else it is 0. */ + if (hmap_count(&cfm->remote_mps) > 1) { + cfm->health = -1; + } else if (hmap_is_empty(&cfm->remote_mps)) { + cfm->health = 0; + } else { + int exp_ccm_recvd; + + rmp = CONTAINER_OF(hmap_first(&cfm->remote_mps), + struct remote_mp, node); + exp_ccm_recvd = (CFM_HEALTH_INTERVAL * 7) / 2; + /* Calculate the percentage of healthy ccm frames received. + * Since the 'fault_interval' is (3.5 * cfm_interval), and + * 1 CCM packet must be received every cfm_interval, + * the 'remote_mpid' health reports the percentage of + * healthy CCM frames received every + * 'CFM_HEALTH_INTERVAL'th 'fault_interval'. */ + cfm->health = (rmp->num_health_ccm * 100) / exp_ccm_recvd; + cfm->health = MIN(cfm->health, 100); + rmp->num_health_ccm = 0; + assert(cfm->health >= 0 && cfm->health <= 100); + } + cfm->health_interval = 0; + } + cfm->health_interval++; + HMAP_FOR_EACH_SAFE (rmp, rmp_next, node, &cfm->remote_mps) { if (!rmp->recv) { - VLOG_DBG("%s: no CCM from RMP %"PRIu64" in the last %lldms", - cfm->name, rmp->mpid, interval); + VLOG_INFO("%s: Received no CCM from RMP %"PRIu64" in the last" + " %lldms", cfm->name, rmp->mpid, + time_msec() - rmp->last_rx); hmap_remove(&cfm->remote_mps, &rmp->node); free(rmp); } 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; } @@ -366,16 +396,21 @@ cfm_run(struct cfm *cfm) 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_cstr(&ds, "from ["); + ds_put_cfm_fault(&ds, old_cfm_fault); + ds_put_cstr(&ds, "] to ["); 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_char(&ds, ']'); + VLOG_INFO("%s: CFM faults changed %s.", cfm->name, ds_cstr(&ds)); ds_destroy(&ds); } + cfm->booted = true; timer_set_duration(&cfm->fault_timer, interval); + VLOG_DBG("%s: new fault interval", cfm->name); } } @@ -432,11 +467,23 @@ cfm_compose_ccm(struct cfm *cfm, struct ofpbuf *packet, 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)) { + if (cfm->booted && 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 @@ -521,7 +568,7 @@ cfm_process_heartbeat(struct cfm *cfm, const struct ofpbuf *p) * 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 " @@ -535,6 +582,7 @@ cfm_process_heartbeat(struct cfm *cfm, const struct ofpbuf *p) uint64_t ccm_mpid; uint32_t ccm_seq; bool ccm_opdown; + enum cfm_fault_reason cfm_fault = 0; if (cfm->extended) { ccm_mpid = ntohll(ccm->mpid64); @@ -546,14 +594,16 @@ cfm_process_heartbeat(struct cfm *cfm, const struct ofpbuf *p) 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); } 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); } @@ -564,7 +614,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->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, @@ -572,11 +622,23 @@ cfm_process_heartbeat(struct cfm *cfm, const struct ofpbuf *p) } } + 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 (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" @@ -585,10 +647,14 @@ cfm_process_heartbeat(struct cfm *cfm, const struct ofpbuf *p) } rmp->mpid = ccm_mpid; + 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; + rmp->last_rx = time_msec(); } } } @@ -605,6 +671,17 @@ cfm_get_fault(const struct cfm *cfm) 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 @@ -643,19 +720,25 @@ 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" : ""); - - if (cfm_get_fault(cfm)) { + fault = cfm_get_fault(cfm); + if (fault) { ds_put_cstr(ds, "\tfault: "); - ds_put_cfm_fault(ds, cfm_get_fault(cfm)); + ds_put_cfm_fault(ds, 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"); @@ -666,9 +749,7 @@ cfm_print_details(struct ds *ds, const struct cfm *cfm) 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");