summaryrefslogtreecommitdiffstats
path: root/pimd/pim_nht.c
diff options
context:
space:
mode:
Diffstat (limited to 'pimd/pim_nht.c')
-rw-r--r--pimd/pim_nht.c393
1 files changed, 195 insertions, 198 deletions
diff --git a/pimd/pim_nht.c b/pimd/pim_nht.c
index 50cfc297d..cd6f4c45f 100644
--- a/pimd/pim_nht.c
+++ b/pimd/pim_nht.c
@@ -110,22 +110,11 @@ static struct pim_nexthop_cache *pim_nexthop_cache_add(struct pim_instance *pim,
return pnc;
}
-/*
- * pim_find_or_track_nexthop
- *
- * This API is used to Register an address with Zebra
- *
- * 1 -> Success
- * 0 -> Failure
- */
-int pim_find_or_track_nexthop(struct pim_instance *pim, struct prefix *addr,
- struct pim_upstream *up, struct rp_info *rp,
- bool bsr_track_needed,
- struct pim_nexthop_cache *out_pnc)
+static struct pim_nexthop_cache *pim_nht_get(struct pim_instance *pim,
+ struct prefix *addr)
{
struct pim_nexthop_cache *pnc = NULL;
struct pim_rpf rpf;
- struct listnode *ch_node = NULL;
struct zclient *zclient = NULL;
zclient = pim_zebra_zclient_get();
@@ -145,6 +134,23 @@ int pim_find_or_track_nexthop(struct pim_instance *pim, struct prefix *addr,
__func__, addr, pim->vrf->name);
}
+ return pnc;
+}
+
+/* TBD: this does several distinct things and should probably be split up.
+ * (checking state vs. returning pnc vs. adding upstream vs. adding rp)
+ */
+int pim_find_or_track_nexthop(struct pim_instance *pim, struct prefix *addr,
+ struct pim_upstream *up, struct rp_info *rp,
+ struct pim_nexthop_cache *out_pnc)
+{
+ struct pim_nexthop_cache *pnc;
+ struct listnode *ch_node = NULL;
+
+ pnc = pim_nht_get(pim, addr);
+
+ assertf(up || rp, "addr=%pFX", addr);
+
if (rp != NULL) {
ch_node = listnode_lookup(pnc->rp_list, rp);
if (ch_node == NULL)
@@ -154,9 +160,6 @@ int pim_find_or_track_nexthop(struct pim_instance *pim, struct prefix *addr,
if (up != NULL)
hash_get(pnc->upstream_hash, up, hash_alloc_intern);
- if (bsr_track_needed)
- pnc->bsr_tracking = true;
-
if (CHECK_FLAG(pnc->flags, PIM_NEXTHOP_VALID)) {
if (out_pnc)
memcpy(out_pnc, pnc, sizeof(struct pim_nexthop_cache));
@@ -166,232 +169,226 @@ int pim_find_or_track_nexthop(struct pim_instance *pim, struct prefix *addr,
return 0;
}
+void pim_nht_bsr_add(struct pim_instance *pim, struct in_addr addr)
+{
+ struct pim_nexthop_cache *pnc;
+ struct prefix pfx;
+
+ pfx.family = AF_INET;
+ pfx.prefixlen = IPV4_MAX_BITLEN;
+ pfx.u.prefix4 = addr;
+
+ pnc = pim_nht_get(pim, &pfx);
+
+ pnc->bsr_count++;
+}
+
+static void pim_nht_drop_maybe(struct pim_instance *pim,
+ struct pim_nexthop_cache *pnc)
+{
+ if (PIM_DEBUG_PIM_NHT)
+ zlog_debug(
+ "%s: NHT %pFX(%s) rp_list count:%d upstream count:%ld BSR count:%u",
+ __func__, &pnc->rpf.rpf_addr, pim->vrf->name,
+ pnc->rp_list->count, pnc->upstream_hash->count,
+ pnc->bsr_count);
+
+ if (pnc->rp_list->count == 0 && pnc->upstream_hash->count == 0
+ && pnc->bsr_count == 0) {
+ struct zclient *zclient = pim_zebra_zclient_get();
+
+ pim_sendmsg_zebra_rnh(pim, zclient, pnc,
+ ZEBRA_NEXTHOP_UNREGISTER);
+
+ list_delete(&pnc->rp_list);
+ hash_free(pnc->upstream_hash);
+
+ hash_release(pim->rpf_hash, pnc);
+ if (pnc->nexthop)
+ nexthops_free(pnc->nexthop);
+ XFREE(MTYPE_PIM_NEXTHOP_CACHE, pnc);
+ }
+}
+
void pim_delete_tracked_nexthop(struct pim_instance *pim, struct prefix *addr,
- struct pim_upstream *up, struct rp_info *rp,
- bool del_bsr_tracking)
+ struct pim_upstream *up, struct rp_info *rp)
{
struct pim_nexthop_cache *pnc = NULL;
struct pim_nexthop_cache lookup;
- struct zclient *zclient = NULL;
struct pim_upstream *upstream = NULL;
- zclient = pim_zebra_zclient_get();
-
/* Remove from RPF hash if it is the last entry */
lookup.rpf.rpf_addr = *addr;
pnc = hash_lookup(pim->rpf_hash, &lookup);
- if (pnc) {
- if (rp) {
- /* Release the (*, G)upstream from pnc->upstream_hash,
- * whose Group belongs to the RP getting deleted
- */
- frr_each (rb_pim_upstream, &pim->upstream_head,
- upstream) {
- struct prefix grp;
- struct rp_info *trp_info;
-
- if (upstream->sg.src.s_addr != INADDR_ANY)
- continue;
-
- grp.family = AF_INET;
- grp.prefixlen = IPV4_MAX_BITLEN;
- grp.u.prefix4 = upstream->sg.grp;
-
- trp_info = pim_rp_find_match_group(pim, &grp);
- if (trp_info == rp)
- hash_release(pnc->upstream_hash,
- upstream);
- }
- listnode_delete(pnc->rp_list, rp);
- }
+ if (!pnc) {
+ zlog_warn("attempting to delete nonexistent NHT entry %pFX",
+ addr);
+ return;
+ }
- if (up)
- hash_release(pnc->upstream_hash, up);
+ if (rp) {
+ /* Release the (*, G)upstream from pnc->upstream_hash,
+ * whose Group belongs to the RP getting deleted
+ */
+ frr_each (rb_pim_upstream, &pim->upstream_head, upstream) {
+ struct prefix grp;
+ struct rp_info *trp_info;
- if (del_bsr_tracking)
- pnc->bsr_tracking = false;
+ if (upstream->sg.src.s_addr != INADDR_ANY)
+ continue;
- if (PIM_DEBUG_PIM_NHT)
- zlog_debug(
- "%s: NHT %pFX(%s) rp_list count:%d upstream count:%ld",
- __func__, addr, pim->vrf->name,
- pnc->rp_list->count, pnc->upstream_hash->count);
-
- if (pnc->rp_list->count == 0
- && pnc->upstream_hash->count == 0
- && pnc->bsr_tracking == false) {
- pim_sendmsg_zebra_rnh(pim, zclient, pnc,
- ZEBRA_NEXTHOP_UNREGISTER);
-
- list_delete(&pnc->rp_list);
- hash_free(pnc->upstream_hash);
-
- hash_release(pim->rpf_hash, pnc);
- if (pnc->nexthop)
- nexthops_free(pnc->nexthop);
- XFREE(MTYPE_PIM_NEXTHOP_CACHE, pnc);
+ grp.family = AF_INET;
+ grp.prefixlen = IPV4_MAX_BITLEN;
+ grp.u.prefix4 = upstream->sg.grp;
+
+ trp_info = pim_rp_find_match_group(pim, &grp);
+ if (trp_info == rp)
+ hash_release(pnc->upstream_hash, upstream);
}
+ listnode_delete(pnc->rp_list, rp);
}
+
+ if (up)
+ hash_release(pnc->upstream_hash, up);
+
+ pim_nht_drop_maybe(pim, pnc);
}
-/* Given a source address and a neighbor address, check if the neighbor is one
- * of the next hop to reach the source. search from zebra route database
- */
-bool pim_nexthop_match(struct pim_instance *pim, struct in_addr addr,
- struct in_addr ip_src)
+void pim_nht_bsr_del(struct pim_instance *pim, struct in_addr addr)
{
- struct pim_zlookup_nexthop nexthop_tab[MULTIPATH_NUM];
- int i = 0;
- ifindex_t first_ifindex = 0;
- struct interface *ifp = NULL;
- struct pim_neighbor *nbr = NULL;
- int num_ifindex;
+ struct pim_nexthop_cache *pnc = NULL;
+ struct pim_nexthop_cache lookup;
- if (addr.s_addr == INADDR_NONE)
- return false;
+ lookup.rpf.rpf_addr.family = AF_INET;
+ lookup.rpf.rpf_addr.prefixlen = IPV4_MAX_BITLEN;
+ lookup.rpf.rpf_addr.u.prefix4 = addr;
- memset(nexthop_tab, 0,
- sizeof(struct pim_zlookup_nexthop) * MULTIPATH_NUM);
- num_ifindex = zclient_lookup_nexthop(pim, nexthop_tab, MULTIPATH_NUM,
- addr, PIM_NEXTHOP_LOOKUP_MAX);
- if (num_ifindex < 1) {
- char addr_str[INET_ADDRSTRLEN];
+ pnc = hash_lookup(pim->rpf_hash, &lookup);
- pim_inet4_dump("<addr?>", addr, addr_str, sizeof(addr_str));
- zlog_warn(
- "%s %s: could not find nexthop ifindex for address %s",
- __FILE__, __func__, addr_str);
- return false;
+ if (!pnc) {
+ zlog_warn("attempting to delete nonexistent NHT BSR entry %pI4",
+ &addr);
+ return;
}
- while (i < num_ifindex) {
- first_ifindex = nexthop_tab[i].ifindex;
+ assertf(pnc->bsr_count > 0, "addr=%pI4", &addr);
+ pnc->bsr_count--;
- ifp = if_lookup_by_index(first_ifindex, pim->vrf->vrf_id);
- if (!ifp) {
- if (PIM_DEBUG_ZEBRA) {
- char addr_str[INET_ADDRSTRLEN];
+ pim_nht_drop_maybe(pim, pnc);
+}
- pim_inet4_dump("<addr?>", addr, addr_str,
- sizeof(addr_str));
- zlog_debug(
- "%s %s: could not find interface for ifindex %d (address %s)",
- __FILE__, __func__, first_ifindex,
- addr_str);
- }
- i++;
- continue;
- }
+bool pim_nht_bsr_rpf_check(struct pim_instance *pim, struct in_addr bsr_addr,
+ struct interface *src_ifp, struct in_addr src_ip)
+{
+ struct pim_nexthop_cache *pnc = NULL;
+ struct pim_nexthop_cache lookup;
+ struct pim_neighbor *nbr = NULL;
+ struct nexthop *nh;
+ struct interface *ifp;
- if (!ifp->info) {
- if (PIM_DEBUG_ZEBRA) {
- char addr_str[INET_ADDRSTRLEN];
+ lookup.rpf.rpf_addr.family = AF_INET;
+ lookup.rpf.rpf_addr.prefixlen = IPV4_MAX_BITLEN;
+ lookup.rpf.rpf_addr.u.prefix4 = bsr_addr;
- pim_inet4_dump("<addr?>", addr, addr_str,
- sizeof(addr_str));
- zlog_debug(
- "%s: multicast not enabled on input interface %s (ifindex=%d, RPF for source %s)",
- __func__, ifp->name, first_ifindex,
- addr_str);
- }
- i++;
- continue;
- }
+ pnc = hash_lookup(pim->rpf_hash, &lookup);
+ if (!pnc || !CHECK_FLAG(pnc->flags, PIM_NEXTHOP_ANSWER_RECEIVED)) {
+ /* 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
+ */
- if (!pim_if_connected_to_source(ifp, addr)) {
- nbr = pim_neighbor_find(
- ifp, nexthop_tab[i].nexthop_addr.u.prefix4);
- if (PIM_DEBUG_PIM_TRACE_DETAIL)
- zlog_debug("ifp name: %s, pim nbr: %p",
- ifp->name, nbr);
- if (!nbr && !if_is_loopback(ifp)) {
- i++;
+ /* FIXME: this should really be moved into a generic NHT
+ * function that does "add and get immediate result" or maybe
+ * "check cache or get immediate result." But until that can
+ * be worked in, here's a copy of the code below :(
+ */
+ struct pim_zlookup_nexthop nexthop_tab[MULTIPATH_NUM];
+ ifindex_t i;
+ struct interface *ifp = NULL;
+ int num_ifindex;
+
+ memset(nexthop_tab, 0, sizeof(nexthop_tab));
+ num_ifindex = zclient_lookup_nexthop(pim, nexthop_tab,
+ MULTIPATH_NUM, bsr_addr,
+ PIM_NEXTHOP_LOOKUP_MAX);
+
+ if (num_ifindex <= 0)
+ return false;
+
+ for (i = 0; i < num_ifindex; i++) {
+ struct pim_zlookup_nexthop *znh = &nexthop_tab[i];
+
+ /* pim_zlookup_nexthop has no ->type */
+
+ /* 1:1 match code below with znh instead of nh */
+ ifp = if_lookup_by_index(znh->ifindex,
+ pim->vrf->vrf_id);
+
+ if (!ifp || !ifp->info)
continue;
- }
- }
- if (nexthop_tab[i].nexthop_addr.u.prefix4.s_addr
- == ip_src.s_addr)
- return true;
+ if (if_is_loopback(ifp) && if_is_loopback(src_ifp))
+ return true;
- i++;
+ nbr = pim_neighbor_find(ifp,
+ znh->nexthop_addr.u.prefix4);
+ if (!nbr)
+ continue;
+
+ return znh->ifindex == src_ifp->ifindex
+ && znh->nexthop_addr.u.prefix4.s_addr
+ == src_ip.s_addr;
+ }
+ return false;
}
- return false;
-}
+ if (!CHECK_FLAG(pnc->flags, PIM_NEXTHOP_VALID))
+ return false;
-/* Given a source address and a neighbor address, check if the neighbor is one
- * of the next hop to reach the source. search from pim next hop cache
- */
-bool pim_nexthop_match_nht_cache(struct pim_instance *pim, struct in_addr addr,
- struct in_addr ip_src)
-{
- struct pim_rpf rpf;
- ifindex_t first_ifindex;
- struct interface *ifp = NULL;
- uint8_t nh_iter = 0;
- struct pim_neighbor *nbr = NULL;
- struct nexthop *nh_node = NULL;
- struct pim_nexthop_cache *pnc = NULL;
+ /* 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
+ * message.
+ *
+ * so... only accept the first (IPv4) valid nexthop as source.
+ */
- memset(&rpf, 0, sizeof(struct pim_rpf));
- rpf.rpf_addr.family = AF_INET;
- rpf.rpf_addr.prefixlen = IPV4_MAX_BITLEN;
- rpf.rpf_addr.u.prefix4 = addr;
+ for (nh = pnc->nexthop; nh; nh = nh->next) {
+ struct in_addr nhaddr;
- pnc = pim_nexthop_cache_find(pim, &rpf);
- if (!pnc || !pnc->nexthop_num)
- return false;
+ switch (nh->type) {
+ case NEXTHOP_TYPE_IPV4:
+ if (nh->ifindex == IFINDEX_INTERNAL)
+ continue;
- for (nh_node = pnc->nexthop; nh_node; nh_node = nh_node->next) {
- first_ifindex = nh_node->ifindex;
- ifp = if_lookup_by_index(first_ifindex, pim->vrf->vrf_id);
- if (!ifp) {
- if (PIM_DEBUG_PIM_NHT) {
- char addr_str[INET_ADDRSTRLEN];
+ /* fallthru */
+ case NEXTHOP_TYPE_IPV4_IFINDEX:
+ nhaddr = nh->gate.ipv4;
+ break;
- pim_inet4_dump("<addr?>", addr, addr_str,
- sizeof(addr_str));
- zlog_debug(
- "%s %s: could not find interface for ifindex %d (address %s(%s))",
- __FILE__, __func__, first_ifindex,
- addr_str, pim->vrf->name);
- }
- nh_iter++;
- continue;
- }
- if (!ifp->info) {
- if (PIM_DEBUG_PIM_NHT) {
- char addr_str[INET_ADDRSTRLEN];
+ case NEXTHOP_TYPE_IFINDEX:
+ nhaddr = bsr_addr;
+ break;
- pim_inet4_dump("<addr?>", addr, addr_str,
- sizeof(addr_str));
- zlog_debug(
- "%s: multicast not enabled on input interface %s(%s) (ifindex=%d, RPF for source %s)",
- __func__, ifp->name, pim->vrf->name,
- first_ifindex, addr_str);
- }
- nh_iter++;
+ default:
continue;
}
- if (!pim_if_connected_to_source(ifp, addr)) {
- nbr = pim_neighbor_find(ifp, nh_node->gate.ipv4);
- if (!nbr && !if_is_loopback(ifp)) {
- if (PIM_DEBUG_PIM_NHT)
- zlog_debug(
- "%s: pim nbr not found on input interface %s(%s)",
- __func__, ifp->name,
- pim->vrf->name);
- nh_iter++;
- continue;
- }
- }
+ ifp = if_lookup_by_index(nh->ifindex, pim->vrf->vrf_id);
+ if (!ifp || !ifp->info)
+ continue;
- if (nh_node->gate.ipv4.s_addr == ip_src.s_addr)
+ if (if_is_loopback(ifp) && if_is_loopback(src_ifp))
return true;
- }
+ /* MRIB (IGP) may be pointing at a router where PIM is down */
+ nbr = pim_neighbor_find(ifp, nhaddr);
+ if (!nbr)
+ continue;
+
+ return nh->ifindex == src_ifp->ifindex
+ && nhaddr.s_addr == src_ip.s_addr;
+ }
return false;
}