X-Git-Url: https://pintos-os.org/cgi-bin/gitweb.cgi?a=blobdiff_plain;f=lib%2Fcfm.c;h=e56ccab75eea36295fd4c0b52c244b8b102f04ec;hb=e5b2afdbde9b7d6dc8d2f1bc641b6bcd3a1c7395;hp=85af22c42ab092cbe283a9c860b3c2a1ca4553f6;hpb=a56104575c4db202bfc5db3079ea969820d77867;p=openvswitch diff --git a/lib/cfm.c b/lib/cfm.c index 85af22c4..e56ccab7 100644 --- a/lib/cfm.c +++ b/lib/cfm.c @@ -36,14 +36,37 @@ VLOG_DEFINE_THIS_MODULE(cfm); -#define CCM_OPCODE 1 /* CFM message opcode meaning CCM. */ +/* Ethernet destination address of CCM packets. */ +static const uint8_t eth_addr_ccm[6] = { 0x01, 0x80, 0xC2, 0x00, 0x00, 0x30 }; + +#define ETH_TYPE_CFM 0x8902 + +/* A 'ccm' represents a Continuity Check Message from the 802.1ag + * specification. Continuity Check Messages are broadcast periodically so that + * hosts can determine whom they have connectivity to. */ +#define CCM_LEN 74 +#define CCM_MAID_LEN 48 +#define CCM_OPCODE 1 /* CFM message opcode meaning CCM. */ +#define CCM_RDI_MASK 0x80 +struct ccm { + 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 zero[16]; /* Defined by ITU-T Y.1731 should be zero */ +} __attribute__((packed)); +BUILD_ASSERT_DECL(CCM_LEN == sizeof(struct ccm)); struct cfm { - uint16_t mpid; - struct list list_node; /* Node in all_cfms list. */ + char *name; /* Name of this CFM object. */ + struct hmap_node hmap_node; /* Node in all_cfms list. */ + uint16_t mpid; bool fault; /* Indicates connectivity fault. */ - char *name; /* Name of this CFM object. */ + bool recv_fault; /* Indicates an inability to receive CCMs. */ uint32_t seq; /* The sequence number of our last CCM. */ uint8_t ccm_interval; /* The CCM transmission interval. */ @@ -64,10 +87,12 @@ struct remote_mp { bool recv; /* CCM was received since last fault check. */ bool fault; /* Indicates a connectivity fault. */ + bool rdi; /* Remote Defect Indicator. Indicates remote_mp isn't + receiving CCMs that it's expecting to. */ }; static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(5, 20); -static struct list all_cfms = LIST_INITIALIZER(&all_cfms); +static struct hmap all_cfms = HMAP_INITIALIZER(&all_cfms); static void cfm_unixctl_show(struct unixctl_conn *, const char *args, void *aux); @@ -75,8 +100,8 @@ static void cfm_unixctl_show(struct unixctl_conn *, const char *args, static void cfm_generate_maid(struct cfm *cfm) { - const char *ovs_md_name = "ovs_md"; - const char *ovs_ma_name = "ovs_ma"; + const char *ovs_md_name = "ovs"; + const char *ovs_ma_name = "ovs"; uint8_t *ma_p; size_t md_len, ma_len; @@ -176,19 +201,18 @@ cfm_init(void) unixctl_command_register("cfm/show", cfm_unixctl_show, NULL); } -/* Allocates a 'cfm' object. This object should have its 'mpid', 'maid', - * 'eth_src', and 'interval' filled out. cfm_configure() should be called - * whenever changes are made to 'cfm', and before cfm_run() is called for the - * first time. */ +/* Allocates a 'cfm' object called 'name'. 'cfm' should be initialized by + * cfm_configure() before use. */ struct cfm * -cfm_create(void) +cfm_create(const char *name) { struct cfm *cfm; cfm = xzalloc(sizeof *cfm); + cfm->name = xstrdup(name); hmap_init(&cfm->remote_mps); cfm_generate_maid(cfm); - list_push_back(&all_cfms, &cfm->list_node); + hmap_insert(&all_cfms, &cfm->hmap_node, hash_string(cfm->name, 0)); return cfm; } @@ -207,7 +231,8 @@ cfm_destroy(struct cfm *cfm) } hmap_destroy(&cfm->remote_mps); - list_remove(&cfm->list_node); + hmap_remove(&all_cfms, &cfm->hmap_node); + free(cfm->name); free(cfm); } @@ -215,25 +240,32 @@ cfm_destroy(struct cfm *cfm) void cfm_run(struct cfm *cfm) { - if (timer_expired(&cfm->fault_timer)) { long long int interval = cfm_fault_interval(cfm); struct remote_mp *rmp; cfm->fault = false; + cfm->recv_fault = false; HMAP_FOR_EACH (rmp, node, &cfm->remote_mps) { rmp->fault = !rmp->recv; rmp->recv = false; if (rmp->fault) { + cfm->recv_fault = true; + VLOG_DBG("%s: No CCM from RMP %"PRIu16" in the last %lldms", + cfm->name, rmp->mpid, interval); + } else if (rmp->rdi) { cfm->fault = true; - VLOG_DBG("No CCM from RMP %"PRIu16" in the last %lldms", - rmp->mpid, interval); + VLOG_DBG("%s: RDI bit flagged from RMP %"PRIu16, cfm->name, + rmp->mpid); } } - if (!cfm->fault) { - VLOG_DBG("All RMPs received CCMs in the last %lldms", interval); + if (cfm->recv_fault) { + cfm->fault = true; + } else { + VLOG_DBG("%s: All RMPs received CCMs in the last %lldms", + cfm->name, interval); } timer_set_duration(&cfm->fault_timer, interval); @@ -245,17 +277,21 @@ cfm_run(struct cfm *cfm) bool cfm_should_send_ccm(struct cfm *cfm) { - return timer_expired(&cfm->tx_timer); } -/* Composes a CCM message into 'ccm'. Messages generated with this function +/* Composes a CCM message into 'packet'. Messages generated with this function * should be sent whenever cfm_should_send_ccm() indicates. */ void -cfm_compose_ccm(struct cfm *cfm, struct ccm *ccm) +cfm_compose_ccm(struct cfm *cfm, struct ofpbuf *packet, + uint8_t eth_src[ETH_ADDR_LEN]) { + struct ccm *ccm; + timer_set_duration(&cfm->tx_timer, cfm->ccm_interval_ms); + ccm = eth_compose(packet, eth_addr_ccm, eth_src, ETH_TYPE_CFM, + sizeof *ccm); ccm->mdlevel_version = 0; ccm->opcode = CCM_OPCODE; ccm->tlv_offset = 70; @@ -263,6 +299,10 @@ cfm_compose_ccm(struct cfm *cfm, struct ccm *ccm) ccm->mpid = htons(cfm->mpid); ccm->flags = cfm->ccm_interval; memcpy(ccm->maid, cfm->maid, sizeof ccm->maid); + + if (cfm->recv_fault) { + ccm->flags |= CCM_RDI_MASK; + } } void @@ -290,11 +330,6 @@ cfm_configure(struct cfm *cfm, const struct cfm_settings *s) cfm->mpid = s->mpid; interval = ms_to_ccm_interval(s->interval); - if (!cfm->name || strcmp(s->name, cfm->name)) { - free(cfm->name); - cfm->name = xstrdup(s->name); - } - if (interval != cfm->ccm_interval) { cfm->ccm_interval = interval; cfm->ccm_interval_ms = ccm_interval_to_ms(interval); @@ -347,6 +382,7 @@ 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; @@ -356,13 +392,14 @@ cfm_process_heartbeat(struct cfm *cfm, const struct ofpbuf *p) ccm = ofpbuf_at(p, (uint8_t *)p->l3 - (uint8_t *)p->data, CCM_LEN); if (!ccm) { - VLOG_INFO_RL(&rl, "Received an un-parseable 802.1ag CCM heartbeat."); + VLOG_INFO_RL(&rl, "%s: Received an unparseable 802.1ag CCM heartbeat.", + cfm->name); return; } if (ccm->opcode != CCM_OPCODE) { - VLOG_INFO_RL(&rl, "Received an unsupported 802.1ag message. " - "(opcode %u)", ccm->opcode); + VLOG_INFO_RL(&rl, "%s: Received an unsupported 802.1ag message. " + "(opcode %u)", cfm->name, ccm->opcode); return; } @@ -376,29 +413,33 @@ 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)) { - VLOG_WARN_RL(&rl, "Received unexpected remote MAID from MAC " - ETH_ADDR_FMT, ETH_ADDR_ARGS(eth->eth_src)); + 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; rmp = lookup_remote_mp(&cfm->remote_mps, ccm_mpid); if (rmp) { rmp->recv = true; + rmp->rdi = ccm_rdi; if (ccm_interval != cfm->ccm_interval) { - VLOG_WARN_RL(&rl, "received a CCM with an invalid interval" - " (%"PRIu8") from RMP %"PRIu16, ccm_interval, - rmp->mpid); + VLOG_WARN_RL(&rl, "%s: received a CCM with an invalid interval" + " (%"PRIu8") from RMP %"PRIu16, cfm->name, + ccm_interval, rmp->mpid); } } else { - VLOG_WARN_RL(&rl, "Received unexpected remote MPID %d from MAC " - ETH_ADDR_FMT, ccm_mpid, ETH_ADDR_ARGS(eth->eth_src)); + VLOG_WARN_RL(&rl, "%s: Received unexpected remote MPID %d from" + " MAC " ETH_ADDR_FMT, cfm->name, ccm_mpid, + ETH_ADDR_ARGS(eth->eth_src)); } - VLOG_DBG("Received CCM (mpid %"PRIu16") (interval %"PRIu8")", ccm_mpid, - ccm_interval); + VLOG_DBG("%s: Received CCM (seq %"PRIu32") (mpid %"PRIu16")" + " (interval %"PRIu8") (RDI %s)", cfm->name, ntohl(ccm->seq), + ccm_mpid, ccm_interval, ccm_rdi ? "true" : "false"); } } @@ -415,8 +456,8 @@ cfm_find(const char *name) { struct cfm *cfm; - LIST_FOR_EACH (cfm, list_node, &all_cfms) { - if (cfm->name && !strcmp(cfm->name, name)) { + HMAP_FOR_EACH_WITH_HASH (cfm, hmap_node, hash_string(name, 0), &all_cfms) { + if (!strcmp(cfm->name, name)) { return cfm; } } @@ -437,8 +478,9 @@ cfm_unixctl_show(struct unixctl_conn *conn, return; } - ds_put_format(&ds, "MPID %"PRIu16": %s\n", cfm->mpid, - cfm->fault ? "fault" : ""); + ds_put_format(&ds, "MPID %"PRIu16":%s%s\n", cfm->mpid, + cfm->fault ? " fault" : "", + cfm->recv_fault ? " recv_fault" : ""); ds_put_format(&ds, "\tinterval: %dms\n", cfm->ccm_interval_ms); ds_put_format(&ds, "\tnext CCM tx: %lldms\n",