diff options
Diffstat (limited to 'pimd/pim_nht.c')
-rw-r--r-- | pimd/pim_nht.c | 417 |
1 files changed, 361 insertions, 56 deletions
diff --git a/pimd/pim_nht.c b/pimd/pim_nht.c index 00ab46b4c..1e9ea24b2 100644 --- a/pimd/pim_nht.c +++ b/pimd/pim_nht.c @@ -34,6 +34,176 @@ #include "pim_register.h" #include "pim_vxlan.h" +DEFINE_MTYPE_STATIC(PIMD, PIM_LOOKUP_MODE, "PIM RPF lookup mode"); +DEFINE_MTYPE_STATIC(PIMD, PIM_LOOKUP_MODE_STR, "PIM RPF lookup mode prefix list string"); + +static void pim_update_rp_nh(struct pim_instance *pim, struct pim_nexthop_cache *pnc); +static int pim_update_upstream_nh(struct pim_instance *pim, struct pim_nexthop_cache *pnc); + +static int pim_lookup_mode_cmp(const struct pim_lookup_mode *l, const struct pim_lookup_mode *r) +{ + /* Let's just sort anything with both lists set above those with only one list set, + * which is above the global where neither are set + */ + + /* Both are set on right, either lower or equal */ + if (l->grp_plist != NULL && l->src_plist != NULL) + return (r->grp_plist == NULL || r->src_plist == NULL) ? -1 : 0; + + /* Only one set on the left */ + if (!(l->grp_plist == NULL && l->src_plist == NULL)) { + /* Lower only if both are not set on right */ + if (r->grp_plist == NULL && r->src_plist == NULL) + return -1; + /* Higher only if both are set on right */ + if (r->grp_plist != NULL && r->src_plist != NULL) + return 1; + /* Otherwise both sides have at least one set, so equal */ + return 0; + } + + /* Neither set on left, so equal if neither set on right also */ + if (r->grp_plist == NULL && r->src_plist == NULL) + return 0; + + /* Otherwise higher */ + return 1; +} + +DECLARE_SORTLIST_NONUNIQ(pim_lookup_mode, struct pim_lookup_mode, list, pim_lookup_mode_cmp); + +static void pim_lookup_mode_free(struct pim_lookup_mode *m) +{ + if (m->grp_plist) + XFREE(MTYPE_PIM_LOOKUP_MODE_STR, m->grp_plist); + if (m->src_plist) + XFREE(MTYPE_PIM_LOOKUP_MODE_STR, m->src_plist); + XFREE(MTYPE_PIM_LOOKUP_MODE, m); +} + +static void pim_lookup_mode_list_free(struct pim_lookup_mode_head *head) +{ + struct pim_lookup_mode *m; + + while ((m = pim_lookup_mode_pop(head))) + pim_lookup_mode_free(m); +} + +enum pim_rpf_lookup_mode pim_get_lookup_mode(struct pim_instance *pim, pim_addr group, + pim_addr source) +{ + struct pim_lookup_mode *m; + struct prefix_list *plist; + struct prefix p; + + frr_each_safe (pim_lookup_mode, &(pim->rpf_mode), m) { + if (!pim_addr_is_any(group) && m->grp_plist) { + /* Match group against plist, continue if no match */ + plist = prefix_list_lookup(PIM_AFI, m->grp_plist); + if (plist == NULL) + continue; + pim_addr_to_prefix(&p, group); + if (prefix_list_apply(plist, &p) == PREFIX_DENY) + continue; + } + + if (!pim_addr_is_any(source) && m->src_plist) { + /* Match source against plist, continue if no match */ + plist = prefix_list_lookup(PIM_AFI, m->src_plist); + if (plist == NULL) + continue; + pim_addr_to_prefix(&p, source); + if (prefix_list_apply(plist, &p) == PREFIX_DENY) + continue; + } + + /* If lookup mode has a group list, but no group is provided, don't match it */ + if (pim_addr_is_any(group) && m->grp_plist) + continue; + + /* If lookup mode has a source list, but no source is provided, don't match it */ + if (pim_addr_is_any(source) && m->src_plist) + continue; + + /* Match found */ + return m->mode; + } + + /* This shouldn't happen since we have the global mode, but if it's gone, + * just return the default of no config + */ + if (PIM_DEBUG_PIM_NHT) + zlog_debug("%s: No RPF lookup matched for given group %pPA and source %pPA", + __func__, &group, &source); + + return MCAST_NO_CONFIG; +} + +static bool pim_rpf_mode_changed(enum pim_rpf_lookup_mode old, enum pim_rpf_lookup_mode new) +{ + if (old != new) { + /* These two are equivalent, so don't update in that case */ + if (old == MCAST_NO_CONFIG && new == MCAST_MIX_MRIB_FIRST) + return false; + if (old == MCAST_MIX_MRIB_FIRST && new == MCAST_NO_CONFIG) + return false; + return true; + } + return false; +} + +struct pnc_mode_update_hash_walk_data { + struct pim_instance *pim; + struct prefix_list *grp_plist; + struct prefix_list *src_plist; +}; + +static int pim_nht_hash_mode_update_helper(struct hash_bucket *bucket, void *arg) +{ + struct pim_nexthop_cache *pnc = bucket->data; + struct pnc_mode_update_hash_walk_data *pwd = arg; + struct pim_instance *pim = pwd->pim; + struct prefix p; + + pim_addr_to_prefix(&p, pnc->addr); + + /* Make sure this pnc entry matches the prefix lists */ + /* TODO: For now, pnc only has the source address, so we can only check that */ + if (pwd->src_plist && + (pim_addr_is_any(pnc->addr) || prefix_list_apply(pwd->src_plist, &p) == PREFIX_DENY)) + return HASHWALK_CONTINUE; + + /* Otherwise the address is any, or matches the prefix list, or no prefix list to match, so do the updates */ + /* TODO for RP, there are groups....but I don't think we'd want to use those */ + if (listcount(pnc->rp_list)) + pim_update_rp_nh(pim, pnc); + + /* TODO for upstream, there is an S,G key...can/should we use that group?? */ + if (pnc->upstream_hash->count) + pim_update_upstream_nh(pim, pnc); + + if (pnc->candrp_count) + pim_crp_nht_update(pim, pnc); + + return HASHWALK_CONTINUE; +} + +static void pim_rpf_mode_changed_update(struct pim_instance *pim, const char *group_plist, + const char *source_plist) +{ + struct pnc_mode_update_hash_walk_data pwd; + + /* Update the refresh time to force new lookups if needed */ + pim_rpf_set_refresh_time(pim); + + /* Force update the registered RP and upstreams for all cache entries */ + pwd.pim = pim; + pwd.grp_plist = prefix_list_lookup(PIM_AFI, group_plist); + pwd.src_plist = prefix_list_lookup(PIM_AFI, source_plist); + + hash_walk(pim->nht_hash, pim_nht_hash_mode_update_helper, &pwd); +} + /** * pim_sendmsg_zebra_rnh -- Format and send a nexthop register/Unregister * command to Zebra. @@ -106,9 +276,10 @@ static struct pim_nexthop_cache *pim_nexthop_cache_add(struct pim_instance *pim, return pnc; } -static bool pim_nht_pnc_has_answer(struct pim_instance *pim, struct pim_nexthop_cache *pnc) +static bool pim_nht_pnc_has_answer(struct pim_instance *pim, struct pim_nexthop_cache *pnc, + pim_addr group) { - switch (pim->rpf_mode) { + switch (pim_get_lookup_mode(pim, group, pnc->addr)) { case MCAST_MRIB_ONLY: return CHECK_FLAG(pnc->mrib.flags, PIM_NEXTHOP_ANSWER_RECEIVED); @@ -133,25 +304,28 @@ static bool pim_nht_pnc_has_answer(struct pim_instance *pim, struct pim_nexthop_ } static struct pim_nexthop_cache_rib *pim_pnc_get_rib(struct pim_instance *pim, - struct pim_nexthop_cache *pnc) + struct pim_nexthop_cache *pnc, pim_addr group) { struct pim_nexthop_cache_rib *pnc_rib = NULL; + enum pim_rpf_lookup_mode mode; - if (pim->rpf_mode == MCAST_MRIB_ONLY) + mode = pim_get_lookup_mode(pim, group, pnc->addr); + + if (mode == MCAST_MRIB_ONLY) pnc_rib = &pnc->mrib; - else if (pim->rpf_mode == MCAST_URIB_ONLY) + else if (mode == MCAST_URIB_ONLY) pnc_rib = &pnc->urib; - else if (pim->rpf_mode == MCAST_MIX_MRIB_FIRST || pim->rpf_mode == MCAST_NO_CONFIG) { + else if (mode == MCAST_MIX_MRIB_FIRST || mode == MCAST_NO_CONFIG) { if (pnc->mrib.nexthop_num > 0) pnc_rib = &pnc->mrib; else pnc_rib = &pnc->urib; - } else if (pim->rpf_mode == MCAST_MIX_DISTANCE) { + } else if (mode == MCAST_MIX_DISTANCE) { if (pnc->mrib.distance <= pnc->urib.distance) pnc_rib = &pnc->mrib; else pnc_rib = &pnc->urib; - } else if (pim->rpf_mode == MCAST_MIX_PFXLEN) { + } else if (mode == MCAST_MIX_PFXLEN) { if (pnc->mrib.prefix_len >= pnc->urib.prefix_len) pnc_rib = &pnc->mrib; else @@ -161,9 +335,151 @@ static struct pim_nexthop_cache_rib *pim_pnc_get_rib(struct pim_instance *pim, return pnc_rib; } -bool pim_nht_pnc_is_valid(struct pim_instance *pim, struct pim_nexthop_cache *pnc) +void pim_nht_change_rpf_mode(struct pim_instance *pim, const char *group_plist, + const char *source_plist, enum pim_rpf_lookup_mode mode) +{ + struct pim_lookup_mode *m; + bool found = false; + bool update = false; + const char *glist = NULL; + const char *slist = NULL; + + /* Prefix lists may be passed in as empty string, leave them NULL instead */ + if (group_plist && strlen(group_plist)) + glist = group_plist; + if (source_plist && strlen(source_plist)) + slist = source_plist; + + frr_each_safe (pim_lookup_mode, &(pim->rpf_mode), m) { + if ((m->grp_plist && glist && strmatch(m->grp_plist, glist)) && + (m->src_plist && slist && strmatch(m->src_plist, slist))) { + /* Group and source plists are both set and matched */ + found = true; + if (mode == MCAST_NO_CONFIG) { + /* MCAST_NO_CONFIG means we should remove this lookup mode + * We don't know what other modes might match, or if only the global, so we need to + * update all lookups + */ + pim_lookup_mode_del(&pim->rpf_mode, m); + pim_lookup_mode_free(m); + glist = NULL; + slist = NULL; + update = true; + } else { + /* Just changing mode */ + update = pim_rpf_mode_changed(m->mode, mode); + m->mode = mode; /* Always make sure the mode is set, even if not updating */ + } + + if (update) + pim_rpf_mode_changed_update(pim, glist, slist); + break; + } + + if ((m->grp_plist && glist && strmatch(m->grp_plist, glist)) && + (!m->src_plist && !slist)) { + /* Only group list set and matched */ + found = true; + if (mode == MCAST_NO_CONFIG) { + /* MCAST_NO_CONFIG means we should remove this lookup mode + * We don't know what other modes might match, or if only the global, so we need to + * update all lookups + */ + pim_lookup_mode_del(&pim->rpf_mode, m); + pim_lookup_mode_free(m); + glist = NULL; + slist = NULL; + update = true; + } else { + /* Just changing mode */ + update = pim_rpf_mode_changed(m->mode, mode); + m->mode = mode; /* Always make sure the mode is set, even if not updating */ + } + + if (update) + pim_rpf_mode_changed_update(pim, glist, slist); + break; + } + + if ((!m->grp_plist && !glist) && + (m->src_plist && slist && strmatch(m->src_plist, slist))) { + /* Only source list set and matched */ + found = true; + if (mode == MCAST_NO_CONFIG) { + /* MCAST_NO_CONFIG means we should remove this lookup mode + * We don't know what other modes might match, or if only the global, so we need to + * update all lookups + */ + pim_lookup_mode_del(&pim->rpf_mode, m); + pim_lookup_mode_free(m); + glist = NULL; + slist = NULL; + update = true; + } else { + /* Just changing mode */ + update = pim_rpf_mode_changed(m->mode, mode); + m->mode = mode; /* Always make sure the mode is set, even if not updating */ + } + + if (update) + pim_rpf_mode_changed_update(pim, glist, slist); + break; + } + + if (!m->grp_plist && !glist && !m->src_plist && !slist) { + /* No prefix lists set, so this is the global mode */ + /* We never delete this mode, even when set back to MCAST_NO_CONFIG */ + update = pim_rpf_mode_changed(m->mode, mode); + m->mode = mode; /* Always make sure the mode is set, even if not updating */ + if (update) + pim_rpf_mode_changed_update(pim, glist, slist); + found = true; + break; + } + } + + if (!found) { + /* Adding a new lookup mode with unique prefix lists, add it */ + m = XCALLOC(MTYPE_PIM_LOOKUP_MODE, sizeof(struct pim_lookup_mode)); + m->grp_plist = XSTRDUP(MTYPE_PIM_LOOKUP_MODE_STR, glist); + m->src_plist = XSTRDUP(MTYPE_PIM_LOOKUP_MODE_STR, slist); + m->mode = mode; + pim_lookup_mode_add(&(pim->rpf_mode), m); + pim_rpf_mode_changed_update(pim, glist, slist); + } +} + +int pim_lookup_mode_write(struct pim_instance *pim, struct vty *vty) { - switch (pim->rpf_mode) { + int writes = 0; + struct pim_lookup_mode *m; + + frr_each_safe (pim_lookup_mode, &(pim->rpf_mode), m) { + if (m->mode == MCAST_NO_CONFIG) + continue; + + ++writes; + vty_out(vty, " rpf-lookup-mode %s", + m->mode == MCAST_URIB_ONLY ? "urib-only" + : m->mode == MCAST_MRIB_ONLY ? "mrib-only" + : m->mode == MCAST_MIX_MRIB_FIRST ? "mrib-then-urib" + : m->mode == MCAST_MIX_DISTANCE ? "lower-distance" + : "longer-prefix"); + + if (m->grp_plist) + vty_out(vty, " group-list %s", m->grp_plist); + + if (m->src_plist) + vty_out(vty, " source-list %s", m->src_plist); + + vty_out(vty, "\n"); + } + return writes; +} + +bool pim_nht_pnc_is_valid(struct pim_instance *pim, struct pim_nexthop_cache *pnc, pim_addr group) +{ + switch (pim_get_lookup_mode(pim, group, pnc->addr)) { case MCAST_MRIB_ONLY: return CHECK_FLAG(pnc->mrib.flags, PIM_NEXTHOP_VALID); @@ -275,6 +591,7 @@ bool pim_nht_find_or_track(struct pim_instance *pim, pim_addr addr, struct pim_u { struct pim_nexthop_cache *pnc; struct listnode *ch_node = NULL; + pim_addr group = PIMADDR_ANY; /* This will find the entry and add it to tracking if not found */ pnc = pim_nht_get(pim, addr); @@ -289,10 +606,12 @@ bool pim_nht_find_or_track(struct pim_instance *pim, pim_addr addr, struct pim_u } /* Store the upstream if provided and not currently in the list */ - if (up != NULL) + if (up != NULL) { (void)hash_get(pnc->upstream_hash, up, hash_alloc_intern); + group = up->sg.grp; + } - if (pim_nht_pnc_is_valid(pim, pnc)) { + if (pim_nht_pnc_is_valid(pim, pnc, group)) { if (out_pnc) memcpy(out_pnc, pnc, sizeof(struct pim_nexthop_cache)); return true; @@ -315,7 +634,7 @@ bool pim_nht_candrp_add(struct pim_instance *pim, pim_addr addr) pnc = pim_nht_get(pim, addr); pnc->candrp_count++; - return pim_nht_pnc_is_valid(pim, pnc); + return pim_nht_pnc_is_valid(pim, pnc, PIMADDR_ANY); } static void pim_nht_drop_maybe(struct pim_instance *pim, struct pim_nexthop_cache *pnc) @@ -448,7 +767,7 @@ bool pim_nht_bsr_rpf_check(struct pim_instance *pim, pim_addr bsr_addr, lookup.addr = bsr_addr; pnc = hash_lookup(pim->nht_hash, &lookup); - if (!pnc || !pim_nht_pnc_has_answer(pim, pnc)) { + if (!pnc || !pim_nht_pnc_has_answer(pim, pnc, PIMADDR_ANY)) { /* BSM from a new freshly registered BSR - do a synchronous * zebra query since otherwise we'd drop the first packet, * leading to additional delay in picking up BSM data @@ -465,9 +784,8 @@ bool pim_nht_bsr_rpf_check(struct pim_instance *pim, pim_addr bsr_addr, int num_ifindex; memset(nexthop_tab, 0, sizeof(nexthop_tab)); - num_ifindex = zclient_lookup_nexthop( - pim, nexthop_tab, router->multipath, bsr_addr, - PIM_NEXTHOP_LOOKUP_MAX); + num_ifindex = zclient_lookup_nexthop(pim, nexthop_tab, router->multipath, bsr_addr, + PIMADDR_ANY, PIM_NEXTHOP_LOOKUP_MAX); if (num_ifindex <= 0) return false; @@ -507,7 +825,7 @@ bool pim_nht_bsr_rpf_check(struct pim_instance *pim, pim_addr bsr_addr, return false; } - if (pim_nht_pnc_is_valid(pim, pnc)) { + if (pim_nht_pnc_is_valid(pim, pnc, PIMADDR_ANY)) { /* if we accept BSMs from more than one ECMP nexthop, this will cause * BSM message "multiplication" for each ECMP hop. i.e. if you have * 4-way ECMP and 4 hops you end up with 256 copies of each BSM @@ -515,7 +833,7 @@ bool pim_nht_bsr_rpf_check(struct pim_instance *pim, pim_addr bsr_addr, * * so... only accept the first (IPv4) valid nexthop as source. */ - struct pim_nexthop_cache_rib *rib = pim_pnc_get_rib(pim, pnc); + struct pim_nexthop_cache_rib *rib = pim_pnc_get_rib(pim, pnc, PIMADDR_ANY); for (nh = rib->nexthop; nh; nh = nh->next) { pim_addr nhaddr; @@ -754,14 +1072,17 @@ static bool pim_ecmp_nexthop_search(struct pim_instance *pim, struct pim_nexthop pim_addr nh_addr; pim_addr grp_addr; struct pim_nexthop_cache_rib *rib; + pim_addr group; + + group = pim_addr_from_prefix(grp); /* Early return if required parameters aren't provided */ - if (!pim || !pnc || !pim_nht_pnc_is_valid(pim, pnc) || !nexthop || !grp) + if (!pim || !pnc || !pim_nht_pnc_is_valid(pim, pnc, group) || !nexthop || !grp) return false; nh_addr = nexthop->mrib_nexthop_addr; grp_addr = pim_addr_from_prefix(grp); - rib = pim_pnc_get_rib(pim, pnc); + rib = pim_pnc_get_rib(pim, pnc, group); /* Current Nexthop is VALID, check to stay on the current path. */ if (nexthop->interface && nexthop->interface->info && @@ -934,6 +1255,9 @@ bool pim_nht_lookup_ecmp(struct pim_instance *pim, struct pim_nexthop *nexthop, uint32_t hash_val = 0; uint32_t mod_val = 0; uint32_t num_nbrs = 0; + pim_addr group; + + group = pim_addr_from_prefix(grp); if (PIM_DEBUG_PIM_NHT_DETAIL) zlog_debug("%s: Looking up: %pPA(%s), last lookup time: %lld", __func__, &src, @@ -941,12 +1265,12 @@ bool pim_nht_lookup_ecmp(struct pim_instance *pim, struct pim_nexthop *nexthop, pnc = pim_nexthop_cache_find(pim, src); if (pnc) { - if (pim_nht_pnc_has_answer(pim, pnc)) + if (pim_nht_pnc_has_answer(pim, pnc, group)) return pim_ecmp_nexthop_search(pim, pnc, nexthop, src, grp, neighbor_needed); } memset(nexthop_tab, 0, sizeof(struct pim_zlookup_nexthop) * router->multipath); - num_ifindex = zclient_lookup_nexthop(pim, nexthop_tab, router->multipath, src, + num_ifindex = zclient_lookup_nexthop(pim, nexthop_tab, router->multipath, src, group, PIM_NEXTHOP_LOOKUP_MAX); if (num_ifindex < 1) { if (PIM_DEBUG_PIM_NHT) @@ -1051,7 +1375,7 @@ bool pim_nht_lookup_ecmp(struct pim_instance *pim, struct pim_nexthop *nexthop, } bool pim_nht_lookup(struct pim_instance *pim, struct pim_nexthop *nexthop, pim_addr addr, - int neighbor_needed) + pim_addr group, bool neighbor_needed) { struct pim_zlookup_nexthop nexthop_tab[router->multipath]; struct pim_neighbor *nbr = NULL; @@ -1087,7 +1411,7 @@ bool pim_nht_lookup(struct pim_instance *pim, struct pim_nexthop *nexthop, pim_a &addr, nexthop->last_lookup_time, pim->last_route_change_time); memset(nexthop_tab, 0, sizeof(struct pim_zlookup_nexthop) * router->multipath); - num_ifindex = zclient_lookup_nexthop(pim, nexthop_tab, router->multipath, addr, + num_ifindex = zclient_lookup_nexthop(pim, nexthop_tab, router->multipath, addr, group, PIM_NEXTHOP_LOOKUP_MAX); if (num_ifindex < 1) { if (PIM_DEBUG_PIM_NHT) @@ -1349,36 +1673,6 @@ void pim_nexthop_update(struct vrf *vrf, struct prefix *match, struct zapi_route pim_crp_nht_update(pim, pnc); } -static int pim_nht_hash_mode_update_helper(struct hash_bucket *bucket, void *arg) -{ - struct pim_nexthop_cache *pnc = bucket->data; - struct pnc_hash_walk_data *pwd = arg; - struct pim_instance *pim = pwd->pim; - - if (listcount(pnc->rp_list)) - pim_update_rp_nh(pim, pnc); - - if (pnc->upstream_hash->count) - pim_update_upstream_nh(pim, pnc); - - if (pnc->candrp_count) - pim_crp_nht_update(pim, pnc); - - return HASHWALK_CONTINUE; -} - -void pim_nht_mode_changed(struct pim_instance *pim) -{ - struct pnc_hash_walk_data pwd; - - /* Update the refresh time to force new lookups if needed */ - pim_rpf_set_refresh_time(pim); - - /* Force update the registered RP and upstreams for all cache entries */ - pwd.pim = pim; - hash_walk(pim->nht_hash, pim_nht_hash_mode_update_helper, &pwd); -} - /* Cleanup pim->nht_hash each node data */ static void pim_nht_hash_clean(void *data) { @@ -1418,11 +1712,19 @@ static bool pim_nht_equal(const void *arg1, const void *arg2) void pim_nht_init(struct pim_instance *pim) { char hash_name[64]; + struct pim_lookup_mode *global_mode; snprintf(hash_name, sizeof(hash_name), "PIM %s NHT Hash", pim->vrf->name); pim->nht_hash = hash_create_size(256, pim_nht_hash_key, pim_nht_equal, hash_name); - pim->rpf_mode = MCAST_NO_CONFIG; + pim_lookup_mode_init(&(pim->rpf_mode)); + + /* Add the default global mode */ + global_mode = XCALLOC(MTYPE_PIM_LOOKUP_MODE, sizeof(*global_mode)); + global_mode->grp_plist = NULL; + global_mode->src_plist = NULL; + global_mode->mode = MCAST_NO_CONFIG; + pim_lookup_mode_add(&(pim->rpf_mode), global_mode); if (PIM_DEBUG_ZEBRA) zlog_debug("%s: NHT hash init: %s ", __func__, hash_name); @@ -1432,4 +1734,7 @@ void pim_nht_terminate(struct pim_instance *pim) { /* Traverse and cleanup nht_hash */ hash_clean_and_free(&pim->nht_hash, (void *)pim_nht_hash_clean); + + pim_lookup_mode_list_free(&(pim->rpf_mode)); + pim_lookup_mode_fini(&(pim->rpf_mode)); } |