summaryrefslogtreecommitdiff
path: root/pimd/pim_rp.c
diff options
context:
space:
mode:
authorRafael Zalamena <rzalamena@opensourcerouting.org>2024-10-02 09:22:48 -0300
committerRafael Zalamena <rzalamena@opensourcerouting.org>2024-11-13 12:05:35 -0300
commit23c7acd2327b4caeec0b4e2a1a082bfd10ede957 (patch)
tree9f77ef3731331f591d021a0c55167430e8026546 /pimd/pim_rp.c
parent5456bc5d9313b53b8059583cba7c95f55f08a26b (diff)
pim6d: support embedded-rp
Implement embedded RP support and configuration commands. Embedded RP is disabled by default and can be globally enabled with the command `embedded-rp` in the PIMv6 configuration node. It supports the following options: - Embedded RP maximum limit - Embedded RP group filtering Signed-off-by: Rafael Zalamena <rzalamena@opensourcerouting.org>
Diffstat (limited to 'pimd/pim_rp.c')
-rw-r--r--pimd/pim_rp.c255
1 files changed, 255 insertions, 0 deletions
diff --git a/pimd/pim_rp.c b/pimd/pim_rp.c
index 0c47bc1582..cf370857ff 100644
--- a/pimd/pim_rp.c
+++ b/pimd/pim_rp.c
@@ -115,10 +115,38 @@ void pim_rp_init(struct pim_instance *pim)
zlog_debug("Allocated: %p for rp_info: %p(%pFX) Lock: %d", rn,
rp_info, &rp_info->group,
route_node_get_lock_count(rn));
+
+#if PIM_IPV == 6
+ /*
+ * Embedded RP defaults
+ */
+ pim->embedded_rp.enable = false;
+ pim->embedded_rp.group_list = NULL;
+ pim->embedded_rp.maximum_rps = PIM_EMBEDDED_RP_MAXIMUM;
+
+ pim->embedded_rp.table = route_table_init();
+#endif /* PIM_IPV == 6 */
}
void pim_rp_free(struct pim_instance *pim)
{
+#if PIM_IPV == 6
+ struct route_node *rn;
+
+ pim_embedded_rp_set_group_list(pim, NULL);
+
+ for (rn = route_top(pim->embedded_rp.table); rn; rn = route_next(rn)) {
+ if (rn->info == NULL)
+ continue;
+
+ pim_embedded_rp_free(pim, rn->info);
+ rn->info = NULL;
+ }
+
+ route_table_finish(pim->embedded_rp.table);
+ pim->embedded_rp.table = NULL;
+#endif /* PIM_IPV == 6 */
+
if (pim->rp_table)
route_table_finish(pim->rp_table);
pim->rp_table = NULL;
@@ -216,6 +244,24 @@ struct rp_info *pim_rp_find_match_group(struct pim_instance *pim,
const struct prefix_list_entry *entry;
struct route_node *rn;
+#if PIM_IPV == 6
+ /*
+ * Embedded RP search. Always try to match against embedded RP first.
+ */
+ rn = route_node_match(pim->embedded_rp.table, group);
+ if (rn != NULL) {
+ rp_info = rn->info ? rn->info : NULL;
+
+ if (rp_info && PIM_DEBUG_PIM_TRACE_DETAIL) {
+ zlog_debug("Lookedup(%pFX): rn %p found:%pFX", group, rn, &rp_info->group);
+ }
+
+ route_unlock_node(rn);
+ if (rp_info)
+ return rp_info;
+ }
+#endif /* PIM_IPV == 6 */
+
bp = NULL;
for (ALL_LIST_ELEMENTS_RO(pim->rp_list, node, rp_info)) {
if (rp_info->plist) {
@@ -1203,6 +1249,10 @@ void pim_rp_show_information(struct pim_instance *pim, struct prefix *range,
strlcpy(source, "BSR", sizeof(source));
else if (rp_info->rp_src == RP_SRC_AUTORP)
strlcpy(source, "AutoRP", sizeof(source));
+#if PIM_IPV == 6
+ else if (rp_info->rp_src == RP_SRC_EMBEDDED_RP)
+ strlcpy(source, "Embedded-RP", sizeof(source));
+#endif /* PIM_IPV == 6 */
else
strlcpy(source, "None", sizeof(source));
if (json) {
@@ -1329,3 +1379,208 @@ void pim_resolve_rp_nh(struct pim_instance *pim, struct pim_neighbor *nbr)
}
}
}
+
+#if PIM_IPV == 6
+DEFINE_MTYPE_STATIC(PIMD, PIM_EMBEDDED_RP_GROUP_LIST, "PIM embedded RP group list");
+DEFINE_MTYPE_STATIC(PIMD, PIM_EMBEDDED_RP_ENTRY, "PIM embedded RP configuration");
+
+void pim_embedded_rp_enable(struct pim_instance *pim, bool enable)
+{
+ struct route_node *rn;
+
+ pim->embedded_rp.enable = enable;
+ if (enable)
+ return;
+
+ /* Remove all learned embedded RPs and reallocate data structure. */
+ for (rn = route_top(pim->embedded_rp.table); rn; rn = route_next(rn)) {
+ pim_embedded_rp_free(pim, rn->info);
+ rn->info = NULL;
+ }
+ route_table_finish(pim->embedded_rp.table);
+
+ pim->embedded_rp.table = route_table_init();
+}
+
+void pim_embedded_rp_set_group_list(struct pim_instance *pim, const char *group_list)
+{
+ if (pim->embedded_rp.group_list)
+ XFREE(MTYPE_PIM_EMBEDDED_RP_GROUP_LIST, pim->embedded_rp.group_list);
+
+ if (group_list == NULL)
+ return;
+
+ pim->embedded_rp.group_list = XSTRDUP(MTYPE_PIM_EMBEDDED_RP_GROUP_LIST, group_list);
+}
+
+void pim_embedded_rp_set_maximum_rps(struct pim_instance *pim, uint32_t maximum)
+{
+ pim->embedded_rp.maximum_rps = maximum;
+}
+
+bool pim_embedded_rp_filter_match(const struct pim_instance *pim, const pim_addr *group)
+{
+ struct prefix_list *list;
+ struct prefix group_prefix = {
+ .family = PIM_AF,
+ .prefixlen = PIM_MAX_BITLEN,
+ .u.prefix6 = *group,
+ };
+
+ list = prefix_list_lookup(PIM_AFI, pim->embedded_rp.group_list);
+ if (list == NULL)
+ return false;
+
+ if (prefix_list_apply_ext(list, NULL, &group_prefix, true) == PREFIX_DENY) {
+ if (PIM_DEBUG_PIM_TRACE)
+ zlog_debug("filtering embedded-rp group %pPA", group);
+ return true;
+ }
+
+ return false;
+}
+
+bool pim_embedded_rp_is_embedded(const pim_addr *group)
+{
+ /*
+ * Embedded RP basic format:
+ * - First byte: 0xFF
+ * - Third nibble: 0x7 (binary 0111)
+ * - Fourth nibble: Scope
+ * - Fifth nibble: Reserved (zero)
+ * - Sixth nibble: RIID (RP interface ID)
+ * - Fourth byte: Prefix length (1..64)
+ * - Fifth byte and on: RP address prefix
+ * - Last four bytes: Multicast group ID
+ */
+ if (group->s6_addr[0] != 0xFF)
+ return false;
+ /* Embedded RP flags must all be set. */
+ if ((group->s6_addr[1] & 0xF0) != 0x70)
+ return false;
+ /* Reserved nibble */
+ if ((group->s6_addr[2] & 0xF0) != 0x00)
+ return false;
+ /* RP Interface ID must not be zero */
+ if ((group->s6_addr[2] & 0x0F) == 0x00)
+ return false;
+ /* Prefix length must be between 1 and 64. */
+ if (group->s6_addr[3] == 0 || group->s6_addr[3] > 64)
+ return false;
+
+ return true;
+}
+
+bool pim_embedded_rp_extract(const pim_addr *group, pim_addr *rp)
+{
+ struct prefix prefix;
+
+ if (!pim_embedded_rp_is_embedded(group))
+ return false;
+
+ /* Copy at most the prefix bytes length to RP prefix. */
+ prefix = (struct prefix){
+ .family = PIM_AF,
+ .prefixlen = group->s6_addr[3],
+ };
+ memcpy(&prefix.u.prefix6, &group->s6_addr[4],
+ (prefix.prefixlen % 8) == 0 ? (prefix.prefixlen / 8) : (prefix.prefixlen / 8) + 1);
+ /* Zero unused address bits. */
+ apply_mask(&prefix);
+
+ /* Return assembled RP address. */
+ *rp = prefix.u.prefix6;
+ rp->s6_addr[15] = group->s6_addr[2] & 0x0F;
+ return true;
+}
+
+void pim_embedded_rp_new(struct pim_instance *pim, const pim_addr *group, const pim_addr *rp)
+{
+ struct route_node *rnode;
+ struct rp_info *rp_info;
+ struct prefix group_prefix = {
+ .family = PIM_AF,
+ .prefixlen = PIM_MAX_BITLEN,
+ .u.prefix6 = *group,
+ };
+
+ rnode = route_node_get(pim->embedded_rp.table, &group_prefix);
+ if (rnode->info != NULL) {
+ route_unlock_node(rnode);
+ return;
+ }
+
+ if (pim->embedded_rp.rp_count >= pim->embedded_rp.maximum_rps) {
+ zlog_info("Embedded RP maximum (%u) has been reached. Disregarding new RP %pPA",
+ pim->embedded_rp.maximum_rps, rp);
+ route_unlock_node(rnode);
+ return;
+ }
+
+ pim->embedded_rp.rp_count++;
+
+ rnode->info = rp_info = XCALLOC(MTYPE_PIM_EMBEDDED_RP_ENTRY, sizeof(struct rp_info));
+ rp_info->rp.rpf_addr = *rp;
+ prefix_copy(&rp_info->group, &group_prefix);
+ rp_info->rp_src = RP_SRC_EMBEDDED_RP;
+ listnode_add_sort(pim->rp_list, rp_info);
+ if (PIM_DEBUG_TRACE)
+ zlog_debug("add embedded RP %pPA for group %pPA", rp, group);
+
+ /*
+ * PIM RP regular maintenance
+ */
+ pim_zebra_update_all_interfaces(pim);
+ pim_rp_check_interfaces(pim, rp_info);
+ if (rp_info->i_am_rp && PIM_DEBUG_PIM_NHT_RP)
+ zlog_debug("new RP %pPA for %pFX is ourselves", &rp_info->rp.rpf_addr,
+ &rp_info->group);
+
+ pim_rp_refresh_group_to_rp_mapping(pim);
+ if (PIM_DEBUG_PIM_NHT_RP)
+ zlog_debug("%s: NHT Register RP addr %pPA grp %pFX with Zebra", __func__,
+ &rp_info->rp.rpf_addr, &rp_info->group);
+
+ pim_find_or_track_nexthop(pim, rp_info->rp.rpf_addr, NULL, rp_info, NULL);
+ pim_ecmp_nexthop_lookup(pim, &rp_info->rp.source_nexthop, rp_info->rp.rpf_addr,
+ &rp_info->group, 1);
+}
+
+void pim_embedded_rp_delete(struct pim_instance *pim, const pim_addr *group)
+{
+ struct route_node *rnode;
+ struct prefix group_prefix = {
+ .family = PIM_AF,
+ .prefixlen = PIM_MAX_BITLEN,
+ .u.prefix6 = *group,
+ };
+
+ /* Avoid NULL accesses during shutdown */
+ if (pim->embedded_rp.table == NULL)
+ return;
+
+ rnode = route_node_lookup(pim->embedded_rp.table, &group_prefix);
+ if (rnode == NULL)
+ return;
+
+ pim_embedded_rp_free(pim, rnode->info);
+ rnode->info = NULL;
+
+ /* Unlock twice to remove the node */
+ route_unlock_node(rnode);
+ route_unlock_node(rnode);
+}
+
+void pim_embedded_rp_free(struct pim_instance *pim, struct rp_info *rp_info)
+{
+ if (pim->embedded_rp.rp_count > 0)
+ pim->embedded_rp.rp_count--;
+
+ if (PIM_DEBUG_TRACE)
+ zlog_debug("delete embedded RP %pPA", &rp_info->rp.rpf_addr);
+
+ pim_delete_tracked_nexthop(pim, rp_info->rp.rpf_addr, NULL, rp_info);
+ listnode_delete(pim->rp_list, rp_info);
+ XFREE(MTYPE_PIM_EMBEDDED_RP_ENTRY, rp_info);
+}
+#endif /* PIM_IPV == 6 */