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>
return ret;
}
+DEFPY_YANG(pim6_embedded_rp,
+ pim6_embedded_rp_cmd,
+ "[no] embedded-rp",
+ NO_STR
+ PIM_EMBEDDED_RP)
+{
+ char xpath[XPATH_MAXLEN];
+
+ snprintf(xpath, sizeof(xpath), FRR_PIM_EMBEDDED_RP_XPATH);
+ nb_cli_enqueue_change(vty, xpath, no ? NB_OP_DESTROY : NB_OP_MODIFY, "true");
+
+ return nb_cli_apply_changes(vty, NULL);
+}
+
+DEFPY_YANG(pim6_embedded_rp_group_list,
+ pim6_embedded_rp_group_list_cmd,
+ "[no] embedded-rp group-list ![WORD$prefix_list]",
+ NO_STR
+ PIM_EMBEDDED_RP
+ "Configure embedded RP permitted groups\n"
+ "Embedded RP permitted groups\n")
+{
+ char xpath[XPATH_MAXLEN];
+
+ snprintf(xpath, sizeof(xpath), FRR_PIM_EMBEDDED_RP_GROUP_LIST_XPATH);
+ nb_cli_enqueue_change(vty, xpath, no ? NB_OP_DESTROY : NB_OP_MODIFY, prefix_list);
+
+ return nb_cli_apply_changes(vty, NULL);
+}
+
+DEFPY_YANG(pim6_embedded_rp_limit,
+ pim6_embedded_rp_limit_cmd,
+ "[no] embedded-rp limit ![(1-4294967295)$limit]",
+ NO_STR
+ PIM_EMBEDDED_RP
+ "Limit the amount of embedded RPs to learn\n"
+ "Maximum amount of embedded RPs to learn\n")
+{
+ char xpath[XPATH_MAXLEN];
+
+ snprintf(xpath, sizeof(xpath), FRR_PIM_EMBEDDED_RP_MAXIMUM_RPS_XPATH);
+ nb_cli_enqueue_change(vty, xpath, no ? NB_OP_DESTROY : NB_OP_MODIFY, limit_str);
+
+ return nb_cli_apply_changes(vty, NULL);
+}
+
DEFPY (ipv6_pim_bsm,
ipv6_pim_bsm_cmd,
"ipv6 pim bsm",
install_element(PIM6_NODE, &no_pim6_rp_cmd);
install_element(PIM6_NODE, &pim6_rp_prefix_list_cmd);
install_element(PIM6_NODE, &no_pim6_rp_prefix_list_cmd);
+
+ install_element(PIM6_NODE, &pim6_embedded_rp_cmd);
+ install_element(PIM6_NODE, &pim6_embedded_rp_group_list_cmd);
+ install_element(PIM6_NODE, &pim6_embedded_rp_limit_cmd);
+
install_element(PIM6_NODE, &pim6_ssmpingd_cmd);
install_element(PIM6_NODE, &no_pim6_ssmpingd_cmd);
install_element(PIM6_NODE, &pim6_bsr_candidate_rp_cmd);
#define DEBUG_PIMV6_ZEBRA_STR "ZEBRA protocol activity\n"
#define DEBUG_MROUTE6_STR "PIMv6 interaction with kernel MFC cache\n"
#define DEBUG_PIMV6_BSM_STR "BSR message processing activity\n"
+#define PIM_EMBEDDED_RP "Embedded Rendezvous Point\n"
void pim_cmd_init(void);
static void gm_sg_free(struct gm_sg *sg)
{
+ if (pim_embedded_rp_is_embedded(&sg->sgaddr.grp))
+ pim_embedded_rp_delete(sg->iface->pim, &sg->sgaddr.grp);
+
/* t_sg_expiry is handled before this is reached */
EVENT_OFF(sg->t_sg_query);
gm_packet_sg_subs_fini(sg->subs_negative);
new_join = gm_sg_state_want_join(desired);
if (new_join && !sg->tib_joined) {
+ pim_addr embedded_rp;
+
+ if (sg->iface->pim->embedded_rp.enable &&
+ pim_embedded_rp_extract(&sg->sgaddr.grp, &embedded_rp) &&
+ !pim_embedded_rp_filter_match(sg->iface->pim, &sg->sgaddr.grp))
+ pim_embedded_rp_new(sg->iface->pim, &sg->sgaddr.grp, &embedded_rp);
+
/* this will retry if join previously failed */
sg->tib_joined = tib_sg_gm_join(gm_ifp->pim, sg->sgaddr,
gm_ifp->ifp, &sg->oil);
zlog_debug("%s: ifchannel entry %s(%s) is deleted ", __func__,
ch->sg_str, ch->interface->name);
+#if PIM_IPV == 6
+ /* Embedded RPs learned via PIM join/connected source are freed here */
+ if (pim_embedded_rp_is_embedded(&ch->sg.grp))
+ pim_embedded_rp_delete(pim_ifp->pim, &ch->sg.grp);
+#endif /* PIM_IPV == 6 */
+
XFREE(MTYPE_PIM_IFCHANNEL, ch);
if (up)
int64_t last_route_change_time;
uint64_t gm_rx_drop_sys;
+
+#if PIM_IPV == 6
+ struct {
+ /** Embedded RP enable state. */
+ bool enable;
+ /** Embedded RP group prefix list. */
+ char *group_list;
+ /** Maximum allowed number of embedded RPs at a time. */
+ uint32_t maximum_rps;
+
+ /** Embedded RP routing table */
+ struct route_table *table;
+ /** Embedded RPs count */
+ size_t rp_count;
+ } embedded_rp;
+#endif /* PIM_IPV == 6 */
};
void pim_vrf_init(void);
uint8_t source_flags)
{
struct pim_interface *pim_ifp = NULL;
+#if PIM_IPV == 6
+ pim_addr embedded_rp;
+#endif /* PIM_IPV == 6 */
if (PIM_DEBUG_PIM_J_P)
zlog_debug(
pim_ifp = ifp->info;
assert(pim_ifp);
+#if PIM_IPV == 6
+ if (pim_ifp->pim->embedded_rp.enable && pim_embedded_rp_extract(&sg->grp, &embedded_rp) &&
+ !pim_embedded_rp_filter_match(pim_ifp->pim, &sg->grp))
+ pim_embedded_rp_new(pim_ifp->pim, &sg->grp, &embedded_rp);
+#endif /* PIM_IPV == 6 */
+
++pim_ifp->pim_ifstat_join_recv;
/*
* so the kernel doesn't keep nagging us.
*/
struct pim_rpf *rpg;
+#if PIM_IPV == 6
+ pim_addr embedded_rp;
+
+ if (pim_ifp->pim->embedded_rp.enable &&
+ pim_embedded_rp_extract(&sg.grp, &embedded_rp) &&
+ !pim_embedded_rp_filter_match(pim_ifp->pim, &sg.grp))
+ pim_embedded_rp_new(pim_ifp->pim, &sg.grp, &embedded_rp);
+#endif /* PIM_IPV == 6 */
rpg = RP(pim_ifp->pim, msg->msg_im_dst);
if (!rpg) {
.destroy = routing_control_plane_protocols_control_plane_protocol_pim_address_family_rp_static_rp_rp_list_prefix_list_destroy,
}
},
+ {
+ .xpath = "/frr-routing:routing/control-plane-protocols/control-plane-protocol/frr-pim:pim/address-family/frr-pim-rp:rp/embedded-rp/enable",
+ .cbs = {
+ .modify = pim_embedded_rp_enable_modify,
+ }
+ },
+ {
+ .xpath = "/frr-routing:routing/control-plane-protocols/control-plane-protocol/frr-pim:pim/address-family/frr-pim-rp:rp/embedded-rp/group-list",
+ .cbs = {
+ .modify = pim_embedded_rp_group_list_modify,
+ .destroy = pim_embedded_rp_group_list_destroy,
+ }
+ },
+ {
+ .xpath = "/frr-routing:routing/control-plane-protocols/control-plane-protocol/frr-pim:pim/address-family/frr-pim-rp:rp/embedded-rp/maximum-rps",
+ .cbs = {
+ .modify = pim_embedded_rp_maximum_rps_modify,
+ }
+ },
{
.xpath = "/frr-routing:routing/control-plane-protocols/control-plane-protocol/frr-pim:pim/address-family/frr-pim-rp:rp/auto-rp/discovery-enabled",
.cbs = {
struct nb_cb_modify_args *args);
int routing_control_plane_protocols_control_plane_protocol_pim_address_family_rp_static_rp_rp_list_prefix_list_destroy(
struct nb_cb_destroy_args *args);
+int pim_embedded_rp_enable_modify(struct nb_cb_modify_args *args);
+int pim_embedded_rp_group_list_modify(struct nb_cb_modify_args *args);
+int pim_embedded_rp_group_list_destroy(struct nb_cb_destroy_args *args);
+int pim_embedded_rp_maximum_rps_modify(struct nb_cb_modify_args *args);
int routing_control_plane_protocols_control_plane_protocol_pim_address_family_rp_auto_rp_discovery_enabled_modify(
struct nb_cb_modify_args *args);
int routing_control_plane_protocols_control_plane_protocol_pim_address_family_rp_auto_rp_discovery_enabled_destroy(
"mroute[source-addr='%s'][group-addr='%s']"
#define FRR_PIM_STATIC_RP_XPATH \
"frr-pim-rp:rp/static-rp/rp-list[rp-address='%s']"
+#define FRR_PIM_EMBEDDED_RP_XPATH "./frr-pim-rp:rp/embedded-rp/enable"
+#define FRR_PIM_EMBEDDED_RP_GROUP_LIST_XPATH "./frr-pim-rp:rp/embedded-rp/group-list"
+#define FRR_PIM_EMBEDDED_RP_MAXIMUM_RPS_XPATH "./frr-pim-rp:rp/embedded-rp/maximum-rps"
#define FRR_PIM_AUTORP_XPATH "./frr-pim-rp:rp/auto-rp"
#define FRR_GMP_INTERFACE_XPATH \
"./frr-gmp:gmp/address-family[address-family='%s']"
#include "pim_mlag.h"
#include "pim_bfd.h"
#include "pim_msdp_socket.h"
+#include "pimd/pim_rp.h"
#include "pim_static.h"
#include "pim_ssm.h"
#include "pim_ssmpingd.h"
return NB_OK;
}
+/*
+ * XPath: /frr-routing:routing/control-plane-protocols/control-plane-protocol/frr-pim:pim/address-family/frr-pim-rp:rp/embedded-rp/enable
+ */
+int pim_embedded_rp_enable_modify(struct nb_cb_modify_args *args)
+{
+#if PIM_IPV == 6
+ struct vrf *vrf;
+#endif /* PIM_IPV == 6 */
+
+ switch (args->event) {
+ case NB_EV_APPLY:
+#if PIM_IPV == 6
+ vrf = nb_running_get_entry(args->dnode, NULL, true);
+ pim_embedded_rp_enable(vrf->info, yang_dnode_get_bool(args->dnode, NULL));
+ return NB_OK;
+#else
+ snprintf(args->errmsg, args->errmsg_len, "embedded RP is IPv6 only");
+ return NB_ERR;
+#endif /* PIM_IPV == 6 */
+
+ case NB_EV_ABORT:
+ case NB_EV_PREPARE:
+ case NB_EV_VALIDATE:
+ default:
+ return NB_OK;
+ }
+}
+
+/*
+ * XPath: /frr-routing:routing/control-plane-protocols/control-plane-protocol/frr-pim:pim/address-family/frr-pim-rp:rp/embedded-rp/group-list
+ */
+int pim_embedded_rp_group_list_modify(struct nb_cb_modify_args *args)
+{
+#if PIM_IPV == 6
+ struct vrf *vrf;
+#endif /* PIM_IPV == 6 */
+
+ switch (args->event) {
+ case NB_EV_APPLY:
+#if PIM_IPV == 6
+ vrf = nb_running_get_entry(args->dnode, NULL, true);
+ pim_embedded_rp_set_group_list(vrf->info, yang_dnode_get_string(args->dnode, NULL));
+ return NB_OK;
+#else
+ snprintf(args->errmsg, args->errmsg_len, "embedded RP is IPv6 only");
+ return NB_ERR;
+#endif /* PIM_IPV == 6 */
+
+ case NB_EV_ABORT:
+ case NB_EV_PREPARE:
+ case NB_EV_VALIDATE:
+ default:
+ return NB_OK;
+ }
+}
+
+int pim_embedded_rp_group_list_destroy(struct nb_cb_destroy_args *args)
+{
+#if PIM_IPV == 6
+ struct vrf *vrf;
+#endif /* PIM_IPV == 6 */
+
+ switch (args->event) {
+ case NB_EV_APPLY:
+#if PIM_IPV == 6
+ vrf = nb_running_get_entry(args->dnode, NULL, true);
+ pim_embedded_rp_set_group_list(vrf->info, NULL);
+ return NB_OK;
+#else
+ snprintf(args->errmsg, args->errmsg_len, "embedded RP is IPv6 only");
+ return NB_ERR;
+#endif /* PIM_IPV == 6 */
+
+ case NB_EV_ABORT:
+ case NB_EV_PREPARE:
+ case NB_EV_VALIDATE:
+ default:
+ return NB_OK;
+ }
+}
+
+/*
+ * XPath: /frr-routing:routing/control-plane-protocols/control-plane-protocol/frr-pim:pim/address-family/frr-pim-rp:rp/embedded-rp/maximum-rps
+ */
+int pim_embedded_rp_maximum_rps_modify(struct nb_cb_modify_args *args)
+{
+#if PIM_IPV == 6
+ struct vrf *vrf;
+#endif /* PIM_IPV == 6 */
+
+ switch (args->event) {
+ case NB_EV_APPLY:
+#if PIM_IPV == 6
+ vrf = nb_running_get_entry(args->dnode, NULL, true);
+ pim_embedded_rp_set_maximum_rps(vrf->info, yang_dnode_get_uint32(args->dnode, NULL));
+ return NB_OK;
+#else
+ snprintf(args->errmsg, args->errmsg_len, "embedded RP is IPv6 only");
+ return NB_ERR;
+#endif /* PIM_IPV == 6 */
+
+ case NB_EV_ABORT:
+ case NB_EV_PREPARE:
+ case NB_EV_VALIDATE:
+ default:
+ return NB_OK;
+ }
+}
+
/*
* XPath: /frr-routing:routing/control-plane-protocols/control-plane-protocol/frr-pim:pim/address-family/frr-pim-rp:rp/auto-rp/discovery-enabled
*/
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;
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) {
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) {
}
}
}
+
+#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 */
struct pim_interface;
-enum rp_source { RP_SRC_NONE = 0, RP_SRC_STATIC, RP_SRC_BSR, RP_SRC_AUTORP };
+enum rp_source {
+ RP_SRC_NONE = 0,
+ RP_SRC_STATIC,
+ RP_SRC_BSR,
+ RP_SRC_AUTORP,
+#if PIM_IPV == 6
+ RP_SRC_EMBEDDED_RP,
+#endif /* PIM_IPV == 6*/
+};
struct rp_info {
struct prefix group;
char *plist;
};
+#if PIM_IPV == 6
+/** Default maximum simultaneous embedded RPs at one time. */
+#define PIM_EMBEDDED_RP_MAXIMUM 25
+#endif /* PIM_IPV == 6 */
+
void pim_rp_init(struct pim_instance *pim);
void pim_rp_free(struct pim_instance *pim);
const struct prefix *group);
void pim_upstream_update(struct pim_instance *pim, struct pim_upstream *up);
void pim_rp_refresh_group_to_rp_mapping(struct pim_instance *pim);
+
+#if PIM_IPV == 6
+/** Check if address has valid embedded RP value. */
+bool pim_embedded_rp_is_embedded(const pim_addr *group) __attribute__((nonnull(1)));
+
+/** Test address against embedded RP group list filter. */
+bool pim_embedded_rp_filter_match(const struct pim_instance *pim, const pim_addr *group)
+ __attribute__((nonnull(1, 2)));
+
+/**
+ * Extract embedded RP address from multicast group.
+ *
+ * Returns true if successful otherwise false.
+ */
+bool pim_embedded_rp_extract(const pim_addr *group, pim_addr *rp) __attribute__((nonnull(1, 2)));
+
+/** Allocate new embedded RP. */
+void pim_embedded_rp_new(struct pim_instance *pim, const pim_addr *group, const pim_addr *rp)
+ __attribute__((nonnull(1, 2, 3)));
+
+/** Remove and free allocated embedded RP. */
+void pim_embedded_rp_delete(struct pim_instance *pim, const pim_addr *group)
+ __attribute__((nonnull(1, 2)));
+
+/** Free memory allocated by embedded RP information. */
+extern void pim_embedded_rp_free(struct pim_instance *pim, struct rp_info *rp_info)
+ __attribute__((nonnull(1, 2)));
+
+/** Toggle embedded RP state. */
+extern void pim_embedded_rp_enable(struct pim_instance *pim, bool enable)
+ __attribute__((nonnull(1)));
+
+/** Configure embedded RP group prefix list. */
+extern void pim_embedded_rp_set_group_list(struct pim_instance *pim, const char *group_list)
+ __attribute__((nonnull(1)));
+
+/** Configure maximum number of embedded RPs to learn. */
+extern void pim_embedded_rp_set_maximum_rps(struct pim_instance *pim, uint32_t maximum)
+ __attribute__((nonnull(1)));
+#endif /* PIM_IPV == 6 */
+
#endif
return false;
}
- if (!*oilp)
+ if (!*oilp) {
*oilp = tib_sg_oil_setup(pim, sg, oif);
+#if PIM_IPV == 6
+ if (pim_embedded_rp_is_embedded(&sg.grp))
+ (*oilp)->oil_ref_count--;
+#endif /* PIM_IPV == 6 */
+ }
if (!*oilp)
return false;
++writes;
}
+#if PIM_IPV == 6
+ if (pim->embedded_rp.enable) {
+ vty_out(vty, " embedded-rp\n");
+ writes++;
+ }
+
+ if (pim->embedded_rp.maximum_rps != PIM_EMBEDDED_RP_MAXIMUM) {
+ vty_out(vty, " embedded-rp limit %u\n", pim->embedded_rp.maximum_rps);
+ writes++;
+ }
+
+ if (pim->embedded_rp.group_list) {
+ vty_out(vty, " embedded-rp group-list %s\n", pim->embedded_rp.group_list);
+ writes++;
+ }
+#endif /* PIM_IPV == 6 */
+
writes += pim_rp_config_write(pim, vty);
#if PIM_IPV == 4
writes += pim_autorp_config_write(pim, vty);
"RFC XXXX: A YANG Data Model for PIM RP";
}
+ revision 2024-09-26 {
+ description
+ "Add support for embedded RP.";
+ reference
+ "RFC 3956: Embedding the Rendezvous Point (RP) Address in an IPv6
+ Multicast Address";
+ }
+
typedef plist-ref {
type string;
}
} // static-rp
} // static-rp-container
+ grouping embedded-rp-group {
+ container embedded-rp {
+ description "Embedded RP configurations.";
+
+ leaf enable {
+ description
+ "Toggle embedded RP state:
+
+ When enabled the learned RP from the multicast group address
+ will be preferred over any static or dynamic RP configuration.
+
+ When disabled the packet will be processed as usual.";
+ type boolean;
+ default "false";
+ }
+
+ leaf group-list {
+ description
+ "Restrict embedded RP prefix ranges.
+
+ The default is to treat all multicast groups in FF70::/12
+ range as embedded RP. When a group prefix list is configured
+ and group does not match one of its permit entries it will
+ be treated as regular multicast group.";
+ type plist-ref;
+ }
+
+ leaf maximum-rps {
+ description
+ "Maximum allowed number of RPs to learn.";
+ type uint32;
+ default 25;
+ }
+ } // embedded-rp container
+ } // embedded-rp group
+
grouping auto-rp-container {
description
"Grouping of AutoRP container.";
"Only applicable to IPv4 address family.";
}
}
+
+ uses embedded-rp-group {
+ when "../frr-pim:address-family = 'frr-rt:ipv6'" {
+ description
+ "Only available for IPv6 addresses.";
+ }
+ }
} // rp
} // augment
}