]> git.puffer.fish Git - mirror/frr.git/commitdiff
pim6d: support embedded-rp
authorRafael Zalamena <rzalamena@opensourcerouting.org>
Wed, 2 Oct 2024 12:22:48 +0000 (09:22 -0300)
committerRafael Zalamena <rzalamena@opensourcerouting.org>
Wed, 13 Nov 2024 15:05:35 +0000 (12:05 -0300)
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>
15 files changed:
pimd/pim6_cmd.c
pimd/pim6_cmd.h
pimd/pim6_mld.c
pimd/pim_ifchannel.c
pimd/pim_instance.h
pimd/pim_join.c
pimd/pim_mroute.c
pimd/pim_nb.c
pimd/pim_nb.h
pimd/pim_nb_config.c
pimd/pim_rp.c
pimd/pim_rp.h
pimd/pim_tib.c
pimd/pim_vty.c
yang/frr-pim-rp.yang

index f1ebdb554c0b1fa8301429a8fd0e7ebe891eccaa..12493b7dbb10c79b489344ac62ac4e054cd349a4 100644 (file)
@@ -1217,6 +1217,52 @@ DEFPY_ATTR(no_ipv6_pim_rp_prefix_list,
        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",
@@ -2788,6 +2834,11 @@ void pim_cmd_init(void)
        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);
index 201d8d6bcd343e8b47a9286fb59b26dce0da656c..6d0aacf7166e57edca128c9f1be76136df8e5bbc 100644 (file)
@@ -46,6 +46,7 @@
 #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);
 
index 59cd9aea5f53a011cbc0681293c2012612111574..97e110e3841fa59de7d3a81062b256dc3c5ac38f 100644 (file)
@@ -319,6 +319,9 @@ static void gm_expiry_calc(struct gm_query_timers *timers)
 
 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);
@@ -415,6 +418,13 @@ static void gm_sg_update(struct gm_sg *sg, bool has_expired)
                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);
index 8f9e41039ae69052204ddd9cbc04364c1aae71e6..1791502b94957c50cf7676b0126b36ccc03164ab 100644 (file)
@@ -208,6 +208,12 @@ void pim_ifchannel_delete(struct pim_ifchannel *ch)
                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)
index f484d847b23375c0e9af2a4c6be3fc66c13fda4d..387926c9078d0fafad9fa6fff78da8577c990ffd 100644 (file)
@@ -191,6 +191,22 @@ struct pim_instance {
        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);
index bfdb0f06b410c8aaa50254dd20f6bbef718eafa8..2feafabb4dff62fafdf36835eb3d747671af3dda 100644 (file)
@@ -42,6 +42,9 @@ static void recv_join(struct interface *ifp, struct pim_neighbor *neigh,
                      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(
@@ -53,6 +56,12 @@ static void recv_join(struct interface *ifp, struct pim_neighbor *neigh,
        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;
 
        /*
index adc47e719d24475c9eb73103a6ef9fc1f188c460..9d290c3c6f2b979971bc4eac911cdb8996378bc5 100644 (file)
@@ -182,6 +182,14 @@ int pim_mroute_msg_nocache(int fd, struct interface *ifp, const kernmsg *msg)
                 * 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) {
index 66001d1463b998683c5507c2458fe5158ff9a7ee..1dc66be82d7daa5a7af7f8e31dc5670a149bc70e 100644 (file)
@@ -379,6 +379,25 @@ const struct frr_yang_module_info frr_pim_rp_info = {
                                .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 = {
index befad4efe435a385f4b2861c6b5534e259e29702..b45af3d589da25338d1d3214d27db8c237734a59 100644 (file)
@@ -159,6 +159,10 @@ int routing_control_plane_protocols_control_plane_protocol_pim_address_family_rp
        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(
@@ -286,6 +290,9 @@ int routing_control_plane_protocols_name_validate(
        "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']"
index ea8b56fee39562e1121d70d8bf37f3784bea6db0..87338f37c0ea113186010629b6cb54b7d12c74f2 100644 (file)
@@ -17,6 +17,7 @@
 #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"
@@ -2684,6 +2685,115 @@ int routing_control_plane_protocols_control_plane_protocol_pim_address_family_rp
        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
  */
index 0c47bc15823d35cf364aa84ff4361574bd9c17b4..cf370857ff924a1fed55caea2416182d0d7a8753 100644 (file)
@@ -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 */
index 24832d0dbd603814fdad92ca8b03d3df4c1ba35d..9da059f8be04087e0e6f506e601c065d4907ea36 100644 (file)
 
 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;
@@ -26,6 +34,11 @@ struct rp_info {
        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);
 
@@ -69,4 +82,45 @@ struct rp_info *pim_rp_find_match_group(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
index ac07154f86c17e463842f10ecfaa857d9b4be12e..12ae0d6246d1e6267402b6327da9898df7d0903d 100644 (file)
@@ -115,8 +115,13 @@ bool tib_sg_gm_join(struct pim_instance *pim, pim_sgaddr sg,
                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;
 
index b633e81d5536fc183f672e19c0c38fad8553fb11..4d83593c17d21237d3344cf9c8e023d299ca5200 100644 (file)
@@ -186,6 +186,23 @@ int pim_global_config_write_worker(struct pim_instance *pim, struct vty *vty)
                ++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);
index dbd5513ee57ba85f23160de12e317b6bbebc0a23..cbc6e87b805bc7384b8d961190f12e6dc0893f54 100644 (file)
@@ -64,6 +64,14 @@ module frr-pim-rp {
       "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;
   }
@@ -111,6 +119,42 @@ module frr-pim-rp {
     } // 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.";
@@ -194,6 +238,13 @@ module frr-pim-rp {
             "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
 }